From 19a294b0cc6ff052692d4ef5b27f5bcf720822fa Mon Sep 17 00:00:00 2001 From: Vid Kersic <38610409+Vid201@users.noreply.github.com> Date: Wed, 28 Feb 2024 13:21:14 +0100 Subject: [PATCH 001/430] fix(foundryup): use fish_add_path in fish shell (#7258) --- foundryup/install | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/foundryup/install b/foundryup/install index 19bfe5738..da8156a09 100755 --- a/foundryup/install +++ b/foundryup/install @@ -45,7 +45,12 @@ esac # Only add foundryup if it isn't already in PATH. if [[ ":$PATH:" != *":${FOUNDRY_BIN_DIR}:"* ]]; then # Add the foundryup directory to the path and ensure the old PATH variables remain. - echo >> "$PROFILE" && echo "export PATH=\"\$PATH:$FOUNDRY_BIN_DIR\"" >> "$PROFILE" + # If the shell is fish, echo fish_add_path instead of export. + if [[ "$PREF_SHELL" == "fish" ]]; then + echo >> "$PROFILE" && echo "fish_add_path -a $FOUNDRY_BIN_DIR" >> "$PROFILE" + else + echo >> "$PROFILE" && echo "export PATH=\"\$PATH:$FOUNDRY_BIN_DIR\"" >> "$PROFILE" + fi fi # Warn MacOS users that they may need to manually install libusb via Homebrew: From 5d572e3f501f8844a3c45dc880debfb315534fe1 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 28 Feb 2024 15:15:09 +0200 Subject: [PATCH 002/430] chore: use collect in invariant code (#7259) --- .../evm/evm/src/executors/invariant/error.rs | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/crates/evm/evm/src/executors/invariant/error.rs b/crates/evm/evm/src/executors/invariant/error.rs index 0d619740e..917e7732f 100644 --- a/crates/evm/evm/src/executors/invariant/error.rs +++ b/crates/evm/evm/src/executors/invariant/error.rs @@ -253,18 +253,14 @@ impl InvariantFuzzError { let shrunk_call_indices = self.try_shrinking_recurse(calls, executor, 0, 0); - // we recreate the call sequence in the same order as they reproduce the failure - // otherwise we could end up with inverted sequence - // e.g. in a sequence of: + // We recreate the call sequence in the same order as they reproduce the failure, + // otherwise we could end up with inverted sequence. + // E.g. in a sequence of: // 1. Alice calls acceptOwnership and reverts // 2. Bob calls transferOwnership to Alice // 3. Alice calls acceptOwnership and test fails - // we shrink to indices of [2, 1] and we recreate call sequence in same order - let mut new_calls_sequence = Vec::with_capacity(shrunk_call_indices.len()); - shrunk_call_indices.iter().for_each(|call_index| { - new_calls_sequence.push(calls.get(*call_index).unwrap()); - }); - new_calls_sequence + // we shrink to indices of [2, 1] and we recreate call sequence in same order. + shrunk_call_indices.iter().map(|idx| &calls[*idx]).collect() } /// We try to construct a [powerset](https://en.wikipedia.org/wiki/Power_set) of the sequence if @@ -341,14 +337,12 @@ impl InvariantFuzzError { ); }); - // SAFETY: there are no more live references to shrunk_call_indices as the parallel - // execution is finished, so it is fine to get the inner value via unwrap & - // into_inner - let shrunk_call_indices = - Arc::>>::try_unwrap(shrunk_call_indices).unwrap().into_inner(); + // There are no more live references to shrunk_call_indices as the parallel execution is + // finished, so it is fine to get the inner value via `Arc::unwrap`. + let shrunk_call_indices = Arc::try_unwrap(shrunk_call_indices).unwrap().into_inner(); if is_powerset { - // a powerset is guaranteed to be smallest local subset, so we return early + // A powerset is guaranteed to be smallest local subset, so we return early. return shrunk_call_indices } From 6af18e4f2cb65fc9eb5a9f25499bacbad4254aad Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 28 Feb 2024 15:37:43 +0200 Subject: [PATCH 003/430] fix(cast): correctly compute mapping indexes (#7261) * fix(cast): correctly compute mapping indexes * chore: better error msg --- crates/cast/src/lib.rs | 57 +++++++++++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index ca6eb83be..d8c2e995c 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -2,7 +2,7 @@ use alloy_dyn_abi::{DynSolType, DynSolValue, FunctionExt}; use alloy_json_abi::ContractObject; use alloy_primitives::{ utils::{keccak256, ParseUnits, Unit}, - Address, Bytes, B256, I256, U256, + Address, Bytes, Keccak256, B256, I256, U256, }; use alloy_rlp::Decodable; use base::{Base, NumberWithBase, ToBase}; @@ -1656,16 +1656,20 @@ impl SimpleCast { .collect::>>() } - /// Prints the slot number for the specified mapping type and input data - /// Uses abi_encode to pad the data to 32 bytes. - /// For value types v, slot number of v is keccak256(concat(h(v) , p)) where h is the padding - /// function and p is slot number of the mapping. + /// Prints the slot number for the specified mapping type and input data. + /// + /// For value types `v`, slot number of `v` is `keccak256(concat(h(v), p))` where `h` is the + /// padding function for `v`'s type, and `p` is slot number of the mapping. + /// + /// See [the Solidity documentation](https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html#mappings-and-dynamic-arrays) + /// for more details. /// /// # Example /// /// ``` /// # use cast::SimpleCast as Cast; /// + /// // Value types. /// assert_eq!( /// Cast::index("address", "0xD0074F4E6490ae3f888d1d4f7E3E43326bD3f0f5", "2").unwrap().as_str(), /// "0x9525a448a9000053a4d151336329d6563b7e80b24f8e628e95527f218e8ab5fb" @@ -1674,13 +1678,48 @@ impl SimpleCast { /// Cast::index("uint256", "42", "6").unwrap().as_str(), /// "0xfc808b0f31a1e6b9cf25ff6289feae9b51017b392cc8e25620a94a38dcdafcc1" /// ); + /// + /// // Strings and byte arrays. + /// assert_eq!( + /// Cast::index("string", "hello", "1").unwrap().as_str(), + /// "0x8404bb4d805e9ca2bd5dd5c43a107e935c8ec393caa7851b353b3192cd5379ae" + /// ); /// # Ok::<_, eyre::Report>(()) /// ``` pub fn index(from_type: &str, from_value: &str, slot_number: &str) -> Result { - let sig = format!("x({from_type},uint256)"); - let encoded = Self::abi_encode(&sig, &[from_value, slot_number])?; - let location: String = Self::keccak(&encoded)?; - Ok(location) + let mut hasher = Keccak256::new(); + + let v_ty = DynSolType::parse(from_type).wrap_err("Could not parse type")?; + let v = v_ty.coerce_str(from_value).wrap_err("Could not parse value")?; + match v_ty { + // For value types, `h` pads the value to 32 bytes in the same way as when storing the + // value in memory. + DynSolType::Bool | + DynSolType::Int(_) | + DynSolType::Uint(_) | + DynSolType::FixedBytes(_) | + DynSolType::Address | + DynSolType::Function => hasher.update(v.as_word().unwrap()), + + // For strings and byte arrays, `h(k)` is just the unpadded data. + DynSolType::String | DynSolType::Bytes => hasher.update(v.as_packed_seq().unwrap()), + + DynSolType::Array(..) | + DynSolType::FixedArray(..) | + DynSolType::Tuple(..) | + DynSolType::CustomStruct { .. } => { + eyre::bail!("Type `{v_ty}` is not supported as a mapping key") + } + } + + let p = DynSolType::Uint(256) + .coerce_str(slot_number) + .wrap_err("Could not parse slot number")?; + let p = p.as_word().unwrap(); + hasher.update(p); + + let location = hasher.finalize(); + Ok(location.to_string()) } /// Converts ENS names to their namehash representation From fa5e71c91170d26b2b90b804bf910200ef9c5e59 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 28 Feb 2024 14:40:18 +0100 Subject: [PATCH 004/430] fix: normalize solc evm if set (#7096) * wip:fix: normalize solc evm if set * feat: normalize evm version for forge config --- crates/config/src/lib.rs | 31 ++++++++++++++++++++++++++----- crates/forge/bin/cmd/config.rs | 5 ++++- crates/forge/tests/cli/config.rs | 8 ++++++++ 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 32c713230..033a6789c 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -580,12 +580,13 @@ impl Config { self.evm_version = self.get_normalized_evm_version(); } - /// Returns the normalized [EvmVersion] if a [SolcReq] is set to a valid version. + /// Returns the normalized [EvmVersion] if a [SolcReq] is set to a valid version or if the solc + /// path is a valid solc binary. /// /// Otherwise it returns the configured [EvmVersion]. pub fn get_normalized_evm_version(&self) -> EvmVersion { - if let Some(SolcReq::Version(version)) = &self.solc { - if let Some(evm_version) = self.evm_version.normalize_version(version) { + if let Some(version) = self.solc.as_ref().and_then(|solc| solc.try_version().ok()) { + if let Some(evm_version) = self.evm_version.normalize_version(&version) { return evm_version; } } @@ -1602,11 +1603,15 @@ impl Config { /// /// See also fn normalize_defaults(&mut self, figment: Figment) -> Figment { - if let Ok(version) = figment.extract_inner::("solc") { + 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) = self.evm_version.normalize_version(&version) { + if let Some(version) = solc + .try_version() + .ok() + .and_then(|version| self.evm_version.normalize_version(&version)) + { // normalize evm_version based on the provided solc version self.evm_version = version; } @@ -1999,6 +2004,22 @@ pub enum SolcReq { Local(PathBuf), } +impl SolcReq { + /// Tries to get the solc version from the `SolcReq` + /// + /// If the `SolcReq` is a `Version` it will return the version, if it's a path to a binary it + /// will try to get the version from the binary. + fn try_version(&self) -> Result { + match self { + SolcReq::Version(version) => Ok(version.clone()), + SolcReq::Local(path) => Solc::new(path).version().map_err(|err| { + warn!("failed to get solc version from {}: {}", path.display(), err); + err + }), + } + } +} + impl> From for SolcReq { fn from(s: T) -> Self { let s = s.as_ref(); diff --git a/crates/forge/bin/cmd/config.rs b/crates/forge/bin/cmd/config.rs index 731a545e4..0758ddf50 100644 --- a/crates/forge/bin/cmd/config.rs +++ b/crates/forge/bin/cmd/config.rs @@ -39,7 +39,10 @@ impl ConfigArgs { return Ok(()) } - let config = self.try_load_config_unsanitized_emit_warnings()?; + let config = self + .try_load_config_unsanitized_emit_warnings()? + // we explicitly normalize the version, so mimic the behavior when invoking solc + .normalized_evm_version(); let s = if self.basic { let config = config.into_basic(); diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index d8f9db310..f2dbf22c7 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -673,3 +673,11 @@ forgetest_init!(can_resolve_symlink_fs_permissions, |prj, cmd| { let permission = fs_permissions.find_permission(&config_path.join("config.json")).unwrap(); assert_eq!(permission, FsAccessPermission::Read); }); + +// tests if evm version is normalized for config output +forgetest!(normalize_config_evm_version, |_prj, cmd| { + cmd.args(["config", "--use", "0.8.0", "--json"]); + let output = cmd.stdout_lossy(); + let config: Config = serde_json::from_str(&output).unwrap(); + assert_eq!(config.evm_version, EvmVersion::Istanbul); +}); From 6ca37340f11efe7dec18446aa1b999e98099954a Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Wed, 28 Feb 2024 15:40:37 +0200 Subject: [PATCH 005/430] invariant shrink #6683: check if test failed instead revert (#7257) * closes #6683: when deciding min seq to shrink to check if test failure instead revert * Fix lint * Changes after review + ensure test shrinked sequence is 3 or less --- .../evm/evm/src/executors/invariant/error.rs | 12 +- crates/forge/tests/it/invariant.rs | 69 ++++++++++- .../common/InvariantShrinkWithAssert.t.sol | 115 ++++++++++++++++++ 3 files changed, 192 insertions(+), 4 deletions(-) create mode 100644 testdata/fuzz/invariant/common/InvariantShrinkWithAssert.t.sol diff --git a/crates/evm/evm/src/executors/invariant/error.rs b/crates/evm/evm/src/executors/invariant/error.rs index 917e7732f..50dedab7c 100644 --- a/crates/evm/evm/src/executors/invariant/error.rs +++ b/crates/evm/evm/src/executors/invariant/error.rs @@ -214,12 +214,18 @@ impl InvariantFuzzError { .call_raw_committing(*sender, *addr, bytes.clone(), U256::ZERO) .expect("bad call to evm"); - // Checks the invariant. If we exit before the last call, all the better. + // Checks the invariant. If we revert or fail before the last call, all the better. if let Some(func) = &self.func { - let error_call_result = executor + let mut call_result = executor .call_raw(CALLER, self.addr, func.clone(), U256::ZERO) .expect("bad call to evm"); - if error_call_result.reverted { + let is_success = executor.is_raw_call_success( + self.addr, + call_result.state_changeset.take().unwrap(), + &call_result, + false, + ); + if !is_success { let mut locked = curr_seq.write(); if new_sequence[..=seq_idx].len() < locked.len() { // update the curr_sequence if the new sequence is lower than diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index efc4364fc..c41bf6823 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -2,7 +2,7 @@ use crate::config::*; use alloy_primitives::U256; -use forge::fuzz::CounterExample; +use forge::{fuzz::CounterExample, result::TestStatus, TestOptions}; use foundry_test_utils::Filter; use std::collections::BTreeMap; @@ -118,6 +118,26 @@ async fn test_invariant() { None, )], ), + ( + "fuzz/invariant/common/InvariantShrinkWithAssert.t.sol:InvariantShrinkWithAssert", + vec![( + "invariant_with_assert()", + false, + Some("".into()), + None, + None, + )], + ), + ( + "fuzz/invariant/common/InvariantShrinkWithAssert.t.sol:InvariantShrinkWithRequire", + vec![( + "invariant_with_require()", + false, + Some("revert: wrong counter".into()), + None, + None, + )], + ), ]), ); } @@ -259,3 +279,50 @@ async fn test_invariant_shrink() { } }; } + +#[tokio::test(flavor = "multi_thread")] +#[cfg_attr(windows, ignore = "for some reason there's different rng")] +async fn test_invariant_assert_shrink() { + let mut opts = test_opts(); + opts.fuzz.seed = Some(U256::from(119u32)); + + // ensure assert and require shrinks to same sequence of 3 or less + test_shrink(opts.clone(), "InvariantShrinkWithAssert").await; + test_shrink(opts.clone(), "InvariantShrinkWithRequire").await; +} + +async fn test_shrink(opts: TestOptions, contract_pattern: &str) { + let mut runner = runner().await; + runner.test_options = opts.clone(); + let results = runner + .test_collect( + &Filter::new( + ".*", + contract_pattern, + ".*fuzz/invariant/common/InvariantShrinkWithAssert.t.sol", + ), + opts, + ) + .await; + let results = results.values().last().expect("`InvariantShrinkWithAssert` should be testable."); + + let result = results + .test_results + .values() + .last() + .expect("`InvariantShrinkWithAssert` should be testable."); + + assert_eq!(result.status, TestStatus::Failure); + + let counter = result + .counterexample + .as_ref() + .expect("`InvariantShrinkWithAssert` should have failed with a counterexample."); + + match counter { + CounterExample::Single(_) => panic!("CounterExample should be a sequence."), + CounterExample::Sequence(sequence) => { + assert!(sequence.len() <= 3); + } + }; +} diff --git a/testdata/fuzz/invariant/common/InvariantShrinkWithAssert.t.sol b/testdata/fuzz/invariant/common/InvariantShrinkWithAssert.t.sol new file mode 100644 index 000000000..0ba6d61c8 --- /dev/null +++ b/testdata/fuzz/invariant/common/InvariantShrinkWithAssert.t.sol @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "ds-test/test.sol"; +import "../../../cheats/Vm.sol"; + +struct FuzzSelector { + address addr; + bytes4[] selectors; +} + +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } + + function decrement() public { + number--; + } + + function double() public { + number *= 2; + } + + function half() public { + number /= 2; + } + + function triple() public { + number *= 3; + } + + function third() public { + number /= 3; + } + + function quadruple() public { + number *= 4; + } + + function quarter() public { + number /= 4; + } +} + +contract Handler is DSTest { + Counter public counter; + + constructor(Counter _counter) { + counter = _counter; + counter.setNumber(0); + } + + function increment() public { + counter.increment(); + } + + function setNumber(uint256 x) public { + counter.setNumber(x); + } +} + +contract InvariantShrinkWithAssert is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + Counter public counter; + Handler handler; + + function setUp() public { + counter = new Counter(); + handler = new Handler(counter); + } + + function targetSelectors() public returns (FuzzSelector[] memory) { + FuzzSelector[] memory targets = new FuzzSelector[](1); + bytes4[] memory selectors = new bytes4[](2); + selectors[0] = handler.increment.selector; + selectors[1] = handler.setNumber.selector; + targets[0] = FuzzSelector(address(handler), selectors); + return targets; + } + + function invariant_with_assert() public { + assertTrue(counter.number() != 3); + } +} + +contract InvariantShrinkWithRequire is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + Counter public counter; + Handler handler; + + function setUp() public { + counter = new Counter(); + handler = new Handler(counter); + } + + function targetSelectors() public returns (FuzzSelector[] memory) { + FuzzSelector[] memory targets = new FuzzSelector[](1); + bytes4[] memory selectors = new bytes4[](2); + selectors[0] = handler.increment.selector; + selectors[1] = handler.setNumber.selector; + targets[0] = FuzzSelector(address(handler), selectors); + return targets; + } + + function invariant_with_require() public { + require(counter.number() != 3, "wrong counter"); + } +} From 485a0d67d409e34491de2bbc111f266813802cbb Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 28 Feb 2024 16:57:59 +0100 Subject: [PATCH 006/430] fix: also try error payload response (#7264) --- crates/common/src/provider/retry.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/common/src/provider/retry.rs b/crates/common/src/provider/retry.rs index 70c5998c7..fd8a33e42 100644 --- a/crates/common/src/provider/retry.rs +++ b/crates/common/src/provider/retry.rs @@ -31,6 +31,10 @@ impl RetryPolicy for RateLimitRetryPolicy { // the start. TransportError::SerError(_) => false, TransportError::DeserError { text, .. } => { + if let Ok(resp) = serde_json::from_str::(text) { + return should_retry_json_rpc_error(&resp) + } + // some providers send invalid JSON RPC in the error case (no `id:u64`), but the // text should be a `JsonRpcError` #[derive(Deserialize)] @@ -41,6 +45,7 @@ impl RetryPolicy for RateLimitRetryPolicy { if let Ok(resp) = serde_json::from_str::(text) { return should_retry_json_rpc_error(&resp.error) } + false } TransportError::ErrorResp(err) => should_retry_json_rpc_error(err), From 165ccc070587805385fa7b5716f5dfa03a68e92b Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Wed, 28 Feb 2024 18:06:31 +0200 Subject: [PATCH 007/430] Contrib docs: clippy all-targets (#7263) --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 859aaa6d6..98b36e853 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -86,7 +86,7 @@ Please also make sure that the following commands pass if you have changed the c cargo check --all cargo test --all --all-features cargo +nightly fmt -- --check -cargo +nightly clippy --all --all-features -- -D warnings +cargo +nightly clippy --all --all-targets --all-features -- -D warnings ``` If you are working in VSCode, we recommend you install the [rust-analyzer](https://rust-analyzer.github.io/) extension, and use the following VSCode user settings: From 27357bfe0cfca520780736b02bd1f4ba48205410 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Wed, 28 Feb 2024 20:55:51 +0200 Subject: [PATCH 008/430] invariant: #6694 add `preserve_state` config (#7219) * - add preserve_state invariant config: useful for handlers that change state (e.g. using cheatcodes like roll, warp), see #6694 - active only in conjunction with fail_on_revert true * Add test from issue 6694 --- crates/config/README.md | 1 + crates/config/src/invariant.rs | 7 +++ crates/evm/evm/src/executors/invariant/mod.rs | 12 +++-- crates/forge/tests/it/config.rs | 1 + crates/forge/tests/it/invariant.rs | 53 +++++++++++++++++++ testdata/foundry.toml | 1 + .../common/InvariantPreserveState.t.sol | 49 +++++++++++++++++ 7 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 testdata/fuzz/invariant/common/InvariantPreserveState.t.sol diff --git a/crates/config/README.md b/crates/config/README.md index 8e4bdf70e..46fa83e5a 100644 --- a/crates/config/README.md +++ b/crates/config/README.md @@ -195,6 +195,7 @@ dictionary_weight = 80 include_storage = true include_push_bytes = true shrink_sequence = true +preserve_state = false [fmt] line_length = 100 diff --git a/crates/config/src/invariant.rs b/crates/config/src/invariant.rs index e4e2f86a8..d2594c820 100644 --- a/crates/config/src/invariant.rs +++ b/crates/config/src/invariant.rs @@ -28,6 +28,11 @@ pub struct InvariantConfig { pub shrink_sequence: bool, /// The maximum number of attempts to shrink the sequence pub shrink_run_limit: usize, + /// If set to true then VM state is committed and available for next call + /// Useful for handlers that use cheatcodes as roll or warp + /// Applies only when `fail_on_revert` set to true. Use it with caution, introduces performance + /// penalty. + pub preserve_state: bool, } impl Default for InvariantConfig { @@ -40,6 +45,7 @@ impl Default for InvariantConfig { dictionary: FuzzDictionaryConfig { dictionary_weight: 80, ..Default::default() }, shrink_sequence: true, shrink_run_limit: 2usize.pow(18_u32), + preserve_state: false, } } } @@ -68,6 +74,7 @@ impl InlineConfigParser for InvariantConfig { "fail-on-revert" => conf_clone.fail_on_revert = parse_config_bool(key, value)?, "call-override" => conf_clone.call_override = parse_config_bool(key, value)?, "shrink-sequence" => conf_clone.shrink_sequence = parse_config_bool(key, value)?, + "preserve-state" => conf_clone.preserve_state = parse_config_bool(key, value)?, _ => Err(InlineConfigParserError::InvalidConfigProperty(key.to_string()))?, } } diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 69e141255..316db1320 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -156,9 +156,15 @@ impl<'a> InvariantExecutor<'a> { let (sender, (address, calldata)) = inputs.last().expect("no input generated"); // Executes the call from the randomly generated sequence. - let call_result = executor - .call_raw(*sender, *address, calldata.clone(), U256::ZERO) - .expect("could not make raw evm call"); + let call_result = if self.config.fail_on_revert && self.config.preserve_state { + executor + .call_raw_committing(*sender, *address, calldata.clone(), U256::ZERO) + .expect("could not make raw evm call") + } else { + executor + .call_raw(*sender, *address, calldata.clone(), U256::ZERO) + .expect("could not make raw evm call") + }; // Collect data for fuzzing from the state changeset. let mut state_changeset = diff --git a/crates/forge/tests/it/config.rs b/crates/forge/tests/it/config.rs index 04f58234f..542f9a88e 100644 --- a/crates/forge/tests/it/config.rs +++ b/crates/forge/tests/it/config.rs @@ -139,6 +139,7 @@ pub fn test_opts() -> TestOptions { }, shrink_sequence: true, shrink_run_limit: 2usize.pow(18u32), + preserve_state: false, }) .build(&COMPILED, &PROJECT.paths.root) .expect("Config loaded") diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index c41bf6823..5959b0926 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -138,6 +138,10 @@ async fn test_invariant() { None, )], ), + ( + "fuzz/invariant/common/InvariantPreserveState.t.sol:InvariantPreserveState", + vec![("invariant_preserve_state()", true, None, None, None)], + ), ]), ); } @@ -326,3 +330,52 @@ async fn test_shrink(opts: TestOptions, contract_pattern: &str) { } }; } + +#[tokio::test(flavor = "multi_thread")] +async fn test_invariant_preserve_state() { + let mut runner = runner().await; + + // should not fail with default options + let mut opts = test_opts(); + opts.invariant.fail_on_revert = true; + runner.test_options = opts.clone(); + let results = runner + .test_collect( + &Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantPreserveState.t.sol"), + opts, + ) + .await; + assert_multiple( + &results, + BTreeMap::from([( + "fuzz/invariant/common/InvariantPreserveState.t.sol:InvariantPreserveState", + vec![("invariant_preserve_state()", true, None, None, None)], + )]), + ); + + // same test should revert when preserve state enabled + let mut opts = test_opts(); + opts.invariant.fail_on_revert = true; + opts.invariant.preserve_state = true; + runner.test_options = opts.clone(); + + let results = runner + .test_collect( + &Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantPreserveState.t.sol"), + opts, + ) + .await; + assert_multiple( + &results, + BTreeMap::from([( + "fuzz/invariant/common/InvariantPreserveState.t.sol:InvariantPreserveState", + vec![( + "invariant_preserve_state()", + false, + Some("EvmError: Revert".into()), + None, + None, + )], + )]), + ); +} diff --git a/testdata/foundry.toml b/testdata/foundry.toml index 94bdeea39..22df7b3e5 100644 --- a/testdata/foundry.toml +++ b/testdata/foundry.toml @@ -17,6 +17,7 @@ force = false invariant_fail_on_revert = false invariant_call_override = false invariant_shrink_sequence = true +invariant_preserve_state = false gas_limit = 9223372036854775807 gas_price = 0 gas_reports = ["*"] diff --git a/testdata/fuzz/invariant/common/InvariantPreserveState.t.sol b/testdata/fuzz/invariant/common/InvariantPreserveState.t.sol new file mode 100644 index 000000000..20f34c68d --- /dev/null +++ b/testdata/fuzz/invariant/common/InvariantPreserveState.t.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "../../../cheats/Vm.sol"; + +struct FuzzSelector { + address addr; + bytes4[] selectors; +} + +// https://github.com/foundry-rs/foundry/issues/7219 + +contract Handler is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + function thisFunctionReverts() external { + if (block.number < 10) {} else { + revert(); + } + } + + function advanceTime(uint256 blocks) external { + blocks = blocks % 10; + vm.roll(block.number + blocks); + vm.warp(block.timestamp + blocks * 12); + } +} + +contract InvariantPreserveState is DSTest { + Handler handler; + + function setUp() public { + handler = new Handler(); + } + + function targetSelectors() public returns (FuzzSelector[] memory) { + FuzzSelector[] memory targets = new FuzzSelector[](1); + bytes4[] memory selectors = new bytes4[](2); + selectors[0] = handler.thisFunctionReverts.selector; + selectors[1] = handler.advanceTime.selector; + targets[0] = FuzzSelector(address(handler), selectors); + return targets; + } + + function invariant_preserve_state() public { + assertTrue(true); + } +} From 2f432fb72e3080c44b1fa472ae050f7e76c42b6d Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 28 Feb 2024 22:39:49 +0100 Subject: [PATCH 009/430] chore: bump alloy chains (#7269) --- Cargo.lock | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a9ccd381b..a3ec2e699 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -78,9 +78,9 @@ checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "alloy-chains" -version = "0.1.12" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146dc3f33a9e282751a62ddd6687292c504605cc285a49500541e5d1e5b7617b" +checksum = "973deb9e9d5db1f28c2a478073aeb435f1c07f72cf5935caa0c421e6b68f2db1" dependencies = [ "num_enum", "serde", @@ -5065,7 +5065,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ - "proc-macro-crate 3.1.0", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 2.0.50", @@ -5737,15 +5737,6 @@ dependencies = [ "toml_edit 0.20.7", ] -[[package]] -name = "proc-macro-crate" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" -dependencies = [ - "toml_edit 0.21.1", -] - [[package]] name = "proc-macro-error" version = "1.0.4" From 576bb59d0e72b4f9c5bf15871ee04745ce39c808 Mon Sep 17 00:00:00 2001 From: "Jongseung (John) Lim" Date: Thu, 29 Feb 2024 05:30:54 -0500 Subject: [PATCH 010/430] fix(forge): list cache files that are saved as block numbers for `cache ls` (#7270) * fix: forge cache ls should include blocknumber files * fix: ignore files that are not numeric only * chore: linting --- crates/config/src/lib.rs | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 033a6789c..5c704d6c7 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -1521,12 +1521,19 @@ impl Config { if !chain_path.exists() { return Ok(blocks) } - for block in chain_path.read_dir()?.flatten().filter(|x| x.file_type().unwrap().is_dir()) { - let filepath = block.path().join("storage.json"); - blocks.push(( - block.file_name().to_string_lossy().into_owned(), - fs::metadata(filepath)?.len(), - )); + for block in chain_path.read_dir()?.flatten() { + let file_type = block.file_type()?; + let file_name = block.file_name(); + let filepath = if file_type.is_dir() { + block.path().join("storage.json") + } else if file_type.is_file() && + file_name.to_string_lossy().chars().all(char::is_numeric) + { + block.path() + } else { + continue + }; + blocks.push((file_name.to_string_lossy().into_owned(), fs::metadata(filepath)?.len())); } Ok(blocks) } @@ -4559,23 +4566,38 @@ mod tests { writeln!(file, "{}", vec![' '; size_bytes - 1].iter().collect::()).unwrap(); } + fn fake_block_cache_block_path_as_file( + chain_path: &Path, + block_number: &str, + size_bytes: usize, + ) { + let block_path = chain_path.join(block_number); + let mut file = File::create(block_path).unwrap(); + writeln!(file, "{}", vec![' '; size_bytes - 1].iter().collect::()).unwrap(); + } + let chain_dir = tempdir()?; fake_block_cache(chain_dir.path(), "1", 100); fake_block_cache(chain_dir.path(), "2", 500); + fake_block_cache_block_path_as_file(chain_dir.path(), "3", 900); // Pollution file that should not show up in the cached block let mut pol_file = File::create(chain_dir.path().join("pol.txt")).unwrap(); writeln!(pol_file, "{}", [' '; 10].iter().collect::()).unwrap(); let result = Config::get_cached_blocks(chain_dir.path())?; - assert_eq!(result.len(), 2); + assert_eq!(result.len(), 3); let block1 = &result.iter().find(|x| x.0 == "1").unwrap(); let block2 = &result.iter().find(|x| x.0 == "2").unwrap(); + let block3 = &result.iter().find(|x| x.0 == "3").unwrap(); + assert_eq!(block1.0, "1"); assert_eq!(block1.1, 100); assert_eq!(block2.0, "2"); assert_eq!(block2.1, 500); + assert_eq!(block3.0, "3"); + assert_eq!(block3.1, 900); chain_dir.close()?; Ok(()) From eab0390707419cf4866ea82b13e6687dfd5fb387 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 29 Feb 2024 13:08:07 +0200 Subject: [PATCH 011/430] chore(cli): fix clap deprecated warnings (#7274) * chore: #[clap] -> #[command], #[arg] * chore: #[arg(name)] -> #[arg(id)] * chore: remove #[arg(use_value_delimiter)] * fix: update IDs in conflicts_with --- crates/anvil/server/src/config.rs | 7 +- crates/anvil/src/anvil.rs | 12 +- crates/anvil/src/cmd.rs | 100 ++++---- crates/cast/bin/cmd/access_list.rs | 16 +- crates/cast/bin/cmd/bind.rs | 10 +- crates/cast/bin/cmd/call.rs | 24 +- crates/cast/bin/cmd/create2.rs | 22 +- crates/cast/bin/cmd/estimate.rs | 16 +- crates/cast/bin/cmd/find_block.rs | 2 +- crates/cast/bin/cmd/interface.rs | 10 +- crates/cast/bin/cmd/logs.rs | 16 +- crates/cast/bin/cmd/rpc.rs | 4 +- crates/cast/bin/cmd/run.rs | 18 +- crates/cast/bin/cmd/send.rs | 20 +- crates/cast/bin/cmd/storage.rs | 12 +- crates/cast/bin/cmd/wallet/list.rs | 12 +- crates/cast/bin/cmd/wallet/mod.rs | 52 +++-- crates/cast/bin/cmd/wallet/vanity.rs | 8 +- crates/cast/bin/opts.rs | 334 +++++++++++++-------------- crates/chisel/bin/main.rs | 10 +- crates/cli/src/opts/build/core.rs | 34 +-- crates/cli/src/opts/build/mod.rs | 14 +- crates/cli/src/opts/build/paths.rs | 18 +- crates/cli/src/opts/ethereum.rs | 18 +- crates/cli/src/opts/transaction.rs | 14 +- crates/common/src/evm.rs | 58 ++--- crates/forge/bin/cmd/bind.rs | 26 +-- crates/forge/bin/cmd/build.rs | 14 +- crates/forge/bin/cmd/cache.rs | 13 +- crates/forge/bin/cmd/config.rs | 10 +- crates/forge/bin/cmd/coverage.rs | 12 +- crates/forge/bin/cmd/create.rs | 22 +- crates/forge/bin/cmd/debug.rs | 12 +- crates/forge/bin/cmd/doc/mod.rs | 18 +- crates/forge/bin/cmd/flatten.rs | 6 +- crates/forge/bin/cmd/fmt.rs | 8 +- crates/forge/bin/cmd/geiger/mod.rs | 10 +- crates/forge/bin/cmd/generate/mod.rs | 4 +- crates/forge/bin/cmd/init.rs | 14 +- crates/forge/bin/cmd/inspect.rs | 6 +- crates/forge/bin/cmd/install.rs | 14 +- crates/forge/bin/cmd/mod.rs | 4 +- crates/forge/bin/cmd/remappings.rs | 4 +- crates/forge/bin/cmd/remove.rs | 4 +- crates/forge/bin/cmd/retry.rs | 6 +- crates/forge/bin/cmd/script/mod.rs | 46 ++-- crates/forge/bin/cmd/selectors.rs | 18 +- crates/forge/bin/cmd/snapshot.rs | 22 +- crates/forge/bin/cmd/test/filter.rs | 16 +- crates/forge/bin/cmd/test/mod.rs | 32 +-- crates/forge/bin/cmd/tree.rs | 6 +- crates/forge/bin/cmd/update.rs | 6 +- crates/forge/bin/cmd/verify/mod.rs | 42 ++-- crates/forge/bin/cmd/watch.rs | 10 +- crates/forge/bin/opts.rs | 50 ++-- crates/wallets/src/multi_wallet.rs | 32 +-- crates/wallets/src/raw_wallet.rs | 14 +- crates/wallets/src/wallet.rs | 20 +- 58 files changed, 686 insertions(+), 696 deletions(-) diff --git a/crates/anvil/server/src/config.rs b/crates/anvil/server/src/config.rs index 7e2352831..f968a6da3 100644 --- a/crates/anvil/server/src/config.rs +++ b/crates/anvil/server/src/config.rs @@ -4,16 +4,15 @@ use std::str::FromStr; /// Additional server options. #[derive(Clone, Debug, Serialize, Deserialize)] -#[cfg_attr(feature = "clap", derive(clap::Parser), clap(next_help_heading = "Server options"))] +#[cfg_attr(feature = "clap", derive(clap::Parser), command(next_help_heading = "Server options"))] pub struct ServerConfig { /// The cors `allow_origin` header #[cfg_attr( feature = "clap", - clap( + arg( long, help = "Set the CORS allow_origin", default_value = "*", - name = "allow-origin", value_name = "ALLOW_ORIGIN" ) )] @@ -21,7 +20,7 @@ pub struct ServerConfig { /// Whether to enable CORS #[cfg_attr( feature = "clap", - clap(long, help = "Disable CORS", conflicts_with = "allow-origin") + arg(long, help = "Disable CORS", conflicts_with = "allow_origin") )] pub no_cors: bool, } diff --git a/crates/anvil/src/anvil.rs b/crates/anvil/src/anvil.rs index 5ff939482..cc60fe722 100644 --- a/crates/anvil/src/anvil.rs +++ b/crates/anvil/src/anvil.rs @@ -4,26 +4,26 @@ use clap::{CommandFactory, Parser, Subcommand}; /// A fast local Ethereum development node. #[derive(Parser)] -#[clap(name = "anvil", version = anvil::VERSION_MESSAGE, next_display_order = None)] +#[command(name = "anvil", version = anvil::VERSION_MESSAGE, next_display_order = None)] pub struct Anvil { - #[clap(flatten)] + #[command(flatten)] pub node: NodeArgs, - #[clap(subcommand)] + #[command(subcommand)] pub cmd: Option, } #[derive(Subcommand)] pub enum AnvilSubcommand { /// Generate shell completions script. - #[clap(visible_alias = "com")] + #[command(visible_alias = "com")] Completions { - #[clap(value_enum)] + #[arg(value_enum)] shell: clap_complete::Shell, }, /// Generate Fig autocompletion spec. - #[clap(visible_alias = "fig")] + #[command(visible_alias = "fig")] GenerateFigSpec, } diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index b87da3557..070a97481 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -30,31 +30,31 @@ use tokio::time::{Instant, Interval}; #[derive(Clone, Debug, Parser)] pub struct NodeArgs { /// Port number to listen on. - #[clap(long, short, default_value = "8545", value_name = "NUM")] + #[arg(long, short, default_value = "8545", value_name = "NUM")] pub port: u16, /// Number of dev accounts to generate and configure. - #[clap(long, short, default_value = "10", value_name = "NUM")] + #[arg(long, short, default_value = "10", value_name = "NUM")] pub accounts: u64, /// The balance of every dev account in Ether. - #[clap(long, default_value = "10000", value_name = "NUM")] + #[arg(long, default_value = "10000", value_name = "NUM")] pub balance: u64, /// The timestamp of the genesis block. - #[clap(long, value_name = "NUM")] + #[arg(long, value_name = "NUM")] pub timestamp: Option, /// BIP39 mnemonic phrase used for generating accounts. /// Cannot be used if `mnemonic_random` or `mnemonic_seed` are used - #[clap(long, short, conflicts_with_all = &["mnemonic_seed", "mnemonic_random"])] + #[arg(long, short, conflicts_with_all = &["mnemonic_seed", "mnemonic_random"])] pub mnemonic: Option, /// Automatically generates a BIP39 mnemonic phrase, and derives accounts from it. /// Cannot be used with other `mnemonic` options /// You can specify the number of words you want in the mnemonic. /// [default: 12] - #[clap(long, conflicts_with_all = &["mnemonic", "mnemonic_seed"], default_missing_value = "12", num_args(0..=1))] + #[arg(long, conflicts_with_all = &["mnemonic", "mnemonic_seed"], default_missing_value = "12", num_args(0..=1))] pub mnemonic_random: Option, /// Generates a BIP39 mnemonic phrase from a given seed @@ -62,40 +62,40 @@ pub struct NodeArgs { /// /// CAREFUL: this is NOT SAFE and should only be used for testing. /// Never use the private keys generated in production. - #[clap(long = "mnemonic-seed-unsafe", conflicts_with_all = &["mnemonic", "mnemonic_random"])] + #[arg(long = "mnemonic-seed-unsafe", conflicts_with_all = &["mnemonic", "mnemonic_random"])] pub mnemonic_seed: Option, /// Sets the derivation path of the child key to be derived. /// /// [default: m/44'/60'/0'/0/] - #[clap(long)] + #[arg(long)] pub derivation_path: Option, /// Don't print anything on startup and don't print logs - #[clap(long)] + #[arg(long)] pub silent: bool, /// The EVM hardfork to use. /// /// Choose the hardfork by name, e.g. `shanghai`, `paris`, `london`, etc... /// [default: latest] - #[clap(long, value_parser = Hardfork::from_str)] + #[arg(long, value_parser = Hardfork::from_str)] pub hardfork: Option, /// Block time in seconds for interval mining. - #[clap(short, long, visible_alias = "blockTime", name = "block-time", value_name = "SECONDS")] + #[arg(short, long, visible_alias = "blockTime", value_name = "SECONDS")] pub block_time: Option, /// Writes output of `anvil` as json to user-specified file. - #[clap(long, value_name = "OUT_FILE")] + #[arg(long, value_name = "OUT_FILE")] pub config_out: Option, /// Disable auto and interval mining, and mine on demand instead. - #[clap(long, visible_alias = "no-mine", conflicts_with = "block-time")] + #[arg(long, visible_alias = "no-mine", conflicts_with = "block_time")] pub no_mining: bool, /// The hosts the server will listen on. - #[clap( + #[arg( long, value_name = "IP_ADDR", env = "ANVIL_IP_ADDR", @@ -106,18 +106,18 @@ pub struct NodeArgs { pub host: Vec, /// How transactions are sorted in the mempool. - #[clap(long, default_value = "fees")] + #[arg(long, default_value = "fees")] pub order: TransactionOrder, /// Initialize the genesis block with the given `genesis.json` file. - #[clap(long, value_name = "PATH", value_parser= read_genesis_file)] + #[arg(long, value_name = "PATH", value_parser= read_genesis_file)] pub init: Option, /// This is an alias for both --load-state and --dump-state. /// /// It initializes the chain with the state and block environment stored at the file, if it /// exists, and dumps the chain's state on exit. - #[clap( + #[arg( long, value_name = "PATH", value_parser = StateFile::parse, @@ -132,17 +132,17 @@ pub struct NodeArgs { /// Interval in seconds at which the state and block environment is to be dumped to disk. /// /// See --state and --dump-state - #[clap(short, long, value_name = "SECONDS")] + #[arg(short, long, value_name = "SECONDS")] pub state_interval: Option, /// Dump the state and block environment of chain on exit to the given file. /// /// If the value is a directory, the state will be written to `/state.json`. - #[clap(long, value_name = "PATH", conflicts_with = "init")] + #[arg(long, value_name = "PATH", conflicts_with = "init")] pub dump_state: Option, /// Initialize the chain from a previously saved state snapshot. - #[clap( + #[arg( long, value_name = "PATH", value_parser = SerializableState::parse, @@ -150,22 +150,22 @@ pub struct NodeArgs { )] pub load_state: Option, - #[clap(long, help = IPC_HELP, value_name = "PATH", visible_alias = "ipcpath")] + #[arg(long, help = IPC_HELP, value_name = "PATH", visible_alias = "ipcpath")] pub ipc: Option>, /// Don't keep full chain history. /// If a number argument is specified, at most this number of states is kept in memory. - #[clap(long)] + #[arg(long)] pub prune_history: Option>, /// Number of blocks with transactions to keep in memory. - #[clap(long)] + #[arg(long)] pub transaction_block_keeper: Option, - #[clap(flatten)] + #[command(flatten)] pub evm_opts: AnvilEvmArgs, - #[clap(flatten)] + #[command(flatten)] pub server_config: ServerConfig, } @@ -344,12 +344,12 @@ impl NodeArgs { /// Anvil's EVM related arguments. #[derive(Clone, Debug, Parser)] -#[clap(next_help_heading = "EVM options")] +#[command(next_help_heading = "EVM options")] pub struct AnvilEvmArgs { /// Fetch state over a remote endpoint instead of starting from an empty state. /// /// If you want to fetch state from a specific block number, add a block number like `http://localhost:8545@1400000` or use the `--fork-block-number` argument. - #[clap( + #[arg( long, short, visible_alias = "rpc-url", @@ -361,7 +361,7 @@ pub struct AnvilEvmArgs { /// Headers to use for the rpc client, e.g. "User-Agent: test-agent" /// /// See --fork-url. - #[clap( + #[arg( long = "fork-header", value_name = "HEADERS", help_heading = "Fork config", @@ -372,35 +372,25 @@ pub struct AnvilEvmArgs { /// Timeout in ms for requests sent to remote JSON-RPC server in forking mode. /// /// Default value 45000 - #[clap( - long = "timeout", - name = "timeout", - help_heading = "Fork config", - requires = "fork_url" - )] + #[arg(id = "timeout", long = "timeout", help_heading = "Fork config", requires = "fork_url")] pub fork_request_timeout: Option, /// Number of retry requests for spurious networks (timed out requests) /// /// Default value 5 - #[clap( - long = "retries", - name = "retries", - help_heading = "Fork config", - requires = "fork_url" - )] + #[arg(id = "retries", long = "retries", help_heading = "Fork config", requires = "fork_url")] pub fork_request_retries: Option, /// Fetch state from a specific block number over a remote endpoint. /// /// See --fork-url. - #[clap(long, requires = "fork_url", value_name = "BLOCK", help_heading = "Fork config")] + #[arg(long, requires = "fork_url", value_name = "BLOCK", help_heading = "Fork config")] pub fork_block_number: Option, /// Initial retry backoff on encountering errors. /// /// See --fork-url. - #[clap(long, requires = "fork_url", value_name = "BACKOFF", help_heading = "Fork config")] + #[arg(long, requires = "fork_url", value_name = "BACKOFF", help_heading = "Fork config")] pub fork_retry_backoff: Option, /// Specify chain id to skip fetching it from remote endpoint. This enables offline-start mode. @@ -408,7 +398,7 @@ pub struct AnvilEvmArgs { /// You still must pass both `--fork-url` and `--fork-block-number`, and already have your /// required state cached on disk, anything missing locally would be fetched from the /// remote. - #[clap( + #[arg( long, help_heading = "Fork config", value_name = "CHAIN", @@ -422,7 +412,7 @@ pub struct AnvilEvmArgs { /// /// See --fork-url. /// See also, https://docs.alchemy.com/reference/compute-units#what-are-cups-compute-units-per-second - #[clap( + #[arg( long, requires = "fork_url", alias = "cups", @@ -437,7 +427,7 @@ pub struct AnvilEvmArgs { /// /// See --fork-url. /// See also, https://docs.alchemy.com/reference/compute-units#what-are-cups-compute-units-per-second - #[clap( + #[arg( long, requires = "fork_url", value_name = "NO_RATE_LIMITS", @@ -453,15 +443,15 @@ pub struct AnvilEvmArgs { /// This flag overrides the project's configuration file. /// /// See --fork-url. - #[clap(long, requires = "fork_url", help_heading = "Fork config")] + #[arg(long, requires = "fork_url", help_heading = "Fork config")] pub no_storage_caching: bool, /// The block gas limit. - #[clap(long, alias = "block-gas-limit", help_heading = "Environment config")] + #[arg(long, alias = "block-gas-limit", help_heading = "Environment config")] pub gas_limit: Option, /// Disable the `call.gas_limit <= block.gas_limit` constraint. - #[clap( + #[arg( long, value_name = "DISABLE_GAS_LIMIT", help_heading = "Environment config", @@ -472,15 +462,15 @@ pub struct AnvilEvmArgs { /// EIP-170: Contract code size limit in bytes. Useful to increase this because of tests. By /// default, it is 0x6000 (~25kb). - #[clap(long, value_name = "CODE_SIZE", help_heading = "Environment config")] + #[arg(long, value_name = "CODE_SIZE", help_heading = "Environment config")] pub code_size_limit: Option, /// The gas price. - #[clap(long, help_heading = "Environment config")] + #[arg(long, help_heading = "Environment config")] pub gas_price: Option, /// The base fee in a block. - #[clap( + #[arg( long, visible_alias = "base-fee", value_name = "FEE", @@ -489,19 +479,19 @@ pub struct AnvilEvmArgs { pub block_base_fee_per_gas: Option, /// The chain ID. - #[clap(long, alias = "chain", help_heading = "Environment config")] + #[arg(long, alias = "chain", help_heading = "Environment config")] pub chain_id: Option, /// Enable steps tracing used for debug calls returning geth-style traces - #[clap(long, visible_alias = "tracing")] + #[arg(long, visible_alias = "tracing")] pub steps_tracing: bool, /// Enable autoImpersonate on startup - #[clap(long, visible_alias = "auto-impersonate")] + #[arg(long, visible_alias = "auto-impersonate")] pub auto_impersonate: bool, /// Run an Optimism chain - #[clap(long, visible_alias = "optimism")] + #[arg(long, visible_alias = "optimism")] pub optimism: bool, } diff --git a/crates/cast/bin/cmd/access_list.rs b/crates/cast/bin/cmd/access_list.rs index fa31380ff..2ac5a0488 100644 --- a/crates/cast/bin/cmd/access_list.rs +++ b/crates/cast/bin/cmd/access_list.rs @@ -15,22 +15,22 @@ use std::str::FromStr; #[derive(Debug, Parser)] pub struct AccessListArgs { /// The destination of the transaction. - #[clap( + #[arg( value_name = "TO", value_parser = NameOrAddress::from_str )] to: Option, /// The signature of the function to call. - #[clap(value_name = "SIG")] + #[arg(value_name = "SIG")] sig: Option, /// The arguments of the function to call. - #[clap(value_name = "ARGS")] + #[arg(value_name = "ARGS")] args: Vec, /// The data for the transaction. - #[clap( + #[arg( long, value_name = "DATA", conflicts_with_all = &["sig", "args"] @@ -40,17 +40,17 @@ pub struct AccessListArgs { /// The block height to query at. /// /// Can also be the tags earliest, finalized, safe, latest, or pending. - #[clap(long, short = 'B')] + #[arg(long, short = 'B')] block: Option, /// Print the access list as JSON. - #[clap(long, short, help_heading = "Display options")] + #[arg(long, short, help_heading = "Display options")] json: bool, - #[clap(flatten)] + #[command(flatten)] tx: TransactionOpts, - #[clap(flatten)] + #[command(flatten)] eth: EthereumOpts, } diff --git a/crates/cast/bin/cmd/bind.rs b/crates/cast/bin/cmd/bind.rs index 2de32e2ff..73a62825a 100644 --- a/crates/cast/bin/cmd/bind.rs +++ b/crates/cast/bin/cmd/bind.rs @@ -18,7 +18,7 @@ pub struct BindArgs { path_or_address: String, /// Path to where bindings will be stored - #[clap( + #[arg( short, long, value_hint = ValueHint::DirPath, @@ -30,7 +30,7 @@ pub struct BindArgs { /// /// This should be a valid crates.io crate name. However, this is currently not validated by /// this command. - #[clap( + #[arg( long, default_value = DEFAULT_CRATE_NAME, value_name = "NAME" @@ -41,7 +41,7 @@ pub struct BindArgs { /// /// This should be a standard semver version string. However, it is not currently validated by /// this command. - #[clap( + #[arg( long, default_value = DEFAULT_CRATE_VERSION, value_name = "VERSION" @@ -49,10 +49,10 @@ pub struct BindArgs { crate_version: String, /// Generate bindings as separate files. - #[clap(long)] + #[arg(long)] separate_files: bool, - #[clap(flatten)] + #[command(flatten)] etherscan: EtherscanOpts, } diff --git a/crates/cast/bin/cmd/call.rs b/crates/cast/bin/cmd/call.rs index 4851e9cf9..3d8afbfd2 100644 --- a/crates/cast/bin/cmd/call.rs +++ b/crates/cast/bin/cmd/call.rs @@ -22,7 +22,7 @@ type Provider = ethers_providers::Provider; #[derive(Debug, Parser)] pub struct CallArgs { /// The destination of the transaction. - #[clap(value_parser = NameOrAddress::from_str)] + #[arg(value_parser = NameOrAddress::from_str)] to: Option, /// The signature of the function to call. @@ -32,51 +32,51 @@ pub struct CallArgs { args: Vec, /// Data for the transaction. - #[clap( + #[arg( long, conflicts_with_all = &["sig", "args"] )] data: Option, /// Forks the remote rpc, executes the transaction locally and prints a trace - #[clap(long, default_value_t = false)] + #[arg(long, default_value_t = false)] trace: bool, /// Opens an interactive debugger. /// Can only be used with `--trace`. - #[clap(long, requires = "trace")] + #[arg(long, requires = "trace")] debug: bool, /// Labels to apply to the traces; format: `address:label`. /// Can only be used with `--trace`. - #[clap(long, requires = "trace")] + #[arg(long, requires = "trace")] labels: Vec, /// The EVM Version to use. /// Can only be used with `--trace`. - #[clap(long, requires = "trace")] + #[arg(long, requires = "trace")] evm_version: Option, /// The block height to query at. /// /// Can also be the tags earliest, finalized, safe, latest, or pending. - #[clap(long, short)] + #[arg(long, short)] block: Option, - #[clap(subcommand)] + #[command(subcommand)] command: Option, - #[clap(flatten)] + #[command(flatten)] tx: TransactionOpts, - #[clap(flatten)] + #[command(flatten)] eth: EthereumOpts, } #[derive(Debug, Parser)] pub enum CallSubcommands { /// ignores the address field and simulates creating a contract - #[clap(name = "--create")] + #[command(name = "--create")] Create { /// Bytecode of contract. code: String, @@ -92,7 +92,7 @@ pub enum CallSubcommands { /// Either specified in wei, or as a string with a unit type. /// /// Examples: 1ether, 10gwei, 0.01ether - #[clap(long, value_parser = parse_ether_value)] + #[arg(long, value_parser = parse_ether_value)] value: Option, }, } diff --git a/crates/cast/bin/cmd/create2.rs b/crates/cast/bin/cmd/create2.rs index dfe34724d..6474d52e5 100644 --- a/crates/cast/bin/cmd/create2.rs +++ b/crates/cast/bin/cmd/create2.rs @@ -19,7 +19,7 @@ const DEPLOYER: &str = "0x4e59b44847b379578588920ca78fbf26c0b4956c"; #[derive(Clone, Debug, Parser)] pub struct Create2Args { /// Prefix for the contract address. - #[clap( + #[arg( long, short, required_unless_present_any = &["ends_with", "matching"], @@ -28,19 +28,19 @@ pub struct Create2Args { starts_with: Option, /// Suffix for the contract address. - #[clap(long, short, value_name = "HEX")] + #[arg(long, short, value_name = "HEX")] ends_with: Option, /// Sequence that the address has to match. - #[clap(long, short, value_name = "HEX")] + #[arg(long, short, value_name = "HEX")] matching: Option, /// Case sensitive matching. - #[clap(short, long)] + #[arg(short, long)] case_sensitive: bool, /// Address of the contract deployer. - #[clap( + #[arg( short, long, default_value = DEPLOYER, @@ -49,27 +49,27 @@ pub struct Create2Args { deployer: Address, /// Init code of the contract to be deployed. - #[clap(short, long, value_name = "HEX")] + #[arg(short, long, value_name = "HEX")] init_code: Option, /// Init code hash of the contract to be deployed. - #[clap(alias = "ch", long, value_name = "HASH", required_unless_present = "init_code")] + #[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. - #[clap(short, long)] + #[arg(short, long)] jobs: Option, /// Address of the caller. Used for the first 20 bytes of the salt. - #[clap(long, value_name = "ADDRESS")] + #[arg(long, value_name = "ADDRESS")] caller: Option
, /// The random number generator's seed, used to initialize the salt. - #[clap(long, value_name = "HEX")] + #[arg(long, value_name = "HEX")] seed: Option, /// Don't initialize the salt with a random value, and instead use the default value of 0. - #[clap(long, conflicts_with = "seed")] + #[arg(long, conflicts_with = "seed")] no_random: bool, } diff --git a/crates/cast/bin/cmd/estimate.rs b/crates/cast/bin/cmd/estimate.rs index 6089f0153..56fdc40d3 100644 --- a/crates/cast/bin/cmd/estimate.rs +++ b/crates/cast/bin/cmd/estimate.rs @@ -14,7 +14,7 @@ use std::str::FromStr; #[derive(Debug, Parser)] pub struct EstimateArgs { /// The destination of the transaction. - #[clap(value_parser = NameOrAddress::from_str)] + #[arg(value_parser = NameOrAddress::from_str)] to: Option, /// The signature of the function to call. @@ -24,7 +24,7 @@ pub struct EstimateArgs { args: Vec, /// The sender account. - #[clap( + #[arg( short, long, value_parser = NameOrAddress::from_str, @@ -38,23 +38,23 @@ pub struct EstimateArgs { /// Either specified in wei, or as a string with a unit type: /// /// Examples: 1ether, 10gwei, 0.01ether - #[clap(long, value_parser = parse_ether_value)] + #[arg(long, value_parser = parse_ether_value)] value: Option, - #[clap(flatten)] + #[command(flatten)] rpc: RpcOpts, - #[clap(flatten)] + #[command(flatten)] etherscan: EtherscanOpts, - #[clap(subcommand)] + #[command(subcommand)] command: Option, } #[derive(Debug, Parser)] pub enum EstimateSubcommands { /// Estimate gas cost to deploy a smart contract - #[clap(name = "--create")] + #[command(name = "--create")] Create { /// The bytecode of contract code: String, @@ -70,7 +70,7 @@ pub enum EstimateSubcommands { /// Either specified in wei, or as a string with a unit type: /// /// Examples: 1ether, 10gwei, 0.01ether - #[clap(long, value_parser = parse_ether_value)] + #[arg(long, value_parser = parse_ether_value)] value: Option, }, } diff --git a/crates/cast/bin/cmd/find_block.rs b/crates/cast/bin/cmd/find_block.rs index 381d52c50..5038ded7b 100644 --- a/crates/cast/bin/cmd/find_block.rs +++ b/crates/cast/bin/cmd/find_block.rs @@ -14,7 +14,7 @@ pub struct FindBlockArgs { /// The UNIX timestamp to search for, in seconds. timestamp: u64, - #[clap(flatten)] + #[command(flatten)] rpc: RpcOpts, } diff --git a/crates/cast/bin/cmd/interface.rs b/crates/cast/bin/cmd/interface.rs index 9d3af767f..14de351f2 100644 --- a/crates/cast/bin/cmd/interface.rs +++ b/crates/cast/bin/cmd/interface.rs @@ -16,17 +16,17 @@ pub struct InterfaceArgs { path_or_address: String, /// The name to use for the generated interface. - #[clap(long, short)] + #[arg(long, short)] name: Option, /// Solidity pragma version. - #[clap(long, short, default_value = "^0.8.4", value_name = "VERSION")] + #[arg(long, short, default_value = "^0.8.4", value_name = "VERSION")] pragma: String, /// The path to the output file. /// /// If not specified, the interface will be output to stdout. - #[clap( + #[arg( short, long, value_hint = clap::ValueHint::FilePath, @@ -35,10 +35,10 @@ pub struct InterfaceArgs { output: Option, /// If specified, the interface will be output as JSON rather than Solidity. - #[clap(long, short)] + #[arg(long, short)] json: bool, - #[clap(flatten)] + #[command(flatten)] etherscan: EtherscanOpts, } diff --git a/crates/cast/bin/cmd/logs.rs b/crates/cast/bin/cmd/logs.rs index 752d90dfe..e7816afa3 100644 --- a/crates/cast/bin/cmd/logs.rs +++ b/crates/cast/bin/cmd/logs.rs @@ -22,17 +22,17 @@ pub struct LogsArgs { /// The block height to start query at. /// /// Can also be the tags earliest, finalized, safe, latest, or pending. - #[clap(long)] + #[arg(long)] from_block: Option, /// The block height to stop query at. /// /// Can also be the tags earliest, finalized, safe, latest, or pending. - #[clap(long)] + #[arg(long)] to_block: Option, /// The contract address to filter on. - #[clap( + #[arg( long, value_parser = NameOrAddress::from_str )] @@ -40,24 +40,24 @@ pub struct LogsArgs { /// The signature of the event to filter logs by which will be converted to the first topic or /// a topic to filter on. - #[clap(value_name = "SIG_OR_TOPIC")] + #[arg(value_name = "SIG_OR_TOPIC")] sig_or_topic: Option, /// If used with a signature, the indexed fields of the event to filter by. Otherwise, the /// remaining topics of the filter. - #[clap(value_name = "TOPICS_OR_ARGS")] + #[arg(value_name = "TOPICS_OR_ARGS")] topics_or_args: Vec, /// If the RPC type and endpoints supports `eth_subscribe` stream logs instead of printing and /// exiting. Will continue until interrupted or TO_BLOCK is reached. - #[clap(long)] + #[arg(long)] subscribe: bool, /// Print the logs as JSON.s - #[clap(long, short, help_heading = "Display options")] + #[arg(long, short, help_heading = "Display options")] json: bool, - #[clap(flatten)] + #[command(flatten)] eth: EthereumOpts, } diff --git a/crates/cast/bin/cmd/rpc.rs b/crates/cast/bin/cmd/rpc.rs index 44275204a..9dffcfd18 100644 --- a/crates/cast/bin/cmd/rpc.rs +++ b/crates/cast/bin/cmd/rpc.rs @@ -26,10 +26,10 @@ pub struct RpcArgs { /// /// cast rpc eth_getBlockByNumber '["0x123", false]' --raw /// => {"method": "eth_getBlockByNumber", "params": ["0x123", false] ... } - #[clap(long, short = 'w')] + #[arg(long, short = 'w')] raw: bool, - #[clap(flatten)] + #[command(flatten)] rpc: RpcOpts, } diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index ccdd26480..796368c69 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -25,36 +25,36 @@ pub struct RunArgs { tx_hash: String, /// Opens the transaction in the debugger. - #[clap(long, short)] + #[arg(long, short)] debug: bool, /// Print out opcode traces. - #[clap(long, short)] + #[arg(long, short)] trace_printer: bool, /// Executes the transaction only with the state from the previous block. /// /// May result in different results than the live execution! - #[clap(long, short)] + #[arg(long, short)] quick: bool, /// Prints the full address of the contract. - #[clap(long, short)] + #[arg(long, short)] verbose: bool, /// Label addresses in the trace. /// /// Example: 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045:vitalik.eth - #[clap(long, short)] + #[arg(long, short)] label: Vec, - #[clap(flatten)] + #[command(flatten)] rpc: RpcOpts, /// The EVM version to use. /// /// Overrides the version specified in the config. - #[clap(long, short)] + #[arg(long, short)] evm_version: Option, /// Sets the number of assumed available compute units per second for this provider @@ -62,7 +62,7 @@ pub struct RunArgs { /// default value: 330 /// /// See also, https://docs.alchemy.com/reference/compute-units#what-are-cups-compute-units-per-second - #[clap(long, alias = "cups", value_name = "CUPS")] + #[arg(long, alias = "cups", value_name = "CUPS")] pub compute_units_per_second: Option, /// Disables rate limiting for this node's provider. @@ -70,7 +70,7 @@ pub struct RunArgs { /// default value: false /// /// See also, https://docs.alchemy.com/reference/compute-units#what-are-cups-compute-units-per-second - #[clap(long, value_name = "NO_RATE_LIMITS", visible_alias = "no-rpc-rate-limit")] + #[arg(long, value_name = "NO_RATE_LIMITS", visible_alias = "no-rpc-rate-limit")] pub no_rate_limit: bool, } diff --git a/crates/cast/bin/cmd/send.rs b/crates/cast/bin/cmd/send.rs index b68ba5c75..22366483c 100644 --- a/crates/cast/bin/cmd/send.rs +++ b/crates/cast/bin/cmd/send.rs @@ -22,7 +22,7 @@ pub struct SendTxArgs { /// The destination of the transaction. /// /// If not provided, you must use cast send --create. - #[clap(value_parser = NameOrAddress::from_str)] + #[arg(value_parser = NameOrAddress::from_str)] to: Option, /// The signature of the function to call. @@ -32,39 +32,39 @@ pub struct SendTxArgs { args: Vec, /// Only print the transaction hash and exit immediately. - #[clap(name = "async", long = "async", alias = "cast-async", env = "CAST_ASYNC")] + #[arg(id = "async", long = "async", alias = "cast-async", env = "CAST_ASYNC")] cast_async: bool, /// The number of confirmations until the receipt is fetched. - #[clap(long, default_value = "1")] + #[arg(long, default_value = "1")] confirmations: usize, /// Print the transaction receipt as JSON. - #[clap(long, short, help_heading = "Display options")] + #[arg(long, short, help_heading = "Display options")] json: bool, /// Reuse the latest nonce for the sender account. - #[clap(long, conflicts_with = "nonce")] + #[arg(long, conflicts_with = "nonce")] resend: bool, - #[clap(subcommand)] + #[command(subcommand)] command: Option, /// Send via `eth_sendTransaction using the `--from` argument or $ETH_FROM as sender - #[clap(long, requires = "from")] + #[arg(long, requires = "from")] unlocked: bool, - #[clap(flatten)] + #[command(flatten)] tx: TransactionOpts, - #[clap(flatten)] + #[command(flatten)] eth: EthereumOpts, } #[derive(Debug, Parser)] pub enum SendTxSubcommands { /// Use to deploy raw contract bytecode. - #[clap(name = "--create")] + #[command(name = "--create")] Create { /// The bytecode of the contract to deploy. code: String, diff --git a/crates/cast/bin/cmd/storage.rs b/crates/cast/bin/cmd/storage.rs index f8f01b7ae..397bd07e2 100644 --- a/crates/cast/bin/cmd/storage.rs +++ b/crates/cast/bin/cmd/storage.rs @@ -36,26 +36,26 @@ const MIN_SOLC: Version = Version::new(0, 6, 5); #[derive(Clone, Debug, Parser)] pub struct StorageArgs { /// The contract address. - #[clap(value_parser = NameOrAddress::from_str)] + #[arg(value_parser = NameOrAddress::from_str)] address: NameOrAddress, /// The storage slot number. - #[clap(value_parser = parse_slot)] + #[arg(value_parser = parse_slot)] slot: Option, /// The block height to query at. /// /// Can also be the tags earliest, finalized, safe, latest, or pending. - #[clap(long, short)] + #[arg(long, short)] block: Option, - #[clap(flatten)] + #[command(flatten)] rpc: RpcOpts, - #[clap(flatten)] + #[command(flatten)] etherscan: EtherscanOpts, - #[clap(flatten)] + #[command(flatten)] build: CoreBuildArgs, } diff --git a/crates/cast/bin/cmd/wallet/list.rs b/crates/cast/bin/cmd/wallet/list.rs index b0984d4ba..2790366a3 100644 --- a/crates/cast/bin/cmd/wallet/list.rs +++ b/crates/cast/bin/cmd/wallet/list.rs @@ -10,27 +10,27 @@ use foundry_wallets::multi_wallet::MultiWalletOptsBuilder; pub struct ListArgs { /// List all the accounts in the keystore directory. /// Default keystore directory is used if no path provided. - #[clap(long, default_missing_value = "", num_args(0..=1))] + #[arg(long, default_missing_value = "", num_args(0..=1))] dir: Option, /// List accounts from a Ledger hardware wallet. - #[clap(long, short, group = "hw-wallets")] + #[arg(long, short, group = "hw-wallets")] ledger: bool, /// List accounts from a Trezor hardware wallet. - #[clap(long, short, group = "hw-wallets")] + #[arg(long, short, group = "hw-wallets")] trezor: bool, /// List accounts from AWS KMS. - #[clap(long)] + #[arg(long)] aws: bool, /// List all configured accounts. - #[clap(long, group = "hw-wallets")] + #[arg(long, group = "hw-wallets")] all: bool, /// Max number of addresses to display from hardware wallets. - #[clap(long, short, default_value = "3", requires = "hw-wallets")] + #[arg(long, short, default_value = "3", requires = "hw-wallets")] max_senders: Option, } diff --git a/crates/cast/bin/cmd/wallet/mod.rs b/crates/cast/bin/cmd/wallet/mod.rs index c5c045b75..9a08984d7 100644 --- a/crates/cast/bin/cmd/wallet/mod.rs +++ b/crates/cast/bin/cmd/wallet/mod.rs @@ -25,7 +25,7 @@ use list::ListArgs; #[derive(Debug, Parser)] pub enum WalletSubcommands { /// Create a new random keypair. - #[clap(visible_alias = "n")] + #[command(visible_alias = "n")] New { /// If provided, then keypair will be written to an encrypted JSON keystore. path: Option, @@ -33,53 +33,53 @@ pub enum WalletSubcommands { /// Triggers a hidden password prompt for the JSON keystore. /// /// Deprecated: prompting for a hidden password is now the default. - #[clap(long, short, requires = "path", conflicts_with = "unsafe_password")] + #[arg(long, short, requires = "path", conflicts_with = "unsafe_password")] password: bool, /// Password for the JSON keystore in cleartext. /// /// This is UNSAFE to use and we recommend using the --password. - #[clap(long, requires = "path", env = "CAST_PASSWORD", value_name = "PASSWORD")] + #[arg(long, requires = "path", env = "CAST_PASSWORD", value_name = "PASSWORD")] unsafe_password: Option, /// Number of wallets to generate. - #[clap(long, short, default_value = "1")] + #[arg(long, short, default_value = "1")] number: u32, /// Output generated wallets as JSON. - #[clap(long, short, default_value = "false")] + #[arg(long, short, default_value = "false")] json: bool, }, /// Generates a random BIP39 mnemonic phrase - #[clap(visible_alias = "nm")] + #[command(visible_alias = "nm")] NewMnemonic { /// Number of words for the mnemonic - #[clap(long, short, default_value = "12")] + #[arg(long, short, default_value = "12")] words: usize, /// Number of accounts to display - #[clap(long, short, default_value = "1")] + #[arg(long, short, default_value = "1")] accounts: u8, }, /// Generate a vanity address. - #[clap(visible_alias = "va")] + #[command(visible_alias = "va")] Vanity(VanityArgs), /// Convert a private key to an address. - #[clap(visible_aliases = &["a", "addr"])] + #[command(visible_aliases = &["a", "addr"])] Address { /// If provided, the address will be derived from the specified private key. - #[clap(value_name = "PRIVATE_KEY")] + #[arg(value_name = "PRIVATE_KEY")] private_key_override: Option, - #[clap(flatten)] + #[command(flatten)] wallet: WalletOpts, }, /// Sign a message or typed data. - #[clap(visible_alias = "s")] + #[command(visible_alias = "s")] Sign { /// The message, typed data, or hash to sign. /// @@ -97,23 +97,23 @@ pub enum WalletSubcommands { message: String, /// Treat the message as JSON typed data. - #[clap(long)] + #[arg(long)] data: bool, /// Treat the message as a file containing JSON typed data. Requires `--data`. - #[clap(long, requires = "data")] + #[arg(long, requires = "data")] from_file: bool, /// Treat the message as a raw 32-byte hash and sign it directly without hashing it again. - #[clap(long, conflicts_with = "data")] + #[arg(long, conflicts_with = "data")] no_hash: bool, - #[clap(flatten)] + #[command(flatten)] wallet: WalletOpts, }, /// Verify the signature of a message. - #[clap(visible_alias = "v")] + #[command(visible_alias = "v")] Verify { /// The original message. message: String, @@ -122,28 +122,30 @@ pub enum WalletSubcommands { signature: Signature, /// The address of the message signer. - #[clap(long, short)] + #[arg(long, short)] address: Address, }, + /// Import a private key into an encrypted keystore. - #[clap(visible_alias = "i")] + #[command(visible_alias = "i")] Import { /// The name for the account in the keystore. - #[clap(value_name = "ACCOUNT_NAME")] + #[arg(value_name = "ACCOUNT_NAME")] account_name: String, /// If provided, keystore will be saved here instead of the default keystores directory /// (~/.foundry/keystores) - #[clap(long, short)] + #[arg(long, short)] keystore_dir: Option, - #[clap(flatten)] + #[command(flatten)] raw_wallet_options: RawWalletOpts, }, + /// List all the accounts in the keystore default directory - #[clap(visible_alias = "ls")] + #[command(visible_alias = "ls")] List(ListArgs), /// Derives private key from mnemonic - #[clap(name = "derive-private-key", visible_aliases = &["--derive-private-key"])] + #[command(name = "derive-private-key", visible_aliases = &["--derive-private-key"])] DerivePrivateKey { mnemonic: String, mnemonic_index: Option }, } diff --git a/crates/cast/bin/cmd/wallet/vanity.rs b/crates/cast/bin/cmd/wallet/vanity.rs index a466bbe31..c3485ddfa 100644 --- a/crates/cast/bin/cmd/wallet/vanity.rs +++ b/crates/cast/bin/cmd/wallet/vanity.rs @@ -18,7 +18,7 @@ pub type GeneratedWallet = (SigningKey, Address); #[derive(Clone, Debug, Parser)] pub struct VanityArgs { /// Prefix for the vanity address. - #[clap( + #[arg( long, required_unless_present = "ends_with", value_parser = HexAddressValidator, @@ -27,20 +27,20 @@ pub struct VanityArgs { pub starts_with: Option, /// Suffix for the vanity address. - #[clap(long, value_parser = HexAddressValidator, value_name = "HEX")] + #[arg(long, value_parser = HexAddressValidator, value_name = "HEX")] pub ends_with: Option, // 2^64-1 is max possible nonce per [eip-2681](https://eips.ethereum.org/EIPS/eip-2681). /// Generate a vanity contract address created by the generated keypair with the specified /// nonce. - #[clap(long)] + #[arg(long)] pub nonce: Option, /// Path to save the generated vanity contract address to. /// /// If provided, the generated vanity addresses will appended to a JSON array in the specified /// file. - #[clap( + #[arg( long, value_hint = clap::ValueHint::FilePath, value_name = "PATH", diff --git a/crates/cast/bin/opts.rs b/crates/cast/bin/opts.rs index 9cce33494..bfb24b01b 100644 --- a/crates/cast/bin/opts.rs +++ b/crates/cast/bin/opts.rs @@ -21,53 +21,53 @@ const VERSION_MESSAGE: &str = concat!( /// Perform Ethereum RPC calls from the comfort of your command line. #[derive(Parser)] -#[clap( +#[command( name = "cast", version = VERSION_MESSAGE, after_help = "Find more information in the book: http://book.getfoundry.sh/reference/cast/cast.html", next_display_order = None, )] pub struct Cast { - #[clap(subcommand)] + #[command(subcommand)] pub cmd: CastSubcommand, } #[derive(Subcommand)] pub enum CastSubcommand { /// Prints the maximum value of the given integer type. - #[clap(visible_aliases = &["--max-int", "maxi"])] + #[command(visible_aliases = &["--max-int", "maxi"])] MaxInt { /// The integer type to get the maximum value of. - #[clap(default_value = "int256")] + #[arg(default_value = "int256")] r#type: String, }, /// Prints the minimum value of the given integer type. - #[clap(visible_aliases = &["--min-int", "mini"])] + #[command(visible_aliases = &["--min-int", "mini"])] MinInt { /// The integer type to get the minimum value of. - #[clap(default_value = "int256")] + #[arg(default_value = "int256")] r#type: String, }, /// Prints the maximum value of the given integer type. - #[clap(visible_aliases = &["--max-uint", "maxu"])] + #[command(visible_aliases = &["--max-uint", "maxu"])] MaxUint { /// The unsigned integer type to get the maximum value of. - #[clap(default_value = "uint256")] + #[arg(default_value = "uint256")] r#type: String, }, /// Prints the zero address. - #[clap(visible_aliases = &["--address-zero", "az"])] + #[command(visible_aliases = &["--address-zero", "az"])] AddressZero, /// Prints the zero hash. - #[clap(visible_aliases = &["--hash-zero", "hz"])] + #[command(visible_aliases = &["--hash-zero", "hz"])] HashZero, /// Convert UTF8 text to hex. - #[clap( + #[command( visible_aliases = &[ "--from-ascii", "--from-utf8", @@ -81,14 +81,14 @@ pub enum CastSubcommand { }, /// Concatenate hex strings. - #[clap(visible_aliases = &["--concat-hex", "ch"])] + #[command(visible_aliases = &["--concat-hex", "ch"])] ConcatHex { /// The data to concatenate. data: Vec, }, /// Convert binary data into hex data. - #[clap(visible_aliases = &["--from-bin", "from-binx", "fb"])] + #[command(visible_aliases = &["--from-bin", "from-binx", "fb"])] FromBin, /// Normalize the input to lowercase, 0x-prefixed hex. @@ -98,14 +98,14 @@ pub enum CastSubcommand { /// - 0x prefixed hex, concatenated with a ':' /// - an absolute path to file /// - @tag, where the tag is defined in an environment variable - #[clap(visible_aliases = &["--to-hexdata", "thd", "2hd"])] + #[command(visible_aliases = &["--to-hexdata", "thd", "2hd"])] ToHexdata { /// The input to normalize. input: Option, }, /// Convert an address to a checksummed format (EIP-55). - #[clap( + #[command( visible_aliases = &["--to-checksum-address", "--to-checksum", "to-checksum", @@ -118,57 +118,57 @@ pub enum CastSubcommand { }, /// Convert hex data to an ASCII string. - #[clap(visible_aliases = &["--to-ascii", "tas", "2as"])] + #[command(visible_aliases = &["--to-ascii", "tas", "2as"])] ToAscii { /// The hex data to convert. hexdata: Option, }, /// Convert a fixed point number into an integer. - #[clap(visible_aliases = &["--from-fix", "ff"])] + #[command(visible_aliases = &["--from-fix", "ff"])] FromFixedPoint { /// The number of decimals to use. decimals: Option, /// The value to convert. - #[clap(allow_hyphen_values = true)] + #[arg(allow_hyphen_values = true)] value: Option, }, /// Right-pads hex data to 32 bytes. - #[clap(visible_aliases = &["--to-bytes32", "tb", "2b"])] + #[command(visible_aliases = &["--to-bytes32", "tb", "2b"])] ToBytes32 { /// The hex data to convert. bytes: Option, }, /// Convert an integer into a fixed point number. - #[clap(visible_aliases = &["--to-fix", "tf", "2f"])] + #[command(visible_aliases = &["--to-fix", "tf", "2f"])] ToFixedPoint { /// The number of decimals to use. decimals: Option, /// The value to convert. - #[clap(allow_hyphen_values = true)] + #[arg(allow_hyphen_values = true)] value: Option, }, /// Convert a number to a hex-encoded uint256. - #[clap(name = "to-uint256", visible_aliases = &["--to-uint256", "tu", "2u"])] + #[command(name = "to-uint256", visible_aliases = &["--to-uint256", "tu", "2u"])] ToUint256 { /// The value to convert. value: Option, }, /// Convert a number to a hex-encoded int256. - #[clap(name = "to-int256", visible_aliases = &["--to-int256", "ti", "2i"])] + #[command(name = "to-int256", visible_aliases = &["--to-int256", "ti", "2i"])] ToInt256 { /// The value to convert. value: Option, }, /// Perform a left shifting operation - #[clap(name = "shl")] + #[command(name = "shl")] LeftShift { /// The value to shift. value: String, @@ -177,16 +177,16 @@ pub enum CastSubcommand { bits: String, /// The input base. - #[clap(long)] + #[arg(long)] base_in: Option, /// The output base. - #[clap(long, default_value = "16")] + #[arg(long, default_value = "16")] base_out: String, }, /// Perform a right shifting operation - #[clap(name = "shr")] + #[command(name = "shr")] RightShift { /// The value to shift. value: String, @@ -195,11 +195,11 @@ pub enum CastSubcommand { bits: String, /// The input base, - #[clap(long)] + #[arg(long)] base_in: Option, /// The output base, - #[clap(long, default_value = "16")] + #[arg(long, default_value = "16")] base_out: String, }, @@ -211,46 +211,46 @@ pub enum CastSubcommand { /// - 1ether /// - 1 gwei /// - 1gwei ether - #[clap(visible_aliases = &["--to-unit", "tun", "2un"])] + #[command(visible_aliases = &["--to-unit", "tun", "2un"])] ToUnit { /// The value to convert. value: Option, /// The unit to convert to (ether, gwei, wei). - #[clap(default_value = "wei")] + #[arg(default_value = "wei")] unit: String, }, /// Convert an ETH amount to wei. /// /// Consider using --to-unit. - #[clap(visible_aliases = &["--to-wei", "tw", "2w"])] + #[command(visible_aliases = &["--to-wei", "tw", "2w"])] ToWei { /// The value to convert. - #[clap(allow_hyphen_values = true)] + #[arg(allow_hyphen_values = true)] value: Option, /// The unit to convert from (ether, gwei, wei). - #[clap(default_value = "eth")] + #[arg(default_value = "eth")] unit: String, }, /// Convert wei into an ETH amount. /// /// Consider using --to-unit. - #[clap(visible_aliases = &["--from-wei", "fw"])] + #[command(visible_aliases = &["--from-wei", "fw"])] FromWei { /// The value to convert. - #[clap(allow_hyphen_values = true)] + #[arg(allow_hyphen_values = true)] value: Option, /// The unit to convert from (ether, gwei, wei). - #[clap(default_value = "eth")] + #[arg(default_value = "eth")] unit: String, }, - /// RLP encodes hex data, or an array of hex data - #[clap(visible_aliases = &["--to-rlp"])] + /// RLP encodes hex data, or an array of hex data. + #[command(visible_aliases = &["--to-rlp"])] ToRlp { /// The value to convert. value: Option, @@ -259,22 +259,22 @@ pub enum CastSubcommand { /// Decodes RLP encoded data. /// /// Input must be hexadecimal. - #[clap(visible_aliases = &["--from-rlp"])] + #[command(visible_aliases = &["--from-rlp"])] FromRlp { /// The value to convert. value: Option, }, /// Converts a number of one base to another - #[clap(visible_aliases = &["--to-hex", "th", "2h"])] + #[command(visible_aliases = &["--to-hex", "th", "2h"])] ToHex(ToBaseArgs), /// Converts a number of one base to decimal - #[clap(visible_aliases = &["--to-dec", "td", "2d"])] + #[command(visible_aliases = &["--to-dec", "td", "2d"])] ToDec(ToBaseArgs), /// Converts a number of one base to another - #[clap( + #[command( visible_aliases = &["--to-base", "--to-radix", "to-radix", @@ -282,21 +282,21 @@ pub enum CastSubcommand { "2r"] )] ToBase { - #[clap(flatten)] + #[command(flatten)] base: ToBaseArgs, /// The output base. - #[clap(value_name = "BASE")] + #[arg(value_name = "BASE")] base_out: Option, }, /// Create an access list for a transaction. - #[clap(visible_aliases = &["ac", "acl"])] + #[command(visible_aliases = &["ac", "acl"])] AccessList(AccessListArgs), /// Get logs by signature or topic. - #[clap(visible_alias = "l")] + #[command(visible_alias = "l")] Logs(LogsArgs), /// Get information about a block. - #[clap(visible_alias = "bl")] + #[command(visible_alias = "bl")] Block { /// The block height to query at. /// @@ -304,89 +304,89 @@ pub enum CastSubcommand { block: Option, /// If specified, only get the given field of the block. - #[clap(long, short)] + #[arg(long, short)] field: Option, - #[clap(long, env = "CAST_FULL_BLOCK")] + #[arg(long, env = "CAST_FULL_BLOCK")] full: bool, /// Print the block as JSON. - #[clap(long, short, help_heading = "Display options")] + #[arg(long, short, help_heading = "Display options")] json: bool, - #[clap(flatten)] + #[command(flatten)] rpc: RpcOpts, }, /// Get the latest block number. - #[clap(visible_alias = "bn")] + #[command(visible_alias = "bn")] BlockNumber { - #[clap(flatten)] + #[command(flatten)] rpc: RpcOpts, }, /// Perform a call on an account without publishing a transaction. - #[clap(visible_alias = "c")] + #[command(visible_alias = "c")] Call(CallArgs), /// ABI-encode a function with arguments. - #[clap(name = "calldata", visible_alias = "cd")] + #[command(name = "calldata", visible_alias = "cd")] CalldataEncode { /// The function signature in the format `()()` sig: String, /// The arguments to encode. - #[clap(allow_hyphen_values = true)] + #[arg(allow_hyphen_values = true)] args: Vec, }, /// Get the symbolic name of the current chain. Chain { - #[clap(flatten)] + #[command(flatten)] rpc: RpcOpts, }, /// Get the Ethereum chain ID. - #[clap(visible_aliases = &["ci", "cid"])] + #[command(visible_aliases = &["ci", "cid"])] ChainId { - #[clap(flatten)] + #[command(flatten)] rpc: RpcOpts, }, /// Get the current client version. - #[clap(visible_alias = "cl")] + #[command(visible_alias = "cl")] Client { - #[clap(flatten)] + #[command(flatten)] rpc: RpcOpts, }, /// Compute the contract address from a given nonce and deployer address. - #[clap(visible_alias = "ca")] + #[command(visible_alias = "ca")] ComputeAddress { /// The deployer address. address: Option, /// The nonce of the deployer address. - #[clap(long)] + #[arg(long)] nonce: Option, - #[clap(flatten)] + #[command(flatten)] rpc: RpcOpts, }, /// Disassembles hex encoded bytecode into individual / human readable opcodes - #[clap(visible_alias = "da")] + #[command(visible_alias = "da")] Disassemble { /// The hex encoded bytecode. bytecode: String, }, /// Calculate the ENS namehash of a name. - #[clap(visible_aliases = &["na", "nh"])] + #[command(visible_aliases = &["na", "nh"])] Namehash { name: Option }, /// Get information about a transaction. - #[clap(visible_alias = "t")] + #[command(visible_alias = "t")] Tx { /// The transaction hash. tx_hash: String, @@ -396,19 +396,19 @@ pub enum CastSubcommand { field: Option, /// Print the raw RLP encoded transaction. - #[clap(long, conflicts_with = "field")] + #[arg(long, conflicts_with = "field")] raw: bool, /// Print as JSON. - #[clap(long, short, help_heading = "Display options")] + #[arg(long, short, help_heading = "Display options")] json: bool, - #[clap(flatten)] + #[command(flatten)] rpc: RpcOpts, }, /// Get the transaction receipt for a transaction. - #[clap(visible_alias = "re")] + #[command(visible_alias = "re")] Receipt { /// The transaction hash. tx_hash: String, @@ -417,48 +417,48 @@ pub enum CastSubcommand { field: Option, /// The number of confirmations until the receipt is fetched - #[clap(long, default_value = "1")] + #[arg(long, default_value = "1")] confirmations: usize, /// Exit immediately if the transaction was not found. - #[clap(long = "async", env = "CAST_ASYNC", name = "async", alias = "cast-async")] + #[arg(id = "async", long = "async", env = "CAST_ASYNC", alias = "cast-async")] cast_async: bool, /// Print as JSON. - #[clap(long, short, help_heading = "Display options")] + #[arg(long, short, help_heading = "Display options")] json: bool, - #[clap(flatten)] + #[command(flatten)] rpc: RpcOpts, }, /// Sign and publish a transaction. - #[clap(name = "send", visible_alias = "s")] + #[command(name = "send", visible_alias = "s")] SendTx(SendTxArgs), /// Publish a raw transaction to the network. - #[clap(name = "publish", visible_alias = "p")] + #[command(name = "publish", visible_alias = "p")] PublishTx { /// The raw transaction raw_tx: String, /// Only print the transaction hash and exit immediately. - #[clap(long = "async", env = "CAST_ASYNC", name = "async", alias = "cast-async")] + #[arg(id = "async", long = "async", env = "CAST_ASYNC", alias = "cast-async")] cast_async: bool, - #[clap(flatten)] + #[command(flatten)] rpc: RpcOpts, }, /// Estimate the gas cost of a transaction. - #[clap(visible_alias = "e")] + #[command(visible_alias = "e")] Estimate(EstimateArgs), /// Decode ABI-encoded input data. /// /// Similar to `abi-decode --input`, but function selector MUST be prefixed in `calldata` /// string - #[clap(visible_aliases = &["--calldata-decode","cdd"])] + #[command(visible_aliases = &["--calldata-decode","cdd"])] CalldataDecode { /// The function signature in the format `()()`. sig: String, @@ -472,7 +472,7 @@ pub enum CastSubcommand { /// Defaults to decoding output data. To decode input data pass --input. /// /// When passing `--input`, function selector must NOT be prefixed in `calldata` string - #[clap(name = "abi-decode", visible_aliases = &["ad", "--abi-decode"])] + #[command(name = "abi-decode", visible_aliases = &["ad", "--abi-decode"])] AbiDecode { /// The function signature in the format `()()`. sig: String, @@ -481,27 +481,27 @@ pub enum CastSubcommand { calldata: String, /// Whether to decode the input or output data. - #[clap(long, short, help_heading = "Decode input data instead of output data")] + #[arg(long, short, help_heading = "Decode input data instead of output data")] input: bool, }, /// ABI encode the given function argument, excluding the selector. - #[clap(visible_alias = "ae")] + #[command(visible_alias = "ae")] AbiEncode { /// The function signature. sig: String, /// Whether to use packed encoding. - #[clap(long)] + #[arg(long)] packed: bool, /// The arguments of the function. - #[clap(allow_hyphen_values = true)] + #[arg(allow_hyphen_values = true)] args: Vec, }, /// Compute the storage slot for an entry in a mapping. - #[clap(visible_alias = "in")] + #[command(visible_alias = "in")] Index { /// The mapping key type. key_type: String, @@ -514,58 +514,58 @@ pub enum CastSubcommand { }, /// Fetch the EIP-1967 implementation account - #[clap(visible_alias = "impl")] + #[command(visible_alias = "impl")] Implementation { /// The block height to query at. /// /// Can also be the tags earliest, finalized, safe, latest, or pending. - #[clap(long, short = 'B')] + #[arg(long, short = 'B')] block: Option, /// The address to get the nonce for. - #[clap(value_parser = NameOrAddress::from_str)] + #[arg(value_parser = NameOrAddress::from_str)] who: NameOrAddress, - #[clap(flatten)] + #[command(flatten)] rpc: RpcOpts, }, /// Fetch the EIP-1967 admin account - #[clap(visible_alias = "adm")] + #[command(visible_alias = "adm")] Admin { /// The block height to query at. /// /// Can also be the tags earliest, finalized, safe, latest, or pending. - #[clap(long, short = 'B')] + #[arg(long, short = 'B')] block: Option, /// The address to get the nonce for. - #[clap(value_parser = NameOrAddress::from_str)] + #[arg(value_parser = NameOrAddress::from_str)] who: NameOrAddress, - #[clap(flatten)] + #[command(flatten)] rpc: RpcOpts, }, /// Get the function signatures for the given selector from https://openchain.xyz. - #[clap(name = "4byte", visible_aliases = &["4", "4b"])] + #[command(name = "4byte", visible_aliases = &["4", "4b"])] FourByte { /// The function selector. selector: Option, }, /// Decode ABI-encoded calldata using https://openchain.xyz. - #[clap(name = "4byte-decode", visible_aliases = &["4d", "4bd"])] + #[command(name = "4byte-decode", visible_aliases = &["4d", "4bd"])] FourByteDecode { /// The ABI-encoded calldata. calldata: Option, }, /// Get the event signature for a given topic 0 from https://openchain.xyz. - #[clap(name = "4byte-event", visible_aliases = &["4e", "4be", "topic0-event", "t0e"])] + #[command(name = "4byte-event", visible_aliases = &["4e", "4be", "topic0-event", "t0e"])] FourByteEvent { /// Topic 0 - #[clap(value_name = "TOPIC_0")] + #[arg(value_name = "TOPIC_0")] topic: Option, }, @@ -576,7 +576,7 @@ pub enum CastSubcommand { /// - "function transfer(address,uint256)" /// - "function transfer(address,uint256)" "event Transfer(address,address,uint256)" /// - "./out/Contract.sol/Contract.json" - #[clap(visible_aliases = &["ups"])] + #[command(visible_aliases = &["ups"])] UploadSignature { /// The signatures to upload. /// @@ -588,228 +588,228 @@ pub enum CastSubcommand { /// Pretty print calldata. /// /// Tries to decode the calldata using https://openchain.xyz unless --offline is passed. - #[clap(visible_alias = "pc")] + #[command(visible_alias = "pc")] PrettyCalldata { /// The calldata. calldata: Option, /// Skip the https://openchain.xyz lookup. - #[clap(long, short)] + #[arg(long, short)] offline: bool, }, /// Get the timestamp of a block. - #[clap(visible_alias = "a")] + #[command(visible_alias = "a")] Age { /// The block height to query at. /// /// Can also be the tags earliest, finalized, safe, latest, or pending. block: Option, - #[clap(flatten)] + #[command(flatten)] rpc: RpcOpts, }, /// Get the balance of an account in wei. - #[clap(visible_alias = "b")] + #[command(visible_alias = "b")] Balance { /// The block height to query at. /// /// Can also be the tags earliest, finalized, safe, latest, or pending. - #[clap(long, short = 'B')] + #[arg(long, short = 'B')] block: Option, /// The account to query. - #[clap(value_parser = NameOrAddress::from_str)] + #[arg(value_parser = NameOrAddress::from_str)] who: NameOrAddress, /// Format the balance in ether. - #[clap(long, short)] + #[arg(long, short)] ether: bool, - #[clap(flatten)] + #[command(flatten)] rpc: RpcOpts, /// erc20 address to query, with the method `balanceOf(address) return (uint256)`, alias /// with '--erc721' - #[clap(long, alias = "erc721")] + #[arg(long, alias = "erc721")] erc20: Option
, }, /// Get the basefee of a block. - #[clap(visible_aliases = &["ba", "fee", "basefee"])] + #[command(visible_aliases = &["ba", "fee", "basefee"])] BaseFee { /// The block height to query at. /// /// Can also be the tags earliest, finalized, safe, latest, or pending. block: Option, - #[clap(flatten)] + #[command(flatten)] rpc: RpcOpts, }, /// Get the runtime bytecode of a contract. - #[clap(visible_alias = "co")] + #[command(visible_alias = "co")] Code { /// The block height to query at. /// /// Can also be the tags earliest, finalized, safe, latest, or pending. - #[clap(long, short = 'B')] + #[arg(long, short = 'B')] block: Option, /// The contract address. - #[clap(value_parser = NameOrAddress::from_str)] + #[arg(value_parser = NameOrAddress::from_str)] who: NameOrAddress, /// Disassemble bytecodes into individual opcodes. - #[clap(long, short)] + #[arg(long, short)] disassemble: bool, - #[clap(flatten)] + #[command(flatten)] rpc: RpcOpts, }, /// Get the runtime bytecode size of a contract. - #[clap(visible_alias = "cs")] + #[command(visible_alias = "cs")] Codesize { /// The block height to query at. /// /// Can also be the tags earliest, finalized, safe, latest, or pending. - #[clap(long, short = 'B')] + #[arg(long, short = 'B')] block: Option, /// The contract address. - #[clap(value_parser = NameOrAddress::from_str)] + #[arg(value_parser = NameOrAddress::from_str)] who: NameOrAddress, - #[clap(flatten)] + #[command(flatten)] rpc: RpcOpts, }, /// Get the current gas price. - #[clap(visible_alias = "g")] + #[command(visible_alias = "g")] GasPrice { - #[clap(flatten)] + #[command(flatten)] rpc: RpcOpts, }, /// Generate event signatures from event string. - #[clap(visible_alias = "se")] + #[command(visible_alias = "se")] SigEvent { /// The event string. event_string: Option, }, /// Hash arbitrary data using Keccak-256. - #[clap(visible_alias = "k")] + #[command(visible_alias = "k")] Keccak { /// The data to hash. data: Option, }, /// Perform an ENS lookup. - #[clap(visible_alias = "rn")] + #[command(visible_alias = "rn")] ResolveName { /// The name to lookup. who: Option, /// Perform a reverse lookup to verify that the name is correct. - #[clap(long, short)] + #[arg(long, short)] verify: bool, - #[clap(flatten)] + #[command(flatten)] rpc: RpcOpts, }, /// Perform an ENS reverse lookup. - #[clap(visible_alias = "la")] + #[command(visible_alias = "la")] LookupAddress { /// The account to perform the lookup for. who: Option
, /// Perform a normal lookup to verify that the address is correct. - #[clap(long, short)] + #[arg(long, short)] verify: bool, - #[clap(flatten)] + #[command(flatten)] rpc: RpcOpts, }, /// Get the raw value of a contract's storage slot. - #[clap(visible_alias = "st")] + #[command(visible_alias = "st")] Storage(StorageArgs), /// Generate a storage proof for a given storage slot. - #[clap(visible_alias = "pr")] + #[command(visible_alias = "pr")] Proof { /// The contract address. - #[clap(value_parser = NameOrAddress::from_str)] + #[arg(value_parser = NameOrAddress::from_str)] address: NameOrAddress, /// The storage slot numbers (hex or decimal). - #[clap(value_parser = parse_slot)] + #[arg(value_parser = parse_slot)] slots: Vec, /// The block height to query at. /// /// Can also be the tags earliest, finalized, safe, latest, or pending. - #[clap(long, short = 'B')] + #[arg(long, short = 'B')] block: Option, - #[clap(flatten)] + #[command(flatten)] rpc: RpcOpts, }, /// Get the nonce for an account. - #[clap(visible_alias = "n")] + #[command(visible_alias = "n")] Nonce { /// The block height to query at. /// /// Can also be the tags earliest, finalized, safe, latest, or pending. - #[clap(long, short = 'B')] + #[arg(long, short = 'B')] block: Option, /// The address to get the nonce for. - #[clap(value_parser = NameOrAddress::from_str)] + #[arg(value_parser = NameOrAddress::from_str)] who: NameOrAddress, - #[clap(flatten)] + #[command(flatten)] rpc: RpcOpts, }, /// Get the source code of a contract from Etherscan. - #[clap(visible_aliases = &["et", "src"])] + #[command(visible_aliases = &["et", "src"])] EtherscanSource { /// The contract's address. address: String, /// The output directory to expand source tree into. - #[clap(short, value_hint = ValueHint::DirPath)] + #[arg(short, value_hint = ValueHint::DirPath)] directory: Option, - #[clap(flatten)] + #[command(flatten)] etherscan: EtherscanOpts, }, /// Wallet management utilities. - #[clap(visible_alias = "w")] + #[command(visible_alias = "w")] Wallet { - #[clap(subcommand)] + #[command(subcommand)] command: WalletSubcommands, }, /// Generate a Solidity interface from a given ABI. /// /// Currently does not support ABI encoder v2. - #[clap(visible_alias = "i")] + #[command(visible_alias = "i")] Interface(InterfaceArgs), /// Generate a rust binding from a given ABI. - #[clap(visible_alias = "bi")] + #[command(visible_alias = "bi")] Bind(BindArgs), /// Get the selector for a function. - #[clap(visible_alias = "si")] + #[command(visible_alias = "si")] Sig { /// The function signature, e.g. transfer(address,uint256). sig: Option, @@ -819,64 +819,64 @@ pub enum CastSubcommand { }, /// Generate a deterministic contract address using CREATE2. - #[clap(visible_alias = "c2")] + #[command(visible_alias = "c2")] Create2(Create2Args), /// Get the block number closest to the provided timestamp. - #[clap(visible_alias = "f")] + #[command(visible_alias = "f")] FindBlock(FindBlockArgs), /// Generate shell completions script. - #[clap(visible_alias = "com")] + #[command(visible_alias = "com")] Completions { - #[clap(value_enum)] + #[arg(value_enum)] shell: clap_complete::Shell, }, /// Generate Fig autocompletion spec. - #[clap(visible_alias = "fig")] + #[command(visible_alias = "fig")] GenerateFigSpec, /// Runs a published transaction in a local environment and prints the trace. - #[clap(visible_alias = "r")] + #[command(visible_alias = "r")] Run(RunArgs), /// Perform a raw JSON-RPC request. - #[clap(visible_alias = "rp")] + #[command(visible_alias = "rp")] Rpc(RpcArgs), /// Formats a string into bytes32 encoding. - #[clap(name = "format-bytes32-string", visible_aliases = &["--format-bytes32-string"])] + #[command(name = "format-bytes32-string", visible_aliases = &["--format-bytes32-string"])] FormatBytes32String { /// The string to format. string: Option, }, /// Parses a string from bytes32 encoding. - #[clap(name = "parse-bytes32-string", visible_aliases = &["--parse-bytes32-string"])] + #[command(name = "parse-bytes32-string", visible_aliases = &["--parse-bytes32-string"])] ParseBytes32String { /// The string to parse. bytes: Option, }, - #[clap(name = "parse-bytes32-address", visible_aliases = &["--parse-bytes32-address"])] - #[clap(about = "Parses a checksummed address from bytes32 encoding.")] + #[command(name = "parse-bytes32-address", visible_aliases = &["--parse-bytes32-address"])] + #[command(about = "Parses a checksummed address from bytes32 encoding.")] ParseBytes32Address { - #[clap(value_name = "BYTES")] + #[arg(value_name = "BYTES")] bytes: Option, }, /// Decodes a raw signed EIP 2718 typed transaction - #[clap(visible_alias = "dt")] + #[command(visible_alias = "dt")] DecodeTransaction { tx: Option }, /// Extracts function selectors and arguments from bytecode - #[clap(visible_alias = "sel")] + #[command(visible_alias = "sel")] Selectors { /// The hex encoded bytecode. bytecode: String, /// Resolve the function signatures for the extracted selectors using https://openchain.xyz - #[clap(long, short)] + #[arg(long, short)] resolve: bool, }, } @@ -885,11 +885,11 @@ pub enum CastSubcommand { #[derive(Debug, Parser)] pub struct ToBaseArgs { /// The value to convert. - #[clap(allow_hyphen_values = true)] + #[arg(allow_hyphen_values = true)] pub value: Option, /// The input base. - #[clap(long, short = 'i')] + #[arg(long, short = 'i')] pub base_in: Option, } diff --git a/crates/chisel/bin/main.rs b/crates/chisel/bin/main.rs index 9da885860..43b6a1b21 100644 --- a/crates/chisel/bin/main.rs +++ b/crates/chisel/bin/main.rs @@ -41,7 +41,7 @@ const VERSION_MESSAGE: &str = concat!( /// Fast, utilitarian, and verbose Solidity REPL. #[derive(Debug, Parser)] -#[clap(name = "chisel", version = VERSION_MESSAGE)] +#[command(name = "chisel", version = VERSION_MESSAGE)] pub struct Chisel { #[command(subcommand)] pub cmd: Option, @@ -50,21 +50,21 @@ pub struct Chisel { /// /// These files will be evaluated before the top-level of the /// REPL, therefore functioning as a prelude - #[clap(long, help_heading = "REPL options")] + #[arg(long, help_heading = "REPL options")] pub prelude: Option, /// Disable the default `Vm` import. - #[clap(long, help_heading = "REPL options", long_help = format!( + #[arg(long, help_heading = "REPL options", long_help = format!( "Disable the default `Vm` import.\n\n\ The import is disabled by default if the Solc version is less than {}.", chisel::session_source::MIN_VM_VERSION ))] pub no_vm: bool, - #[clap(flatten)] + #[command(flatten)] pub opts: CoreBuildArgs, - #[clap(flatten)] + #[command(flatten)] pub evm_opts: EvmArgs, } diff --git a/crates/cli/src/opts/build/core.rs b/crates/cli/src/opts/build/core.rs index dd01710bd..9ed8f98b3 100644 --- a/crates/cli/src/opts/build/core.rs +++ b/crates/cli/src/opts/build/core.rs @@ -19,59 +19,59 @@ use serde::Serialize; use std::path::PathBuf; #[derive(Clone, Debug, Default, Serialize, Parser)] -#[clap(next_help_heading = "Build options")] +#[command(next_help_heading = "Build options")] pub struct CoreBuildArgs { /// Clear the cache and artifacts folder and recompile. - #[clap(long, help_heading = "Cache options")] + #[arg(long, help_heading = "Cache options")] #[serde(skip)] pub force: bool, /// Disable the cache. - #[clap(long)] + #[arg(long)] #[serde(skip)] pub no_cache: bool, /// Set pre-linked libraries. - #[clap(long, help_heading = "Linker options", env = "DAPP_LIBRARIES")] + #[arg(long, help_heading = "Linker options", env = "DAPP_LIBRARIES")] #[serde(skip_serializing_if = "Vec::is_empty")] pub libraries: Vec, /// Ignore solc warnings by error code. - #[clap(long, help_heading = "Compiler options", value_name = "ERROR_CODES")] + #[arg(long, help_heading = "Compiler options", value_name = "ERROR_CODES")] #[serde(skip_serializing_if = "Vec::is_empty")] pub ignored_error_codes: Vec, /// Warnings will trigger a compiler error - #[clap(long, help_heading = "Compiler options")] + #[arg(long, help_heading = "Compiler options")] #[serde(skip)] pub deny_warnings: bool, /// Do not auto-detect the `solc` version. - #[clap(long, help_heading = "Compiler options")] + #[arg(long, help_heading = "Compiler options")] #[serde(skip)] pub no_auto_detect: bool, /// Specify the solc version, or a path to a local solc, to build with. /// /// Valid values are in the format `x.y.z`, `solc:x.y.z` or `path/to/solc`. - #[clap(long = "use", help_heading = "Compiler options", value_name = "SOLC_VERSION")] + #[arg(long = "use", help_heading = "Compiler options", value_name = "SOLC_VERSION")] #[serde(skip)] pub use_solc: Option, /// Do not access the network. /// /// Missing solc versions will not be installed. - #[clap(help_heading = "Compiler options", long)] + #[arg(help_heading = "Compiler options", long)] #[serde(skip)] pub offline: bool, /// Use the Yul intermediate representation compilation pipeline. - #[clap(long, help_heading = "Compiler options")] + #[arg(long, help_heading = "Compiler options")] #[serde(skip)] pub via_ir: bool, /// The path to the contract artifacts folder. - #[clap( + #[arg( long = "out", short, help_heading = "Project options", @@ -85,22 +85,22 @@ pub struct CoreBuildArgs { /// /// Possible values are "default", "strip" (remove), /// "debug" (Solidity-generated revert strings) and "verboseDebug" - #[clap(long, help_heading = "Project options", value_name = "REVERT")] + #[arg(long, help_heading = "Project options", value_name = "REVERT")] #[serde(skip)] pub revert_strings: Option, /// Don't print anything on startup. - #[clap(long, help_heading = "Compiler options")] + #[arg(long, help_heading = "Compiler options")] #[serde(skip)] pub silent: bool, /// Generate build info files. - #[clap(long, help_heading = "Project options")] + #[arg(long, help_heading = "Project options")] #[serde(skip)] pub build_info: bool, /// Output path to directory that build info files will be written to. - #[clap( + #[arg( long, help_heading = "Project options", value_hint = ValueHint::DirPath, @@ -110,11 +110,11 @@ pub struct CoreBuildArgs { #[serde(skip_serializing_if = "Option::is_none")] pub build_info_path: Option, - #[clap(flatten)] + #[command(flatten)] #[serde(flatten)] pub compiler: CompilerArgs, - #[clap(flatten)] + #[command(flatten)] #[serde(flatten)] pub project_paths: ProjectPathsArgs, } diff --git a/crates/cli/src/opts/build/mod.rs b/crates/cli/src/opts/build/mod.rs index 0b97ed2df..e752ae53f 100644 --- a/crates/cli/src/opts/build/mod.rs +++ b/crates/cli/src/opts/build/mod.rs @@ -13,25 +13,25 @@ pub use self::paths::ProjectPathsArgs; // // See also `BuildArgs`. #[derive(Clone, Debug, Default, Serialize, Parser)] -#[clap(next_help_heading = "Compiler options")] +#[command(next_help_heading = "Compiler options")] pub struct CompilerArgs { /// Includes the AST as JSON in the compiler output. - #[clap(long, help_heading = "Compiler options")] + #[arg(long, help_heading = "Compiler options")] #[serde(skip)] pub ast: bool, /// The target EVM version. - #[clap(long, value_name = "VERSION")] + #[arg(long, value_name = "VERSION")] #[serde(skip_serializing_if = "Option::is_none")] pub evm_version: Option, /// Activate the Solidity optimizer. - #[clap(long)] + #[arg(long)] #[serde(skip)] pub optimize: bool, /// The number of optimizer runs. - #[clap(long, value_name = "RUNS")] + #[arg(long, value_name = "RUNS")] #[serde(skip_serializing_if = "Option::is_none")] pub optimizer_runs: Option, @@ -40,14 +40,14 @@ pub struct CompilerArgs { /// Example keys: evm.assembly, ewasm, ir, irOptimized, metadata /// /// For a full description, see https://docs.soliditylang.org/en/v0.8.13/using-the-compiler.html#input-description - #[clap(long, num_args(1..), value_name = "SELECTOR")] + #[arg(long, num_args(1..), value_name = "SELECTOR")] #[serde(skip_serializing_if = "Vec::is_empty")] pub extra_output: Vec, /// Extra output to write to separate files. /// /// Valid values: metadata, ir, irOptimized, ewasm, evm.assembly - #[clap(long, num_args(1..), value_name = "SELECTOR")] + #[arg(long, num_args(1..), value_name = "SELECTOR")] #[serde(skip_serializing_if = "Vec::is_empty")] pub extra_output_files: Vec, } diff --git a/crates/cli/src/opts/build/paths.rs b/crates/cli/src/opts/build/paths.rs index 29f505ee3..692da4588 100644 --- a/crates/cli/src/opts/build/paths.rs +++ b/crates/cli/src/opts/build/paths.rs @@ -15,50 +15,50 @@ use std::path::PathBuf; /// Common arguments for a project's paths. #[derive(Clone, Debug, Default, Serialize, Parser)] -#[clap(next_help_heading = "Project options")] +#[command(next_help_heading = "Project options")] pub struct ProjectPathsArgs { /// The project's root path. /// /// By default root of the Git repository, if in one, /// or the current working directory. - #[clap(long, value_hint = ValueHint::DirPath, value_name = "PATH")] + #[arg(long, value_hint = ValueHint::DirPath, value_name = "PATH")] #[serde(skip)] pub root: Option, /// The contracts source directory. - #[clap(long, short = 'C', value_hint = ValueHint::DirPath, value_name = "PATH")] + #[arg(long, short = 'C', value_hint = ValueHint::DirPath, value_name = "PATH")] #[serde(rename = "src", skip_serializing_if = "Option::is_none")] pub contracts: Option, /// The project's remappings. - #[clap(long, short = 'R')] + #[arg(long, short = 'R')] #[serde(skip)] pub remappings: Vec, /// The project's remappings from the environment. - #[clap(long, value_name = "ENV")] + #[arg(long, value_name = "ENV")] #[serde(skip)] pub remappings_env: Option, /// The path to the compiler cache. - #[clap(long, value_hint = ValueHint::DirPath, value_name = "PATH")] + #[arg(long, value_hint = ValueHint::DirPath, value_name = "PATH")] #[serde(skip_serializing_if = "Option::is_none")] pub cache_path: Option, /// The path to the library folder. - #[clap(long, value_hint = ValueHint::DirPath, value_name = "PATH")] + #[arg(long, value_hint = ValueHint::DirPath, value_name = "PATH")] #[serde(rename = "libs", skip_serializing_if = "Vec::is_empty")] pub lib_paths: Vec, /// Use the Hardhat-style project layout. /// /// This is the same as using: `--contracts contracts --lib-paths node_modules`. - #[clap(long, conflicts_with = "contracts", visible_alias = "hh")] + #[arg(long, conflicts_with = "contracts", visible_alias = "hh")] #[serde(skip)] pub hardhat: bool, /// Path to the config file. - #[clap(long, value_hint = ValueHint::FilePath, value_name = "FILE")] + #[arg(long, value_hint = ValueHint::FilePath, value_name = "FILE")] #[serde(skip)] pub config_path: Option, } diff --git a/crates/cli/src/opts/ethereum.rs b/crates/cli/src/opts/ethereum.rs index 62cebeaec..272d3a5a2 100644 --- a/crates/cli/src/opts/ethereum.rs +++ b/crates/cli/src/opts/ethereum.rs @@ -18,13 +18,13 @@ const FLASHBOTS_URL: &str = "https://rpc.flashbots.net/fast"; #[derive(Clone, Debug, Default, Parser)] pub struct RpcOpts { /// The RPC endpoint. - #[clap(short = 'r', long = "rpc-url", env = "ETH_RPC_URL")] + #[arg(short = 'r', long = "rpc-url", env = "ETH_RPC_URL")] pub url: Option, /// Use the Flashbots RPC URL with fast mode (https://rpc.flashbots.net/fast). /// This shares the transaction privately with all registered builders. /// https://docs.flashbots.net/flashbots-protect/quick-start#faster-transactions - #[clap(long)] + #[arg(long)] pub flashbots: bool, /// JWT Secret for the RPC endpoint. @@ -36,7 +36,7 @@ pub struct RpcOpts { /// '["0x6bb38c26db65749ab6e472080a3d20a2f35776494e72016d1e339593f21c59bc", /// "0x6bb38c26db65749ab6e472080a3d20a2f35776494e72016d1e339593f21c59bc", /// "0x6bb38c26db65749ab6e472080a3d20a2f35776494e72016d1e339593f21c59bc"]' - #[clap(long, env = "ETH_RPC_JWT_SECRET")] + #[arg(long, env = "ETH_RPC_JWT_SECRET")] pub jwt_secret: Option, } @@ -89,12 +89,12 @@ impl RpcOpts { #[derive(Clone, Debug, Default, Serialize, Parser)] pub struct EtherscanOpts { /// The Etherscan (or equivalent) API key. - #[clap(short = 'e', long = "etherscan-api-key", alias = "api-key", env = "ETHERSCAN_API_KEY")] + #[arg(short = 'e', long = "etherscan-api-key", alias = "api-key", env = "ETHERSCAN_API_KEY")] #[serde(rename = "etherscan_api_key", skip_serializing_if = "Option::is_none")] pub key: Option, /// The chain name or EIP-155 chain ID. - #[clap( + #[arg( short, long, alias = "chain-id", @@ -141,15 +141,15 @@ impl EtherscanOpts { } #[derive(Clone, Debug, Default, Parser)] -#[clap(next_help_heading = "Ethereum options")] +#[command(next_help_heading = "Ethereum options")] pub struct EthereumOpts { - #[clap(flatten)] + #[command(flatten)] pub rpc: RpcOpts, - #[clap(flatten)] + #[command(flatten)] pub etherscan: EtherscanOpts, - #[clap(flatten)] + #[command(flatten)] pub wallet: WalletOpts, } diff --git a/crates/cli/src/opts/transaction.rs b/crates/cli/src/opts/transaction.rs index bda2fb021..84173eaaf 100644 --- a/crates/cli/src/opts/transaction.rs +++ b/crates/cli/src/opts/transaction.rs @@ -4,14 +4,14 @@ use clap::Parser; use serde::Serialize; #[derive(Clone, Debug, Serialize, Parser)] -#[clap(next_help_heading = "Transaction options")] +#[command(next_help_heading = "Transaction options")] pub struct TransactionOpts { /// Gas limit for the transaction. - #[clap(long, env = "ETH_GAS_LIMIT")] + #[arg(long, env = "ETH_GAS_LIMIT")] pub gas_limit: Option, /// Gas price for legacy transactions, or max fee per gas for EIP1559 transactions. - #[clap( + #[arg( long, env = "ETH_GAS_PRICE", value_parser = parse_ether_value, @@ -20,7 +20,7 @@ pub struct TransactionOpts { pub gas_price: Option, /// Max priority fee per gas for EIP1559 transactions. - #[clap( + #[arg( long, env = "ETH_PRIORITY_GAS_PRICE", value_parser = parse_ether_value, @@ -33,17 +33,17 @@ pub struct TransactionOpts { /// /// /// Examples: 1ether, 10gwei, 0.01ether - #[clap(long, value_parser = parse_ether_value)] + #[arg(long, value_parser = parse_ether_value)] pub value: Option, /// Nonce for the transaction. - #[clap(long)] + #[arg(long)] pub nonce: Option, /// Send a legacy transaction instead of an EIP1559 transaction. /// /// This is automatically enabled for common networks without EIP1559. - #[clap(long)] + #[arg(long)] pub legacy: bool, } diff --git a/crates/common/src/evm.rs b/crates/common/src/evm.rs index 2230bd760..e23c1da33 100644 --- a/crates/common/src/evm.rs +++ b/crates/common/src/evm.rs @@ -39,33 +39,33 @@ pub type Breakpoints = HashMap; /// # } /// ``` #[derive(Clone, Debug, Default, Serialize, Parser)] -#[clap(next_help_heading = "EVM options", about = None, long_about = None)] // override doc +#[command(next_help_heading = "EVM options", about = None, long_about = None)] // override doc pub struct EvmArgs { /// Fetch state over a remote endpoint instead of starting from an empty state. /// /// If you want to fetch state from a specific block number, see --fork-block-number. - #[clap(long, short, visible_alias = "rpc-url", value_name = "URL")] + #[arg(long, short, visible_alias = "rpc-url", value_name = "URL")] #[serde(rename = "eth_rpc_url", skip_serializing_if = "Option::is_none")] pub fork_url: Option, /// Fetch state from a specific block number over a remote endpoint. /// /// See --fork-url. - #[clap(long, requires = "fork_url", value_name = "BLOCK")] + #[arg(long, requires = "fork_url", value_name = "BLOCK")] #[serde(skip_serializing_if = "Option::is_none")] pub fork_block_number: Option, /// Number of retries. /// /// See --fork-url. - #[clap(long, requires = "fork_url", value_name = "RETRIES")] + #[arg(long, requires = "fork_url", value_name = "RETRIES")] #[serde(skip_serializing_if = "Option::is_none")] pub fork_retries: Option, /// Initial retry backoff on encountering errors. /// /// See --fork-url. - #[clap(long, requires = "fork_url", value_name = "BACKOFF")] + #[arg(long, requires = "fork_url", value_name = "BACKOFF")] #[serde(skip_serializing_if = "Option::is_none")] pub fork_retry_backoff: Option, @@ -76,27 +76,27 @@ pub struct EvmArgs { /// This flag overrides the project's configuration file. /// /// See --fork-url. - #[clap(long)] + #[arg(long)] #[serde(skip)] pub no_storage_caching: bool, /// The initial balance of deployed test contracts. - #[clap(long, value_name = "BALANCE")] + #[arg(long, value_name = "BALANCE")] #[serde(skip_serializing_if = "Option::is_none")] pub initial_balance: Option, /// The address which will be executing tests. - #[clap(long, value_name = "ADDRESS")] + #[arg(long, value_name = "ADDRESS")] #[serde(skip_serializing_if = "Option::is_none")] pub sender: Option
, /// Enable the FFI cheatcode. - #[clap(long)] + #[arg(long)] #[serde(skip)] pub ffi: bool, /// Use the create 2 factory in all cases including tests and non-broadcasting scripts. - #[clap(long)] + #[arg(long)] #[serde(skip)] pub always_use_create_2_factory: bool, @@ -109,7 +109,7 @@ pub struct EvmArgs { /// - 3: Print execution traces for failing tests /// - 4: Print execution traces for all tests, and setup traces for failing tests /// - 5: Print execution and setup traces for all tests - #[clap(long, short, verbatim_doc_comment, action = ArgAction::Count)] + #[arg(long, short, verbatim_doc_comment, action = ArgAction::Count)] #[serde(skip)] pub verbosity: u8, @@ -118,7 +118,7 @@ pub struct EvmArgs { /// default value: 330 /// /// See also --fork-url and https://docs.alchemy.com/reference/compute-units#what-are-cups-compute-units-per-second - #[clap( + #[arg( long, requires = "fork_url", alias = "cups", @@ -130,7 +130,7 @@ pub struct EvmArgs { /// Disables rate limiting for this node's provider. /// /// See also --fork-url and https://docs.alchemy.com/reference/compute-units#what-are-cups-compute-units-per-second - #[clap( + #[arg( long, requires = "fork_url", value_name = "NO_RATE_LIMITS", @@ -141,14 +141,14 @@ pub struct EvmArgs { pub no_rpc_rate_limit: bool, /// All ethereum environment related arguments - #[clap(flatten)] + #[command(flatten)] #[serde(flatten)] pub env: EnvArgs, /// Whether to enable isolation of calls. /// In isolation mode all top-level calls are executed as a separate transaction in a separate /// EVM context, enabling more precise gas accounting and transaction state changes. - #[clap(long)] + #[arg(long)] #[serde(skip)] pub isolate: bool, } @@ -202,66 +202,66 @@ impl Provider for EvmArgs { /// Configures the executor environment during tests. #[derive(Clone, Debug, Default, Serialize, Parser)] -#[clap(next_help_heading = "Executor environment config")] +#[command(next_help_heading = "Executor environment config")] pub struct EnvArgs { /// The block gas limit. - #[clap(long, value_name = "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). - #[clap(long, value_name = "CODE_SIZE")] + #[arg(long, value_name = "CODE_SIZE")] #[serde(skip_serializing_if = "Option::is_none")] pub code_size_limit: Option, /// The chain name or EIP-155 chain ID. - #[clap(long, visible_alias = "chain-id", value_name = "CHAIN")] + #[arg(long, visible_alias = "chain-id", value_name = "CHAIN")] #[serde(rename = "chain_id", skip_serializing_if = "Option::is_none", serialize_with = "id")] pub chain: Option, /// The gas price. - #[clap(long, value_name = "GAS_PRICE")] + #[arg(long, value_name = "GAS_PRICE")] #[serde(skip_serializing_if = "Option::is_none")] pub gas_price: Option, /// The base fee in a block. - #[clap(long, visible_alias = "base-fee", value_name = "FEE")] + #[arg(long, visible_alias = "base-fee", value_name = "FEE")] #[serde(skip_serializing_if = "Option::is_none")] pub block_base_fee_per_gas: Option, /// The transaction origin. - #[clap(long, value_name = "ADDRESS")] + #[arg(long, value_name = "ADDRESS")] #[serde(skip_serializing_if = "Option::is_none")] pub tx_origin: Option
, /// The coinbase of the block. - #[clap(long, value_name = "ADDRESS")] + #[arg(long, value_name = "ADDRESS")] #[serde(skip_serializing_if = "Option::is_none")] pub block_coinbase: Option
, /// The timestamp of the block. - #[clap(long, value_name = "TIMESTAMP")] + #[arg(long, value_name = "TIMESTAMP")] #[serde(skip_serializing_if = "Option::is_none")] pub block_timestamp: Option, /// The block number. - #[clap(long, value_name = "BLOCK")] + #[arg(long, value_name = "BLOCK")] #[serde(skip_serializing_if = "Option::is_none")] pub block_number: Option, /// The block difficulty. - #[clap(long, value_name = "DIFFICULTY")] + #[arg(long, value_name = "DIFFICULTY")] #[serde(skip_serializing_if = "Option::is_none")] pub block_difficulty: Option, /// The block prevrandao value. NOTE: Before merge this field was mix_hash. - #[clap(long, value_name = "PREVRANDAO")] + #[arg(long, value_name = "PREVRANDAO")] #[serde(skip_serializing_if = "Option::is_none")] pub block_prevrandao: Option, /// The block gas limit. - #[clap(long, value_name = "GAS_LIMIT")] + #[arg(long, value_name = "GAS_LIMIT")] #[serde(skip_serializing_if = "Option::is_none")] pub block_gas_limit: Option, @@ -269,7 +269,7 @@ pub struct EnvArgs { /// If this limit is exceeded, a `MemoryLimitOOG` result is thrown. /// /// The default is 128MiB. - #[clap(long, value_name = "MEMORY_LIMIT")] + #[arg(long, value_name = "MEMORY_LIMIT")] #[serde(skip_serializing_if = "Option::is_none")] pub memory_limit: Option, } diff --git a/crates/forge/bin/cmd/bind.rs b/crates/forge/bin/cmd/bind.rs index 93d188260..ca76aafa0 100644 --- a/crates/forge/bin/cmd/bind.rs +++ b/crates/forge/bin/cmd/bind.rs @@ -18,7 +18,7 @@ const DEFAULT_CRATE_VERSION: &str = "0.1.0"; #[derive(Clone, Debug, Parser)] pub struct BindArgs { /// Path to where the contract artifacts are stored. - #[clap( + #[arg( long = "bindings-path", short, value_hint = ValueHint::DirPath, @@ -27,61 +27,61 @@ pub struct BindArgs { pub bindings: Option, /// Create bindings only for contracts whose names match the specified filter(s) - #[clap(long)] + #[arg(long)] pub select: Vec, /// Create bindings only for contracts whose names do not match the specified filter(s) - #[clap(long, conflicts_with = "select")] + #[arg(long, conflicts_with = "select")] pub skip: Vec, /// Explicitly generate bindings for all contracts /// /// By default all contracts ending with `Test` or `Script` are excluded. - #[clap(long, conflicts_with_all = &["select", "skip"])] + #[arg(long, conflicts_with_all = &["select", "skip"])] pub select_all: bool, /// The name of the Rust crate to generate. /// /// This should be a valid crates.io crate name, /// however, this is not currently validated by this command. - #[clap(long, default_value = DEFAULT_CRATE_NAME, value_name = "NAME")] + #[arg(long, default_value = DEFAULT_CRATE_NAME, value_name = "NAME")] crate_name: String, /// The version of the Rust crate to generate. /// /// This should be a standard semver version string, /// however, this is not currently validated by this command. - #[clap(long, default_value = DEFAULT_CRATE_VERSION, value_name = "VERSION")] + #[arg(long, default_value = DEFAULT_CRATE_VERSION, value_name = "VERSION")] crate_version: String, /// Generate the bindings as a module instead of a crate. - #[clap(long)] + #[arg(long)] module: bool, /// Overwrite existing generated bindings. /// /// By default, the command will check that the bindings are correct, and then exit. If /// --overwrite is passed, it will instead delete and overwrite the bindings. - #[clap(long)] + #[arg(long)] overwrite: bool, /// Generate bindings as a single file. - #[clap(long)] + #[arg(long)] single_file: bool, /// Skip Cargo.toml consistency checks. - #[clap(long)] + #[arg(long)] skip_cargo_toml: bool, /// Skips running forge build before generating binding - #[clap(long)] + #[arg(long)] skip_build: bool, /// Don't add any additional derives to generated bindings - #[clap(long)] + #[arg(long)] skip_extra_derives: bool, - #[clap(flatten)] + #[command(flatten)] build_args: CoreBuildArgs, } diff --git a/crates/forge/bin/cmd/build.rs b/crates/forge/bin/cmd/build.rs index a24f84b69..66dae630e 100644 --- a/crates/forge/bin/cmd/build.rs +++ b/crates/forge/bin/cmd/build.rs @@ -40,36 +40,36 @@ foundry_config::merge_impl_figment_convert!(BuildArgs, args); /// Some arguments are marked as `#[serde(skip)]` and require manual processing in /// `figment::Provider` implementation #[derive(Clone, Debug, Default, Serialize, Parser)] -#[clap(next_help_heading = "Build options", about = None, long_about = None)] // override doc +#[command(next_help_heading = "Build options", about = None, long_about = None)] // override doc pub struct BuildArgs { /// Print compiled contract names. - #[clap(long)] + #[arg(long)] #[serde(skip)] pub names: bool, /// Print compiled contract sizes. - #[clap(long)] + #[arg(long)] #[serde(skip)] pub sizes: bool, /// Skip building files whose names contain the given filter. /// /// `test` and `script` are aliases for `.t.sol` and `.s.sol`. - #[clap(long, num_args(1..))] + #[arg(long, num_args(1..))] #[serde(skip)] pub skip: Option>, - #[clap(flatten)] + #[command(flatten)] #[serde(flatten)] pub args: CoreBuildArgs, - #[clap(flatten)] + #[command(flatten)] #[serde(skip)] pub watch: WatchArgs, /// Output the compilation errors in the json format. /// This is useful when you want to use the output in other tools. - #[clap(long, conflicts_with = "silent")] + #[arg(long, conflicts_with = "silent")] #[serde(skip)] pub format_json: bool, } diff --git a/crates/forge/bin/cmd/cache.rs b/crates/forge/bin/cmd/cache.rs index 4ae2056c4..ff3117d34 100644 --- a/crates/forge/bin/cmd/cache.rs +++ b/crates/forge/bin/cmd/cache.rs @@ -11,7 +11,7 @@ use strum::VariantNames; /// CLI arguments for `forge cache`. #[derive(Debug, Parser)] pub struct CacheArgs { - #[clap(subcommand)] + #[command(subcommand)] pub sub: CacheSubcommands, } @@ -26,12 +26,12 @@ pub enum CacheSubcommands { /// CLI arguments for `forge clean`. #[derive(Debug, Parser)] -#[clap(group = clap::ArgGroup::new("etherscan-blocks").multiple(false))] +#[command(group = clap::ArgGroup::new("etherscan-blocks").multiple(false))] pub struct CleanArgs { /// The chains to clean the cache for. /// /// Can also be "all" to clean all chains. - #[clap( + #[arg( env = "CHAIN", default_value = "all", value_parser = ChainOrAllValueParser::default(), @@ -39,18 +39,17 @@ pub struct CleanArgs { chains: Vec, /// The blocks to clean the cache for. - #[clap( + #[arg( short, long, num_args(1..), - use_value_delimiter(true), value_delimiter(','), group = "etherscan-blocks" )] blocks: Vec, /// Whether to clean the Etherscan cache. - #[clap(long, group = "etherscan-blocks")] + #[arg(long, group = "etherscan-blocks")] etherscan: bool, } @@ -82,7 +81,7 @@ pub struct LsArgs { /// The chains to list the cache for. /// /// Can also be "all" to list all chains. - #[clap( + #[arg( env = "CHAIN", default_value = "all", value_parser = ChainOrAllValueParser::default(), diff --git a/crates/forge/bin/cmd/config.rs b/crates/forge/bin/cmd/config.rs index 0758ddf50..fc325e39d 100644 --- a/crates/forge/bin/cmd/config.rs +++ b/crates/forge/bin/cmd/config.rs @@ -11,22 +11,22 @@ foundry_config::impl_figment_convert!(ConfigArgs, opts, evm_opts); #[derive(Clone, Debug, Parser)] pub struct ConfigArgs { /// Print only a basic set of the currently set config values. - #[clap(long)] + #[arg(long)] basic: bool, /// Print currently set config values as JSON. - #[clap(long)] + #[arg(long)] json: bool, /// Attempt to fix any configuration warnings. - #[clap(long)] + #[arg(long)] fix: bool, // support nested build arguments - #[clap(flatten)] + #[command(flatten)] opts: BuildArgs, - #[clap(flatten)] + #[command(flatten)] evm_opts: EvmArgs, } diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index 3505fc318..0c1232d7a 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -42,20 +42,20 @@ pub struct CoverageArgs { /// The report type to use for coverage. /// /// This flag can be used multiple times. - #[clap(long, value_enum, default_value = "summary")] + #[arg(long, value_enum, default_value = "summary")] report: Vec, /// Enable viaIR with minimum optimization /// /// This can fix most of the "stack too deep" errors while resulting a /// relatively accurate source map. - #[clap(long)] + #[arg(long)] ir_minimum: bool, /// The path to output the report. /// /// If not specified, the report will be stored in the root of the project. - #[clap( + #[arg( long, short, value_hint = ValueHint::FilePath, @@ -63,13 +63,13 @@ pub struct CoverageArgs { )] report_file: Option, - #[clap(flatten)] + #[command(flatten)] filter: FilterArgs, - #[clap(flatten)] + #[command(flatten)] evm_opts: EvmArgs, - #[clap(flatten)] + #[command(flatten)] opts: CoreBuildArgs, } diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index 8c4752603..194428191 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -35,7 +35,7 @@ pub struct CreateArgs { contract: ContractInfo, /// The constructor arguments. - #[clap( + #[arg( long, num_args(1..), conflicts_with = "constructor_args_path", @@ -44,7 +44,7 @@ pub struct CreateArgs { constructor_args: Vec, /// The path to a file containing the constructor arguments. - #[clap( + #[arg( long, value_hint = ValueHint::FilePath, value_name = "PATH", @@ -52,37 +52,37 @@ pub struct CreateArgs { constructor_args_path: Option, /// Print the deployment information as JSON. - #[clap(long, help_heading = "Display options")] + #[arg(long, help_heading = "Display options")] json: bool, /// Verify contract after creation. - #[clap(long)] + #[arg(long)] verify: bool, /// Send via `eth_sendTransaction` using the `--from` argument or `$ETH_FROM` as sender - #[clap(long, requires = "from")] + #[arg(long, requires = "from")] unlocked: bool, /// Prints the standard json compiler input if `--verify` is provided. /// /// The standard json compiler input can be used to manually submit contract verification in /// the browser. - #[clap(long, requires = "verify")] + #[arg(long, requires = "verify")] show_standard_json_input: bool, - #[clap(flatten)] + #[command(flatten)] opts: CoreBuildArgs, - #[clap(flatten)] + #[command(flatten)] tx: TransactionOpts, - #[clap(flatten)] + #[command(flatten)] eth: EthereumOpts, - #[clap(flatten)] + #[command(flatten)] pub verifier: verify::VerifierArgs, - #[clap(flatten)] + #[command(flatten)] retry: RetryArgs, } diff --git a/crates/forge/bin/cmd/debug.rs b/crates/forge/bin/cmd/debug.rs index a69c2da3c..dafbf965a 100644 --- a/crates/forge/bin/cmd/debug.rs +++ b/crates/forge/bin/cmd/debug.rs @@ -14,28 +14,28 @@ pub struct DebugArgs { /// /// If multiple contracts exist in the same file you must specify the target contract with /// --target-contract. - #[clap(value_hint = ValueHint::FilePath)] + #[arg(value_hint = ValueHint::FilePath)] pub path: PathBuf, /// Arguments to pass to the script function. pub args: Vec, /// The name of the contract you want to run. - #[clap(long, visible_alias = "tc", value_name = "CONTRACT_NAME")] + #[arg(long, visible_alias = "tc", value_name = "CONTRACT_NAME")] pub target_contract: Option, /// The signature of the function you want to call in the contract, or raw calldata. - #[clap(long, short, default_value = "run()", value_name = "SIGNATURE")] + #[arg(long, short, default_value = "run()", value_name = "SIGNATURE")] pub sig: String, /// Open the script in the debugger. - #[clap(long)] + #[arg(long)] pub debug: bool, - #[clap(flatten)] + #[command(flatten)] pub opts: CoreBuildArgs, - #[clap(flatten)] + #[command(flatten)] pub evm_opts: EvmArgs, } diff --git a/crates/forge/bin/cmd/doc/mod.rs b/crates/forge/bin/cmd/doc/mod.rs index c3ef50aa2..d594a0e56 100644 --- a/crates/forge/bin/cmd/doc/mod.rs +++ b/crates/forge/bin/cmd/doc/mod.rs @@ -16,13 +16,13 @@ pub struct DocArgs { /// /// By default root of the Git repository, if in one, /// or the current working directory. - #[clap(long, value_hint = ValueHint::DirPath, value_name = "PATH")] + #[arg(long, value_hint = ValueHint::DirPath, value_name = "PATH")] pub root: Option, /// The doc's output path. /// /// By default, it is the `docs/` in project root. - #[clap( + #[arg( long, short, value_hint = ValueHint::DirPath, @@ -31,32 +31,32 @@ pub struct DocArgs { out: Option, /// Build the `mdbook` from generated files. - #[clap(long, short)] + #[arg(long, short)] build: bool, /// Serve the documentation. - #[clap(long, short)] + #[arg(long, short)] serve: bool, /// Open the documentation in a browser after serving. - #[clap(long, requires = "serve")] + #[arg(long, requires = "serve")] open: bool, /// Hostname for serving documentation. - #[clap(long, requires = "serve")] + #[arg(long, requires = "serve")] hostname: Option, /// Port for serving documentation. - #[clap(long, short, requires = "serve")] + #[arg(long, short, requires = "serve")] port: Option, /// The relative path to the `hardhat-deploy` or `forge-deploy` artifact directory. Leave blank /// for default. - #[clap(long)] + #[arg(long)] deployments: Option>, /// Whether to create docs for external libraries. - #[clap(long, short)] + #[arg(long, short)] include_libraries: bool, } diff --git a/crates/forge/bin/cmd/flatten.rs b/crates/forge/bin/cmd/flatten.rs index b4c1edcd9..c61369320 100644 --- a/crates/forge/bin/cmd/flatten.rs +++ b/crates/forge/bin/cmd/flatten.rs @@ -12,13 +12,13 @@ use std::path::PathBuf; #[derive(Clone, Debug, Parser)] pub struct FlattenArgs { /// The path to the contract to flatten. - #[clap(value_hint = ValueHint::FilePath, value_name = "PATH")] + #[arg(value_hint = ValueHint::FilePath, value_name = "PATH")] pub target_path: PathBuf, /// The path to output the flattened contract. /// /// If not specified, the flattened contract will be output to stdout. - #[clap( + #[arg( long, short, value_hint = ValueHint::FilePath, @@ -26,7 +26,7 @@ pub struct FlattenArgs { )] pub output: Option, - #[clap(flatten)] + #[command(flatten)] project_paths: ProjectPathsArgs, } diff --git a/crates/forge/bin/cmd/fmt.rs b/crates/forge/bin/cmd/fmt.rs index a4787ed7d..36c080000 100644 --- a/crates/forge/bin/cmd/fmt.rs +++ b/crates/forge/bin/cmd/fmt.rs @@ -18,25 +18,25 @@ use yansi::Color; #[derive(Clone, Debug, Parser)] pub struct FmtArgs { /// Path to the file, directory or '-' to read from stdin. - #[clap(value_hint = ValueHint::FilePath, value_name = "PATH", num_args(1..))] + #[arg(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. - #[clap(long, value_hint = ValueHint::DirPath, value_name = "PATH")] + #[arg(long, value_hint = ValueHint::DirPath, value_name = "PATH")] root: Option, /// Run in 'check' mode. /// /// Exits with 0 if input is formatted correctly. /// Exits with 1 if formatting is required. - #[clap(long)] + #[arg(long)] check: bool, /// In 'check' and stdin modes, outputs raw formatted code instead of the diff. - #[clap(long, short)] + #[arg(long, short)] raw: bool, } diff --git a/crates/forge/bin/cmd/geiger/mod.rs b/crates/forge/bin/cmd/geiger/mod.rs index 79d8e25b2..8e3bc6fc5 100644 --- a/crates/forge/bin/cmd/geiger/mod.rs +++ b/crates/forge/bin/cmd/geiger/mod.rs @@ -19,7 +19,7 @@ mod visitor; #[derive(Clone, Debug, Parser)] pub struct GeigerArgs { /// Paths to files or directories to detect. - #[clap( + #[arg( conflicts_with = "root", value_hint = ValueHint::FilePath, value_name = "PATH", @@ -31,17 +31,17 @@ pub struct GeigerArgs { /// /// By default root of the Git repository, if in one, /// or the current working directory. - #[clap(long, value_hint = ValueHint::DirPath, value_name = "PATH")] + #[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. - #[clap(long)] + #[arg(long)] pub check: bool, /// Globs to ignore. - #[clap( + #[arg( long, value_hint = ValueHint::FilePath, value_name = "PATH", @@ -50,7 +50,7 @@ pub struct GeigerArgs { ignore: Vec, /// Print a report of all files, even if no unsafe functions are found. - #[clap(long)] + #[arg(long)] full: bool, } diff --git a/crates/forge/bin/cmd/generate/mod.rs b/crates/forge/bin/cmd/generate/mod.rs index 9e25d6532..190ea52b7 100644 --- a/crates/forge/bin/cmd/generate/mod.rs +++ b/crates/forge/bin/cmd/generate/mod.rs @@ -7,7 +7,7 @@ use yansi::Paint; /// CLI arguments for `forge generate`. #[derive(Debug, Parser)] pub struct GenerateArgs { - #[clap(subcommand)] + #[command(subcommand)] pub sub: GenerateSubcommands, } @@ -20,7 +20,7 @@ pub enum GenerateSubcommands { #[derive(Debug, Parser)] pub struct GenerateTestArgs { /// Contract name for test generation. - #[clap(long, short, value_name = "CONTRACT_NAME")] + #[arg(long, short, value_name = "CONTRACT_NAME")] pub contract_name: String, } diff --git a/crates/forge/bin/cmd/init.rs b/crates/forge/bin/cmd/init.rs index 9dc1eea1b..9c8c3fc90 100644 --- a/crates/forge/bin/cmd/init.rs +++ b/crates/forge/bin/cmd/init.rs @@ -12,32 +12,32 @@ use yansi::Paint; #[derive(Clone, Debug, Parser)] pub struct InitArgs { /// The root directory of the new project. - #[clap(value_hint = ValueHint::DirPath, default_value = ".", value_name = "PATH")] + #[arg(value_hint = ValueHint::DirPath, default_value = ".", value_name = "PATH")] root: PathBuf, /// The template to start from. - #[clap(long, short)] + #[arg(long, short)] template: Option, /// Branch argument that can only be used with template option. /// If not specified, the default branch is used. - #[clap(long, short, requires = "template")] + #[arg(long, short, requires = "template")] branch: Option, /// Do not install dependencies from the network. - #[clap(long, conflicts_with = "template", visible_alias = "no-deps")] + #[arg(long, conflicts_with = "template", visible_alias = "no-deps")] offline: bool, /// Create the project even if the specified root directory is not empty. - #[clap(long, conflicts_with = "template")] + #[arg(long, conflicts_with = "template")] force: bool, /// Create a .vscode/settings.json file with Solidity settings, and generate a remappings.txt /// file. - #[clap(long, conflicts_with = "template")] + #[arg(long, conflicts_with = "template")] vscode: bool, - #[clap(flatten)] + #[command(flatten)] opts: DependencyInstallOpts, } diff --git a/crates/forge/bin/cmd/inspect.rs b/crates/forge/bin/cmd/inspect.rs index e85fc37b0..b76ca2878 100644 --- a/crates/forge/bin/cmd/inspect.rs +++ b/crates/forge/bin/cmd/inspect.rs @@ -23,15 +23,15 @@ pub struct InspectArgs { pub contract: ContractInfo, /// The contract artifact field to inspect. - #[clap(value_enum)] + #[arg(value_enum)] pub field: ContractArtifactField, /// Pretty print the selected field, if supported. - #[clap(long)] + #[arg(long)] pub pretty: bool, /// All build arguments are supported - #[clap(flatten)] + #[command(flatten)] build: CoreBuildArgs, } diff --git a/crates/forge/bin/cmd/install.rs b/crates/forge/bin/cmd/install.rs index b7690d458..75fdbe3aa 100644 --- a/crates/forge/bin/cmd/install.rs +++ b/crates/forge/bin/cmd/install.rs @@ -22,7 +22,7 @@ static DEPENDENCY_VERSION_TAG_REGEX: Lazy = /// CLI arguments for `forge install`. #[derive(Clone, Debug, Parser)] -#[clap(override_usage = "forge install [OPTIONS] [DEPENDENCIES]... +#[command(override_usage = "forge install [OPTIONS] [DEPENDENCIES]... forge install [OPTIONS] /@... forge install [OPTIONS] =/@... forge install [OPTIONS] ...")] @@ -46,10 +46,10 @@ pub struct InstallArgs { /// /// By default root of the Git repository, if in one, /// or the current working directory. - #[clap(long, value_hint = ValueHint::DirPath, value_name = "PATH")] + #[arg(long, value_hint = ValueHint::DirPath, value_name = "PATH")] pub root: Option, - #[clap(flatten)] + #[command(flatten)] opts: DependencyInstallOpts, } @@ -67,19 +67,19 @@ pub struct DependencyInstallOpts { /// Perform shallow clones instead of deep ones. /// /// Improves performance and reduces disk usage, but prevents switching branches or tags. - #[clap(long)] + #[arg(long)] pub shallow: bool, /// Install without adding the dependency as a submodule. - #[clap(long)] + #[arg(long)] pub no_git: bool, /// Do not create a commit. - #[clap(long)] + #[arg(long)] pub no_commit: bool, /// Do not print any messages. - #[clap(short, long)] + #[arg(short, long)] pub quiet: bool, } diff --git a/crates/forge/bin/cmd/mod.rs b/crates/forge/bin/cmd/mod.rs index 97d188773..92c80e219 100644 --- a/crates/forge/bin/cmd/mod.rs +++ b/crates/forge/bin/cmd/mod.rs @@ -23,9 +23,9 @@ //! // A new clap subcommand that accepts both `EvmArgs` and `BuildArgs` //! #[derive(Clone, Debug, Parser)] //! pub struct MyArgs { -//! #[clap(flatten)] +//! #[command(flatten)] //! evm_opts: EvmArgs, -//! #[clap(flatten)] +//! #[command(flatten)] //! opts: BuildArgs, //! } //! diff --git a/crates/forge/bin/cmd/remappings.rs b/crates/forge/bin/cmd/remappings.rs index 2a0379af2..6728f0ae1 100644 --- a/crates/forge/bin/cmd/remappings.rs +++ b/crates/forge/bin/cmd/remappings.rs @@ -12,10 +12,10 @@ pub struct RemappingArgs { /// /// By default root of the Git repository, if in one, /// or the current working directory. - #[clap(long, value_hint = ValueHint::DirPath, value_name = "PATH")] + #[arg(long, value_hint = ValueHint::DirPath, value_name = "PATH")] root: Option, /// Pretty-print the remappings, grouping each of them by context. - #[clap(long)] + #[arg(long)] pretty: bool, } impl_figment_convert_basic!(RemappingArgs); diff --git a/crates/forge/bin/cmd/remove.rs b/crates/forge/bin/cmd/remove.rs index 0b77515c8..22343ef7d 100644 --- a/crates/forge/bin/cmd/remove.rs +++ b/crates/forge/bin/cmd/remove.rs @@ -17,11 +17,11 @@ pub struct RemoveArgs { /// /// By default root of the Git repository, if in one, /// or the current working directory. - #[clap(long, value_hint = ValueHint::DirPath, value_name = "PATH")] + #[arg(long, value_hint = ValueHint::DirPath, value_name = "PATH")] root: Option, /// Override the up-to-date check. - #[clap(short, long)] + #[arg(short, long)] force: bool, } impl_figment_convert_basic!(RemoveArgs); diff --git a/crates/forge/bin/cmd/retry.rs b/crates/forge/bin/cmd/retry.rs index 45305288e..8ffc61b88 100644 --- a/crates/forge/bin/cmd/retry.rs +++ b/crates/forge/bin/cmd/retry.rs @@ -10,10 +10,10 @@ pub const RETRY_VERIFY_ON_CREATE: RetryArgs = RetryArgs { retries: 15, delay: 5 /// Retry arguments for contract verification. #[derive(Clone, Copy, Debug, Parser)] -#[clap(about = "Allows to use retry arguments for contract verification")] // override doc +#[command(about = "Allows to use retry arguments for contract verification")] // override doc pub struct RetryArgs { /// Number of attempts for retrying verification. - #[clap( + #[arg( long, value_parser = RangedU64ValueParser::::new().range(1..), default_value = "5", @@ -21,7 +21,7 @@ pub struct RetryArgs { pub retries: u32, /// Optional delay to apply inbetween verification attempts, in seconds. - #[clap( + #[arg( long, value_parser = RangedU64ValueParser::::new().range(0..=30), default_value = "5", diff --git a/crates/forge/bin/cmd/script/mod.rs b/crates/forge/bin/cmd/script/mod.rs index 5df893bb0..8fe66f839 100644 --- a/crates/forge/bin/cmd/script/mod.rs +++ b/crates/forge/bin/cmd/script/mod.rs @@ -72,22 +72,22 @@ pub struct ScriptArgs { /// /// If multiple contracts exist in the same file you must specify the target contract with /// --target-contract. - #[clap(value_hint = ValueHint::FilePath)] + #[arg(value_hint = ValueHint::FilePath)] pub path: String, /// Arguments to pass to the script function. pub args: Vec, /// The name of the contract you want to run. - #[clap(long, visible_alias = "tc", value_name = "CONTRACT_NAME")] + #[arg(long, visible_alias = "tc", value_name = "CONTRACT_NAME")] pub target_contract: Option, /// The signature of the function you want to call in the contract, or raw calldata. - #[clap(long, short, default_value = "run()")] + #[arg(long, short, default_value = "run()")] pub sig: String, /// Max priority fee per gas for EIP1559 transactions. - #[clap( + #[arg( long, env = "ETH_PRIORITY_GAS_PRICE", value_parser = foundry_cli::utils::parse_ether_value, @@ -98,23 +98,23 @@ pub struct ScriptArgs { /// Use legacy transactions instead of EIP1559 ones. /// /// This is auto-enabled for common networks without EIP1559. - #[clap(long)] + #[arg(long)] pub legacy: bool, /// Broadcasts the transactions. - #[clap(long)] + #[arg(long)] pub broadcast: bool, /// Skips on-chain simulation. - #[clap(long)] + #[arg(long)] pub skip_simulation: bool, /// Relative percentage to multiply gas estimates by. - #[clap(long, short, default_value = "130")] + #[arg(long, short, default_value = "130")] pub gas_estimate_multiplier: u64, /// Send via `eth_sendTransaction` using the `--from` argument or `$ETH_FROM` as sender - #[clap( + #[arg( long, requires = "sender", conflicts_with_all = &["private_key", "private_keys", "froms", "ledger", "trezor", "aws"], @@ -127,44 +127,44 @@ pub struct ScriptArgs { /// /// Example: If transaction N has a nonce of 22, then the account should have a nonce of 22, /// otherwise it fails. - #[clap(long)] + #[arg(long)] pub resume: bool, /// If present, --resume or --verify will be assumed to be a multi chain deployment. - #[clap(long)] + #[arg(long)] pub multi: bool, /// Open the script in the debugger. /// /// Takes precedence over broadcast. - #[clap(long)] + #[arg(long)] pub debug: bool, /// Makes sure a transaction is sent, /// only after its previous one has been confirmed and succeeded. - #[clap(long)] + #[arg(long)] pub slow: bool, /// Disables interactive prompts that might appear when deploying big contracts. /// /// For more info on the contract size limit, see EIP-170: - #[clap(long)] + #[arg(long)] pub non_interactive: bool, /// The Etherscan (or equivalent) API key - #[clap(long, env = "ETHERSCAN_API_KEY", value_name = "KEY")] + #[arg(long, env = "ETHERSCAN_API_KEY", value_name = "KEY")] pub etherscan_api_key: Option, /// Verifies all the contracts found in the receipts of a script, if any. - #[clap(long)] + #[arg(long)] pub verify: bool, /// Output results in JSON format. - #[clap(long)] + #[arg(long)] pub json: bool, /// Gas price for legacy transactions, or max fee per gas for EIP1559 transactions. - #[clap( + #[arg( long, env = "ETH_GAS_PRICE", value_parser = foundry_cli::utils::parse_ether_value, @@ -172,19 +172,19 @@ pub struct ScriptArgs { )] pub with_gas_price: Option, - #[clap(flatten)] + #[command(flatten)] pub opts: BuildArgs, - #[clap(flatten)] + #[command(flatten)] pub wallets: MultiWalletOpts, - #[clap(flatten)] + #[command(flatten)] pub evm_opts: EvmArgs, - #[clap(flatten)] + #[command(flatten)] pub verifier: super::verify::VerifierArgs, - #[clap(flatten)] + #[command(flatten)] pub retry: RetryArgs, } diff --git a/crates/forge/bin/cmd/selectors.rs b/crates/forge/bin/cmd/selectors.rs index 7318fa04f..1ee251082 100644 --- a/crates/forge/bin/cmd/selectors.rs +++ b/crates/forge/bin/cmd/selectors.rs @@ -16,7 +16,7 @@ use std::fs::canonicalize; #[derive(Clone, Debug, Parser)] pub enum SelectorsSubcommands { /// Check for selector collisions between contracts - #[clap(visible_alias = "co")] + #[command(visible_alias = "co")] Collision { /// The first of the two contracts for which to look selector collisions for, in the form /// `(:)?`. @@ -26,33 +26,33 @@ pub enum SelectorsSubcommands { /// `(:)?`. second_contract: ContractInfo, - #[clap(flatten)] + #[command(flatten)] build: Box, }, /// Upload selectors to registry - #[clap(visible_alias = "up")] + #[command(visible_alias = "up")] Upload { /// The name of the contract to upload selectors for. - #[clap(required_unless_present = "all")] + #[arg(required_unless_present = "all")] contract: Option, /// Upload selectors for all contracts in the project. - #[clap(long, required_unless_present = "contract")] + #[arg(long, required_unless_present = "contract")] all: bool, - #[clap(flatten)] + #[command(flatten)] project_paths: ProjectPathsArgs, }, /// List selectors from current workspace - #[clap(visible_alias = "ls")] + #[command(visible_alias = "ls")] List { /// The name of the contract to list selectors for. - #[clap(help = "The name of the contract to list selectors for.")] + #[arg(help = "The name of the contract to list selectors for.")] contract: Option, - #[clap(flatten)] + #[command(flatten)] project_paths: ProjectPathsArgs, }, } diff --git a/crates/forge/bin/cmd/snapshot.rs b/crates/forge/bin/cmd/snapshot.rs index f60ab01bb..d7147f49b 100644 --- a/crates/forge/bin/cmd/snapshot.rs +++ b/crates/forge/bin/cmd/snapshot.rs @@ -29,7 +29,7 @@ pub struct SnapshotArgs { /// Output a diff against a pre-existing snapshot. /// /// By default, the comparison is done with .gas-snapshot. - #[clap( + #[arg( conflicts_with = "snap", long, value_hint = ValueHint::FilePath, @@ -42,7 +42,7 @@ pub struct SnapshotArgs { /// Outputs a diff if the snapshots do not match. /// /// By default, the comparison is done with .gas-snapshot. - #[clap( + #[arg( conflicts_with = "diff", long, value_hint = ValueHint::FilePath, @@ -52,11 +52,11 @@ pub struct SnapshotArgs { // Hidden because there is only one option /// How to format the output. - #[clap(long, hide(true))] + #[arg(long, hide(true))] format: Option, /// Output file for the snapshot. - #[clap( + #[arg( long, default_value = ".gas-snapshot", value_hint = ValueHint::FilePath, @@ -65,7 +65,7 @@ pub struct SnapshotArgs { snap: PathBuf, /// Tolerates gas deviations up to the specified percentage. - #[clap( + #[arg( long, value_parser = RangedU64ValueParser::::new().range(0..100), value_name = "SNAPSHOT_THRESHOLD" @@ -73,11 +73,11 @@ pub struct SnapshotArgs { tolerance: Option, /// All test arguments are supported - #[clap(flatten)] + #[command(flatten)] pub(crate) test: test::TestArgs, /// Additional configs for test results - #[clap(flatten)] + #[command(flatten)] config: SnapshotConfig, } @@ -141,19 +141,19 @@ impl FromStr for Format { #[derive(Clone, Debug, Default, Parser)] struct SnapshotConfig { /// Sort results by gas used (ascending). - #[clap(long)] + #[arg(long)] asc: bool, /// Sort results by gas used (descending). - #[clap(conflicts_with = "asc", long)] + #[arg(conflicts_with = "asc", long)] desc: bool, /// Only include tests that used more gas that the given amount. - #[clap(long, value_name = "MIN_GAS")] + #[arg(long, value_name = "MIN_GAS")] min: Option, /// Only include tests that used less gas that the given amount. - #[clap(long, value_name = "MAX_GAS")] + #[arg(long, value_name = "MAX_GAS")] max: Option, } diff --git a/crates/forge/bin/cmd/test/filter.rs b/crates/forge/bin/cmd/test/filter.rs index 81497ca14..65d3d0ed5 100644 --- a/crates/forge/bin/cmd/test/filter.rs +++ b/crates/forge/bin/cmd/test/filter.rs @@ -10,31 +10,31 @@ use std::{fmt, path::Path}; /// /// See also `FileFilter`. #[derive(Clone, Parser)] -#[clap(next_help_heading = "Test filtering")] +#[command(next_help_heading = "Test filtering")] pub struct FilterArgs { /// Only run test functions matching the specified regex pattern. - #[clap(long = "match-test", visible_alias = "mt", value_name = "REGEX")] + #[arg(long = "match-test", visible_alias = "mt", value_name = "REGEX")] pub test_pattern: Option, /// Only run test functions that do not match the specified regex pattern. - #[clap(long = "no-match-test", visible_alias = "nmt", value_name = "REGEX")] + #[arg(long = "no-match-test", visible_alias = "nmt", value_name = "REGEX")] pub test_pattern_inverse: Option, /// Only run tests in contracts matching the specified regex pattern. - #[clap(long = "match-contract", visible_alias = "mc", value_name = "REGEX")] + #[arg(long = "match-contract", visible_alias = "mc", value_name = "REGEX")] pub contract_pattern: Option, /// Only run tests in contracts that do not match the specified regex pattern. - #[clap(long = "no-match-contract", visible_alias = "nmc", value_name = "REGEX")] + #[arg(long = "no-match-contract", visible_alias = "nmc", value_name = "REGEX")] pub contract_pattern_inverse: Option, /// Only run tests in source files matching the specified glob pattern. - #[clap(long = "match-path", visible_alias = "mp", value_name = "GLOB")] + #[arg(long = "match-path", visible_alias = "mp", value_name = "GLOB")] pub path_pattern: Option, /// Only run tests in source files that do not match the specified glob pattern. - #[clap( - name = "no-match-path", + #[arg( + id = "no-match-path", long = "no-match-path", visible_alias = "nmp", value_name = "GLOB" diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index b9ca859b8..b81a5b362 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -48,7 +48,7 @@ foundry_config::merge_impl_figment_convert!(TestArgs, opts, evm_opts); /// CLI arguments for `forge test`. #[derive(Clone, Debug, Parser)] -#[clap(next_help_heading = "Test options")] +#[command(next_help_heading = "Test options")] pub struct TestArgs { /// Run a test in the debugger. /// @@ -65,58 +65,58 @@ pub struct TestArgs { /// If the fuzz test does not fail, it will open the debugger on the last fuzz case. /// /// For more fine-grained control of which fuzz case is run, see forge run. - #[clap(long, value_name = "TEST_FUNCTION")] + #[arg(long, value_name = "TEST_FUNCTION")] debug: Option, /// Print a gas report. - #[clap(long, env = "FORGE_GAS_REPORT")] + #[arg(long, env = "FORGE_GAS_REPORT")] gas_report: bool, /// Exit with code 0 even if a test fails. - #[clap(long, env = "FORGE_ALLOW_FAILURE")] + #[arg(long, env = "FORGE_ALLOW_FAILURE")] allow_failure: bool, /// Output test results in JSON format. - #[clap(long, short, help_heading = "Display options")] + #[arg(long, short, help_heading = "Display options")] json: bool, /// Stop running tests after the first failure. - #[clap(long)] + #[arg(long)] pub fail_fast: bool, /// The Etherscan (or equivalent) API key. - #[clap(long, env = "ETHERSCAN_API_KEY", value_name = "KEY")] + #[arg(long, env = "ETHERSCAN_API_KEY", value_name = "KEY")] etherscan_api_key: Option, /// List tests instead of running them. - #[clap(long, short, help_heading = "Display options")] + #[arg(long, short, help_heading = "Display options")] list: bool, /// Set seed used to generate randomness during your fuzz runs. - #[clap(long)] + #[arg(long)] pub fuzz_seed: Option, - #[clap(long, env = "FOUNDRY_FUZZ_RUNS", value_name = "RUNS")] + #[arg(long, env = "FOUNDRY_FUZZ_RUNS", value_name = "RUNS")] pub fuzz_runs: Option, - #[clap(flatten)] + #[command(flatten)] filter: FilterArgs, - #[clap(flatten)] + #[command(flatten)] evm_opts: EvmArgs, - #[clap(flatten)] + #[command(flatten)] opts: CoreBuildArgs, - #[clap(flatten)] + #[command(flatten)] pub watch: WatchArgs, /// Print test summary table. - #[clap(long, help_heading = "Display options")] + #[arg(long, help_heading = "Display options")] pub summary: bool, /// Print detailed test summary table. - #[clap(long, help_heading = "Display options", requires = "summary")] + #[arg(long, help_heading = "Display options", requires = "summary")] pub detailed: bool, } diff --git a/crates/forge/bin/cmd/tree.rs b/crates/forge/bin/cmd/tree.rs index 689d6e444..813301025 100644 --- a/crates/forge/bin/cmd/tree.rs +++ b/crates/forge/bin/cmd/tree.rs @@ -10,16 +10,16 @@ use foundry_compilers::{ #[derive(Clone, Debug, Parser)] pub struct TreeArgs { /// Do not de-duplicate (repeats all shared dependencies) - #[clap(long)] + #[arg(long)] no_dedupe: bool, /// Character set to use in output. /// /// [possible values: utf8, ascii] - #[clap(long, default_value = "utf8")] + #[arg(long, default_value = "utf8")] charset: Charset, - #[clap(flatten)] + #[command(flatten)] opts: ProjectPathsArgs, } diff --git a/crates/forge/bin/cmd/update.rs b/crates/forge/bin/cmd/update.rs index 37e5baccb..0cc25b6b6 100644 --- a/crates/forge/bin/cmd/update.rs +++ b/crates/forge/bin/cmd/update.rs @@ -17,15 +17,15 @@ pub struct UpdateArgs { /// /// By default root of the Git repository, if in one, /// or the current working directory. - #[clap(long, value_hint = ValueHint::DirPath, value_name = "PATH")] + #[arg(long, value_hint = ValueHint::DirPath, value_name = "PATH")] root: Option, /// Override the up-to-date check. - #[clap(short, long)] + #[arg(short, long)] force: bool, /// Recursively update submodules. - #[clap(short, long)] + #[arg(short, long)] recursive: bool, } impl_figment_convert_basic!(UpdateArgs); diff --git a/crates/forge/bin/cmd/verify/mod.rs b/crates/forge/bin/cmd/verify/mod.rs index c598006d9..f9839d717 100644 --- a/crates/forge/bin/cmd/verify/mod.rs +++ b/crates/forge/bin/cmd/verify/mod.rs @@ -21,11 +21,11 @@ mod sourcify; #[derive(Clone, Debug, Parser)] pub struct VerifierArgs { /// The contract verification provider to use. - #[clap(long, help_heading = "Verifier options", default_value = "etherscan", value_enum)] + #[arg(long, help_heading = "Verifier options", default_value = "etherscan", value_enum)] pub verifier: VerificationProviderType, /// The verifier URL, if using a custom provider - #[clap(long, help_heading = "Verifier options", env = "VERIFIER_URL")] + #[arg(long, help_heading = "Verifier options", env = "VERIFIER_URL")] pub verifier_url: Option, } @@ -45,7 +45,7 @@ pub struct VerifyArgs { pub contract: ContractInfo, /// The ABI-encoded constructor arguments. - #[clap( + #[arg( long, conflicts_with = "constructor_args_path", value_name = "ARGS", @@ -54,68 +54,68 @@ pub struct VerifyArgs { pub constructor_args: Option, /// The path to a file containing the constructor arguments. - #[clap(long, value_hint = ValueHint::FilePath, value_name = "PATH")] + #[arg(long, value_hint = ValueHint::FilePath, value_name = "PATH")] pub constructor_args_path: Option, /// The `solc` version to use to build the smart contract. - #[clap(long, value_name = "VERSION")] + #[arg(long, value_name = "VERSION")] pub compiler_version: Option, /// The number of optimization runs used to build the smart contract. - #[clap(long, visible_alias = "optimizer-runs", value_name = "NUM")] + #[arg(long, visible_alias = "optimizer-runs", value_name = "NUM")] pub num_of_optimizations: Option, /// Flatten the source code before verifying. - #[clap(long)] + #[arg(long)] pub flatten: bool, /// Do not compile the flattened smart contract before verifying (if --flatten is passed). - #[clap(short, long)] + #[arg(short, long)] pub force: bool, /// Do not check if the contract is already verified before verifying. - #[clap(long)] + #[arg(long)] pub skip_is_verified_check: bool, /// Wait for verification result after submission. - #[clap(long)] + #[arg(long)] pub watch: bool, /// Set pre-linked libraries. - #[clap(long, help_heading = "Linker options", env = "DAPP_LIBRARIES")] + #[arg(long, help_heading = "Linker options", env = "DAPP_LIBRARIES")] pub libraries: Vec, /// The project's root path. /// /// By default root of the Git repository, if in one, /// or the current working directory. - #[clap(long, value_hint = ValueHint::DirPath, value_name = "PATH")] + #[arg(long, value_hint = ValueHint::DirPath, value_name = "PATH")] pub root: Option, /// Prints the standard json compiler input. /// /// The standard json compiler input can be used to manually submit contract verification in /// the browser. - #[clap(long, conflicts_with = "flatten")] + #[arg(long, conflicts_with = "flatten")] pub show_standard_json_input: bool, /// Use the Yul intermediate representation compilation pipeline. - #[clap(long)] + #[arg(long)] pub via_ir: bool, /// The EVM version to use. /// /// Overrides the version specified in the config. - #[clap(long)] + #[arg(long)] pub evm_version: Option, - #[clap(flatten)] + #[command(flatten)] pub etherscan: EtherscanOpts, - #[clap(flatten)] + #[command(flatten)] pub retry: RetryArgs, - #[clap(flatten)] + #[command(flatten)] pub verifier: VerifierArgs, } @@ -205,13 +205,13 @@ pub struct VerifyCheckArgs { /// For Sourcify - Contract Address. id: String, - #[clap(flatten)] + #[command(flatten)] retry: RetryArgs, - #[clap(flatten)] + #[command(flatten)] etherscan: EtherscanOpts, - #[clap(flatten)] + #[command(flatten)] verifier: VerifierArgs, } diff --git a/crates/forge/bin/cmd/watch.rs b/crates/forge/bin/cmd/watch.rs index f2ca664be..1412cb15e 100644 --- a/crates/forge/bin/cmd/watch.rs +++ b/crates/forge/bin/cmd/watch.rs @@ -16,12 +16,12 @@ use watchexec::{ }; #[derive(Clone, Debug, Default, Parser)] -#[clap(next_help_heading = "Watch options")] +#[command(next_help_heading = "Watch options")] pub struct WatchArgs { /// Watch the given files or directories for changes. /// /// If no paths are provided, the source and test directories of the project are watched. - #[clap( + #[arg( long, short, num_args(0..), @@ -30,13 +30,13 @@ pub struct WatchArgs { pub watch: Option>, /// Do not restart the command while it's still running. - #[clap(long)] + #[arg(long)] pub no_restart: bool, /// Explicitly re-run all tests when a change is made. /// /// By default, only the tests of the last modified test file are executed. - #[clap(long)] + #[arg(long)] pub run_all: bool, /// File update debounce delay. @@ -52,7 +52,7 @@ pub struct WatchArgs { /// /// When using --poll mode, you'll want a larger duration, or risk /// overloading disk I/O. - #[clap(long, value_name = "DELAY")] + #[arg(long, value_name = "DELAY")] pub watch_delay: Option, } diff --git a/crates/forge/bin/opts.rs b/crates/forge/bin/opts.rs index e62ac1908..4a72b80fc 100644 --- a/crates/forge/bin/opts.rs +++ b/crates/forge/bin/opts.rs @@ -33,14 +33,14 @@ const VERSION_MESSAGE: &str = concat!( /// Build, test, fuzz, debug and deploy Solidity contracts. #[derive(Parser)] -#[clap( +#[command( name = "forge", version = VERSION_MESSAGE, after_help = "Find more information in the book: http://book.getfoundry.sh/reference/forge/forge.html", next_display_order = None, )] pub struct Forge { - #[clap(subcommand)] + #[command(subcommand)] pub cmd: ForgeSubcommand, } @@ -48,7 +48,7 @@ pub struct Forge { #[allow(clippy::large_enum_variant)] pub enum ForgeSubcommand { /// Run the project's tests. - #[clap(visible_alias = "t")] + #[command(visible_alias = "t")] Test(test::TestArgs), /// Run a smart contract as a script, building transactions that can be sent onchain. @@ -58,71 +58,71 @@ pub enum ForgeSubcommand { Coverage(coverage::CoverageArgs), /// Generate Rust bindings for smart contracts. - #[clap(alias = "bi")] + #[command(alias = "bi")] Bind(BindArgs), /// Build the project's smart contracts. - #[clap(visible_aliases = ["b", "compile"])] + #[command(visible_aliases = ["b", "compile"])] Build(BuildArgs), /// Debugs a single smart contract as a script. - #[clap(visible_alias = "d")] + #[command(visible_alias = "d")] Debug(DebugArgs), /// Update one or multiple dependencies. /// /// If no arguments are provided, then all dependencies are updated. - #[clap(visible_alias = "u")] + #[command(visible_alias = "u")] Update(update::UpdateArgs), /// Install one or multiple dependencies. /// /// If no arguments are provided, then existing dependencies will be installed. - #[clap(visible_alias = "i")] + #[command(visible_alias = "i")] Install(InstallArgs), /// Remove one or multiple dependencies. - #[clap(visible_alias = "rm")] + #[command(visible_alias = "rm")] Remove(RemoveArgs), /// Get the automatically inferred remappings for the project. - #[clap(visible_alias = "re")] + #[command(visible_alias = "re")] Remappings(RemappingArgs), /// Verify smart contracts on Etherscan. - #[clap(visible_alias = "v")] + #[command(visible_alias = "v")] VerifyContract(VerifyArgs), /// Check verification status on Etherscan. - #[clap(visible_alias = "vc")] + #[command(visible_alias = "vc")] VerifyCheck(VerifyCheckArgs), /// Deploy a smart contract. - #[clap(visible_alias = "c")] + #[command(visible_alias = "c")] Create(CreateArgs), /// Create a new Forge project. Init(InitArgs), /// Generate shell completions script. - #[clap(visible_alias = "com")] + #[command(visible_alias = "com")] Completions { - #[clap(value_enum)] + #[arg(value_enum)] shell: clap_complete::Shell, }, /// Generate Fig autocompletion spec. - #[clap(visible_alias = "fig")] + #[command(visible_alias = "fig")] GenerateFigSpec, /// Remove the build artifacts and cache directories. - #[clap(visible_alias = "cl")] + #[command(visible_alias = "cl")] Clean { /// The project's root path. /// /// By default root of the Git repository, if in one, /// or the current working directory. - #[clap(long, value_hint = ValueHint::DirPath, value_name = "PATH")] + #[arg(long, value_hint = ValueHint::DirPath, value_name = "PATH")] root: Option, }, @@ -130,26 +130,26 @@ pub enum ForgeSubcommand { Cache(CacheArgs), /// Create a snapshot of each test's gas usage. - #[clap(visible_alias = "s")] + #[command(visible_alias = "s")] Snapshot(snapshot::SnapshotArgs), /// Display the current config. - #[clap(visible_alias = "co")] + #[command(visible_alias = "co")] Config(config::ConfigArgs), /// Flatten a source file and all of its imports into one file. - #[clap(visible_alias = "f")] + #[command(visible_alias = "f")] Flatten(flatten::FlattenArgs), /// Format Solidity source files. Fmt(FmtArgs), /// Get specialized information about a smart contract. - #[clap(visible_alias = "in")] + #[command(visible_alias = "in")] Inspect(inspect::InspectArgs), /// Display a tree visualization of the project's dependency graph. - #[clap(visible_alias = "tr")] + #[command(visible_alias = "tr")] Tree(tree::TreeArgs), /// Detects usage of unsafe cheat codes in a project and its dependencies. @@ -159,9 +159,9 @@ pub enum ForgeSubcommand { Doc(DocArgs), /// Function selector utilities - #[clap(visible_alias = "se")] + #[command(visible_alias = "se")] Selectors { - #[clap(subcommand)] + #[command(subcommand)] command: SelectorsSubcommands, }, diff --git a/crates/wallets/src/multi_wallet.rs b/crates/wallets/src/multi_wallet.rs index d3d7c9618..d9673985a 100644 --- a/crates/wallets/src/multi_wallet.rs +++ b/crates/wallets/src/multi_wallet.rs @@ -89,10 +89,10 @@ macro_rules! create_hw_wallets { /// 6. Private Keys (interactively via secure prompt) /// 7. AWS KMS #[derive(Builder, Clone, Debug, Default, Serialize, Parser)] -#[clap(next_help_heading = "Wallet options", about = None, long_about = None)] +#[command(next_help_heading = "Wallet options", about = None, long_about = None)] pub struct MultiWalletOpts { /// The sender accounts. - #[clap( + #[arg( long, short = 'a', help_heading = "Wallet options - raw", @@ -106,7 +106,7 @@ pub struct MultiWalletOpts { /// Open an interactive prompt to enter your private key. /// /// Takes a value for the number of keys to enter. - #[clap( + #[arg( long, short, help_heading = "Wallet options - raw", @@ -116,12 +116,12 @@ pub struct MultiWalletOpts { pub interactives: u32, /// Use the provided private keys. - #[clap(long, help_heading = "Wallet options - raw", value_name = "RAW_PRIVATE_KEYS")] + #[arg(long, help_heading = "Wallet options - raw", value_name = "RAW_PRIVATE_KEYS")] #[builder(default = "None")] pub private_keys: Option>, /// Use the provided private key. - #[clap( + #[arg( long, help_heading = "Wallet options - raw", conflicts_with = "private_keys", @@ -131,19 +131,19 @@ pub struct MultiWalletOpts { pub private_key: Option, /// Use the mnemonic phrases of mnemonic files at the specified paths. - #[clap(long, alias = "mnemonic-paths", help_heading = "Wallet options - raw")] + #[arg(long, alias = "mnemonic-paths", help_heading = "Wallet options - raw")] #[builder(default = "None")] pub mnemonics: Option>, /// Use a BIP39 passphrases for the mnemonic. - #[clap(long, help_heading = "Wallet options - raw", value_name = "PASSPHRASE")] + #[arg(long, help_heading = "Wallet options - raw", value_name = "PASSPHRASE")] #[builder(default = "None")] pub mnemonic_passphrases: Option>, /// The wallet derivation path. /// /// Works with both --mnemonic-path and hardware wallets. - #[clap( + #[arg( long = "mnemonic-derivation-paths", alias = "hd-paths", help_heading = "Wallet options - raw", @@ -155,7 +155,7 @@ pub struct MultiWalletOpts { /// Use the private key from the given mnemonic index. /// /// Can be used with --mnemonics, --ledger, --aws and --trezor. - #[clap( + #[arg( long, conflicts_with = "hd_paths", help_heading = "Wallet options - raw", @@ -165,7 +165,7 @@ pub struct MultiWalletOpts { pub mnemonic_indexes: Option>, /// Use the keystore in the given folder or file. - #[clap( + #[arg( long = "keystore", visible_alias = "keystores", help_heading = "Wallet options - keystore", @@ -176,7 +176,7 @@ pub struct MultiWalletOpts { pub keystore_paths: Option>, /// Use a keystore from the default keystores folder (~/.foundry/keystores) by its filename - #[clap( + #[arg( long = "account", visible_alias = "accounts", help_heading = "Wallet options - keystore", @@ -190,7 +190,7 @@ pub struct MultiWalletOpts { /// The keystore password. /// /// Used with --keystore. - #[clap( + #[arg( long = "password", help_heading = "Wallet options - keystore", requires = "keystore_paths", @@ -202,7 +202,7 @@ pub struct MultiWalletOpts { /// The keystore password file path. /// /// Used with --keystore. - #[clap( + #[arg( long = "password-file", help_heading = "Wallet options - keystore", requires = "keystore_paths", @@ -213,15 +213,15 @@ pub struct MultiWalletOpts { pub keystore_password_files: Option>, /// Use a Ledger hardware wallet. - #[clap(long, short, help_heading = "Wallet options - hardware wallet")] + #[arg(long, short, help_heading = "Wallet options - hardware wallet")] pub ledger: bool, /// Use a Trezor hardware wallet. - #[clap(long, short, help_heading = "Wallet options - hardware wallet")] + #[arg(long, short, help_heading = "Wallet options - hardware wallet")] pub trezor: bool, /// Use AWS Key Management Service. - #[clap(long, help_heading = "Wallet options - remote")] + #[arg(long, help_heading = "Wallet options - remote")] pub aws: bool, } diff --git a/crates/wallets/src/raw_wallet.rs b/crates/wallets/src/raw_wallet.rs index ccb1d6388..f8a9d447c 100644 --- a/crates/wallets/src/raw_wallet.rs +++ b/crates/wallets/src/raw_wallet.rs @@ -9,34 +9,34 @@ use serde::Serialize; /// 2. Private Key (interactively via secure prompt) /// 3. Mnemonic (via file path) #[derive(Clone, Debug, Default, Serialize, Parser)] -#[clap(next_help_heading = "Wallet options - raw", about = None, long_about = None)] +#[command(next_help_heading = "Wallet options - raw", about = None, long_about = None)] pub struct RawWalletOpts { /// Open an interactive prompt to enter your private key. - #[clap(long, short)] + #[arg(long, short)] pub interactive: bool, /// Use the provided private key. - #[clap(long, value_name = "RAW_PRIVATE_KEY")] + #[arg(long, value_name = "RAW_PRIVATE_KEY")] pub private_key: Option, /// Use the mnemonic phrase of mnemonic file at the specified path. - #[clap(long, alias = "mnemonic-path")] + #[arg(long, alias = "mnemonic-path")] pub mnemonic: Option, /// Use a BIP39 passphrase for the mnemonic. - #[clap(long, value_name = "PASSPHRASE")] + #[arg(long, value_name = "PASSPHRASE")] pub mnemonic_passphrase: Option, /// The wallet derivation path. /// /// Works with both --mnemonic-path and hardware wallets. - #[clap(long = "mnemonic-derivation-path", alias = "hd-path", value_name = "PATH")] + #[arg(long = "mnemonic-derivation-path", alias = "hd-path", value_name = "PATH")] pub hd_path: Option, /// Use the private key from the given mnemonic index. /// /// Used with --mnemonic-path. - #[clap(long, conflicts_with = "hd_path", default_value_t = 0, value_name = "INDEX")] + #[arg(long, conflicts_with = "hd_path", default_value_t = 0, value_name = "INDEX")] pub mnemonic_index: u32, } diff --git a/crates/wallets/src/wallet.rs b/crates/wallets/src/wallet.rs index 0cb06980d..cd7359f2e 100644 --- a/crates/wallets/src/wallet.rs +++ b/crates/wallets/src/wallet.rs @@ -13,10 +13,10 @@ use serde::Serialize; /// 4. Keystore (via file path) /// 5. AWS KMS #[derive(Clone, Debug, Default, Serialize, Parser)] -#[clap(next_help_heading = "Wallet options", about = None, long_about = None)] +#[command(next_help_heading = "Wallet options", about = None, long_about = None)] pub struct WalletOpts { /// The sender account. - #[clap( + #[arg( long, short, value_name = "ADDRESS", @@ -25,11 +25,11 @@ pub struct WalletOpts { )] pub from: Option
, - #[clap(flatten)] + #[command(flatten)] pub raw: RawWalletOpts, /// Use the keystore in the given folder or file. - #[clap( + #[arg( long = "keystore", help_heading = "Wallet options - keystore", value_name = "PATH", @@ -38,7 +38,7 @@ pub struct WalletOpts { pub keystore_path: Option, /// Use a keystore from the default keystores folder (~/.foundry/keystores) by its filename - #[clap( + #[arg( long = "account", help_heading = "Wallet options - keystore", value_name = "ACCOUNT_NAME", @@ -50,7 +50,7 @@ pub struct WalletOpts { /// The keystore password. /// /// Used with --keystore. - #[clap( + #[arg( long = "password", help_heading = "Wallet options - keystore", requires = "keystore_path", @@ -61,7 +61,7 @@ pub struct WalletOpts { /// The keystore password file path. /// /// Used with --keystore. - #[clap( + #[arg( long = "password-file", help_heading = "Wallet options - keystore", requires = "keystore_path", @@ -71,15 +71,15 @@ pub struct WalletOpts { pub keystore_password_file: Option, /// Use a Ledger hardware wallet. - #[clap(long, short, help_heading = "Wallet options - hardware wallet")] + #[arg(long, short, help_heading = "Wallet options - hardware wallet")] pub ledger: bool, /// Use a Trezor hardware wallet. - #[clap(long, short, help_heading = "Wallet options - hardware wallet")] + #[arg(long, short, help_heading = "Wallet options - hardware wallet")] pub trezor: bool, /// Use AWS Key Management Service. - #[clap(long, help_heading = "Wallet options - AWS KMS")] + #[arg(long, help_heading = "Wallet options - AWS KMS")] pub aws: bool, } From 5c171f78b5c025f489c809036f69e7883c776bc7 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 29 Feb 2024 16:40:16 +0200 Subject: [PATCH 012/430] fix: use ArtifactId.source instead of .path (#7271) --- crates/common/src/compile.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index 459ae516e..1132e99bb 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -289,7 +289,7 @@ impl ContractSources { let mut sources = ContractSources::default(); for (id, artifact) in output.artifact_ids() { if let Some(file_id) = artifact.id { - let abs_path = root.join(&id.path); + let abs_path = root.join(&id.source); let source_code = std::fs::read_to_string(abs_path).wrap_err_with(|| { format!("failed to read artifact source file for `{}`", id.identifier()) })?; From 4a91072e326126cd852b9c43f577e98c8e13f84f Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 29 Feb 2024 20:25:53 +0400 Subject: [PATCH 013/430] feat(anvil): expose --disable-default-create2-deployer CLI flag (#7282) --- crates/anvil/src/cmd.rs | 5 +++++ crates/anvil/src/config.rs | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index 070a97481..fca14e75d 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -229,6 +229,7 @@ impl NodeArgs { .with_init_state(self.load_state.or_else(|| self.state.and_then(|s| s.state))) .with_transaction_block_keeper(self.transaction_block_keeper) .with_optimism(self.evm_opts.optimism) + .with_disable_default_create2_deployer(self.evm_opts.disable_default_create2_deployer) } fn account_generator(&self) -> AccountGenerator { @@ -493,6 +494,10 @@ pub struct AnvilEvmArgs { /// Run an Optimism chain #[arg(long, visible_alias = "optimism")] pub optimism: bool, + + /// Disable the default create2 deployer + #[arg(long, visible_alias = "no-create2")] + pub disable_default_create2_deployer: bool, } /// Resolves an alias passed as fork-url to the matching url defined in the rpc_endpoints section diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 5f471e32f..32db9e25f 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -796,6 +796,13 @@ impl NodeConfig { self } + /// Sets whether to disable the default create2 deployer + #[must_use] + pub fn with_disable_default_create2_deployer(mut self, yes: bool) -> Self { + self.disable_default_create2_deployer = yes; + self + } + /// Configures everything related to env, backend and database and returns the /// [Backend](mem::Backend) /// From b671576e738e1dc938ddee50f1fcbd730e381d67 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 1 Mar 2024 10:14:00 +0200 Subject: [PATCH 014/430] chore(forge): simplify run_tests, reduce parallel tasks (#7283) --- crates/forge/src/runner.rs | 90 ++++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 43 deletions(-) diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 356248a61..f94da089c 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -253,58 +253,62 @@ impl<'a> ContractRunner<'a> { ) } - let functions: Vec<_> = self.contract.functions().collect(); - let mut test_results = functions - .par_iter() - .filter(|&&func| func.is_test() && filter.matches_test(&func.signature())) - .map(|&func| { - let should_fail = func.is_test_fail(); - let res = if func.is_fuzz_test() { - 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.clone(), *fuzz_config) - } else { - self.run_test(func, should_fail, setup.clone()) - }; - (func.signature(), res) - }) - .collect::>(); + // Filter out functions sequentially since it's very fast and there is no need to do it + // in parallel. + let find_timer = Instant::now(); + let functions = self + .contract + .functions() + .filter(|func| func.is_test() || func.is_invariant_test()) + .map(|func| (func.signature(), func)) + .filter(|(sig, _func)| filter.matches_test(sig)) + .collect::>(); + let find_time = find_timer.elapsed(); + debug!( + "Found {} test functions out of {} in {:?}", + functions.len(), + self.contract.functions().count(), + find_time, + ); - if has_invariants { - let identified_contracts = load_contracts(setup.traces.clone(), known_contracts); - let results: Vec<_> = functions - .par_iter() - .filter(|&&func| func.is_invariant_test() && filter.matches_test(&func.signature())) - .map(|&func| { + let identified_contracts = + has_invariants.then(|| load_contracts(setup.traces.clone(), known_contracts)); + let test_results = functions + .into_par_iter() + .map(|(sig, func)| { + let setup = setup.clone(); + let should_fail = func.is_test_fail(); + let res = if func.is_invariant_test() { let runner = test_options.invariant_runner(self.name, &func.name); let invariant_config = test_options.invariant_config(self.name, &func.name); - let res = self.run_invariant_test( + self.run_invariant_test( runner, - setup.clone(), + setup, *invariant_config, func, known_contracts, - &identified_contracts, - ); - (func.signature(), res) - }) - .collect(); - test_results.extend(results); - } + identified_contracts.as_ref().unwrap(), + ) + } else if func.is_fuzz_test() { + 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) + } else { + self.run_test(func, should_fail, setup) + }; + (sig, res) + }) + .collect::>(); let duration = start.elapsed(); - if !test_results.is_empty() { - let successful = - test_results.iter().filter(|(_, tst)| tst.status == TestStatus::Success).count(); - info!( - duration = ?duration, - "done. {}/{} successful", - successful, - test_results.len() - ); - } - - SuiteResult::new(duration, test_results, warnings) + let suite_result = SuiteResult::new(duration, test_results, warnings); + info!( + duration=?suite_result.duration, + "done. {}/{} successful", + suite_result.passed(), + suite_result.test_results.len() + ); + suite_result } /// Runs a single test From e57e82ce569a2d8abe9f1419ace9ec1f137b56ce Mon Sep 17 00:00:00 2001 From: Alex Y Date: Fri, 1 Mar 2024 01:37:23 -0800 Subject: [PATCH 015/430] feat: `cast mktx` (#7056) * feat: `cast mktx` * refactor: similar code in `cast send` and `cast mktx` * update clap --------- Co-authored-by: Matthias Seitz --- crates/cast/bin/cmd/mktx.rs | 109 ++++++++++++++++++++++++++++++++++ crates/cast/bin/cmd/mod.rs | 1 + crates/cast/bin/cmd/send.rs | 62 +++++-------------- crates/cast/bin/main.rs | 2 + crates/cast/bin/opts.rs | 7 ++- crates/cast/bin/tx.rs | 73 +++++++++++++++++++++++ crates/cast/src/lib.rs | 4 +- crates/cast/tests/cli/main.rs | 83 ++++++++++++++++++++++++++ 8 files changed, 290 insertions(+), 51 deletions(-) create mode 100644 crates/cast/bin/cmd/mktx.rs create mode 100644 crates/cast/bin/tx.rs diff --git a/crates/cast/bin/cmd/mktx.rs b/crates/cast/bin/cmd/mktx.rs new file mode 100644 index 000000000..dc7f78461 --- /dev/null +++ b/crates/cast/bin/cmd/mktx.rs @@ -0,0 +1,109 @@ +use crate::tx; +use clap::Parser; +use ethers_core::types::NameOrAddress; +use ethers_middleware::MiddlewareBuilder; +use ethers_providers::Middleware; +use ethers_signers::Signer; +use eyre::Result; +use foundry_cli::{ + opts::{EthereumOpts, TransactionOpts}, + utils, +}; +use foundry_common::types::ToAlloy; +use foundry_config::Config; +use std::str::FromStr; + +/// CLI arguments for `cast mktx`. +#[derive(Debug, Parser)] +pub struct MakeTxArgs { + /// The destination of the transaction. + /// + /// If not provided, you must use `cast mktx --create`. + #[arg(value_parser = NameOrAddress::from_str)] + to: Option, + + /// The signature of the function to call. + sig: Option, + + /// The arguments of the function to call. + args: Vec, + + /// Reuse the latest nonce for the sender account. + #[arg(long, conflicts_with = "nonce")] + resend: bool, + + #[command(subcommand)] + command: Option, + + #[command(flatten)] + tx: TransactionOpts, + + #[command(flatten)] + eth: EthereumOpts, +} + +#[derive(Debug, Parser)] +pub enum MakeTxSubcommands { + /// Use to deploy raw contract bytecode. + #[clap(name = "--create")] + Create { + /// The initialization bytecode of the contract to deploy. + code: String, + + /// The signature of the constructor. + sig: Option, + + /// The constructor arguments. + args: Vec, + }, +} + +impl MakeTxArgs { + pub async fn run(self) -> Result<()> { + let MakeTxArgs { to, mut sig, mut args, resend, command, mut tx, eth } = self; + + let code = if let Some(MakeTxSubcommands::Create { + code, + sig: constructor_sig, + args: constructor_args, + }) = command + { + sig = constructor_sig; + args = constructor_args; + Some(code) + } else { + None + }; + + tx::validate_to_address(&code, &to)?; + + let config = Config::from(ð); + let provider = utils::get_provider(&config)?; + let chain = utils::get_chain(config.chain, &provider).await?; + let api_key = config.get_etherscan_api_key(Some(chain)); + + // Retrieve the signer, and bail if it can't be constructed. + let signer = eth.wallet.signer().await?; + let from = signer.address(); + + tx::validate_from_address(eth.wallet.from, from.to_alloy())?; + + if resend { + tx.nonce = Some(provider.get_transaction_count(from, None).await?.to_alloy()); + } + + let provider = provider.with_signer(signer); + + let (mut tx, _) = + tx::build_tx(&provider, from, to, code, sig, args, tx, chain, api_key).await?; + + // Fill nonce, gas limit, gas price, and max priority fee per gas if needed + provider.fill_transaction(&mut tx, None).await?; + + let signature = provider.sign_transaction(&tx, from).await?; + let signed_tx = tx.rlp_signed(&signature); + println!("{signed_tx}"); + + Ok(()) + } +} diff --git a/crates/cast/bin/cmd/mod.rs b/crates/cast/bin/cmd/mod.rs index bd1c8ddf6..6c9044174 100644 --- a/crates/cast/bin/cmd/mod.rs +++ b/crates/cast/bin/cmd/mod.rs @@ -13,6 +13,7 @@ pub mod estimate; pub mod find_block; pub mod interface; pub mod logs; +pub mod mktx; pub mod rpc; pub mod run; pub mod send; diff --git a/crates/cast/bin/cmd/send.rs b/crates/cast/bin/cmd/send.rs index 22366483c..092d759eb 100644 --- a/crates/cast/bin/cmd/send.rs +++ b/crates/cast/bin/cmd/send.rs @@ -1,4 +1,5 @@ -use cast::{Cast, TxBuilder}; +use crate::tx; +use cast::Cast; use clap::Parser; use ethers_core::types::NameOrAddress; use ethers_middleware::SignerMiddleware; @@ -82,7 +83,7 @@ impl SendTxArgs { let SendTxArgs { eth, to, - sig, + mut sig, cast_async, mut args, mut tx, @@ -93,24 +94,20 @@ impl SendTxArgs { unlocked, } = self; - let mut sig = sig.unwrap_or_default(); let code = if let Some(SendTxSubcommands::Create { code, sig: constructor_sig, args: constructor_args, }) = command { - sig = constructor_sig.unwrap_or_default(); + sig = constructor_sig; args = constructor_args; Some(code) } else { None }; - // ensure mandatory fields are provided - if code.is_none() && to.is_none() { - eyre::bail!("Must specify a recipient address or contract code to deploy"); - } + tx::validate_to_address(&code, &to)?; let config = Config::from(ð); let provider = utils::get_provider(&config)?; @@ -155,7 +152,8 @@ impl SendTxArgs { config.sender.to_ethers(), to, code, - (sig, args), + sig, + args, tx, chain, api_key, @@ -173,19 +171,7 @@ impl SendTxArgs { let signer = eth.wallet.signer().await?; let from = signer.address(); - // prevent misconfigured hwlib from sending a transaction that defies - // user-specified --from - if let Some(specified_from) = eth.wallet.from { - if specified_from != from.to_alloy() { - eyre::bail!( - "\ -The specified sender via CLI/env vars does not match the sender configured via -the hardware wallet's HD Path. -Please use the `--hd-path ` parameter to specify the BIP32 Path which -corresponds to the sender, or let foundry automatically detect it by not specifying any sender address." - ) - } - } + tx::validate_from_address(eth.wallet.from, from.to_alloy())?; if resend { tx.nonce = Some(provider.get_transaction_count(from, None).await?.to_alloy()); @@ -198,7 +184,8 @@ corresponds to the sender, or let foundry automatically detect it by not specify from, to, code, - (sig, args), + sig, + args, tx, chain, api_key, @@ -217,7 +204,8 @@ async fn cast_send, T: Into from: F, to: Option, code: Option, - args: (String, Vec), + sig: Option, + args: Vec, tx: TransactionOpts, chain: Chain, etherscan_api_key: Option, @@ -228,30 +216,8 @@ async fn cast_send, T: Into where M::Error: 'static, { - let (sig, params) = args; - let params = if !sig.is_empty() { Some((&sig[..], params)) } else { None }; - let mut builder = TxBuilder::new(&provider, from, to, chain, tx.legacy).await?; - builder - .etherscan_api_key(etherscan_api_key) - .gas(tx.gas_limit) - .gas_price(tx.gas_price) - .priority_gas_price(tx.priority_gas_price) - .value(tx.value) - .nonce(tx.nonce); - - if let Some(code) = code { - let mut data = hex::decode(code)?; - - if let Some((sig, args)) = params { - let (mut sigdata, _) = builder.create_args(sig, args).await?; - data.append(&mut sigdata); - } - - builder.set_data(data); - } else { - builder.args(params).await?; - }; - let builder_output = builder.build(); + let builder_output = + tx::build_tx(&provider, from, to, code, sig, args, tx, chain, etherscan_api_key).await?; let cast = Cast::new(provider); diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index 93c686cb3..9ea81369e 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -26,6 +26,7 @@ use std::time::Instant; pub mod cmd; pub mod opts; +pub mod tx; use opts::{Cast as Opts, CastSubcommand, ToBaseArgs}; @@ -366,6 +367,7 @@ async fn main() -> Result<()> { // Calls & transactions CastSubcommand::Call(cmd) => cmd.run().await?, CastSubcommand::Estimate(cmd) => cmd.run().await?, + CastSubcommand::MakeTx(cmd) => cmd.run().await?, CastSubcommand::PublishTx { raw_tx, cast_async, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; diff --git a/crates/cast/bin/opts.rs b/crates/cast/bin/opts.rs index bfb24b01b..5a1af1fdc 100644 --- a/crates/cast/bin/opts.rs +++ b/crates/cast/bin/opts.rs @@ -1,7 +1,8 @@ use crate::cmd::{ access_list::AccessListArgs, bind::BindArgs, call::CallArgs, create2::Create2Args, estimate::EstimateArgs, find_block::FindBlockArgs, interface::InterfaceArgs, logs::LogsArgs, - rpc::RpcArgs, run::RunArgs, send::SendTxArgs, storage::StorageArgs, wallet::WalletSubcommands, + mktx::MakeTxArgs, rpc::RpcArgs, run::RunArgs, send::SendTxArgs, storage::StorageArgs, + wallet::WalletSubcommands, }; use alloy_primitives::{Address, B256, U256}; use clap::{Parser, Subcommand, ValueHint}; @@ -381,6 +382,10 @@ pub enum CastSubcommand { bytecode: String, }, + /// Build and sign a transaction. + #[command(name = "mktx", visible_alias = "m")] + MakeTx(MakeTxArgs), + /// Calculate the ENS namehash of a name. #[command(visible_aliases = &["na", "nh"])] Namehash { name: Option }, diff --git a/crates/cast/bin/tx.rs b/crates/cast/bin/tx.rs new file mode 100644 index 000000000..cd155de01 --- /dev/null +++ b/crates/cast/bin/tx.rs @@ -0,0 +1,73 @@ +use alloy_primitives::Address; +use cast::{TxBuilder, TxBuilderOutput}; +use ethers_core::types::NameOrAddress; +use ethers_providers::Middleware; +use eyre::Result; +use foundry_cli::opts::TransactionOpts; +use foundry_config::Chain; + +/// Prevents a misconfigured hwlib from sending a transaction that defies user-specified --from +pub fn validate_from_address( + specified_from: Option
, + signer_address: Address, +) -> Result<()> { + if let Some(specified_from) = specified_from { + if specified_from != signer_address { + eyre::bail!( + "\ +The specified sender via CLI/env vars does not match the sender configured via +the hardware wallet's HD Path. +Please use the `--hd-path ` parameter to specify the BIP32 Path which +corresponds to the sender, or let foundry automatically detect it by not specifying any sender address." + ) + } + } + Ok(()) +} + +/// Ensures the transaction is either a contract deployment or a recipient address is specified +pub fn validate_to_address(code: &Option, to: &Option) -> Result<()> { + if code.is_none() && to.is_none() { + eyre::bail!("Must specify a recipient address or contract code to deploy"); + } + Ok(()) +} + +#[allow(clippy::too_many_arguments)] +pub async fn build_tx, T: Into>( + provider: &M, + from: F, + to: Option, + code: Option, + sig: Option, + args: Vec, + tx: TransactionOpts, + chain: impl Into, + etherscan_api_key: Option, +) -> Result { + let mut builder = TxBuilder::new(provider, from, to, chain, tx.legacy).await?; + builder + .etherscan_api_key(etherscan_api_key) + .gas(tx.gas_limit) + .gas_price(tx.gas_price) + .priority_gas_price(tx.priority_gas_price) + .value(tx.value) + .nonce(tx.nonce); + + let params = sig.as_deref().map(|sig| (sig, args)); + if let Some(code) = code { + let mut data = hex::decode(code)?; + + if let Some((sig, args)) = params { + let (mut sigdata, _) = builder.create_args(sig, args).await?; + data.append(&mut sigdata); + } + + builder.set_data(data); + } else { + builder.args(params).await?; + } + + let builder_output = builder.build(); + Ok(builder_output) +} diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index d8c2e995c..da7453f52 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -34,7 +34,7 @@ use std::{ sync::atomic::{AtomicBool, Ordering}, }; use tokio::signal::ctrl_c; -use tx::{TxBuilderOutput, TxBuilderPeekOutput}; +use tx::TxBuilderPeekOutput; use foundry_common::abi::encode_function_args_packed; pub use foundry_evm::*; @@ -43,7 +43,7 @@ pub use rusoto_core::{ request::HttpClient as AwsHttpClient, Client as AwsClient, }; pub use rusoto_kms::KmsClient; -pub use tx::TxBuilder; +pub use tx::{TxBuilder, TxBuilderOutput}; pub mod base; pub mod errors; diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 91c8b6109..41cef5e61 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -514,6 +514,89 @@ casttest!(logs_sig_2, |_prj, cmd| { ); }); +casttest!(mktx, |_prj, cmd| { + cmd.args([ + "mktx", + "--private-key", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "--chain", + "1", + "--nonce", + "0", + "--value", + "100", + "--gas-limit", + "21000", + "--gas-price", + "10000000000", + "--priority-gas-price", + "1000000000", + "0x0000000000000000000000000000000000000001", + ]); + let output = cmd.stdout_lossy(); + assert_eq!( + output.trim(), + "0x02f86b0180843b9aca008502540be4008252089400000000000000000000000000000000000000016480c001a070d55e79ed3ac9fc8f51e78eb91fd054720d943d66633f2eb1bc960f0126b0eca052eda05a792680de3181e49bab4093541f75b49d1ecbe443077b3660c836016a" + ); +}); + +// ensure recipient or code is required +casttest!(mktx_requires_to, |_prj, cmd| { + cmd.args([ + "mktx", + "--private-key", + "0x0000000000000000000000000000000000000000000000000000000000000001", + ]); + let output = cmd.stderr_lossy(); + assert_eq!( + output.trim(), + "Error: \nMust specify a recipient address or contract code to deploy" + ); +}); + +casttest!(mktx_signer_from_mismatch, |_prj, cmd| { + cmd.args([ + "mktx", + "--private-key", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "--from", + "0x0000000000000000000000000000000000000001", + "--chain", + "1", + "0x0000000000000000000000000000000000000001", + ]); + let output = cmd.stderr_lossy(); + assert!( + output.contains("The specified sender via CLI/env vars does not match the sender configured via\nthe hardware wallet's HD Path.") + ); +}); + +casttest!(mktx_signer_from_match, |_prj, cmd| { + cmd.args([ + "mktx", + "--private-key", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "--from", + "0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf", + "--chain", + "1", + "--nonce", + "0", + "--gas-limit", + "21000", + "--gas-price", + "10000000000", + "--priority-gas-price", + "1000000000", + "0x0000000000000000000000000000000000000001", + ]); + let output = cmd.stdout_lossy(); + assert_eq!( + output.trim(), + "0x02f86b0180843b9aca008502540be4008252089400000000000000000000000000000000000000018080c001a0cce9a61187b5d18a89ecd27ec675e3b3f10d37f165627ef89a15a7fe76395ce8a07537f5bffb358ffbef22cda84b1c92f7211723f9e09ae037e81686805d3e5505" + ); +}); + // tests that the raw encoded transaction is returned casttest!(tx_raw, |_prj, cmd| { let rpc = next_http_rpc_endpoint(); From 9fff5c20c08b235fd6587362c9ff639c093f68c3 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 1 Mar 2024 13:36:40 +0200 Subject: [PATCH 016/430] refactor(forge/test): cache initial executor, don't clone options (#7286) * refactor(forge/test): cache initial executor, don't clone options * chore: clippy * fix: don't share Db and state * fix: don't even share the builder (???) * fix: fuzz tests must also start with test * chore: simplify filtering * fix: filter * fix: filter 2 * chore: comment, logs --- crates/common/src/compile.rs | 3 +- crates/evm/evm/src/executors/builder.rs | 2 +- crates/forge/bin/cmd/coverage.rs | 4 +- crates/forge/bin/cmd/test/mod.rs | 14 +-- crates/forge/src/lib.rs | 14 +-- crates/forge/src/multi_runner.rs | 161 +++++++++++------------- crates/forge/src/runner.rs | 24 ++-- crates/forge/tests/it/cheats.rs | 18 +-- crates/forge/tests/it/config.rs | 52 ++------ crates/forge/tests/it/core.rs | 38 +++--- crates/forge/tests/it/fork.rs | 16 +-- crates/forge/tests/it/fuzz.rs | 57 ++++----- crates/forge/tests/it/inline.rs | 13 +- crates/forge/tests/it/invariant.rs | 120 +++++------------- crates/forge/tests/it/test_helpers.rs | 41 +++++- 15 files changed, 242 insertions(+), 335 deletions(-) diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index 1132e99bb..935e098ee 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -19,6 +19,7 @@ use std::{ path::{Path, PathBuf}, result, str::FromStr, + time::Instant, }; /// Builder type to configure how to compile a project. @@ -185,7 +186,7 @@ impl ProjectCompiler { let output = foundry_compilers::report::with_scoped(&reporter, || { tracing::debug!("compiling project"); - let timer = std::time::Instant::now(); + let timer = Instant::now(); let r = f(); let elapsed = timer.elapsed(); diff --git a/crates/evm/evm/src/executors/builder.rs b/crates/evm/evm/src/executors/builder.rs index ab9bd7629..d7cca61a7 100644 --- a/crates/evm/evm/src/executors/builder.rs +++ b/crates/evm/evm/src/executors/builder.rs @@ -10,7 +10,7 @@ use revm::primitives::{Env, SpecId}; /// /// [`Cheatcodes`]: super::inspector::Cheatcodes /// [`InspectorStack`]: super::inspector::InspectorStack -#[derive(Debug)] +#[derive(Clone, Debug)] #[must_use = "builders do nothing unless you call `build` on them"] pub struct ExecutorBuilder { /// The configuration used to build an [InspectorStack]. diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index 0c1232d7a..718d10790 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -313,9 +313,7 @@ impl CoverageArgs { let known_contracts = runner.known_contracts.clone(); let filter = self.filter; let (tx, rx) = channel::<(String, SuiteResult)>(); - let handle = tokio::task::spawn(async move { - runner.test(&filter, tx, runner.test_options.clone()).await - }); + let handle = tokio::task::spawn(async move { runner.test(&filter, tx).await }); // Add hit data to the coverage report let data = rx diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index b81a5b362..0d49b4640 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -200,7 +200,7 @@ impl TestArgs { .sender(evm_opts.sender) .with_fork(evm_opts.get_fork(&config, env.clone())) .with_cheats_config(CheatsConfig::new(&config, evm_opts.clone(), None)) - .with_test_options(test_options.clone()) + .with_test_options(test_options) .enable_isolation(evm_opts.isolate) .build(project_root, output, env, evm_opts)?; @@ -215,7 +215,7 @@ impl TestArgs { *test_pattern = Some(debug_test_pattern.clone()); } - let outcome = self.run_tests(runner, config, verbosity, &filter, test_options).await?; + let outcome = self.run_tests(runner, config, verbosity, &filter).await?; if should_debug { // There is only one test. @@ -250,7 +250,6 @@ impl TestArgs { config: Config, verbosity: u8, filter: &ProjectPathsAwareFilter, - test_options: TestOptions, ) -> eyre::Result { if self.list { return list(runner, filter, self.json); @@ -258,7 +257,7 @@ impl TestArgs { trace!(target: "forge::test", "running all tests"); - let num_filtered = runner.matching_test_function_count(filter); + let num_filtered = runner.matching_test_functions(filter).count(); if num_filtered == 0 { println!(); if filter.is_empty() { @@ -273,7 +272,8 @@ impl TestArgs { // Try to suggest a test when there's no match if let Some(test_pattern) = &filter.args().test_pattern { let test_name = test_pattern.as_str(); - let candidates = runner.get_tests(filter); + // Filter contracts but not test functions. + let candidates = runner.all_test_functions(filter).map(|f| &f.name); if let Some(suggestion) = utils::did_you_mean(test_name, candidates).pop() { println!("\nDid you mean `{suggestion}`?"); } @@ -289,7 +289,7 @@ impl TestArgs { } if self.json { - let results = runner.test_collect(filter, test_options).await; + let results = runner.test_collect(filter).await; println!("{}", serde_json::to_string(&results)?); return Ok(TestOutcome::new(results, self.allow_failure)); } @@ -305,7 +305,7 @@ impl TestArgs { let timer = Instant::now(); let handle = tokio::task::spawn({ let filter = filter.clone(); - async move { runner.test(&filter, tx, test_options).await } + async move { runner.test(&filter, tx).await } }); let mut gas_report = diff --git a/crates/forge/src/lib.rs b/crates/forge/src/lib.rs index 16343fc09..00481591e 100644 --- a/crates/forge/src/lib.rs +++ b/crates/forge/src/lib.rs @@ -1,13 +1,11 @@ #[macro_use] extern crate tracing; -use alloy_primitives::B256; use foundry_compilers::ProjectCompileOutput; use foundry_config::{ validate_profiles, Config, FuzzConfig, InlineConfig, InlineConfigError, InlineConfigParser, InvariantConfig, NatSpec, }; - use proptest::test_runner::{RngAlgorithm, TestRng, TestRunner}; use std::path::Path; @@ -146,20 +144,20 @@ impl TestOptions { pub fn fuzzer_with_cases(&self, cases: u32) -> TestRunner { // TODO: Add Options to modify the persistence - let cfg = proptest::test_runner::Config { + let config = proptest::test_runner::Config { failure_persistence: None, cases, max_global_rejects: self.fuzz.max_test_rejects, ..Default::default() }; - if let Some(ref fuzz_seed) = self.fuzz.seed { - trace!(target: "forge::test", "building deterministic fuzzer with seed {}", fuzz_seed); - let rng = TestRng::from_seed(RngAlgorithm::ChaCha, &B256::from(*fuzz_seed).0); - TestRunner::new_with_rng(cfg, rng) + 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(cfg) + TestRunner::new(config) } } } diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 699df0da0..ea6c79c98 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -26,6 +26,7 @@ use std::{ fmt::Debug, path::Path, sync::{mpsc, Arc}, + time::Instant, }; pub type DeployableContracts = BTreeMap)>; @@ -65,57 +66,47 @@ pub struct MultiContractRunner { } impl MultiContractRunner { - /// Returns the number of matching tests - pub fn matching_test_function_count(&self, filter: &dyn TestFilter) -> usize { - self.matching_test_functions(filter).count() + /// Returns an iterator over all contracts that match the filter. + pub fn matching_contracts<'a>( + &'a self, + filter: &'a dyn TestFilter, + ) -> impl Iterator))> { + self.contracts.iter().filter(|&(id, (abi, _, _))| matches_contract(id, abi, filter)) } - /// Returns all test functions matching the filter + /// Returns an iterator over all test functions that match the filter. pub fn matching_test_functions<'a>( &'a self, filter: &'a dyn TestFilter, ) -> impl Iterator { - self.contracts - .iter() - .filter(|(id, _)| filter.matches_path(&id.source) && filter.matches_contract(&id.name)) - .flat_map(|(_, (abi, _, _))| { - abi.functions().filter(|func| filter.matches_test(&func.signature())) - }) + self.matching_contracts(filter) + .flat_map(|(_, (abi, _, _))| abi.functions()) + .filter(|func| is_matching_test(func, filter)) } - /// Get an iterator over all test contract functions that matches the filter path and contract - /// name - fn filtered_tests<'a>(&'a self, filter: &'a dyn TestFilter) -> impl Iterator { + /// Returns an iterator over all test functions in contracts that match the filter. + pub fn all_test_functions<'a>( + &'a self, + filter: &'a dyn TestFilter, + ) -> impl Iterator { self.contracts .iter() .filter(|(id, _)| filter.matches_path(&id.source) && filter.matches_contract(&id.name)) .flat_map(|(_, (abi, _, _))| abi.functions()) - } - - /// Get all test names matching the filter - pub fn get_tests(&self, filter: &dyn TestFilter) -> Vec { - self.filtered_tests(filter) - .map(|func| func.name.clone()) - .filter(|name| name.is_test()) - .collect() + .filter(|func| func.is_test() || func.is_invariant_test()) } /// Returns all matching tests grouped by contract grouped by file (file -> (contract -> tests)) pub fn list(&self, filter: &dyn TestFilter) -> BTreeMap>> { - self.contracts - .iter() - .filter(|(id, _)| filter.matches_path(&id.source) && filter.matches_contract(&id.name)) - .filter(|(_, (abi, _, _))| abi.functions().any(|func| filter.matches_test(&func.name))) + self.matching_contracts(filter) .map(|(id, (abi, _, _))| { let source = id.source.as_path().display().to_string(); let name = id.name.clone(); let tests = abi .functions() - .filter(|func| func.name.is_test()) - .filter(|func| filter.matches_test(&func.signature())) + .filter(|func| is_matching_test(func, filter)) .map(|func| func.name.clone()) .collect::>(); - (source, name, tests) }) .fold(BTreeMap::new(), |mut acc, (source, name, tests)| { @@ -129,12 +120,8 @@ impl MultiContractRunner { /// The same as [`test`](Self::test), but returns the results instead of streaming them. /// /// Note that this method returns only when all tests have been executed. - pub async fn test_collect( - &mut self, - filter: &dyn TestFilter, - test_options: TestOptions, - ) -> BTreeMap { - self.test_iter(filter, test_options).await.collect() + pub async fn test_collect(&mut self, filter: &dyn TestFilter) -> BTreeMap { + self.test_iter(filter).await.collect() } /// Executes _all_ tests that match the given `filter`. @@ -145,10 +132,9 @@ impl MultiContractRunner { pub async fn test_iter( &mut self, filter: &dyn TestFilter, - test_options: TestOptions, ) -> impl Iterator { let (tx, rx) = mpsc::channel(); - self.test(filter, tx, test_options).await; + self.test(filter, tx).await; rx.into_iter() } @@ -158,50 +144,40 @@ impl MultiContractRunner { /// before executing all contracts and their tests in _parallel_. /// /// Each Executor gets its own instance of the `Backend`. - pub async fn test( - &mut self, - filter: &dyn TestFilter, - stream_result: mpsc::Sender<(String, SuiteResult)>, - test_options: TestOptions, - ) { + pub async fn test(&mut self, filter: &dyn TestFilter, tx: mpsc::Sender<(String, SuiteResult)>) { trace!("running all tests"); - // the db backend that serves all the data, each contract gets its own instance + // The DB backend that serves all the data. let db = Backend::spawn(self.fork.take()).await; - - self.contracts - .par_iter() - .filter(|(id, _)| filter.matches_path(&id.source) && filter.matches_contract(&id.name)) - .filter(|(_, (abi, _, _))| abi.functions().any(|func| filter.matches_test(&func.name))) - .for_each_with(stream_result, |stream_result, (id, (abi, deploy_code, libs))| { - let executor = ExecutorBuilder::new() - .inspectors(|stack| { - stack - .cheatcodes(self.cheats_config.clone()) - .trace(self.evm_opts.verbosity >= 3 || self.debug) - .debug(self.debug) - .coverage(self.coverage) - .enable_isolation(self.isolation) - }) - .spec(self.evm_spec) - .gas_limit(self.evm_opts.gas_limit()) - .build(self.env.clone(), db.clone()); - let identifier = id.identifier(); - trace!(contract=%identifier, "start executing all tests in contract"); - - let result = self.run_tests( - &identifier, - abi, - executor, - deploy_code.clone(), - libs, - filter, - test_options.clone(), - ); - trace!(contract=?identifier, "executed all tests in contract"); - - let _ = stream_result.send((identifier, result)); + let executor = ExecutorBuilder::new() + .inspectors(|stack| { + stack + .cheatcodes(self.cheats_config.clone()) + .trace(self.evm_opts.verbosity >= 3 || self.debug) + .debug(self.debug) + .coverage(self.coverage) + .enable_isolation(self.isolation) }) + .spec(self.evm_spec) + .gas_limit(self.evm_opts.gas_limit()) + .build(self.env.clone(), db); + + let find_timer = Instant::now(); + let contracts = self.matching_contracts(filter).collect::>(); + let find_time = find_timer.elapsed(); + debug!( + "Found {} test contracts out of {} in {:?}", + contracts.len(), + self.contracts.len(), + find_time, + ); + + contracts.par_iter().for_each_with(tx, |tx, &(id, (abi, deploy_code, libs))| { + let identifier = id.identifier(); + let executor = executor.clone(); + let result = self.run_tests(&identifier, abi, executor, deploy_code, libs, filter); + let _ = tx.send((identifier, result)); + }) } #[allow(clippy::too_many_arguments)] @@ -210,20 +186,17 @@ impl MultiContractRunner { name: &str, contract: &JsonAbi, executor: Executor, - deploy_code: Bytes, + deploy_code: &Bytes, libs: &[Bytes], filter: &dyn TestFilter, - test_options: TestOptions, ) -> SuiteResult { - let span = info_span!("run_tests"); - if !span.is_disabled() { - if enabled!(tracing::Level::TRACE) { - span.record("contract", name); - } else { - span.record("contract", get_contract_name(name)); - } + let mut span_name = name; + if !enabled!(tracing::Level::TRACE) { + span_name = get_contract_name(span_name); } - let _guard = span.enter(); + let _guard = info_span!("run_tests", name = span_name).entered(); + + debug!("start executing all tests in contract"); let runner = ContractRunner::new( name, @@ -236,7 +209,11 @@ impl MultiContractRunner { libs, self.debug, ); - runner.run_tests(filter, test_options, Some(&self.known_contracts)) + let r = runner.run_tests(filter, &self.test_options, Some(&self.known_contracts)); + + debug!(duration=?r.duration, "executed all tests in contract"); + + r } } @@ -395,3 +372,13 @@ impl MultiContractRunnerBuilder { }) } } + +fn matches_contract(id: &ArtifactId, abi: &JsonAbi, filter: &dyn TestFilter) -> bool { + (filter.matches_path(&id.source) && filter.matches_contract(&id.name)) && + abi.functions().any(|func| is_matching_test(func, filter)) +} + +/// Returns `true` if the function is a test function that matches the given filter. +pub(crate) fn is_matching_test(func: &Function, filter: &dyn TestFilter) -> bool { + (func.is_test() || func.is_invariant_test()) && filter.matches_test(&func.signature()) +} diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index f94da089c..ca3e83629 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -1,6 +1,7 @@ //! The Forge test runner. use crate::{ + multi_runner::is_matching_test, result::{SuiteResult, TestKind, TestResult, TestSetup, TestStatus}, TestFilter, TestOptions, }; @@ -40,7 +41,7 @@ pub struct ContractRunner<'a> { /// Library contracts to be deployed before the test contract pub predeploy_libs: &'a [Bytes], /// The deployed contract's code - pub code: Bytes, + pub code: &'a Bytes, /// The test contract's ABI pub contract: &'a JsonAbi, /// Revert decoder. Contains all known errors. @@ -59,7 +60,7 @@ impl<'a> ContractRunner<'a> { name: &'a str, executor: Executor, contract: &'a JsonAbi, - code: Bytes, + code: &'a Bytes, initial_balance: U256, sender: Option
, revert_decoder: &'a RevertDecoder, @@ -185,7 +186,7 @@ impl<'a> ContractRunner<'a> { pub fn run_tests( mut self, filter: &dyn TestFilter, - test_options: TestOptions, + test_options: &TestOptions, known_contracts: Option<&ContractsByArtifact>, ) -> SuiteResult { info!("starting tests"); @@ -259,9 +260,7 @@ impl<'a> ContractRunner<'a> { let functions = self .contract .functions() - .filter(|func| func.is_test() || func.is_invariant_test()) - .map(|func| (func.signature(), func)) - .filter(|(sig, _func)| filter.matches_test(sig)) + .filter(|func| is_matching_test(func, filter)) .collect::>(); let find_time = find_timer.elapsed(); debug!( @@ -274,8 +273,10 @@ impl<'a> ContractRunner<'a> { let identified_contracts = has_invariants.then(|| load_contracts(setup.traces.clone(), known_contracts)); let test_results = functions - .into_par_iter() - .map(|(sig, func)| { + .par_iter() + .map(|&func| { + let sig = func.signature(); + let setup = setup.clone(); let should_fail = func.is_test_fail(); let res = if func.is_invariant_test() { @@ -290,12 +291,15 @@ impl<'a> ContractRunner<'a> { identified_contracts.as_ref().unwrap(), ) } else if func.is_fuzz_test() { + debug_assert!(func.is_test()); 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) } else { + debug_assert!(func.is_test()); self.run_test(func, should_fail, setup) }; + (sig, res) }) .collect::>(); @@ -417,7 +421,7 @@ impl<'a> ContractRunner<'a> { // Record test execution time let duration = start.elapsed(); - debug!(?duration, gas, reverted, should_fail, success); + trace!(?duration, gas, reverted, should_fail, success); TestResult { status: match success { @@ -674,7 +678,7 @@ impl<'a> ContractRunner<'a> { // Record test execution time let duration = start.elapsed(); - debug!(?duration, success = %result.success); + trace!(?duration, success = %result.success); TestResult { status: match result.success { diff --git a/crates/forge/tests/it/cheats.rs b/crates/forge/tests/it/cheats.rs index 078361fab..2959014bc 100644 --- a/crates/forge/tests/it/cheats.rs +++ b/crates/forge/tests/it/cheats.rs @@ -10,15 +10,17 @@ use foundry_test_utils::Filter; /// Executes all cheat code tests but not fork cheat codes #[tokio::test(flavor = "multi_thread")] async fn test_cheats_local() { - let mut config = Config::with_root(PROJECT.root()); - config.fs_permissions = FsPermissions::new(vec![PathPermission::read_write("./")]); - let runner = runner_with_config(config); - let filter = + let mut filter = Filter::new(".*", ".*", &format!(".*cheats{RE_PATH_SEPARATOR}*")).exclude_paths("Fork"); - // on windows exclude ffi tests since no echo and file test that expect a certain file path - #[cfg(windows)] - let filter = filter.exclude_tests("(Ffi|File|Line|Root)"); + // Exclude FFI tests on Windows because no `echo`, and file tests that expect certain file paths + if cfg!(windows) { + filter = filter.exclude_tests("(Ffi|File|Line|Root)"); + } + + let mut config = Config::with_root(PROJECT.root()); + config.fs_permissions = FsPermissions::new(vec![PathPermission::read_write("./")]); + let runner = runner_with_config(config).await; - TestConfig::with_filter(runner.await, filter).run().await; + TestConfig::with_filter(runner, filter).run().await; } diff --git a/crates/forge/tests/it/config.rs b/crates/forge/tests/it/config.rs index 542f9a88e..c56a626c4 100644 --- a/crates/forge/tests/it/config.rs +++ b/crates/forge/tests/it/config.rs @@ -1,13 +1,12 @@ //! Test config. -use crate::test_helpers::{COMPILED, EVM_OPTS, PROJECT}; +use crate::test_helpers::{COMPILED, EVM_OPTS, PROJECT, TEST_OPTS}; use forge::{ result::{SuiteResult, TestStatus}, - MultiContractRunner, MultiContractRunnerBuilder, TestOptions, TestOptionsBuilder, + MultiContractRunner, MultiContractRunnerBuilder, }; use foundry_config::{ - fs_permissions::PathPermission, Config, FsPermissions, FuzzConfig, FuzzDictionaryConfig, - InvariantConfig, RpcEndpoint, RpcEndpoints, + fs_permissions::PathPermission, Config, FsPermissions, RpcEndpoint, RpcEndpoints, }; use foundry_evm::{ decode::decode_console_logs, @@ -25,7 +24,6 @@ pub struct TestConfig { pub runner: MultiContractRunner, pub should_fail: bool, pub filter: Filter, - pub opts: TestOptions, } impl TestConfig { @@ -39,7 +37,7 @@ impl TestConfig { pub fn with_filter(runner: MultiContractRunner, filter: Filter) -> Self { init_tracing(); - Self { runner, should_fail: false, filter, opts: test_opts() } + Self { runner, should_fail: false, filter } } pub fn evm_spec(mut self, spec: SpecId) -> Self { @@ -58,7 +56,7 @@ impl TestConfig { /// Executes the test runner pub async fn test(&mut self) -> BTreeMap { - self.runner.test_collect(&self.filter, self.opts.clone()).await + self.runner.test_collect(&self.filter).await } pub async fn run(&mut self) { @@ -110,41 +108,6 @@ impl TestConfig { } } -/// Returns the [`TestOptions`] used by the tests. -pub fn test_opts() -> 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, - }, - }) - .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_sequence: true, - shrink_run_limit: 2usize.pow(18u32), - preserve_state: false, - }) - .build(&COMPILED, &PROJECT.paths.root) - .expect("Config loaded") -} - pub fn manifest_root() -> &'static Path { let mut root = Path::new(env!("CARGO_MANIFEST_DIR")); // need to check here where we're executing the test from, if in `forge` we need to also allow @@ -158,7 +121,9 @@ pub fn manifest_root() -> &'static Path { /// Builds a base runner pub fn base_runner() -> MultiContractRunnerBuilder { init_tracing(); - MultiContractRunnerBuilder::default().sender(EVM_OPTS.sender) + MultiContractRunnerBuilder::default() + .sender(EVM_OPTS.sender) + .with_test_options(TEST_OPTS.clone()) } /// Builds a non-tracing runner @@ -178,7 +143,6 @@ pub async fn runner_with_config(mut config: Config) -> MultiContractRunner { let env = opts.evm_env().await.expect("could not instantiate fork environment"); let output = COMPILED.clone(); base_runner() - .with_test_options(test_opts()) .with_cheats_config(CheatsConfig::new(&config, opts.clone(), None)) .sender(config.sender) .build(root, output, env, opts.clone()) diff --git a/crates/forge/tests/it/core.rs b/crates/forge/tests/it/core.rs index 660ab0677..29ce68c9e 100644 --- a/crates/forge/tests/it/core.rs +++ b/crates/forge/tests/it/core.rs @@ -8,8 +8,9 @@ use std::{collections::BTreeMap, env}; #[tokio::test(flavor = "multi_thread")] async fn test_core() { + let filter = Filter::new(".*", ".*", ".*core"); let mut runner = runner().await; - let results = runner.test_collect(&Filter::new(".*", ".*", ".*core"), test_opts()).await; + let results = runner.test_collect(&filter).await; assert_multiple( &results, @@ -77,8 +78,9 @@ async fn test_core() { #[tokio::test(flavor = "multi_thread")] async fn test_linking() { + let filter = Filter::new(".*", ".*", ".*linking"); let mut runner = runner().await; - let results = runner.test_collect(&Filter::new(".*", ".*", ".*linking"), test_opts()).await; + let results = runner.test_collect(&filter).await; assert_multiple( &results, @@ -110,8 +112,9 @@ async fn test_linking() { #[tokio::test(flavor = "multi_thread")] async fn test_logs() { + let filter = Filter::new(".*", ".*", ".*logs"); let mut runner = runner().await; - let results = runner.test_collect(&Filter::new(".*", ".*", ".*logs"), test_opts()).await; + let results = runner.test_collect(&filter).await; assert_multiple( &results, @@ -670,38 +673,31 @@ async fn test_logs() { #[tokio::test(flavor = "multi_thread")] async fn test_env_vars() { - let mut runner = runner().await; - - // test `setEnv` first, and confirm that it can correctly set environment variables, - // so that we can use it in subsequent `env*` tests - let _ = runner.test_collect(&Filter::new("testSetEnv", ".*", ".*"), test_opts()).await; let env_var_key = "_foundryCheatcodeSetEnvTestKey"; let env_var_val = "_foundryCheatcodeSetEnvTestVal"; - let res = env::var(env_var_key); - assert!( - res.is_ok() && res.unwrap() == env_var_val, - "Test `testSetEnv` did not pass as expected. -Reason: `setEnv` failed to set an environment variable `{env_var_key}={env_var_val}`" - ); + env::remove_var(env_var_key); + + let filter = Filter::new("testSetEnv", ".*", ".*"); + let mut runner = runner().await; + let _ = runner.test_collect(&filter).await; + + assert_eq!(env::var(env_var_key).unwrap(), env_var_val); } #[tokio::test(flavor = "multi_thread")] async fn test_doesnt_run_abstract_contract() { + let filter = Filter::new(".*", ".*", ".*Abstract.t.sol".to_string().as_str()); let mut runner = runner().await; - let results = runner - .test_collect( - &Filter::new(".*", ".*", ".*Abstract.t.sol".to_string().as_str()), - test_opts(), - ) - .await; + let results = runner.test_collect(&filter).await; assert!(results.get("core/Abstract.t.sol:AbstractTestBase").is_none()); assert!(results.get("core/Abstract.t.sol:AbstractTest").is_some()); } #[tokio::test(flavor = "multi_thread")] async fn test_trace() { + let filter = Filter::new(".*", ".*", ".*trace"); let mut runner = tracing_runner().await; - let suite_result = runner.test_collect(&Filter::new(".*", ".*", ".*trace"), test_opts()).await; + let suite_result = runner.test_collect(&filter).await; // TODO: This trace test is very basic - it is probably a good candidate for snapshot // testing. diff --git a/crates/forge/tests/it/fork.rs b/crates/forge/tests/it/fork.rs index f34cc59bb..76a06e57b 100644 --- a/crates/forge/tests/it/fork.rs +++ b/crates/forge/tests/it/fork.rs @@ -11,17 +11,13 @@ use foundry_test_utils::Filter; /// Executes reverting fork test #[tokio::test(flavor = "multi_thread")] async fn test_cheats_fork_revert() { + let filter = Filter::new( + "testNonExistingContractRevert", + ".*", + &format!(".*cheats{RE_PATH_SEPARATOR}Fork"), + ); let mut runner = runner().await; - let suite_result = runner - .test_collect( - &Filter::new( - "testNonExistingContractRevert", - ".*", - &format!(".*cheats{RE_PATH_SEPARATOR}Fork"), - ), - test_opts(), - ) - .await; + let suite_result = runner.test_collect(&filter).await; assert_eq!(suite_result.len(), 1); for (_, SuiteResult { test_results, .. }) in suite_result { diff --git a/crates/forge/tests/it/fuzz.rs b/crates/forge/tests/it/fuzz.rs index 2eefa530d..011497f4c 100644 --- a/crates/forge/tests/it/fuzz.rs +++ b/crates/forge/tests/it/fuzz.rs @@ -8,16 +8,11 @@ use std::collections::BTreeMap; #[tokio::test(flavor = "multi_thread")] async fn test_fuzz() { + let filter = Filter::new(".*", ".*", ".*fuzz/") + .exclude_tests(r"invariantCounter|testIncrement\(address\)|testNeedle\(uint256\)|testSuccessChecker\(uint256\)|testSuccessChecker2\(int256\)|testSuccessChecker3\(uint32\)") + .exclude_paths("invariant"); let mut runner = runner().await; - - let suite_result = runner - .test_collect( - &Filter::new(".*", ".*", ".*fuzz/") - .exclude_tests(r"invariantCounter|testIncrement\(address\)|testNeedle\(uint256\)|testSuccessChecker\(uint256\)|testSuccessChecker2\(int256\)|testSuccessChecker3\(uint32\)") - .exclude_paths("invariant"), - test_opts(), - ) - .await; + let suite_result = runner.test_collect(&filter).await; assert!(!suite_result.is_empty()); @@ -27,15 +22,17 @@ async fn test_fuzz() { "testPositive(uint256)" | "testPositive(int256)" | "testSuccessfulFuzz(uint128,uint128)" | - "testToStringFuzz(bytes32)" => assert!( - result.status == TestStatus::Success, + "testToStringFuzz(bytes32)" => assert_eq!( + result.status, + TestStatus::Success, "Test {} did not pass as expected.\nReason: {:?}\nLogs:\n{}", test_name, result.reason, result.decoded_logs.join("\n") ), - _ => assert!( - result.status == TestStatus::Failure, + _ => assert_eq!( + result.status, + TestStatus::Failure, "Test {} did not fail as expected.\nReason: {:?}\nLogs:\n{}", test_name, result.reason, @@ -48,16 +45,11 @@ async fn test_fuzz() { #[tokio::test(flavor = "multi_thread")] async fn test_successful_fuzz_cases() { + let filter = Filter::new(".*", ".*", ".*fuzz/FuzzPositive") + .exclude_tests(r"invariantCounter|testIncrement\(address\)|testNeedle\(uint256\)") + .exclude_paths("invariant"); let mut runner = runner().await; - - let suite_result = runner - .test_collect( - &Filter::new(".*", ".*", ".*fuzz/FuzzPositive") - .exclude_tests(r"invariantCounter|testIncrement\(address\)|testNeedle\(uint256\)") - .exclude_paths("invariant"), - test_opts(), - ) - .await; + let suite_result = runner.test_collect(&filter).await; assert!(!suite_result.is_empty()); @@ -66,8 +58,9 @@ async fn test_successful_fuzz_cases() { match test_name.as_str() { "testSuccessChecker(uint256)" | "testSuccessChecker2(int256)" | - "testSuccessChecker3(uint32)" => assert!( - result.status == TestStatus::Success, + "testSuccessChecker3(uint32)" => assert_eq!( + result.status, + TestStatus::Success, "Test {} did not pass as expected.\nReason: {:?}\nLogs:\n{}", test_name, result.reason, @@ -84,17 +77,13 @@ async fn test_successful_fuzz_cases() { #[tokio::test(flavor = "multi_thread")] #[ignore] async fn test_fuzz_collection() { + let filter = Filter::new(".*", ".*", ".*fuzz/FuzzCollection.t.sol"); let mut runner = runner().await; - - let mut opts = test_opts(); - opts.invariant.depth = 100; - opts.invariant.runs = 1000; - opts.fuzz.runs = 1000; - opts.fuzz.seed = Some(U256::from(6u32)); - runner.test_options = opts.clone(); - - let results = - runner.test_collect(&Filter::new(".*", ".*", ".*fuzz/FuzzCollection.t.sol"), opts).await; + 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 results = runner.test_collect(&filter).await; assert_multiple( &results, diff --git a/crates/forge/tests/it/inline.rs b/crates/forge/tests/it/inline.rs index 0001bea29..3503606d7 100644 --- a/crates/forge/tests/it/inline.rs +++ b/crates/forge/tests/it/inline.rs @@ -13,14 +13,9 @@ use foundry_test_utils::Filter; #[tokio::test(flavor = "multi_thread")] async fn inline_config_run_fuzz() { - let opts = default_test_options(); - let filter = Filter::new(".*", ".*", ".*inline/FuzzInlineConf.t.sol"); - let mut runner = runner().await; - runner.test_options = opts.clone(); - - let result = runner.test_collect(&filter, opts).await; + let result = runner.test_collect(&filter).await; let suite_result: &SuiteResult = result.get("inline/FuzzInlineConf.t.sol:FuzzInlineConf").unwrap(); let test_result: &TestResult = @@ -39,12 +34,10 @@ async fn inline_config_run_fuzz() { async fn inline_config_run_invariant() { const ROOT: &str = "inline/InvariantInlineConf.t.sol"; - let opts = default_test_options(); let filter = Filter::new(".*", ".*", ".*inline/InvariantInlineConf.t.sol"); let mut runner = runner().await; - runner.test_options = opts.clone(); - - let result = runner.test_collect(&filter, opts).await; + runner.test_options = default_test_options(); + let result = runner.test_collect(&filter).await; let suite_result_1 = result.get(&format!("{ROOT}:InvariantInlineConf")).expect("Result exists"); let suite_result_2 = diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index 5959b0926..b5132fefa 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -1,6 +1,6 @@ //! Invariant tests. -use crate::config::*; +use crate::{config::*, test_helpers::TEST_OPTS}; use alloy_primitives::U256; use forge::{fuzz::CounterExample, result::TestStatus, TestOptions}; use foundry_test_utils::Filter; @@ -8,14 +8,9 @@ use std::collections::BTreeMap; #[tokio::test(flavor = "multi_thread")] async fn test_invariant() { + let filter = Filter::new(".*", ".*", ".*fuzz/invariant/(target|targetAbi|common)"); let mut runner = runner().await; - - let results = runner - .test_collect( - &Filter::new(".*", ".*", ".*fuzz/invariant/(target|targetAbi|common)"), - test_opts(), - ) - .await; + let results = runner.test_collect(&filter).await; assert_multiple( &results, @@ -148,18 +143,10 @@ async fn test_invariant() { #[tokio::test(flavor = "multi_thread")] async fn test_invariant_override() { + let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantReentrancy.t.sol"); let mut runner = runner().await; - - let mut opts = test_opts(); - opts.invariant.call_override = true; - runner.test_options = opts.clone(); - - let results = runner - .test_collect( - &Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantReentrancy.t.sol"), - opts, - ) - .await; + runner.test_options.invariant.call_override = true; + let results = runner.test_collect(&filter).await; assert_multiple( &results, @@ -172,20 +159,12 @@ 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 = runner().await; - - let mut opts = test_opts(); - opts.invariant.fail_on_revert = true; - opts.invariant.runs = 1; - opts.invariant.depth = 10; - runner.test_options = opts.clone(); - - let results = runner - .test_collect( - &Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantHandlerFailure.t.sol"), - opts, - ) - .await; + runner.test_options.invariant.fail_on_revert = true; + runner.test_options.invariant.runs = 1; + runner.test_options.invariant.depth = 10; + let results = runner.test_collect(&filter).await; assert_multiple( &results, @@ -205,19 +184,11 @@ async fn test_invariant_fail_on_revert() { #[tokio::test(flavor = "multi_thread")] #[ignore] async fn test_invariant_storage() { + let filter = Filter::new(".*", ".*", ".*fuzz/invariant/storage/InvariantStorageTest.t.sol"); let mut runner = runner().await; - - let mut opts = test_opts(); - opts.invariant.depth = 100 + (50 * cfg!(windows) as u32); - opts.fuzz.seed = Some(U256::from(6u32)); - runner.test_options = opts.clone(); - - let results = runner - .test_collect( - &Filter::new(".*", ".*", ".*fuzz/invariant/storage/InvariantStorageTest.t.sol"), - opts, - ) - .await; + runner.test_options.invariant.depth = 100 + (50 * cfg!(windows) as u32); + runner.test_options.fuzz.seed = Some(U256::from(6u32)); + let results = runner.test_collect(&filter).await; assert_multiple( &results, @@ -236,18 +207,10 @@ async fn test_invariant_storage() { #[tokio::test(flavor = "multi_thread")] #[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 = runner().await; - - let mut opts = test_opts(); - opts.fuzz.seed = Some(U256::from(119u32)); - runner.test_options = opts.clone(); - - let results = runner - .test_collect( - &Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantInnerContract.t.sol"), - opts, - ) - .await; + runner.test_options.fuzz.seed = Some(U256::from(119u32)); + let results = runner.test_collect(&filter).await; let results = results.values().last().expect("`InvariantInnerContract.t.sol` should be testable."); @@ -287,7 +250,7 @@ async fn test_invariant_shrink() { #[tokio::test(flavor = "multi_thread")] #[cfg_attr(windows, ignore = "for some reason there's different rng")] async fn test_invariant_assert_shrink() { - let mut opts = test_opts(); + let mut opts = TEST_OPTS.clone(); opts.fuzz.seed = Some(U256::from(119u32)); // ensure assert and require shrinks to same sequence of 3 or less @@ -296,18 +259,14 @@ async fn test_invariant_assert_shrink() { } async fn test_shrink(opts: TestOptions, contract_pattern: &str) { + let filter = Filter::new( + ".*", + contract_pattern, + ".*fuzz/invariant/common/InvariantShrinkWithAssert.t.sol", + ); let mut runner = runner().await; runner.test_options = opts.clone(); - let results = runner - .test_collect( - &Filter::new( - ".*", - contract_pattern, - ".*fuzz/invariant/common/InvariantShrinkWithAssert.t.sol", - ), - opts, - ) - .await; + let results = runner.test_collect(&filter).await; let results = results.values().last().expect("`InvariantShrinkWithAssert` should be testable."); let result = results @@ -333,18 +292,11 @@ async fn test_shrink(opts: TestOptions, contract_pattern: &str) { #[tokio::test(flavor = "multi_thread")] async fn test_invariant_preserve_state() { + let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantPreserveState.t.sol"); let mut runner = runner().await; - - // should not fail with default options - let mut opts = test_opts(); - opts.invariant.fail_on_revert = true; - runner.test_options = opts.clone(); - let results = runner - .test_collect( - &Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantPreserveState.t.sol"), - opts, - ) - .await; + // Should not fail with default options. + runner.test_options.invariant.fail_on_revert = true; + let results = runner.test_collect(&filter).await; assert_multiple( &results, BTreeMap::from([( @@ -354,17 +306,9 @@ async fn test_invariant_preserve_state() { ); // same test should revert when preserve state enabled - let mut opts = test_opts(); - opts.invariant.fail_on_revert = true; - opts.invariant.preserve_state = true; - runner.test_options = opts.clone(); - - let results = runner - .test_collect( - &Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantPreserveState.t.sol"), - opts, - ) - .await; + runner.test_options.invariant.fail_on_revert = true; + runner.test_options.invariant.preserve_state = true; + let results = runner.test_collect(&filter).await; assert_multiple( &results, BTreeMap::from([( diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index 31a330d2a..873e3071f 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -1,11 +1,12 @@ //! Test helpers for Forge integration tests. use alloy_primitives::U256; +use forge::{TestOptions, TestOptionsBuilder}; use foundry_compilers::{ artifacts::{Libraries, Settings}, Project, ProjectCompileOutput, ProjectPathsConfig, SolcConfig, }; -use foundry_config::Config; +use foundry_config::{Config, FuzzConfig, FuzzDictionaryConfig, InvariantConfig}; use foundry_evm::{ constants::CALLER, executors::{Executor, FuzzedExecutor}, @@ -53,7 +54,7 @@ pub static COMPILED: Lazy = Lazy::new(|| { write.write_all(b"1").unwrap(); out = project.compile(); drop(write); - }; + } let out = out.unwrap(); if out.has_compiler_errors() { @@ -79,6 +80,40 @@ pub static EVM_OPTS: Lazy = Lazy::new(|| EvmOpts { ..Default::default() }); +pub static TEST_OPTS: Lazy = Lazy::new(|| { + 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, + }, + }) + .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_sequence: true, + shrink_run_limit: 2usize.pow(18u32), + preserve_state: false, + }) + .build(&COMPILED, &PROJECT.paths.root) + .expect("Config loaded") +}); + pub fn fuzz_executor(executor: Executor) -> FuzzedExecutor { let cfg = proptest::test_runner::Config { failure_persistence: None, ..Default::default() }; @@ -86,6 +121,6 @@ pub fn fuzz_executor(executor: Executor) -> FuzzedExecutor { executor, proptest::test_runner::TestRunner::new(cfg), CALLER, - crate::config::test_opts().fuzz, + TEST_OPTS.fuzz, ) } From e78b947a2ab043d9dd29663e6e6c162ec6a0db95 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 1 Mar 2024 18:29:34 +0200 Subject: [PATCH 017/430] chore: remove some unnecessary async/await (#7289) --- crates/cast/bin/cmd/call.rs | 8 ++----- crates/cast/bin/cmd/run.rs | 3 +-- crates/chisel/src/executor.rs | 6 ++--- crates/common/src/compile.rs | 2 +- crates/evm/core/src/backend/mod.rs | 16 +++++-------- crates/evm/core/src/fork/backend.rs | 4 ++-- crates/evm/core/src/fork/multi.rs | 2 +- crates/evm/evm/src/executors/tracing.rs | 4 ++-- crates/forge/bin/cmd/coverage.rs | 2 +- crates/forge/bin/cmd/script/executor.rs | 3 +-- crates/forge/bin/cmd/test/mod.rs | 6 ++--- crates/forge/src/multi_runner.rs | 12 +++++----- crates/forge/tests/it/cheats.rs | 2 +- crates/forge/tests/it/config.rs | 27 +++++++++------------- crates/forge/tests/it/core.rs | 24 ++++++++++---------- crates/forge/tests/it/fork.rs | 14 ++++++------ crates/forge/tests/it/fs.rs | 4 ++-- crates/forge/tests/it/fuzz.rs | 12 +++++----- crates/forge/tests/it/inline.rs | 8 +++---- crates/forge/tests/it/invariant.rs | 30 ++++++++++++------------- crates/forge/tests/it/repros.rs | 4 ++-- crates/forge/tests/it/spec.rs | 2 +- crates/test-utils/src/util.rs | 2 +- 23 files changed, 89 insertions(+), 108 deletions(-) diff --git a/crates/cast/bin/cmd/call.rs b/crates/cast/bin/cmd/call.rs index 3d8afbfd2..ece090bf7 100644 --- a/crates/cast/bin/cmd/call.rs +++ b/crates/cast/bin/cmd/call.rs @@ -140,9 +140,7 @@ impl CallArgs { let (env, fork, chain) = TracingExecutor::get_fork_material(&config, evm_opts).await?; - let mut executor = - foundry_evm::executors::TracingExecutor::new(env, fork, evm_version, debug) - .await; + let mut executor = TracingExecutor::new(env, fork, evm_version, debug); let trace = match executor.deploy( sender, @@ -175,9 +173,7 @@ impl CallArgs { let (env, fork, chain) = TracingExecutor::get_fork_material(&config, evm_opts).await?; - let mut executor = - foundry_evm::executors::TracingExecutor::new(env, fork, evm_version, debug) - .await; + let mut executor = TracingExecutor::new(env, fork, evm_version, debug); let (tx, _) = builder.build(); diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index 796368c69..ca83c084d 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -121,8 +121,7 @@ impl RunArgs { let (mut env, fork, chain) = TracingExecutor::get_fork_material(&config, evm_opts).await?; - let mut executor = - TracingExecutor::new(env.clone(), fork, self.evm_version, self.debug).await; + let mut executor = TracingExecutor::new(env.clone(), fork, self.evm_version, self.debug); env.block.number = U256::from(tx_block_number); diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index 3b243b7b0..6d76d5b0e 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -292,10 +292,8 @@ impl SessionSource { let backend = match self.config.backend.take() { Some(backend) => backend, None => { - let backend = Backend::spawn( - self.config.evm_opts.get_fork(&self.config.foundry_config, env.clone()), - ) - .await; + let fork = self.config.evm_opts.get_fork(&self.config.foundry_config, env.clone()); + let backend = Backend::spawn(fork); self.config.backend = Some(backend.clone()); backend } diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index 935e098ee..96574f460 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -26,7 +26,7 @@ use std::{ /// /// This is merely a wrapper for [`Project::compile()`] which also prints to stdout depending on its /// settings. -#[must_use = "this builder does nothing unless you call a `compile*` method"] +#[must_use = "ProjectCompiler does nothing unless you call a `compile*` method"] pub struct ProjectCompiler { /// Whether we are going to verify the contracts after compilation. verify: Option, diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index 1ea450d23..5e2c8fdad 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -405,8 +405,8 @@ pub struct Backend { impl Backend { /// Creates a new Backend with a spawned multi fork thread. - pub async fn spawn(fork: Option) -> Self { - Self::new(MultiFork::spawn().await, fork) + pub fn spawn(fork: Option) -> Self { + Self::new(MultiFork::spawn(), fork) } /// Creates a new instance of `Backend` @@ -449,16 +449,11 @@ impl Backend { /// Creates a new instance of `Backend` with fork added to the fork database and sets the fork /// as active - pub(crate) async fn new_with_fork( - id: &ForkId, - fork: Fork, - journaled_state: JournaledState, - ) -> Self { - let mut backend = Self::spawn(None).await; + pub(crate) fn new_with_fork(id: &ForkId, fork: Fork, journaled_state: JournaledState) -> Self { + let mut backend = Self::spawn(None); let fork_ids = backend.inner.insert_new_fork(id.clone(), fork.db, journaled_state); backend.inner.launched_with_fork = Some((id.clone(), fork_ids.0, fork_ids.1)); backend.active_fork_ids = Some(fork_ids); - backend } @@ -1885,8 +1880,7 @@ fn commit_transaction>( let fork = fork.clone(); let journaled_state = journaled_state.clone(); - let db = crate::utils::RuntimeOrHandle::new() - .block_on(async move { Backend::new_with_fork(fork_id, fork, journaled_state).await }); + let db = Backend::new_with_fork(fork_id, fork, journaled_state); evm.database(db); match evm.inspect(inspector) { diff --git a/crates/evm/core/src/fork/backend.rs b/crates/evm/core/src/fork/backend.rs index e319069d0..ccf64fd10 100644 --- a/crates/evm/core/src/fork/backend.rs +++ b/crates/evm/core/src/fork/backend.rs @@ -74,7 +74,7 @@ enum BackendRequest { /// /// This handler will remain active as long as it is reachable (request channel still open) and /// requests are in progress. -#[must_use = "BackendHandler does nothing unless polled."] +#[must_use = "futures do nothing unless polled"] pub struct BackendHandler

{ provider: P, /// Stores all the data. @@ -759,7 +759,7 @@ mod tests { evm_opts, }; - let backend = Backend::spawn(Some(fork)).await; + let backend = Backend::spawn(Some(fork)); // some rng contract from etherscan let address: Address = "63091244180ae240c87d1f528f5f269134cb07b3".parse().unwrap(); diff --git a/crates/evm/core/src/fork/multi.rs b/crates/evm/core/src/fork/multi.rs index deb0e9f55..f84af067c 100644 --- a/crates/evm/core/src/fork/multi.rs +++ b/crates/evm/core/src/fork/multi.rs @@ -83,7 +83,7 @@ impl MultiFork { } /// Creates a new pair and spawns the `MultiForkHandler` on a background thread. - pub async fn spawn() -> Self { + pub fn spawn() -> Self { trace!(target: "fork::multi", "spawning multifork"); let (fork, mut handler) = Self::new(); diff --git a/crates/evm/evm/src/executors/tracing.rs b/crates/evm/evm/src/executors/tracing.rs index 19db5fc57..5beabdd1d 100644 --- a/crates/evm/evm/src/executors/tracing.rs +++ b/crates/evm/evm/src/executors/tracing.rs @@ -11,13 +11,13 @@ pub struct TracingExecutor { } impl TracingExecutor { - pub async fn new( + pub fn new( env: revm::primitives::Env, fork: Option, version: Option, debug: bool, ) -> Self { - let db = Backend::spawn(fork).await; + let db = Backend::spawn(fork); 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/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index 718d10790..7acfa8fdf 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -313,7 +313,7 @@ impl CoverageArgs { let known_contracts = runner.known_contracts.clone(); let filter = self.filter; let (tx, rx) = channel::<(String, SuiteResult)>(); - let handle = tokio::task::spawn(async move { runner.test(&filter, tx).await }); + let handle = tokio::task::spawn_blocking(move || runner.test(&filter, tx)); // Add hit data to the coverage report let data = rx diff --git a/crates/forge/bin/cmd/script/executor.rs b/crates/forge/bin/cmd/script/executor.rs index 071cbcbb2..a78f5b9f4 100644 --- a/crates/forge/bin/cmd/script/executor.rs +++ b/crates/forge/bin/cmd/script/executor.rs @@ -281,7 +281,7 @@ impl ScriptArgs { Some(db) => db.clone(), None => { let fork = script_config.evm_opts.get_fork(&script_config.config, env.clone()); - let backend = Backend::spawn(fork).await; + let backend = Backend::spawn(fork); script_config.backends.insert(url.clone(), backend.clone()); backend } @@ -291,7 +291,6 @@ impl ScriptArgs { // no need to cache it, since there won't be any onchain simulation that we'd need // to cache the backend for. Backend::spawn(script_config.evm_opts.get_fork(&script_config.config, env.clone())) - .await } }; diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 0d49b4640..2a0e7bbdb 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -289,7 +289,7 @@ impl TestArgs { } if self.json { - let results = runner.test_collect(filter).await; + let results = runner.test_collect(filter); println!("{}", serde_json::to_string(&results)?); return Ok(TestOutcome::new(results, self.allow_failure)); } @@ -303,9 +303,9 @@ impl TestArgs { // Run tests. let (tx, rx) = channel::<(String, SuiteResult)>(); let timer = Instant::now(); - let handle = tokio::task::spawn({ + let handle = tokio::task::spawn_blocking({ let filter = filter.clone(); - async move { runner.test(&filter, tx).await } + move || runner.test(&filter, tx) }); let mut gas_report = diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index ea6c79c98..6a8e821b2 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -120,8 +120,8 @@ impl MultiContractRunner { /// The same as [`test`](Self::test), but returns the results instead of streaming them. /// /// Note that this method returns only when all tests have been executed. - pub async fn test_collect(&mut self, filter: &dyn TestFilter) -> BTreeMap { - self.test_iter(filter).await.collect() + pub fn test_collect(&mut self, filter: &dyn TestFilter) -> BTreeMap { + self.test_iter(filter).collect() } /// Executes _all_ tests that match the given `filter`. @@ -129,12 +129,12 @@ impl MultiContractRunner { /// The same as [`test`](Self::test), but returns the results instead of streaming them. /// /// Note that this method returns only when all tests have been executed. - pub async fn test_iter( + pub fn test_iter( &mut self, filter: &dyn TestFilter, ) -> impl Iterator { let (tx, rx) = mpsc::channel(); - self.test(filter, tx).await; + self.test(filter, tx); rx.into_iter() } @@ -144,11 +144,11 @@ impl MultiContractRunner { /// before executing all contracts and their tests in _parallel_. /// /// Each Executor gets its own instance of the `Backend`. - pub async fn test(&mut self, filter: &dyn TestFilter, tx: mpsc::Sender<(String, SuiteResult)>) { + pub fn test(&mut self, filter: &dyn TestFilter, tx: mpsc::Sender<(String, SuiteResult)>) { trace!("running all tests"); // The DB backend that serves all the data. - let db = Backend::spawn(self.fork.take()).await; + let db = Backend::spawn(self.fork.take()); let executor = ExecutorBuilder::new() .inspectors(|stack| { stack diff --git a/crates/forge/tests/it/cheats.rs b/crates/forge/tests/it/cheats.rs index 2959014bc..8fa7ae31e 100644 --- a/crates/forge/tests/it/cheats.rs +++ b/crates/forge/tests/it/cheats.rs @@ -20,7 +20,7 @@ async fn test_cheats_local() { let mut config = Config::with_root(PROJECT.root()); config.fs_permissions = FsPermissions::new(vec![PathPermission::read_write("./")]); - let runner = runner_with_config(config).await; + let runner = runner_with_config(config); TestConfig::with_filter(runner, filter).run().await; } diff --git a/crates/forge/tests/it/config.rs b/crates/forge/tests/it/config.rs index c56a626c4..bc56b5c1c 100644 --- a/crates/forge/tests/it/config.rs +++ b/crates/forge/tests/it/config.rs @@ -31,8 +31,8 @@ impl TestConfig { Self::with_filter(runner, Filter::matches_all()) } - pub async fn filter(filter: Filter) -> Self { - Self::with_filter(runner().await, filter) + pub fn filter(filter: Filter) -> Self { + Self::with_filter(runner(), filter) } pub fn with_filter(runner: MultiContractRunner, filter: Filter) -> Self { @@ -55,8 +55,8 @@ impl TestConfig { } /// Executes the test runner - pub async fn test(&mut self) -> BTreeMap { - self.runner.test_collect(&self.filter).await + pub fn test(&mut self) -> BTreeMap { + self.runner.test_collect(&self.filter) } pub async fn run(&mut self) { @@ -69,7 +69,7 @@ impl TestConfig { /// * filter matched 0 test cases /// * a test results deviates from the configured `should_fail` setting pub async fn try_run(&mut self) -> eyre::Result<()> { - let suite_result = self.test().await; + let suite_result = self.test(); if suite_result.is_empty() { eyre::bail!("empty test result"); } @@ -127,20 +127,20 @@ pub fn base_runner() -> MultiContractRunnerBuilder { } /// Builds a non-tracing runner -pub async fn runner() -> MultiContractRunner { +pub fn runner() -> MultiContractRunner { let mut config = Config::with_root(PROJECT.root()); config.fs_permissions = FsPermissions::new(vec![PathPermission::read_write(manifest_root())]); - runner_with_config(config).await + runner_with_config(config) } /// Builds a non-tracing runner -pub async fn runner_with_config(mut config: Config) -> MultiContractRunner { +pub fn runner_with_config(mut config: Config) -> MultiContractRunner { config.rpc_endpoints = rpc_endpoints(); config.allow_paths.push(manifest_root().to_path_buf()); let root = &PROJECT.paths.root; let opts = &*EVM_OPTS; - let env = opts.evm_env().await.expect("could not instantiate fork environment"); + let env = opts.local_evm_env(); let output = COMPILED.clone(); base_runner() .with_cheats_config(CheatsConfig::new(&config, opts.clone(), None)) @@ -150,16 +150,11 @@ pub async fn runner_with_config(mut config: Config) -> MultiContractRunner { } /// Builds a tracing runner -pub async fn tracing_runner() -> MultiContractRunner { +pub fn tracing_runner() -> MultiContractRunner { let mut opts = EVM_OPTS.clone(); opts.verbosity = 5; base_runner() - .build( - &PROJECT.paths.root, - (*COMPILED).clone(), - EVM_OPTS.evm_env().await.expect("Could not instantiate fork environment"), - opts, - ) + .build(&PROJECT.paths.root, (*COMPILED).clone(), EVM_OPTS.local_evm_env(), opts) .unwrap() } diff --git a/crates/forge/tests/it/core.rs b/crates/forge/tests/it/core.rs index 29ce68c9e..f581407d5 100644 --- a/crates/forge/tests/it/core.rs +++ b/crates/forge/tests/it/core.rs @@ -9,8 +9,8 @@ use std::{collections::BTreeMap, env}; #[tokio::test(flavor = "multi_thread")] async fn test_core() { let filter = Filter::new(".*", ".*", ".*core"); - let mut runner = runner().await; - let results = runner.test_collect(&filter).await; + let mut runner = runner(); + let results = runner.test_collect(&filter); assert_multiple( &results, @@ -79,8 +79,8 @@ async fn test_core() { #[tokio::test(flavor = "multi_thread")] async fn test_linking() { let filter = Filter::new(".*", ".*", ".*linking"); - let mut runner = runner().await; - let results = runner.test_collect(&filter).await; + let mut runner = runner(); + let results = runner.test_collect(&filter); assert_multiple( &results, @@ -113,8 +113,8 @@ async fn test_linking() { #[tokio::test(flavor = "multi_thread")] async fn test_logs() { let filter = Filter::new(".*", ".*", ".*logs"); - let mut runner = runner().await; - let results = runner.test_collect(&filter).await; + let mut runner = runner(); + let results = runner.test_collect(&filter); assert_multiple( &results, @@ -678,8 +678,8 @@ async fn test_env_vars() { env::remove_var(env_var_key); let filter = Filter::new("testSetEnv", ".*", ".*"); - let mut runner = runner().await; - let _ = runner.test_collect(&filter).await; + let mut runner = runner(); + let _ = runner.test_collect(&filter); assert_eq!(env::var(env_var_key).unwrap(), env_var_val); } @@ -687,8 +687,8 @@ async fn test_env_vars() { #[tokio::test(flavor = "multi_thread")] async fn test_doesnt_run_abstract_contract() { let filter = Filter::new(".*", ".*", ".*Abstract.t.sol".to_string().as_str()); - let mut runner = runner().await; - let results = runner.test_collect(&filter).await; + let mut runner = runner(); + let results = runner.test_collect(&filter); assert!(results.get("core/Abstract.t.sol:AbstractTestBase").is_none()); assert!(results.get("core/Abstract.t.sol:AbstractTest").is_some()); } @@ -696,8 +696,8 @@ async fn test_doesnt_run_abstract_contract() { #[tokio::test(flavor = "multi_thread")] async fn test_trace() { let filter = Filter::new(".*", ".*", ".*trace"); - let mut runner = tracing_runner().await; - let suite_result = runner.test_collect(&filter).await; + let mut runner = tracing_runner(); + let suite_result = runner.test_collect(&filter); // TODO: This trace test is very basic - it is probably a good candidate for snapshot // testing. diff --git a/crates/forge/tests/it/fork.rs b/crates/forge/tests/it/fork.rs index 76a06e57b..da103801c 100644 --- a/crates/forge/tests/it/fork.rs +++ b/crates/forge/tests/it/fork.rs @@ -16,8 +16,8 @@ async fn test_cheats_fork_revert() { ".*", &format!(".*cheats{RE_PATH_SEPARATOR}Fork"), ); - let mut runner = runner().await; - let suite_result = runner.test_collect(&filter).await; + let mut runner = runner(); + let suite_result = runner.test_collect(&filter); assert_eq!(suite_result.len(), 1); for (_, SuiteResult { test_results, .. }) in suite_result { @@ -38,7 +38,7 @@ async fn test_cheats_fork() { let runner = runner_with_config(config); let filter = Filter::new(".*", ".*", &format!(".*cheats{RE_PATH_SEPARATOR}Fork")) .exclude_tests(".*Revert"); - TestConfig::with_filter(runner.await, filter).run().await; + TestConfig::with_filter(runner, filter).run().await; } /// Executes eth_getLogs cheatcode @@ -49,7 +49,7 @@ async fn test_get_logs_fork() { let runner = runner_with_config(config); let filter = Filter::new("testEthGetLogs", ".*", &format!(".*cheats{RE_PATH_SEPARATOR}Fork")) .exclude_tests(".*Revert"); - TestConfig::with_filter(runner.await, filter).run().await; + TestConfig::with_filter(runner, filter).run().await; } /// Executes rpc cheatcode @@ -60,7 +60,7 @@ async fn test_rpc_fork() { let runner = runner_with_config(config); let filter = Filter::new("testRpc", ".*", &format!(".*cheats{RE_PATH_SEPARATOR}Fork")) .exclude_tests(".*Revert"); - TestConfig::with_filter(runner.await, filter).run().await; + TestConfig::with_filter(runner, filter).run().await; } /// Tests that we can launch in forking mode @@ -85,12 +85,12 @@ async fn test_launch_fork_ws() { #[tokio::test(flavor = "multi_thread")] async fn test_transact_fork() { let filter = Filter::new(".*", ".*", &format!(".*fork{RE_PATH_SEPARATOR}Transact")); - TestConfig::filter(filter).await.run().await; + TestConfig::filter(filter).run().await; } /// Tests that we can create the same fork (provider,block) concurretnly in different tests #[tokio::test(flavor = "multi_thread")] async fn test_create_same_fork() { let filter = Filter::new(".*", ".*", &format!(".*fork{RE_PATH_SEPARATOR}ForkSame")); - TestConfig::filter(filter).await.run().await; + TestConfig::filter(filter).run().await; } diff --git a/crates/forge/tests/it/fs.rs b/crates/forge/tests/it/fs.rs index 29affe05c..72ae2cb32 100644 --- a/crates/forge/tests/it/fs.rs +++ b/crates/forge/tests/it/fs.rs @@ -8,7 +8,7 @@ use foundry_test_utils::Filter; async fn test_fs_disabled() { let mut config = Config::with_root(PROJECT.root()); config.fs_permissions = FsPermissions::new(vec![PathPermission::none("./")]); - let runner = runner_with_config(config).await; + let runner = runner_with_config(config); let filter = Filter::new(".*", ".*", ".*fs/Disabled"); TestConfig::with_filter(runner, filter).run().await; } @@ -19,5 +19,5 @@ async fn test_fs_default() { config.fs_permissions = FsPermissions::new(vec![PathPermission::read("./fixtures")]); let runner = runner_with_config(config); let filter = Filter::new(".*", ".*", ".*fs/Default"); - TestConfig::with_filter(runner.await, filter).run().await; + TestConfig::with_filter(runner, filter).run().await; } diff --git a/crates/forge/tests/it/fuzz.rs b/crates/forge/tests/it/fuzz.rs index 011497f4c..14a27d6b4 100644 --- a/crates/forge/tests/it/fuzz.rs +++ b/crates/forge/tests/it/fuzz.rs @@ -11,8 +11,8 @@ async fn test_fuzz() { let filter = Filter::new(".*", ".*", ".*fuzz/") .exclude_tests(r"invariantCounter|testIncrement\(address\)|testNeedle\(uint256\)|testSuccessChecker\(uint256\)|testSuccessChecker2\(int256\)|testSuccessChecker3\(uint32\)") .exclude_paths("invariant"); - let mut runner = runner().await; - let suite_result = runner.test_collect(&filter).await; + let mut runner = runner(); + let suite_result = runner.test_collect(&filter); assert!(!suite_result.is_empty()); @@ -48,8 +48,8 @@ async fn test_successful_fuzz_cases() { let filter = Filter::new(".*", ".*", ".*fuzz/FuzzPositive") .exclude_tests(r"invariantCounter|testIncrement\(address\)|testNeedle\(uint256\)") .exclude_paths("invariant"); - let mut runner = runner().await; - let suite_result = runner.test_collect(&filter).await; + let mut runner = runner(); + let suite_result = runner.test_collect(&filter); assert!(!suite_result.is_empty()); @@ -78,12 +78,12 @@ async fn test_successful_fuzz_cases() { #[ignore] async fn test_fuzz_collection() { let filter = Filter::new(".*", ".*", ".*fuzz/FuzzCollection.t.sol"); - let mut runner = runner().await; + let mut runner = 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 results = runner.test_collect(&filter).await; + let results = runner.test_collect(&filter); assert_multiple( &results, diff --git a/crates/forge/tests/it/inline.rs b/crates/forge/tests/it/inline.rs index 3503606d7..3e7b46163 100644 --- a/crates/forge/tests/it/inline.rs +++ b/crates/forge/tests/it/inline.rs @@ -14,8 +14,8 @@ use foundry_test_utils::Filter; #[tokio::test(flavor = "multi_thread")] async fn inline_config_run_fuzz() { let filter = Filter::new(".*", ".*", ".*inline/FuzzInlineConf.t.sol"); - let mut runner = runner().await; - let result = runner.test_collect(&filter).await; + let mut runner = runner(); + let result = runner.test_collect(&filter); let suite_result: &SuiteResult = result.get("inline/FuzzInlineConf.t.sol:FuzzInlineConf").unwrap(); let test_result: &TestResult = @@ -35,9 +35,9 @@ async fn inline_config_run_invariant() { const ROOT: &str = "inline/InvariantInlineConf.t.sol"; let filter = Filter::new(".*", ".*", ".*inline/InvariantInlineConf.t.sol"); - let mut runner = runner().await; + let mut runner = runner(); runner.test_options = default_test_options(); - let result = runner.test_collect(&filter).await; + let result = runner.test_collect(&filter); let suite_result_1 = result.get(&format!("{ROOT}:InvariantInlineConf")).expect("Result exists"); let suite_result_2 = diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index b5132fefa..617889371 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -9,8 +9,8 @@ use std::collections::BTreeMap; #[tokio::test(flavor = "multi_thread")] async fn test_invariant() { let filter = Filter::new(".*", ".*", ".*fuzz/invariant/(target|targetAbi|common)"); - let mut runner = runner().await; - let results = runner.test_collect(&filter).await; + let mut runner = runner(); + let results = runner.test_collect(&filter); assert_multiple( &results, @@ -144,9 +144,9 @@ async fn test_invariant() { #[tokio::test(flavor = "multi_thread")] async fn test_invariant_override() { let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantReentrancy.t.sol"); - let mut runner = runner().await; + let mut runner = runner(); runner.test_options.invariant.call_override = true; - let results = runner.test_collect(&filter).await; + let results = runner.test_collect(&filter); assert_multiple( &results, @@ -160,11 +160,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 = runner().await; + let mut runner = runner(); runner.test_options.invariant.fail_on_revert = true; runner.test_options.invariant.runs = 1; runner.test_options.invariant.depth = 10; - let results = runner.test_collect(&filter).await; + let results = runner.test_collect(&filter); assert_multiple( &results, @@ -185,10 +185,10 @@ 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 = runner().await; + let mut runner = runner(); runner.test_options.invariant.depth = 100 + (50 * cfg!(windows) as u32); runner.test_options.fuzz.seed = Some(U256::from(6u32)); - let results = runner.test_collect(&filter).await; + let results = runner.test_collect(&filter); assert_multiple( &results, @@ -208,9 +208,9 @@ async fn test_invariant_storage() { #[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 = runner().await; + let mut runner = runner(); runner.test_options.fuzz.seed = Some(U256::from(119u32)); - let results = runner.test_collect(&filter).await; + let results = runner.test_collect(&filter); let results = results.values().last().expect("`InvariantInnerContract.t.sol` should be testable."); @@ -264,9 +264,9 @@ async fn test_shrink(opts: TestOptions, contract_pattern: &str) { contract_pattern, ".*fuzz/invariant/common/InvariantShrinkWithAssert.t.sol", ); - let mut runner = runner().await; + let mut runner = runner(); runner.test_options = opts.clone(); - let results = runner.test_collect(&filter).await; + let results = runner.test_collect(&filter); let results = results.values().last().expect("`InvariantShrinkWithAssert` should be testable."); let result = results @@ -293,10 +293,10 @@ async fn test_shrink(opts: TestOptions, contract_pattern: &str) { #[tokio::test(flavor = "multi_thread")] async fn test_invariant_preserve_state() { let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantPreserveState.t.sol"); - let mut runner = runner().await; + let mut runner = runner(); // Should not fail with default options. runner.test_options.invariant.fail_on_revert = true; - let results = runner.test_collect(&filter).await; + let results = runner.test_collect(&filter); assert_multiple( &results, BTreeMap::from([( @@ -308,7 +308,7 @@ async fn test_invariant_preserve_state() { // same test should revert when preserve state enabled runner.test_options.invariant.fail_on_revert = true; runner.test_options.invariant.preserve_state = true; - let results = runner.test_collect(&filter).await; + let results = runner.test_collect(&filter); assert_multiple( &results, BTreeMap::from([( diff --git a/crates/forge/tests/it/repros.rs b/crates/forge/tests/it/repros.rs index 8506a248d..d8b4447cf 100644 --- a/crates/forge/tests/it/repros.rs +++ b/crates/forge/tests/it/repros.rs @@ -32,7 +32,7 @@ macro_rules! test_repro { paste::paste! { #[tokio::test(flavor = "multi_thread")] async fn [< issue_ $issue_number >]() { - let mut $res = repro_config($issue_number, $should_fail, $sender.into()).await.test().await; + let mut $res = repro_config($issue_number, $should_fail, $sender.into()).await.test(); $e } } @@ -60,7 +60,7 @@ async fn repro_config(issue: usize, should_fail: bool, sender: Option

) config.sender = sender; } - let runner = runner_with_config(config).await; + let runner = runner_with_config(config); TestConfig::with_filter(runner, filter).set_should_fail(should_fail) } diff --git a/crates/forge/tests/it/spec.rs b/crates/forge/tests/it/spec.rs index 724aaa0ff..4dd6aeee5 100644 --- a/crates/forge/tests/it/spec.rs +++ b/crates/forge/tests/it/spec.rs @@ -7,5 +7,5 @@ use foundry_test_utils::Filter; #[tokio::test(flavor = "multi_thread")] async fn test_shanghai_compat() { let filter = Filter::new("", "ShanghaiCompat", ".*spec"); - TestConfig::filter(filter).await.evm_spec(SpecId::SHANGHAI).run().await; + TestConfig::filter(filter).evm_spec(SpecId::SHANGHAI).run().await; } diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index a26936489..48b3562b2 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -51,7 +51,7 @@ pub const OTHER_SOLC_VERSION: &str = "0.8.22"; /// External test builder #[derive(Clone, Debug)] -#[must_use = "call run()"] +#[must_use = "ExtTester does nothing unless you `run` it"] pub struct ExtTester { pub org: &'static str, pub name: &'static str, From de33b6af53005037b463318d2628b5cfcaf39916 Mon Sep 17 00:00:00 2001 From: Steve <1848680+misko9@users.noreply.github.com> Date: Fri, 1 Mar 2024 10:36:07 -0700 Subject: [PATCH 018/430] feat: add unsafe-password support to cast wallet import (#6671) * feat: add unsafe-password support to cast wallet import * rustfmt fix * Change env CAST_PASSWORD to CAST_UNSAFE_PASSWORD for `cast wallet import` --------- Co-authored-by: Steve Miskovetz --- crates/cast/bin/cmd/wallet/mod.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/crates/cast/bin/cmd/wallet/mod.rs b/crates/cast/bin/cmd/wallet/mod.rs index 9a08984d7..931ecefa4 100644 --- a/crates/cast/bin/cmd/wallet/mod.rs +++ b/crates/cast/bin/cmd/wallet/mod.rs @@ -136,6 +136,10 @@ pub enum WalletSubcommands { /// (~/.foundry/keystores) #[arg(long, short)] keystore_dir: Option, + /// Password for the JSON keystore in cleartext + /// This is unsafe, we recommend using the default hidden password prompt + #[arg(long, env = "CAST_UNSAFE_PASSWORD", value_name = "PASSWORD")] + unsafe_password: Option, #[command(flatten)] raw_wallet_options: RawWalletOpts, }, @@ -282,7 +286,12 @@ impl WalletSubcommands { println!("Validation failed. Address {address} did not sign this message."); } } - WalletSubcommands::Import { account_name, keystore_dir, raw_wallet_options } => { + WalletSubcommands::Import { + account_name, + keystore_dir, + unsafe_password, + raw_wallet_options, + } => { // Set up keystore directory let dir = if let Some(path) = keystore_dir { Path::new(&path).to_path_buf() @@ -318,7 +327,12 @@ flag to set your key via: })?; let private_key = wallet.signer().to_bytes(); - let password = rpassword::prompt_password("Enter password: ")?; + let password = if let Some(password) = unsafe_password { + password + } else { + // if no --unsafe-password was provided read via stdin + rpassword::prompt_password("Enter password: ")? + }; let mut rng = thread_rng(); eth_keystore::encrypt_key( From f6fcfa500c0d0738a78c75de3e133184b9ffca0e Mon Sep 17 00:00:00 2001 From: Enrique Date: Sat, 2 Mar 2024 04:02:51 -0400 Subject: [PATCH 019/430] chore: doc fixes and rm unused stuff on common (#7291) --- crates/common/src/provider/alloy.rs | 12 ------------ crates/common/src/provider/retry.rs | 3 ++- crates/common/src/provider/runtime_transport.rs | 5 +---- crates/common/src/provider/tower.rs | 5 ++--- 4 files changed, 5 insertions(+), 20 deletions(-) diff --git a/crates/common/src/provider/alloy.rs b/crates/common/src/provider/alloy.rs index bb5277814..d51be9f9a 100644 --- a/crates/common/src/provider/alloy.rs +++ b/crates/common/src/provider/alloy.rs @@ -203,18 +203,6 @@ impl ProviderBuilder { self } - /// Same as [`Self:build()`] but also retrieves the `chainId` in order to derive an appropriate - /// interval. - pub async fn connect(self) -> Result { - let provider = self.build()?; - // todo: port poll interval hint - /*if let Some(blocktime) = provider.get_chainid().await.ok().and_then(|id| { - }) { - provider = provider.interval(blocktime / 2); - }*/ - Ok(provider) - } - /// Constructs the `RetryProvider` taking all configs into account. pub fn build(self) -> Result { let ProviderBuilder { diff --git a/crates/common/src/provider/retry.rs b/crates/common/src/provider/retry.rs index fd8a33e42..2137dedbd 100644 --- a/crates/common/src/provider/retry.rs +++ b/crates/common/src/provider/retry.rs @@ -3,7 +3,7 @@ use alloy_json_rpc::ErrorPayload; use alloy_transport::{TransportError, TransportErrorKind}; use serde::Deserialize; -/// [RetryPolicy] defines logic for which [JsonRpcClient::Error] instances should +/// [RetryPolicy] defines logic for which [TransportError] instances should /// the client retry the request and try to recover from. pub trait RetryPolicy: Send + Sync + std::fmt::Debug { /// Whether to retry the request based on the given `error` @@ -52,6 +52,7 @@ impl RetryPolicy for RateLimitRetryPolicy { } } + /// Provides a backoff hint if the error response contains it fn backoff_hint(&self, error: &TransportError) -> Option { if let TransportError::ErrorResp(resp) = error { let data = resp.try_data_as::(); diff --git a/crates/common/src/provider/runtime_transport.rs b/crates/common/src/provider/runtime_transport.rs index eab2addb8..5c2eededf 100644 --- a/crates/common/src/provider/runtime_transport.rs +++ b/crates/common/src/provider/runtime_transport.rs @@ -25,7 +25,6 @@ pub enum InnerTransport { Http(Http), /// WebSocket transport Ws(PubSubFrontend), - // TODO: IPC /// IPC transport Ipc(PubSubFrontend), } @@ -177,7 +176,6 @@ impl RuntimeTransport { let client = client_builder.build().map_err(RuntimeTransportError::HttpConstructionError)?; - // todo: retry tower layer Ok(InnerTransport::Http(Http::with_client(client, self.url.clone()))) } @@ -271,7 +269,7 @@ impl tower::Service for RuntimeTransport { } } -impl Service for &RuntimeTransport { +impl tower::Service for &RuntimeTransport { type Response = ResponsePacket; type Error = TransportError; type Future = TransportFut<'static>; @@ -297,7 +295,6 @@ fn build_auth(jwt: String) -> eyre::Result { let auth = JwtAuth::new(secret, None, None); let token = auth.generate_token()?; - // Essentially unrolled ethers-rs new_with_auth to accommodate the custom timeout let auth = Authorization::Bearer(token); Ok(auth) diff --git a/crates/common/src/provider/tower.rs b/crates/common/src/provider/tower.rs index 0df22bb01..2f97d2f8f 100644 --- a/crates/common/src/provider/tower.rs +++ b/crates/common/src/provider/tower.rs @@ -10,7 +10,6 @@ use std::{ use alloy_json_rpc::{RequestPacket, ResponsePacket}; use alloy_transport::{TransportError, TransportErrorKind, TransportFut}; -use tower::Service; use super::{ retry::{RateLimitRetryPolicy, RetryPolicy}, @@ -65,7 +64,7 @@ impl tower::layer::Layer for RetryBackoffLayer { } /// An Alloy Tower Service that is responsible for retrying requests based on the -/// error type. See [TransportError] and [RetryWithPolicyLayer]. +/// error type. See [TransportError] and [RateLimitRetryPolicy]. #[derive(Debug, Clone)] pub struct RetryBackoffService { /// The inner service @@ -85,7 +84,7 @@ pub struct RetryBackoffService { } // impl tower service -impl Service for RetryBackoffService { +impl tower::Service for RetryBackoffService { type Response = ResponsePacket; type Error = TransportError; type Future = TransportFut<'static>; From 1b6d0fab362cf184997c420b96ccc8221a3ab4cb Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 2 Mar 2024 18:15:19 +0200 Subject: [PATCH 020/430] test: modify some flaky tests (#7293) --- crates/forge/tests/cli/build.rs | 18 ++---------------- crates/forge/tests/cli/cmd.rs | 5 ++--- crates/test-utils/src/util.rs | 6 ++++++ 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/crates/forge/tests/cli/build.rs b/crates/forge/tests/cli/build.rs index 240b54614..0002ec145 100644 --- a/crates/forge/tests/cli/build.rs +++ b/crates/forge/tests/cli/build.rs @@ -28,20 +28,6 @@ contract Dummy { // tests build output is as expected forgetest_init!(exact_build_output, |prj, cmd| { cmd.args(["build", "--force"]); - let (stdout, _) = cmd.unchecked_output_lossy(); - // Expected output from build - let expected = r#"Compiling 24 files with 0.8.23 -Solc 0.8.23 finished in 2.36s -Compiler run successful! -"#; - - // skip all dynamic parts of the output (numbers) - let expected_words = - expected.split(|c: char| c == ' ').filter(|w| !w.chars().next().unwrap().is_numeric()); - let output_words = - stdout.split(|c: char| c == ' ').filter(|w| !w.chars().next().unwrap().is_numeric()); - - for (expected, output) in expected_words.zip(output_words) { - assert_eq!(expected, output, "expected: {}, output: {}", expected, output); - } + let stdout = cmd.stdout_lossy(); + assert!(stdout.contains("Compiling"), "\n{stdout}"); }); diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 188772657..48e86d9df 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -1556,7 +1556,7 @@ forgetest_init!(can_install_missing_deps_build, |prj, cmd| { // checks that extra output works forgetest_init!(can_build_skip_contracts, |prj, cmd| { - prj.clear_cache(); + prj.clear(); // only builds the single template contract `src/*` cmd.args(["build", "--skip", "tests", "--skip", "scripts"]); @@ -1573,8 +1573,6 @@ forgetest_init!(can_build_skip_contracts, |prj, cmd| { }); forgetest_init!(can_build_skip_glob, |prj, cmd| { - prj.clear_cache(); - prj.add_test( "Foo", r" @@ -1585,6 +1583,7 @@ function test_run() external {} .unwrap(); // only builds the single template contract `src/*` even if `*.t.sol` or `.s.sol` is absent + prj.clear(); cmd.args(["build", "--skip", "*/test/**", "--skip", "*/script/**"]); cmd.unchecked_output().stdout_matches_path( PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/can_build_skip_glob.stdout"), diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index 48b3562b2..6a6f15d0f 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -444,6 +444,12 @@ impl TestProject { &self.paths().artifacts } + /// Removes the project's cache and artifacts directory. + pub fn clear(&self) { + self.clear_cache(); + self.clear_artifacts(); + } + /// Removes this project's cache file. pub fn clear_cache(&self) { let _ = fs::remove_file(self.cache()); From 2d54c1fbe83092596ff542d2bec9a70b478031b7 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Sat, 2 Mar 2024 21:54:43 +0200 Subject: [PATCH 021/430] feat invariant (#5868) - configure calldata fuzzed addresses dictionary (#7240) * issue #5868 - added `FuzzDictionaryConfig.max_calldata_fuzz_dictionary_addresses` option to specify how many random addresses to generate and to randomly select from when fuzzing calldata. If option is not specified then current behavior applies - to narrow down number of runs / addresses involved in invariant test the `CalldataFuzzDictionaryConfig` is populated with random addresses plus all accounts from db (from `EvmFuzzState`) - added `fuzz_calldata_with_config` fn that accepts `Option` as param. Non invariants tests use existing `fuzz_calldata` fn and pass None as config arg * max_calldata_fuzz_dictionary_addresses usize * Add test from issue 5868 * Changes after review - comments, wrap Arc as CalldataFuzzDictionary.inner, code cleanup --- crates/config/src/fuzz.rs | 5 ++ crates/evm/evm/src/executors/invariant/mod.rs | 20 ++++- crates/evm/fuzz/src/strategies/calldata.rs | 73 +++++++++++++++- crates/evm/fuzz/src/strategies/invariants.rs | 34 ++++++-- crates/evm/fuzz/src/strategies/mod.rs | 4 +- crates/evm/fuzz/src/strategies/param.rs | 52 +++++++++--- crates/forge/tests/it/invariant.rs | 44 ++++++++++ crates/forge/tests/it/test_helpers.rs | 2 + .../common/InvariantCalldataDictionary.t.sol | 84 +++++++++++++++++++ 9 files changed, 291 insertions(+), 27 deletions(-) create mode 100644 testdata/fuzz/invariant/common/InvariantCalldataDictionary.t.sol diff --git a/crates/config/src/fuzz.rs b/crates/config/src/fuzz.rs index 1d033b3ef..11d214670 100644 --- a/crates/config/src/fuzz.rs +++ b/crates/config/src/fuzz.rs @@ -86,6 +86,10 @@ pub struct FuzzDictionaryConfig { /// Once the fuzzer exceeds this limit, it will start evicting random entries #[serde(deserialize_with = "crate::deserialize_usize_or_max")] pub max_fuzz_dictionary_values: usize, + /// How many random addresses to use and to recycle when fuzzing calldata. + /// If not specified then `max_fuzz_dictionary_addresses` value applies. + #[serde(deserialize_with = "crate::deserialize_usize_or_max")] + pub max_calldata_fuzz_dictionary_addresses: usize, } impl Default for FuzzDictionaryConfig { @@ -98,6 +102,7 @@ impl Default for FuzzDictionaryConfig { max_fuzz_dictionary_addresses: (300 * 1024 * 1024) / 20, // limit this to 200MB max_fuzz_dictionary_values: (200 * 1024 * 1024) / 32, + max_calldata_fuzz_dictionary_addresses: 0, } } } diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 316db1320..79d055eab 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -33,13 +33,18 @@ use std::{cell::RefCell, collections::BTreeMap, sync::Arc}; mod error; pub use error::{InvariantFailures, InvariantFuzzError, InvariantFuzzTestResult}; +use foundry_evm_fuzz::strategies::CalldataFuzzDictionary; mod funcs; pub use funcs::{assert_invariants, replay_run}; /// Alias for (Dictionary for fuzzing, initial contracts to fuzz and an InvariantStrategy). -type InvariantPreparation = - (EvmFuzzState, FuzzRunIdentifiedContracts, BoxedStrategy>); +type InvariantPreparation = ( + EvmFuzzState, + FuzzRunIdentifiedContracts, + BoxedStrategy>, + CalldataFuzzDictionary, +); /// Enriched results of an invariant run check. /// @@ -104,7 +109,8 @@ impl<'a> InvariantExecutor<'a> { return Err(eyre!("Invariant test function should have no inputs")) } - let (fuzz_state, targeted_contracts, strat) = self.prepare_fuzzing(&invariant_contract)?; + let (fuzz_state, targeted_contracts, strat, calldata_fuzz_dictionary) = + self.prepare_fuzzing(&invariant_contract)?; // Stores the consumed gas and calldata of every successful fuzz call. let fuzz_cases: RefCell> = RefCell::new(Default::default()); @@ -245,6 +251,7 @@ impl<'a> InvariantExecutor<'a> { Ok(()) }); + trace!(target: "forge::test::invariant::calldata_address_fuzz_dictionary", "{:?}", calldata_fuzz_dictionary.inner.addresses); trace!(target: "forge::test::invariant::dictionary", "{:?}", fuzz_state.read().values().iter().map(hex::encode).collect::>()); let (reverts, error) = failures.into_inner().into_inner(); @@ -283,12 +290,16 @@ impl<'a> InvariantExecutor<'a> { let targeted_contracts: FuzzRunIdentifiedContracts = Arc::new(Mutex::new(targeted_contracts)); + let calldata_fuzz_config = + CalldataFuzzDictionary::new(&self.config.dictionary, fuzz_state.clone()); + // Creates the invariant strategy. let strat = invariant_strat( fuzz_state.clone(), targeted_senders, targeted_contracts.clone(), self.config.dictionary.dictionary_weight, + calldata_fuzz_config.clone(), ) .no_shrink() .boxed(); @@ -306,6 +317,7 @@ impl<'a> InvariantExecutor<'a> { fuzz_state.clone(), targeted_contracts.clone(), target_contract_ref.clone(), + calldata_fuzz_config.clone(), ), target_contract_ref, )); @@ -314,7 +326,7 @@ impl<'a> InvariantExecutor<'a> { self.executor.inspector.fuzzer = Some(Fuzzer { call_generator, fuzz_state: fuzz_state.clone(), collect: true }); - Ok((fuzz_state, targeted_contracts, strat)) + Ok((fuzz_state, targeted_contracts, strat, calldata_fuzz_config)) } /// Fills the `InvariantExecutor` with the artifact identifier filters (in `path:name` string diff --git a/crates/evm/fuzz/src/strategies/calldata.rs b/crates/evm/fuzz/src/strategies/calldata.rs index 7a1566243..cb56f3330 100644 --- a/crates/evm/fuzz/src/strategies/calldata.rs +++ b/crates/evm/fuzz/src/strategies/calldata.rs @@ -1,18 +1,85 @@ -use super::fuzz_param; +use crate::strategies::{fuzz_param, EvmFuzzState}; use alloy_dyn_abi::JsonAbiExt; use alloy_json_abi::Function; -use alloy_primitives::Bytes; +use alloy_primitives::{Address, Bytes}; +use foundry_config::FuzzDictionaryConfig; +use hashbrown::HashSet; use proptest::prelude::{BoxedStrategy, Strategy}; +use std::{fmt, sync::Arc}; + +/// Clonable wrapper around [CalldataFuzzDictionary]. +#[derive(Debug, Clone)] +pub struct CalldataFuzzDictionary { + pub inner: Arc, +} + +impl CalldataFuzzDictionary { + pub fn new(config: &FuzzDictionaryConfig, state: EvmFuzzState) -> Self { + Self { inner: Arc::new(CalldataFuzzDictionaryConfig::new(config, state)) } + } +} + +#[derive(Clone)] +pub struct CalldataFuzzDictionaryConfig { + /// Addresses that can be used for fuzzing calldata. + pub addresses: Vec
, +} + +impl fmt::Debug for CalldataFuzzDictionaryConfig { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("CalldataFuzzDictionaryConfig").field("addresses", &self.addresses).finish() + } +} + +/// Represents custom configuration for invariant fuzzed calldata strategies. +/// +/// At the moment only the dictionary of addresses to be used for a fuzzed `function(address)` can +/// be configured, but support for other types can be added. +impl CalldataFuzzDictionaryConfig { + /// Creates config with the set of addresses that can be used for fuzzing invariant calldata (if + /// `max_calldata_fuzz_dictionary_addresses` configured). + /// The set of addresses contains a number of `max_calldata_fuzz_dictionary_addresses` random + /// addresses plus all addresses that already had their PUSH bytes collected (retrieved from + /// `EvmFuzzState`, if `include_push_bytes` config enabled). + pub fn new(config: &FuzzDictionaryConfig, state: EvmFuzzState) -> Self { + let mut addresses: HashSet
= HashSet::new(); + let dict_size = config.max_calldata_fuzz_dictionary_addresses; + + if dict_size > 0 { + loop { + if addresses.len() == dict_size { + break + } + addresses.insert(Address::random()); + } + + // Add all addresses that already had their PUSH bytes collected. + let mut state = state.write(); + addresses.extend(state.addresses()); + } + + Self { addresses: Vec::from_iter(addresses) } + } +} /// Given a function, it returns a strategy which generates valid calldata /// for that function's input types. pub fn fuzz_calldata(func: Function) -> BoxedStrategy { + fuzz_calldata_with_config(func, None) +} + +/// Given a function, it returns a strategy which generates valid calldata +/// for that function's input types, following custom configuration rules. +pub fn fuzz_calldata_with_config( + func: Function, + config: Option, +) -> BoxedStrategy { // We need to compose all the strategies generated for each parameter in all // possible combinations let strats = func .inputs .iter() - .map(|input| fuzz_param(&input.selector_type().parse().unwrap())) + .map(|input| fuzz_param(&input.selector_type().parse().unwrap(), config.clone())) .collect::>(); strats diff --git a/crates/evm/fuzz/src/strategies/invariants.rs b/crates/evm/fuzz/src/strategies/invariants.rs index d095f1009..29d868bad 100644 --- a/crates/evm/fuzz/src/strategies/invariants.rs +++ b/crates/evm/fuzz/src/strategies/invariants.rs @@ -1,7 +1,7 @@ -use super::fuzz_param_from_state; +use super::{fuzz_calldata_with_config, fuzz_param_from_state, CalldataFuzzDictionary}; use crate::{ invariant::{BasicTxDetails, FuzzRunIdentifiedContracts, SenderFilters}, - strategies::{fuzz_calldata, fuzz_calldata_from_state, fuzz_param, EvmFuzzState}, + strategies::{fuzz_calldata_from_state, fuzz_param, EvmFuzzState}, }; use alloy_json_abi::{Function, JsonAbi}; use alloy_primitives::{Address, Bytes}; @@ -14,6 +14,7 @@ pub fn override_call_strat( fuzz_state: EvmFuzzState, contracts: FuzzRunIdentifiedContracts, target: Arc>, + calldata_fuzz_config: CalldataFuzzDictionary, ) -> SBoxedStrategy<(Address, Bytes)> { let contracts_ref = contracts.clone(); @@ -27,10 +28,16 @@ pub fn override_call_strat( ]) .prop_flat_map(move |target_address| { let fuzz_state = fuzz_state.clone(); + let calldata_fuzz_config = calldata_fuzz_config.clone(); let (_, abi, functions) = contracts.lock().get(&target_address).unwrap().clone(); let func = select_random_function(abi, functions); func.prop_flat_map(move |func| { - fuzz_contract_with_calldata(fuzz_state.clone(), target_address, func) + fuzz_contract_with_calldata( + fuzz_state.clone(), + calldata_fuzz_config.clone(), + target_address, + func, + ) }) }) .sboxed() @@ -51,10 +58,12 @@ pub fn invariant_strat( senders: SenderFilters, contracts: FuzzRunIdentifiedContracts, dictionary_weight: u32, + calldata_fuzz_config: CalldataFuzzDictionary, ) -> impl Strategy> { // We only want to seed the first value, since we want to generate the rest as we mutate the // state - generate_call(fuzz_state, senders, contracts, dictionary_weight).prop_map(|x| vec![x]) + generate_call(fuzz_state, senders, contracts, dictionary_weight, calldata_fuzz_config) + .prop_map(|x| vec![x]) } /// Strategy to generate a transaction where the `sender`, `target` and `calldata` are all generated @@ -64,6 +73,7 @@ fn generate_call( senders: SenderFilters, contracts: FuzzRunIdentifiedContracts, dictionary_weight: u32, + calldata_fuzz_config: CalldataFuzzDictionary, ) -> BoxedStrategy { let random_contract = select_random_contract(contracts); let senders = Rc::new(senders); @@ -72,10 +82,19 @@ fn generate_call( let func = select_random_function(abi, functions); let senders = senders.clone(); let fuzz_state = fuzz_state.clone(); + let calldata_fuzz_config = calldata_fuzz_config.clone(); func.prop_flat_map(move |func| { let sender = select_random_sender(fuzz_state.clone(), senders.clone(), dictionary_weight); - (sender, fuzz_contract_with_calldata(fuzz_state.clone(), contract, func)) + ( + sender, + fuzz_contract_with_calldata( + fuzz_state.clone(), + calldata_fuzz_config.clone(), + contract, + func, + ), + ) }) }) .boxed() @@ -93,7 +112,7 @@ fn select_random_sender( let fuzz_strategy = proptest::strategy::Union::new_weighted(vec![ ( 100 - dictionary_weight, - fuzz_param(&alloy_dyn_abi::DynSolType::Address) + fuzz_param(&alloy_dyn_abi::DynSolType::Address, None) .prop_map(move |addr| addr.as_address().unwrap()) .boxed(), ), @@ -165,6 +184,7 @@ fn select_random_function( /// for that function's input types. pub fn fuzz_contract_with_calldata( fuzz_state: EvmFuzzState, + calldata_fuzz_config: CalldataFuzzDictionary, contract: Address, func: Function, ) -> impl Strategy { @@ -173,7 +193,7 @@ pub fn fuzz_contract_with_calldata( // `prop_oneof!` / `TupleUnion` `Arc`s for cheap cloning #[allow(clippy::arc_with_non_send_sync)] let strats = prop_oneof![ - 60 => fuzz_calldata(func.clone()), + 60 => fuzz_calldata_with_config(func.clone(), Some(calldata_fuzz_config)), 40 => fuzz_calldata_from_state(func, fuzz_state), ]; strats.prop_map(move |calldata| { diff --git a/crates/evm/fuzz/src/strategies/mod.rs b/crates/evm/fuzz/src/strategies/mod.rs index a795905de..63e008ec0 100644 --- a/crates/evm/fuzz/src/strategies/mod.rs +++ b/crates/evm/fuzz/src/strategies/mod.rs @@ -8,7 +8,9 @@ mod param; pub use param::{fuzz_param, fuzz_param_from_state}; mod calldata; -pub use calldata::fuzz_calldata; +pub use calldata::{ + fuzz_calldata, fuzz_calldata_with_config, CalldataFuzzDictionary, CalldataFuzzDictionaryConfig, +}; mod state; pub use state::{ diff --git a/crates/evm/fuzz/src/strategies/param.rs b/crates/evm/fuzz/src/strategies/param.rs index 093cb29c8..4c8dc03ec 100644 --- a/crates/evm/fuzz/src/strategies/param.rs +++ b/crates/evm/fuzz/src/strategies/param.rs @@ -1,4 +1,5 @@ use super::state::EvmFuzzState; +use crate::strategies::calldata::CalldataFuzzDictionary; use alloy_dyn_abi::{DynSolType, DynSolValue}; use alloy_primitives::{Address, FixedBytes, I256, U256}; use arbitrary::Unstructured; @@ -10,12 +11,32 @@ const MAX_ARRAY_LEN: usize = 256; /// Given a parameter type, returns a strategy for generating values for that type. /// /// Works with ABI Encoder v2 tuples. -pub fn fuzz_param(param: &DynSolType) -> BoxedStrategy { +pub fn fuzz_param( + param: &DynSolType, + config: Option, +) -> BoxedStrategy { let param = param.to_owned(); match param { - DynSolType::Address => any::<[u8; 32]>() - .prop_map(|x| DynSolValue::Address(Address::from_word(x.into()))) - .boxed(), + DynSolType::Address => { + if config.is_some() { + let fuzz_config = config.unwrap().inner; + let address_dict_len = fuzz_config.addresses.len(); + if address_dict_len > 0 { + // Create strategy to return random address from configured dictionary. + return any::() + .prop_map(move |index| index.index(address_dict_len)) + .prop_map(move |index| { + DynSolValue::Address(fuzz_config.addresses.get(index).cloned().unwrap()) + }) + .boxed() + } + } + + // If no config for addresses dictionary then create unbounded addresses strategy. + any::<[u8; 32]>() + .prop_map(|x| DynSolValue::Address(Address::from_word(x.into()))) + .boxed() + } DynSolType::Int(n) => { let strat = super::IntStrategy::new(n, vec![]); let strat = strat.prop_map(move |x| DynSolValue::Int(x, n)); @@ -48,15 +69,22 @@ pub fn fuzz_param(param: &DynSolType) -> BoxedStrategy { ) }) .boxed(), - DynSolType::Tuple(params) => { - params.iter().map(fuzz_param).collect::>().prop_map(DynSolValue::Tuple).boxed() - } - DynSolType::FixedArray(param, size) => proptest::collection::vec(fuzz_param(¶m), size) - .prop_map(DynSolValue::FixedArray) - .boxed(), - DynSolType::Array(param) => proptest::collection::vec(fuzz_param(¶m), 0..MAX_ARRAY_LEN) - .prop_map(DynSolValue::Array) + DynSolType::Tuple(params) => params + .iter() + .map(|p| fuzz_param(p, config.clone())) + .collect::>() + .prop_map(DynSolValue::Tuple) .boxed(), + DynSolType::FixedArray(param, size) => { + proptest::collection::vec(fuzz_param(¶m, config), size) + .prop_map(DynSolValue::FixedArray) + .boxed() + } + DynSolType::Array(param) => { + proptest::collection::vec(fuzz_param(¶m, config), 0..MAX_ARRAY_LEN) + .prop_map(DynSolValue::Array) + .boxed() + } DynSolType::CustomStruct { .. } => panic!("unsupported type"), } } diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index 617889371..28ac405cc 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -137,6 +137,10 @@ async fn test_invariant() { "fuzz/invariant/common/InvariantPreserveState.t.sol:InvariantPreserveState", vec![("invariant_preserve_state()", true, None, None, None)], ), + ( + "fuzz/invariant/common/InvariantCalldataDictionary.t.sol:InvariantCalldataDictionary", + vec![("invariant_owner_never_changes()", true, None, None, None)], + ), ]), ); } @@ -323,3 +327,43 @@ async fn test_invariant_preserve_state() { )]), ); } + +#[tokio::test(flavor = "multi_thread")] +async fn test_invariant_calldata_fuzz_dictionary_addresses() { + // should not fail with default options (address dict not finite) + let mut runner = runner(); + let results = runner.test_collect(&Filter::new( + ".*", + ".*", + ".*fuzz/invariant/common/InvariantCalldataDictionary.t.sol", + )); + assert_multiple( + &results, + BTreeMap::from([( + "fuzz/invariant/common/InvariantCalldataDictionary.t.sol:InvariantCalldataDictionary", + vec![("invariant_owner_never_changes()", true, None, None, None)], + )]), + ); + + // same test should fail when calldata address dict is bounded + // set address dictionary to single entry to fail fast + runner.test_options.invariant.dictionary.max_calldata_fuzz_dictionary_addresses = 1; + let results = runner.test_collect(&Filter::new( + ".*", + ".*", + ".*fuzz/invariant/common/InvariantCalldataDictionary.t.sol", + )); + assert_multiple( + &results, + BTreeMap::from([( + "fuzz/invariant/common/InvariantCalldataDictionary.t.sol:InvariantCalldataDictionary", + vec![( + "invariant_owner_never_changes()", + false, + Some("".into()), + None, + None, + )], + )]), + ); +} diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index 873e3071f..968a09280 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -92,6 +92,7 @@ pub static TEST_OPTS: Lazy = Lazy::new(|| { dictionary_weight: 40, max_fuzz_dictionary_addresses: 10_000, max_fuzz_dictionary_values: 10_000, + max_calldata_fuzz_dictionary_addresses: 0, }, }) .invariant(InvariantConfig { @@ -105,6 +106,7 @@ pub static TEST_OPTS: Lazy = Lazy::new(|| { include_push_bytes: true, max_fuzz_dictionary_addresses: 10_000, max_fuzz_dictionary_values: 10_000, + max_calldata_fuzz_dictionary_addresses: 0, }, shrink_sequence: true, shrink_run_limit: 2usize.pow(18u32), diff --git a/testdata/fuzz/invariant/common/InvariantCalldataDictionary.t.sol b/testdata/fuzz/invariant/common/InvariantCalldataDictionary.t.sol new file mode 100644 index 000000000..c8f87a600 --- /dev/null +++ b/testdata/fuzz/invariant/common/InvariantCalldataDictionary.t.sol @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "../../../cheats/Vm.sol"; + +struct FuzzSelector { + address addr; + bytes4[] selectors; +} + +// https://github.com/foundry-rs/foundry/issues/5868 +contract Owned { + address public owner; + address private ownerCandidate; + + constructor() { + owner = msg.sender; + } + + modifier onlyOwner() { + require(msg.sender == owner); + _; + } + + modifier onlyOwnerCandidate() { + require(msg.sender == ownerCandidate); + _; + } + + function transferOwnership(address candidate) external onlyOwner { + ownerCandidate = candidate; + } + + function acceptOwnership() external onlyOwnerCandidate { + owner = ownerCandidate; + } +} + +contract Handler is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + Owned owned; + + constructor(Owned _owned) { + owned = _owned; + } + + function transferOwnership(address sender, address candidate) external { + vm.assume(sender != address(0)); + vm.prank(sender); + owned.transferOwnership(candidate); + } + + function acceptOwnership(address sender) external { + vm.assume(sender != address(0)); + vm.prank(sender); + owned.acceptOwnership(); + } +} + +contract InvariantCalldataDictionary is DSTest { + address owner; + Owned owned; + Handler handler; + + function setUp() public { + owner = address(this); + owned = new Owned(); + handler = new Handler(owned); + } + + function targetSelectors() public returns (FuzzSelector[] memory) { + FuzzSelector[] memory targets = new FuzzSelector[](1); + bytes4[] memory selectors = new bytes4[](2); + selectors[0] = handler.transferOwnership.selector; + selectors[1] = handler.acceptOwnership.selector; + targets[0] = FuzzSelector(address(handler), selectors); + return targets; + } + + function invariant_owner_never_changes() public { + assertEq(owned.owner(), owner); + } +} From acdc57a4eef1660fdacc3c4d56f1186a76a7508d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 3 Mar 2024 12:37:51 +0100 Subject: [PATCH 022/430] chore(deps): weekly `cargo update` (#7296) Updating git repository `https://github.com/gakonst/ethers-rs` Updating git repository `https://github.com/bluealloy/revm` Updating git repository `https://github.com/alloy-rs/alloy` Updating git repository `https://github.com/paradigmxyz/evm-inspectors` Updating ahash v0.8.9 -> v0.8.10 Updating alloy-consensus v0.1.0 (https://github.com/alloy-rs/alloy#29a7886e) -> #52bf7125 Updating alloy-dyn-abi v0.6.3 -> v0.6.4 Updating alloy-eips v0.1.0 (https://github.com/alloy-rs/alloy#29a7886e) -> #52bf7125 Updating alloy-genesis v0.1.0 (https://github.com/alloy-rs/alloy#29a7886e) -> #52bf7125 Updating alloy-json-abi v0.6.3 -> v0.6.4 Updating alloy-json-rpc v0.1.0 (https://github.com/alloy-rs/alloy#29a7886e) -> #52bf7125 Updating alloy-network v0.1.0 (https://github.com/alloy-rs/alloy#29a7886e) -> #52bf7125 Updating alloy-primitives v0.6.3 -> v0.6.4 Updating alloy-providers v0.1.0 (https://github.com/alloy-rs/alloy#29a7886e) -> #52bf7125 Updating alloy-pubsub v0.1.0 (https://github.com/alloy-rs/alloy#29a7886e) -> #52bf7125 Updating alloy-rpc-client v0.1.0 (https://github.com/alloy-rs/alloy#29a7886e) -> #52bf7125 Updating alloy-rpc-trace-types v0.1.0 (https://github.com/alloy-rs/alloy#29a7886e) -> #52bf7125 Updating alloy-rpc-types v0.1.0 (https://github.com/alloy-rs/alloy#29a7886e) -> #52bf7125 Updating alloy-signer v0.1.0 (https://github.com/alloy-rs/alloy#29a7886e) -> #52bf7125 Updating alloy-sol-macro v0.6.3 -> v0.6.4 Updating alloy-sol-type-parser v0.6.3 -> v0.6.4 Updating alloy-sol-types v0.6.3 -> v0.6.4 Updating alloy-transport v0.1.0 (https://github.com/alloy-rs/alloy#29a7886e) -> #52bf7125 Updating alloy-transport-http v0.1.0 (https://github.com/alloy-rs/alloy#29a7886e) -> #52bf7125 Updating alloy-transport-ipc v0.1.0 (https://github.com/alloy-rs/alloy#29a7886e) -> #52bf7125 Updating alloy-transport-ws v0.1.0 (https://github.com/alloy-rs/alloy#29a7886e) -> #52bf7125 Updating anstream v0.6.12 -> v0.6.13 Updating auto_impl v1.1.2 -> v1.2.0 Updating cc v1.0.87 -> v1.0.88 Updating crossbeam-channel v0.5.11 -> v0.5.12 Updating dyn-clone v1.0.16 -> v1.0.17 Updating event-listener v5.1.0 -> v5.2.0 Updating evmole v0.3.2 -> v0.3.3 Updating gix-utils v0.1.9 -> v0.1.10 Updating half v2.3.1 -> v2.4.0 Updating hermit-abi v0.3.8 -> v0.3.9 Updating indexmap v2.2.3 -> v2.2.5 Updating lalrpop v0.20.0 -> v0.20.2 Updating lalrpop-util v0.20.0 -> v0.20.2 Updating log v0.4.20 -> v0.4.21 Updating mio v0.8.10 -> v0.8.11 Updating opaque-debug v0.3.0 -> v0.3.1 Updating pest v2.7.7 -> v2.7.8 Updating pest_derive v2.7.7 -> v2.7.8 Updating pest_generator v2.7.7 -> v2.7.8 Updating pest_meta v2.7.7 -> v2.7.8 Adding proc-macro-crate v3.1.0 Updating rayon v1.8.1 -> v1.9.0 Removing regex-syntax v0.7.5 Updating ruint v1.11.1 -> v1.12.0 Updating ruint-macro v1.1.0 -> v1.2.0 Updating syn v2.0.50 -> v2.0.52 Updating syn-solidity v0.6.3 -> v0.6.4 Updating tempfile v3.10.0 -> v3.10.1 Updating walkdir v2.4.0 -> v2.5.0 Updating windows-targets v0.52.3 -> v0.52.4 Updating windows_aarch64_gnullvm v0.52.3 -> v0.52.4 Updating windows_aarch64_msvc v0.52.3 -> v0.52.4 Updating windows_i686_gnu v0.52.3 -> v0.52.4 Updating windows_i686_msvc v0.52.3 -> v0.52.4 Updating windows_x86_64_gnu v0.52.3 -> v0.52.4 Updating windows_x86_64_gnullvm v0.52.3 -> v0.52.4 Updating windows_x86_64_msvc v0.52.3 -> v0.52.4 Updating winnow v0.6.2 -> v0.6.5 note: pass `--verbose` to see 178 unchanged dependencies behind latest Co-authored-by: mattsse --- Cargo.lock | 341 +++++++++++++++++++++++++++-------------------------- 1 file changed, 173 insertions(+), 168 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a3ec2e699..c9216e3cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -51,9 +51,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d713b3834d76b85304d4d525563c1276e2e30dc97cc67bfb4585a4a29fc2c89f" +checksum = "8b79b82693f705137f8fb9b37871d99e4f9a7df12b917eed79c3d3954830a60b" dependencies = [ "cfg-if", "once_cell", @@ -90,7 +90,7 @@ dependencies = [ [[package]] name = "alloy-consensus" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#29a7886e1311062c589a6853a0f21f32901835d9" +source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" dependencies = [ "alloy-eips", "alloy-network", @@ -100,9 +100,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13b1a44ed6b4126e4818d20c9e48176ae9d6d4fcbe6c909f8cd0bf050eb56fd8" +checksum = "2919acdad13336bc5dc26b636cdd6892c2f27fb0d4a58320a00c2713cf6a4e9a" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -116,13 +116,13 @@ dependencies = [ "proptest", "serde", "serde_json", - "winnow 0.5.40", + "winnow 0.6.5", ] [[package]] name = "alloy-eips" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#29a7886e1311062c589a6853a0f21f32901835d9" +source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -133,7 +133,7 @@ dependencies = [ [[package]] name = "alloy-genesis" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#29a7886e1311062c589a6853a0f21f32901835d9" +source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -142,9 +142,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30c6a6c5140fc762edfe55349f9ddefa821f4b7f2339cef582de911a3f1fb6d3" +checksum = "24ed0f2a6c3a1c947b4508522a53a190dba8f94dcd4e3e1a5af945a498e78f2f" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -155,7 +155,7 @@ dependencies = [ [[package]] name = "alloy-json-rpc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#29a7886e1311062c589a6853a0f21f32901835d9" +source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" dependencies = [ "alloy-primitives", "serde", @@ -166,7 +166,7 @@ dependencies = [ [[package]] name = "alloy-network" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#29a7886e1311062c589a6853a0f21f32901835d9" +source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" dependencies = [ "alloy-eips", "alloy-json-rpc", @@ -177,9 +177,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef197eb250c64962003cb08b90b17f0882c192f4a6f2f544809d424fd7cb0e7d" +checksum = "600d34d8de81e23b6d909c094e23b3d357e01ca36b78a8c5424c501eedbe86f0" dependencies = [ "alloy-rlp", "arbitrary", @@ -205,7 +205,7 @@ dependencies = [ [[package]] name = "alloy-providers" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#29a7886e1311062c589a6853a0f21f32901835d9" +source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" dependencies = [ "alloy-network", "alloy-primitives", @@ -224,7 +224,7 @@ dependencies = [ [[package]] name = "alloy-pubsub" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#29a7886e1311062c589a6853a0f21f32901835d9" +source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -257,13 +257,13 @@ checksum = "1a047897373be4bbb0224c1afdabca92648dc57a9c9ef6e7b0be3aff7a859c83" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] name = "alloy-rpc-client" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#29a7886e1311062c589a6853a0f21f32901835d9" +source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -280,7 +280,7 @@ dependencies = [ [[package]] name = "alloy-rpc-trace-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#29a7886e1311062c589a6853a0f21f32901835d9" +source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -291,7 +291,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#29a7886e1311062c589a6853a0f21f32901835d9" +source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -304,7 +304,7 @@ dependencies = [ [[package]] name = "alloy-signer" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#29a7886e1311062c589a6853a0f21f32901835d9" +source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" dependencies = [ "alloy-network", "alloy-primitives", @@ -322,9 +322,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e92100dee7fd1e44abbe0ef6607f18758cf0ad4e483f4c65ff5c8d85428a6d" +checksum = "e86ec0a47740b20bc5613b8712d0d321d031c4efc58e9645af96085d5cccfc27" dependencies = [ "alloy-json-abi", "const-hex", @@ -335,25 +335,25 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.50", + "syn 2.0.52", "syn-solidity", "tiny-keccak", ] [[package]] name = "alloy-sol-type-parser" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d146adca22a853b5aaaa98a6c78bd9d8f1d627ca7b01d170edccf45430e9b2cb" +checksum = "0045cc89524e1451ccf33e8581355b6027ac7c6e494bb02959d4213ad0d8e91d" dependencies = [ - "winnow 0.5.40", + "winnow 0.6.5", ] [[package]] name = "alloy-sol-types" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e7c6a8c492b1d6a4f92a8fc6a13cf39473978dd7d459d7221969ce5a73d97cd" +checksum = "ad09ec5853fa700d12d778ad224dcdec636af424d29fad84fb9a2f16a5b0ef09" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -365,7 +365,7 @@ dependencies = [ [[package]] name = "alloy-transport" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#29a7886e1311062c589a6853a0f21f32901835d9" +source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" dependencies = [ "alloy-json-rpc", "base64 0.21.7", @@ -382,7 +382,7 @@ dependencies = [ [[package]] name = "alloy-transport-http" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#29a7886e1311062c589a6853a0f21f32901835d9" +source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -395,7 +395,7 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#29a7886e1311062c589a6853a0f21f32901835d9" +source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -413,7 +413,7 @@ dependencies = [ [[package]] name = "alloy-transport-ws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#29a7886e1311062c589a6853a0f21f32901835d9" +source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -462,9 +462,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.12" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" dependencies = [ "anstyle", "anstyle-parse", @@ -797,7 +797,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" dependencies = [ "concurrent-queue", - "event-listener 5.1.0", + "event-listener 5.2.0", "event-listener-strategy 0.5.0", "futures-core", "pin-project-lite", @@ -831,7 +831,7 @@ checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -848,7 +848,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -895,13 +895,13 @@ dependencies = [ [[package]] name = "auto_impl" -version = "1.1.2" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "823b8bb275161044e2ac7a25879cb3e2480cb403e3943022c7c769c599b756aa" +checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -1309,9 +1309,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.87" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3286b845d0fccbdd15af433f61c5970e711987036cb468f437ff6badd70f4e24" +checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc" dependencies = [ "libc", ] @@ -1370,7 +1370,7 @@ dependencies = [ "iana-time-zone", "num-traits", "serde", - "windows-targets 0.52.3", + "windows-targets 0.52.4", ] [[package]] @@ -1463,7 +1463,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -1779,9 +1779,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" dependencies = [ "crossbeam-utils", ] @@ -1914,7 +1914,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -1925,7 +1925,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -1986,7 +1986,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -2007,7 +2007,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -2017,7 +2017,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" dependencies = [ "derive_builder_core", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -2147,9 +2147,9 @@ checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" [[package]] name = "dyn-clone" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" [[package]] name = "ecdsa" @@ -2259,7 +2259,7 @@ checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -2467,7 +2467,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "syn 2.0.50", + "syn 2.0.52", "toml 0.8.10", "walkdir", ] @@ -2484,7 +2484,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -2509,7 +2509,7 @@ dependencies = [ "serde", "serde_json", "strum 0.26.1", - "syn 2.0.50", + "syn 2.0.52", "tempfile", "thiserror", "tiny-keccak", @@ -2678,9 +2678,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "5.1.0" +version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7ad6fd685ce13acd6d9541a30f6db6567a7a24c9ffd4ba2955d29e3f22c8b27" +checksum = "2b5fb89194fa3cad959b833185b3063ba881dbfc7030680b314250779fb4cc91" dependencies = [ "concurrent-queue", "parking", @@ -2703,7 +2703,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "feedafcaa9b749175d5ac357452a9d41ea2911da598fde46ce1fe02c37751291" dependencies = [ - "event-listener 5.1.0", + "event-listener 5.2.0", "pin-project-lite", ] @@ -2719,9 +2719,9 @@ dependencies = [ [[package]] name = "evmole" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ef57dfcf13fc3486c3a760427d88ab0d97cb911f7104fe5a132f2b934d0fe29" +checksum = "cd4e05af4c306bcba507bd358feac33ec73f4314a89bd93758d035c629f2f5fe" dependencies = [ "ruint", ] @@ -2763,7 +2763,7 @@ dependencies = [ "bytes", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -3380,7 +3380,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -3547,7 +3547,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -3840,9 +3840,9 @@ checksum = "02b202d766a7fefc596e2cc6a89cda8ad8ad733aed82da635ac120691112a9b1" [[package]] name = "gix-utils" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e839f3d0798b296411263da6bee780a176ef8008a5dfc31287f7eda9266ab8" +checksum = "60157a15b9f14b11af1c6817ad7a93b10b50b4e5136d98a127c46a37ff16eeb6" dependencies = [ "fastrand", "unicode-normalization", @@ -3921,9 +3921,9 @@ dependencies = [ [[package]] name = "half" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc52e53916c08643f1b56ec082790d1e86a32e58dc5268f897f313fbae7b4872" +checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e" dependencies = [ "cfg-if", "crunchy", @@ -3973,7 +3973,7 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ - "ahash 0.8.9", + "ahash 0.8.10", "allocator-api2", "serde", ] @@ -3995,9 +3995,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "379dada1584ad501b383485dd706b8afb7a70fcbc7f4da7d780638a5a6124a60" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex" @@ -4299,9 +4299,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "2.2.3" +version = "2.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" +checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -4547,31 +4547,33 @@ dependencies = [ [[package]] name = "lalrpop" -version = "0.20.0" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da4081d44f4611b66c6dd725e6de3169f9f63905421e8626fcb86b6a898998b8" +checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" dependencies = [ "ascii-canvas", "bit-set", - "diff", "ena", - "is-terminal", - "itertools 0.10.5", + "itertools 0.11.0", "lalrpop-util", "petgraph", "regex", - "regex-syntax 0.7.5", + "regex-syntax 0.8.2", "string_cache", "term", "tiny-keccak", "unicode-xid", + "walkdir", ] [[package]] name = "lalrpop-util" -version = "0.20.0" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f35c735096c0293d313e8f2a641627472b83d01b937177fe76e5e2708d31e0d" +checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" +dependencies = [ + "regex-automata 0.4.5", +] [[package]] name = "lazy_static" @@ -4635,9 +4637,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "lru" @@ -4802,7 +4804,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -4838,9 +4840,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", @@ -5065,10 +5067,10 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -5109,9 +5111,9 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "opaque-debug" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "open-fastrlp" @@ -5172,7 +5174,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -5382,7 +5384,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -5411,9 +5413,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.7" +version = "2.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219c0dcc30b6a27553f9cc242972b67f75b60eb0db71f0b5462f38b058c41546" +checksum = "56f8023d0fb78c8e03784ea1c7f3fa36e68a723138990b8d5a47d916b651e7a8" dependencies = [ "memchr", "thiserror", @@ -5422,9 +5424,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.7" +version = "2.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e1288dbd7786462961e69bfd4df7848c1e37e8b74303dbdab82c3a9cdd2809" +checksum = "b0d24f72393fd16ab6ac5738bc33cdb6a9aa73f8b902e8fe29cf4e67d7dd1026" dependencies = [ "pest", "pest_generator", @@ -5432,22 +5434,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.7" +version = "2.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1381c29a877c6d34b8c176e734f35d7f7f5b3adaefe940cb4d1bb7af94678e2e" +checksum = "fdc17e2a6c7d0a492f0158d7a4bd66cc17280308bbaff78d5bef566dca35ab80" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] name = "pest_meta" -version = "2.7.7" +version = "2.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0934d6907f148c22a3acbda520c7eed243ad7487a30f51f6ce52b58b7077a8a" +checksum = "934cd7631c050f4674352a6e835d5f6711ffbfb9345c2fc0107155ac495ae293" dependencies = [ "once_cell", "pest", @@ -5543,7 +5545,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -5581,7 +5583,7 @@ checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -5692,7 +5694,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" dependencies = [ "proc-macro2", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -5737,6 +5739,15 @@ dependencies = [ "toml_edit 0.20.7", ] +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit 0.21.1", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -5778,7 +5789,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", "version_check", "yansi 1.0.0-rc.1", ] @@ -5994,9 +6005,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051" +checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" dependencies = [ "either", "rayon-core", @@ -6084,12 +6095,6 @@ version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" -[[package]] -name = "regex-syntax" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" - [[package]] name = "regex-syntax" version = "0.8.2" @@ -6303,9 +6308,9 @@ dependencies = [ [[package]] name = "ruint" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608a5726529f2f0ef81b8fde9873c4bb829d6b5b5ca6be4d97345ddf0749c825" +checksum = "49b1d9521f889713d1221270fdd63370feca7e5c71a18745343402fa86e4f04f" dependencies = [ "alloy-rlp", "arbitrary", @@ -6328,9 +6333,9 @@ dependencies = [ [[package]] name = "ruint-macro" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e666a5496a0b2186dbcd0ff6106e29e093c15591bde62c20d3842007c6978a09" +checksum = "f86854cf50259291520509879a5c294c3c9a4c334e9ff65071c51e42ef1e2343" [[package]] name = "rusb" @@ -6784,7 +6789,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -6838,7 +6843,7 @@ checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -6884,7 +6889,7 @@ checksum = "b93fb4adc70021ac1b47f7d45e8cc4169baaa7ea58483bc5b721d19a26202212" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -7181,7 +7186,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -7194,7 +7199,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -7275,9 +7280,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.50" +version = "2.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" dependencies = [ "proc-macro2", "quote", @@ -7286,14 +7291,14 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e656cbcef8a77543b5accbd76f60f9e0bc4be364b0aba4263a6f313f8a355511" +checksum = "cb3d0961cd53c23ea94eeec56ba940f636f6394788976e9f16ca5ee0aca7464a" dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -7343,9 +7348,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.10.0" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", @@ -7415,7 +7420,7 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -7537,7 +7542,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -7688,7 +7693,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.2", + "winnow 0.6.5", ] [[package]] @@ -7770,7 +7775,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -8086,9 +8091,9 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -8136,7 +8141,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", "wasm-bindgen-shared", ] @@ -8170,7 +8175,7 @@ checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -8313,7 +8318,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.3", + "windows-targets 0.52.4", ] [[package]] @@ -8340,7 +8345,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.3", + "windows-targets 0.52.4", ] [[package]] @@ -8375,17 +8380,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" dependencies = [ - "windows_aarch64_gnullvm 0.52.3", - "windows_aarch64_msvc 0.52.3", - "windows_i686_gnu 0.52.3", - "windows_i686_msvc 0.52.3", - "windows_x86_64_gnu 0.52.3", - "windows_x86_64_gnullvm 0.52.3", - "windows_x86_64_msvc 0.52.3", + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", ] [[package]] @@ -8402,9 +8407,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" [[package]] name = "windows_aarch64_msvc" @@ -8420,9 +8425,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" [[package]] name = "windows_i686_gnu" @@ -8438,9 +8443,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" [[package]] name = "windows_i686_msvc" @@ -8456,9 +8461,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" [[package]] name = "windows_x86_64_gnu" @@ -8474,9 +8479,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" [[package]] name = "windows_x86_64_gnullvm" @@ -8492,9 +8497,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" [[package]] name = "windows_x86_64_msvc" @@ -8510,9 +8515,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" [[package]] name = "winnow" @@ -8525,9 +8530,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.2" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a4191c47f15cc3ec71fcb4913cb83d58def65dd3787610213c649283b5ce178" +checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" dependencies = [ "memchr", ] @@ -8605,7 +8610,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -8625,7 +8630,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] From 3df3e0c0d72dedcba3ea57afd2f57ebd231624dd Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sun, 3 Mar 2024 16:01:24 +0400 Subject: [PATCH 023/430] feat(forge verify-contract): `--guess-constructor-args` (#6724) * Add RpcOpts to VerifyArgs * --guess-constructor-args * Add support for CREATE2 deployer * Fix artifact lookup * Update verification tests + Test for --guess-constructor-args * chore: clippy * update compilation + separate function * doc --------- Co-authored-by: Matthias Seitz --- crates/forge/bin/cmd/create.rs | 9 +- crates/forge/bin/cmd/script/verify.rs | 2 + crates/forge/bin/cmd/verify/etherscan/mod.rs | 87 ++++++++++++- crates/forge/bin/cmd/verify/mod.rs | 33 ++++- crates/forge/tests/cli/verify.rs | 123 ++++++++++++++----- 5 files changed, 214 insertions(+), 40 deletions(-) diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index 194428191..ed9a17aad 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -176,7 +176,11 @@ impl CreateArgs { constructor_args, constructor_args_path: None, num_of_optimizations: None, - etherscan: EtherscanOpts { key: self.eth.etherscan.key(), chain: Some(chain.into()) }, + etherscan: EtherscanOpts { + key: self.eth.etherscan.key.clone(), + chain: Some(chain.into()), + }, + rpc: Default::default(), flatten: false, force: false, skip_is_verified_check: true, @@ -188,6 +192,7 @@ impl CreateArgs { via_ir: self.opts.via_ir, evm_version: self.opts.compiler.evm_version, show_standard_json_input: self.show_standard_json_input, + guess_constructor_args: false, }; // Check config for Etherscan API Keys to avoid preflight check failing if no @@ -326,6 +331,7 @@ impl CreateArgs { constructor_args_path: None, num_of_optimizations, etherscan: EtherscanOpts { key: self.eth.etherscan.key(), chain: Some(chain.into()) }, + rpc: Default::default(), flatten: false, force: false, skip_is_verified_check: false, @@ -337,6 +343,7 @@ impl CreateArgs { via_ir: self.opts.via_ir, evm_version: self.opts.compiler.evm_version, show_standard_json_input: self.show_standard_json_input, + guess_constructor_args: false, }; println!("Waiting for {} to detect contract deployment...", verify.verifier.verifier); verify.run().await diff --git a/crates/forge/bin/cmd/script/verify.rs b/crates/forge/bin/cmd/script/verify.rs index 6498b8f8a..96b778eda 100644 --- a/crates/forge/bin/cmd/script/verify.rs +++ b/crates/forge/bin/cmd/script/verify.rs @@ -105,6 +105,7 @@ impl VerifyBundle { constructor_args_path: None, num_of_optimizations: self.num_of_optimizations, etherscan: self.etherscan.clone(), + rpc: Default::default(), flatten: false, force: false, skip_is_verified_check: true, @@ -116,6 +117,7 @@ impl VerifyBundle { via_ir: self.via_ir, evm_version: None, show_standard_json_input: false, + guess_constructor_args: false, }; return Some(verify) diff --git a/crates/forge/bin/cmd/verify/etherscan/mod.rs b/crates/forge/bin/cmd/verify/etherscan/mod.rs index 919c83b8f..f63db1240 100644 --- a/crates/forge/bin/cmd/verify/etherscan/mod.rs +++ b/crates/forge/bin/cmd/verify/etherscan/mod.rs @@ -1,7 +1,8 @@ use super::{provider::VerificationProvider, VerifyArgs, VerifyCheckArgs}; use crate::cmd::retry::RETRY_CHECK_ON_VERIFY; use alloy_json_abi::Function; -use eyre::{eyre, Context, Result}; +use ethers_providers::Middleware; +use eyre::{eyre, Context, OptionExt, Result}; use forge::hashbrown::HashSet; use foundry_block_explorers::{ errors::EtherscanError, @@ -9,12 +10,16 @@ use foundry_block_explorers::{ verify::{CodeFormat, VerifyContract}, Client, }; -use foundry_cli::utils::{get_cached_entry_by_name, read_constructor_args_file, LoadConfig}; -use foundry_common::{abi::encode_function_args, retry::Retry}; +use foundry_cli::utils::{self, get_cached_entry_by_name, read_constructor_args_file, LoadConfig}; +use foundry_common::{abi::encode_function_args, retry::Retry, types::ToEthers}; use foundry_compilers::{ - artifacts::CompactContract, cache::CacheEntry, info::ContractInfo, Project, Solc, + artifacts::{BytecodeObject, CompactContract}, + cache::CacheEntry, + info::ContractInfo, + Artifact, Project, Solc, }; use foundry_config::{Chain, Config, SolcReq}; +use foundry_evm::constants::DEFAULT_CREATE2_DEPLOYER; use futures::FutureExt; use once_cell::sync::Lazy; use regex::Regex; @@ -332,7 +337,7 @@ impl EtherscanVerificationProvider { self.source_provider(args).source(args, &project, &contract_path, &compiler_version)?; let compiler_version = format!("v{}", ensure_solc_build_metadata(compiler_version).await?); - let constructor_args = self.constructor_args(args, &project)?; + let constructor_args = self.constructor_args(args, &project, &config).await?; let mut verify_args = VerifyContract::new(args.address, contract_name, source, compiler_version) .constructor_arguments(constructor_args) @@ -435,7 +440,12 @@ impl EtherscanVerificationProvider { /// Return the optional encoded constructor arguments. If the path to /// constructor arguments was provided, read them and encode. Otherwise, /// return whatever was set in the [VerifyArgs] args. - fn constructor_args(&mut self, args: &VerifyArgs, project: &Project) -> Result> { + async fn constructor_args( + &mut self, + args: &VerifyArgs, + project: &Project, + config: &Config, + ) -> Result> { if let Some(ref constructor_args_path) = args.constructor_args_path { let (_, _, contract) = self.cache_entry(project, &args.contract).wrap_err( "Cache must be enabled in order to use the `--constructor-args-path` option", @@ -459,9 +469,74 @@ impl EtherscanVerificationProvider { let encoded_args = hex::encode(encoded_args); return Ok(Some(encoded_args[8..].into())) } + if args.guess_constructor_args { + return Ok(Some(self.guess_constructor_args(args, project, config).await?)) + } Ok(args.constructor_args.clone()) } + + /// Uses Etherscan API to fetch contract creation transaction. + /// If transaction is a create transaction or a invocation of default CREATE2 deployer, tries to + /// match provided creation code with local bytecode of the target contract. + /// If bytecode match, returns latest bytes of on-chain creation code as constructor arguments. + async fn guess_constructor_args( + &mut self, + args: &VerifyArgs, + project: &Project, + config: &Config, + ) -> Result { + let provider = utils::get_provider(config)?; + let client = self.client( + args.etherscan.chain.unwrap_or_default(), + args.verifier.verifier_url.as_deref(), + args.etherscan.key.as_deref(), + config, + )?; + + let creation_data = client.contract_creation_data(args.address).await?; + let transaction = provider + .get_transaction(creation_data.transaction_hash.to_ethers()) + .await? + .ok_or_eyre("Couldn't fetch transaction data from RPC")?; + let receipt = provider + .get_transaction_receipt(creation_data.transaction_hash.to_ethers()) + .await? + .ok_or_eyre("Couldn't fetch transaction receipt from RPC")?; + + let maybe_creation_code: &[u8]; + + if receipt.contract_address == Some(args.address.to_ethers()) { + maybe_creation_code = &transaction.input; + } else if transaction.to == Some(DEFAULT_CREATE2_DEPLOYER.to_ethers()) { + maybe_creation_code = &transaction.input[32..]; + } else { + eyre::bail!("Fetching of constructor arguments is not supported for contracts created by contracts") + } + + let contract_path = self.contract_path(args, project)?.to_string_lossy().into_owned(); + let output = project.compile()?; + let artifact = output + .find(contract_path, &args.contract.name) + .ok_or_eyre("Contract artifact wasn't found locally")?; + let bytecode = artifact + .get_bytecode_object() + .ok_or_eyre("Contract artifact does not contain bytecode")?; + + let bytecode = match bytecode.as_ref() { + BytecodeObject::Bytecode(bytes) => Ok(bytes), + BytecodeObject::Unlinked(_) => { + Err(eyre!("You have to provide correct libraries to use --guess-constructor-args")) + } + }?; + + if maybe_creation_code.starts_with(bytecode) { + let constructor_args = &maybe_creation_code[bytecode.len()..]; + Ok(hex::encode(constructor_args)) + } else { + eyre::bail!("Local bytecode doesn't match on-chain bytecode") + } + } } /// Given any solc [Version] return a [Version] with build metadata diff --git a/crates/forge/bin/cmd/verify/mod.rs b/crates/forge/bin/cmd/verify/mod.rs index f9839d717..eeff2cb83 100644 --- a/crates/forge/bin/cmd/verify/mod.rs +++ b/crates/forge/bin/cmd/verify/mod.rs @@ -2,7 +2,11 @@ use super::retry::RetryArgs; use alloy_primitives::Address; use clap::{Parser, ValueHint}; use eyre::Result; -use foundry_cli::{opts::EtherscanOpts, utils::LoadConfig}; +use foundry_cli::{ + opts::{EtherscanOpts, RpcOpts}, + utils, + utils::LoadConfig, +}; use foundry_compilers::{info::ContractInfo, EvmVersion}; use foundry_config::{figment, impl_figment_convert, impl_figment_convert_cast, Config}; use provider::VerificationProviderType; @@ -57,6 +61,10 @@ pub struct VerifyArgs { #[arg(long, value_hint = ValueHint::FilePath, value_name = "PATH")] pub constructor_args_path: Option, + /// Try to extract constructor arguments from on-chain creation code. + #[arg(long)] + pub guess_constructor_args: bool, + /// The `solc` version to use to build the smart contract. #[arg(long, value_name = "VERSION")] pub compiler_version: Option, @@ -112,6 +120,9 @@ pub struct VerifyArgs { #[command(flatten)] pub etherscan: EtherscanOpts, + #[command(flatten)] + pub rpc: RpcOpts, + #[command(flatten)] pub retry: RetryArgs, @@ -130,6 +141,8 @@ impl figment::Provider for VerifyArgs { &self, ) -> Result, figment::Error> { let mut dict = self.etherscan.dict(); + dict.extend(self.rpc.dict()); + if let Some(root) = self.root.as_ref() { dict.insert("root".to_string(), figment::value::Value::serialize(root)?); } @@ -154,7 +167,23 @@ impl VerifyArgs { /// Run the verify command to submit the contract's source code for verification on etherscan pub async fn run(mut self) -> Result<()> { let config = self.load_config_emit_warnings(); - let chain = config.chain.unwrap_or_default(); + + if self.guess_constructor_args && config.get_rpc_url().is_none() { + eyre::bail!( + "You have to provide a valid RPC URL to use --guess-constructor-args feature" + ) + } + + // If chain is not set, we try to get it from the RPC + // If RPC is not set, the default chain is used + let chain = match config.get_rpc_url() { + Some(_) => { + let provider = utils::get_provider(&config)?; + utils::get_chain(config.chain, provider).await? + } + None => config.chain.unwrap_or_default(), + }; + self.etherscan.chain = Some(chain); self.etherscan.key = config.get_etherscan_config_with_chain(Some(chain))?.map(|c| c.key); diff --git a/crates/forge/tests/cli/verify.rs b/crates/forge/tests/cli/verify.rs index 8874af953..deedcc9ab 100644 --- a/crates/forge/tests/cli/verify.rs +++ b/crates/forge/tests/cli/verify.rs @@ -55,6 +55,24 @@ function doStuff() external {{}} prj.add_source("Verify.sol", &contract).unwrap(); } +fn add_verify_target_with_constructor(prj: &TestProject) { + prj.add_source( + "Verify.sol", + r#" +import {Unique} from "./unique.sol"; +contract Verify is Unique { + struct SomeStruct { + uint256 a; + string str; + } + + constructor(SomeStruct memory st, address owner) {} +} +"#, + ) + .unwrap(); +} + fn parse_verification_result(cmd: &mut TestCommand, retries: u32) -> eyre::Result<()> { // give etherscan some time to verify the contract let retry = Retry::new(retries, Some(Duration::from_secs(30))); @@ -73,6 +91,39 @@ fn parse_verification_result(cmd: &mut TestCommand, retries: u32) -> eyre::Resul }) } +fn await_verification_response(info: EnvExternalities, mut cmd: TestCommand) { + let guid = { + // give etherscan some time to detect the transaction + let retry = Retry::new(5, Some(Duration::from_secs(60))); + retry + .run(|| -> eyre::Result { + let output = cmd.unchecked_output(); + let out = String::from_utf8_lossy(&output.stdout); + utils::parse_verification_guid(&out).ok_or_else(|| { + eyre::eyre!( + "Failed to get guid, stdout: {}, stderr: {}", + out, + String::from_utf8_lossy(&output.stderr) + ) + }) + }) + .expect("Failed to get verify guid") + }; + + // verify-check + cmd.forge_fuse() + .arg("verify-check") + .arg(guid) + .arg("--chain-id") + .arg(info.chain.to_string()) + .arg("--etherscan-api-key") + .arg(info.etherscan) + .arg("--verifier") + .arg(info.verifier); + + parse_verification_result(&mut cmd, 6).expect("Failed to verify check") +} + fn verify_on_chain(info: Option, prj: TestProject, mut cmd: TestCommand) { // only execute if keys present if let Some(info) = info { @@ -98,37 +149,41 @@ fn verify_on_chain(info: Option, prj: TestProject, mut cmd: Te info.verifier.to_string(), ]); - // `verify-contract` - let guid = { - // give etherscan some time to detect the transaction - let retry = Retry::new(5, Some(Duration::from_secs(60))); - retry - .run(|| -> eyre::Result { - let output = cmd.unchecked_output(); - let out = String::from_utf8_lossy(&output.stdout); - utils::parse_verification_guid(&out).ok_or_else(|| { - eyre::eyre!( - "Failed to get guid, stdout: {}, stderr: {}", - out, - String::from_utf8_lossy(&output.stderr) - ) - }) - }) - .expect("Failed to get verify guid") - }; - - // verify-check - cmd.forge_fuse() - .arg("verify-check") - .arg(guid) - .arg("--chain-id") - .arg(info.chain.to_string()) - .arg("--etherscan-api-key") - .arg(info.etherscan) - .arg("--verifier") - .arg(info.verifier); - - parse_verification_result(&mut cmd, 6).expect("Failed to verify check") + await_verification_response(info, cmd) + } +} + +fn guess_constructor_args(info: Option, prj: TestProject, mut cmd: TestCommand) { + // only execute if keys present + if let Some(info) = info { + println!("verifying on {}", info.chain); + add_unique(&prj); + add_verify_target_with_constructor(&prj); + + let contract_path = "src/Verify.sol:Verify"; + cmd.arg("create").args(info.create_args()).arg(contract_path).args(vec![ + "--constructor-args", + "(239,SomeString)", + "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", + ]); + + let out = cmd.stdout_lossy(); + let address = utils::parse_deployed_address(out.as_str()) + .unwrap_or_else(|| panic!("Failed to parse deployer {out}")); + + cmd.forge_fuse().arg("verify-contract").root_arg().args([ + "--rpc-url".to_string(), + info.rpc.to_string(), + address, + contract_path.to_string(), + "--etherscan-api-key".to_string(), + info.etherscan.to_string(), + "--verifier".to_string(), + info.verifier.to_string(), + "--guess-constructor-args".to_string(), + ]); + + await_verification_response(info, cmd) } } @@ -174,3 +229,9 @@ forgetest!(can_verify_random_contract_sepolia, |prj, cmd| { forgetest!(can_create_verify_random_contract_sepolia, |prj, cmd| { create_verify_on_chain(EnvExternalities::sepolia(), prj, cmd); }); + +// tests `create && contract-verify --guess-constructor-args && verify-check` on Goerli testnet if +// correct env vars are set +forgetest!(can_guess_constructor_args, |prj, cmd| { + guess_constructor_args(EnvExternalities::goerli(), prj, cmd); +}); From c24933da985419ea143de7e8636d5b0a48d2fab7 Mon Sep 17 00:00:00 2001 From: James Date: Sun, 3 Mar 2024 18:28:02 -0700 Subject: [PATCH 024/430] Add transient storage warning to default `ignored_error_codes` (#7299) * add transient-storage error code and add to default ignored * add reverse lookups --- crates/config/src/error.rs | 6 ++++++ crates/config/src/lib.rs | 1 + 2 files changed, 7 insertions(+) diff --git a/crates/config/src/error.rs b/crates/config/src/error.rs index b4f7b85aa..00692d67d 100644 --- a/crates/config/src/error.rs +++ b/crates/config/src/error.rs @@ -134,6 +134,8 @@ pub enum SolidityErrorCode { Unreachable, /// Missing pragma solidity PragmaSolidity, + /// Uses transient opcodes + TransientStorageUsed, /// All other error codes Other(u64), } @@ -162,6 +164,7 @@ impl SolidityErrorCode { SolidityErrorCode::PragmaSolidity => "pragma-solidity", SolidityErrorCode::Other(code) => return Err(*code), SolidityErrorCode::VisibilityForConstructorIsIgnored => "constructor-visibility", + SolidityErrorCode::TransientStorageUsed => "transient-storage", }; Ok(s) } @@ -185,6 +188,7 @@ impl From for u64 { SolidityErrorCode::PragmaSolidity => 3420, SolidityErrorCode::ContractInitCodeSizeExceeds49152Bytes => 3860, SolidityErrorCode::VisibilityForConstructorIsIgnored => 2462, + SolidityErrorCode::TransientStorageUsed => 2394, SolidityErrorCode::Other(code) => code, } } @@ -218,6 +222,7 @@ impl FromStr for SolidityErrorCode { "missing-receive-ether" => SolidityErrorCode::PayableNoReceiveEther, "same-varname" => SolidityErrorCode::DeclarationSameNameAsAnother, "constructor-visibility" => SolidityErrorCode::VisibilityForConstructorIsIgnored, + "transient-storage" => SolidityErrorCode::TransientStorageUsed, _ => return Err(format!("Unknown variant {s}")), }; @@ -243,6 +248,7 @@ impl From for SolidityErrorCode { 3420 => SolidityErrorCode::PragmaSolidity, 5740 => SolidityErrorCode::Unreachable, 2462 => SolidityErrorCode::VisibilityForConstructorIsIgnored, + 2394 => SolidityErrorCode::TransientStorageUsed, other => SolidityErrorCode::Other(other), } } diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 5c704d6c7..d9853fae6 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -1901,6 +1901,7 @@ impl Default for Config { SolidityErrorCode::SpdxLicenseNotProvided, SolidityErrorCode::ContractExceeds24576Bytes, SolidityErrorCode::ContractInitCodeSizeExceeds49152Bytes, + SolidityErrorCode::TransientStorageUsed, ], ignored_file_paths: vec![], deny_warnings: false, From d176715dbb5193f60008df7c4c78515070aa3d6b Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 4 Mar 2024 15:04:58 +0100 Subject: [PATCH 025/430] chore: reduce unnecessary collected fuzz state (#7305) --- crates/evm/fuzz/src/strategies/state.rs | 27 +++++++++++++------------ 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/crates/evm/fuzz/src/strategies/state.rs b/crates/evm/fuzz/src/strategies/state.rs index 2beedb1e7..862b38bca 100644 --- a/crates/evm/fuzz/src/strategies/state.rs +++ b/crates/evm/fuzz/src/strategies/state.rs @@ -14,7 +14,7 @@ use revm::{ interpreter::opcode::{self, spec_opcode_gas}, primitives::SpecId, }; -use std::{fmt, io::Write, str::FromStr, sync::Arc}; +use std::{fmt, str::FromStr, sync::Arc}; /// A set of arbitrary 32 byte data from the VM used to generate values for the strategy. /// @@ -185,19 +185,20 @@ pub fn collect_state_from_call( } else { return; } + } - // Insert log topics and data - for log in logs { - log.data.topics().iter().for_each(|topic| { - state.values_mut().insert(topic.0); - }); - log.data.data.chunks(32).for_each(|chunk| { - let mut buffer: [u8; 32] = [0; 32]; - let _ = (&mut buffer[..]) - .write(chunk) - .expect("log data chunk was larger than 32 bytes"); - state.values_mut().insert(buffer); - }); + // Insert log topics and data. + for log in logs { + for topic in log.topics() { + state.values_mut().insert(topic.0); + } + let chunks = log.data.data.chunks_exact(32); + let rem = chunks.remainder(); + for chunk in chunks { + state.values_mut().insert(chunk.try_into().unwrap()); + } + if !rem.is_empty() { + state.values_mut().insert(B256::right_padding_from(rem).0); } } } From b8047b7cb94a91358d32aab8290bf4927848c323 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 4 Mar 2024 16:31:50 +0100 Subject: [PATCH 026/430] chore: reduce unnecessary collected fuzz state (#7306) --- crates/evm/fuzz/src/strategies/state.rs | 32 ++++++++++++------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/crates/evm/fuzz/src/strategies/state.rs b/crates/evm/fuzz/src/strategies/state.rs index 862b38bca..cf2f6ec6e 100644 --- a/crates/evm/fuzz/src/strategies/state.rs +++ b/crates/evm/fuzz/src/strategies/state.rs @@ -150,6 +150,21 @@ pub fn collect_state_from_call( ) { let mut state = state.write(); + // Insert log topics and data. + for log in logs { + for topic in log.topics() { + state.values_mut().insert(topic.0); + } + let chunks = log.data.data.chunks_exact(32); + let rem = chunks.remainder(); + for chunk in chunks { + state.values_mut().insert(chunk.try_into().unwrap()); + } + if !rem.is_empty() { + state.values_mut().insert(B256::right_padding_from(rem).0); + } + } + for (address, account) in state_changeset { // Insert basic account information state.values_mut().insert(address.into_word().into()); @@ -182,23 +197,6 @@ pub fn collect_state_from_call( state.values_mut().insert(B256::from(above_value).0); } } - } else { - return; - } - } - - // Insert log topics and data. - for log in logs { - for topic in log.topics() { - state.values_mut().insert(topic.0); - } - let chunks = log.data.data.chunks_exact(32); - let rem = chunks.remainder(); - for chunk in chunks { - state.values_mut().insert(chunk.try_into().unwrap()); - } - if !rem.is_empty() { - state.values_mut().insert(B256::right_padding_from(rem).0); } } } From 28e80130ad8e802e462bf141350bd209846ff4e3 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 4 Mar 2024 22:06:38 +0400 Subject: [PATCH 027/430] feat(forge): --disable-block-gas-limit flag (#7287) * feat(forge): --disable-block-gas-limit flag * test * fix test --- crates/common/src/evm.rs | 4 +++ crates/config/src/lib.rs | 4 +++ crates/evm/core/src/fork/init.rs | 2 ++ crates/evm/core/src/opts.rs | 5 ++++ crates/evm/evm/src/inspectors/stack.rs | 10 ++++--- crates/forge/tests/cli/config.rs | 1 + crates/forge/tests/cli/test_cmd.rs | 36 ++++++++++++++++++++++++++ 7 files changed, 59 insertions(+), 3 deletions(-) diff --git a/crates/common/src/evm.rs b/crates/common/src/evm.rs index e23c1da33..4b2a94f43 100644 --- a/crates/common/src/evm.rs +++ b/crates/common/src/evm.rs @@ -272,6 +272,10 @@ pub struct EnvArgs { #[arg(long, value_name = "MEMORY_LIMIT")] #[serde(skip_serializing_if = "Option::is_none")] pub memory_limit: Option, + + /// Whether to disable the block gas limit checks. + #[arg(long, visible_alias = "no-gas-limit")] + pub disable_block_gas_limit: bool, } impl EvmArgs { diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index d9853fae6..a076acf77 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -379,6 +379,9 @@ pub struct Config { /// Useful for more correct gas accounting and EVM behavior in general. pub isolate: bool, + /// Whether to disable the block gas limit. + pub disable_block_gas_limit: bool, + /// Address labels pub labels: HashMap, @@ -1889,6 +1892,7 @@ impl Default for Config { block_difficulty: 0, block_prevrandao: Default::default(), block_gas_limit: None, + disable_block_gas_limit: false, memory_limit: 1 << 27, // 2**27 = 128MiB = 134_217_728 bytes eth_rpc_url: None, eth_rpc_jwt: None, diff --git a/crates/evm/core/src/fork/init.rs b/crates/evm/core/src/fork/init.rs index 11e659164..84830d5a2 100644 --- a/crates/evm/core/src/fork/init.rs +++ b/crates/evm/core/src/fork/init.rs @@ -17,6 +17,7 @@ pub async fn environment( override_chain_id: Option, pin_block: Option, origin: Address, + disable_block_gas_limit: bool, ) -> eyre::Result<(Env, Block)> { let block_number = if let Some(pin_block) = pin_block { pin_block @@ -55,6 +56,7 @@ pub async fn environment( // If EIP-3607 is enabled it can cause issues during fuzz/invariant tests if the caller // is a contract. So we disable the check by default. cfg.disable_eip3607 = true; + cfg.disable_block_gas_limit = disable_block_gas_limit; let mut env = Env { cfg, diff --git a/crates/evm/core/src/opts.rs b/crates/evm/core/src/opts.rs index 510f14254..2dee3a9b5 100644 --- a/crates/evm/core/src/opts.rs +++ b/crates/evm/core/src/opts.rs @@ -64,6 +64,9 @@ pub struct EvmOpts { /// Whether to enable isolation of calls. pub isolate: bool, + + /// Whether to disable block gas limit checks. + pub disable_block_gas_limit: bool, } impl EvmOpts { @@ -96,6 +99,7 @@ impl EvmOpts { self.env.chain_id, self.fork_block_number, self.sender, + self.disable_block_gas_limit, ) .await .wrap_err_with(|| { @@ -114,6 +118,7 @@ impl EvmOpts { // If EIP-3607 is enabled it can cause issues during fuzz/invariant tests if the // caller is a contract. So we disable the check by default. cfg.disable_eip3607 = true; + cfg.disable_block_gas_limit = self.disable_block_gas_limit; revm::primitives::Env { block: BlockEnv { diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 2a66ac762..94152a444 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -467,9 +467,13 @@ impl InspectorStack { data.env.tx.value = value; data.env.tx.nonce = Some(nonce); // Add 21000 to the gas limit to account for the base cost of transaction. - // We might have modified block gas limit earlier and revm will reject tx with gas limit > - // block gas limit, so we adjust. - data.env.tx.gas_limit = std::cmp::min(gas_limit + 21000, data.env.block.gas_limit.to()); + data.env.tx.gas_limit = gas_limit + 21000; + // If we haven't disabled gas limit checks, ensure that transaction gas limit will not + // exceed block gas limit. + if !data.env.cfg.disable_block_gas_limit { + data.env.tx.gas_limit = + std::cmp::min(data.env.tx.gas_limit, data.env.block.gas_limit.to()); + } data.env.tx.gas_price = U256::ZERO; self.inner_context_data = Some(InnerContextData { diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index f2dbf22c7..20bbb8e1b 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -85,6 +85,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { block_difficulty: 10, block_prevrandao: B256::random(), block_gas_limit: Some(100u64.into()), + disable_block_gas_limit: false, memory_limit: 1 << 27, eth_rpc_url: Some("localhost".to_string()), eth_rpc_jwt: None, diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 61cbab489..f449a0ac0 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -490,3 +490,39 @@ contract TransientTest is Test { cmd.args(["test", "-vvvv", "--isolate", "--evm-version", "cancun"]).assert_success(); }); + +forgetest_init!(can_disable_block_gas_limit, |prj, cmd| { + prj.wipe_contracts(); + + let endpoint = rpc::next_http_archive_rpc_endpoint(); + + prj.add_test( + "Contract.t.sol", + &r#"pragma solidity 0.8.24; +import {Test} from "forge-std/Test.sol"; + +contract C is Test {} + +contract GasWaster { + function waste() public { + for (uint256 i = 0; i < 100; i++) { + new C(); + } + } +} + +contract GasLimitTest is Test { + function test() public { + vm.createSelectFork(""); + + GasWaster waster = new GasWaster(); + waster.waste(); + } +} + "# + .replace("", &endpoint), + ) + .unwrap(); + + cmd.args(["test", "-vvvv", "--isolate", "--disable-block-gas-limit"]).assert_success(); +}); From 5efb5181e5cc40624f3b7b43cfa9f0fbd8c65664 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Mon, 4 Mar 2024 20:08:40 +0200 Subject: [PATCH 028/430] closes #7303 - apply invariant preserve_state setting even when fail_on_revert is false (#7304) --- crates/config/src/invariant.rs | 3 +-- crates/evm/evm/src/executors/invariant/mod.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/config/src/invariant.rs b/crates/config/src/invariant.rs index d2594c820..17994c3e3 100644 --- a/crates/config/src/invariant.rs +++ b/crates/config/src/invariant.rs @@ -30,8 +30,7 @@ pub struct InvariantConfig { pub shrink_run_limit: usize, /// If set to true then VM state is committed and available for next call /// Useful for handlers that use cheatcodes as roll or warp - /// Applies only when `fail_on_revert` set to true. Use it with caution, introduces performance - /// penalty. + /// Use it with caution, introduces performance penalty. pub preserve_state: bool, } diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 79d055eab..7b87c0f06 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -162,7 +162,7 @@ impl<'a> InvariantExecutor<'a> { let (sender, (address, calldata)) = inputs.last().expect("no input generated"); // Executes the call from the randomly generated sequence. - let call_result = if self.config.fail_on_revert && self.config.preserve_state { + let call_result = if self.config.preserve_state { executor .call_raw_committing(*sender, *address, calldata.clone(), U256::ZERO) .expect("could not make raw evm call") From 3dfa43bf1fa030a6c76c36a12feb4f2fe623b89b Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 4 Mar 2024 19:08:54 +0100 Subject: [PATCH 029/430] chore: bump alloy-chains (#7307) --- Cargo.lock | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c9216e3cf..0f30364d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -78,9 +78,9 @@ checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "alloy-chains" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "973deb9e9d5db1f28c2a478073aeb435f1c07f72cf5935caa0c421e6b68f2db1" +checksum = "e96c81b05c893348760f232c4cc6a6a77fd91cfb09885d4eaad25cd03bd7732e" dependencies = [ "num_enum", "serde", @@ -5067,7 +5067,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ - "proc-macro-crate 3.1.0", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 2.0.52", @@ -5739,15 +5739,6 @@ dependencies = [ "toml_edit 0.20.7", ] -[[package]] -name = "proc-macro-crate" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" -dependencies = [ - "toml_edit 0.21.1", -] - [[package]] name = "proc-macro-error" version = "1.0.4" From 381d76cbdbab921f7929c0027c601178a7cdb1e4 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 5 Mar 2024 11:31:54 +0100 Subject: [PATCH 030/430] chore: add panic message (#7316) --- crates/cli/src/opts/build/paths.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/cli/src/opts/build/paths.rs b/crates/cli/src/opts/build/paths.rs index 692da4588..949742751 100644 --- a/crates/cli/src/opts/build/paths.rs +++ b/crates/cli/src/opts/build/paths.rs @@ -67,8 +67,14 @@ impl ProjectPathsArgs { /// Returns the root directory to use for configuring the [Project] /// /// This will be the `--root` argument if provided, otherwise see [find_project_root_path()] + /// + /// # Panics + /// + /// If the project root directory cannot be found: [find_project_root_path()] pub fn project_root(&self) -> PathBuf { - self.root.clone().unwrap_or_else(|| find_project_root_path(None).unwrap()) + self.root + .clone() + .unwrap_or_else(|| find_project_root_path(None).expect("Failed to find project root")) } /// Returns the remappings to add to the config From 36440d87bd0f211fd70e78130ac29e2191fbdffe Mon Sep 17 00:00:00 2001 From: risinek Date: Tue, 5 Mar 2024 12:16:59 +0100 Subject: [PATCH 031/430] Remove --offline flag from cargo install command (#7315) Otherwise cargo throws error when installing on a clean Rust installation error: failed to load source for dependency `ethers` Caused by: Unable to update https://github.com/gakonst/ethers-rs?rev=73e5de211c32a1f5777eb5194205bdb31f6a3502#73e5de21 Caused by: can't checkout from 'https://github.com/gakonst/ethers-rs': you are in the offline mode (--offline) --- crates/anvil/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/anvil/README.md b/crates/anvil/README.md index d811bf3ce..cb62354fb 100644 --- a/crates/anvil/README.md +++ b/crates/anvil/README.md @@ -20,7 +20,7 @@ A local Ethereum node, akin to Ganache, designed for development with [**Forge** ```sh git clone https://github.com/foundry-rs/foundry cd foundry -cargo install --path ./crates/anvil --profile local --locked --offline --force +cargo install --path ./crates/anvil --profile local --locked --force ``` ## Getting started From ce22450e4d625d12ff88fae347a68e3d3d9d2b61 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Tue, 5 Mar 2024 16:38:19 +0400 Subject: [PATCH 032/430] fix(invariants): support `vm.assume` in invariant tests (#7309) * fix(invariants): support vm.assume in invariant tests * fix * add .sol file * review fix --- crates/config/src/invariant.rs | 4 + .../evm/evm/src/executors/invariant/error.rs | 26 +++- .../evm/evm/src/executors/invariant/funcs.rs | 7 +- crates/evm/evm/src/executors/invariant/mod.rs | 135 ++++++++++-------- crates/evm/fuzz/src/strategies/invariants.rs | 3 +- crates/forge/src/runner.rs | 38 ++--- crates/forge/tests/it/invariant.rs | 41 ++++++ crates/forge/tests/it/test_helpers.rs | 1 + .../invariant/common/InvariantAssume.t.sol | 23 +++ 9 files changed, 194 insertions(+), 84 deletions(-) create mode 100644 testdata/fuzz/invariant/common/InvariantAssume.t.sol diff --git a/crates/config/src/invariant.rs b/crates/config/src/invariant.rs index 17994c3e3..0eb96bdee 100644 --- a/crates/config/src/invariant.rs +++ b/crates/config/src/invariant.rs @@ -32,6 +32,9 @@ pub struct InvariantConfig { /// Useful for handlers that use cheatcodes as roll or warp /// Use it with caution, introduces performance penalty. pub preserve_state: bool, + /// The maximum number of rejects via `vm.assume` which can be encountered during a single + /// invariant run. + pub max_assume_rejects: u32, } impl Default for InvariantConfig { @@ -45,6 +48,7 @@ impl Default for InvariantConfig { shrink_sequence: true, shrink_run_limit: 2usize.pow(18_u32), preserve_state: false, + max_assume_rejects: 65536, } } } diff --git a/crates/evm/evm/src/executors/invariant/error.rs b/crates/evm/evm/src/executors/invariant/error.rs index 50dedab7c..8acc67f6a 100644 --- a/crates/evm/evm/src/executors/invariant/error.rs +++ b/crates/evm/evm/src/executors/invariant/error.rs @@ -53,7 +53,27 @@ pub struct InvariantFuzzTestResult { } #[derive(Clone, Debug)] -pub struct InvariantFuzzError { +pub enum InvariantFuzzError { + Revert(FailedInvariantCaseData), + BrokenInvariant(FailedInvariantCaseData), + MaxAssumeRejects(u32), +} + +impl InvariantFuzzError { + pub fn revert_reason(&self) -> Option { + match self { + Self::BrokenInvariant(case_data) | Self::Revert(case_data) => { + (!case_data.revert_reason.is_empty()).then(|| case_data.revert_reason.clone()) + } + Self::MaxAssumeRejects(allowed) => Some(format!( + "The `vm.assume` cheatcode rejected too many inputs ({allowed} allowed)" + )), + } + } +} + +#[derive(Clone, Debug)] +pub struct FailedInvariantCaseData { pub logs: Vec, pub traces: Option, /// The proptest error occurred as a result of a test case. @@ -74,7 +94,7 @@ pub struct InvariantFuzzError { pub shrink_run_limit: usize, } -impl InvariantFuzzError { +impl FailedInvariantCaseData { pub fn new( invariant_contract: &InvariantContract<'_>, error_func: Option<&Function>, @@ -93,7 +113,7 @@ impl InvariantFuzzError { .with_abi(invariant_contract.abi) .decode(call_result.result.as_ref(), Some(call_result.exit_reason)); - InvariantFuzzError { + Self { logs: call_result.logs, traces: call_result.traces, test_error: proptest::test_runner::TestError::Fail( diff --git a/crates/evm/evm/src/executors/invariant/funcs.rs b/crates/evm/evm/src/executors/invariant/funcs.rs index 810abb259..237b4dad8 100644 --- a/crates/evm/evm/src/executors/invariant/funcs.rs +++ b/crates/evm/evm/src/executors/invariant/funcs.rs @@ -1,4 +1,4 @@ -use super::{InvariantFailures, InvariantFuzzError}; +use super::{error::FailedInvariantCaseData, InvariantFailures, InvariantFuzzError}; use crate::executors::{Executor, RawCallResult}; use alloy_dyn_abi::JsonAbiExt; use alloy_json_abi::Function; @@ -50,7 +50,7 @@ pub fn assert_invariants( if is_err { // We only care about invariants which we haven't broken yet. if invariant_failures.error.is_none() { - invariant_failures.error = Some(InvariantFuzzError::new( + let case_data = FailedInvariantCaseData::new( invariant_contract, Some(func), calldata, @@ -58,7 +58,8 @@ pub fn assert_invariants( &inner_sequence, shrink_sequence, shrink_run_limit, - )); + ); + invariant_failures.error = Some(InvariantFuzzError::BrokenInvariant(case_data)); return None } } diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 7b87c0f06..2ec627b11 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -9,7 +9,7 @@ use eyre::{eyre, ContextCompat, Result}; use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; use foundry_config::{FuzzDictionaryConfig, InvariantConfig}; use foundry_evm_core::{ - constants::{CALLER, CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS}, + constants::{CALLER, CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME}, utils::{get_function, StateChangeset}, }; use foundry_evm_fuzz::{ @@ -38,11 +38,13 @@ use foundry_evm_fuzz::strategies::CalldataFuzzDictionary; mod funcs; pub use funcs::{assert_invariants, replay_run}; +use self::error::FailedInvariantCaseData; + /// Alias for (Dictionary for fuzzing, initial contracts to fuzz and an InvariantStrategy). type InvariantPreparation = ( EvmFuzzState, FuzzRunIdentifiedContracts, - BoxedStrategy>, + BoxedStrategy, CalldataFuzzDictionary, ); @@ -143,7 +145,9 @@ impl<'a> InvariantExecutor<'a> { // during the run. We need another proptest runner to query for random // values. let branch_runner = RefCell::new(self.runner.clone()); - let _ = self.runner.run(&strat, |mut inputs| { + let _ = self.runner.run(&strat, |first_input| { + let mut inputs = vec![first_input]; + // We stop the run immediately if we have reverted, and `fail_on_revert` is set. if self.config.fail_on_revert && failures.borrow().reverts > 0 { return Err(TestCaseError::fail("Revert occurred.")) @@ -158,7 +162,10 @@ impl<'a> InvariantExecutor<'a> { // Created contracts during a run. let mut created_contracts = vec![]; - for current_run in 0..self.config.depth { + let mut current_run = 0; + let mut assume_rejects_counter = 0; + + while current_run < self.config.depth { let (sender, (address, calldata)) = inputs.last().expect("no input generated"); // Executes the call from the randomly generated sequence. @@ -172,65 +179,77 @@ impl<'a> InvariantExecutor<'a> { .expect("could not make raw evm call") }; - // Collect data for fuzzing from the state changeset. - let mut state_changeset = - call_result.state_changeset.to_owned().expect("no changesets"); - - collect_data( - &mut state_changeset, - sender, - &call_result, - fuzz_state.clone(), - &self.config.dictionary, - ); + if call_result.result.as_ref() == MAGIC_ASSUME { + inputs.pop(); + assume_rejects_counter += 1; + if assume_rejects_counter > self.config.max_assume_rejects { + failures.borrow_mut().error = Some(InvariantFuzzError::MaxAssumeRejects( + self.config.max_assume_rejects, + )); + return Err(TestCaseError::fail("Max number of vm.assume rejects reached.")) + } + } else { + // Collect data for fuzzing from the state changeset. + let mut state_changeset = + call_result.state_changeset.to_owned().expect("no changesets"); + + collect_data( + &mut state_changeset, + sender, + &call_result, + fuzz_state.clone(), + &self.config.dictionary, + ); - if let Err(error) = collect_created_contracts( - &state_changeset, - self.project_contracts, - self.setup_contracts, - &self.artifact_filters, - targeted_contracts.clone(), - &mut created_contracts, - ) { - warn!(target: "forge::test", "{error}"); - } + if let Err(error) = collect_created_contracts( + &state_changeset, + self.project_contracts, + self.setup_contracts, + &self.artifact_filters, + targeted_contracts.clone(), + &mut created_contracts, + ) { + warn!(target: "forge::test", "{error}"); + } - // Commit changes to the database. - executor.backend.commit(state_changeset.clone()); - - fuzz_runs.push(FuzzCase { - calldata: calldata.clone(), - gas: call_result.gas_used, - stipend: call_result.stipend, - }); - - let RichInvariantResults { success: can_continue, call_result: call_results } = - can_continue( - &invariant_contract, - call_result, - &executor, - &inputs, - &mut failures.borrow_mut(), - &targeted_contracts, - state_changeset, - self.config.fail_on_revert, - self.config.shrink_sequence, - self.config.shrink_run_limit, - ); + // Commit changes to the database. + executor.backend.commit(state_changeset.clone()); + + fuzz_runs.push(FuzzCase { + calldata: calldata.clone(), + gas: call_result.gas_used, + stipend: call_result.stipend, + }); + + let RichInvariantResults { success: can_continue, call_result: call_results } = + can_continue( + &invariant_contract, + call_result, + &executor, + &inputs, + &mut failures.borrow_mut(), + &targeted_contracts, + state_changeset, + self.config.fail_on_revert, + self.config.shrink_sequence, + self.config.shrink_run_limit, + ); + + if !can_continue || current_run == self.config.depth - 1 { + *last_run_calldata.borrow_mut() = inputs.clone(); + } - if !can_continue || current_run == self.config.depth - 1 { - *last_run_calldata.borrow_mut() = inputs.clone(); - } + if !can_continue { + break + } - if !can_continue { - break + *last_call_results.borrow_mut() = call_results; + current_run += 1; } - *last_call_results.borrow_mut() = call_results; - // Generates the next call from the run using the recently updated // dictionary. - inputs.extend( + inputs.push( strat .new_tree(&mut branch_runner.borrow_mut()) .map_err(|_| TestCaseError::Fail("Could not generate case".into()))? @@ -772,7 +791,7 @@ fn can_continue( failures.reverts += 1; // If fail on revert is set, we must return immediately. if fail_on_revert { - let error = InvariantFuzzError::new( + let case_data = FailedInvariantCaseData::new( invariant_contract, None, calldata, @@ -781,8 +800,8 @@ fn can_continue( shrink_sequence, shrink_run_limit, ); - - failures.revert_reason = Some(error.revert_reason.clone()); + failures.revert_reason = Some(case_data.revert_reason.clone()); + let error = InvariantFuzzError::Revert(case_data); failures.error = Some(error); return RichInvariantResults::new(false, None) diff --git a/crates/evm/fuzz/src/strategies/invariants.rs b/crates/evm/fuzz/src/strategies/invariants.rs index 29d868bad..e6dedc9cd 100644 --- a/crates/evm/fuzz/src/strategies/invariants.rs +++ b/crates/evm/fuzz/src/strategies/invariants.rs @@ -59,11 +59,10 @@ pub fn invariant_strat( contracts: FuzzRunIdentifiedContracts, dictionary_weight: u32, calldata_fuzz_config: CalldataFuzzDictionary, -) -> impl Strategy> { +) -> impl Strategy { // We only want to seed the first value, since we want to generate the rest as we mutate the // state generate_call(fuzz_state, senders, contracts, dictionary_weight, calldata_fuzz_config) - .prop_map(|x| vec![x]) } /// Strategy to generate a transaction where the `sender`, `target` and `calldata` are all generated diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index ca3e83629..29507de8d 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -25,7 +25,7 @@ use foundry_evm::{ fuzz::{invariant::InvariantContract, CounterExample}, traces::{load_contracts, TraceKind}, }; -use proptest::test_runner::{TestError, TestRunner}; +use proptest::test_runner::TestRunner; use rayon::prelude::*; use std::{ collections::{BTreeMap, HashMap}, @@ -513,26 +513,28 @@ impl<'a> ContractRunner<'a> { let mut logs = logs.clone(); let mut traces = traces.clone(); let success = error.is_none(); - let reason = error - .as_ref() - .and_then(|err| (!err.revert_reason.is_empty()).then(|| err.revert_reason.clone())); + let reason = error.as_ref().and_then(|err| err.revert_reason()); let mut coverage = coverage.clone(); match error { // If invariants were broken, replay the error to collect logs and traces - Some(error @ InvariantFuzzError { test_error: TestError::Fail(_, _), .. }) => { - match error.replay( - self.executor.clone(), - known_contracts, - identified_contracts.clone(), - &mut logs, - &mut traces, - ) { - Ok(c) => counterexample = c, - Err(err) => { - error!(%err, "Failed to replay invariant error"); - } - }; - } + Some(error) => match error { + InvariantFuzzError::BrokenInvariant(case_data) | + InvariantFuzzError::Revert(case_data) => { + match case_data.replay( + self.executor.clone(), + known_contracts, + identified_contracts.clone(), + &mut logs, + &mut traces, + ) { + Ok(c) => counterexample = c, + Err(err) => { + error!(%err, "Failed to replay invariant error"); + } + }; + } + InvariantFuzzError::MaxAssumeRejects(_) => {} + }, // If invariants ran successfully, replay the last run to collect logs and // traces. diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index 28ac405cc..99b9ad962 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -141,6 +141,10 @@ async fn test_invariant() { "fuzz/invariant/common/InvariantCalldataDictionary.t.sol:InvariantCalldataDictionary", vec![("invariant_owner_never_changes()", true, None, None, None)], ), + ( + "fuzz/invariant/common/InvariantAssume.t.sol:InvariantAssume", + vec![("invariant_dummy()", true, None, None, None)], + ), ]), ); } @@ -367,3 +371,40 @@ async fn test_invariant_calldata_fuzz_dictionary_addresses() { )]), ); } + +#[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 = runner(); + // Should not treat vm.assume as revert. + runner.test_options.invariant.fail_on_revert = true; + let results = runner.test_collect(&filter); + assert_multiple( + &results, + BTreeMap::from([( + "fuzz/invariant/common/InvariantAssume.t.sol:InvariantAssume", + vec![("invariant_dummy()", true, None, None, None)], + )]), + ); +} + +#[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 = runner(); + runner.test_options.invariant.max_assume_rejects = 1; + let results = runner.test_collect(&filter); + assert_multiple( + &results, + BTreeMap::from([( + "fuzz/invariant/common/InvariantAssume.t.sol:InvariantAssume", + vec![( + "invariant_dummy()", + false, + Some("The `vm.assume` cheatcode rejected too many inputs (1 allowed)".into()), + None, + None, + )], + )]), + ); +} diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index 968a09280..609181f1a 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -111,6 +111,7 @@ pub static TEST_OPTS: Lazy = Lazy::new(|| { shrink_sequence: true, shrink_run_limit: 2usize.pow(18u32), preserve_state: false, + max_assume_rejects: 65536, }) .build(&COMPILED, &PROJECT.paths.root) .expect("Config loaded") diff --git a/testdata/fuzz/invariant/common/InvariantAssume.t.sol b/testdata/fuzz/invariant/common/InvariantAssume.t.sol new file mode 100644 index 000000000..3065a70a5 --- /dev/null +++ b/testdata/fuzz/invariant/common/InvariantAssume.t.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.0; + +import "ds-test/test.sol"; +import "../../../cheats/Vm.sol"; + +contract Handler is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + function doSomething(uint256 param) public { + vm.assume(param != 0); + } +} + +contract InvariantAssume is DSTest { + Handler handler; + + function setUp() public { + handler = new Handler(); + } + + function invariant_dummy() public {} +} From 6e0f3919fa6527e72150e0fdc12962970c6a3580 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 6 Mar 2024 19:25:53 +0100 Subject: [PATCH 033/430] chore(deps): unpin and bump ethers (#7327) * chore(deps): unpin and bump ethers Hopefully for the last time. * chore: cargo clippo --- Cargo.lock | 71 ++++++++++++--------- Cargo.toml | 37 +++-------- crates/anvil/src/eth/backend/mem/storage.rs | 2 +- crates/config/src/lib.rs | 1 + crates/evm/coverage/src/analysis.rs | 2 +- crates/forge/tests/it/core.rs | 4 +- 6 files changed, 53 insertions(+), 64 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0f30364d0..ba859dda4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2407,8 +2407,9 @@ dependencies = [ [[package]] name = "ethers" -version = "2.0.13" -source = "git+https://github.com/gakonst/ethers-rs?rev=73e5de211c32a1f5777eb5194205bdb31f6a3502#73e5de211c32a1f5777eb5194205bdb31f6a3502" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "816841ea989f0c69e459af1cf23a6b0033b19a55424a1ea3a30099becdb8dec0" dependencies = [ "ethers-addressbook", "ethers-contract", @@ -2422,8 +2423,9 @@ dependencies = [ [[package]] name = "ethers-addressbook" -version = "2.0.13" -source = "git+https://github.com/gakonst/ethers-rs?rev=73e5de211c32a1f5777eb5194205bdb31f6a3502#73e5de211c32a1f5777eb5194205bdb31f6a3502" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5495afd16b4faa556c3bba1f21b98b4983e53c1755022377051a975c3b021759" dependencies = [ "ethers-core", "once_cell", @@ -2433,8 +2435,9 @@ dependencies = [ [[package]] name = "ethers-contract" -version = "2.0.13" -source = "git+https://github.com/gakonst/ethers-rs?rev=73e5de211c32a1f5777eb5194205bdb31f6a3502#73e5de211c32a1f5777eb5194205bdb31f6a3502" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fceafa3578c836eeb874af87abacfb041f92b4da0a78a5edd042564b8ecdaaa" dependencies = [ "const-hex", "ethers-contract-abigen", @@ -2451,8 +2454,9 @@ dependencies = [ [[package]] name = "ethers-contract-abigen" -version = "2.0.13" -source = "git+https://github.com/gakonst/ethers-rs?rev=73e5de211c32a1f5777eb5194205bdb31f6a3502#73e5de211c32a1f5777eb5194205bdb31f6a3502" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04ba01fbc2331a38c429eb95d4a570166781f14290ef9fdb144278a90b5a739b" dependencies = [ "Inflector", "const-hex", @@ -2474,8 +2478,9 @@ dependencies = [ [[package]] name = "ethers-contract-derive" -version = "2.0.13" -source = "git+https://github.com/gakonst/ethers-rs?rev=73e5de211c32a1f5777eb5194205bdb31f6a3502#73e5de211c32a1f5777eb5194205bdb31f6a3502" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87689dcabc0051cde10caaade298f9e9093d65f6125c14575db3fd8c669a168f" dependencies = [ "Inflector", "const-hex", @@ -2489,8 +2494,9 @@ dependencies = [ [[package]] name = "ethers-core" -version = "2.0.13" -source = "git+https://github.com/gakonst/ethers-rs?rev=73e5de211c32a1f5777eb5194205bdb31f6a3502#73e5de211c32a1f5777eb5194205bdb31f6a3502" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82d80cc6ad30b14a48ab786523af33b37f28a8623fc06afd55324816ef18fb1f" dependencies = [ "arrayvec", "bytes", @@ -2518,8 +2524,9 @@ dependencies = [ [[package]] name = "ethers-etherscan" -version = "2.0.13" -source = "git+https://github.com/gakonst/ethers-rs?rev=73e5de211c32a1f5777eb5194205bdb31f6a3502#73e5de211c32a1f5777eb5194205bdb31f6a3502" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79e5973c26d4baf0ce55520bd732314328cabe53193286671b47144145b9649" dependencies = [ "chrono", "ethers-core", @@ -2533,8 +2540,9 @@ dependencies = [ [[package]] name = "ethers-middleware" -version = "2.0.13" -source = "git+https://github.com/gakonst/ethers-rs?rev=73e5de211c32a1f5777eb5194205bdb31f6a3502#73e5de211c32a1f5777eb5194205bdb31f6a3502" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48f9fdf09aec667c099909d91908d5eaf9be1bd0e2500ba4172c1d28bfaa43de" dependencies = [ "async-trait", "auto_impl", @@ -2558,8 +2566,9 @@ dependencies = [ [[package]] name = "ethers-providers" -version = "2.0.13" -source = "git+https://github.com/gakonst/ethers-rs?rev=73e5de211c32a1f5777eb5194205bdb31f6a3502#73e5de211c32a1f5777eb5194205bdb31f6a3502" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6434c9a33891f1effc9c75472e12666db2fa5a0fec4b29af6221680a6fe83ab2" dependencies = [ "async-trait", "auto_impl", @@ -2596,8 +2605,9 @@ dependencies = [ [[package]] name = "ethers-signers" -version = "2.0.13" -source = "git+https://github.com/gakonst/ethers-rs?rev=73e5de211c32a1f5777eb5194205bdb31f6a3502#73e5de211c32a1f5777eb5194205bdb31f6a3502" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "228875491c782ad851773b652dd8ecac62cda8571d3bc32a5853644dd26766c2" dependencies = [ "async-trait", "coins-bip32", @@ -2610,7 +2620,6 @@ dependencies = [ "futures-executor", "futures-util", "home", - "protobuf", "rand 0.8.5", "rusoto_core", "rusoto_kms", @@ -2624,8 +2633,9 @@ dependencies = [ [[package]] name = "ethers-solc" -version = "2.0.13" -source = "git+https://github.com/gakonst/ethers-rs?rev=73e5de211c32a1f5777eb5194205bdb31f6a3502#73e5de211c32a1f5777eb5194205bdb31f6a3502" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66244a771d9163282646dbeffe0e6eca4dda4146b6498644e678ac6089b11edd" dependencies = [ "cfg-if", "const-hex", @@ -5067,7 +5077,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate 2.0.0", "proc-macro2", "quote", "syn 2.0.52", @@ -5829,9 +5839,9 @@ dependencies = [ [[package]] name = "protobuf" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55bad9126f378a853655831eb7363b7b01b81d19f8cb1218861086ca4a1a61e" +checksum = "b65f4a8ec18723a734e5dc09c173e0abf9690432da5340285d536edcb4dac190" dependencies = [ "once_cell", "protobuf-support", @@ -5840,9 +5850,9 @@ dependencies = [ [[package]] name = "protobuf-support" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d4d7b8601c814cfb36bcebb79f0e61e45e1e93640cf778837833bbed05c372" +checksum = "6872f4d4f4b98303239a2b5838f5bbbb77b01ffc892d627957f37a22d7cfe69c" dependencies = [ "thiserror", ] @@ -7830,13 +7840,12 @@ dependencies = [ [[package]] name = "trezor-client" -version = "0.1.0" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cddb76a030b141d9639470eca2a236f3057a651bba78227cfa77830037a8286" +checksum = "f62c95b37f6c769bd65a0d0beb8b2b003e72998003b896a616a6777c645c05ed" dependencies = [ "byteorder", "hex", - "primitive-types", "protobuf", "rusb", "thiserror", diff --git a/Cargo.toml b/Cargo.toml index fa91bfd44..1fd0d18ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -144,14 +144,14 @@ revm-primitives = { version = "1", default-features = false } revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "e90052361276aebcdc67cb24d8e2c4d907b6d299", default-features = false } ## ethers -ethers = { version = "2.0", default-features = false } -ethers-core = { version = "2.0", default-features = false } -ethers-contract = { version = "2.0", default-features = false } -ethers-contract-abigen = { version = "2.0", default-features = false } -ethers-providers = { version = "2.0", default-features = false } -ethers-signers = { version = "2.0", default-features = false } -ethers-middleware = { version = "2.0", default-features = false } -ethers-solc = { version = "2.0", default-features = false } +ethers = { version = "2.0.14", default-features = false } +ethers-core = { version = "2.0.14", default-features = false } +ethers-contract = { version = "2.0.14", default-features = false } +ethers-contract-abigen = { version = "2.0.14", default-features = false } +ethers-providers = { version = "2.0.14", default-features = false } +ethers-signers = { version = "2.0.14", default-features = false } +ethers-middleware = { version = "2.0.14", default-features = false } +ethers-solc = { version = "2.0.14", default-features = false } ## alloy alloy-consensus = { git = "https://github.com/alloy-rs/alloy" } @@ -209,28 +209,7 @@ hyper = "0.14" tower = "0.4" tower-http = "0.4" -#[patch."https://github.com/gakonst/ethers-rs"] -#ethers = { path = "../ethers-rs/ethers" } -#ethers-addressbook = { path = "../ethers-rs/ethers-addressbook" } -#ethers-contract = { path = "../ethers-rs/ethers-contract" } -#ethers-contract-abigen = { path = "../ethers-rs/ethers-contract/ethers-contract-abigen" } -#ethers-core = { path = "../ethers-rs/ethers-core" } -#ethers-etherscan = { path = "../ethers-rs/ethers-etherscan" } -#ethers-middleware = { path = "../ethers-rs/ethers-middleware" } -#ethers-providers = { path = "../ethers-rs/ethers-providers" } -#ethers-signers = { path = "../ethers-rs/ethers-signers" } -#ethers-solc = { path = "../ethers-rs/ethers-solc" } - [patch.crates-io] -ethers = { git = "https://github.com/gakonst/ethers-rs", rev = "73e5de211c32a1f5777eb5194205bdb31f6a3502" } -ethers-core = { git = "https://github.com/gakonst/ethers-rs", rev = "73e5de211c32a1f5777eb5194205bdb31f6a3502" } -ethers-contract = { git = "https://github.com/gakonst/ethers-rs", rev = "73e5de211c32a1f5777eb5194205bdb31f6a3502" } -ethers-contract-abigen = { git = "https://github.com/gakonst/ethers-rs", rev = "73e5de211c32a1f5777eb5194205bdb31f6a3502" } -ethers-providers = { git = "https://github.com/gakonst/ethers-rs", rev = "73e5de211c32a1f5777eb5194205bdb31f6a3502" } -ethers-signers = { git = "https://github.com/gakonst/ethers-rs", rev = "73e5de211c32a1f5777eb5194205bdb31f6a3502" } -ethers-middleware = { git = "https://github.com/gakonst/ethers-rs", rev = "73e5de211c32a1f5777eb5194205bdb31f6a3502" } -ethers-solc = { git = "https://github.com/gakonst/ethers-rs", rev = "73e5de211c32a1f5777eb5194205bdb31f6a3502" } - revm = { git = "https://github.com/bluealloy/revm", branch = "reth_freeze" } revm-primitives = { git = "https://github.com/bluealloy/revm", branch = "reth_freeze" } revm-interpreter = { git = "https://github.com/bluealloy/revm", branch = "reth_freeze" } diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index 9e907cff4..0e2ac2b63 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -471,7 +471,7 @@ mod tests { tokio::time::sleep(std::time::Duration::from_secs(2)).await; assert_eq!(storage.on_disk_states.len(), 1); - assert!(storage.on_disk_states.get(&one).is_some()); + assert!(storage.on_disk_states.contains_key(&one)); let loaded = storage.get(&one).unwrap(); diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index a076acf77..f14f446c2 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -4525,6 +4525,7 @@ mod tests { } #[test] + #[allow(unknown_lints, non_local_definitions)] fn can_use_impl_figment_macro() { #[derive(Default, Serialize)] struct MyArgs { diff --git a/crates/evm/coverage/src/analysis.rs b/crates/evm/coverage/src/analysis.rs index 2ebd1be10..9af93cb68 100644 --- a/crates/evm/coverage/src/analysis.rs +++ b/crates/evm/coverage/src/analysis.rs @@ -576,7 +576,7 @@ impl SourceAnalyzer { fn analyze_contracts(&mut self) -> eyre::Result<()> { for contract_id in self.contracts.keys() { // Find this contract's coverage items if we haven't already - if self.contract_items.get(contract_id).is_none() { + if !self.contract_items.contains_key(contract_id) { let ContractVisitor { items, base_contract_node_ids, .. } = ContractVisitor::new( contract_id.source_id, self.sources.get(&contract_id.source_id).unwrap_or_else(|| { diff --git a/crates/forge/tests/it/core.rs b/crates/forge/tests/it/core.rs index f581407d5..96a0aedce 100644 --- a/crates/forge/tests/it/core.rs +++ b/crates/forge/tests/it/core.rs @@ -689,8 +689,8 @@ async fn test_doesnt_run_abstract_contract() { let filter = Filter::new(".*", ".*", ".*Abstract.t.sol".to_string().as_str()); let mut runner = runner(); let results = runner.test_collect(&filter); - assert!(results.get("core/Abstract.t.sol:AbstractTestBase").is_none()); - assert!(results.get("core/Abstract.t.sol:AbstractTest").is_some()); + assert!(!results.contains_key("core/Abstract.t.sol:AbstractTestBase")); + assert!(results.contains_key("core/Abstract.t.sol:AbstractTest")); } #[tokio::test(flavor = "multi_thread")] From 938f848ec4e94ca81f6c2eb096fcd83be13765a1 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 6 Mar 2024 22:28:06 +0400 Subject: [PATCH 034/430] refactor: extract verify to separate crate (#7326) * refactor: extract verify to separate crate * warn unused deps --- Cargo.lock | 31 ++++++++++++++ Cargo.toml | 1 + crates/forge/Cargo.toml | 1 + crates/forge/bin/cmd/create.rs | 8 ++-- crates/forge/bin/cmd/debug.rs | 3 +- crates/forge/bin/cmd/mod.rs | 2 - crates/forge/bin/cmd/script/mod.rs | 5 ++- crates/forge/bin/cmd/script/sequence.rs | 2 +- crates/forge/bin/cmd/script/verify.rs | 5 +-- crates/forge/bin/opts.rs | 24 +++-------- crates/verify/Cargo.toml | 41 +++++++++++++++++++ .../src}/etherscan/flatten.rs | 0 .../verify => verify/src}/etherscan/mod.rs | 5 +-- .../src}/etherscan/standard_json.rs | 0 .../cmd/verify/mod.rs => verify/src/lib.rs} | 9 +++- .../bin/cmd/verify => verify/src}/provider.rs | 0 crates/{forge/bin/cmd => verify/src}/retry.rs | 0 .../bin/cmd/verify => verify/src}/sourcify.rs | 0 18 files changed, 100 insertions(+), 37 deletions(-) create mode 100644 crates/verify/Cargo.toml rename crates/{forge/bin/cmd/verify => verify/src}/etherscan/flatten.rs (100%) rename crates/{forge/bin/cmd/verify => verify/src}/etherscan/mod.rs (99%) rename crates/{forge/bin/cmd/verify => verify/src}/etherscan/standard_json.rs (100%) rename crates/{forge/bin/cmd/verify/mod.rs => verify/src/lib.rs} (98%) rename crates/{forge/bin/cmd/verify => verify/src}/provider.rs (100%) rename crates/{forge/bin/cmd => verify/src}/retry.rs (100%) rename crates/{forge/bin/cmd/verify => verify/src}/sourcify.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index ba859dda4..b936a44b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2924,6 +2924,7 @@ dependencies = [ "eyre", "forge-doc", "forge-fmt", + "forge-verify", "foundry-block-explorers", "foundry-cli", "foundry-common", @@ -3008,6 +3009,36 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "forge-verify" +version = "0.2.0" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "async-trait", + "clap", + "const-hex", + "ethers-providers", + "eyre", + "foundry-block-explorers", + "foundry-cli", + "foundry-common", + "foundry-compilers", + "foundry-config", + "foundry-evm", + "foundry-test-utils", + "futures", + "once_cell", + "regex", + "reqwest", + "semver 1.0.22", + "serde", + "serde_json", + "tempfile", + "tokio", + "tracing", +] + [[package]] name = "form_urlencoded" version = "1.2.1" diff --git a/Cargo.toml b/Cargo.toml index 1fd0d18ef..ba0cddfa2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -118,6 +118,7 @@ forge = { path = "crates/forge" } forge-doc = { path = "crates/doc" } forge-fmt = { path = "crates/fmt" } +forge-verify = { path = "crates/verify" } foundry-cheatcodes = { path = "crates/cheatcodes" } foundry-cheatcodes-spec = { path = "crates/cheatcodes/spec" } foundry-cli = { path = "crates/cli" } diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 516575ce5..29d01b5c5 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -45,6 +45,7 @@ yansi = "0.5" # bin forge-doc.workspace = true forge-fmt.workspace = true +forge-verify.workspace = true foundry-cli.workspace = true foundry-debugger.workspace = true diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index ed9a17aad..9dee30675 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -1,4 +1,3 @@ -use super::{retry::RetryArgs, verify}; use alloy_dyn_abi::{DynSolValue, JsonAbiExt, ResolveSolType}; use alloy_json_abi::{Constructor, JsonAbi}; use alloy_primitives::{Address, Bytes}; @@ -14,6 +13,7 @@ use ethers_core::{ use ethers_middleware::SignerMiddleware; use ethers_providers::Middleware; use eyre::{Context, Result}; +use forge_verify::RetryArgs; use foundry_cli::{ opts::{CoreBuildArgs, EthereumOpts, EtherscanOpts, TransactionOpts}, utils::{self, read_constructor_args_file, remove_contract, LoadConfig}, @@ -80,7 +80,7 @@ pub struct CreateArgs { eth: EthereumOpts, #[command(flatten)] - pub verifier: verify::VerifierArgs, + pub verifier: forge_verify::VerifierArgs, #[command(flatten)] retry: RetryArgs, @@ -169,7 +169,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 = verify::VerifyArgs { + let mut verify = forge_verify::VerifyArgs { address: Default::default(), contract: self.contract.clone(), compiler_version: None, @@ -323,7 +323,7 @@ impl CreateArgs { let num_of_optimizations = if self.opts.compiler.optimize { self.opts.compiler.optimizer_runs } else { None }; - let verify = verify::VerifyArgs { + let verify = forge_verify::VerifyArgs { address, contract: self.contract, compiler_version: None, diff --git a/crates/forge/bin/cmd/debug.rs b/crates/forge/bin/cmd/debug.rs index dafbf965a..75f27da53 100644 --- a/crates/forge/bin/cmd/debug.rs +++ b/crates/forge/bin/cmd/debug.rs @@ -1,5 +1,6 @@ -use super::{build::BuildArgs, retry::RETRY_VERIFY_ON_CREATE, script::ScriptArgs}; +use super::{build::BuildArgs, script::ScriptArgs}; use clap::{Parser, ValueHint}; +use forge_verify::retry::RETRY_VERIFY_ON_CREATE; use foundry_cli::opts::CoreBuildArgs; use foundry_common::evm::EvmArgs; use std::path::PathBuf; diff --git a/crates/forge/bin/cmd/mod.rs b/crates/forge/bin/cmd/mod.rs index 92c80e219..1e1a91cbf 100644 --- a/crates/forge/bin/cmd/mod.rs +++ b/crates/forge/bin/cmd/mod.rs @@ -56,12 +56,10 @@ pub mod inspect; pub mod install; pub mod remappings; pub mod remove; -pub mod retry; pub mod script; pub mod selectors; pub mod snapshot; pub mod test; pub mod tree; pub mod update; -pub mod verify; pub mod watch; diff --git a/crates/forge/bin/cmd/script/mod.rs b/crates/forge/bin/cmd/script/mod.rs index 8fe66f839..79c8f937f 100644 --- a/crates/forge/bin/cmd/script/mod.rs +++ b/crates/forge/bin/cmd/script/mod.rs @@ -1,4 +1,4 @@ -use super::{build::BuildArgs, retry::RetryArgs}; +use super::build::BuildArgs; use alloy_dyn_abi::FunctionExt; use alloy_json_abi::{Function, InternalType, JsonAbi}; use alloy_primitives::{Address, Bytes, Log, U256, U64}; @@ -17,6 +17,7 @@ use forge::{ render_trace_arena, CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, Traces, }, }; +use forge_verify::RetryArgs; use foundry_common::{ abi::{encode_function_args, get_func}, errors::UnlinkedByteCode, @@ -182,7 +183,7 @@ pub struct ScriptArgs { pub evm_opts: EvmArgs, #[command(flatten)] - pub verifier: super::verify::VerifierArgs, + pub verifier: forge_verify::VerifierArgs, #[command(flatten)] pub retry: RetryArgs, diff --git a/crates/forge/bin/cmd/script/sequence.rs b/crates/forge/bin/cmd/script/sequence.rs index 6223bd104..0e97862bc 100644 --- a/crates/forge/bin/cmd/script/sequence.rs +++ b/crates/forge/bin/cmd/script/sequence.rs @@ -5,11 +5,11 @@ use crate::cmd::{ transaction::{wrapper, AdditionalContract, TransactionWithMetadata}, verify::VerifyBundle, }, - verify::provider::VerificationProviderType, }; use alloy_primitives::{Address, TxHash}; use ethers_core::types::{transaction::eip2718::TypedTransaction, TransactionReceipt}; use eyre::{ContextCompat, Result, WrapErr}; +use forge_verify::provider::VerificationProviderType; use foundry_cli::utils::now; use foundry_common::{ fs, shell, diff --git a/crates/forge/bin/cmd/script/verify.rs b/crates/forge/bin/cmd/script/verify.rs index 96b778eda..43293268d 100644 --- a/crates/forge/bin/cmd/script/verify.rs +++ b/crates/forge/bin/cmd/script/verify.rs @@ -1,8 +1,5 @@ -use crate::cmd::{ - retry::RetryArgs, - verify::{VerifierArgs, VerifyArgs}, -}; use alloy_primitives::Address; +use forge_verify::{RetryArgs, VerifierArgs, VerifyArgs}; use foundry_cli::opts::{EtherscanOpts, ProjectPathsArgs}; use foundry_common::ContractsByArtifact; use foundry_compilers::{info::ContractInfo, Project}; diff --git a/crates/forge/bin/opts.rs b/crates/forge/bin/opts.rs index 4a72b80fc..5e5cfda7c 100644 --- a/crates/forge/bin/opts.rs +++ b/crates/forge/bin/opts.rs @@ -1,25 +1,11 @@ use crate::cmd::{ - bind::BindArgs, - build::BuildArgs, - cache::CacheArgs, - config, coverage, - create::CreateArgs, - debug::DebugArgs, - doc::DocArgs, - flatten, - fmt::FmtArgs, - geiger, generate, - init::InitArgs, - inspect, - install::InstallArgs, - remappings::RemappingArgs, - remove::RemoveArgs, - script::ScriptArgs, - selectors::SelectorsSubcommands, - snapshot, test, tree, update, - verify::{VerifyArgs, VerifyCheckArgs}, + bind::BindArgs, build::BuildArgs, cache::CacheArgs, config, coverage, create::CreateArgs, + debug::DebugArgs, doc::DocArgs, flatten, fmt::FmtArgs, geiger, generate, init::InitArgs, + inspect, install::InstallArgs, remappings::RemappingArgs, remove::RemoveArgs, + script::ScriptArgs, selectors::SelectorsSubcommands, snapshot, test, tree, update, }; use clap::{Parser, Subcommand, ValueHint}; +use forge_verify::{VerifyArgs, VerifyCheckArgs}; use std::path::PathBuf; const VERSION_MESSAGE: &str = concat!( diff --git a/crates/verify/Cargo.toml b/crates/verify/Cargo.toml new file mode 100644 index 000000000..a735b39af --- /dev/null +++ b/crates/verify/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "forge-verify" +description = "Contract verification tools" + +version.workspace = true +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +foundry-config.workspace = true +foundry-cli.workspace = true +foundry-common.workspace = true +foundry-evm.workspace = true + +serde_json.workspace = true +hex.workspace = true +alloy-json-abi.workspace = true +alloy-primitives.workspace = true +serde.workspace = true +eyre.workspace = true +ethers-providers.workspace = true +tracing.workspace = true +foundry-compilers = { workspace = true, features = ["full"] } +foundry-block-explorers = { workspace = true, features = ["foundry-compilers"] } + +clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } +reqwest = { version = "0.11", default-features = false, features = ["json"] } +async-trait = "0.1" +futures = "0.3" +semver = "1" +regex = { version = "1", default-features = false } +once_cell = "1" + +[dev-dependencies] +tokio = { version = "1", features = ["macros"] } +foundry-test-utils.workspace = true +tempfile = "3" \ No newline at end of file diff --git a/crates/forge/bin/cmd/verify/etherscan/flatten.rs b/crates/verify/src/etherscan/flatten.rs similarity index 100% rename from crates/forge/bin/cmd/verify/etherscan/flatten.rs rename to crates/verify/src/etherscan/flatten.rs diff --git a/crates/forge/bin/cmd/verify/etherscan/mod.rs b/crates/verify/src/etherscan/mod.rs similarity index 99% rename from crates/forge/bin/cmd/verify/etherscan/mod.rs rename to crates/verify/src/etherscan/mod.rs index f63db1240..42986a160 100644 --- a/crates/forge/bin/cmd/verify/etherscan/mod.rs +++ b/crates/verify/src/etherscan/mod.rs @@ -1,9 +1,8 @@ use super::{provider::VerificationProvider, VerifyArgs, VerifyCheckArgs}; -use crate::cmd::retry::RETRY_CHECK_ON_VERIFY; +use crate::retry::RETRY_CHECK_ON_VERIFY; use alloy_json_abi::Function; use ethers_providers::Middleware; use eyre::{eyre, Context, OptionExt, Result}; -use forge::hashbrown::HashSet; use foundry_block_explorers::{ errors::EtherscanError, utils::lookup_compiler_version, @@ -19,7 +18,7 @@ use foundry_compilers::{ Artifact, Project, Solc, }; use foundry_config::{Chain, Config, SolcReq}; -use foundry_evm::constants::DEFAULT_CREATE2_DEPLOYER; +use foundry_evm::{constants::DEFAULT_CREATE2_DEPLOYER, hashbrown::HashSet}; use futures::FutureExt; use once_cell::sync::Lazy; use regex::Regex; diff --git a/crates/forge/bin/cmd/verify/etherscan/standard_json.rs b/crates/verify/src/etherscan/standard_json.rs similarity index 100% rename from crates/forge/bin/cmd/verify/etherscan/standard_json.rs rename to crates/verify/src/etherscan/standard_json.rs diff --git a/crates/forge/bin/cmd/verify/mod.rs b/crates/verify/src/lib.rs similarity index 98% rename from crates/forge/bin/cmd/verify/mod.rs rename to crates/verify/src/lib.rs index eeff2cb83..be451d83f 100644 --- a/crates/forge/bin/cmd/verify/mod.rs +++ b/crates/verify/src/lib.rs @@ -1,4 +1,8 @@ -use super::retry::RetryArgs; +#![cfg_attr(not(test), warn(unused_crate_dependencies))] + +#[macro_use] +extern crate tracing; + use alloy_primitives::Address; use clap::{Parser, ValueHint}; use eyre::Result; @@ -19,8 +23,11 @@ use etherscan::EtherscanVerificationProvider; pub mod provider; use provider::VerificationProvider; +pub mod retry; mod sourcify; +pub use retry::RetryArgs; + /// Verification provider arguments #[derive(Clone, Debug, Parser)] pub struct VerifierArgs { diff --git a/crates/forge/bin/cmd/verify/provider.rs b/crates/verify/src/provider.rs similarity index 100% rename from crates/forge/bin/cmd/verify/provider.rs rename to crates/verify/src/provider.rs diff --git a/crates/forge/bin/cmd/retry.rs b/crates/verify/src/retry.rs similarity index 100% rename from crates/forge/bin/cmd/retry.rs rename to crates/verify/src/retry.rs diff --git a/crates/forge/bin/cmd/verify/sourcify.rs b/crates/verify/src/sourcify.rs similarity index 100% rename from crates/forge/bin/cmd/verify/sourcify.rs rename to crates/verify/src/sourcify.rs From b6e7c8b50a351ec5779c8cf4790854cc0b23ce8c Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 7 Mar 2024 01:22:48 +0400 Subject: [PATCH 035/430] refactor: extract linking logic to separate crate (#7329) * refactor: extract linking logic to separate crate * fix cargo check --- Cargo.lock | 11 +++++++++++ Cargo.toml | 1 + crates/forge/Cargo.toml | 1 + crates/forge/bin/cmd/script/build.rs | 2 +- crates/forge/bin/cmd/script/cmd.rs | 3 ++- crates/forge/src/lib.rs | 2 -- crates/forge/src/multi_runner.rs | 7 ++----- crates/linking/Cargo.toml | 17 +++++++++++++++++ .../{forge/src/link.rs => linking/src/lib.rs} | 2 ++ 9 files changed, 37 insertions(+), 9 deletions(-) create mode 100644 crates/linking/Cargo.toml rename crates/{forge/src/link.rs => linking/src/lib.rs} (99%) diff --git a/Cargo.lock b/Cargo.lock index b936a44b1..c35e148ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2932,6 +2932,7 @@ dependencies = [ "foundry-config", "foundry-debugger", "foundry-evm", + "foundry-linking", "foundry-test-utils", "foundry-wallets", "futures", @@ -3414,6 +3415,16 @@ dependencies = [ "yansi 0.5.1", ] +[[package]] +name = "foundry-linking" +version = "0.2.0" +dependencies = [ + "alloy-primitives", + "foundry-compilers", + "semver 1.0.22", + "thiserror", +] + [[package]] name = "foundry-macros" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index ba0cddfa2..e0c94e746 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -133,6 +133,7 @@ foundry-evm-traces = { path = "crates/evm/traces" } foundry-macros = { path = "crates/macros" } foundry-test-utils = { path = "crates/test-utils" } foundry-wallets = { path = "crates/wallets" } +foundry-linking = { path = "crates/linking" } # solc & compilation utilities foundry-block-explorers = { version = "0.2.3", default-features = false } diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 29d01b5c5..6d4e40f6b 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -25,6 +25,7 @@ foundry-compilers = { workspace = true, features = ["full"] } foundry-config.workspace = true foundry-evm.workspace = true foundry-wallets.workspace = true +foundry-linking.workspace = true ethers-contract.workspace = true ethers-core.workspace = true diff --git a/crates/forge/bin/cmd/script/build.rs b/crates/forge/bin/cmd/script/build.rs index 4b188d955..a2bdc1490 100644 --- a/crates/forge/bin/cmd/script/build.rs +++ b/crates/forge/bin/cmd/script/build.rs @@ -1,7 +1,6 @@ use super::{ScriptArgs, ScriptConfig}; use alloy_primitives::{Address, Bytes}; use eyre::{Context, ContextCompat, Result}; -use forge::link::{LinkOutput, Linker}; use foundry_cli::utils::get_cached_entry_by_name; use foundry_common::compile::{self, ContractSources, ProjectCompiler}; use foundry_compilers::{ @@ -11,6 +10,7 @@ use foundry_compilers::{ info::ContractInfo, ArtifactId, Project, ProjectCompileOutput, }; +use foundry_linking::{LinkOutput, Linker}; use std::str::FromStr; impl ScriptArgs { diff --git a/crates/forge/bin/cmd/script/cmd.rs b/crates/forge/bin/cmd/script/cmd.rs index 70fce6680..f864f29f8 100644 --- a/crates/forge/bin/cmd/script/cmd.rs +++ b/crates/forge/bin/cmd/script/cmd.rs @@ -7,7 +7,7 @@ use alloy_primitives::{Address, Bytes}; use ethers_providers::Middleware; use ethers_signers::Signer; use eyre::{OptionExt, Result}; -use forge::{link::Linker, traces::CallTraceDecoder}; +use forge::traces::CallTraceDecoder; use foundry_cli::utils::LoadConfig; use foundry_common::{ contracts::flatten_contracts, provider::ethers::try_get_http_provider, types::ToAlloy, @@ -18,6 +18,7 @@ use foundry_compilers::{ }; use foundry_debugger::Debugger; use foundry_evm::inspectors::cheatcodes::{BroadcastableTransaction, ScriptWallets}; +use foundry_linking::Linker; use foundry_wallets::WalletSigner; use std::{collections::HashMap, sync::Arc}; diff --git a/crates/forge/src/lib.rs b/crates/forge/src/lib.rs index 00481591e..39854abac 100644 --- a/crates/forge/src/lib.rs +++ b/crates/forge/src/lib.rs @@ -13,8 +13,6 @@ pub mod coverage; pub mod gas_report; -pub mod link; - mod multi_runner; pub use multi_runner::{MultiContractRunner, MultiContractRunnerBuilder}; diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 6a8e821b2..36d2f2fd8 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -1,10 +1,6 @@ //! Forge test runner for multiple contracts. -use crate::{ - link::{LinkOutput, Linker}, - result::SuiteResult, - ContractRunner, TestFilter, TestOptions, -}; +use crate::{result::SuiteResult, ContractRunner, TestFilter, TestOptions}; use alloy_json_abi::{Function, JsonAbi}; use alloy_primitives::{Address, Bytes, U256}; use eyre::Result; @@ -19,6 +15,7 @@ use foundry_evm::{ opts::EvmOpts, revm, }; +use foundry_linking::{LinkOutput, Linker}; use rayon::prelude::*; use revm::primitives::SpecId; use std::{ diff --git a/crates/linking/Cargo.toml b/crates/linking/Cargo.toml new file mode 100644 index 000000000..31edf1c5d --- /dev/null +++ b/crates/linking/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "foundry-linking" +description = "Smart contract linking tools" + +version.workspace = true +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +foundry-compilers = { workspace = true, features = ["full"] } +semver = "1" +alloy-primitives = { workspace = true, features = ["rlp"] } +thiserror = "1" \ No newline at end of file diff --git a/crates/forge/src/link.rs b/crates/linking/src/lib.rs similarity index 99% rename from crates/forge/src/link.rs rename to crates/linking/src/lib.rs index 55f3f5487..110a6d8f2 100644 --- a/crates/forge/src/link.rs +++ b/crates/linking/src/lib.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(test), warn(unused_crate_dependencies))] + use alloy_primitives::{Address, Bytes}; use foundry_compilers::{ artifacts::{CompactContractBytecode, Libraries}, From f787fed080e42528cae431c49be5ea611d045f90 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 7 Mar 2024 11:22:42 +0100 Subject: [PATCH 036/430] perf: mine new blocks on blocking task (#7328) --- crates/anvil/src/service.rs | 40 +++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/crates/anvil/src/service.rs b/crates/anvil/src/service.rs index 18ee20d99..71cfdfecb 100644 --- a/crates/anvil/src/service.rs +++ b/crates/anvil/src/service.rs @@ -18,7 +18,7 @@ use std::{ sync::Arc, task::{Context, Poll}, }; -use tokio::time::Interval; +use tokio::{task::JoinHandle, time::Interval}; /// The type that drives the blockchain's state /// @@ -101,17 +101,13 @@ impl Future for NodeService { } } -// The type of the future that mines a new block -type BlockMiningFuture = - Pin)> + Send + Sync>>; - /// A type that exclusively mines one block at a time #[must_use = "streams do nothing unless polled"] struct BlockProducer { /// Holds the backend if no block is being mined idle_backend: Option>, /// Single active future that mines a new block - block_mining: Option, + block_mining: Option)>>, /// backlog of sets of transactions ready to be mined queued: VecDeque>>, } @@ -133,19 +129,33 @@ impl Stream for BlockProducer { if !pin.queued.is_empty() { if let Some(backend) = pin.idle_backend.take() { let transactions = pin.queued.pop_front().expect("not empty; qed"); - pin.block_mining = Some(Box::pin(async move { - trace!(target: "miner", "creating new block"); - let block = backend.mine_block(transactions).await; - trace!(target: "miner", "created new block: {}", block.block_number); - (block, backend) - })); + + // we spawn this on as blocking task because in this can be blocking for a while in + // forking mode, because of all the rpc calls to fetch the required state + let handle = tokio::runtime::Handle::current(); + let mining = tokio::task::spawn_blocking(move || { + handle.block_on(async move { + trace!(target: "miner", "creating new block"); + let block = backend.mine_block(transactions).await; + trace!(target: "miner", "created new block: {}", block.block_number); + (block, backend) + }) + }); + pin.block_mining = Some(mining); } } if let Some(mut mining) = pin.block_mining.take() { - if let Poll::Ready((outcome, backend)) = mining.poll_unpin(cx) { - pin.idle_backend = Some(backend); - return Poll::Ready(Some(outcome)) + if let Poll::Ready(res) = mining.poll_unpin(cx) { + return match res { + Ok((outcome, backend)) => { + pin.idle_backend = Some(backend); + Poll::Ready(Some(outcome)) + } + Err(err) => { + panic!("miner task failed: {}", err); + } + } } else { pin.block_mining = Some(mining) } From ebb71622b4720908d4b7aa3b82362fbb78984495 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 7 Mar 2024 18:45:36 +0100 Subject: [PATCH 037/430] chore: remove fork caches for eth call and call estimate (#7333) --- crates/anvil/src/eth/backend/fork.rs | 44 +++------------------------- 1 file changed, 4 insertions(+), 40 deletions(-) diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index 94408756f..c763f4c9a 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -172,26 +172,8 @@ impl ClientFork { request: &TransactionRequest, block: Option, ) -> Result { - let request = Arc::new(request.clone()); let block = block.unwrap_or(BlockNumber::Latest); - - if let BlockNumber::Number(num) = block { - // check if this request was already been sent - let key = (request.clone(), num); - if let Some(res) = self.storage_read().eth_call.get(&key).cloned() { - return Ok(res); - } - } - - let block_id: BlockId = block.into(); - - let res: Bytes = self.provider().call((*request).clone(), Some(block_id)).await?; - - if let BlockNumber::Number(num) = block { - // cache result - let mut storage = self.storage_write(); - storage.eth_call.insert((request, num), res.clone()); - } + let res = self.provider().call((*request).clone(), Some(block.into())).await?; Ok(res) } @@ -202,26 +184,8 @@ impl ClientFork { request: &TransactionRequest, block: Option, ) -> Result { - let request = Arc::new(request.clone()); let block = block.unwrap_or(BlockNumber::Latest); - - if let BlockNumber::Number(num) = block { - // check if this request was already been sent - let key = (request.clone(), num); - if let Some(res) = self.storage_read().eth_gas_estimations.get(&key).cloned() { - return Ok(res); - } - } - - let block_id: BlockId = block.into(); - - let res = self.provider().estimate_gas((*request).clone(), Some(block_id)).await?; - - if let BlockNumber::Number(num) = block { - // cache result - let mut storage = self.storage_write(); - storage.eth_gas_estimations.insert((request, num), res); - } + let res = self.provider().estimate_gas(request.clone(), Some(block.into())).await?; Ok(res) } @@ -662,6 +626,8 @@ impl ClientForkConfig { } /// Contains cached state fetched to serve EthApi requests +/// +/// 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: HashMap>, @@ -674,8 +640,6 @@ pub struct ForkedStorage { pub geth_transaction_traces: HashMap, pub block_traces: HashMap>, pub block_receipts: HashMap>, - pub eth_gas_estimations: HashMap<(Arc, u64), U256>, - pub eth_call: HashMap<(Arc, u64), Bytes>, pub code_at: HashMap<(Address, u64), Bytes>, } From c3a190290a798b0060c9f22d2dfd320fd02c5c0c Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 7 Mar 2024 22:10:53 +0100 Subject: [PATCH 038/430] chore(meta): update CODEOWNERS (#7339) --- .github/CODEOWNERS | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 99659760a..b452279e7 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,6 +1,10 @@ -* @danipopes @evalir @mattsse +* @danipopes @mattsse -crates/anvil/ @evalir @mattsse -crates/evm/coverage/ @evalir @onbjerg +crates/anvil/ @danipopes @mattsse +crates/cheatcodes/ @danipopes @mattsse @klkvr +crates/evm/coverage/ @onbjerg crates/fmt/ @rkrasiuk -crates/macros/impls/ @danipopes +crates/linking/ @klkvr +crates/macros/ @danipopes +crates/script/ @danipopes @mattsse @klkvr +crates/wallets/ @klkvr From b253d8475fa1836bb697e76dfa4b9c08ca0856cd Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 7 Mar 2024 22:46:31 +0100 Subject: [PATCH 039/430] chore: unify etherscan resolve functions (#7340) --- crates/config/src/lib.rs | 47 ++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index f14f446c2..7a1d4195d 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -918,6 +918,8 @@ impl Config { /// Returns /// - the matching `ResolvedEtherscanConfig` of the `etherscan` table if `etherscan_api_key` is /// an alias + /// - the matching `ResolvedEtherscanConfig` of the `etherscan` table if a `chain` is + /// configured. an alias /// - the Mainnet `ResolvedEtherscanConfig` if `etherscan_api_key` is set, `None` otherwise /// /// # Example @@ -933,18 +935,7 @@ impl Config { pub fn get_etherscan_config( &self, ) -> Option> { - let maybe_alias = self.etherscan_api_key.as_ref().or(self.eth_rpc_url.as_ref())?; - if self.etherscan.contains_key(maybe_alias) { - // etherscan points to an alias in the `etherscan` table, so we try to resolve that - let mut resolved = self.etherscan.clone().resolved(); - return resolved.remove(maybe_alias) - } - - // we treat the `etherscan_api_key` as actual API key - // if no chain provided, we assume mainnet - let chain = self.chain.unwrap_or(Chain::mainnet()); - let api_key = self.etherscan_api_key.as_ref()?; - ResolvedEtherscanConfig::create(api_key, chain).map(Ok) + self.get_etherscan_config_with_chain(None).transpose() } /// Same as [`Self::get_etherscan_config()`] but optionally updates the config with the given @@ -964,8 +955,9 @@ impl Config { } // try to find by comparing chain IDs after resolving - if let Some(res) = - chain.and_then(|chain| self.etherscan.clone().resolved().find_chain(chain)) + if let Some(res) = chain + .or(self.chain) + .and_then(|chain| self.etherscan.clone().resolved().find_chain(chain)) { match (res, self.etherscan_api_key.as_ref()) { (Ok(mut config), Some(key)) => { @@ -992,6 +984,10 @@ impl Config { } /// Helper function to just get the API key + /// + /// Optionally updates the config with the given `chain`. + /// + /// See also [Self::get_etherscan_config_with_chain] pub fn get_etherscan_api_key(&self, chain: Option) -> Option { self.get_etherscan_config_with_chain(chain).ok().flatten().map(|c| c.key) } @@ -3120,6 +3116,29 @@ mod tests { }); } + #[test] + fn test_resolve_etherscan_chain_id() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "foundry.toml", + r#" + [profile.default] + chain_id = "sepolia" + + [etherscan] + sepolia = { key = "FX42Z3BBJJEWXWGYV2X1CIPRSCN" } + "#, + )?; + + let config = Config::load(); + let etherscan = config.get_etherscan_config().unwrap().unwrap(); + assert_eq!(etherscan.chain, Some(NamedChain::Sepolia.into())); + assert_eq!(etherscan.key, "FX42Z3BBJJEWXWGYV2X1CIPRSCN"); + + Ok(()) + }); + } + #[test] fn test_resolve_rpc_url() { figment::Jail::expect_with(|jail| { From 5c3b075f6e2adbba6089d15b383450930de283e7 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 7 Mar 2024 22:52:23 +0100 Subject: [PATCH 040/430] chore(traces): add a trace identifier stack/builder (#7338) --- crates/chisel/src/dispatcher.rs | 18 +++--- crates/cli/src/utils/cmd.rs | 12 +++- crates/evm/traces/src/decoder/mod.rs | 19 ++++-- crates/evm/traces/src/identifier/etherscan.rs | 62 ++++++++----------- crates/evm/traces/src/identifier/mod.rs | 58 ++++++++++++++++- crates/forge/bin/cmd/script/mod.rs | 27 ++++---- crates/forge/bin/cmd/test/mod.rs | 16 +++-- 7 files changed, 133 insertions(+), 79 deletions(-) diff --git a/crates/chisel/src/dispatcher.rs b/crates/chisel/src/dispatcher.rs index 46086f1dc..b6a4411ac 100644 --- a/crates/chisel/src/dispatcher.rs +++ b/crates/chisel/src/dispatcher.rs @@ -17,7 +17,7 @@ use foundry_config::{Config, RpcEndpoint}; use foundry_evm::{ decode::decode_console_logs, traces::{ - identifier::{EtherscanIdentifier, SignaturesIdentifier}, + identifier::{SignaturesIdentifier, TraceIdentifiers}, render_trace_arena, CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, }, }; @@ -893,11 +893,6 @@ impl ChiselDispatcher { result: &mut ChiselResult, // known_contracts: &ContractsByArtifact, ) -> eyre::Result { - let mut etherscan_identifier = EtherscanIdentifier::new( - &session_config.foundry_config, - session_config.evm_opts.get_remote_chain_id(), - )?; - let mut decoder = CallTraceDecoderBuilder::new() .with_labels(result.labeled_addresses.clone()) .with_signature_identifier(SignaturesIdentifier::new( @@ -906,9 +901,14 @@ impl ChiselDispatcher { )?) .build(); - for (_, trace) in &mut result.traces { - // decoder.identify(trace, &mut local_identifier); - decoder.identify(trace, &mut etherscan_identifier); + let mut identifier = TraceIdentifiers::new().with_etherscan( + &session_config.foundry_config, + session_config.evm_opts.get_remote_chain_id(), + )?; + if !identifier.is_empty() { + for (_, trace) in &mut result.traces { + decoder.identify(trace, &mut identifier); + } } Ok(decoder) } diff --git a/crates/cli/src/utils/cmd.rs b/crates/cli/src/utils/cmd.rs index 00a76002c..1ea54d3f6 100644 --- a/crates/cli/src/utils/cmd.rs +++ b/crates/cli/src/utils/cmd.rs @@ -397,12 +397,18 @@ pub async fn handle_traces( .build(); let mut etherscan_identifier = EtherscanIdentifier::new(config, chain)?; - for (_, trace) in &mut result.traces { - decoder.identify(trace, &mut etherscan_identifier); + if let Some(etherscan_identifier) = &mut etherscan_identifier { + for (_, trace) in &mut result.traces { + decoder.identify(trace, etherscan_identifier); + } } if debug { - let sources = etherscan_identifier.get_compiled_contracts().await?; + let sources = if let Some(etherscan_identifier) = etherscan_identifier { + etherscan_identifier.get_compiled_contracts().await? + } else { + Default::default() + }; let mut debugger = Debugger::builder() .debug_arena(&result.debug) .decoder(&decoder) diff --git a/crates/evm/traces/src/decoder/mod.rs b/crates/evm/traces/src/decoder/mod.rs index 6b5aaf35d..fbe797a00 100644 --- a/crates/evm/traces/src/decoder/mod.rs +++ b/crates/evm/traces/src/decoder/mod.rs @@ -7,7 +7,9 @@ use crate::{ use alloy_dyn_abi::{DecodedEvent, DynSolValue, EventExt, FunctionExt, JsonAbiExt}; use alloy_json_abi::{Error, Event, Function, JsonAbi}; use alloy_primitives::{Address, LogData, Selector, B256}; -use foundry_common::{abi::get_indexed_event, fmt::format_token, SELECTOR_LEN}; +use foundry_common::{ + abi::get_indexed_event, fmt::format_token, ContractsByArtifact, SELECTOR_LEN, +}; use foundry_evm_core::{ abi::{Console, HardhatConsole, Vm, HARDHAT_CONSOLE_SELECTOR_PATCHES}, constants::{ @@ -50,17 +52,22 @@ impl CallTraceDecoderBuilder { self } - /// Add known contracts to the decoder from a `LocalTraceIdentifier`. + /// Add known contracts to the decoder. #[inline] - pub fn with_local_identifier_abis(mut self, identifier: &LocalTraceIdentifier<'_>) -> Self { - let contracts = identifier.contracts(); - trace!(target: "evm::traces", len=contracts.len(), "collecting local identifier ABIs"); + pub fn with_known_contracts(mut self, contracts: &ContractsByArtifact) -> Self { + trace!(target: "evm::traces", len=contracts.len(), "collecting known contract ABIs"); for (abi, _) in contracts.values() { self.decoder.collect_abi(abi, None); } self } + /// Add known contracts to the decoder from a `LocalTraceIdentifier`. + #[inline] + pub fn with_local_identifier_abis(self, identifier: &LocalTraceIdentifier<'_>) -> Self { + self.with_known_contracts(identifier.contracts()) + } + /// Sets the verbosity level of the decoder. #[inline] pub fn with_verbosity(mut self, level: u8) -> Self { @@ -225,7 +232,7 @@ impl CallTraceDecoder { fn addresses<'a>( &'a self, arena: &'a CallTraceArena, - ) -> impl Iterator)> + 'a { + ) -> impl Iterator)> + Clone + 'a { arena .nodes() .iter() diff --git a/crates/evm/traces/src/identifier/etherscan.rs b/crates/evm/traces/src/identifier/etherscan.rs index 43552f12b..50c273d07 100644 --- a/crates/evm/traces/src/identifier/etherscan.rs +++ b/crates/evm/traces/src/identifier/etherscan.rs @@ -25,10 +25,9 @@ use std::{ use tokio::time::{Duration, Interval}; /// A trace identifier that tries to identify addresses using Etherscan. -#[derive(Default)] pub struct EtherscanIdentifier { /// The Etherscan client - client: Option>, + client: Arc, /// Tracks whether the API key provides was marked as invalid /// /// After the first [EtherscanError::InvalidApiKey] this will get set to true, so we can @@ -40,22 +39,21 @@ pub struct EtherscanIdentifier { impl EtherscanIdentifier { /// Creates a new Etherscan identifier with the given client - pub fn new(config: &Config, chain: Option) -> eyre::Result { + pub fn new(config: &Config, chain: Option) -> eyre::Result> { + // In offline mode, don't use Etherscan. if config.offline { - // offline mode, don't use etherscan - return Ok(Default::default()) - } - if let Some(config) = config.get_etherscan_config_with_chain(chain)? { - trace!(target: "etherscanidentifier", chain=?config.chain, url=?config.api_url, "using etherscan identifier"); - Ok(Self { - client: Some(Arc::new(config.into_client()?)), - invalid_api_key: Arc::new(Default::default()), - contracts: BTreeMap::new(), - sources: BTreeMap::new(), - }) - } else { - Ok(Default::default()) + return Ok(None); } + let Some(config) = config.get_etherscan_config_with_chain(chain)? else { + return Ok(None); + }; + trace!(target: "traces::etherscan", chain=?config.chain, url=?config.api_url, "using etherscan identifier"); + Ok(Some(Self { + client: Arc::new(config.into_client()?), + invalid_api_key: Arc::new(AtomicBool::new(false)), + contracts: BTreeMap::new(), + sources: BTreeMap::new(), + })) } /// Goes over the list of contracts we have pulled from the traces, clones their source from @@ -101,18 +99,13 @@ impl TraceIdentifier for EtherscanIdentifier { { trace!(target: "evm::traces", "identify {:?} addresses", addresses.size_hint().1); - let Some(client) = self.client.clone() else { - // no client was configured - return Vec::new() - }; - if self.invalid_api_key.load(Ordering::Relaxed) { // api key was marked as invalid return Vec::new() } let mut fetcher = EtherscanFetcher::new( - client, + self.client.clone(), Duration::from_secs(1), 5, Arc::clone(&self.invalid_api_key), @@ -191,16 +184,13 @@ impl EtherscanFetcher { fn queue_next_reqs(&mut self) { while self.in_progress.len() < self.concurrency { - if let Some(addr) = self.queue.pop() { - let client = Arc::clone(&self.client); - trace!(target: "etherscanidentifier", "fetching info for {:?}", addr); - self.in_progress.push(Box::pin(async move { - let res = client.contract_source_code(addr).await; - (addr, res) - })); - } else { - break - } + let Some(addr) = self.queue.pop() else { break }; + let client = Arc::clone(&self.client); + self.in_progress.push(Box::pin(async move { + trace!(target: "traces::etherscan", ?addr, "fetching info"); + let res = client.contract_source_code(addr).await; + (addr, res) + })); } } } @@ -234,24 +224,24 @@ impl Stream for EtherscanFetcher { } } Err(EtherscanError::RateLimitExceeded) => { - warn!(target: "etherscanidentifier", "rate limit exceeded on attempt"); + warn!(target: "traces::etherscan", "rate limit exceeded on attempt"); pin.backoff = Some(tokio::time::interval(pin.timeout)); pin.queue.push(addr); } Err(EtherscanError::InvalidApiKey) => { - warn!(target: "etherscanidentifier", "invalid api key"); + warn!(target: "traces::etherscan", "invalid api key"); // mark key as invalid pin.invalid_api_key.store(true, Ordering::Relaxed); return Poll::Ready(None) } Err(EtherscanError::BlockedByCloudflare) => { - warn!(target: "etherscanidentifier", "blocked by cloudflare"); + warn!(target: "traces::etherscan", "blocked by cloudflare"); // mark key as invalid pin.invalid_api_key.store(true, Ordering::Relaxed); return Poll::Ready(None) } Err(err) => { - warn!(target: "etherscanidentifier", "could not get etherscan info: {:?}", err); + warn!(target: "traces::etherscan", "could not get etherscan info: {:?}", err); } } } diff --git a/crates/evm/traces/src/identifier/mod.rs b/crates/evm/traces/src/identifier/mod.rs index 6d86b072a..a16b108d8 100644 --- a/crates/evm/traces/src/identifier/mod.rs +++ b/crates/evm/traces/src/identifier/mod.rs @@ -1,6 +1,8 @@ use alloy_json_abi::JsonAbi; use alloy_primitives::Address; +use foundry_common::ContractsByArtifact; use foundry_compilers::ArtifactId; +use foundry_config::{Chain, Config}; use std::borrow::Cow; mod local; @@ -33,5 +35,59 @@ pub trait TraceIdentifier { /// Attempts to identify an address in one or more call traces. fn identify_addresses<'a, A>(&mut self, addresses: A) -> Vec> where - A: Iterator)>; + A: Iterator)> + Clone; +} + +/// A collection of trace identifiers. +pub struct TraceIdentifiers<'a> { + /// The local trace identifier. + pub local: Option>, + /// The optional Etherscan trace identifier. + pub etherscan: Option, +} + +impl Default for TraceIdentifiers<'_> { + fn default() -> Self { + Self::new() + } +} + +impl TraceIdentifier for TraceIdentifiers<'_> { + fn identify_addresses<'a, A>(&mut self, addresses: A) -> Vec> + where + A: Iterator)> + Clone, + { + let mut identities = Vec::new(); + if let Some(local) = &mut self.local { + identities.extend(local.identify_addresses(addresses.clone())); + } + if let Some(etherscan) = &mut self.etherscan { + identities.extend(etherscan.identify_addresses(addresses)); + } + identities + } +} + +impl<'a> TraceIdentifiers<'a> { + /// Creates a new, empty instance. + pub const fn new() -> Self { + Self { local: None, etherscan: None } + } + + /// Sets the local identifier. + pub fn with_local(mut self, known_contracts: &'a ContractsByArtifact) -> Self { + self.local = Some(LocalTraceIdentifier::new(known_contracts)); + self + } + + /// Sets the etherscan identifier. + pub fn with_etherscan(mut self, config: &Config, chain: Option) -> eyre::Result { + self.etherscan = EtherscanIdentifier::new(config, chain)?; + Ok(self) + } + + /// Returns `true` if there are no set identifiers. + pub fn is_empty(&self) -> bool { + self.local.is_none() && self.etherscan.is_none() + } } diff --git a/crates/forge/bin/cmd/script/mod.rs b/crates/forge/bin/cmd/script/mod.rs index 79c8f937f..98c3e223d 100644 --- a/crates/forge/bin/cmd/script/mod.rs +++ b/crates/forge/bin/cmd/script/mod.rs @@ -13,8 +13,8 @@ use forge::{ decode::decode_console_logs, opts::EvmOpts, traces::{ - identifier::{EtherscanIdentifier, LocalTraceIdentifier, SignaturesIdentifier}, - render_trace_arena, CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, Traces, + identifier::SignaturesIdentifier, render_trace_arena, CallTraceDecoder, + CallTraceDecoderBuilder, TraceKind, Traces, }, }; use forge_verify::RetryArgs; @@ -42,6 +42,7 @@ use foundry_evm::{ constants::DEFAULT_CREATE2_DEPLOYER, decode::RevertDecoder, inspectors::cheatcodes::{BroadcastableTransaction, BroadcastableTransactions}, + traces::identifier::TraceIdentifiers, }; use foundry_wallets::MultiWalletOpts; use futures::future; @@ -198,33 +199,29 @@ impl ScriptArgs { result: &mut ScriptResult, known_contracts: &ContractsByArtifact, ) -> Result { - let verbosity = script_config.evm_opts.verbosity; - let mut etherscan_identifier = EtherscanIdentifier::new( - &script_config.config, - script_config.evm_opts.get_remote_chain_id(), - )?; - - let mut local_identifier = LocalTraceIdentifier::new(known_contracts); let mut decoder = CallTraceDecoderBuilder::new() .with_labels(result.labeled_addresses.clone()) - .with_verbosity(verbosity) - .with_local_identifier_abis(&local_identifier) + .with_verbosity(script_config.evm_opts.verbosity) + .with_known_contracts(known_contracts) .with_signature_identifier(SignaturesIdentifier::new( Config::foundry_cache_dir(), script_config.config.offline, )?) .build(); + let mut identifier = TraceIdentifiers::new() + .with_local(known_contracts) + .with_etherscan(&script_config.config, script_config.evm_opts.get_remote_chain_id())?; // Decoding traces using etherscan is costly as we run into rate limits, // causing scripts to run for a very long time unnecessarily. // Therefore, we only try and use etherscan if the user has provided an API key. let should_use_etherscan_traces = script_config.config.etherscan_api_key.is_some(); + if !should_use_etherscan_traces { + identifier.etherscan = None; + } for (_, trace) in &mut result.traces { - decoder.identify(trace, &mut local_identifier); - if should_use_etherscan_traces { - decoder.identify(trace, &mut etherscan_identifier); - } + decoder.identify(trace, &mut identifier); } Ok(decoder) } diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 2a0e7bbdb..3e7d14848 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -7,10 +7,7 @@ use forge::{ gas_report::GasReport, inspectors::CheatsConfig, result::{SuiteResult, TestOutcome, TestStatus}, - traces::{ - identifier::{EtherscanIdentifier, LocalTraceIdentifier, SignaturesIdentifier}, - CallTraceDecoderBuilder, TraceKind, - }, + traces::{identifier::SignaturesIdentifier, CallTraceDecoderBuilder, TraceKind}, MultiContractRunner, MultiContractRunnerBuilder, TestOptions, TestOptionsBuilder, }; use foundry_cli::{ @@ -31,6 +28,7 @@ use foundry_config::{ get_available_profiles, Config, }; use foundry_debugger::Debugger; +use foundry_evm::traces::identifier::TraceIdentifiers; use regex::Regex; use std::{sync::mpsc::channel, time::Instant}; use watchexec::config::{InitConfig, RuntimeConfig}; @@ -296,9 +294,10 @@ impl TestArgs { // Set up trace identifiers. let known_contracts = runner.known_contracts.clone(); - let mut local_identifier = LocalTraceIdentifier::new(&known_contracts); let remote_chain_id = runner.evm_opts.get_remote_chain_id(); - let mut etherscan_identifier = EtherscanIdentifier::new(&config, remote_chain_id)?; + let mut identifier = TraceIdentifiers::new() + .with_local(&known_contracts) + .with_etherscan(&config, remote_chain_id)?; // Run tests. let (tx, rx) = channel::<(String, SuiteResult)>(); @@ -313,7 +312,7 @@ impl TestArgs { // Build the trace decoder. let mut builder = CallTraceDecoderBuilder::new() - .with_local_identifier_abis(&local_identifier) + .with_known_contracts(&known_contracts) .with_verbosity(verbosity); // Signatures are of no value for gas reports. if !self.gas_report { @@ -379,8 +378,7 @@ impl TestArgs { let mut decoded_traces = Vec::with_capacity(result.traces.len()); for (kind, arena) in &result.traces { if identify_addresses { - decoder.identify(arena, &mut local_identifier); - decoder.identify(arena, &mut etherscan_identifier); + decoder.identify(arena, &mut identifier); } // verbosity: From cab82fbbb431815fdfdf2232f97e7de24d135091 Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Fri, 8 Mar 2024 08:59:00 +0100 Subject: [PATCH 041/430] Add TOML cheatcode support (#7317) * rough outline, handling value to token conversion - different than JSON * handle all value cases * remove serialization for now, only support read and write for now * serialization is required * reuse in-memory serialized_jsons to allow for toml manipulation and serialization, implement additional serializeToml using serializeJson under the hood - this prevents any breaking changes in the cheatcodes * notes * more notes * run cargo cheats, investigate if we can implement path parsing, like JSON implementation * initial test, it is likely easier to implement a json <> toml and toml <> json representation upon parse and write * use simplified serialization toml <> json * use direct Serde conversions * implement basic parser, default one is insufficient * improve test structure, encoding and decoding works correctly * enhance test suite * add explicit type coercion * implement write tests, fix write step for key swap * add parseTomlKeys * complete feature parity and tests * remove debug lines * clean up * revert solc ignore * fix clippy warning * handle "null" string and dedup convert by using explicit internal methods that handle conversion internally * use direct Serde conversion, had issues with arrays and objects before but turn out to be unrelated * dedup formatting * clean up, work through edge cases and undefined behavior * add keyExistsJson, add TODO for deprecation warning of keyExists and handle TOML in traces - like JSON * add deprecated status flag to keyExists * add comments regarding deprecation of `keyExists` in favor of `keyExistsJson` * simplify `Toml` check * update spec * fix broken JSON test * switch back to custom type conversion due to limitations with built-in Serde and enhance test suite * increase robustness of test suite, better unify inputs outputs * technically empty string is the same as empty bytes but for clarity use bytes(0) * handle number edge case, prefer unwrap * Update crates/cheatcodes/src/toml.rs Co-authored-by: Matthias Seitz * remove implicit string bool translation, it is intended to only be done explicitly by coercion * add explicit checks for bool casting in both JSON and TOML tests --------- Co-authored-by: Matthias Seitz --- Cargo.lock | 1 + crates/cheatcodes/Cargo.toml | 2 +- crates/cheatcodes/assets/cheatcodes.json | 422 +++++++++++++++++- .../cheatcodes/assets/cheatcodes.schema.json | 7 + crates/cheatcodes/spec/src/cheatcode.rs | 8 + crates/cheatcodes/spec/src/vm.rs | 98 +++- crates/cheatcodes/src/evm/fork.rs | 2 +- crates/cheatcodes/src/json.rs | 65 +-- crates/cheatcodes/src/lib.rs | 1 + crates/cheatcodes/src/toml.rs | 243 ++++++++++ crates/evm/traces/src/decoder/mod.rs | 35 +- testdata/cheats/Json.t.sol | 138 ++++-- testdata/cheats/Toml.t.sol | 318 +++++++++++++ testdata/cheats/Vm.sol | 21 + testdata/fixtures/Json/test.json | 33 +- .../Json/{wholeJson.json => whole_json.json} | 0 testdata/fixtures/Toml/test.toml | 50 +++ testdata/fixtures/Toml/whole_toml.toml | 3 + .../fixtures/Toml/write_complex_test.toml | 6 + testdata/fixtures/Toml/write_test.toml | 2 + 20 files changed, 1355 insertions(+), 100 deletions(-) create mode 100644 crates/cheatcodes/src/toml.rs create mode 100644 testdata/cheats/Toml.t.sol rename testdata/fixtures/Json/{wholeJson.json => whole_json.json} (100%) create mode 100644 testdata/fixtures/Toml/test.toml create mode 100644 testdata/fixtures/Toml/whole_toml.toml create mode 100644 testdata/fixtures/Toml/write_complex_test.toml create mode 100644 testdata/fixtures/Toml/write_test.toml diff --git a/Cargo.lock b/Cargo.lock index c35e148ff..e5ec2d586 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3096,6 +3096,7 @@ dependencies = [ "revm", "serde_json", "thiserror", + "toml 0.8.10", "tracing", "walkdir", ] diff --git a/crates/cheatcodes/Cargo.toml b/crates/cheatcodes/Cargo.toml index 00cfaf7c3..7524afb63 100644 --- a/crates/cheatcodes/Cargo.toml +++ b/crates/cheatcodes/Cargo.toml @@ -29,7 +29,6 @@ alloy-rpc-types.workspace = true alloy-signer = { workspace = true, features = ["mnemonic", "keystore"] } parking_lot = "0.12" - eyre.workspace = true hex.workspace = true itertools.workspace = true @@ -37,6 +36,7 @@ jsonpath_lib.workspace = true revm.workspace = true serde_json.workspace = true base64.workspace = true +toml = { workspace = true, features = ["preserve_order"] } tracing.workspace = true k256.workspace = true walkdir = "2" diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 8e300ad1a..b08607226 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -4861,7 +4861,7 @@ { "func": { "id": "keyExists", - "description": "Checks if `key` exists in a JSON object.", + "description": "Checks if `key` exists in a JSON object\n`keyExists` is being deprecated in favor of `keyExistsJson`. It will be removed in future versions.", "declaration": "function keyExists(string calldata json, string calldata key) external view returns (bool);", "visibility": "external", "mutability": "view", @@ -4875,6 +4875,46 @@ ] }, "group": "json", + "status": "deprecated", + "safety": "safe" + }, + { + "func": { + "id": "keyExistsJson", + "description": "Checks if `key` exists in a JSON object.", + "declaration": "function keyExistsJson(string calldata json, string calldata key) external view returns (bool);", + "visibility": "external", + "mutability": "view", + "signature": "keyExistsJson(string,string)", + "selector": "0xdb4235f6", + "selectorBytes": [ + 219, + 66, + 53, + 246 + ] + }, + "group": "json", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "keyExistsToml", + "description": "Checks if `key` exists in a TOML table.", + "declaration": "function keyExistsToml(string calldata toml, string calldata key) external view returns (bool);", + "visibility": "external", + "mutability": "view", + "signature": "keyExistsToml(string,string)", + "selector": "0x600903ad", + "selectorBytes": [ + 96, + 9, + 3, + 173 + ] + }, + "group": "toml", "status": "stable", "safety": "safe" }, @@ -5538,6 +5578,346 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "parseTomlAddress", + "description": "Parses a string of TOML data at `key` and coerces it to `address`.", + "declaration": "function parseTomlAddress(string calldata toml, string calldata key) external pure returns (address);", + "visibility": "external", + "mutability": "pure", + "signature": "parseTomlAddress(string,string)", + "selector": "0x65e7c844", + "selectorBytes": [ + 101, + 231, + 200, + 68 + ] + }, + "group": "toml", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "parseTomlAddressArray", + "description": "Parses a string of TOML data at `key` and coerces it to `address[]`.", + "declaration": "function parseTomlAddressArray(string calldata toml, string calldata key) external pure returns (address[] memory);", + "visibility": "external", + "mutability": "pure", + "signature": "parseTomlAddressArray(string,string)", + "selector": "0x65c428e7", + "selectorBytes": [ + 101, + 196, + 40, + 231 + ] + }, + "group": "toml", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "parseTomlBool", + "description": "Parses a string of TOML data at `key` and coerces it to `bool`.", + "declaration": "function parseTomlBool(string calldata toml, string calldata key) external pure returns (bool);", + "visibility": "external", + "mutability": "pure", + "signature": "parseTomlBool(string,string)", + "selector": "0xd30dced6", + "selectorBytes": [ + 211, + 13, + 206, + 214 + ] + }, + "group": "toml", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "parseTomlBoolArray", + "description": "Parses a string of TOML data at `key` and coerces it to `bool[]`.", + "declaration": "function parseTomlBoolArray(string calldata toml, string calldata key) external pure returns (bool[] memory);", + "visibility": "external", + "mutability": "pure", + "signature": "parseTomlBoolArray(string,string)", + "selector": "0x127cfe9a", + "selectorBytes": [ + 18, + 124, + 254, + 154 + ] + }, + "group": "toml", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "parseTomlBytes", + "description": "Parses a string of TOML data at `key` and coerces it to `bytes`.", + "declaration": "function parseTomlBytes(string calldata toml, string calldata key) external pure returns (bytes memory);", + "visibility": "external", + "mutability": "pure", + "signature": "parseTomlBytes(string,string)", + "selector": "0xd77bfdb9", + "selectorBytes": [ + 215, + 123, + 253, + 185 + ] + }, + "group": "toml", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "parseTomlBytes32", + "description": "Parses a string of TOML data at `key` and coerces it to `bytes32`.", + "declaration": "function parseTomlBytes32(string calldata toml, string calldata key) external pure returns (bytes32);", + "visibility": "external", + "mutability": "pure", + "signature": "parseTomlBytes32(string,string)", + "selector": "0x8e214810", + "selectorBytes": [ + 142, + 33, + 72, + 16 + ] + }, + "group": "toml", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "parseTomlBytes32Array", + "description": "Parses a string of TOML data at `key` and coerces it to `bytes32[]`.", + "declaration": "function parseTomlBytes32Array(string calldata toml, string calldata key) external pure returns (bytes32[] memory);", + "visibility": "external", + "mutability": "pure", + "signature": "parseTomlBytes32Array(string,string)", + "selector": "0x3e716f81", + "selectorBytes": [ + 62, + 113, + 111, + 129 + ] + }, + "group": "toml", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "parseTomlBytesArray", + "description": "Parses a string of TOML data at `key` and coerces it to `bytes[]`.", + "declaration": "function parseTomlBytesArray(string calldata toml, string calldata key) external pure returns (bytes[] memory);", + "visibility": "external", + "mutability": "pure", + "signature": "parseTomlBytesArray(string,string)", + "selector": "0xb197c247", + "selectorBytes": [ + 177, + 151, + 194, + 71 + ] + }, + "group": "toml", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "parseTomlInt", + "description": "Parses a string of TOML data at `key` and coerces it to `int256`.", + "declaration": "function parseTomlInt(string calldata toml, string calldata key) external pure returns (int256);", + "visibility": "external", + "mutability": "pure", + "signature": "parseTomlInt(string,string)", + "selector": "0xc1350739", + "selectorBytes": [ + 193, + 53, + 7, + 57 + ] + }, + "group": "toml", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "parseTomlIntArray", + "description": "Parses a string of TOML data at `key` and coerces it to `int256[]`.", + "declaration": "function parseTomlIntArray(string calldata toml, string calldata key) external pure returns (int256[] memory);", + "visibility": "external", + "mutability": "pure", + "signature": "parseTomlIntArray(string,string)", + "selector": "0xd3522ae6", + "selectorBytes": [ + 211, + 82, + 42, + 230 + ] + }, + "group": "toml", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "parseTomlKeys", + "description": "Returns an array of all the keys in a TOML table.", + "declaration": "function parseTomlKeys(string calldata toml, string calldata key) external pure returns (string[] memory keys);", + "visibility": "external", + "mutability": "pure", + "signature": "parseTomlKeys(string,string)", + "selector": "0x812a44b2", + "selectorBytes": [ + 129, + 42, + 68, + 178 + ] + }, + "group": "toml", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "parseTomlString", + "description": "Parses a string of TOML data at `key` and coerces it to `string`.", + "declaration": "function parseTomlString(string calldata toml, string calldata key) external pure returns (string memory);", + "visibility": "external", + "mutability": "pure", + "signature": "parseTomlString(string,string)", + "selector": "0x8bb8dd43", + "selectorBytes": [ + 139, + 184, + 221, + 67 + ] + }, + "group": "toml", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "parseTomlStringArray", + "description": "Parses a string of TOML data at `key` and coerces it to `string[]`.", + "declaration": "function parseTomlStringArray(string calldata toml, string calldata key) external pure returns (string[] memory);", + "visibility": "external", + "mutability": "pure", + "signature": "parseTomlStringArray(string,string)", + "selector": "0x9f629281", + "selectorBytes": [ + 159, + 98, + 146, + 129 + ] + }, + "group": "toml", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "parseTomlUint", + "description": "Parses a string of TOML data at `key` and coerces it to `uint256`.", + "declaration": "function parseTomlUint(string calldata toml, string calldata key) external pure returns (uint256);", + "visibility": "external", + "mutability": "pure", + "signature": "parseTomlUint(string,string)", + "selector": "0xcc7b0487", + "selectorBytes": [ + 204, + 123, + 4, + 135 + ] + }, + "group": "toml", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "parseTomlUintArray", + "description": "Parses a string of TOML data at `key` and coerces it to `uint256[]`.", + "declaration": "function parseTomlUintArray(string calldata toml, string calldata key) external pure returns (uint256[] memory);", + "visibility": "external", + "mutability": "pure", + "signature": "parseTomlUintArray(string,string)", + "selector": "0xb5df27c8", + "selectorBytes": [ + 181, + 223, + 39, + 200 + ] + }, + "group": "toml", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "parseToml_0", + "description": "ABI-encodes a TOML table.", + "declaration": "function parseToml(string calldata toml) external pure returns (bytes memory abiEncodedData);", + "visibility": "external", + "mutability": "pure", + "signature": "parseToml(string)", + "selector": "0x592151f0", + "selectorBytes": [ + 89, + 33, + 81, + 240 + ] + }, + "group": "toml", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "parseToml_1", + "description": "ABI-encodes a TOML table at `key`.", + "declaration": "function parseToml(string calldata toml, string calldata key) external pure returns (bytes memory abiEncodedData);", + "visibility": "external", + "mutability": "pure", + "signature": "parseToml(string,string)", + "selector": "0x37736e08", + "selectorBytes": [ + 55, + 115, + 110, + 8 + ] + }, + "group": "toml", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "parseUint", @@ -7497,6 +7877,46 @@ "group": "filesystem", "status": "stable", "safety": "safe" + }, + { + "func": { + "id": "writeToml_0", + "description": "Takes serialized JSON, converts to TOML and write a serialized TOML to a file.", + "declaration": "function writeToml(string calldata json, string calldata path) external;", + "visibility": "external", + "mutability": "", + "signature": "writeToml(string,string)", + "selector": "0xc0865ba7", + "selectorBytes": [ + 192, + 134, + 91, + 167 + ] + }, + "group": "toml", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "writeToml_1", + "description": "Takes serialized JSON, converts to TOML and write a serialized TOML table to an **existing** TOML file, replacing a value with key = \nThis is useful to replace a specific value of a TOML file, without having to parse the entire thing.", + "declaration": "function writeToml(string calldata json, string calldata path, string calldata valueKey) external;", + "visibility": "external", + "mutability": "", + "signature": "writeToml(string,string,string)", + "selector": "0x51ac6a33", + "selectorBytes": [ + 81, + 172, + 106, + 51 + ] + }, + "group": "toml", + "status": "stable", + "safety": "safe" } ] } \ No newline at end of file diff --git a/crates/cheatcodes/assets/cheatcodes.schema.json b/crates/cheatcodes/assets/cheatcodes.schema.json index 31f9a1922..cd66ecdc4 100644 --- a/crates/cheatcodes/assets/cheatcodes.schema.json +++ b/crates/cheatcodes/assets/cheatcodes.schema.json @@ -298,6 +298,13 @@ "json" ] }, + { + "description": "Utility cheatcodes that deal with parsing values from and converting values to TOML.\n\nExamples: `parseToml`, `writeToml`.\n\nSafety: safe.", + "type": "string", + "enum": [ + "toml" + ] + }, { "description": "Generic, uncategorized utilities.\n\nExamples: `toString`, `parse*`, `serialize*`.\n\nSafety: safe.", "type": "string", diff --git a/crates/cheatcodes/spec/src/cheatcode.rs b/crates/cheatcodes/spec/src/cheatcode.rs index b78659410..95aa9aa47 100644 --- a/crates/cheatcodes/spec/src/cheatcode.rs +++ b/crates/cheatcodes/spec/src/cheatcode.rs @@ -108,6 +108,12 @@ pub enum Group { /// /// Safety: safe. Json, + /// Utility cheatcodes that deal with parsing values from and converting values to TOML. + /// + /// Examples: `parseToml`, `writeToml`. + /// + /// Safety: safe. + Toml, /// Generic, uncategorized utilities. /// /// Examples: `toString`, `parse*`, `serialize*`. @@ -130,6 +136,7 @@ impl Group { Self::Environment | Self::String | Self::Json | + Self::Toml | Self::Utilities => Some(Safety::Safe), } } @@ -145,6 +152,7 @@ impl Group { Self::Environment => "environment", Self::String => "string", Self::Json => "json", + Self::Toml => "toml", Self::Utilities => "utilities", } } diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index f9e0ca51d..bd6500fb7 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1652,9 +1652,13 @@ interface Vm { // NOTE: Please read https://book.getfoundry.sh/cheatcodes/parse-json to understand the // limitations and caveats of the JSON parsing cheats. + /// Checks if `key` exists in a JSON object + /// `keyExists` is being deprecated in favor of `keyExistsJson`. It will be removed in future versions. + #[cheatcode(group = Json, status = Deprecated)] + function keyExists(string calldata json, string calldata key) external view returns (bool); /// Checks if `key` exists in a JSON object. #[cheatcode(group = Json)] - function keyExists(string calldata json, string calldata key) external view returns (bool); + function keyExistsJson(string calldata json, string calldata key) external view returns (bool); /// ABI-encodes a JSON object. #[cheatcode(group = Json)] @@ -1816,6 +1820,98 @@ interface Vm { #[cheatcode(group = Json)] function writeJson(string calldata json, string calldata path, string calldata valueKey) external; + // ======== TOML Parsing and Manipulation ======== + + // -------- Reading -------- + + // NOTE: Please read https://book.getfoundry.sh/cheatcodes/parse-toml to understand the + // limitations and caveats of the TOML parsing cheat. + + /// Checks if `key` exists in a TOML table. + #[cheatcode(group = Toml)] + function keyExistsToml(string calldata toml, string calldata key) external view returns (bool); + + /// ABI-encodes a TOML table. + #[cheatcode(group = Toml)] + function parseToml(string calldata toml) external pure returns (bytes memory abiEncodedData); + + /// ABI-encodes a TOML table at `key`. + #[cheatcode(group = Toml)] + function parseToml(string calldata toml, string calldata key) external pure returns (bytes memory abiEncodedData); + + // The following parseToml cheatcodes will do type coercion, for the type that they indicate. + // For example, parseTomlUint will coerce all values to a uint256. That includes stringified numbers '12.' + // and hex numbers '0xEF.'. + // Type coercion works ONLY for discrete values or arrays. That means that the key must return a value or array, not + // a TOML table. + + /// Parses a string of TOML data at `key` and coerces it to `uint256`. + #[cheatcode(group = Toml)] + function parseTomlUint(string calldata toml, string calldata key) external pure returns (uint256); + /// Parses a string of TOML data at `key` and coerces it to `uint256[]`. + #[cheatcode(group = Toml)] + function parseTomlUintArray(string calldata toml, string calldata key) external pure returns (uint256[] memory); + /// Parses a string of TOML data at `key` and coerces it to `int256`. + #[cheatcode(group = Toml)] + function parseTomlInt(string calldata toml, string calldata key) external pure returns (int256); + /// Parses a string of TOML data at `key` and coerces it to `int256[]`. + #[cheatcode(group = Toml)] + function parseTomlIntArray(string calldata toml, string calldata key) external pure returns (int256[] memory); + /// Parses a string of TOML data at `key` and coerces it to `bool`. + #[cheatcode(group = Toml)] + function parseTomlBool(string calldata toml, string calldata key) external pure returns (bool); + /// Parses a string of TOML data at `key` and coerces it to `bool[]`. + #[cheatcode(group = Toml)] + function parseTomlBoolArray(string calldata toml, string calldata key) external pure returns (bool[] memory); + /// Parses a string of TOML data at `key` and coerces it to `address`. + #[cheatcode(group = Toml)] + function parseTomlAddress(string calldata toml, string calldata key) external pure returns (address); + /// Parses a string of TOML data at `key` and coerces it to `address[]`. + #[cheatcode(group = Toml)] + function parseTomlAddressArray(string calldata toml, string calldata key) + external + pure + returns (address[] memory); + /// Parses a string of TOML data at `key` and coerces it to `string`. + #[cheatcode(group = Toml)] + function parseTomlString(string calldata toml, string calldata key) external pure returns (string memory); + /// Parses a string of TOML data at `key` and coerces it to `string[]`. + #[cheatcode(group = Toml)] + function parseTomlStringArray(string calldata toml, string calldata key) external pure returns (string[] memory); + /// Parses a string of TOML data at `key` and coerces it to `bytes`. + #[cheatcode(group = Toml)] + function parseTomlBytes(string calldata toml, string calldata key) external pure returns (bytes memory); + /// Parses a string of TOML data at `key` and coerces it to `bytes[]`. + #[cheatcode(group = Toml)] + function parseTomlBytesArray(string calldata toml, string calldata key) external pure returns (bytes[] memory); + /// Parses a string of TOML data at `key` and coerces it to `bytes32`. + #[cheatcode(group = Toml)] + function parseTomlBytes32(string calldata toml, string calldata key) external pure returns (bytes32); + /// Parses a string of TOML data at `key` and coerces it to `bytes32[]`. + #[cheatcode(group = Toml)] + function parseTomlBytes32Array(string calldata toml, string calldata key) + external + pure + returns (bytes32[] memory); + + /// Returns an array of all the keys in a TOML table. + #[cheatcode(group = Toml)] + function parseTomlKeys(string calldata toml, string calldata key) external pure returns (string[] memory keys); + + // -------- Writing -------- + + // NOTE: Please read https://book.getfoundry.sh/cheatcodes/write-toml to understand how + // to use the TOML writing cheat. + + /// Takes serialized JSON, converts to TOML and write a serialized TOML to a file. + #[cheatcode(group = Toml)] + function writeToml(string calldata json, string calldata path) external; + + /// Takes serialized JSON, converts to TOML and write a serialized TOML table to an **existing** TOML file, replacing a value with key = + /// This is useful to replace a specific value of a TOML file, without having to parse the entire thing. + #[cheatcode(group = Toml)] + function writeToml(string calldata json, string calldata path, string calldata valueKey) external; + // -------- Key Management -------- /// Derives a private key from the name, labels the account with that name, and returns the wallet. diff --git a/crates/cheatcodes/src/evm/fork.rs b/crates/cheatcodes/src/evm/fork.rs index 436dd1c62..1d8fc2fe4 100644 --- a/crates/cheatcodes/src/evm/fork.rs +++ b/crates/cheatcodes/src/evm/fork.rs @@ -228,7 +228,7 @@ impl Cheatcode for rpcCall { .block_on(provider.raw_request(method, params_json)) .map_err(|err| fmt_err!("{method:?}: {err}"))?; - let result_as_tokens = crate::json::value_to_token(&result) + let result_as_tokens = crate::json::json_value_to_token(&result) .map_err(|err| fmt_err!("failed to parse result: {err}"))?; Ok(result_as_tokens.abi_encode()) diff --git a/crates/cheatcodes/src/json.rs b/crates/cheatcodes/src/json.rs index 758d46160..b837c8425 100644 --- a/crates/cheatcodes/src/json.rs +++ b/crates/cheatcodes/src/json.rs @@ -12,10 +12,14 @@ use std::{borrow::Cow, collections::BTreeMap, fmt::Write}; impl Cheatcode for keyExistsCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { json, key } = self; - let json = parse_json_str(json)?; - let values = select(&json, key)?; - let exists = !values.is_empty(); - Ok(exists.abi_encode()) + check_json_key_exists(json, key) + } +} + +impl Cheatcode for keyExistsJsonCall { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { json, key } = self; + check_json_key_exists(json, key) } } @@ -134,16 +138,7 @@ impl Cheatcode for parseJsonBytes32ArrayCall { impl Cheatcode for parseJsonKeysCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { json, key } = self; - let json = parse_json_str(json)?; - let values = select(&json, key)?; - let [value] = values[..] else { - bail!("key {key:?} must return exactly one JSON object"); - }; - let Value::Object(object) = value else { - bail!("JSON value at {key:?} is not an object"); - }; - let keys = object.keys().collect::>(); - Ok(keys.abi_encode()) + parse_json_keys(json, key) } } @@ -280,14 +275,21 @@ impl Cheatcode for writeJson_1Call { } } -fn parse_json(json: &str, path: &str) -> Result { +pub(super) fn check_json_key_exists(json: &str, key: &str) -> Result { + let json = parse_json_str(json)?; + let values = select(&json, key)?; + let exists = !values.is_empty(); + Ok(exists.abi_encode()) +} + +pub(super) fn parse_json(json: &str, path: &str) -> Result { let value = parse_json_str(json)?; let selected = select(&value, path)?; let sol = json_to_sol(&selected)?; Ok(encode(sol)) } -fn parse_json_coerce(json: &str, path: &str, ty: &DynSolType) -> Result { +pub(super) fn parse_json_coerce(json: &str, path: &str, ty: &DynSolType) -> Result { let value = parse_json_str(json)?; let values = select(&value, path)?; ensure!(!values.is_empty(), "no matching value found at {path:?}"); @@ -311,6 +313,19 @@ fn parse_json_coerce(json: &str, path: &str, ty: &DynSolType) -> Result { } } +pub(super) fn parse_json_keys(json: &str, key: &str) -> Result { + let json = parse_json_str(json)?; + let values = select(&json, key)?; + let [value] = values[..] else { + bail!("key {key:?} must return exactly one JSON object"); + }; + let Value::Object(object) = value else { + bail!("JSON value at {key:?} is not an object"); + }; + let keys = object.keys().collect::>(); + Ok(keys.abi_encode()) +} + fn parse_json_str(json: &str) -> Result { serde_json::from_str(json).map_err(|e| fmt_err!("failed parsing JSON: {e}")) } @@ -318,7 +333,7 @@ fn parse_json_str(json: &str) -> Result { fn json_to_sol(json: &[&Value]) -> Result> { let mut sol = Vec::with_capacity(json.len()); for value in json { - sol.push(value_to_token(value)?); + sol.push(json_value_to_token(value)?); } Ok(sol) } @@ -345,7 +360,7 @@ fn encode(values: Vec) -> Vec { /// Canonicalize a json path key to always start from the root of the document. /// Read more about json path syntax: -fn canonicalize_json_path(path: &str) -> Cow<'_, str> { +pub(super) fn canonicalize_json_path(path: &str) -> Cow<'_, str> { if !path.starts_with('$') { format!("${path}").into() } else { @@ -359,12 +374,12 @@ fn canonicalize_json_path(path: &str) -> Cow<'_, str> { /// it will call itself to convert each of it's value and encode the whole as a /// Tuple #[instrument(target = "cheatcodes", level = "trace", ret)] -pub(super) fn value_to_token(value: &Value) -> Result { +pub(super) fn json_value_to_token(value: &Value) -> Result { match value { Value::Null => Ok(DynSolValue::FixedBytes(B256::ZERO, 32)), Value::Bool(boolean) => Ok(DynSolValue::Bool(*boolean)), Value::Array(array) => { - array.iter().map(value_to_token).collect::>().map(DynSolValue::Array) + array.iter().map(json_value_to_token).collect::>().map(DynSolValue::Array) } value @ Value::Object(_) => { // See: [#3647](https://github.com/foundry-rs/foundry/pull/3647) @@ -372,7 +387,7 @@ pub(super) fn value_to_token(value: &Value) -> Result { serde_json::from_value(value.clone()).unwrap(); ordered_object .values() - .map(value_to_token) + .map(json_value_to_token) .collect::>() .map(DynSolValue::Tuple) } @@ -400,18 +415,18 @@ pub(super) fn value_to_token(value: &Value) -> Result { // used. let fallback_s = f.to_string(); if let Ok(n) = fallback_s.parse() { - return Ok(DynSolValue::Uint(n, 256)) + return Ok(DynSolValue::Uint(n, 256)); } if let Ok(n) = I256::from_dec_str(&fallback_s) { - return Ok(DynSolValue::Int(n, 256)) + return Ok(DynSolValue::Int(n, 256)); } } if let Ok(n) = s.parse() { - return Ok(DynSolValue::Uint(n, 256)) + return Ok(DynSolValue::Uint(n, 256)); } if let Ok(n) = s.parse() { - return Ok(DynSolValue::Int(n, 256)) + return Ok(DynSolValue::Int(n, 256)); } } } diff --git a/crates/cheatcodes/src/lib.rs b/crates/cheatcodes/src/lib.rs index 86cfb0e5d..a2654e953 100644 --- a/crates/cheatcodes/src/lib.rs +++ b/crates/cheatcodes/src/lib.rs @@ -34,6 +34,7 @@ mod json; mod script; mod string; mod test; +mod toml; mod utils; pub use script::ScriptWallets; diff --git a/crates/cheatcodes/src/toml.rs b/crates/cheatcodes/src/toml.rs new file mode 100644 index 000000000..886c38a4a --- /dev/null +++ b/crates/cheatcodes/src/toml.rs @@ -0,0 +1,243 @@ +//! Implementations of [`Toml`](crate::Group::Toml) cheatcodes. + +use crate::{ + json::{ + canonicalize_json_path, check_json_key_exists, parse_json, parse_json_coerce, + parse_json_keys, + }, + Cheatcode, Cheatcodes, Result, + Vm::*, +}; +use alloy_dyn_abi::DynSolType; +use foundry_common::fs; +use foundry_config::fs_permissions::FsAccessKind; +use serde_json::Value as JsonValue; +use toml::Value as TomlValue; + +impl Cheatcode for keyExistsTomlCall { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { toml, key } = self; + check_json_key_exists(&toml_to_json_string(toml)?, key) + } +} + +impl Cheatcode for parseToml_0Call { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { toml } = self; + parse_toml(toml, "$") + } +} + +impl Cheatcode for parseToml_1Call { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { toml, key } = self; + parse_toml(toml, key) + } +} + +impl Cheatcode for parseTomlUintCall { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { toml, key } = self; + parse_toml_coerce(toml, key, &DynSolType::Uint(256)) + } +} + +impl Cheatcode for parseTomlUintArrayCall { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { toml, key } = self; + parse_toml_coerce(toml, key, &DynSolType::Uint(256)) + } +} + +impl Cheatcode for parseTomlIntCall { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { toml, key } = self; + parse_toml_coerce(toml, key, &DynSolType::Int(256)) + } +} + +impl Cheatcode for parseTomlIntArrayCall { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { toml, key } = self; + parse_toml_coerce(toml, key, &DynSolType::Int(256)) + } +} + +impl Cheatcode for parseTomlBoolCall { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { toml, key } = self; + parse_toml_coerce(toml, key, &DynSolType::Bool) + } +} + +impl Cheatcode for parseTomlBoolArrayCall { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { toml, key } = self; + parse_toml_coerce(toml, key, &DynSolType::Bool) + } +} + +impl Cheatcode for parseTomlAddressCall { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { toml, key } = self; + parse_toml_coerce(toml, key, &DynSolType::Address) + } +} + +impl Cheatcode for parseTomlAddressArrayCall { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { toml, key } = self; + parse_toml_coerce(toml, key, &DynSolType::Address) + } +} + +impl Cheatcode for parseTomlStringCall { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { toml, key } = self; + parse_toml_coerce(toml, key, &DynSolType::String) + } +} + +impl Cheatcode for parseTomlStringArrayCall { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { toml, key } = self; + parse_toml_coerce(toml, key, &DynSolType::String) + } +} + +impl Cheatcode for parseTomlBytesCall { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { toml, key } = self; + parse_toml_coerce(toml, key, &DynSolType::Bytes) + } +} + +impl Cheatcode for parseTomlBytesArrayCall { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { toml, key } = self; + parse_toml_coerce(toml, key, &DynSolType::Bytes) + } +} + +impl Cheatcode for parseTomlBytes32Call { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { toml, key } = self; + parse_toml_coerce(toml, key, &DynSolType::FixedBytes(32)) + } +} + +impl Cheatcode for parseTomlBytes32ArrayCall { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { toml, key } = self; + parse_toml_coerce(toml, key, &DynSolType::FixedBytes(32)) + } +} + +impl Cheatcode for parseTomlKeysCall { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { toml, key } = self; + parse_toml_keys(toml, key) + } +} + +impl Cheatcode for writeToml_0Call { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let Self { json, path } = self; + let value = + serde_json::from_str(json).unwrap_or_else(|_| JsonValue::String(json.to_owned())); + + let toml_string = format_json_to_toml(value)?; + super::fs::write_file(state, path.as_ref(), toml_string.as_bytes()) + } +} + +impl Cheatcode for writeToml_1Call { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let Self { json, path, valueKey } = self; + let json = + serde_json::from_str(json).unwrap_or_else(|_| JsonValue::String(json.to_owned())); + + let data_path = state.config.ensure_path_allowed(path, FsAccessKind::Read)?; + let toml_data = fs::read_to_string(data_path)?; + let json_data: JsonValue = + toml::from_str(&toml_data).map_err(|e| fmt_err!("failed parsing TOML: {e}"))?; + let value = + jsonpath_lib::replace_with(json_data, &canonicalize_json_path(valueKey), &mut |_| { + Some(json.clone()) + })?; + + let toml_string = format_json_to_toml(value)?; + super::fs::write_file(state, path.as_ref(), toml_string.as_bytes()) + } +} + +/// Parse +fn parse_toml_str(toml: &str) -> Result { + toml::from_str(toml).map_err(|e| fmt_err!("failed parsing TOML: {e}")) +} + +/// Parse a TOML string and return the value at the given path. +fn parse_toml(toml: &str, key: &str) -> Result { + parse_json(&toml_to_json_string(toml)?, key) +} + +/// Parse a TOML string and return the value at the given path, coercing it to the given type. +fn parse_toml_coerce(toml: &str, key: &str, ty: &DynSolType) -> Result { + parse_json_coerce(&toml_to_json_string(toml)?, key, ty) +} + +/// Parse a TOML string and return an array of all keys at the given path. +fn parse_toml_keys(toml: &str, key: &str) -> Result { + parse_json_keys(&toml_to_json_string(toml)?, key) +} + +/// Convert a TOML string to a JSON string. +fn toml_to_json_string(toml: &str) -> Result { + let toml = parse_toml_str(toml)?; + let json = toml_to_json_value(toml); + serde_json::to_string(&json).map_err(|e| fmt_err!("failed to serialize JSON: {e}")) +} + +/// Format a JSON value to a TOML pretty string. +fn format_json_to_toml(json: JsonValue) -> Result { + let toml = json_to_toml_value(json); + toml::to_string_pretty(&toml).map_err(|e| fmt_err!("failed to serialize TOML: {e}")) +} + +/// Convert a TOML value to a JSON value. +fn toml_to_json_value(toml: TomlValue) -> JsonValue { + match toml { + TomlValue::String(s) => match s.as_str() { + "null" => JsonValue::Null, + _ => JsonValue::String(s), + }, + TomlValue::Integer(i) => JsonValue::Number(i.into()), + TomlValue::Float(f) => JsonValue::Number(serde_json::Number::from_f64(f).unwrap()), + TomlValue::Boolean(b) => JsonValue::Bool(b), + TomlValue::Array(a) => JsonValue::Array(a.into_iter().map(toml_to_json_value).collect()), + TomlValue::Table(t) => { + JsonValue::Object(t.into_iter().map(|(k, v)| (k, toml_to_json_value(v))).collect()) + } + TomlValue::Datetime(d) => JsonValue::String(d.to_string()), + } +} + +/// Convert a JSON value to a TOML value. +fn json_to_toml_value(json: JsonValue) -> TomlValue { + match json { + JsonValue::String(s) => TomlValue::String(s), + JsonValue::Number(n) => match n.as_i64() { + Some(i) => TomlValue::Integer(i), + None => match n.as_f64() { + Some(f) => TomlValue::Float(f), + None => TomlValue::String(n.to_string()), + }, + }, + JsonValue::Bool(b) => TomlValue::Boolean(b), + JsonValue::Array(a) => TomlValue::Array(a.into_iter().map(json_to_toml_value).collect()), + JsonValue::Object(o) => { + TomlValue::Table(o.into_iter().map(|(k, v)| (k, json_to_toml_value(v))).collect()) + } + JsonValue::Null => TomlValue::String("null".to_string()), + } +} diff --git a/crates/evm/traces/src/decoder/mod.rs b/crates/evm/traces/src/decoder/mod.rs index fbe797a00..78193a5f9 100644 --- a/crates/evm/traces/src/decoder/mod.rs +++ b/crates/evm/traces/src/decoder/mod.rs @@ -436,7 +436,9 @@ impl CallTraceDecoder { "parseJsonBytes32" | "parseJsonBytes32Array" | "writeJson" | - "keyExists" | + // `keyExists` is being deprecated in favor of `keyExistsJson`. It will be removed in future versions. + "keyExists" | + "keyExistsJson" | "serializeBool" | "serializeUint" | "serializeInt" | @@ -448,12 +450,31 @@ impl CallTraceDecoder { None } else { let mut decoded = func.abi_decode_input(&data[SELECTOR_LEN..], false).ok()?; - let token = - if func.name.as_str() == "parseJson" || func.name.as_str() == "keyExists" { - "" - } else { - "" - }; + let token = if func.name.as_str() == "parseJson" || + // `keyExists` is being deprecated in favor of `keyExistsJson`. It will be removed in future versions. + func.name.as_str() == "keyExists" || + func.name.as_str() == "keyExistsJson" + { + "" + } else { + "" + }; + decoded[0] = DynSolValue::String(token.to_string()); + Some(decoded.iter().map(format_token).collect()) + } + } + s if s.contains("Toml") => { + if self.verbosity >= 5 { + None + } else { + let mut decoded = func.abi_decode_input(&data[SELECTOR_LEN..], false).ok()?; + let token = if func.name.as_str() == "parseToml" || + func.name.as_str() == "keyExistsToml" + { + "" + } else { + "" + }; decoded[0] = DynSolValue::String(token.to_string()); Some(decoded.iter().map(format_token).collect()) } diff --git a/testdata/cheats/Json.t.sol b/testdata/cheats/Json.t.sol index a43a7be5a..3d44dbd2c 100644 --- a/testdata/cheats/Json.t.sol +++ b/testdata/cheats/Json.t.sol @@ -14,39 +14,25 @@ contract ParseJsonTest is DSTest { json = vm.readFile(path); } - function test_uintArray() public { - bytes memory data = vm.parseJson(json, ".uintArray"); - uint256[] memory decodedData = abi.decode(data, (uint256[])); - assertEq(42, decodedData[0]); - assertEq(43, decodedData[1]); - } - - function test_str() public { - bytes memory data = vm.parseJson(json, ".str"); + function test_basicString() public { + bytes memory data = vm.parseJson(json, ".basicString"); string memory decodedData = abi.decode(data, (string)); assertEq("hai", decodedData); } - function test_strArray() public { - bytes memory data = vm.parseJson(json, ".strArray"); + function test_null() public { + bytes memory data = vm.parseJson(json, ".null"); + bytes memory decodedData = abi.decode(data, (bytes)); + assertEq(new bytes(0), decodedData); + } + + function test_stringArray() public { + bytes memory data = vm.parseJson(json, ".stringArray"); string[] memory decodedData = abi.decode(data, (string[])); assertEq("hai", decodedData[0]); assertEq("there", decodedData[1]); } - function test_bool() public { - bytes memory data = vm.parseJson(json, ".bool"); - bool decodedData = abi.decode(data, (bool)); - assertTrue(decodedData); - } - - function test_boolArray() public { - bytes memory data = vm.parseJson(json, ".boolArray"); - bool[] memory decodedData = abi.decode(data, (bool[])); - assertTrue(decodedData[0]); - assertTrue(!decodedData[1]); - } - function test_address() public { bytes memory data = vm.parseJson(json, ".address"); address decodedData = abi.decode(data, (address)); @@ -65,18 +51,31 @@ contract ParseJsonTest is DSTest { assertEq("0000000000000000000000000000000000001337", data); } - struct Nested { - uint256 number; - string str; + function test_bool() public { + bytes memory data = vm.parseJson(json, ".boolTrue"); + bool decodedData = abi.decode(data, (bool)); + assertTrue(decodedData); + + data = vm.parseJson(json, ".boolFalse"); + decodedData = abi.decode(data, (bool)); + assertTrue(!decodedData); } - function test_nestedObject() public { - bytes memory data = vm.parseJson(json, ".nestedObject"); - Nested memory nested = abi.decode(data, (Nested)); - assertEq(nested.number, 115792089237316195423570985008687907853269984665640564039457584007913129639935); - assertEq(nested.str, "NEST"); + function test_boolArray() public { + bytes memory data = vm.parseJson(json, ".boolArray"); + bool[] memory decodedData = abi.decode(data, (bool[])); + assertTrue(decodedData[0]); + assertTrue(!decodedData[1]); + } + + function test_uintArray() public { + bytes memory data = vm.parseJson(json, ".uintArray"); + uint256[] memory decodedData = abi.decode(data, (uint256[])); + assertEq(42, decodedData[0]); + assertEq(43, decodedData[1]); } + // Object keys are sorted alphabetically, regardless of input. struct Whole { string str; string[] strArray; @@ -85,7 +84,7 @@ contract ParseJsonTest is DSTest { function test_wholeObject() public { // we need to make the path relative to the crate that's running tests for it (forge crate) - string memory path = "fixtures/Json/wholeJson.json"; + string memory path = "fixtures/Json/whole_json.json"; console.log(path); json = vm.readFile(path); bytes memory data = vm.parseJson(json); @@ -99,35 +98,68 @@ contract ParseJsonTest is DSTest { function test_coercionRevert() public { vm._expectCheatcodeRevert("values at \".nestedObject\" must not be JSON objects"); - uint256 number = vm.parseJsonUint(json, ".nestedObject"); + vm.parseJsonUint(json, ".nestedObject"); } function test_coercionUint() public { - uint256 number = vm.parseJsonUint(json, ".hexUint"); + uint256 number = vm.parseJsonUint(json, ".uintHex"); assertEq(number, 1231232); - number = vm.parseJsonUint(json, ".stringUint"); + number = vm.parseJsonUint(json, ".uintString"); assertEq(number, 115792089237316195423570985008687907853269984665640564039457584007913129639935); - number = vm.parseJsonUint(json, ".numberUint"); + number = vm.parseJsonUint(json, ".uintNumber"); assertEq(number, 115792089237316195423570985008687907853269984665640564039457584007913129639935); - uint256[] memory numbers = vm.parseJsonUintArray(json, ".arrayUint"); + uint256[] memory numbers = vm.parseJsonUintArray(json, ".uintArray"); + assertEq(numbers[0], 42); + assertEq(numbers[1], 43); + numbers = vm.parseJsonUintArray(json, ".uintStringArray"); assertEq(numbers[0], 1231232); assertEq(numbers[1], 1231232); assertEq(numbers[2], 1231232); } function test_coercionInt() public { - int256 number = vm.parseJsonInt(json, ".hexInt"); + int256 number = vm.parseJsonInt(json, ".intNumber"); assertEq(number, -12); - number = vm.parseJsonInt(json, ".stringInt"); + number = vm.parseJsonInt(json, ".intString"); + assertEq(number, -12); + number = vm.parseJsonInt(json, ".intHex"); assertEq(number, -12); } function test_coercionBool() public { - bool boolean = vm.parseJsonBool(json, ".booleanString"); + bool boolean = vm.parseJsonBool(json, ".boolTrue"); + assertTrue(boolean); + bool boolFalse = vm.parseJsonBool(json, ".boolFalse"); + assertTrue(!boolFalse); + boolean = vm.parseJsonBool(json, ".boolString"); assertEq(boolean, true); - bool[] memory booleans = vm.parseJsonBoolArray(json, ".booleanArray"); - assert(booleans[0]); - assert(!booleans[1]); + bool[] memory booleans = vm.parseJsonBoolArray(json, ".boolArray"); + assertTrue(booleans[0]); + assertTrue(!booleans[1]); + booleans = vm.parseJsonBoolArray(json, ".boolStringArray"); + assertTrue(booleans[0]); + assertTrue(!booleans[1]); + } + + function test_coercionBytes() public { + bytes memory bytes_ = vm.parseJsonBytes(json, ".bytesString"); + assertEq(bytes_, hex"01"); + + bytes[] memory bytesArray = vm.parseJsonBytesArray(json, ".bytesStringArray"); + assertEq(bytesArray[0], hex"01"); + assertEq(bytesArray[1], hex"02"); + } + + struct Nested { + uint256 number; + string str; + } + + function test_nestedObject() public { + bytes memory data = vm.parseJson(json, ".nestedObject"); + Nested memory nested = abi.decode(data, (Nested)); + assertEq(nested.number, 115792089237316195423570985008687907853269984665640564039457584007913129639935); + assertEq(nested.str, "NEST"); } function test_advancedJsonPath() public { @@ -138,7 +170,7 @@ contract ParseJsonTest is DSTest { } function test_canonicalizePath() public { - bytes memory data = vm.parseJson(json, "$.str"); + bytes memory data = vm.parseJson(json, "$.basicString"); string memory decodedData = abi.decode(data, (string)); assertEq("hai", decodedData); } @@ -286,17 +318,25 @@ contract WriteJsonTest is DSTest { assertEq(decodedData.a, 123); } - function test_checkKeyExists() public { + function test_checkKeyExistsJson() public { string memory path = "fixtures/Json/write_complex_test.json"; string memory json = vm.readFile(path); - bool exists = vm.keyExists(json, ".a"); + bool exists = vm.keyExistsJson(json, ".a"); + assertTrue(exists); + + // TODO: issue deprecation warning + exists = vm.keyExists(json, ".a"); assertTrue(exists); } - function test_checkKeyDoesNotExist() public { + function test_checkKeyDoesNotExistJson() public { string memory path = "fixtures/Json/write_complex_test.json"; string memory json = vm.readFile(path); - bool exists = vm.keyExists(json, ".d"); + bool exists = vm.keyExistsJson(json, ".d"); + assertTrue(!exists); + + // TODO: issue deprecation warning + exists = vm.keyExists(json, ".d"); assertTrue(!exists); } diff --git a/testdata/cheats/Toml.t.sol b/testdata/cheats/Toml.t.sol new file mode 100644 index 000000000..ccb73ab87 --- /dev/null +++ b/testdata/cheats/Toml.t.sol @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "./Vm.sol"; +import "../logs/console.sol"; + +contract ParseTomlTest is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + string toml; + + function setUp() public { + string memory path = "fixtures/Toml/test.toml"; + toml = vm.readFile(path); + } + + function test_basicString() public { + bytes memory data = vm.parseToml(toml, ".basicString"); + string memory decodedData = abi.decode(data, (string)); + assertEq("hai", decodedData); + } + + function test_nullString() public { + bytes memory data = vm.parseToml(toml, ".nullString"); + string memory decodedData = abi.decode(data, (string)); + assertEq("", decodedData); + } + + function test_stringMultiline() public { + bytes memory data = vm.parseToml(toml, ".multilineString"); + string memory decodedData = abi.decode(data, (string)); + assertEq("hai\nthere\n", decodedData); + } + + function test_stringArray() public { + bytes memory data = vm.parseToml(toml, ".stringArray"); + string[] memory decodedData = abi.decode(data, (string[])); + assertEq("hai", decodedData[0]); + assertEq("there", decodedData[1]); + } + + function test_address() public { + bytes memory data = vm.parseToml(toml, ".address"); + address decodedData = abi.decode(data, (address)); + assertEq(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, decodedData); + } + + function test_addressArray() public { + bytes memory data = vm.parseToml(toml, ".addressArray"); + address[] memory decodedData = abi.decode(data, (address[])); + assertEq(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, decodedData[0]); + assertEq(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D, decodedData[1]); + } + + function test_H160ButNotaddress() public { + string memory data = abi.decode(vm.parseToml(toml, ".H160NotAddress"), (string)); + assertEq("0000000000000000000000000000000000001337", data); + } + + function test_bool() public { + bytes memory data = vm.parseToml(toml, ".boolTrue"); + bool decodedData = abi.decode(data, (bool)); + assertTrue(decodedData); + + data = vm.parseToml(toml, ".boolFalse"); + decodedData = abi.decode(data, (bool)); + assertTrue(!decodedData); + } + + function test_boolArray() public { + bytes memory data = vm.parseToml(toml, ".boolArray"); + bool[] memory decodedData = abi.decode(data, (bool[])); + assertTrue(decodedData[0]); + assertTrue(!decodedData[1]); + } + + function test_dateTime() public { + bytes memory data = vm.parseToml(toml, ".datetime"); + string memory decodedData = abi.decode(data, (string)); + assertEq(decodedData, "2021-08-10T14:48:00Z"); + } + + function test_dateTimeArray() public { + bytes memory data = vm.parseToml(toml, ".datetimeArray"); + string[] memory decodedData = abi.decode(data, (string[])); + assertEq(decodedData[0], "2021-08-10T14:48:00Z"); + assertEq(decodedData[1], "2021-08-10T14:48:00Z"); + } + + function test_uintArray() public { + bytes memory data = vm.parseToml(toml, ".uintArray"); + uint256[] memory decodedData = abi.decode(data, (uint256[])); + assertEq(42, decodedData[0]); + assertEq(43, decodedData[1]); + } + + // Object keys are sorted alphabetically, regardless of input. + struct Whole { + string str; + string[] strArray; + uint256[] uintArray; + } + + function test_wholeToml() public { + // we need to make the path relative to the crate that's running tests for it (forge crate) + string memory path = "fixtures/Toml/whole_toml.toml"; + console.log(path); + toml = vm.readFile(path); + bytes memory data = vm.parseToml(toml); + Whole memory whole = abi.decode(data, (Whole)); + assertEq(whole.str, "hai"); + assertEq(whole.strArray[0], "hai"); + assertEq(whole.strArray[1], "there"); + assertEq(whole.uintArray[0], 42); + assertEq(whole.uintArray[1], 43); + } + + function test_coercionRevert() public { + vm._expectCheatcodeRevert("values at \".nestedObject\" must not be JSON objects"); + vm.parseTomlUint(toml, ".nestedObject"); + } + + function test_coercionUint() public { + uint256 number = vm.parseTomlUint(toml, ".uintNumber"); + assertEq(number, 9223372036854775807); // TOML is limited to 64-bit integers + number = vm.parseTomlUint(toml, ".uintString"); + assertEq(number, 115792089237316195423570985008687907853269984665640564039457584007913129639935); + number = vm.parseTomlUint(toml, ".uintHex"); + assertEq(number, 1231232); + uint256[] memory numbers = vm.parseTomlUintArray(toml, ".uintArray"); + assertEq(numbers[0], 42); + assertEq(numbers[1], 43); + numbers = vm.parseTomlUintArray(toml, ".uintStringArray"); + assertEq(numbers[0], 1231232); + assertEq(numbers[1], 1231232); + assertEq(numbers[2], 1231232); + } + + function test_coercionInt() public { + int256 number = vm.parseTomlInt(toml, ".intNumber"); + assertEq(number, -12); + number = vm.parseTomlInt(toml, ".intString"); + assertEq(number, -12); + number = vm.parseTomlInt(toml, ".intHex"); + assertEq(number, -12); + } + + function test_coercionBool() public { + bool boolean = vm.parseTomlBool(toml, ".boolTrue"); + assertTrue(boolean); + bool boolFalse = vm.parseTomlBool(toml, ".boolFalse"); + assertTrue(!boolFalse); + boolean = vm.parseTomlBool(toml, ".boolString"); + assertEq(boolean, true); + bool[] memory booleans = vm.parseTomlBoolArray(toml, ".boolArray"); + assertTrue(booleans[0]); + assertTrue(!booleans[1]); + booleans = vm.parseTomlBoolArray(toml, ".boolStringArray"); + assertTrue(booleans[0]); + assertTrue(!booleans[1]); + } + + function test_coercionBytes() public { + bytes memory bytes_ = vm.parseTomlBytes(toml, ".bytesString"); + assertEq(bytes_, hex"01"); + + bytes[] memory bytesArray = vm.parseTomlBytesArray(toml, ".bytesStringArray"); + assertEq(bytesArray[0], hex"01"); + assertEq(bytesArray[1], hex"02"); + } + + struct Nested { + uint256 number; + string str; + } + + function test_nestedObject() public { + bytes memory data = vm.parseToml(toml, ".nestedObject"); + Nested memory nested = abi.decode(data, (Nested)); + assertEq(nested.number, 9223372036854775807); // TOML is limited to 64-bit integers + assertEq(nested.str, "NEST"); + } + + function test_advancedJsonPath() public { + bytes memory data = vm.parseToml(toml, ".advancedJsonPath[*].id"); + uint256[] memory numbers = abi.decode(data, (uint256[])); + assertEq(numbers[0], 1); + assertEq(numbers[1], 2); + } + + function test_canonicalizePath() public { + bytes memory data = vm.parseToml(toml, "$.basicString"); + string memory decodedData = abi.decode(data, (string)); + assertEq("hai", decodedData); + } + + function test_nonExistentKey() public { + bytes memory data = vm.parseToml(toml, ".thisKeyDoesNotExist"); + assertEq(0, data.length); + } + + function test_parseTomlKeys() public { + string memory tomlString = + "some_key_to_value = \"some_value\"\n some_key_to_array = [1, 2, 3]\n [some_key_to_object]\n key1 = \"value1\"\n key2 = 2"; + + string[] memory keys = vm.parseTomlKeys(tomlString, "$"); + string[] memory expected = new string[](3); + expected[0] = "some_key_to_value"; + expected[1] = "some_key_to_array"; + expected[2] = "some_key_to_object"; + assertEq(abi.encode(keys), abi.encode(expected)); + + keys = vm.parseTomlKeys(tomlString, ".some_key_to_object"); + expected = new string[](2); + expected[0] = "key1"; + expected[1] = "key2"; + assertEq(abi.encode(keys), abi.encode(expected)); + + vm._expectCheatcodeRevert("JSON value at \".some_key_to_array\" is not an object"); + vm.parseTomlKeys(tomlString, ".some_key_to_array"); + + vm._expectCheatcodeRevert("JSON value at \".some_key_to_value\" is not an object"); + vm.parseTomlKeys(tomlString, ".some_key_to_value"); + + vm._expectCheatcodeRevert("key \".*\" must return exactly one JSON object"); + vm.parseTomlKeys(tomlString, ".*"); + } +} + +contract WriteTomlTest is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + string json1; + string json2; + + function setUp() public { + json1 = "example"; + json2 = "example2"; + } + + struct simpleJson { + uint256 a; + string b; + } + + struct notSimpleJson { + uint256 a; + string b; + simpleJson c; + } + + function test_serializeNotSimpleToml() public { + string memory json3 = "json3"; + string memory path = "fixtures/Toml/write_complex_test.toml"; + vm.serializeUint(json3, "a", uint256(123)); + string memory semiFinal = vm.serializeString(json3, "b", "test"); + string memory finalJson = vm.serializeString(json3, "c", semiFinal); + console.log(finalJson); + vm.writeToml(finalJson, path); + string memory toml = vm.readFile(path); + bytes memory data = vm.parseToml(toml); + notSimpleJson memory decodedData = abi.decode(data, (notSimpleJson)); + } + + function test_retrieveEntireToml() public { + string memory path = "fixtures/Toml/write_complex_test.toml"; + string memory toml = vm.readFile(path); + bytes memory data = vm.parseToml(toml, "."); + notSimpleJson memory decodedData = abi.decode(data, (notSimpleJson)); + console.log(decodedData.a); + assertEq(decodedData.a, 123); + } + + function test_checkKeyExists() public { + string memory path = "fixtures/Toml/write_complex_test.toml"; + string memory toml = vm.readFile(path); + bool exists = vm.keyExistsToml(toml, ".a"); + assertTrue(exists); + } + + function test_checkKeyDoesNotExist() public { + string memory path = "fixtures/Toml/write_complex_test.toml"; + string memory toml = vm.readFile(path); + bool exists = vm.keyExistsToml(toml, ".d"); + assertTrue(!exists); + } + + function test_writeToml() public { + string memory json3 = "json3"; + string memory path = "fixtures/Toml/write_test.toml"; + vm.serializeUint(json3, "a", uint256(123)); + string memory finalJson = vm.serializeString(json3, "b", "test"); + vm.writeToml(finalJson, path); + + string memory toml = vm.readFile(path); + bytes memory data = vm.parseToml(toml); + simpleJson memory decodedData = abi.decode(data, (simpleJson)); + assertEq(decodedData.a, 123); + assertEq(decodedData.b, "test"); + + // write json3 to key b + vm.writeToml(finalJson, path, ".b"); + // read again + toml = vm.readFile(path); + data = vm.parseToml(toml, ".b"); + decodedData = abi.decode(data, (simpleJson)); + assertEq(decodedData.a, 123); + assertEq(decodedData.b, "test"); + + // replace a single value to key b + address ex = address(0xBEEF); + vm.writeToml(vm.toString(ex), path, ".b"); + toml = vm.readFile(path); + data = vm.parseToml(toml, ".b"); + address decodedAddress = abi.decode(data, (address)); + assertEq(decodedAddress, ex); + } +} diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index f28c71923..623ef254b 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -241,6 +241,8 @@ interface Vm { function isFile(string calldata path) external returns (bool result); function isPersistent(address account) external view returns (bool persistent); function keyExists(string calldata json, string calldata key) external view returns (bool); + function keyExistsJson(string calldata json, string calldata key) external view returns (bool); + function keyExistsToml(string calldata toml, string calldata key) external view returns (bool); function label(address account, string calldata newLabel) external; function load(address target, bytes32 slot) external view returns (bytes32 data); function loadAllocs(string calldata pathToAllocsJson) external; @@ -274,6 +276,23 @@ interface Vm { function parseJsonUintArray(string calldata json, string calldata key) external pure returns (uint256[] memory); function parseJson(string calldata json) external pure returns (bytes memory abiEncodedData); function parseJson(string calldata json, string calldata key) external pure returns (bytes memory abiEncodedData); + function parseTomlAddress(string calldata toml, string calldata key) external pure returns (address); + function parseTomlAddressArray(string calldata toml, string calldata key) external pure returns (address[] memory); + function parseTomlBool(string calldata toml, string calldata key) external pure returns (bool); + function parseTomlBoolArray(string calldata toml, string calldata key) external pure returns (bool[] memory); + function parseTomlBytes(string calldata toml, string calldata key) external pure returns (bytes memory); + function parseTomlBytes32(string calldata toml, string calldata key) external pure returns (bytes32); + function parseTomlBytes32Array(string calldata toml, string calldata key) external pure returns (bytes32[] memory); + function parseTomlBytesArray(string calldata toml, string calldata key) external pure returns (bytes[] memory); + function parseTomlInt(string calldata toml, string calldata key) external pure returns (int256); + function parseTomlIntArray(string calldata toml, string calldata key) external pure returns (int256[] memory); + function parseTomlKeys(string calldata toml, string calldata key) external pure returns (string[] memory keys); + function parseTomlString(string calldata toml, string calldata key) external pure returns (string memory); + function parseTomlStringArray(string calldata toml, string calldata key) external pure returns (string[] memory); + function parseTomlUint(string calldata toml, string calldata key) external pure returns (uint256); + function parseTomlUintArray(string calldata toml, string calldata key) external pure returns (uint256[] memory); + function parseToml(string calldata toml) external pure returns (bytes memory abiEncodedData); + function parseToml(string calldata toml, string calldata key) external pure returns (bytes memory abiEncodedData); function parseUint(string calldata stringifiedValue) external pure returns (uint256 parsedValue); function pauseGasMetering() external; function prank(address msgSender) external; @@ -372,4 +391,6 @@ interface Vm { function writeJson(string calldata json, string calldata path) external; function writeJson(string calldata json, string calldata path, string calldata valueKey) external; function writeLine(string calldata path, string calldata data) external; + function writeToml(string calldata json, string calldata path) external; + function writeToml(string calldata json, string calldata path, string calldata valueKey) external; } diff --git a/testdata/fixtures/Json/test.json b/testdata/fixtures/Json/test.json index 4e4ade783..1f59ba456 100644 --- a/testdata/fixtures/Json/test.json +++ b/testdata/fixtures/Json/test.json @@ -1,28 +1,31 @@ { - "str": "hai", - "uintArray": [42, 43], - "strArray": ["hai", "there"], - "bool": true, - "boolArray": [true, false], + "basicString": "hai", + "null": null, + "stringArray": ["hai", "there"], "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", "addressArray": [ "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", "0x7109709ECfa91a80626fF3989D68f67F5b1DD12D" ], "H160NotAddress": "0000000000000000000000000000000000001337", + "boolTrue": true, + "boolFalse": false, + "boolArray": [true, false], + "boolString": "true", + "boolStringArray": [true, "false"], + "uintNumber": 115792089237316195423570985008687907853269984665640564039457584007913129639935, + "uintString": "115792089237316195423570985008687907853269984665640564039457584007913129639935", + "uintHex": "0x12C980", + "uintArray": [42, 43], + "uintStringArray": [1231232, "0x12C980", "1231232"], + "intNumber": -12, + "intString": "-12", + "intHex": "-0xC", + "bytesString": "0x01", + "bytesStringArray": ["0x01", "0x02"], "nestedObject": { "number": 115792089237316195423570985008687907853269984665640564039457584007913129639935, "str": "NEST" }, - "bytesArray": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000966666920776f726b730000000000000000000000000000000000000000000000", - "hexUint": "0x12C980", - "stringUint": "115792089237316195423570985008687907853269984665640564039457584007913129639935", - "numberUint": 115792089237316195423570985008687907853269984665640564039457584007913129639935, - "arrayUint": [1231232, "0x12C980", "1231232"], - "stringInt": "-12", - "numberInt": -12, - "hexInt": "-0xC", - "booleanString": "true", - "booleanArray": [true, "false"], "advancedJsonPath": [{ "id": 1 }, { "id": 2 }] } diff --git a/testdata/fixtures/Json/wholeJson.json b/testdata/fixtures/Json/whole_json.json similarity index 100% rename from testdata/fixtures/Json/wholeJson.json rename to testdata/fixtures/Json/whole_json.json diff --git a/testdata/fixtures/Toml/test.toml b/testdata/fixtures/Toml/test.toml new file mode 100644 index 000000000..ce735b8f1 --- /dev/null +++ b/testdata/fixtures/Toml/test.toml @@ -0,0 +1,50 @@ +basicString = "hai" +nullString = "null" +multilineString = """ +hai +there +""" +stringArray = ["hai", "there"] + +address = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" +addressArray = [ + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "0x7109709ECfa91a80626fF3989D68f67F5b1DD12D", +] +H160NotAddress = "0000000000000000000000000000000000001337" + +boolTrue = true +boolFalse = false +boolArray = [true, false] +boolString = "true" +boolStringArray = ["true", "false"] # Array values can't have mixed types + +datetime = 2021-08-10T14:48:00Z +datetimeArray = [2021-08-10T14:48:00Z, 2021-08-10T14:48:00Z] + +uintNumber = 9223372036854775807 # TOML is limited to 64-bit integers +uintString = "115792089237316195423570985008687907853269984665640564039457584007913129639935" +uintHex = "0x12C980" +uintArray = [42, 43] +uintStringArray = [ + "1231232", + "0x12C980", + "1231232", +] # Array values can't have mixed types + +intNumber = -12 +intString = "-12" +intHex = "-0xC" + +bytesString = "0x01" +bytesStringArray = ["0x01", "0x02"] + +[nestedObject] +number = 9223372036854775807 # TOML is limited to 64-bit integers +str = "NEST" + +[[advancedJsonPath]] +id = 1 + +[[advancedJsonPath]] +id = 2 diff --git a/testdata/fixtures/Toml/whole_toml.toml b/testdata/fixtures/Toml/whole_toml.toml new file mode 100644 index 000000000..badbd9fbb --- /dev/null +++ b/testdata/fixtures/Toml/whole_toml.toml @@ -0,0 +1,3 @@ +str = "hai" +uintArray = [42, 43] +strArray = ["hai", "there"] diff --git a/testdata/fixtures/Toml/write_complex_test.toml b/testdata/fixtures/Toml/write_complex_test.toml new file mode 100644 index 000000000..60692bc75 --- /dev/null +++ b/testdata/fixtures/Toml/write_complex_test.toml @@ -0,0 +1,6 @@ +a = 123 +b = "test" + +[c] +a = 123 +b = "test" diff --git a/testdata/fixtures/Toml/write_test.toml b/testdata/fixtures/Toml/write_test.toml new file mode 100644 index 000000000..6c084e370 --- /dev/null +++ b/testdata/fixtures/Toml/write_test.toml @@ -0,0 +1,2 @@ +a = 123 +b = "0x000000000000000000000000000000000000bEEF" From 18bffa695215c605300788e0e4307a55bd5fd589 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Fri, 8 Mar 2024 17:30:42 +0400 Subject: [PATCH 042/430] script refactoring (#7247) * [wip] script refactoring * execution refactor * refactor simulation * wip * wip * address #7244 * wip: enum for multi/single sequences * refactor execution + resume * wip: refactor verification * wip: cleaning up * naming * wip: clean up * cleanup ScriptSequence * fmt * better rpc tracking * fix rpc logic + extract states to separate file * fmt * some docs * remove --multi flag mentions * docs * checkpoint saves for multichain sequences * docs + broadcasted renamed into dry_run * fmt * Update crates/forge/bin/cmd/script/resume.rs Co-authored-by: Matthias Seitz * fmt * review fixes * fmt * wip: start extracting to separate crate * Use CoreBuildArgs + skip * fmt * review fixes * review fixes * remove redundant methods --------- Co-authored-by: Matthias Seitz --- Cargo.lock | 41 + Cargo.toml | 1 + crates/common/src/contracts.rs | 17 - crates/forge/Cargo.toml | 24 +- crates/forge/bin/cmd/debug.rs | 4 +- crates/forge/bin/cmd/init.rs | 5 - crates/forge/bin/cmd/mod.rs | 1 - crates/forge/bin/cmd/script/broadcast.rs | 701 ------------------ crates/forge/bin/cmd/script/build.rs | 208 ------ crates/forge/bin/cmd/script/cmd.rs | 382 ---------- crates/forge/bin/cmd/script/executor.rs | 325 -------- crates/forge/bin/cmd/script/multi.rs | 240 ------ crates/forge/bin/main.rs | 2 +- crates/forge/bin/opts.rs | 3 +- crates/forge/src/lib.rs | 20 - crates/forge/tests/cli/multi_script.rs | 1 - crates/forge/tests/cli/script.rs | 22 + crates/script/Cargo.toml | 50 ++ .../cmd/script => script/src}/artifacts.rs | 0 crates/script/src/broadcast.rs | 431 +++++++++++ crates/script/src/build.rs | 248 +++++++ crates/script/src/execute.rs | 522 +++++++++++++ .../cmd/script/mod.rs => script/src/lib.rs} | 520 ++++++------- crates/script/src/multi_sequence.rs | 154 ++++ .../cmd/script => script/src}/providers.rs | 0 .../bin/cmd/script => script/src}/receipts.rs | 0 crates/script/src/resume.rs | 106 +++ .../bin/cmd/script => script/src}/runner.rs | 10 +- .../bin/cmd/script => script/src}/sequence.rs | 223 +++--- crates/script/src/simulate.rs | 455 ++++++++++++ .../cmd/script => script/src}/transaction.rs | 15 +- .../bin/cmd/script => script/src}/verify.rs | 40 + 32 files changed, 2421 insertions(+), 2350 deletions(-) delete mode 100644 crates/forge/bin/cmd/script/broadcast.rs delete mode 100644 crates/forge/bin/cmd/script/build.rs delete mode 100644 crates/forge/bin/cmd/script/cmd.rs delete mode 100644 crates/forge/bin/cmd/script/executor.rs delete mode 100644 crates/forge/bin/cmd/script/multi.rs create mode 100644 crates/script/Cargo.toml rename crates/{forge/bin/cmd/script => script/src}/artifacts.rs (100%) create mode 100644 crates/script/src/broadcast.rs create mode 100644 crates/script/src/build.rs create mode 100644 crates/script/src/execute.rs rename crates/{forge/bin/cmd/script/mod.rs => script/src/lib.rs} (62%) create mode 100644 crates/script/src/multi_sequence.rs rename crates/{forge/bin/cmd/script => script/src}/providers.rs (100%) rename crates/{forge/bin/cmd/script => script/src}/receipts.rs (100%) create mode 100644 crates/script/src/resume.rs rename crates/{forge/bin/cmd/script => script/src}/runner.rs (98%) rename crates/{forge/bin/cmd/script => script/src}/sequence.rs (72%) create mode 100644 crates/script/src/simulate.rs rename crates/{forge/bin/cmd/script => script/src}/transaction.rs (98%) rename crates/{forge/bin/cmd/script => script/src}/verify.rs (80%) diff --git a/Cargo.lock b/Cargo.lock index e5ec2d586..8bea54c23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2924,6 +2924,7 @@ dependencies = [ "eyre", "forge-doc", "forge-fmt", + "forge-script", "forge-verify", "foundry-block-explorers", "foundry-cli", @@ -3010,6 +3011,46 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "forge-script" +version = "0.2.0" +dependencies = [ + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-primitives", + "alloy-rpc-types", + "async-recursion", + "clap", + "const-hex", + "dialoguer", + "dunce", + "ethers-core", + "ethers-providers", + "ethers-signers", + "eyre", + "forge-verify", + "foundry-cheatcodes", + "foundry-cli", + "foundry-common", + "foundry-compilers", + "foundry-config", + "foundry-debugger", + "foundry-evm", + "foundry-linking", + "foundry-wallets", + "futures", + "indicatif", + "itertools 0.11.0", + "parking_lot", + "revm-inspectors", + "semver 1.0.22", + "serde", + "serde_json", + "tempfile", + "tracing", + "yansi 0.5.1", +] + [[package]] name = "forge-verify" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index e0c94e746..68240fde2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -119,6 +119,7 @@ forge = { path = "crates/forge" } forge-doc = { path = "crates/doc" } forge-fmt = { path = "crates/fmt" } forge-verify = { path = "crates/verify" } +forge-script = { path = "crates/script" } foundry-cheatcodes = { path = "crates/cheatcodes" } foundry-cheatcodes-spec = { path = "crates/cheatcodes/spec" } foundry-cli = { path = "crates/cli" } diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index a1b251b76..2687b93e3 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -135,23 +135,6 @@ unsafe fn count_different_bytes(a: &[u8], b: &[u8]) -> usize { sum } -/// Flattens the contracts into (`id` -> (`JsonAbi`, `Vec`)) pairs -pub fn flatten_contracts( - contracts: &BTreeMap, - deployed_code: bool, -) -> ContractsByArtifact { - ContractsByArtifact( - contracts - .iter() - .filter_map(|(id, c)| { - let bytecode = - if deployed_code { c.deployed_bytecode.bytes() } else { c.bytecode.bytes() }; - bytecode.cloned().map(|code| (id.clone(), (c.abi.clone(), code.into()))) - }) - .collect(), - ) -} - /// Artifact/Contract identifier can take the following form: /// `:`, the `artifact file name` is the name of the json file of /// the contract's artifact and the contract name is the name of the solidity contract, like diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 6d4e40f6b..0a120f975 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -15,7 +15,11 @@ name = "forge" path = "bin/main.rs" [build-dependencies] -vergen = { workspace = true, default-features = false, features = ["build", "git", "gitcl"] } +vergen = { workspace = true, default-features = false, features = [ + "build", + "git", + "gitcl", +] } [dependencies] # lib @@ -47,6 +51,7 @@ yansi = "0.5" forge-doc.workspace = true forge-fmt.workspace = true forge-verify.workspace = true +forge-script.workspace = true foundry-cli.workspace = true foundry-debugger.workspace = true @@ -94,14 +99,25 @@ globset = "0.4" paste = "1.0" path-slash = "0.2" pretty_assertions.workspace = true -svm = { package = "svm-rs", version = "0.3", default-features = false, features = ["rustls"] } +svm = { package = "svm-rs", version = "0.3", default-features = false, features = [ + "rustls", +] } tempfile = "3" tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] } [features] default = ["rustls"] -rustls = ["foundry-cli/rustls", "foundry-wallets/rustls", "reqwest/rustls-tls", "reqwest/rustls-tls-native-roots"] -openssl = ["foundry-cli/openssl", "reqwest/default-tls", "foundry-wallets/openssl"] +rustls = [ + "foundry-cli/rustls", + "foundry-wallets/rustls", + "reqwest/rustls-tls", + "reqwest/rustls-tls-native-roots", +] +openssl = [ + "foundry-cli/openssl", + "reqwest/default-tls", + "foundry-wallets/openssl", +] asm-keccak = ["alloy-primitives/asm-keccak"] [[bench]] diff --git a/crates/forge/bin/cmd/debug.rs b/crates/forge/bin/cmd/debug.rs index 75f27da53..8fe1d2e32 100644 --- a/crates/forge/bin/cmd/debug.rs +++ b/crates/forge/bin/cmd/debug.rs @@ -1,5 +1,5 @@ -use super::{build::BuildArgs, script::ScriptArgs}; use clap::{Parser, ValueHint}; +use forge_script::ScriptArgs; use forge_verify::retry::RETRY_VERIFY_ON_CREATE; use foundry_cli::opts::CoreBuildArgs; use foundry_common::evm::EvmArgs; @@ -48,7 +48,7 @@ impl DebugArgs { target_contract: self.target_contract, sig: self.sig, gas_estimate_multiplier: 130, - opts: BuildArgs { args: self.opts, ..Default::default() }, + opts: self.opts, evm_opts: self.evm_opts, debug: true, retry: RETRY_VERIFY_ON_CREATE, diff --git a/crates/forge/bin/cmd/init.rs b/crates/forge/bin/cmd/init.rs index 9c8c3fc90..96144dc63 100644 --- a/crates/forge/bin/cmd/init.rs +++ b/crates/forge/bin/cmd/init.rs @@ -164,11 +164,6 @@ impl InitArgs { } } -/// Returns the commit hash of the project if it exists -pub fn get_commit_hash(root: &Path) -> Option { - Git::new(root).commit_hash(true, "HEAD").ok() -} - /// Initialises `root` as a git repository, if it isn't one already. /// /// Creates `.gitignore` and `.github/workflows/test.yml`, if they don't exist already. diff --git a/crates/forge/bin/cmd/mod.rs b/crates/forge/bin/cmd/mod.rs index 1e1a91cbf..b01366aa7 100644 --- a/crates/forge/bin/cmd/mod.rs +++ b/crates/forge/bin/cmd/mod.rs @@ -56,7 +56,6 @@ pub mod inspect; pub mod install; pub mod remappings; pub mod remove; -pub mod script; pub mod selectors; pub mod snapshot; pub mod test; diff --git a/crates/forge/bin/cmd/script/broadcast.rs b/crates/forge/bin/cmd/script/broadcast.rs deleted file mode 100644 index a7ab05633..000000000 --- a/crates/forge/bin/cmd/script/broadcast.rs +++ /dev/null @@ -1,701 +0,0 @@ -use super::{ - multi::MultiChainSequence, providers::ProvidersManager, receipts::clear_pendings, - sequence::ScriptSequence, transaction::TransactionWithMetadata, verify::VerifyBundle, - NestedValue, ScriptArgs, ScriptConfig, ScriptResult, -}; -use alloy_primitives::{utils::format_units, Address, TxHash, U256}; -use ethers_core::types::transaction::eip2718::TypedTransaction; -use ethers_providers::{JsonRpcClient, Middleware, Provider}; -use ethers_signers::Signer; -use eyre::{bail, Context, ContextCompat, Result}; -use forge::{inspectors::cheatcodes::BroadcastableTransactions, traces::CallTraceDecoder}; -use foundry_cli::{ - init_progress, update_progress, - utils::{has_batch_support, has_different_gas_calc}, -}; -use foundry_common::{ - provider::{ - alloy::RpcUrl, - ethers::{estimate_eip1559_fees, try_get_http_provider, RetryProvider}, - }, - shell, - types::{ToAlloy, ToEthers}, - ContractsByArtifact, -}; -use foundry_compilers::{artifacts::Libraries, ArtifactId}; -use foundry_config::Config; -use foundry_wallets::WalletSigner; -use futures::StreamExt; -use std::{ - cmp::min, - collections::{HashMap, HashSet, VecDeque}, - sync::Arc, -}; - -impl ScriptArgs { - /// Sends the transactions which haven't been broadcasted yet. - pub async fn send_transactions( - &self, - deployment_sequence: &mut ScriptSequence, - fork_url: &str, - signers: &HashMap, - ) -> Result<()> { - let provider = Arc::new(try_get_http_provider(fork_url)?); - let already_broadcasted = deployment_sequence.receipts.len(); - - if already_broadcasted < deployment_sequence.transactions.len() { - let required_addresses: HashSet
= deployment_sequence - .typed_transactions() - .skip(already_broadcasted) - .map(|tx| (*tx.from().expect("No sender for onchain transaction!")).to_alloy()) - .collect(); - - let (send_kind, chain) = if self.unlocked { - let chain = provider.get_chainid().await?; - let mut senders = HashSet::from([self - .evm_opts - .sender - .wrap_err("--sender must be set with --unlocked")?]); - // also take all additional senders that where set manually via broadcast - senders.extend( - deployment_sequence - .typed_transactions() - .filter_map(|tx| tx.from().copied().map(|addr| addr.to_alloy())), - ); - (SendTransactionsKind::Unlocked(senders), chain.as_u64()) - } else { - let mut missing_addresses = Vec::new(); - - println!("\n###\nFinding wallets for all the necessary addresses..."); - for addr in &required_addresses { - if !signers.contains_key(addr) { - missing_addresses.push(addr); - } - } - - if !missing_addresses.is_empty() { - let mut error_msg = String::new(); - - // This is an actual used address - if required_addresses.contains(&Config::DEFAULT_SENDER) { - error_msg += "\nYou seem to be using Foundry's default sender. Be sure to set your own --sender.\n"; - } - - eyre::bail!( - "{}No associated wallet for addresses: {:?}. Unlocked wallets: {:?}", - error_msg, - missing_addresses, - signers.keys().collect::>() - ); - } - - let chain = provider.get_chainid().await?.as_u64(); - - (SendTransactionsKind::Raw(signers), chain) - }; - - // We only wait for a transaction receipt before sending the next transaction, if there - // is more than one signer. There would be no way of assuring their order - // otherwise. Or if the chain does not support batched transactions (eg. Arbitrum). - let sequential_broadcast = - send_kind.signers_count() != 1 || self.slow || !has_batch_support(chain); - - // Make a one-time gas price estimation - let (gas_price, eip1559_fees) = { - match deployment_sequence.transactions.front().unwrap().typed_tx() { - TypedTransaction::Eip1559(_) => { - let fees = estimate_eip1559_fees(&provider, Some(chain)) - .await - .wrap_err("Failed to estimate EIP1559 fees. This chain might not support EIP1559, try adding --legacy to your command.")?; - - (None, Some(fees)) - } - _ => (provider.get_gas_price().await.ok(), None), - } - }; - - // Iterate through transactions, matching the `from` field with the associated - // wallet. Then send the transaction. Panics if we find a unknown `from` - let sequence = deployment_sequence - .transactions - .iter() - .skip(already_broadcasted) - .map(|tx_with_metadata| { - let tx = tx_with_metadata.typed_tx(); - let from = (*tx.from().expect("No sender for onchain transaction!")).to_alloy(); - - let kind = send_kind.for_sender(&from)?; - let is_fixed_gas_limit = tx_with_metadata.is_fixed_gas_limit; - - let mut tx = tx.clone(); - - tx.set_chain_id(chain); - - if let Some(gas_price) = self.with_gas_price { - tx.set_gas_price(gas_price.to_ethers()); - } else { - // fill gas price - match tx { - TypedTransaction::Eip1559(ref mut inner) => { - let eip1559_fees = - eip1559_fees.expect("Could not get eip1559 fee estimation."); - if let Some(priority_gas_price) = self.priority_gas_price { - inner.max_priority_fee_per_gas = - Some(priority_gas_price.to_ethers()); - } else { - inner.max_priority_fee_per_gas = Some(eip1559_fees.1); - } - inner.max_fee_per_gas = Some(eip1559_fees.0); - } - _ => { - tx.set_gas_price(gas_price.expect("Could not get gas_price.")); - } - } - } - - Ok((tx, kind, is_fixed_gas_limit)) - }) - .collect::>>()?; - - let pb = init_progress!(deployment_sequence.transactions, "txes"); - - // We send transactions and wait for receipts in batches of 100, since some networks - // cannot handle more than that. - let batch_size = 100; - let mut index = 0; - - for (batch_number, batch) in sequence.chunks(batch_size).map(|f| f.to_vec()).enumerate() - { - let mut pending_transactions = vec![]; - - shell::println(format!( - "##\nSending transactions [{} - {}].", - batch_number * batch_size, - batch_number * batch_size + min(batch_size, batch.len()) - 1 - ))?; - for (tx, kind, is_fixed_gas_limit) in batch.into_iter() { - let tx_hash = self.send_transaction( - provider.clone(), - tx, - kind, - sequential_broadcast, - fork_url, - is_fixed_gas_limit, - ); - - if sequential_broadcast { - let tx_hash = tx_hash.await?; - deployment_sequence.add_pending(index, tx_hash); - - update_progress!(pb, (index + already_broadcasted)); - index += 1; - - clear_pendings(provider.clone(), deployment_sequence, Some(vec![tx_hash])) - .await?; - } else { - pending_transactions.push(tx_hash); - } - } - - if !pending_transactions.is_empty() { - let mut buffer = futures::stream::iter(pending_transactions).buffered(7); - - while let Some(tx_hash) = buffer.next().await { - let tx_hash = tx_hash?; - deployment_sequence.add_pending(index, tx_hash); - - update_progress!(pb, (index + already_broadcasted)); - index += 1; - } - - // Checkpoint save - deployment_sequence.save()?; - - if !sequential_broadcast { - shell::println("##\nWaiting for receipts.")?; - clear_pendings(provider.clone(), deployment_sequence, None).await?; - } - } - - // Checkpoint save - deployment_sequence.save()?; - } - } - - shell::println("\n\n==========================")?; - shell::println("\nONCHAIN EXECUTION COMPLETE & SUCCESSFUL.")?; - - let (total_gas, total_gas_price, total_paid) = deployment_sequence.receipts.iter().fold( - (U256::ZERO, U256::ZERO, U256::ZERO), - |acc, receipt| { - let gas_used = receipt.gas_used.unwrap_or_default().to_alloy(); - let gas_price = receipt.effective_gas_price.unwrap_or_default().to_alloy(); - (acc.0 + gas_used, acc.1 + gas_price, acc.2 + gas_used * gas_price) - }, - ); - let paid = format_units(total_paid, 18).unwrap_or_else(|_| "N/A".to_string()); - let avg_gas_price = - format_units(total_gas_price / U256::from(deployment_sequence.receipts.len()), 9) - .unwrap_or_else(|_| "N/A".to_string()); - shell::println(format!( - "Total Paid: {} ETH ({} gas * avg {} gwei)", - paid.trim_end_matches('0'), - total_gas, - avg_gas_price.trim_end_matches('0').trim_end_matches('.') - ))?; - - Ok(()) - } - - async fn send_transaction( - &self, - provider: Arc, - mut tx: TypedTransaction, - kind: SendTransactionKind<'_>, - sequential_broadcast: bool, - fork_url: &str, - is_fixed_gas_limit: bool, - ) -> Result { - let from = tx.from().expect("no sender"); - - if sequential_broadcast { - let nonce = forge::next_nonce((*from).to_alloy(), fork_url, None) - .await - .map_err(|_| eyre::eyre!("Not able to query the EOA nonce."))?; - - let tx_nonce = tx.nonce().expect("no nonce"); - if let Ok(tx_nonce) = u64::try_from(tx_nonce.to_alloy()) { - if nonce != tx_nonce { - bail!("EOA nonce changed unexpectedly while sending transactions. Expected {tx_nonce} got {nonce} from provider.") - } - } - } - - match kind { - SendTransactionKind::Unlocked(addr) => { - debug!("sending transaction from unlocked account {:?}: {:?}", addr, tx); - - // Chains which use `eth_estimateGas` are being sent sequentially and require their - // gas to be re-estimated right before broadcasting. - if !is_fixed_gas_limit && - (has_different_gas_calc(provider.get_chainid().await?.as_u64()) || - self.skip_simulation) - { - self.estimate_gas(&mut tx, &provider).await?; - } - - // Submit the transaction - let pending = provider.send_transaction(tx, None).await?; - - Ok(pending.tx_hash().to_alloy()) - } - SendTransactionKind::Raw(signer) => self.broadcast(provider, signer, tx).await, - } - } - - /// Executes the created transactions, and if no error has occurred, broadcasts - /// them. - pub async fn handle_broadcastable_transactions( - &self, - mut result: ScriptResult, - libraries: Libraries, - decoder: &CallTraceDecoder, - mut script_config: ScriptConfig, - verify: VerifyBundle, - signers: &HashMap, - ) -> Result<()> { - if let Some(txs) = result.transactions.take() { - script_config.collect_rpcs(&txs); - script_config.check_multi_chain_constraints(&libraries)?; - script_config.check_shanghai_support().await?; - - if !script_config.missing_rpc { - trace!(target: "script", "creating deployments"); - - let mut deployments = self - .create_script_sequences( - txs, - &result, - &mut script_config, - decoder, - &verify.known_contracts, - ) - .await?; - - if script_config.has_multiple_rpcs() { - trace!(target: "script", "broadcasting multi chain deployment"); - - let multi = MultiChainSequence::new( - deployments.clone(), - &self.sig, - script_config.target_contract(), - &script_config.config, - self.broadcast, - )?; - - if self.broadcast { - self.multi_chain_deployment( - multi, - libraries, - &script_config.config, - verify, - signers, - ) - .await?; - } - } else if self.broadcast { - self.single_deployment( - deployments.first_mut().expect("missing deployment"), - script_config, - libraries, - verify, - signers, - ) - .await?; - } - - if !self.broadcast { - shell::println("\nSIMULATION COMPLETE. To broadcast these transactions, add --broadcast and wallet configuration(s) to the previous command. See forge script --help for more.")?; - } - } else { - shell::println("\nIf you wish to simulate on-chain transactions pass a RPC URL.")?; - } - } - Ok(()) - } - - /// Broadcasts a single chain script. - async fn single_deployment( - &self, - deployment_sequence: &mut ScriptSequence, - script_config: ScriptConfig, - libraries: Libraries, - verify: VerifyBundle, - signers: &HashMap, - ) -> Result<()> { - trace!(target: "script", "broadcasting single chain deployment"); - - if self.verify { - deployment_sequence.verify_preflight_check(&script_config.config, &verify)?; - } - - let rpc = script_config.total_rpcs.into_iter().next().expect("exists; qed"); - - deployment_sequence.add_libraries(libraries); - - self.send_transactions(deployment_sequence, &rpc, signers).await?; - - if self.verify { - return deployment_sequence.verify_contracts(&script_config.config, verify).await; - } - Ok(()) - } - - /// Given the collected transactions it creates a list of [`ScriptSequence`]. List length will - /// be higher than 1, if we're dealing with a multi chain deployment. - /// - /// If `--skip-simulation` is not passed, it will make an onchain simulation of the transactions - /// before adding them to [`ScriptSequence`]. - async fn create_script_sequences( - &self, - txs: BroadcastableTransactions, - script_result: &ScriptResult, - script_config: &mut ScriptConfig, - decoder: &CallTraceDecoder, - known_contracts: &ContractsByArtifact, - ) -> Result> { - if !txs.is_empty() { - let gas_filled_txs = self - .fills_transactions_with_gas(txs, script_config, decoder, known_contracts) - .await?; - - let returns = self.get_returns(&*script_config, &script_result.returned)?; - - return self - .bundle_transactions( - gas_filled_txs, - &script_config.target_contract().clone(), - &mut script_config.config, - returns, - ) - .await; - } else if self.broadcast { - eyre::bail!("No onchain transactions generated in script"); - } - - Ok(vec![]) - } - - /// Takes the collected transactions and executes them locally before converting them to - /// [`TransactionWithMetadata`] with the appropriate gas execution estimation. If - /// `--skip-simulation` is passed, then it will skip the execution. - async fn fills_transactions_with_gas( - &self, - txs: BroadcastableTransactions, - script_config: &ScriptConfig, - decoder: &CallTraceDecoder, - known_contracts: &ContractsByArtifact, - ) -> Result> { - let gas_filled_txs = if self.skip_simulation { - shell::println("\nSKIPPING ON CHAIN SIMULATION.")?; - txs.into_iter() - .map(|btx| { - let mut tx = TransactionWithMetadata::from_tx_request(btx.transaction); - tx.rpc = btx.rpc; - tx - }) - .collect() - } else { - self.onchain_simulation( - txs, - script_config, - decoder, - known_contracts, - ) - .await - .wrap_err("\nTransaction failed when running the on-chain simulation. Check the trace above for more information.")? - }; - Ok(gas_filled_txs) - } - - /// Returns all transactions of the [`TransactionWithMetadata`] type in a list of - /// [`ScriptSequence`]. List length will be higher than 1, if we're dealing with a multi - /// chain deployment. - /// - /// Each transaction will be added with the correct transaction type and gas estimation. - async fn bundle_transactions( - &self, - transactions: VecDeque, - target: &ArtifactId, - config: &mut Config, - returns: HashMap, - ) -> Result> { - // User might be using both "in-code" forks and `--fork-url`. - let last_rpc = &transactions.back().expect("exists; qed").rpc; - let is_multi_deployment = transactions.iter().any(|tx| &tx.rpc != last_rpc); - - let mut total_gas_per_rpc: HashMap = HashMap::new(); - - // Batches sequence of transactions from different rpcs. - let mut new_sequence = VecDeque::new(); - let mut manager = ProvidersManager::default(); - let mut deployments = vec![]; - - // Config is used to initialize the sequence chain, so we need to change when handling a new - // sequence. This makes sure we don't lose the original value. - let original_config_chain = config.chain; - - // Peeking is used to check if the next rpc url is different. If so, it creates a - // [`ScriptSequence`] from all the collected transactions up to this point. - let mut txes_iter = transactions.into_iter().peekable(); - - while let Some(mut tx) = txes_iter.next() { - let tx_rpc = match tx.rpc.clone() { - Some(rpc) => rpc, - None => { - let rpc = self.evm_opts.ensure_fork_url()?.clone(); - // Fills the RPC inside the transaction, if missing one. - tx.rpc = Some(rpc.clone()); - rpc - } - }; - - let provider_info = manager.get_or_init_provider(&tx_rpc, self.legacy).await?; - - // Handles chain specific requirements. - tx.change_type(provider_info.is_legacy); - tx.transaction.set_chain_id(provider_info.chain); - - if !self.skip_simulation { - let typed_tx = tx.typed_tx_mut(); - - if has_different_gas_calc(provider_info.chain) { - trace!("estimating with different gas calculation"); - let gas = *typed_tx.gas().expect("gas is set by simulation."); - - // We are trying to show the user an estimation of the total gas usage. - // - // However, some transactions might depend on previous ones. For - // example, tx1 might deploy a contract that tx2 uses. That - // will result in the following `estimate_gas` call to fail, - // since tx1 hasn't been broadcasted yet. - // - // Not exiting here will not be a problem when actually broadcasting, because - // for chains where `has_different_gas_calc` returns true, - // we await each transaction before broadcasting the next - // one. - if let Err(err) = self.estimate_gas(typed_tx, &provider_info.provider).await { - trace!("gas estimation failed: {err}"); - - // Restore gas value, since `estimate_gas` will remove it. - typed_tx.set_gas(gas); - } - } - - let total_gas = total_gas_per_rpc.entry(tx_rpc.clone()).or_insert(U256::ZERO); - *total_gas += (*typed_tx.gas().expect("gas is set")).to_alloy(); - } - - new_sequence.push_back(tx); - // We only create a [`ScriptSequence`] object when we collect all the rpc related - // transactions. - if let Some(next_tx) = txes_iter.peek() { - if next_tx.rpc == Some(tx_rpc) { - continue; - } - } - - config.chain = Some(provider_info.chain.into()); - let sequence = ScriptSequence::new( - new_sequence, - returns.clone(), - &self.sig, - target, - config, - self.broadcast, - is_multi_deployment, - )?; - - deployments.push(sequence); - - new_sequence = VecDeque::new(); - } - - // Restore previous config chain. - config.chain = original_config_chain; - - if !self.skip_simulation { - // Present gas information on a per RPC basis. - for (rpc, total_gas) in total_gas_per_rpc { - let provider_info = manager.get(&rpc).expect("provider is set."); - - // We don't store it in the transactions, since we want the most updated value. - // Right before broadcasting. - let per_gas = if let Some(gas_price) = self.with_gas_price { - gas_price - } else { - provider_info.gas_price()? - }; - - shell::println("\n==========================")?; - shell::println(format!("\nChain {}", provider_info.chain))?; - - shell::println(format!( - "\nEstimated gas price: {} gwei", - format_units(per_gas, 9) - .unwrap_or_else(|_| "[Could not calculate]".to_string()) - .trim_end_matches('0') - .trim_end_matches('.') - ))?; - shell::println(format!("\nEstimated total gas used for script: {total_gas}"))?; - shell::println(format!( - "\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') - ))?; - shell::println("\n==========================")?; - } - } - Ok(deployments) - } - - /// Uses the signer to submit a transaction to the network. If it fails, it tries to retrieve - /// the transaction hash that can be used on a later run with `--resume`. - async fn broadcast( - &self, - provider: Arc, - signer: &WalletSigner, - mut legacy_or_1559: TypedTransaction, - ) -> Result { - debug!("sending transaction: {:?}", legacy_or_1559); - - // Chains which use `eth_estimateGas` are being sent sequentially and require their gas - // to be re-estimated right before broadcasting. - if has_different_gas_calc(signer.chain_id()) || self.skip_simulation { - // if already set, some RPC endpoints might simply return the gas value that is - // already set in the request and omit the estimate altogether, so - // we remove it here - let _ = legacy_or_1559.gas_mut().take(); - - self.estimate_gas(&mut legacy_or_1559, &provider).await?; - } - - // Signing manually so we skip `fill_transaction` and its `eth_createAccessList` - // request. - let signature = signer - .sign_transaction(&legacy_or_1559) - .await - .wrap_err("Failed to sign transaction")?; - - // Submit the raw transaction - let pending = provider.send_raw_transaction(legacy_or_1559.rlp_signed(&signature)).await?; - - Ok(pending.tx_hash().to_alloy()) - } - - async fn estimate_gas(&self, tx: &mut TypedTransaction, provider: &Provider) -> Result<()> - where - T: JsonRpcClient, - { - // if already set, some RPC endpoints might simply return the gas value that is already - // set in the request and omit the estimate altogether, so we remove it here - let _ = tx.gas_mut().take(); - - tx.set_gas( - provider - .estimate_gas(tx, None) - .await - .wrap_err_with(|| format!("Failed to estimate gas for tx: {:?}", tx.sighash()))? * - self.gas_estimate_multiplier / - 100, - ); - Ok(()) - } -} - -/// How to send a single transaction -#[derive(Clone)] -enum SendTransactionKind<'a> { - Unlocked(Address), - Raw(&'a WalletSigner), -} - -/// Represents how to send _all_ transactions -enum SendTransactionsKind<'a> { - /// Send via `eth_sendTransaction` and rely on the `from` address being unlocked. - Unlocked(HashSet
), - /// Send a signed transaction via `eth_sendRawTransaction` - Raw(&'a HashMap), -} - -impl SendTransactionsKind<'_> { - /// Returns the [`SendTransactionKind`] for the given address - /// - /// Returns an error if no matching signer is found or the address is not unlocked - fn for_sender(&self, addr: &Address) -> Result> { - match self { - SendTransactionsKind::Unlocked(unlocked) => { - if !unlocked.contains(addr) { - bail!("Sender address {:?} is not unlocked", addr) - } - Ok(SendTransactionKind::Unlocked(*addr)) - } - SendTransactionsKind::Raw(wallets) => { - if let Some(wallet) = wallets.get(addr) { - Ok(SendTransactionKind::Raw(wallet)) - } else { - bail!("No matching signer for {:?} found", addr) - } - } - } - } - - /// How many signers are set - fn signers_count(&self) -> usize { - match self { - SendTransactionsKind::Unlocked(addr) => addr.len(), - SendTransactionsKind::Raw(signers) => signers.len(), - } - } -} diff --git a/crates/forge/bin/cmd/script/build.rs b/crates/forge/bin/cmd/script/build.rs deleted file mode 100644 index a2bdc1490..000000000 --- a/crates/forge/bin/cmd/script/build.rs +++ /dev/null @@ -1,208 +0,0 @@ -use super::{ScriptArgs, ScriptConfig}; -use alloy_primitives::{Address, Bytes}; -use eyre::{Context, ContextCompat, Result}; -use foundry_cli::utils::get_cached_entry_by_name; -use foundry_common::compile::{self, ContractSources, ProjectCompiler}; -use foundry_compilers::{ - artifacts::{ContractBytecode, ContractBytecodeSome, Libraries}, - cache::SolFilesCache, - contracts::ArtifactContracts, - info::ContractInfo, - ArtifactId, Project, ProjectCompileOutput, -}; -use foundry_linking::{LinkOutput, Linker}; -use std::str::FromStr; - -impl ScriptArgs { - /// Compiles the file or project and the verify metadata. - pub fn compile(&mut self, script_config: &mut ScriptConfig) -> Result { - trace!(target: "script", "compiling script"); - - self.build(script_config) - } - - /// Compiles the file with auto-detection and compiler params. - pub fn build(&mut self, script_config: &mut ScriptConfig) -> Result { - let (project, output) = self.get_project_and_output(script_config)?; - let root = project.root(); - let output = output.with_stripped_file_prefixes(root); - let sources = ContractSources::from_project_output(&output, root)?; - let contracts = output.into_artifacts().collect(); - - let target = self.find_target(&project, &contracts)?.clone(); - script_config.target_contract = Some(target.clone()); - - let libraries = script_config.config.libraries_with_remappings()?; - let linker = Linker::new(project.root(), contracts); - - let (highlevel_known_contracts, libraries, predeploy_libraries) = self.link_script_target( - &linker, - libraries, - script_config.evm_opts.sender, - script_config.sender_nonce, - target.clone(), - )?; - - let contract = highlevel_known_contracts.get(&target).unwrap(); - - Ok(BuildOutput { - project, - linker, - contract: contract.clone(), - highlevel_known_contracts, - libraries, - predeploy_libraries, - sources, - }) - } - - /// Tries to find artifact for the target script contract. - pub fn find_target<'a>( - &self, - project: &Project, - contracts: &'a ArtifactContracts, - ) -> Result<&'a ArtifactId> { - let mut target_fname = dunce::canonicalize(&self.path) - .wrap_err("Couldn't convert contract path to absolute path.")? - .strip_prefix(project.root()) - .wrap_err("Couldn't strip project root from contract path.")? - .to_str() - .wrap_err("Bad path to string.")? - .to_string(); - - let no_target_name = if let Some(target_name) = &self.target_contract { - target_fname = target_fname + ":" + target_name; - false - } else { - true - }; - - let mut target: Option<&ArtifactId> = None; - - for (id, contract) in contracts.iter() { - if no_target_name { - // Match artifact source, and ignore interfaces - if id.source == std::path::Path::new(&target_fname) && - contract.bytecode.as_ref().map_or(false, |b| b.object.bytes_len() > 0) - { - if let Some(target) = target { - // We might have multiple artifacts for the same contract but with different - // solc versions. Their names will have form of {name}.0.X.Y, so we are - // stripping versions off before comparing them. - let target_name = target.name.split('.').next().unwrap(); - let id_name = id.name.split('.').next().unwrap(); - if target_name != id_name { - eyre::bail!("Multiple contracts in the target path. Please specify the contract name with `--tc ContractName`") - } - } - target = Some(id); - } - } else { - let (path, name) = - target_fname.rsplit_once(':').expect("The target specifier is malformed."); - let path = std::path::Path::new(path); - if path == id.source && name == id.name { - target = Some(id); - } - } - } - - target.ok_or_else(|| eyre::eyre!("Could not find target contract: {}", target_fname)) - } - - /// Links script artifact with given libraries or library addresses computed from script sender - /// and nonce. - /// - /// Populates [BuildOutput] with linked target contract, libraries, bytes of libs that need to - /// be predeployed and `highlevel_known_contracts` - set of known fully linked contracts - pub fn link_script_target( - &self, - linker: &Linker, - libraries: Libraries, - sender: Address, - nonce: u64, - target: ArtifactId, - ) -> Result<(ArtifactContracts, Libraries, Vec)> { - let LinkOutput { libs_to_deploy, libraries } = - linker.link_with_nonce_or_address(libraries, sender, nonce, &target)?; - - // Collect all linked contracts with non-empty bytecode - let highlevel_known_contracts = linker - .get_linked_artifacts(&libraries)? - .iter() - .filter_map(|(id, contract)| { - ContractBytecodeSome::try_from(ContractBytecode::from(contract.clone())) - .ok() - .map(|tc| (id.clone(), tc)) - }) - .filter(|(_, tc)| tc.bytecode.object.is_non_empty_bytecode()) - .collect(); - - Ok((highlevel_known_contracts, libraries, libs_to_deploy)) - } - - pub fn get_project_and_output( - &mut self, - script_config: &ScriptConfig, - ) -> Result<(Project, ProjectCompileOutput)> { - let project = script_config.config.project()?; - - let filters = self.opts.skip.clone().unwrap_or_default(); - // We received a valid file path. - // If this file does not exist, `dunce::canonicalize` will - // result in an error and it will be handled below. - if let Ok(target_contract) = dunce::canonicalize(&self.path) { - let output = compile::compile_target_with_filter( - &target_contract, - &project, - self.opts.args.silent, - self.verify, - filters, - )?; - return Ok((project, output)) - } - - if !project.paths.has_input_files() { - eyre::bail!("The project doesn't have any input files. Make sure the `script` directory is configured properly in foundry.toml. Otherwise, provide the path to the file.") - } - - let contract = ContractInfo::from_str(&self.path)?; - self.target_contract = Some(contract.name.clone()); - - // We received `contract_path:contract_name` - if let Some(path) = contract.path { - let path = - dunce::canonicalize(path).wrap_err("Could not canonicalize the target path")?; - let output = compile::compile_target_with_filter( - &path, - &project, - self.opts.args.silent, - self.verify, - filters, - )?; - self.path = path.to_string_lossy().to_string(); - return Ok((project, output)) - } - - // We received `contract_name`, and need to find its file path. - let output = ProjectCompiler::new().compile(&project)?; - let cache = - SolFilesCache::read_joined(&project.paths).wrap_err("Could not open compiler cache")?; - - let (path, _) = get_cached_entry_by_name(&cache, &contract.name) - .wrap_err("Could not find target contract in cache")?; - self.path = path.to_string_lossy().to_string(); - - Ok((project, output)) - } -} - -pub struct BuildOutput { - pub project: Project, - pub contract: ContractBytecodeSome, - pub linker: Linker, - pub highlevel_known_contracts: ArtifactContracts, - pub libraries: Libraries, - pub predeploy_libraries: Vec, - pub sources: ContractSources, -} diff --git a/crates/forge/bin/cmd/script/cmd.rs b/crates/forge/bin/cmd/script/cmd.rs deleted file mode 100644 index f864f29f8..000000000 --- a/crates/forge/bin/cmd/script/cmd.rs +++ /dev/null @@ -1,382 +0,0 @@ -use super::{ - multi::MultiChainSequence, sequence::ScriptSequence, verify::VerifyBundle, ScriptArgs, - ScriptConfig, ScriptResult, -}; -use crate::cmd::script::{build::BuildOutput, receipts}; -use alloy_primitives::{Address, Bytes}; -use ethers_providers::Middleware; -use ethers_signers::Signer; -use eyre::{OptionExt, Result}; -use forge::traces::CallTraceDecoder; -use foundry_cli::utils::LoadConfig; -use foundry_common::{ - contracts::flatten_contracts, provider::ethers::try_get_http_provider, types::ToAlloy, -}; -use foundry_compilers::{ - artifacts::{ContractBytecodeSome, Libraries}, - contracts::ArtifactContracts, -}; -use foundry_debugger::Debugger; -use foundry_evm::inspectors::cheatcodes::{BroadcastableTransaction, ScriptWallets}; -use foundry_linking::Linker; -use foundry_wallets::WalletSigner; -use std::{collections::HashMap, sync::Arc}; - -/// Helper alias type for the collection of data changed due to the new sender. -type NewSenderChanges = (CallTraceDecoder, Libraries, ArtifactContracts); - -impl ScriptArgs { - /// Executes the script - pub async fn run_script(mut self) -> Result<()> { - trace!(target: "script", "executing script command"); - - let (config, evm_opts) = self.load_config_and_evm_opts_emit_warnings()?; - let mut script_config = ScriptConfig { - // dapptools compatibility - sender_nonce: 1, - config, - evm_opts, - debug: self.debug, - ..Default::default() - }; - - if let Some(sender) = self.maybe_load_private_key()? { - script_config.evm_opts.sender = sender; - } - - if let Some(ref fork_url) = script_config.evm_opts.fork_url { - // when forking, override the sender's nonce to the onchain value - script_config.sender_nonce = - forge::next_nonce(script_config.evm_opts.sender, fork_url, None).await? - } else { - // if not forking, then ignore any pre-deployed library addresses - script_config.config.libraries = Default::default(); - } - - let build_output = self.compile(&mut script_config)?; - - let mut verify = VerifyBundle::new( - &build_output.project, - &script_config.config, - flatten_contracts(&build_output.highlevel_known_contracts, false), - self.retry, - self.verifier.clone(), - ); - - let BuildOutput { - contract, - mut highlevel_known_contracts, - predeploy_libraries, - linker, - sources, - mut libraries, - .. - } = build_output; - - // Execute once with default sender. - let sender = script_config.evm_opts.sender; - - let multi_wallet = self.wallets.get_multi_wallet().await?; - let script_wallets = ScriptWallets::new(multi_wallet, self.evm_opts.sender); - - // We need to execute the script even if just resuming, in case we need to collect private - // keys from the execution. - let mut result = self - .execute( - &mut script_config, - contract, - sender, - &predeploy_libraries, - script_wallets.clone(), - ) - .await?; - - if self.resume || (self.verify && !self.broadcast) { - let signers = script_wallets.into_multi_wallet().into_signers()?; - return self.resume_deployment(script_config, linker, libraries, verify, &signers).await; - } - - let known_contracts = flatten_contracts(&highlevel_known_contracts, true); - let mut decoder = self.decode_traces(&script_config, &mut result, &known_contracts)?; - - if self.debug { - let mut debugger = Debugger::builder() - .debug_arenas(result.debug.as_deref().unwrap_or_default()) - .decoder(&decoder) - .sources(sources) - .breakpoints(result.breakpoints.clone()) - .build(); - debugger.try_run()?; - } - - if let Some((new_traces, updated_libraries, updated_contracts)) = self - .maybe_prepare_libraries( - &mut script_config, - linker, - predeploy_libraries, - &mut result, - script_wallets.clone(), - ) - .await? - { - decoder = new_traces; - highlevel_known_contracts = updated_contracts; - libraries = updated_libraries; - } - - if self.json { - self.show_json(&script_config, &result)?; - } else { - self.show_traces(&script_config, &decoder, &mut result).await?; - } - - verify.known_contracts = flatten_contracts(&highlevel_known_contracts, false); - self.check_contract_sizes(&result, &highlevel_known_contracts)?; - - let signers = script_wallets.into_multi_wallet().into_signers()?; - - self.handle_broadcastable_transactions( - result, - libraries, - &decoder, - script_config, - verify, - &signers, - ) - .await - } - - // In case there are libraries to be deployed, it makes sure that these are added to the list of - // broadcastable transactions with the appropriate sender. - async fn maybe_prepare_libraries( - &mut self, - script_config: &mut ScriptConfig, - linker: Linker, - predeploy_libraries: Vec, - result: &mut ScriptResult, - script_wallets: ScriptWallets, - ) -> Result> { - if let Some(new_sender) = self.maybe_new_sender( - &script_config.evm_opts, - result.transactions.as_ref(), - &predeploy_libraries, - )? { - // We have a new sender, so we need to relink all the predeployed libraries. - let (libraries, highlevel_known_contracts) = self - .rerun_with_new_deployer(script_config, new_sender, result, linker, script_wallets) - .await?; - - // redo traces for the new addresses - let new_traces = self.decode_traces( - &*script_config, - result, - &flatten_contracts(&highlevel_known_contracts, true), - )?; - - return Ok(Some((new_traces, libraries, highlevel_known_contracts))); - } - - // Add predeploy libraries to the list of broadcastable transactions. - let mut lib_deploy = self.create_deploy_transactions( - script_config.evm_opts.sender, - script_config.sender_nonce, - &predeploy_libraries, - &script_config.evm_opts.fork_url, - ); - - if let Some(txs) = &mut result.transactions { - for tx in txs.iter() { - lib_deploy.push_back(BroadcastableTransaction { - rpc: tx.rpc.clone(), - transaction: tx.transaction.clone(), - }); - } - *txs = lib_deploy; - } - - Ok(None) - } - - /// Resumes the deployment and/or verification of the script. - async fn resume_deployment( - &mut self, - script_config: ScriptConfig, - linker: Linker, - libraries: Libraries, - verify: VerifyBundle, - signers: &HashMap, - ) -> Result<()> { - if self.multi { - return self - .multi_chain_deployment( - MultiChainSequence::load( - &script_config.config, - &self.sig, - script_config.target_contract(), - )?, - libraries, - &script_config.config, - verify, - signers, - ) - .await; - } - self.resume_single_deployment( - script_config, - linker, - verify, - signers, - ) - .await - .map_err(|err| { - eyre::eyre!("{err}\n\nIf you were trying to resume or verify a multi chain deployment, add `--multi` to your command invocation.") - }) - } - - /// Resumes the deployment and/or verification of a single RPC script. - async fn resume_single_deployment( - &mut self, - script_config: ScriptConfig, - linker: Linker, - mut verify: VerifyBundle, - signers: &HashMap, - ) -> Result<()> { - trace!(target: "script", "resuming single deployment"); - - let fork_url = script_config - .evm_opts - .fork_url - .as_deref() - .ok_or_else(|| eyre::eyre!("Missing `--fork-url` field."))?; - let provider = Arc::new(try_get_http_provider(fork_url)?); - - let chain = provider.get_chainid().await?.as_u64(); - verify.set_chain(&script_config.config, chain.into()); - - let broadcasted = self.broadcast || self.resume; - let mut deployment_sequence = match ScriptSequence::load( - &script_config.config, - &self.sig, - script_config.target_contract(), - chain, - broadcasted, - ) { - Ok(seq) => seq, - // If the script was simulated, but there was no attempt to broadcast yet, - // try to read the script sequence from the `dry-run/` folder - Err(_) if broadcasted => ScriptSequence::load( - &script_config.config, - &self.sig, - script_config.target_contract(), - chain, - false, - )?, - Err(err) => eyre::bail!(err), - }; - - if self.verify { - deployment_sequence.verify_preflight_check(&script_config.config, &verify)?; - } - - receipts::wait_for_pending(provider, &mut deployment_sequence).await?; - - if self.resume { - self.send_transactions(&mut deployment_sequence, fork_url, signers).await?; - } - - if self.verify { - let target = script_config.target_contract(); - let libraries = Libraries::parse(&deployment_sequence.libraries)? - .with_stripped_file_prefixes(linker.root.as_path()); - // We might have predeployed libraries from the broadcasting, so we need to - // relink the contracts with them, since their mapping is - // not included in the solc cache files. - let (highlevel_known_contracts, _, predeploy_libraries) = self.link_script_target( - &linker, - libraries, - script_config.config.sender, // irrelevant, since we're not creating any - 0, // irrelevant, since we're not creating any - target.clone(), - )?; - - if !predeploy_libraries.is_empty() { - eyre::bail!("Incomplete set of libraries in deployment artifact."); - } - - verify.known_contracts = flatten_contracts(&highlevel_known_contracts, false); - - deployment_sequence.verify_contracts(&script_config.config, verify).await?; - } - - Ok(()) - } - - /// Reruns the execution with a new sender and relinks the libraries accordingly - async fn rerun_with_new_deployer( - &mut self, - script_config: &mut ScriptConfig, - new_sender: Address, - first_run_result: &mut ScriptResult, - linker: Linker, - script_wallets: ScriptWallets, - ) -> Result<(Libraries, ArtifactContracts)> { - // if we had a new sender that requires relinking, we need to - // get the nonce mainnet for accurate addresses for predeploy libs - let nonce = forge::next_nonce( - new_sender, - script_config.evm_opts.fork_url.as_ref().ok_or_else(|| { - eyre::eyre!("You must provide an RPC URL (see --fork-url) when broadcasting.") - })?, - None, - ) - .await?; - script_config.sender_nonce = nonce; - let target = script_config.target_contract(); - - let libraries = script_config.config.libraries_with_remappings()?; - - let (highlevel_known_contracts, libraries, predeploy_libraries) = - self.link_script_target(&linker, libraries, new_sender, nonce, target.clone())?; - - let contract = highlevel_known_contracts - .get(target) - .ok_or_eyre("target not found in linked artifacts")? - .clone(); - - let mut txs = self.create_deploy_transactions( - new_sender, - nonce, - &predeploy_libraries, - &script_config.evm_opts.fork_url, - ); - - let result = self - .execute(script_config, contract, new_sender, &predeploy_libraries, script_wallets) - .await?; - - if let Some(new_txs) = &result.transactions { - for new_tx in new_txs.iter() { - txs.push_back(BroadcastableTransaction { - rpc: new_tx.rpc.clone(), - transaction: new_tx.transaction.clone(), - }); - } - } - - *first_run_result = result; - first_run_result.transactions = Some(txs); - - Ok((libraries, highlevel_known_contracts)) - } - - /// In case the user has loaded *only* one private-key, we can assume that he's using it as the - /// `--sender` - fn maybe_load_private_key(&mut self) -> Result> { - let maybe_sender = self - .wallets - .private_keys()? - .filter(|pks| pks.len() == 1) - .map(|pks| pks.first().unwrap().address().to_alloy()); - Ok(maybe_sender) - } -} diff --git a/crates/forge/bin/cmd/script/executor.rs b/crates/forge/bin/cmd/script/executor.rs deleted file mode 100644 index a78f5b9f4..000000000 --- a/crates/forge/bin/cmd/script/executor.rs +++ /dev/null @@ -1,325 +0,0 @@ -use super::{ - artifacts::ArtifactInfo, - runner::{ScriptRunner, SimulationStage}, - transaction::{AdditionalContract, TransactionWithMetadata}, - ScriptArgs, ScriptConfig, ScriptResult, -}; -use alloy_primitives::{Address, Bytes, U256}; -use eyre::{Context, Result}; -use forge::{ - backend::Backend, - executors::ExecutorBuilder, - inspectors::{cheatcodes::BroadcastableTransactions, CheatsConfig}, - traces::{render_trace_arena, CallTraceDecoder}, -}; -use foundry_cli::utils::{ensure_clean_constructor, needs_setup}; -use foundry_common::{get_contract_name, provider::ethers::RpcUrl, shell, ContractsByArtifact}; -use foundry_compilers::artifacts::ContractBytecodeSome; -use foundry_evm::inspectors::cheatcodes::ScriptWallets; -use futures::future::join_all; -use parking_lot::RwLock; -use std::{ - collections::{BTreeMap, HashMap, VecDeque}, - sync::Arc, -}; - -impl ScriptArgs { - /// Locally deploys and executes the contract method that will collect all broadcastable - /// transactions. - pub async fn execute( - &self, - script_config: &mut ScriptConfig, - contract: ContractBytecodeSome, - sender: Address, - predeploy_libraries: &[Bytes], - script_wallets: ScriptWallets, - ) -> Result { - trace!(target: "script", "start executing script"); - - let ContractBytecodeSome { abi, bytecode, .. } = contract; - - let bytecode = bytecode.into_bytes().ok_or_else(|| { - eyre::eyre!("expected fully linked bytecode, found unlinked bytecode") - })?; - - ensure_clean_constructor(&abi)?; - - let mut runner = self - .prepare_runner(script_config, sender, SimulationStage::Local, Some(script_wallets)) - .await?; - let (address, mut result) = runner.setup( - predeploy_libraries, - bytecode, - needs_setup(&abi), - script_config.sender_nonce, - self.broadcast, - script_config.evm_opts.fork_url.is_none(), - )?; - - let (func, calldata) = self.get_method_and_calldata(&abi)?; - script_config.called_function = Some(func); - - // Only call the method if `setUp()` succeeded. - if result.success { - let script_result = runner.script(address, calldata)?; - - result.success &= script_result.success; - result.gas_used = script_result.gas_used; - result.logs.extend(script_result.logs); - result.traces.extend(script_result.traces); - result.debug = script_result.debug; - result.labeled_addresses.extend(script_result.labeled_addresses); - result.returned = script_result.returned; - result.breakpoints = script_result.breakpoints; - - match (&mut result.transactions, script_result.transactions) { - (Some(txs), Some(new_txs)) => { - txs.extend(new_txs); - } - (None, Some(new_txs)) => { - result.transactions = Some(new_txs); - } - _ => {} - } - } - - Ok(result) - } - - /// Simulates onchain state by executing a list of transactions locally and persisting their - /// state. Returns the transactions and any CREATE2 contract address created. - pub async fn onchain_simulation( - &self, - transactions: BroadcastableTransactions, - script_config: &ScriptConfig, - decoder: &CallTraceDecoder, - contracts: &ContractsByArtifact, - ) -> Result> { - trace!(target: "script", "executing onchain simulation"); - - let runners = Arc::new( - self.build_runners(script_config) - .await? - .into_iter() - .map(|(rpc, runner)| (rpc, Arc::new(RwLock::new(runner)))) - .collect::>(), - ); - - if script_config.evm_opts.verbosity > 3 { - println!("=========================="); - println!("Simulated On-chain Traces:\n"); - } - - let address_to_abi: BTreeMap = decoder - .contracts - .iter() - .filter_map(|(addr, contract_id)| { - let contract_name = get_contract_name(contract_id); - if let Ok(Some((_, (abi, code)))) = - contracts.find_by_name_or_identifier(contract_name) - { - let info = ArtifactInfo { - contract_name: contract_name.to_string(), - contract_id: contract_id.to_string(), - abi, - code, - }; - return Some((*addr, info)); - } - None - }) - .collect(); - - let mut final_txs = VecDeque::new(); - - // Executes all transactions from the different forks concurrently. - let futs = transactions - .into_iter() - .map(|transaction| async { - let rpc = transaction.rpc.as_ref().expect("missing broadcastable tx rpc url"); - let mut runner = runners.get(rpc).expect("invalid rpc url").write(); - - let mut tx = transaction.transaction; - let result = runner - .simulate( - tx.from - .expect("transaction doesn't have a `from` address at execution time"), - tx.to, - tx.input.clone().into_input(), - tx.value, - ) - .wrap_err("Internal EVM error during simulation")?; - - if !result.success || result.traces.is_empty() { - return Ok((None, result.traces)); - } - - let created_contracts = result - .traces - .iter() - .flat_map(|(_, traces)| { - traces.nodes().iter().filter_map(|node| { - if node.trace.kind.is_any_create() { - return Some(AdditionalContract { - opcode: node.trace.kind, - address: node.trace.address, - init_code: node.trace.data.clone(), - }); - } - None - }) - }) - .collect(); - - // Simulate mining the transaction if the user passes `--slow`. - if self.slow { - runner.executor.env.block.number += U256::from(1); - } - - let is_fixed_gas_limit = tx.gas.is_some(); - match tx.gas { - // If tx.gas is already set that means it was specified in script - Some(gas) => { - println!("Gas limit was set in script to {gas}"); - } - // We inflate the gas used by the user specified percentage - None => { - let gas = U256::from(result.gas_used * self.gas_estimate_multiplier / 100); - tx.gas = Some(gas); - } - } - - let tx = TransactionWithMetadata::new( - tx, - transaction.rpc, - &result, - &address_to_abi, - decoder, - created_contracts, - is_fixed_gas_limit, - )?; - - eyre::Ok((Some(tx), result.traces)) - }) - .collect::>(); - - let mut abort = false; - for res in join_all(futs).await { - let (tx, traces) = res?; - - // Transaction will be `None`, if execution didn't pass. - if tx.is_none() || script_config.evm_opts.verbosity > 3 { - // Identify all contracts created during the call. - if traces.is_empty() { - eyre::bail!( - "forge script requires tracing enabled to collect created contracts" - ); - } - - for (_, trace) in &traces { - println!("{}", render_trace_arena(trace, decoder).await?); - } - } - - if let Some(tx) = tx { - final_txs.push_back(tx); - } else { - abort = true; - } - } - - if abort { - eyre::bail!("Simulated execution failed.") - } - - Ok(final_txs) - } - - /// Build the multiple runners from different forks. - async fn build_runners( - &self, - script_config: &ScriptConfig, - ) -> Result> { - let sender = script_config.evm_opts.sender; - - if !shell::verbosity().is_silent() { - let n = script_config.total_rpcs.len(); - let s = if n != 1 { "s" } else { "" }; - println!("\n## Setting up {n} EVM{s}."); - } - - let futs = script_config - .total_rpcs - .iter() - .map(|rpc| async { - let mut script_config = script_config.clone(); - script_config.evm_opts.fork_url = Some(rpc.clone()); - let runner = self - .prepare_runner(&mut script_config, sender, SimulationStage::OnChain, None) - .await?; - Ok((rpc.clone(), runner)) - }) - .collect::>(); - - join_all(futs).await.into_iter().collect() - } - - /// Creates the Runner that drives script execution - async fn prepare_runner( - &self, - script_config: &mut ScriptConfig, - sender: Address, - stage: SimulationStage, - script_wallets: Option, - ) -> Result { - trace!("preparing script runner"); - let env = script_config.evm_opts.evm_env().await?; - - // The db backend that serves all the data. - let db = match &script_config.evm_opts.fork_url { - Some(url) => match script_config.backends.get(url) { - Some(db) => db.clone(), - None => { - let fork = script_config.evm_opts.get_fork(&script_config.config, env.clone()); - let backend = Backend::spawn(fork); - script_config.backends.insert(url.clone(), backend.clone()); - backend - } - }, - None => { - // It's only really `None`, when we don't pass any `--fork-url`. And if so, there is - // no need to cache it, since there won't be any onchain simulation that we'd need - // to cache the backend for. - Backend::spawn(script_config.evm_opts.get_fork(&script_config.config, env.clone())) - } - }; - - // We need to enable tracing to decode contract names: local or external. - let mut builder = ExecutorBuilder::new() - .inspectors(|stack| stack.trace(true)) - .spec(script_config.config.evm_spec_id()) - .gas_limit(script_config.evm_opts.gas_limit()); - - if let SimulationStage::Local = stage { - builder = builder.inspectors(|stack| { - stack - .debug(self.debug) - .cheatcodes( - CheatsConfig::new( - &script_config.config, - script_config.evm_opts.clone(), - script_wallets, - ) - .into(), - ) - .enable_isolation(script_config.evm_opts.isolate) - }); - } - - Ok(ScriptRunner::new( - builder.build(env, db), - script_config.evm_opts.initial_balance, - sender, - )) - } -} diff --git a/crates/forge/bin/cmd/script/multi.rs b/crates/forge/bin/cmd/script/multi.rs deleted file mode 100644 index 874dd24ba..000000000 --- a/crates/forge/bin/cmd/script/multi.rs +++ /dev/null @@ -1,240 +0,0 @@ -use super::{ - receipts, - sequence::{sig_to_file_name, ScriptSequence, SensitiveScriptSequence, DRY_RUN_DIR}, - verify::VerifyBundle, - ScriptArgs, -}; -use alloy_primitives::Address; -use eyre::{ContextCompat, Report, Result, WrapErr}; -use foundry_cli::utils::now; -use foundry_common::{fs, provider::ethers::get_http_provider}; -use foundry_compilers::{artifacts::Libraries, ArtifactId}; -use foundry_config::Config; -use foundry_wallets::WalletSigner; -use futures::future::join_all; -use serde::{Deserialize, Serialize}; -use std::{ - collections::HashMap, - io::{BufWriter, Write}, - path::{Path, PathBuf}, - sync::Arc, -}; - -/// Holds the sequences of multiple chain deployments. -#[derive(Clone, Default, Serialize, Deserialize)] -pub struct MultiChainSequence { - pub deployments: Vec, - #[serde(skip)] - pub path: PathBuf, - #[serde(skip)] - pub sensitive_path: PathBuf, - pub timestamp: u64, -} - -/// Sensitive values from script sequences. -#[derive(Clone, Default, Serialize, Deserialize)] -pub struct SensitiveMultiChainSequence { - pub deployments: Vec, -} - -fn to_sensitive(sequence: &mut MultiChainSequence) -> SensitiveMultiChainSequence { - SensitiveMultiChainSequence { - deployments: sequence.deployments.iter_mut().map(|sequence| sequence.into()).collect(), - } -} - -impl Drop for MultiChainSequence { - fn drop(&mut self) { - self.deployments.iter_mut().for_each(|sequence| sequence.sort_receipts()); - self.save().expect("could not save multi deployment sequence"); - } -} - -impl MultiChainSequence { - pub fn new( - deployments: Vec, - sig: &str, - target: &ArtifactId, - config: &Config, - broadcasted: bool, - ) -> Result { - let (path, sensitive_path) = MultiChainSequence::get_paths( - &config.broadcast, - &config.cache_path, - sig, - target, - broadcasted, - )?; - - Ok(MultiChainSequence { deployments, path, sensitive_path, timestamp: now().as_secs() }) - } - - /// Gets paths in the formats - /// ./broadcast/multi/contract_filename[-timestamp]/sig.json and - /// ./cache/multi/contract_filename[-timestamp]/sig.json - pub fn get_paths( - broadcast: &Path, - cache: &Path, - sig: &str, - target: &ArtifactId, - broadcasted: bool, - ) -> Result<(PathBuf, PathBuf)> { - let mut broadcast = broadcast.to_path_buf(); - let mut cache = cache.to_path_buf(); - let mut common = PathBuf::new(); - - common.push("multi"); - - if !broadcasted { - common.push(DRY_RUN_DIR); - } - - let target_fname = target - .source - .file_name() - .wrap_err_with(|| format!("No filename for {:?}", target.source))? - .to_string_lossy(); - - common.push(format!("{target_fname}-latest")); - - broadcast.push(common.clone()); - cache.push(common); - - fs::create_dir_all(&broadcast)?; - fs::create_dir_all(&cache)?; - - let filename = format!("{}.json", sig_to_file_name(sig)); - - broadcast.push(filename.clone()); - cache.push(filename); - - Ok((broadcast, cache)) - } - - /// Loads the sequences for the multi chain deployment. - pub fn load(config: &Config, sig: &str, target: &ArtifactId) -> Result { - let (path, sensitive_path) = MultiChainSequence::get_paths( - &config.broadcast, - &config.cache_path, - sig, - target, - true, - )?; - let mut sequence: MultiChainSequence = foundry_compilers::utils::read_json_file(&path) - .wrap_err("Multi-chain deployment not found.")?; - let sensitive_sequence: SensitiveMultiChainSequence = - foundry_compilers::utils::read_json_file(&sensitive_path) - .wrap_err("Multi-chain deployment sensitive details not found.")?; - - sequence.deployments.iter_mut().enumerate().for_each(|(i, sequence)| { - sequence.fill_sensitive(&sensitive_sequence.deployments[i]); - }); - - sequence.path = path; - sequence.sensitive_path = sensitive_path; - - Ok(sequence) - } - - /// Saves the transactions as file if it's a standalone deployment. - pub fn save(&mut self) -> Result<()> { - self.timestamp = now().as_secs(); - - let sensitive_sequence: SensitiveMultiChainSequence = to_sensitive(self); - - // broadcast writes - //../Contract-latest/run.json - let mut writer = BufWriter::new(fs::create_file(&self.path)?); - serde_json::to_writer_pretty(&mut writer, &self)?; - writer.flush()?; - - //../Contract-[timestamp]/run.json - let path = self.path.to_string_lossy(); - let file = PathBuf::from(&path.replace("-latest", &format!("-{}", self.timestamp))); - fs::create_dir_all(file.parent().unwrap())?; - fs::copy(&self.path, &file)?; - - // cache writes - //../Contract-latest/run.json - let mut writer = BufWriter::new(fs::create_file(&self.sensitive_path)?); - serde_json::to_writer_pretty(&mut writer, &sensitive_sequence)?; - writer.flush()?; - - //../Contract-[timestamp]/run.json - let path = self.sensitive_path.to_string_lossy(); - let file = PathBuf::from(&path.replace("-latest", &format!("-{}", self.timestamp))); - fs::create_dir_all(file.parent().unwrap())?; - fs::copy(&self.sensitive_path, &file)?; - - println!("\nTransactions saved to: {}\n", self.path.display()); - println!("Sensitive details saved to: {}\n", self.sensitive_path.display()); - - Ok(()) - } -} - -impl ScriptArgs { - /// Given a [`MultiChainSequence`] with multiple sequences of different chains, it executes them - /// all in parallel. Supports `--resume` and `--verify`. - pub async fn multi_chain_deployment( - &self, - mut deployments: MultiChainSequence, - libraries: Libraries, - config: &Config, - verify: VerifyBundle, - signers: &HashMap, - ) -> Result<()> { - if !libraries.is_empty() { - eyre::bail!("Libraries are currently not supported on multi deployment setups."); - } - - if self.verify { - for sequence in &deployments.deployments { - sequence.verify_preflight_check(config, &verify)?; - } - } - - if self.resume { - trace!(target: "script", "resuming multi chain deployment"); - - let futs = deployments - .deployments - .iter_mut() - .map(|sequence| async move { - let rpc_url = sequence.rpc_url().unwrap(); - let provider = Arc::new(get_http_provider(rpc_url)); - receipts::wait_for_pending(provider, sequence).await - }) - .collect::>(); - - let errors = - join_all(futs).await.into_iter().filter(|res| res.is_err()).collect::>(); - - if !errors.is_empty() { - return Err(eyre::eyre!("{errors:?}")); - } - } - - trace!(target: "script", "broadcasting multi chain deployments"); - - let mut results: Vec> = Vec::new(); - - for sequence in deployments.deployments.iter_mut() { - let rpc_url = sequence.rpc_url().unwrap().to_string(); - let result = match self.send_transactions(sequence, &rpc_url, signers).await { - Ok(_) if self.verify => sequence.verify_contracts(config, verify.clone()).await, - Ok(_) => Ok(()), - Err(err) => Err(err), - }; - results.push(result); - } - - let errors = results.into_iter().filter(|res| res.is_err()).collect::>(); - - if !errors.is_empty() { - return Err(eyre::eyre!("{errors:?}")); - } - - Ok(()) - } -} diff --git a/crates/forge/bin/main.rs b/crates/forge/bin/main.rs index 5fdc7c408..0743d69e6 100644 --- a/crates/forge/bin/main.rs +++ b/crates/forge/bin/main.rs @@ -31,7 +31,7 @@ fn main() -> Result<()> { ForgeSubcommand::Script(cmd) => { // install the shell before executing the command foundry_common::shell::set_shell(foundry_common::shell::Shell::from_args( - cmd.opts.args.silent, + cmd.opts.silent, cmd.json, ))?; utils::block_on(cmd.run_script()) diff --git a/crates/forge/bin/opts.rs b/crates/forge/bin/opts.rs index 5e5cfda7c..03ed4d551 100644 --- a/crates/forge/bin/opts.rs +++ b/crates/forge/bin/opts.rs @@ -2,9 +2,10 @@ use crate::cmd::{ bind::BindArgs, build::BuildArgs, cache::CacheArgs, config, coverage, create::CreateArgs, debug::DebugArgs, doc::DocArgs, flatten, fmt::FmtArgs, geiger, generate, init::InitArgs, inspect, install::InstallArgs, remappings::RemappingArgs, remove::RemoveArgs, - script::ScriptArgs, selectors::SelectorsSubcommands, snapshot, test, tree, update, + selectors::SelectorsSubcommands, snapshot, test, tree, update, }; use clap::{Parser, Subcommand, ValueHint}; +use forge_script::ScriptArgs; use forge_verify::{VerifyArgs, VerifyCheckArgs}; use std::path::PathBuf; diff --git a/crates/forge/src/lib.rs b/crates/forge/src/lib.rs index 39854abac..f6b9a54a8 100644 --- a/crates/forge/src/lib.rs +++ b/crates/forge/src/lib.rs @@ -207,23 +207,3 @@ impl TestOptionsBuilder { TestOptions::new(output, root, profiles, base_fuzz, base_invariant) } } - -mod utils2 { - use alloy_primitives::Address; - use ethers_core::types::BlockId; - use ethers_providers::{Middleware, Provider}; - use eyre::Context; - use foundry_common::types::{ToAlloy, ToEthers}; - - pub async fn next_nonce( - caller: Address, - provider_url: &str, - block: Option, - ) -> eyre::Result { - let provider = Provider::try_from(provider_url) - .wrap_err_with(|| format!("bad fork_url provider: {provider_url}"))?; - let res = provider.get_transaction_count(caller.to_ethers(), block).await?.to_alloy(); - res.try_into().map_err(Into::into) - } -} -pub use utils2::*; diff --git a/crates/forge/tests/cli/multi_script.rs b/crates/forge/tests/cli/multi_script.rs index d6f7628da..121fa9862 100644 --- a/crates/forge/tests/cli/multi_script.rs +++ b/crates/forge/tests/cli/multi_script.rs @@ -61,6 +61,5 @@ forgetest_async!(can_resume_multi_chain_script, |prj, cmd| { .broadcast(ScriptOutcome::MissingWallet) .load_private_keys(&[0, 1]) .await - .arg("--multi") .resume(ScriptOutcome::OkBroadcast); }); diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index b45ce52cf..e212683c1 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -1080,6 +1080,28 @@ interface Interface {} assert!(cmd.stdout_lossy().contains("Script ran successfully.")); }); +forgetest_async!(assert_can_detect_unlinked_target_with_libraries, |prj, cmd| { + let script = prj + .add_script( + "ScriptWithExtLib.s.sol", + r#" +library Lib { + function f() public {} +} + +contract Script { + function run() external { + Lib.f(); + } +} + "#, + ) + .unwrap(); + + cmd.arg("script").arg(script); + assert!(cmd.stdout_lossy().contains("Script ran successfully.")); +}); + forgetest_async!(assert_can_resume_with_additional_contracts, |prj, cmd| { let (_api, handle) = spawn(NodeConfig::test()).await; let mut tester = ScriptTester::new_broadcast(cmd, &handle.http_endpoint(), prj.root()); diff --git a/crates/script/Cargo.toml b/crates/script/Cargo.toml new file mode 100644 index 000000000..5ae574822 --- /dev/null +++ b/crates/script/Cargo.toml @@ -0,0 +1,50 @@ +[package] +name = "forge-script" +description = "Solidity scripting" + +version.workspace = true +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +forge-verify.workspace = true +foundry-cli.workspace = true +foundry-config.workspace = true +foundry-common.workspace = true +foundry-evm.workspace = true +foundry-debugger.workspace = true +foundry-cheatcodes.workspace = true +foundry-wallets.workspace = true +foundry-linking.workspace = true + +hex.workspace = true +serde.workspace = true +eyre.workspace = true +serde_json.workspace = true +dunce = "1" +foundry-compilers = { workspace = true, features = ["full"] } +tracing.workspace = true +clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } +semver = "1" +futures = "0.3" +async-recursion = "1.0.5" +alloy-primitives.workspace = true +alloy-dyn-abi.workspace = true +itertools.workspace = true +parking_lot = "0.12" +yansi = "0.5" +ethers-core.workspace = true +ethers-providers.workspace = true +ethers-signers.workspace = true +revm-inspectors.workspace = true +alloy-rpc-types.workspace = true +alloy-json-abi.workspace = true +dialoguer = { version = "0.11", default-features = false } +indicatif = "0.17" + +[dev-dependencies] +tempfile = "3" \ No newline at end of file diff --git a/crates/forge/bin/cmd/script/artifacts.rs b/crates/script/src/artifacts.rs similarity index 100% rename from crates/forge/bin/cmd/script/artifacts.rs rename to crates/script/src/artifacts.rs diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs new file mode 100644 index 000000000..fb21276c2 --- /dev/null +++ b/crates/script/src/broadcast.rs @@ -0,0 +1,431 @@ +use crate::{ + build::LinkedBuildData, + execute::{ExecutionArtifacts, ExecutionData}, + sequence::ScriptSequenceKind, + verify::BroadcastedState, + ScriptArgs, ScriptConfig, +}; + +use super::receipts; +use alloy_primitives::{utils::format_units, Address, TxHash, U256}; +use ethers_core::types::{transaction::eip2718::TypedTransaction, BlockId}; +use ethers_providers::{JsonRpcClient, Middleware, Provider}; +use ethers_signers::Signer; +use eyre::{bail, Context, Result}; +use forge_verify::provider::VerificationProviderType; +use foundry_cheatcodes::ScriptWallets; +use foundry_cli::{ + init_progress, update_progress, + utils::{has_batch_support, has_different_gas_calc}, +}; +use foundry_common::{ + provider::ethers::{ + estimate_eip1559_fees, get_http_provider, try_get_http_provider, RetryProvider, + }, + shell, + types::{ToAlloy, ToEthers}, +}; +use foundry_config::Config; +use foundry_wallets::WalletSigner; +use futures::{future::join_all, StreamExt}; +use itertools::Itertools; +use std::{ + collections::{HashMap, HashSet}, + sync::Arc, +}; + +pub async fn estimate_gas( + tx: &mut TypedTransaction, + provider: &Provider, + estimate_multiplier: u64, +) -> Result<()> +where + T: JsonRpcClient, +{ + // if already set, some RPC endpoints might simply return the gas value that is already + // set in the request and omit the estimate altogether, so we remove it here + let _ = tx.gas_mut().take(); + + tx.set_gas( + provider + .estimate_gas(tx, None) + .await + .wrap_err_with(|| format!("Failed to estimate gas for tx: {:?}", tx.sighash()))? * + estimate_multiplier / + 100, + ); + Ok(()) +} + +pub async fn next_nonce( + caller: Address, + provider_url: &str, + block: Option, +) -> eyre::Result { + let provider = Provider::try_from(provider_url) + .wrap_err_with(|| format!("bad fork_url provider: {provider_url}"))?; + let res = provider.get_transaction_count(caller.to_ethers(), block).await?.to_alloy(); + res.try_into().map_err(Into::into) +} + +pub async fn send_transaction( + provider: Arc, + mut tx: TypedTransaction, + kind: SendTransactionKind<'_>, + sequential_broadcast: bool, + is_fixed_gas_limit: bool, + estimate_via_rpc: bool, + estimate_multiplier: u64, +) -> Result { + let from = tx.from().expect("no sender"); + + if sequential_broadcast { + let nonce = provider.get_transaction_count(*from, None).await?; + + let tx_nonce = tx.nonce().expect("no nonce"); + if nonce != *tx_nonce { + bail!("EOA nonce changed unexpectedly while sending transactions. Expected {tx_nonce} got {nonce} from provider.") + } + } + + // Chains which use `eth_estimateGas` are being sent sequentially and require their + // gas to be re-estimated right before broadcasting. + if !is_fixed_gas_limit && estimate_via_rpc { + estimate_gas(&mut tx, &provider, estimate_multiplier).await?; + } + + let pending = match kind { + SendTransactionKind::Unlocked(addr) => { + debug!("sending transaction from unlocked account {:?}: {:?}", addr, tx); + + // Submit the transaction + provider.send_transaction(tx, None).await? + } + SendTransactionKind::Raw(signer) => { + debug!("sending transaction: {:?}", tx); + + // Signing manually so we skip `fill_transaction` and its `eth_createAccessList` + // request. + let signature = + signer.sign_transaction(&tx).await.wrap_err("Failed to sign transaction")?; + + // Submit the raw transaction + provider.send_raw_transaction(tx.rlp_signed(&signature)).await? + } + }; + + Ok(pending.tx_hash().to_alloy()) +} + +/// How to send a single transaction +#[derive(Clone)] +pub enum SendTransactionKind<'a> { + Unlocked(Address), + Raw(&'a WalletSigner), +} + +/// Represents how to send _all_ transactions +pub enum SendTransactionsKind { + /// Send via `eth_sendTransaction` and rely on the `from` address being unlocked. + Unlocked(HashSet
), + /// Send a signed transaction via `eth_sendRawTransaction` + Raw(HashMap), +} + +impl SendTransactionsKind { + /// Returns the [`SendTransactionKind`] for the given address + /// + /// Returns an error if no matching signer is found or the address is not unlocked + pub fn for_sender(&self, addr: &Address) -> Result> { + match self { + SendTransactionsKind::Unlocked(unlocked) => { + if !unlocked.contains(addr) { + bail!("Sender address {:?} is not unlocked", addr) + } + Ok(SendTransactionKind::Unlocked(*addr)) + } + SendTransactionsKind::Raw(wallets) => { + if let Some(wallet) = wallets.get(addr) { + Ok(SendTransactionKind::Raw(wallet)) + } else { + bail!("No matching signer for {:?} found", addr) + } + } + } + } + + /// How many signers are set + pub fn signers_count(&self) -> usize { + match self { + SendTransactionsKind::Unlocked(addr) => addr.len(), + SendTransactionsKind::Raw(signers) => signers.len(), + } + } +} + +/// State after we have bundled all [TransactionWithMetadata] objects into a single +/// [ScriptSequenceKind] object containing one or more script sequences. +pub struct BundledState { + pub args: ScriptArgs, + pub script_config: ScriptConfig, + pub script_wallets: ScriptWallets, + pub build_data: LinkedBuildData, + pub execution_data: ExecutionData, + pub execution_artifacts: ExecutionArtifacts, + pub sequence: ScriptSequenceKind, +} + +impl BundledState { + pub async fn wait_for_pending(mut self) -> Result { + let futs = self + .sequence + .sequences_mut() + .iter_mut() + .map(|sequence| async move { + let rpc_url = sequence.rpc_url(); + let provider = Arc::new(get_http_provider(rpc_url)); + receipts::wait_for_pending(provider, sequence).await + }) + .collect::>(); + + let errors = join_all(futs).await.into_iter().filter_map(Result::err).collect::>(); + + self.sequence.save(true, false)?; + + if !errors.is_empty() { + return Err(eyre::eyre!("{}", errors.iter().format("\n"))); + } + + Ok(self) + } + + /// Broadcasts transactions from all sequences. + pub async fn broadcast(mut self) -> Result { + let required_addresses = self + .sequence + .sequences() + .iter() + .flat_map(|sequence| { + sequence + .typed_transactions() + .map(|tx| (*tx.from().expect("No sender for onchain transaction!")).to_alloy()) + }) + .collect::>(); + + if required_addresses.contains(&Config::DEFAULT_SENDER) { + eyre::bail!( + "You seem to be using Foundry's default sender. Be sure to set your own --sender." + ); + } + + let send_kind = if self.args.unlocked { + SendTransactionsKind::Unlocked(required_addresses) + } else { + let signers = self.script_wallets.into_multi_wallet().into_signers()?; + let mut missing_addresses = Vec::new(); + + for addr in &required_addresses { + if !signers.contains_key(addr) { + missing_addresses.push(addr); + } + } + + if !missing_addresses.is_empty() { + eyre::bail!( + "No associated wallet for addresses: {:?}. Unlocked wallets: {:?}", + missing_addresses, + signers.keys().collect::>() + ); + } + + SendTransactionsKind::Raw(signers) + }; + + for i in 0..self.sequence.sequences().len() { + let mut sequence = self.sequence.sequences_mut().get_mut(i).unwrap(); + + let provider = Arc::new(try_get_http_provider(sequence.rpc_url())?); + let already_broadcasted = sequence.receipts.len(); + + if already_broadcasted < sequence.transactions.len() { + // Make a one-time gas price estimation + let (gas_price, eip1559_fees) = match self.args.with_gas_price { + None => match sequence.transactions.front().unwrap().typed_tx() { + TypedTransaction::Eip1559(_) => { + let mut fees = estimate_eip1559_fees(&provider, Some(sequence.chain)) + .await + .wrap_err("Failed to estimate EIP1559 fees. This chain might not support EIP1559, try adding --legacy to your command.")?; + + if let Some(priority_gas_price) = self.args.priority_gas_price { + fees.1 = priority_gas_price.to_ethers(); + } + + (None, Some(fees)) + } + _ => (provider.get_gas_price().await.ok(), None), + }, + Some(gas_price) => (Some(gas_price.to_ethers()), None), + }; + + // Iterate through transactions, matching the `from` field with the associated + // wallet. Then send the transaction. Panics if we find a unknown `from` + let transactions = sequence + .transactions + .iter() + .skip(already_broadcasted) + .map(|tx_with_metadata| { + let tx = tx_with_metadata.typed_tx(); + let from = + (*tx.from().expect("No sender for onchain transaction!")).to_alloy(); + + let kind = send_kind.for_sender(&from)?; + let is_fixed_gas_limit = tx_with_metadata.is_fixed_gas_limit; + + let mut tx = tx.clone(); + + tx.set_chain_id(sequence.chain); + + if let Some(gas_price) = gas_price { + tx.set_gas_price(gas_price); + } else { + let eip1559_fees = eip1559_fees.expect("was set above"); + // fill gas price + match tx { + TypedTransaction::Eip1559(ref mut inner) => { + inner.max_priority_fee_per_gas = Some(eip1559_fees.1); + inner.max_fee_per_gas = Some(eip1559_fees.0); + } + _ => { + // If we're here, it means that first transaction of the + // sequence was EIP1559 transaction (see match statement above), + // however, we can only have transactions of the same type in + // the sequence. + unreachable!() + } + } + } + + Ok((tx, kind, is_fixed_gas_limit)) + }) + .collect::>>()?; + + let estimate_via_rpc = + has_different_gas_calc(sequence.chain) || self.args.skip_simulation; + + // We only wait for a transaction receipt before sending the next transaction, if + // there is more than one signer. There would be no way of assuring + // their order otherwise. + // Or if the chain does not support batched transactions (eg. Arbitrum). + // Or if we need to invoke eth_estimateGas before sending transactions. + let sequential_broadcast = estimate_via_rpc || + self.args.slow || + send_kind.signers_count() != 1 || + !has_batch_support(sequence.chain); + + let pb = init_progress!(transactions, "txes"); + + // We send transactions and wait for receipts in batches of 100, since some networks + // cannot handle more than that. + let batch_size = if sequential_broadcast { 1 } else { 100 }; + let mut index = already_broadcasted; + + for (batch_number, batch) in + transactions.chunks(batch_size).map(|f| f.to_vec()).enumerate() + { + let mut pending_transactions = vec![]; + + shell::println(format!( + "##\nSending transactions [{} - {}].", + batch_number * batch_size, + batch_number * batch_size + std::cmp::min(batch_size, batch.len()) - 1 + ))?; + for (tx, kind, is_fixed_gas_limit) in batch.into_iter() { + let tx_hash = send_transaction( + provider.clone(), + tx, + kind, + sequential_broadcast, + is_fixed_gas_limit, + estimate_via_rpc, + self.args.gas_estimate_multiplier, + ); + pending_transactions.push(tx_hash); + } + + if !pending_transactions.is_empty() { + let mut buffer = futures::stream::iter(pending_transactions).buffered(7); + + while let Some(tx_hash) = buffer.next().await { + let tx_hash = tx_hash.wrap_err("Failed to send transaction")?; + sequence.add_pending(index, tx_hash); + + // Checkpoint save + self.sequence.save(true, false)?; + sequence = self.sequence.sequences_mut().get_mut(i).unwrap(); + + update_progress!(pb, index - already_broadcasted); + index += 1; + } + + // Checkpoint save + self.sequence.save(true, false)?; + sequence = self.sequence.sequences_mut().get_mut(i).unwrap(); + + shell::println("##\nWaiting for receipts.")?; + receipts::clear_pendings(provider.clone(), sequence, None).await?; + } + // Checkpoint save + self.sequence.save(true, false)?; + sequence = self.sequence.sequences_mut().get_mut(i).unwrap(); + } + } + + shell::println("\n\n==========================")?; + shell::println("\nONCHAIN EXECUTION COMPLETE & SUCCESSFUL.")?; + + let (total_gas, total_gas_price, total_paid) = sequence.receipts.iter().fold( + (U256::ZERO, U256::ZERO, U256::ZERO), + |acc, receipt| { + let gas_used = receipt.gas_used.unwrap_or_default().to_alloy(); + let gas_price = receipt.effective_gas_price.unwrap_or_default().to_alloy(); + (acc.0 + gas_used, acc.1 + gas_price, acc.2 + gas_used * gas_price) + }, + ); + let paid = format_units(total_paid, 18).unwrap_or_else(|_| "N/A".to_string()); + let avg_gas_price = + format_units(total_gas_price / U256::from(sequence.receipts.len()), 9) + .unwrap_or_else(|_| "N/A".to_string()); + + shell::println(format!( + "Total Paid: {} ETH ({} gas * avg {} gwei)", + paid.trim_end_matches('0'), + total_gas, + avg_gas_price.trim_end_matches('0').trim_end_matches('.') + ))?; + } + + Ok(BroadcastedState { + args: self.args, + script_config: self.script_config, + build_data: self.build_data, + execution_data: self.execution_data, + execution_artifacts: self.execution_artifacts, + sequence: self.sequence, + }) + } + + pub fn verify_preflight_check(&self) -> Result<()> { + for sequence in self.sequence.sequences() { + if self.args.verifier.verifier == VerificationProviderType::Etherscan && + self.script_config + .config + .get_etherscan_api_key(Some(sequence.chain.into())) + .is_none() + { + eyre::bail!("Missing etherscan key for chain {}", sequence.chain); + } + } + + Ok(()) + } +} diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs new file mode 100644 index 000000000..4dc78b0cd --- /dev/null +++ b/crates/script/src/build.rs @@ -0,0 +1,248 @@ +use crate::{execute::LinkedState, ScriptArgs, ScriptConfig}; + +use alloy_primitives::{Address, Bytes}; +use eyre::{Context, OptionExt, Result}; +use foundry_cheatcodes::ScriptWallets; +use foundry_cli::utils::get_cached_entry_by_name; +use foundry_common::{ + compile::{self, ContractSources, ProjectCompiler}, + ContractsByArtifact, +}; +use foundry_compilers::{ + artifacts::{BytecodeObject, ContractBytecode, ContractBytecodeSome, Libraries}, + cache::SolFilesCache, + contracts::ArtifactContracts, + info::ContractInfo, + ArtifactId, +}; +use foundry_linking::{LinkOutput, Linker}; +use std::str::FromStr; + +/// Container for the compiled contracts. +pub struct BuildData { + /// Linker which can be used to link contracts, owns [ArtifactContracts] map. + pub linker: Linker, + /// Id of target contract artifact. + pub target: ArtifactId, + /// Source files of the contracts. Used by debugger. + pub sources: ContractSources, +} + +impl BuildData { + /// Links the build data with given libraries, using sender and nonce to compute addresses of + /// missing libraries. + pub fn link( + self, + known_libraries: Libraries, + sender: Address, + nonce: u64, + ) -> Result { + let link_output = + self.linker.link_with_nonce_or_address(known_libraries, sender, nonce, &self.target)?; + + LinkedBuildData::new(link_output, self) + } + + /// Links the build data with the given libraries. Expects supplied libraries set being enough + /// to fully link target contract. + pub fn link_with_libraries(self, libraries: Libraries) -> Result { + let link_output = + self.linker.link_with_nonce_or_address(libraries, Address::ZERO, 0, &self.target)?; + + if !link_output.libs_to_deploy.is_empty() { + eyre::bail!("incomplete libraries set"); + } + + LinkedBuildData::new(link_output, self) + } +} + +/// Container for the linked contracts and their dependencies +pub struct LinkedBuildData { + /// Original build data, might be used to relink this object with different libraries. + pub build_data: BuildData, + /// Known fully linked contracts. + pub highlevel_known_contracts: ArtifactContracts, + /// Libraries used to link the contracts. + pub libraries: Libraries, + /// Libraries that need to be deployed by sender before script execution. + pub predeploy_libraries: Vec, +} + +impl LinkedBuildData { + pub fn new(link_output: LinkOutput, build_data: BuildData) -> Result { + let highlevel_known_contracts = build_data + .linker + .get_linked_artifacts(&link_output.libraries)? + .iter() + .filter_map(|(id, contract)| { + ContractBytecodeSome::try_from(ContractBytecode::from(contract.clone())) + .ok() + .map(|tc| (id.clone(), tc)) + }) + .filter(|(_, tc)| tc.bytecode.object.is_non_empty_bytecode()) + .collect(); + + Ok(Self { + build_data, + highlevel_known_contracts, + libraries: link_output.libraries, + predeploy_libraries: link_output.libs_to_deploy, + }) + } + + /// Flattens the contracts into (`id` -> (`JsonAbi`, `Vec`)) pairs + pub fn get_flattened_contracts(&self, deployed_code: bool) -> ContractsByArtifact { + ContractsByArtifact( + self.highlevel_known_contracts + .iter() + .filter_map(|(id, c)| { + let bytecode = if deployed_code { + c.deployed_bytecode.bytes() + } else { + c.bytecode.bytes() + }; + bytecode.cloned().map(|code| (id.clone(), (c.abi.clone(), code.into()))) + }) + .collect(), + ) + } + + /// Fetches target bytecode from linked contracts. + pub fn get_target_contract(&self) -> Result { + self.highlevel_known_contracts + .get(&self.build_data.target) + .cloned() + .ok_or_eyre("target not found in linked artifacts") + } +} + +/// First state basically containing only inputs of the user. +pub struct PreprocessedState { + pub args: ScriptArgs, + pub script_config: ScriptConfig, + pub script_wallets: ScriptWallets, +} + +impl PreprocessedState { + /// Parses user input and compiles the contracts depending on script target. + /// After compilation, finds exact [ArtifactId] of the target contract. + pub fn compile(self) -> Result { + let Self { args, script_config, script_wallets } = self; + let project = script_config.config.project()?; + let filters = args.skip.clone().unwrap_or_default(); + + let mut target_name = args.target_contract.clone(); + + // If we've received correct path, use it as target_path + // Otherwise, parse input as : and use the path from the contract info, if + // present. + let target_path = if let Ok(path) = dunce::canonicalize(&args.path) { + Some(path) + } else { + let contract = ContractInfo::from_str(&args.path)?; + target_name = Some(contract.name.clone()); + if let Some(path) = contract.path { + Some(dunce::canonicalize(path)?) + } else { + None + } + }; + + // If we've found target path above, only compile it. + // Otherwise, compile everything to match contract by name later. + let output = if let Some(target_path) = target_path.clone() { + compile::compile_target_with_filter( + &target_path, + &project, + args.opts.silent, + args.verify, + filters, + ) + } else if !project.paths.has_input_files() { + Err(eyre::eyre!("The project doesn't have any input files. Make sure the `script` directory is configured properly in foundry.toml. Otherwise, provide the path to the file.")) + } else { + ProjectCompiler::new().compile(&project) + }?; + + // If we still don't have target path, find it by name in the compilation cache. + let target_path = if let Some(target_path) = target_path { + target_path + } else { + let target_name = target_name.clone().expect("was set above"); + let cache = SolFilesCache::read_joined(&project.paths) + .wrap_err("Could not open compiler cache")?; + let (path, _) = get_cached_entry_by_name(&cache, &target_name) + .wrap_err("Could not find target contract in cache")?; + path + }; + + let target_path = project.root().join(target_path); + + let mut target_id: Option = None; + + // Find target artfifact id by name and path in compilation artifacts. + for (id, contract) in output.artifact_ids().filter(|(id, _)| id.source == target_path) { + if let Some(name) = &target_name { + if id.name != *name { + continue; + } + } else if contract.abi.as_ref().map_or(true, |abi| abi.is_empty()) || + contract.bytecode.as_ref().map_or(true, |b| match &b.object { + BytecodeObject::Bytecode(b) => b.is_empty(), + BytecodeObject::Unlinked(_) => false, + }) + { + // Ignore contracts with empty abi or linked bytecode of length 0 which are + // interfaces/abstract contracts/libraries. + continue; + } + + if let Some(target) = target_id { + // We might have multiple artifacts for the same contract but with different + // solc versions. Their names will have form of {name}.0.X.Y, so we are + // stripping versions off before comparing them. + let target_name = target.name.split('.').next().unwrap(); + let id_name = id.name.split('.').next().unwrap(); + if target_name != id_name { + eyre::bail!("Multiple contracts in the target path. Please specify the contract name with `--tc ContractName`") + } + } + target_id = Some(id); + } + + let sources = ContractSources::from_project_output(&output, project.root())?; + let contracts = output.into_artifacts().collect(); + let target = target_id.ok_or_eyre("Could not find target contract")?; + let linker = Linker::new(project.root(), contracts); + + Ok(CompiledState { + args, + script_config, + script_wallets, + build_data: BuildData { linker, target, sources }, + }) + } +} + +/// State after we have determined and compiled target contract to be executed. +pub struct CompiledState { + pub args: ScriptArgs, + pub script_config: ScriptConfig, + pub script_wallets: ScriptWallets, + pub build_data: BuildData, +} + +impl CompiledState { + /// Uses provided sender address to compute library addresses and link contracts with them. + pub fn link(self) -> Result { + let Self { args, script_config, script_wallets, build_data } = self; + + let sender = script_config.evm_opts.sender; + let nonce = script_config.sender_nonce; + let known_libraries = script_config.config.libraries_with_remappings()?; + let build_data = build_data.link(known_libraries, sender, nonce)?; + + Ok(LinkedState { args, script_config, script_wallets, build_data }) + } +} diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs new file mode 100644 index 000000000..cdf4353d3 --- /dev/null +++ b/crates/script/src/execute.rs @@ -0,0 +1,522 @@ +use crate::{ + build::{CompiledState, LinkedBuildData}, + simulate::PreSimulationState, + ScriptArgs, ScriptConfig, +}; + +use super::{runner::ScriptRunner, JsonResult, NestedValue, ScriptResult}; +use alloy_dyn_abi::FunctionExt; +use alloy_json_abi::{Function, InternalType, JsonAbi}; +use alloy_primitives::{Address, Bytes, U64}; +use alloy_rpc_types::request::TransactionRequest; +use async_recursion::async_recursion; +use ethers_providers::Middleware; +use eyre::Result; +use foundry_cheatcodes::ScriptWallets; +use foundry_cli::utils::{ensure_clean_constructor, needs_setup}; +use foundry_common::{ + fmt::{format_token, format_token_raw}, + provider::ethers::{get_http_provider, RpcUrl}, + shell, ContractsByArtifact, +}; +use foundry_compilers::artifacts::ContractBytecodeSome; +use foundry_config::{Config, NamedChain}; +use foundry_debugger::Debugger; +use foundry_evm::{ + decode::{decode_console_logs, RevertDecoder}, + inspectors::cheatcodes::{BroadcastableTransaction, BroadcastableTransactions}, + traces::{ + identifier::{SignaturesIdentifier, TraceIdentifiers}, + render_trace_arena, CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, + }, +}; +use futures::future::join_all; +use itertools::Itertools; +use std::collections::{HashMap, HashSet}; +use yansi::Paint; + +/// State after linking, contains the linked build data along with library addresses and optional +/// array of libraries that need to be predeployed. +pub struct LinkedState { + pub args: ScriptArgs, + pub script_config: ScriptConfig, + pub script_wallets: ScriptWallets, + pub build_data: LinkedBuildData, +} + +/// Container for data we need for execution which can only be obtained after linking stage. +pub struct ExecutionData { + /// Function to call. + pub func: Function, + /// Calldata to pass to the target contract. + pub calldata: Bytes, + /// Bytecode of the target contract. + pub bytecode: Bytes, + /// ABI of the target contract. + pub abi: JsonAbi, +} + +impl LinkedState { + /// Given linked and compiled artifacts, prepares data we need for execution. + /// This includes the function to call and the calldata to pass to it. + pub async fn prepare_execution(self) -> Result { + let Self { args, script_config, script_wallets, build_data } = self; + + let ContractBytecodeSome { abi, bytecode, .. } = build_data.get_target_contract()?; + + let bytecode = bytecode.into_bytes().ok_or_else(|| { + eyre::eyre!("expected fully linked bytecode, found unlinked bytecode") + })?; + + let (func, calldata) = args.get_method_and_calldata(&abi)?; + + ensure_clean_constructor(&abi)?; + + Ok(PreExecutionState { + args, + script_config, + script_wallets, + build_data, + execution_data: ExecutionData { func, calldata, bytecode, abi }, + }) + } +} + +/// Same as [LinkedState], but also contains [ExecutionData]. +pub struct PreExecutionState { + pub args: ScriptArgs, + pub script_config: ScriptConfig, + pub script_wallets: ScriptWallets, + pub build_data: LinkedBuildData, + pub execution_data: ExecutionData, +} + +impl PreExecutionState { + /// Executes the script and returns the state after execution. + /// Might require executing script twice in cases when we determine sender from execution. + #[async_recursion] + pub async fn execute(mut self) -> Result { + let mut runner = self + .script_config + .get_runner_with_cheatcodes(self.script_wallets.clone(), self.args.debug) + .await?; + let mut result = self.execute_with_runner(&mut runner).await?; + + // If we have a new sender from execution, we need to use it to deploy libraries and relink + // contracts. + if let Some(new_sender) = self.maybe_new_sender(result.transactions.as_ref())? { + self.script_config.update_sender(new_sender).await?; + + // Rollback to rerun linking with the new sender. + let state = CompiledState { + args: self.args, + script_config: self.script_config, + script_wallets: self.script_wallets, + build_data: self.build_data.build_data, + }; + + return state.link()?.prepare_execution().await?.execute().await; + } + + // Add library deployment transactions to broadcastable transactions list. + if let Some(txs) = result.transactions.take() { + result.transactions = Some( + self.build_data + .predeploy_libraries + .iter() + .enumerate() + .map(|(i, bytes)| BroadcastableTransaction { + rpc: self.script_config.evm_opts.fork_url.clone(), + transaction: TransactionRequest { + from: Some(self.script_config.evm_opts.sender), + input: Some(bytes.clone()).into(), + nonce: Some(U64::from(self.script_config.sender_nonce + i as u64)), + ..Default::default() + }, + }) + .chain(txs) + .collect(), + ); + } + + Ok(ExecutedState { + args: self.args, + script_config: self.script_config, + script_wallets: self.script_wallets, + build_data: self.build_data, + execution_data: self.execution_data, + execution_result: result, + }) + } + + /// Executes the script using the provided runner and returns the [ScriptResult]. + pub async fn execute_with_runner(&self, runner: &mut ScriptRunner) -> Result { + let (address, mut setup_result) = runner.setup( + &self.build_data.predeploy_libraries, + self.execution_data.bytecode.clone(), + needs_setup(&self.execution_data.abi), + self.script_config.sender_nonce, + self.args.broadcast, + self.script_config.evm_opts.fork_url.is_none(), + )?; + + if setup_result.success { + let script_result = runner.script(address, self.execution_data.calldata.clone())?; + + setup_result.success &= script_result.success; + setup_result.gas_used = script_result.gas_used; + setup_result.logs.extend(script_result.logs); + setup_result.traces.extend(script_result.traces); + setup_result.debug = script_result.debug; + setup_result.labeled_addresses.extend(script_result.labeled_addresses); + setup_result.returned = script_result.returned; + setup_result.breakpoints = script_result.breakpoints; + + match (&mut setup_result.transactions, script_result.transactions) { + (Some(txs), Some(new_txs)) => { + txs.extend(new_txs); + } + (None, Some(new_txs)) => { + setup_result.transactions = Some(new_txs); + } + _ => {} + } + } + + Ok(setup_result) + } + + /// It finds the deployer from the running script and uses it to predeploy libraries. + /// + /// If there are multiple candidate addresses, it skips everything and lets `--sender` deploy + /// them instead. + fn maybe_new_sender( + &self, + transactions: Option<&BroadcastableTransactions>, + ) -> Result> { + let mut new_sender = None; + + if let Some(txs) = transactions { + // If the user passed a `--sender` don't check anything. + if !self.build_data.predeploy_libraries.is_empty() && + self.args.evm_opts.sender.is_none() + { + for tx in txs.iter() { + if tx.transaction.to.is_none() { + let sender = tx.transaction.from.expect("no sender"); + if let Some(ns) = new_sender { + if sender != ns { + shell::println("You have more than one deployer who could predeploy libraries. Using `--sender` instead.")?; + return Ok(None); + } + } else if sender != self.script_config.evm_opts.sender { + new_sender = Some(sender); + } + } + } + } + } + Ok(new_sender) + } +} + +/// Container for information about RPC-endpoints used during script execution. +pub struct RpcData { + /// Unique list of rpc urls present. + pub total_rpcs: HashSet, + /// If true, one of the transactions did not have a rpc. + pub missing_rpc: bool, +} + +impl RpcData { + /// Iterates over script transactions and collects RPC urls. + fn from_transactions(txs: &BroadcastableTransactions) -> Self { + let missing_rpc = txs.iter().any(|tx| tx.rpc.is_none()); + let total_rpcs = + txs.iter().filter_map(|tx| tx.rpc.as_ref().cloned()).collect::>(); + + Self { total_rpcs, missing_rpc } + } + + /// Returns true if script might be multi-chain. + /// Returns false positive in case when missing rpc is the same as the only rpc present. + pub fn is_multi_chain(&self) -> bool { + self.total_rpcs.len() > 1 || (self.missing_rpc && !self.total_rpcs.is_empty()) + } + + /// Checks if all RPCs support EIP-3855. Prints a warning if not. + async fn check_shanghai_support(&self) -> Result<()> { + let chain_ids = self.total_rpcs.iter().map(|rpc| async move { + let provider = get_http_provider(rpc); + let id = provider.get_chainid().await.ok()?; + let id_u64: u64 = id.try_into().ok()?; + NamedChain::try_from(id_u64).ok() + }); + + let chains = join_all(chain_ids).await; + let iter = chains.iter().flatten().map(|c| (c.supports_shanghai(), c)); + if iter.clone().any(|(s, _)| !s) { + let msg = format!( + "\ +EIP-3855 is not supported in one or more of the RPCs used. +Unsupported Chain IDs: {}. +Contracts deployed with a Solidity version equal or higher than 0.8.20 might not work properly. +For more information, please see https://eips.ethereum.org/EIPS/eip-3855", + iter.filter(|(supported, _)| !supported) + .map(|(_, chain)| *chain as u64) + .format(", ") + ); + shell::println(Paint::yellow(msg))?; + } + Ok(()) + } +} + +/// Container for data being collected after execution. +pub struct ExecutionArtifacts { + /// Mapping from contract to its runtime code. + pub known_contracts: ContractsByArtifact, + /// Trace decoder used to decode traces. + pub decoder: CallTraceDecoder, + /// Return values from the execution result. + pub returns: HashMap, + /// Information about RPC endpoints used during script execution. + pub rpc_data: RpcData, +} + +/// State after the script has been executed. +pub struct ExecutedState { + pub args: ScriptArgs, + pub script_config: ScriptConfig, + pub script_wallets: ScriptWallets, + pub build_data: LinkedBuildData, + pub execution_data: ExecutionData, + pub execution_result: ScriptResult, +} + +impl ExecutedState { + /// Collects the data we need for simulation and various post-execution tasks. + pub async fn prepare_simulation(self) -> Result { + let returns = self.get_returns()?; + + let known_contracts = self.build_data.get_flattened_contracts(true); + let decoder = self.build_trace_decoder(&known_contracts)?; + + let txs = self.execution_result.transactions.clone().unwrap_or_default(); + let rpc_data = RpcData::from_transactions(&txs); + + if rpc_data.is_multi_chain() { + shell::eprintln(format!( + "{}", + Paint::yellow( + "Multi chain deployment is still under development. Use with caution." + ) + ))?; + if !self.build_data.libraries.is_empty() { + eyre::bail!( + "Multi chain deployment does not support library linking at the moment." + ) + } + } + rpc_data.check_shanghai_support().await?; + + Ok(PreSimulationState { + args: self.args, + script_config: self.script_config, + script_wallets: self.script_wallets, + build_data: self.build_data, + execution_data: self.execution_data, + execution_result: self.execution_result, + execution_artifacts: ExecutionArtifacts { known_contracts, decoder, returns, rpc_data }, + }) + } + + /// Builds [CallTraceDecoder] from the execution result and known contracts. + fn build_trace_decoder( + &self, + known_contracts: &ContractsByArtifact, + ) -> Result { + let mut decoder = CallTraceDecoderBuilder::new() + .with_labels(self.execution_result.labeled_addresses.clone()) + .with_verbosity(self.script_config.evm_opts.verbosity) + .with_known_contracts(known_contracts) + .with_signature_identifier(SignaturesIdentifier::new( + Config::foundry_cache_dir(), + self.script_config.config.offline, + )?) + .build(); + + let mut identifier = TraceIdentifiers::new().with_local(known_contracts).with_etherscan( + &self.script_config.config, + self.script_config.evm_opts.get_remote_chain_id(), + )?; + + // Decoding traces using etherscan is costly as we run into rate limits, + // causing scripts to run for a very long time unnecessarily. + // Therefore, we only try and use etherscan if the user has provided an API key. + let should_use_etherscan_traces = self.script_config.config.etherscan_api_key.is_some(); + if !should_use_etherscan_traces { + identifier.etherscan = None; + } + + for (_, trace) in &self.execution_result.traces { + decoder.identify(trace, &mut identifier); + } + + Ok(decoder) + } + + /// Collects the return values from the execution result. + fn get_returns(&self) -> Result> { + let mut returns = HashMap::new(); + let returned = &self.execution_result.returned; + let func = &self.execution_data.func; + + match func.abi_decode_output(returned, false) { + Ok(decoded) => { + for (index, (token, output)) in decoded.iter().zip(&func.outputs).enumerate() { + let internal_type = + output.internal_type.clone().unwrap_or(InternalType::Other { + contract: None, + ty: "unknown".to_string(), + }); + + let label = if !output.name.is_empty() { + output.name.to_string() + } else { + index.to_string() + }; + + returns.insert( + label, + NestedValue { + internal_type: internal_type.to_string(), + value: format_token_raw(token), + }, + ); + } + } + Err(_) => { + shell::println(format!("{returned:?}"))?; + } + } + + Ok(returns) + } +} + +impl PreSimulationState { + pub fn show_json(&self) -> Result<()> { + let result = &self.execution_result; + + let console_logs = decode_console_logs(&result.logs); + let output = JsonResult { + logs: console_logs, + gas_used: result.gas_used, + returns: self.execution_artifacts.returns.clone(), + }; + let j = serde_json::to_string(&output)?; + shell::println(j)?; + + if !self.execution_result.success { + return Err(eyre::eyre!( + "script failed: {}", + RevertDecoder::new().decode(&self.execution_result.returned[..], None) + )); + } + + Ok(()) + } + + pub async fn show_traces(&self) -> Result<()> { + let verbosity = self.script_config.evm_opts.verbosity; + let func = &self.execution_data.func; + let result = &self.execution_result; + let decoder = &self.execution_artifacts.decoder; + + if !result.success || verbosity > 3 { + if result.traces.is_empty() { + warn!(verbosity, "no traces"); + } + + shell::println("Traces:")?; + for (kind, trace) in &result.traces { + let should_include = match kind { + TraceKind::Setup => verbosity >= 5, + TraceKind::Execution => verbosity > 3, + _ => false, + } || !result.success; + + if should_include { + shell::println(render_trace_arena(trace, decoder).await?)?; + } + } + shell::println(String::new())?; + } + + if result.success { + shell::println(format!("{}", Paint::green("Script ran successfully.")))?; + } + + if self.script_config.evm_opts.fork_url.is_none() { + shell::println(format!("Gas used: {}", result.gas_used))?; + } + + if result.success && !result.returned.is_empty() { + shell::println("\n== Return ==")?; + match func.abi_decode_output(&result.returned, false) { + Ok(decoded) => { + for (index, (token, output)) in decoded.iter().zip(&func.outputs).enumerate() { + let internal_type = + output.internal_type.clone().unwrap_or(InternalType::Other { + contract: None, + ty: "unknown".to_string(), + }); + + let label = if !output.name.is_empty() { + output.name.to_string() + } else { + index.to_string() + }; + shell::println(format!( + "{}: {internal_type} {}", + label.trim_end(), + format_token(token) + ))?; + } + } + Err(_) => { + shell::println(format!("{:x?}", (&result.returned)))?; + } + } + } + + let console_logs = decode_console_logs(&result.logs); + if !console_logs.is_empty() { + shell::println("\n== Logs ==")?; + for log in console_logs { + shell::println(format!(" {log}"))?; + } + } + + if !result.success { + return Err(eyre::eyre!( + "script failed: {}", + RevertDecoder::new().decode(&result.returned[..], None) + )); + } + + Ok(()) + } + + pub fn run_debugger(&self) -> Result<()> { + let mut debugger = Debugger::builder() + .debug_arenas(self.execution_result.debug.as_deref().unwrap_or_default()) + .decoder(&self.execution_artifacts.decoder) + .sources(self.build_data.build_data.sources.clone()) + .breakpoints(self.execution_result.breakpoints.clone()) + .build(); + debugger.try_run()?; + Ok(()) + } +} diff --git a/crates/forge/bin/cmd/script/mod.rs b/crates/script/src/lib.rs similarity index 62% rename from crates/forge/bin/cmd/script/mod.rs rename to crates/script/src/lib.rs index 98c3e223d..6d8fb87b8 100644 --- a/crates/forge/bin/cmd/script/mod.rs +++ b/crates/script/src/lib.rs @@ -1,67 +1,68 @@ -use super::build::BuildArgs; -use alloy_dyn_abi::FunctionExt; -use alloy_json_abi::{Function, InternalType, JsonAbi}; -use alloy_primitives::{Address, Bytes, Log, U256, U64}; -use alloy_rpc_types::request::TransactionRequest; +#![cfg_attr(not(test), warn(unused_crate_dependencies))] + +#[macro_use] +extern crate tracing; + +use self::transaction::AdditionalContract; +use crate::runner::ScriptRunner; +use alloy_json_abi::{Function, JsonAbi}; +use alloy_primitives::{Address, Bytes, Log, U256}; +use broadcast::next_nonce; +use build::PreprocessedState; use clap::{Parser, ValueHint}; use dialoguer::Confirm; -use ethers_providers::{Http, Middleware}; +use ethers_signers::Signer; use eyre::{ContextCompat, Result, WrapErr}; -use forge::{ - backend::Backend, - debug::DebugArena, - decode::decode_console_logs, - opts::EvmOpts, - traces::{ - identifier::SignaturesIdentifier, render_trace_arena, CallTraceDecoder, - CallTraceDecoderBuilder, TraceKind, Traces, - }, -}; use forge_verify::RetryArgs; +use foundry_cli::{opts::CoreBuildArgs, utils::LoadConfig}; use foundry_common::{ abi::{encode_function_args, get_func}, + compile::SkipBuildFilter, errors::UnlinkedByteCode, evm::{Breakpoints, EvmArgs}, - fmt::{format_token, format_token_raw}, provider::ethers::RpcUrl, - shell, ContractsByArtifact, CONTRACT_MAX_SIZE, SELECTOR_LEN, -}; -use foundry_compilers::{ - artifacts::{ContractBytecodeSome, Libraries}, - ArtifactId, + shell, + types::ToAlloy, + CONTRACT_MAX_SIZE, SELECTOR_LEN, }; +use foundry_compilers::{artifacts::ContractBytecodeSome, ArtifactId}; use foundry_config::{ figment, figment::{ value::{Dict, Map}, Metadata, Profile, Provider, }, - Config, NamedChain, + Config, }; use foundry_evm::{ + backend::Backend, constants::DEFAULT_CREATE2_DEPLOYER, - decode::RevertDecoder, - inspectors::cheatcodes::{BroadcastableTransaction, BroadcastableTransactions}, - traces::identifier::TraceIdentifiers, + debug::DebugArena, + executors::ExecutorBuilder, + inspectors::{ + cheatcodes::{BroadcastableTransactions, ScriptWallets}, + CheatsConfig, + }, + opts::EvmOpts, + traces::Traces, }; use foundry_wallets::MultiWalletOpts; -use futures::future; -use itertools::Itertools; use serde::{Deserialize, Serialize}; -use std::collections::{BTreeMap, HashMap, HashSet}; +use std::collections::{BTreeMap, HashMap}; use yansi::Paint; mod artifacts; mod broadcast; mod build; -mod cmd; -mod executor; -mod multi; +mod execute; +mod multi_sequence; mod providers; mod receipts; +mod resume; mod runner; mod sequence; -pub mod transaction; +mod simulate; +mod transaction; mod verify; // Loads project's figment and merges the build cli arguments into it @@ -174,8 +175,14 @@ pub struct ScriptArgs { )] pub with_gas_price: Option, + /// Skip building files whose names contain the given filter. + /// + /// `test` and `script` are aliases for `.t.sol` and `.s.sol`. + #[arg(long, num_args(1..))] + pub skip: Option>, + #[command(flatten)] - pub opts: BuildArgs, + pub opts: CoreBuildArgs, #[command(flatten)] pub wallets: MultiWalletOpts, @@ -193,235 +200,106 @@ pub struct ScriptArgs { // === impl ScriptArgs === impl ScriptArgs { - fn decode_traces( - &self, - script_config: &ScriptConfig, - result: &mut ScriptResult, - known_contracts: &ContractsByArtifact, - ) -> Result { - let mut decoder = CallTraceDecoderBuilder::new() - .with_labels(result.labeled_addresses.clone()) - .with_verbosity(script_config.evm_opts.verbosity) - .with_known_contracts(known_contracts) - .with_signature_identifier(SignaturesIdentifier::new( - Config::foundry_cache_dir(), - script_config.config.offline, - )?) - .build(); - let mut identifier = TraceIdentifiers::new() - .with_local(known_contracts) - .with_etherscan(&script_config.config, script_config.evm_opts.get_remote_chain_id())?; - - // Decoding traces using etherscan is costly as we run into rate limits, - // causing scripts to run for a very long time unnecessarily. - // Therefore, we only try and use etherscan if the user has provided an API key. - let should_use_etherscan_traces = script_config.config.etherscan_api_key.is_some(); - if !should_use_etherscan_traces { - identifier.etherscan = None; - } + async fn preprocess(self) -> Result { + let script_wallets = + ScriptWallets::new(self.wallets.get_multi_wallet().await?, self.evm_opts.sender); - for (_, trace) in &mut result.traces { - decoder.identify(trace, &mut identifier); - } - Ok(decoder) - } + let (config, mut evm_opts) = self.load_config_and_evm_opts_emit_warnings()?; - fn get_returns( - &self, - script_config: &ScriptConfig, - returned: &Bytes, - ) -> Result> { - let func = script_config.called_function.as_ref().expect("There should be a function."); - let mut returns = HashMap::new(); - - match func.abi_decode_output(returned, false) { - Ok(decoded) => { - for (index, (token, output)) in decoded.iter().zip(&func.outputs).enumerate() { - let internal_type = - output.internal_type.clone().unwrap_or(InternalType::Other { - contract: None, - ty: "unknown".to_string(), - }); - - let label = if !output.name.is_empty() { - output.name.to_string() - } else { - index.to_string() - }; - - returns.insert( - label, - NestedValue { - internal_type: internal_type.to_string(), - value: format_token_raw(token), - }, - ); - } - } - Err(_) => { - shell::println(format!("{returned:?}"))?; - } + if let Some(sender) = self.maybe_load_private_key()? { + evm_opts.sender = sender; } - Ok(returns) - } - - async fn show_traces( - &self, - script_config: &ScriptConfig, - decoder: &CallTraceDecoder, - result: &mut ScriptResult, - ) -> Result<()> { - let verbosity = script_config.evm_opts.verbosity; - let func = script_config.called_function.as_ref().expect("There should be a function."); + let script_config = ScriptConfig::new(config, evm_opts).await?; - if !result.success || verbosity > 3 { - if result.traces.is_empty() { - warn!(verbosity, "no traces"); - } - - shell::println("Traces:")?; - for (kind, trace) in &result.traces { - let should_include = match kind { - TraceKind::Setup => verbosity >= 5, - TraceKind::Execution => verbosity > 3, - _ => false, - } || !result.success; + Ok(PreprocessedState { args: self, script_config, script_wallets }) + } - if should_include { - shell::println(render_trace_arena(trace, decoder).await?)?; - } - } - shell::println(String::new())?; + /// Executes the script + pub async fn run_script(self) -> Result<()> { + trace!(target: "script", "executing script command"); + + // Drive state machine to point at which we have everything needed for simulation/resuming. + let pre_simulation = self + .preprocess() + .await? + .compile()? + .link()? + .prepare_execution() + .await? + .execute() + .await? + .prepare_simulation() + .await?; + + if pre_simulation.args.debug { + pre_simulation.run_debugger()?; } - if result.success { - shell::println(format!("{}", Paint::green("Script ran successfully.")))?; + if pre_simulation.args.json { + pre_simulation.show_json()?; + } else { + pre_simulation.show_traces().await?; } - if script_config.evm_opts.fork_url.is_none() { - shell::println(format!("Gas used: {}", result.gas_used))?; + // Ensure that we have transactions to simulate/broadcast, otherwise exit early to avoid + // hard error. + if pre_simulation.execution_result.transactions.as_ref().map_or(true, |txs| txs.is_empty()) + { + return Ok(()); } - if result.success && !result.returned.is_empty() { - shell::println("\n== Return ==")?; - match func.abi_decode_output(&result.returned, false) { - Ok(decoded) => { - for (index, (token, output)) in decoded.iter().zip(&func.outputs).enumerate() { - let internal_type = - output.internal_type.clone().unwrap_or(InternalType::Other { - contract: None, - ty: "unknown".to_string(), - }); - - let label = if !output.name.is_empty() { - output.name.to_string() - } else { - index.to_string() - }; - shell::println(format!( - "{}: {internal_type} {}", - label.trim_end(), - format_token(token) - ))?; - } - } - Err(_) => { - shell::println(format!("{:x?}", (&result.returned)))?; - } - } + // Check if there are any missing RPCs and exit early to avoid hard error. + if pre_simulation.execution_artifacts.rpc_data.missing_rpc { + shell::println("\nIf you wish to simulate on-chain transactions pass a RPC URL.")?; + return Ok(()); } - let console_logs = decode_console_logs(&result.logs); - if !console_logs.is_empty() { - shell::println("\n== Logs ==")?; - for log in console_logs { - shell::println(format!(" {log}"))?; - } - } + // Move from `PreSimulationState` to `BundledState` either by resuming or simulating + // transactions. + let bundled = if pre_simulation.args.resume || + (pre_simulation.args.verify && !pre_simulation.args.broadcast) + { + pre_simulation.resume().await? + } else { + pre_simulation.args.check_contract_sizes( + &pre_simulation.execution_result, + &pre_simulation.build_data.highlevel_known_contracts, + )?; - if !result.success { - return Err(eyre::eyre!( - "script failed: {}", - RevertDecoder::new().decode(&result.returned[..], None) - )); - } + pre_simulation.fill_metadata().await?.bundle().await? + }; - Ok(()) - } + // Exit early in case user didn't provide any broadcast/verify related flags. + if !bundled.args.broadcast && !bundled.args.resume && !bundled.args.verify { + shell::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(()); + } - fn show_json(&self, script_config: &ScriptConfig, result: &ScriptResult) -> Result<()> { - let returns = self.get_returns(script_config, &result.returned)?; + // Exit early if something is wrong with verification options. + if bundled.args.verify { + bundled.verify_preflight_check()?; + } - let console_logs = decode_console_logs(&result.logs); - let output = JsonResult { logs: console_logs, gas_used: result.gas_used, returns }; - let j = serde_json::to_string(&output)?; - shell::println(j)?; + // Wait for pending txes and broadcast others. + let broadcasted = bundled.wait_for_pending().await?.broadcast().await?; - if !result.success { - return Err(eyre::eyre!( - "script failed: {}", - RevertDecoder::new().decode(&result.returned[..], None) - )); + if broadcasted.args.verify { + broadcasted.verify().await?; } Ok(()) } - /// It finds the deployer from the running script and uses it to predeploy libraries. - /// - /// If there are multiple candidate addresses, it skips everything and lets `--sender` deploy - /// them instead. - fn maybe_new_sender( - &self, - evm_opts: &EvmOpts, - transactions: Option<&BroadcastableTransactions>, - predeploy_libraries: &[Bytes], - ) -> Result> { - let mut new_sender = None; - - if let Some(txs) = transactions { - // If the user passed a `--sender` don't check anything. - if !predeploy_libraries.is_empty() && self.evm_opts.sender.is_none() { - for tx in txs.iter() { - if tx.transaction.to.is_none() { - let sender = tx.transaction.from.expect("no sender"); - if let Some(ns) = new_sender { - if sender != ns { - shell::println("You have more than one deployer who could predeploy libraries. Using `--sender` instead.")?; - return Ok(None); - } - } else if sender != evm_opts.sender { - new_sender = Some(sender); - } - } - } - } - } - Ok(new_sender) - } - - /// Helper for building the transactions for any libraries that need to be deployed ahead of - /// linking - fn create_deploy_transactions( - &self, - from: Address, - nonce: u64, - data: &[Bytes], - fork_url: &Option, - ) -> BroadcastableTransactions { - data.iter() - .enumerate() - .map(|(i, bytes)| BroadcastableTransaction { - rpc: fork_url.clone(), - transaction: TransactionRequest { - from: Some(from), - input: Some(bytes.clone()).into(), - nonce: Some(U64::from(nonce + i as u64)), - ..Default::default() - }, - }) - .collect() + /// In case the user has loaded *only* one private-key, we can assume that he's using it as the + /// `--sender` + fn maybe_load_private_key(&self) -> Result> { + let maybe_sender = self + .wallets + .private_keys()? + .filter(|pks| pks.len() == 1) + .map(|pks| pks.first().unwrap().address().to_alloy()); + Ok(maybe_sender) } /// Returns the Function and calldata based on the signature @@ -593,6 +471,26 @@ pub struct ScriptResult { pub breakpoints: Breakpoints, } +impl ScriptResult { + pub fn get_created_contracts(&self) -> Vec { + self.traces + .iter() + .flat_map(|(_, traces)| { + traces.nodes().iter().filter_map(|node| { + if node.trace.kind.is_any_create() { + return Some(AdditionalContract { + opcode: node.trace.kind, + address: node.trace.address, + init_code: node.trace.data.clone(), + }); + } + None + }) + }) + .collect() + } +} + #[derive(Serialize, Deserialize)] struct JsonResult { logs: Vec, @@ -606,91 +504,101 @@ pub struct NestedValue { pub value: String, } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug)] pub struct ScriptConfig { pub config: Config, pub evm_opts: EvmOpts, pub sender_nonce: u64, /// Maps a rpc url to a backend pub backends: HashMap, - /// Script target contract - pub target_contract: Option, - /// Function called by the script - pub called_function: Option, - /// Unique list of rpc urls present - pub total_rpcs: HashSet, - /// If true, one of the transactions did not have a rpc - pub missing_rpc: bool, - /// Should return some debug information - pub debug: bool, } impl ScriptConfig { - fn collect_rpcs(&mut self, txs: &BroadcastableTransactions) { - self.missing_rpc = txs.iter().any(|tx| tx.rpc.is_none()); - - self.total_rpcs - .extend(txs.iter().filter_map(|tx| tx.rpc.as_ref().cloned()).collect::>()); - - if let Some(rpc) = &self.evm_opts.fork_url { - self.total_rpcs.insert(rpc.clone()); - } + pub async fn new(config: Config, evm_opts: EvmOpts) -> Result { + let sender_nonce = if let Some(fork_url) = evm_opts.fork_url.as_ref() { + next_nonce(evm_opts.sender, fork_url, None).await? + } else { + // dapptools compatibility + 1 + }; + Ok(Self { config, evm_opts, sender_nonce, backends: HashMap::new() }) } - fn has_multiple_rpcs(&self) -> bool { - self.total_rpcs.len() > 1 + pub async fn update_sender(&mut self, sender: Address) -> Result<()> { + self.sender_nonce = if let Some(fork_url) = self.evm_opts.fork_url.as_ref() { + next_nonce(sender, fork_url, None).await? + } else { + // dapptools compatibility + 1 + }; + self.evm_opts.sender = sender; + Ok(()) } - /// Certain features are disabled for multi chain deployments, and if tried, will return - /// error. [library support] - fn check_multi_chain_constraints(&self, libraries: &Libraries) -> Result<()> { - if self.has_multiple_rpcs() || (self.missing_rpc && !self.total_rpcs.is_empty()) { - shell::eprintln(format!( - "{}", - Paint::yellow( - "Multi chain deployment is still under development. Use with caution." - ) - ))?; - if !libraries.libs.is_empty() { - eyre::bail!( - "Multi chain deployment does not support library linking at the moment." - ) - } - } - Ok(()) + async fn get_runner(&mut self) -> Result { + self._get_runner(None, false).await } - /// Returns the script target contract - fn target_contract(&self) -> &ArtifactId { - self.target_contract.as_ref().expect("should exist after building") + async fn get_runner_with_cheatcodes( + &mut self, + script_wallets: ScriptWallets, + debug: bool, + ) -> Result { + self._get_runner(Some(script_wallets), debug).await } - /// Checks if the RPCs used point to chains that support EIP-3855. - /// If not, warns the user. - async fn check_shanghai_support(&self) -> Result<()> { - let chain_ids = self.total_rpcs.iter().map(|rpc| async move { - let provider = ethers_providers::Provider::::try_from(rpc).ok()?; - let id = provider.get_chainid().await.ok()?; - let id_u64: u64 = id.try_into().ok()?; - NamedChain::try_from(id_u64).ok() - }); + async fn _get_runner( + &mut self, + script_wallets: Option, + debug: bool, + ) -> Result { + trace!("preparing script runner"); + let env = self.evm_opts.evm_env().await?; + + let db = if let Some(fork_url) = self.evm_opts.fork_url.as_ref() { + match self.backends.get(fork_url) { + Some(db) => db.clone(), + None => { + let fork = self.evm_opts.get_fork(&self.config, env.clone()); + let backend = Backend::spawn(fork); + self.backends.insert(fork_url.clone(), backend.clone()); + backend + } + } + } else { + // It's only really `None`, when we don't pass any `--fork-url`. And if so, there is + // no need to cache it, since there won't be any onchain simulation that we'd need + // to cache the backend for. + Backend::spawn(None) + }; - let chains = future::join_all(chain_ids).await; - let iter = chains.iter().flatten().map(|c| (c.supports_shanghai(), c)); - if iter.clone().any(|(s, _)| !s) { - let msg = format!( - "\ -EIP-3855 is not supported in one or more of the RPCs used. -Unsupported Chain IDs: {}. -Contracts deployed with a Solidity version equal or higher than 0.8.20 might not work properly. -For more information, please see https://eips.ethereum.org/EIPS/eip-3855", - iter.filter(|(supported, _)| !supported) - .map(|(_, chain)| *chain as u64) - .format(", ") - ); - shell::println(Paint::yellow(msg))?; + // We need to enable tracing to decode contract names: local or external. + let mut builder = ExecutorBuilder::new() + .inspectors(|stack| stack.trace(true)) + .spec(self.config.evm_spec_id()) + .gas_limit(self.evm_opts.gas_limit()); + + if let Some(script_wallets) = script_wallets { + builder = builder.inspectors(|stack| { + stack + .debug(debug) + .cheatcodes( + CheatsConfig::new( + &self.config, + self.evm_opts.clone(), + Some(script_wallets), + ) + .into(), + ) + .enable_isolation(self.evm_opts.isolate) + }); } - Ok(()) + + Ok(ScriptRunner::new( + builder.build(env, db), + self.evm_opts.initial_balance, + self.evm_opts.sender, + )) } } @@ -698,7 +606,7 @@ For more information, please see https://eips.ethereum.org/EIPS/eip-3855", mod tests { use super::*; use foundry_cli::utils::LoadConfig; - use foundry_config::UnresolvedEnvVarError; + use foundry_config::{NamedChain, UnresolvedEnvVarError}; use std::fs; use tempfile::tempdir; diff --git a/crates/script/src/multi_sequence.rs b/crates/script/src/multi_sequence.rs new file mode 100644 index 000000000..ea6dfd0d4 --- /dev/null +++ b/crates/script/src/multi_sequence.rs @@ -0,0 +1,154 @@ +use super::sequence::{sig_to_file_name, ScriptSequence, SensitiveScriptSequence, DRY_RUN_DIR}; +use eyre::{ContextCompat, Result, WrapErr}; +use foundry_cli::utils::now; +use foundry_common::fs; +use foundry_compilers::ArtifactId; +use foundry_config::Config; +use serde::{Deserialize, Serialize}; +use std::{ + io::{BufWriter, Write}, + path::PathBuf, +}; + +/// Holds the sequences of multiple chain deployments. +#[derive(Clone, Default, Serialize, Deserialize)] +pub struct MultiChainSequence { + pub deployments: Vec, + #[serde(skip)] + pub path: PathBuf, + #[serde(skip)] + pub sensitive_path: PathBuf, + pub timestamp: u64, +} + +/// Sensitive values from script sequences. +#[derive(Clone, Default, Serialize, Deserialize)] +pub struct SensitiveMultiChainSequence { + pub deployments: Vec, +} + +impl SensitiveMultiChainSequence { + fn from_multi_sequence(sequence: MultiChainSequence) -> SensitiveMultiChainSequence { + SensitiveMultiChainSequence { + deployments: sequence.deployments.into_iter().map(|sequence| sequence.into()).collect(), + } + } +} + +impl MultiChainSequence { + pub fn new( + deployments: Vec, + sig: &str, + target: &ArtifactId, + config: &Config, + dry_run: bool, + ) -> Result { + let (path, sensitive_path) = MultiChainSequence::get_paths(config, sig, target, dry_run)?; + + Ok(MultiChainSequence { deployments, path, sensitive_path, timestamp: now().as_secs() }) + } + + /// Gets paths in the formats + /// ./broadcast/multi/contract_filename[-timestamp]/sig.json and + /// ./cache/multi/contract_filename[-timestamp]/sig.json + pub fn get_paths( + config: &Config, + sig: &str, + target: &ArtifactId, + dry_run: bool, + ) -> Result<(PathBuf, PathBuf)> { + let mut broadcast = config.broadcast.to_path_buf(); + let mut cache = config.cache_path.to_path_buf(); + let mut common = PathBuf::new(); + + common.push("multi"); + + if dry_run { + common.push(DRY_RUN_DIR); + } + + let target_fname = target + .source + .file_name() + .wrap_err_with(|| format!("No filename for {:?}", target.source))? + .to_string_lossy(); + + common.push(format!("{target_fname}-latest")); + + broadcast.push(common.clone()); + cache.push(common); + + fs::create_dir_all(&broadcast)?; + fs::create_dir_all(&cache)?; + + let filename = format!("{}.json", sig_to_file_name(sig)); + + broadcast.push(filename.clone()); + cache.push(filename); + + Ok((broadcast, cache)) + } + + /// Loads the sequences for the multi chain deployment. + pub fn load(config: &Config, sig: &str, target: &ArtifactId, dry_run: bool) -> Result { + let (path, sensitive_path) = MultiChainSequence::get_paths(config, sig, target, dry_run)?; + let mut sequence: MultiChainSequence = foundry_compilers::utils::read_json_file(&path) + .wrap_err("Multi-chain deployment not found.")?; + let sensitive_sequence: SensitiveMultiChainSequence = + foundry_compilers::utils::read_json_file(&sensitive_path) + .wrap_err("Multi-chain deployment sensitive details not found.")?; + + sequence.deployments.iter_mut().enumerate().for_each(|(i, sequence)| { + sequence.fill_sensitive(&sensitive_sequence.deployments[i]); + }); + + sequence.path = path; + sequence.sensitive_path = sensitive_path; + + Ok(sequence) + } + + /// Saves the transactions as file if it's a standalone deployment. + pub fn save(&mut self, silent: bool, save_ts: bool) -> Result<()> { + self.deployments.iter_mut().for_each(|sequence| sequence.sort_receipts()); + + self.timestamp = now().as_secs(); + + let sensitive_sequence = SensitiveMultiChainSequence::from_multi_sequence(self.clone()); + + // broadcast writes + //../Contract-latest/run.json + let mut writer = BufWriter::new(fs::create_file(&self.path)?); + serde_json::to_writer_pretty(&mut writer, &self)?; + writer.flush()?; + + if save_ts { + //../Contract-[timestamp]/run.json + let path = self.path.to_string_lossy(); + let file = PathBuf::from(&path.replace("-latest", &format!("-{}", self.timestamp))); + fs::create_dir_all(file.parent().unwrap())?; + fs::copy(&self.path, &file)?; + } + + // cache writes + //../Contract-latest/run.json + let mut writer = BufWriter::new(fs::create_file(&self.sensitive_path)?); + serde_json::to_writer_pretty(&mut writer, &sensitive_sequence)?; + writer.flush()?; + + if save_ts { + //../Contract-[timestamp]/run.json + let path = self.sensitive_path.to_string_lossy(); + let file = PathBuf::from(&path.replace("-latest", &format!("-{}", self.timestamp))); + fs::create_dir_all(file.parent().unwrap())?; + fs::copy(&self.sensitive_path, &file)?; + } + + if !silent { + println!("\nTransactions saved to: {}\n", self.path.display()); + println!("Sensitive details saved to: {}\n", self.sensitive_path.display()); + } + + Ok(()) + } +} diff --git a/crates/forge/bin/cmd/script/providers.rs b/crates/script/src/providers.rs similarity index 100% rename from crates/forge/bin/cmd/script/providers.rs rename to crates/script/src/providers.rs diff --git a/crates/forge/bin/cmd/script/receipts.rs b/crates/script/src/receipts.rs similarity index 100% rename from crates/forge/bin/cmd/script/receipts.rs rename to crates/script/src/receipts.rs diff --git a/crates/script/src/resume.rs b/crates/script/src/resume.rs new file mode 100644 index 000000000..4f704ed60 --- /dev/null +++ b/crates/script/src/resume.rs @@ -0,0 +1,106 @@ +use crate::{broadcast::BundledState, simulate::PreSimulationState}; + +use super::{ + multi_sequence::MultiChainSequence, + sequence::{ScriptSequence, ScriptSequenceKind}, +}; +use ethers_providers::Middleware; +use eyre::Result; +use foundry_common::provider::ethers::try_get_http_provider; +use foundry_compilers::artifacts::Libraries; +use std::sync::Arc; + +impl PreSimulationState { + /// Tries loading the resumed state from the cache files, skipping simulation stage. + pub async fn resume(mut self) -> Result { + if self.execution_artifacts.rpc_data.missing_rpc { + eyre::bail!("Missing `--fork-url` field.") + } + + let chain = match self.execution_artifacts.rpc_data.total_rpcs.len() { + 2.. => None, + 1 => { + let fork_url = self.execution_artifacts.rpc_data.total_rpcs.iter().next().unwrap(); + + let provider = Arc::new(try_get_http_provider(fork_url)?); + Some(provider.get_chainid().await?.as_u64()) + } + 0 => eyre::bail!("No RPC URLs"), + }; + + let sequence = match self.try_load_sequence(chain, false) { + Ok(sequence) => sequence, + Err(_) => { + // If the script was simulated, but there was no attempt to broadcast yet, + // try to read the script sequence from the `dry-run/` folder + let mut sequence = self.try_load_sequence(chain, true)?; + + // If sequence was in /dry-run, Update its paths so it is not saved into /dry-run + // this time as we are about to broadcast it. + sequence.update_paths_to_broadcasted( + &self.script_config.config, + &self.args.sig, + &self.build_data.build_data.target, + )?; + + sequence.save(true, true)?; + sequence + } + }; + + match sequence { + ScriptSequenceKind::Single(ref seq) => { + // We might have predeployed libraries from the broadcasting, so we need to + // relink the contracts with them, since their mapping is not included in the solc + // cache files. + self.build_data = self + .build_data + .build_data + .link_with_libraries(Libraries::parse(&seq.libraries)?)?; + } + // Library linking is not supported for multi-chain sequences + ScriptSequenceKind::Multi(_) => {} + } + + let Self { + args, + script_config, + script_wallets, + build_data, + execution_data, + execution_result: _, + execution_artifacts, + } = self; + + Ok(BundledState { + args, + script_config, + script_wallets, + build_data, + execution_data, + execution_artifacts, + sequence, + }) + } + + fn try_load_sequence(&self, chain: Option, dry_run: bool) -> Result { + if let Some(chain) = chain { + let sequence = ScriptSequence::load( + &self.script_config.config, + &self.args.sig, + &self.build_data.build_data.target, + chain, + dry_run, + )?; + Ok(ScriptSequenceKind::Single(sequence)) + } else { + let sequence = MultiChainSequence::load( + &self.script_config.config, + &self.args.sig, + &self.build_data.build_data.target, + dry_run, + )?; + Ok(ScriptSequenceKind::Multi(sequence)) + } + } +} diff --git a/crates/forge/bin/cmd/script/runner.rs b/crates/script/src/runner.rs similarity index 98% rename from crates/forge/bin/cmd/script/runner.rs rename to crates/script/src/runner.rs index 96937bfdb..59f6402d3 100644 --- a/crates/forge/bin/cmd/script/runner.rs +++ b/crates/script/src/runner.rs @@ -1,21 +1,15 @@ use super::ScriptResult; use alloy_primitives::{Address, Bytes, U256}; use eyre::Result; -use forge::{ +use foundry_config::Config; +use foundry_evm::{ constants::CALLER, executors::{CallResult, DeployResult, EvmError, ExecutionErr, Executor, RawCallResult}, revm::interpreter::{return_ok, InstructionResult}, traces::{TraceKind, Traces}, }; -use foundry_config::Config; use yansi::Paint; -/// Represents which simulation stage is the script execution at. -pub enum SimulationStage { - Local, - OnChain, -} - /// Drives script execution #[derive(Debug)] pub struct ScriptRunner { diff --git a/crates/forge/bin/cmd/script/sequence.rs b/crates/script/src/sequence.rs similarity index 72% rename from crates/forge/bin/cmd/script/sequence.rs rename to crates/script/src/sequence.rs index 0e97862bc..6a78d1ac4 100644 --- a/crates/forge/bin/cmd/script/sequence.rs +++ b/crates/script/src/sequence.rs @@ -1,22 +1,19 @@ -use super::NestedValue; -use crate::cmd::{ - init::get_commit_hash, - script::{ - transaction::{wrapper, AdditionalContract, TransactionWithMetadata}, - verify::VerifyBundle, - }, +use super::{multi_sequence::MultiChainSequence, NestedValue}; +use crate::{ + transaction::{wrapper, AdditionalContract, TransactionWithMetadata}, + verify::VerifyBundle, }; use alloy_primitives::{Address, TxHash}; use ethers_core::types::{transaction::eip2718::TypedTransaction, TransactionReceipt}; use eyre::{ContextCompat, Result, WrapErr}; use forge_verify::provider::VerificationProviderType; -use foundry_cli::utils::now; +use foundry_cli::utils::{now, Git}; use foundry_common::{ fs, shell, types::{ToAlloy, ToEthers}, SELECTOR_LEN, }; -use foundry_compilers::{artifacts::Libraries, ArtifactId}; +use foundry_compilers::ArtifactId; use foundry_config::Config; use serde::{Deserialize, Serialize}; use std::{ @@ -26,6 +23,67 @@ use std::{ }; use yansi::Paint; +/// Returns the commit hash of the project if it exists +pub fn get_commit_hash(root: &Path) -> Option { + Git::new(root).commit_hash(true, "HEAD").ok() +} + +pub enum ScriptSequenceKind { + Single(ScriptSequence), + Multi(MultiChainSequence), +} + +impl ScriptSequenceKind { + pub fn save(&mut self, silent: bool, save_ts: bool) -> Result<()> { + match self { + ScriptSequenceKind::Single(sequence) => sequence.save(silent, save_ts), + ScriptSequenceKind::Multi(sequence) => sequence.save(silent, save_ts), + } + } + + pub fn sequences(&self) -> &[ScriptSequence] { + match self { + ScriptSequenceKind::Single(sequence) => std::slice::from_ref(sequence), + ScriptSequenceKind::Multi(sequence) => &sequence.deployments, + } + } + + pub fn sequences_mut(&mut self) -> &mut [ScriptSequence] { + match self { + ScriptSequenceKind::Single(sequence) => std::slice::from_mut(sequence), + ScriptSequenceKind::Multi(sequence) => &mut sequence.deployments, + } + } + /// Updates underlying sequence paths to not be under /dry-run directory. + pub fn update_paths_to_broadcasted( + &mut self, + config: &Config, + sig: &str, + target: &ArtifactId, + ) -> Result<()> { + match self { + ScriptSequenceKind::Single(sequence) => { + sequence.paths = + Some(ScriptSequence::get_paths(config, sig, target, sequence.chain, false)?); + } + ScriptSequenceKind::Multi(sequence) => { + (sequence.path, sequence.sensitive_path) = + MultiChainSequence::get_paths(config, sig, target, false)?; + } + }; + + Ok(()) + } +} + +impl Drop for ScriptSequenceKind { + fn drop(&mut self) { + if let Err(err) = self.save(false, true) { + error!(?err, "could not save deployment sequence"); + } + } +} + pub const DRY_RUN_DIR: &str = "dry-run"; /// Helper that saves the transactions sequence and its state on which transactions have been @@ -38,21 +96,19 @@ pub struct ScriptSequence { pub libraries: Vec, pub pending: Vec, #[serde(skip)] - pub path: PathBuf, - #[serde(skip)] - pub sensitive_path: PathBuf, + /// Contains paths to the sequence files + /// None if sequence should not be saved to disk (e.g. part of a multi-chain sequence) + pub paths: Option<(PathBuf, PathBuf)>, pub returns: HashMap, pub timestamp: u64, pub chain: u64, - /// If `True`, the sequence belongs to a `MultiChainSequence` and won't save to disk as usual. - pub multi: bool, pub commit: Option, } /// Sensitive values from the transactions in a script sequence #[derive(Clone, Default, Serialize, Deserialize)] pub struct SensitiveTransactionMetadata { - pub rpc: Option, + pub rpc: String, } /// Sensitive info from the script sequence which is saved into the cache folder @@ -61,8 +117,8 @@ pub struct SensitiveScriptSequence { pub transactions: VecDeque, } -impl From<&mut ScriptSequence> for SensitiveScriptSequence { - fn from(sequence: &mut ScriptSequence) -> Self { +impl From for SensitiveScriptSequence { + fn from(sequence: ScriptSequence) -> Self { SensitiveScriptSequence { transactions: sequence .transactions @@ -74,59 +130,16 @@ impl From<&mut ScriptSequence> for SensitiveScriptSequence { } impl ScriptSequence { - pub fn new( - transactions: VecDeque, - returns: HashMap, - sig: &str, - target: &ArtifactId, - config: &Config, - broadcasted: bool, - is_multi: bool, - ) -> Result { - let chain = config.chain.unwrap_or_default().id(); - - let (path, sensitive_path) = ScriptSequence::get_paths( - &config.broadcast, - &config.cache_path, - sig, - target, - chain, - broadcasted && !is_multi, - )?; - - let commit = get_commit_hash(&config.__root.0); - - Ok(ScriptSequence { - transactions, - returns, - receipts: vec![], - pending: vec![], - path, - sensitive_path, - timestamp: now().as_secs(), - libraries: vec![], - chain, - multi: is_multi, - commit, - }) - } - /// Loads The sequence for the corresponding json file pub fn load( config: &Config, sig: &str, target: &ArtifactId, chain_id: u64, - broadcasted: bool, + dry_run: bool, ) -> Result { - let (path, sensitive_path) = ScriptSequence::get_paths( - &config.broadcast, - &config.cache_path, - sig, - target, - chain_id, - broadcasted, - )?; + let (path, sensitive_path) = + ScriptSequence::get_paths(config, sig, target, chain_id, dry_run)?; let mut script_sequence: Self = foundry_compilers::utils::read_json_file(&path) .wrap_err(format!("Deployment not found for chain `{chain_id}`."))?; @@ -138,41 +151,52 @@ impl ScriptSequence { script_sequence.fill_sensitive(&sensitive_script_sequence); - script_sequence.path = path; - script_sequence.sensitive_path = sensitive_path; + script_sequence.paths = Some((path, sensitive_path)); Ok(script_sequence) } /// Saves the transactions as file if it's a standalone deployment. - pub fn save(&mut self) -> Result<()> { - if self.multi || self.transactions.is_empty() { + /// `save_ts` should be set to true for checkpoint updates, which might happen many times and + /// could result in us saving many identical files. + pub fn save(&mut self, silent: bool, save_ts: bool) -> Result<()> { + self.sort_receipts(); + + if self.transactions.is_empty() { return Ok(()) } + let Some((path, sensitive_path)) = self.paths.clone() else { return Ok(()) }; + self.timestamp = now().as_secs(); let ts_name = format!("run-{}.json", self.timestamp); - let sensitive_script_sequence: SensitiveScriptSequence = self.into(); + let sensitive_script_sequence: SensitiveScriptSequence = self.clone().into(); // broadcast folder writes //../run-latest.json - let mut writer = BufWriter::new(fs::create_file(&self.path)?); + let mut writer = BufWriter::new(fs::create_file(&path)?); serde_json::to_writer_pretty(&mut writer, &self)?; writer.flush()?; - //../run-[timestamp].json - fs::copy(&self.path, self.path.with_file_name(&ts_name))?; + if save_ts { + //../run-[timestamp].json + fs::copy(&path, path.with_file_name(&ts_name))?; + } // cache folder writes //../run-latest.json - let mut writer = BufWriter::new(fs::create_file(&self.sensitive_path)?); + let mut writer = BufWriter::new(fs::create_file(&sensitive_path)?); serde_json::to_writer_pretty(&mut writer, &sensitive_script_sequence)?; writer.flush()?; - //../run-[timestamp].json - fs::copy(&self.sensitive_path, self.sensitive_path.with_file_name(&ts_name))?; + if save_ts { + //../run-[timestamp].json + fs::copy(&sensitive_path, sensitive_path.with_file_name(&ts_name))?; + } - shell::println(format!("\nTransactions saved to: {}\n", self.path.display()))?; - shell::println(format!("Sensitive values saved to: {}\n", self.sensitive_path.display()))?; + if !silent { + shell::println(format!("\nTransactions saved to: {}\n", path.display()))?; + shell::println(format!("Sensitive values saved to: {}\n", sensitive_path.display()))?; + } Ok(()) } @@ -197,36 +221,24 @@ impl ScriptSequence { self.pending.retain(|element| element != &tx_hash); } - pub fn add_libraries(&mut self, libraries: Libraries) { - self.libraries = libraries - .libs - .iter() - .flat_map(|(file, libs)| { - libs.iter() - .map(|(name, address)| format!("{}:{name}:{address}", file.to_string_lossy())) - }) - .collect(); - } - /// Gets paths in the formats /// ./broadcast/[contract_filename]/[chain_id]/[sig]-[timestamp].json and /// ./cache/[contract_filename]/[chain_id]/[sig]-[timestamp].json pub fn get_paths( - broadcast: &Path, - cache: &Path, + config: &Config, sig: &str, target: &ArtifactId, chain_id: u64, - broadcasted: bool, + dry_run: bool, ) -> Result<(PathBuf, PathBuf)> { - let mut broadcast = broadcast.to_path_buf(); - let mut cache = cache.to_path_buf(); + let mut broadcast = config.broadcast.to_path_buf(); + let mut cache = config.cache_path.to_path_buf(); let mut common = PathBuf::new(); let target_fname = target.source.file_name().wrap_err("No filename.")?; common.push(target_fname); common.push(chain_id.to_string()); - if !broadcasted { + if dry_run { common.push(DRY_RUN_DIR); } @@ -245,20 +257,6 @@ impl ScriptSequence { Ok((broadcast, cache)) } - /// Checks that there is an Etherscan key for the chain id of this sequence. - pub fn verify_preflight_check(&self, config: &Config, verify: &VerifyBundle) -> Result<()> { - if config.get_etherscan_api_key(Some(self.chain.into())).is_none() && - verify.verifier.verifier == VerificationProviderType::Etherscan - { - eyre::bail!( - "Etherscan API key wasn't found for chain id {}. On-chain execution aborted", - self.chain - ) - } - - Ok(()) - } - /// Given the broadcast log, it matches transactions with receipts, and tries to verify any /// created contract on etherscan. pub async fn verify_contracts( @@ -354,8 +352,8 @@ impl ScriptSequence { } /// Returns the first RPC URL of this sequence. - pub fn rpc_url(&self) -> Option<&str> { - self.transactions.front().and_then(|tx| tx.rpc.as_deref()) + pub fn rpc_url(&self) -> &str { + self.transactions.front().expect("empty sequence").rpc.as_str() } /// Returns the list of the transactions without the metadata. @@ -371,13 +369,6 @@ impl ScriptSequence { } } -impl Drop for ScriptSequence { - fn drop(&mut self) { - self.sort_receipts(); - self.save().expect("not able to save deployment sequence"); - } -} - /// Converts the `sig` argument into the corresponding file path. /// /// This accepts either the signature of the function or the raw calldata diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs new file mode 100644 index 000000000..99bd39ad9 --- /dev/null +++ b/crates/script/src/simulate.rs @@ -0,0 +1,455 @@ +use super::{ + artifacts::ArtifactInfo, + multi_sequence::MultiChainSequence, + providers::ProvidersManager, + runner::ScriptRunner, + sequence::{ScriptSequence, ScriptSequenceKind}, + transaction::TransactionWithMetadata, +}; +use crate::{ + broadcast::{estimate_gas, BundledState}, + build::LinkedBuildData, + execute::{ExecutionArtifacts, ExecutionData}, + sequence::get_commit_hash, + ScriptArgs, ScriptConfig, ScriptResult, +}; +use alloy_primitives::{utils::format_units, Address, U256}; +use eyre::{Context, Result}; +use foundry_cheatcodes::{BroadcastableTransactions, ScriptWallets}; +use foundry_cli::utils::{has_different_gas_calc, now}; +use foundry_common::{ + get_contract_name, provider::ethers::RpcUrl, shell, types::ToAlloy, ContractsByArtifact, +}; +use foundry_evm::traces::render_trace_arena; +use futures::future::join_all; +use parking_lot::RwLock; +use std::{ + collections::{BTreeMap, HashMap, VecDeque}, + sync::Arc, +}; + +/// Same as [ExecutedState], but also contains [ExecutionArtifacts] which are obtained from +/// [ScriptResult]. +/// +/// Can be either converted directly to [BundledState] via [PreSimulationState::resume] or driven to +/// it through [FilledTransactionsState]. +pub struct PreSimulationState { + pub args: ScriptArgs, + pub script_config: ScriptConfig, + pub script_wallets: ScriptWallets, + pub build_data: LinkedBuildData, + pub execution_data: ExecutionData, + pub execution_result: ScriptResult, + pub execution_artifacts: ExecutionArtifacts, +} + +impl PreSimulationState { + /// If simulation is enabled, simulates transactions against fork and fills gas estimation and + /// metadata. Otherwise, metadata (e.g. additional contracts, created contract names) is + /// left empty. + /// + /// Both modes will panic if any of the transactions have None for the `rpc` field. + pub async fn fill_metadata(self) -> Result { + let transactions = if let Some(txs) = self.execution_result.transactions.as_ref() { + if self.args.skip_simulation { + shell::println("\nSKIPPING ON CHAIN SIMULATION.")?; + self.no_simulation(txs.clone())? + } else { + self.onchain_simulation(txs.clone()).await? + } + } else { + VecDeque::new() + }; + + Ok(FilledTransactionsState { + args: self.args, + script_config: self.script_config, + script_wallets: self.script_wallets, + build_data: self.build_data, + execution_data: self.execution_data, + execution_artifacts: self.execution_artifacts, + transactions, + }) + } + + /// Builds separate runners and environments for each RPC used in script and executes all + /// transactions in those environments. + /// + /// Collects gas usage and metadata for each transaction. + pub async fn onchain_simulation( + &self, + transactions: BroadcastableTransactions, + ) -> Result> { + trace!(target: "script", "executing onchain simulation"); + + let runners = Arc::new( + self.build_runners() + .await? + .into_iter() + .map(|(rpc, runner)| (rpc, Arc::new(RwLock::new(runner)))) + .collect::>(), + ); + + let contracts = self.build_data.get_flattened_contracts(false); + let address_to_abi: BTreeMap = + self.build_address_to_abi_map(&contracts); + + let mut final_txs = VecDeque::new(); + + // Executes all transactions from the different forks concurrently. + let futs = transactions + .into_iter() + .map(|transaction| async { + let rpc = transaction.rpc.expect("missing broadcastable tx rpc url"); + let mut runner = runners.get(&rpc).expect("invalid rpc url").write(); + + let mut tx = transaction.transaction; + let result = runner + .simulate( + tx.from + .expect("transaction doesn't have a `from` address at execution time"), + tx.to, + tx.input.clone().into_input(), + tx.value, + ) + .wrap_err("Internal EVM error during simulation")?; + + if !result.success { + return Ok((None, result.traces)); + } + + let created_contracts = result.get_created_contracts(); + + // Simulate mining the transaction if the user passes `--slow`. + if self.args.slow { + runner.executor.env.block.number += U256::from(1); + } + + let is_fixed_gas_limit = tx.gas.is_some(); + match tx.gas { + // If tx.gas is already set that means it was specified in script + Some(gas) => { + println!("Gas limit was set in script to {gas}"); + } + // We inflate the gas used by the user specified percentage + None => { + let gas = + U256::from(result.gas_used * self.args.gas_estimate_multiplier / 100); + tx.gas = Some(gas); + } + } + let tx = TransactionWithMetadata::new( + tx, + rpc, + &result, + &address_to_abi, + &self.execution_artifacts.decoder, + created_contracts, + is_fixed_gas_limit, + )?; + + eyre::Ok((Some(tx), result.traces)) + }) + .collect::>(); + + if self.script_config.evm_opts.verbosity > 3 { + println!("=========================="); + println!("Simulated On-chain Traces:\n"); + } + + let mut abort = false; + for res in join_all(futs).await { + let (tx, traces) = res?; + + // Transaction will be `None`, if execution didn't pass. + if tx.is_none() || self.script_config.evm_opts.verbosity > 3 { + for (_, trace) in &traces { + println!( + "{}", + render_trace_arena(trace, &self.execution_artifacts.decoder).await? + ); + } + } + + if let Some(tx) = tx { + final_txs.push_back(tx); + } else { + abort = true; + } + } + + if abort { + eyre::bail!("Simulated execution failed.") + } + + Ok(final_txs) + } + + /// Build mapping from contract address to its ABI, code and contract name. + fn build_address_to_abi_map<'a>( + &self, + contracts: &'a ContractsByArtifact, + ) -> BTreeMap> { + self.execution_artifacts + .decoder + .contracts + .iter() + .filter_map(move |(addr, contract_id)| { + let contract_name = get_contract_name(contract_id); + if let Ok(Some((_, (abi, code)))) = + contracts.find_by_name_or_identifier(contract_name) + { + let info = ArtifactInfo { + contract_name: contract_name.to_string(), + contract_id: contract_id.to_string(), + abi, + code, + }; + return Some((*addr, info)); + } + None + }) + .collect() + } + + /// Build [ScriptRunner] forking given RPC for each RPC used in the script. + async fn build_runners(&self) -> Result> { + let rpcs = self.execution_artifacts.rpc_data.total_rpcs.clone(); + if !shell::verbosity().is_silent() { + let n = rpcs.len(); + let s = if n != 1 { "s" } else { "" }; + println!("\n## Setting up {n} EVM{s}."); + } + + let futs = rpcs + .into_iter() + .map(|rpc| async move { + let mut script_config = self.script_config.clone(); + script_config.evm_opts.fork_url = Some(rpc.clone()); + let runner = script_config.get_runner().await?; + Ok((rpc.clone(), runner)) + }) + .collect::>(); + + join_all(futs).await.into_iter().collect() + } + + /// If simulation is disabled, converts transactions into [TransactionWithMetadata] type + /// skipping metadata filling. + fn no_simulation( + &self, + transactions: BroadcastableTransactions, + ) -> Result> { + Ok(transactions + .into_iter() + .map(|btx| { + let mut tx = TransactionWithMetadata::from_tx_request(btx.transaction); + tx.rpc = btx.rpc.expect("missing broadcastable tx rpc url"); + tx + }) + .collect()) + } +} + +/// At this point we have converted transactions collected during script execution to +/// [TransactionWithMetadata] objects which contain additional metadata needed for broadcasting and +/// verification. +pub struct FilledTransactionsState { + pub args: ScriptArgs, + pub script_config: ScriptConfig, + pub script_wallets: ScriptWallets, + pub build_data: LinkedBuildData, + pub execution_data: ExecutionData, + pub execution_artifacts: ExecutionArtifacts, + pub transactions: VecDeque, +} + +impl FilledTransactionsState { + /// Bundles all transactions of the [`TransactionWithMetadata`] type in a list of + /// [`ScriptSequence`]. List length will be higher than 1, if we're dealing with a multi + /// chain deployment. + /// + /// Each transaction will be added with the correct transaction type and gas estimation. + pub async fn bundle(self) -> Result { + let is_multi_deployment = self.execution_artifacts.rpc_data.total_rpcs.len() > 1; + + if is_multi_deployment && !self.build_data.libraries.is_empty() { + eyre::bail!("Multi-chain deployment is not supported with libraries."); + } + + let mut total_gas_per_rpc: HashMap = HashMap::new(); + + // Batches sequence of transactions from different rpcs. + let mut new_sequence = VecDeque::new(); + let mut manager = ProvidersManager::default(); + let mut sequences = vec![]; + + // Peeking is used to check if the next rpc url is different. If so, it creates a + // [`ScriptSequence`] from all the collected transactions up to this point. + let mut txes_iter = self.transactions.clone().into_iter().peekable(); + + while let Some(mut tx) = txes_iter.next() { + let tx_rpc = tx.rpc.clone(); + let provider_info = manager.get_or_init_provider(&tx.rpc, self.args.legacy).await?; + + // Handles chain specific requirements. + tx.change_type(provider_info.is_legacy); + tx.transaction.set_chain_id(provider_info.chain); + + if !self.args.skip_simulation { + let typed_tx = tx.typed_tx_mut(); + + if has_different_gas_calc(provider_info.chain) { + trace!("estimating with different gas calculation"); + let gas = *typed_tx.gas().expect("gas is set by simulation."); + + // We are trying to show the user an estimation of the total gas usage. + // + // However, some transactions might depend on previous ones. For + // example, tx1 might deploy a contract that tx2 uses. That + // will result in the following `estimate_gas` call to fail, + // since tx1 hasn't been broadcasted yet. + // + // Not exiting here will not be a problem when actually broadcasting, because + // for chains where `has_different_gas_calc` returns true, + // we await each transaction before broadcasting the next + // one. + if let Err(err) = estimate_gas( + typed_tx, + &provider_info.provider, + self.args.gas_estimate_multiplier, + ) + .await + { + trace!("gas estimation failed: {err}"); + + // Restore gas value, since `estimate_gas` will remove it. + typed_tx.set_gas(gas); + } + } + + let total_gas = total_gas_per_rpc.entry(tx_rpc.clone()).or_insert(U256::ZERO); + *total_gas += (*typed_tx.gas().expect("gas is set")).to_alloy(); + } + + new_sequence.push_back(tx); + // We only create a [`ScriptSequence`] object when we collect all the rpc related + // transactions. + if let Some(next_tx) = txes_iter.peek() { + if next_tx.rpc == tx_rpc { + continue; + } + } + + let sequence = + self.create_sequence(is_multi_deployment, provider_info.chain, new_sequence)?; + + sequences.push(sequence); + + new_sequence = VecDeque::new(); + } + + if !self.args.skip_simulation { + // Present gas information on a per RPC basis. + for (rpc, total_gas) in total_gas_per_rpc { + let provider_info = manager.get(&rpc).expect("provider is set."); + + // We don't store it in the transactions, since we want the most updated value. + // Right before broadcasting. + let per_gas = if let Some(gas_price) = self.args.with_gas_price { + gas_price + } else { + provider_info.gas_price()? + }; + + shell::println("\n==========================")?; + shell::println(format!("\nChain {}", provider_info.chain))?; + + shell::println(format!( + "\nEstimated gas price: {} gwei", + format_units(per_gas, 9) + .unwrap_or_else(|_| "[Could not calculate]".to_string()) + .trim_end_matches('0') + .trim_end_matches('.') + ))?; + shell::println(format!("\nEstimated total gas used for script: {total_gas}"))?; + shell::println(format!( + "\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') + ))?; + shell::println("\n==========================")?; + } + } + + let sequence = if sequences.len() == 1 { + ScriptSequenceKind::Single(sequences.pop().expect("empty sequences")) + } else { + ScriptSequenceKind::Multi(MultiChainSequence::new( + sequences, + &self.args.sig, + &self.build_data.build_data.target, + &self.script_config.config, + !self.args.broadcast, + )?) + }; + + Ok(BundledState { + args: self.args, + script_config: self.script_config, + script_wallets: self.script_wallets, + build_data: self.build_data, + execution_data: self.execution_data, + execution_artifacts: self.execution_artifacts, + sequence, + }) + } + + /// Creates a [ScriptSequence] object from the given transactions. + fn create_sequence( + &self, + multi: bool, + chain: u64, + transactions: VecDeque, + ) -> Result { + // Paths are set to None for multi-chain sequences parts, because they don't need to be + // saved to a separate file. + let paths = if multi { + None + } else { + Some(ScriptSequence::get_paths( + &self.script_config.config, + &self.args.sig, + &self.build_data.build_data.target, + chain, + !self.args.broadcast, + )?) + }; + + let commit = get_commit_hash(&self.script_config.config.__root.0); + + let libraries = self + .build_data + .libraries + .libs + .iter() + .flat_map(|(file, libs)| { + libs.iter() + .map(|(name, address)| format!("{}:{name}:{address}", file.to_string_lossy())) + }) + .collect(); + + Ok(ScriptSequence { + transactions, + returns: self.execution_artifacts.returns.clone(), + receipts: vec![], + pending: vec![], + paths, + timestamp: now().as_secs(), + libraries, + chain, + commit, + }) + } +} diff --git a/crates/forge/bin/cmd/script/transaction.rs b/crates/script/src/transaction.rs similarity index 98% rename from crates/forge/bin/cmd/script/transaction.rs rename to crates/script/src/transaction.rs index fec73b22f..3f92e2f31 100644 --- a/crates/forge/bin/cmd/script/transaction.rs +++ b/crates/script/src/transaction.rs @@ -44,7 +44,7 @@ pub struct TransactionWithMetadata { #[serde(default = "default_vec_of_strings")] pub arguments: Option>, #[serde(skip)] - pub rpc: Option, + pub rpc: RpcUrl, pub transaction: TypedTransaction, pub additional_contracts: Vec, pub is_fixed_gas_limit: bool, @@ -80,7 +80,7 @@ impl TransactionWithMetadata { pub fn new( transaction: TransactionRequest, - rpc: Option, + rpc: RpcUrl, result: &ScriptResult, local_contracts: &BTreeMap, decoder: &CallTraceDecoder, @@ -195,6 +195,7 @@ impl TransactionWithMetadata { decoder: &CallTraceDecoder, ) -> Result<()> { self.opcode = CallKind::Call; + self.contract_address = Some(target); let Some(data) = self.transaction.data() else { return Ok(()) }; if data.len() < SELECTOR_LEN { @@ -211,10 +212,6 @@ impl TransactionWithMetadata { decoder.functions.get(selector).and_then(|v| v.first()) }; if let Some(function) = function { - if self.contract_address.is_none() { - self.contract_name = decoder.contracts.get(&target).cloned(); - } - self.function = Some(function.signature()); let values = function.abi_decode_input(data, false).map_err(|e| { @@ -229,15 +226,9 @@ impl TransactionWithMetadata { self.arguments = Some(values.iter().map(format_token_raw).collect()); } - self.contract_address = Some(target); - Ok(()) } - pub fn set_tx(&mut self, tx: TypedTransaction) { - self.transaction = tx; - } - pub fn change_type(&mut self, is_legacy: bool) { self.transaction = if is_legacy { TypedTransaction::Legacy(self.transaction.clone().into()) diff --git a/crates/forge/bin/cmd/script/verify.rs b/crates/script/src/verify.rs similarity index 80% rename from crates/forge/bin/cmd/script/verify.rs rename to crates/script/src/verify.rs index 43293268d..be5825dfc 100644 --- a/crates/forge/bin/cmd/script/verify.rs +++ b/crates/script/src/verify.rs @@ -1,4 +1,12 @@ +use crate::{ + build::LinkedBuildData, + execute::{ExecutionArtifacts, ExecutionData}, + sequence::ScriptSequenceKind, + ScriptArgs, ScriptConfig, +}; + use alloy_primitives::Address; +use eyre::Result; use forge_verify::{RetryArgs, VerifierArgs, VerifyArgs}; use foundry_cli::opts::{EtherscanOpts, ProjectPathsArgs}; use foundry_common::ContractsByArtifact; @@ -6,6 +14,38 @@ use foundry_compilers::{info::ContractInfo, Project}; use foundry_config::{Chain, Config}; use semver::Version; +/// State after we have broadcasted the script. +/// It is assumed that at this point [BroadcastedState::sequence] contains receipts for all +/// broadcasted transactions. +pub struct BroadcastedState { + pub args: ScriptArgs, + pub script_config: ScriptConfig, + pub build_data: LinkedBuildData, + pub execution_data: ExecutionData, + pub execution_artifacts: ExecutionArtifacts, + pub sequence: ScriptSequenceKind, +} + +impl BroadcastedState { + pub async fn verify(self) -> Result<()> { + let Self { args, script_config, build_data, mut sequence, .. } = self; + + let verify = VerifyBundle::new( + &script_config.config.project()?, + &script_config.config, + build_data.get_flattened_contracts(false), + args.retry, + args.verifier, + ); + + for sequence in sequence.sequences_mut() { + sequence.verify_contracts(&script_config.config, verify.clone()).await?; + } + + Ok(()) + } +} + /// Data struct to help `ScriptSequence` verify contracts on `etherscan`. #[derive(Clone)] pub struct VerifyBundle { From 345858f98af6ab8e4fdb4b92f40d4d67b94d5478 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Fri, 8 Mar 2024 08:36:14 -0500 Subject: [PATCH 043/430] feat(anvil): configure slots_in_an_epoch (#7335) * feat(anvil): configure slots_in_an_epoch * Option nit Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * nit: use primitive u64 * nit: semicolon * nits Co-authored-by: Matthias Seitz * nit: anvil config --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: Matthias Seitz --- crates/anvil/src/cmd.rs | 5 +++++ crates/anvil/src/config.rs | 10 ++++++++++ crates/anvil/src/eth/backend/mem/mod.rs | 21 ++++++++++----------- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index fca14e75d..c16328ee2 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -86,6 +86,10 @@ pub struct NodeArgs { #[arg(short, long, visible_alias = "blockTime", value_name = "SECONDS")] pub block_time: Option, + /// Slots in an epoch + #[arg(long, value_name = "SLOTS_IN_AN_EPOCH", default_value_t = 32)] + pub slots_in_an_epoch: u64, + /// Writes output of `anvil` as json to user-specified file. #[arg(long, value_name = "OUT_FILE")] pub config_out: Option, @@ -230,6 +234,7 @@ impl NodeArgs { .with_transaction_block_keeper(self.transaction_block_keeper) .with_optimism(self.evm_opts.optimism) .with_disable_default_create2_deployer(self.evm_opts.disable_default_create2_deployer) + .with_slots_in_an_epoch(self.slots_in_an_epoch) } fn account_generator(&self) -> AccountGenerator { diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 32db9e25f..7c1098ec7 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -170,6 +170,8 @@ pub struct NodeConfig { pub disable_default_create2_deployer: bool, /// Enable Optimism deposit transaction pub enable_optimism: bool, + /// Slots in an epoch + pub slots_in_an_epoch: u64, } impl NodeConfig { @@ -404,6 +406,7 @@ impl Default for NodeConfig { transaction_block_keeper: None, disable_default_create2_deployer: false, enable_optimism: false, + slots_in_an_epoch: 32, } } } @@ -596,6 +599,13 @@ impl NodeConfig { self } + /// Sets the slots in an epoch + #[must_use] + pub fn with_slots_in_an_epoch(mut self, slots_in_an_epoch: u64) -> Self { + self.slots_in_an_epoch = slots_in_an_epoch; + self + } + /// Sets the port to use #[must_use] pub fn with_port(mut self, port: u16) -> Self { diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 202cc880e..64e694255 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -168,6 +168,8 @@ pub struct Backend { /// max number of blocks with transactions in memory transaction_block_keeper: Option, node_config: Arc>, + /// Slots in an epoch + slots_in_an_epoch: u64, } impl Backend { @@ -214,6 +216,8 @@ impl Backend { Default::default() }; + let slots_in_an_epoch = node_config.read().await.slots_in_an_epoch; + let backend = Self { db, blockchain, @@ -230,6 +234,7 @@ impl Backend { prune_state_history_config, transaction_block_keeper, node_config, + slots_in_an_epoch, }; if let Some(interval_block_time) = automine_block_time { @@ -1478,7 +1483,7 @@ impl Backend { BlockId::Hash(hash) => hash.block_hash, BlockId::Number(number) => { let storage = self.blockchain.storage.read(); - let slots_in_an_epoch = U64::from(32u64); + let slots_in_an_epoch = U64::from(self.slots_in_an_epoch); match number { BlockNumber::Latest => storage.best_hash, BlockNumber::Earliest => storage.genesis_hash, @@ -1599,7 +1604,6 @@ impl Backend { block_id: Option, ) -> Result { let current = self.best_number(); - let slots_in_an_epoch = 32u64; let requested = match block_id.map(Into::into).unwrap_or(BlockId::Number(BlockNumber::Latest)) { BlockId::Hash(hash) => self @@ -1614,12 +1618,8 @@ impl Backend { BlockNumber::Latest | BlockNumber::Pending => self.best_number(), BlockNumber::Earliest => U64::ZERO.to::(), BlockNumber::Number(num) => num, - BlockNumber::Safe => { - U64::from(current).saturating_sub(U64::from(slots_in_an_epoch)).to::() - } - BlockNumber::Finalized => U64::from(current) - .saturating_sub(U64::from(slots_in_an_epoch) * U64::from(2)) - .to::(), + BlockNumber::Safe => current.saturating_sub(self.slots_in_an_epoch), + BlockNumber::Finalized => current.saturating_sub(self.slots_in_an_epoch * 2), }, }; @@ -1632,13 +1632,12 @@ impl Backend { pub fn convert_block_number(&self, block: Option) -> u64 { let current = self.best_number(); - let slots_in_an_epoch = 32u64; match block.unwrap_or(BlockNumber::Latest) { BlockNumber::Latest | BlockNumber::Pending => current, BlockNumber::Earliest => 0, BlockNumber::Number(num) => num, - BlockNumber::Safe => current.saturating_sub(slots_in_an_epoch), - BlockNumber::Finalized => current.saturating_sub(slots_in_an_epoch * 2), + BlockNumber::Safe => current.saturating_sub(self.slots_in_an_epoch), + BlockNumber::Finalized => current.saturating_sub(self.slots_in_an_epoch * 2), } } From 12cbf67711d312b77a3b895df67d3c733cc6a3e2 Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Fri, 8 Mar 2024 14:44:26 +0100 Subject: [PATCH 044/430] evaluate .env in Anvil, like we do in other binaries (#7344) --- Cargo.lock | 1 + crates/anvil/Cargo.toml | 13 +++++++++++-- crates/anvil/src/anvil.rs | 4 ++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8bea54c23..67bca0286 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -545,6 +545,7 @@ dependencies = [ "eyre", "fdlimit", "flate2", + "foundry-cli", "foundry-common", "foundry-config", "foundry-evm", diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index ff59985f0..36607d126 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -16,13 +16,18 @@ path = "src/anvil.rs" required-features = ["cli"] [build-dependencies] -vergen = { workspace = true, default-features = false, features = ["build", "git", "gitcl"] } +vergen = { workspace = true, default-features = false, features = [ + "build", + "git", + "gitcl", +] } [dependencies] # foundry internal anvil-core = { path = "core", features = ["serde", "impersonated-tx"] } anvil-rpc = { path = "rpc" } anvil-server = { path = "server" } +foundry-cli.workspace = true foundry-common.workspace = true foundry-config.workspace = true foundry-evm.workspace = true @@ -76,7 +81,11 @@ rand = "0.8" eyre.workspace = true # cli -clap = { version = "4", features = ["derive", "env", "wrap_help"], optional = true } +clap = { version = "4", features = [ + "derive", + "env", + "wrap_help", +], optional = true } clap_complete = { version = "4", optional = true } chrono.workspace = true auto_impl = "1" diff --git a/crates/anvil/src/anvil.rs b/crates/anvil/src/anvil.rs index cc60fe722..5ac169c3a 100644 --- a/crates/anvil/src/anvil.rs +++ b/crates/anvil/src/anvil.rs @@ -1,6 +1,8 @@ //! The `anvil` cli + use anvil::cmd::NodeArgs; use clap::{CommandFactory, Parser, Subcommand}; +use foundry_cli::utils; /// A fast local Ethereum development node. #[derive(Parser)] @@ -29,6 +31,8 @@ pub enum AnvilSubcommand { #[tokio::main] async fn main() -> Result<(), Box> { + utils::load_dotenv(); + let mut app = Anvil::parse(); app.node.evm_opts.resolve_rpc_alias(); From 7bd5b35885c01a6e2e712b23fdd066100c97d54e Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 8 Mar 2024 15:14:30 +0100 Subject: [PATCH 045/430] fix: remove constructor when generating interface (#7341) --- crates/cast/src/lib.rs | 12 +- crates/cast/tests/cli/main.rs | 34 ++++++ crates/cast/tests/fixtures/interface.json | 141 ++++++++++++++++++++++ 3 files changed, 184 insertions(+), 3 deletions(-) create mode 100644 crates/cast/tests/fixtures/interface.json diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index da7453f52..8a0e34c94 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -1609,7 +1609,10 @@ impl SimpleCast { /// Generates an interface in solidity from either a local file ABI or a verified contract on /// Etherscan. It returns a vector of [`InterfaceSource`] structs that contain the source of the /// interface and their name. - /// ```ignore + /// + /// Note: This removes the constructor from the ABI before generating the interface. + /// + /// ```no_run /// use cast::{AbiPath, SimpleCast as Cast}; /// # async fn foo() -> eyre::Result<()> { /// let path = @@ -1620,7 +1623,7 @@ impl SimpleCast { /// # } /// ``` pub async fn generate_interface(address_or_path: AbiPath) -> Result> { - let (contract_abis, contract_names) = match address_or_path { + let (mut contract_abis, contract_names) = match address_or_path { AbiPath::Local { path, name } => { let file = std::fs::read_to_string(&path).wrap_err("unable to read abi file")?; let obj: ContractObject = serde_json::from_str(&file)?; @@ -1643,9 +1646,12 @@ impl SimpleCast { } }; contract_abis - .iter() + .iter_mut() .zip(contract_names) .map(|(contract_abi, name)| { + // need to filter out the constructor + contract_abi.constructor.take(); + let source = foundry_cli::utils::abi_to_solidity(contract_abi, &name)?; Ok(InterfaceSource { name, diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 41cef5e61..7df9d4b91 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -757,3 +757,37 @@ casttest!(balance, |_prj, cmd| { assert_ne!(usdt_result, "0x0000000000000000000000000000000000000000000000000000000000000000"); assert_eq!(alias_result, usdt_result); }); + +// tests that `cast interface` excludes the constructor +// +casttest!(interface_no_constructor, |prj, cmd| { + let interface = include_str!("../fixtures/interface.json"); + + let path = prj.root().join("interface.json"); + fs::write(&path, interface).unwrap(); + // Call `cast find-block` + cmd.args(["interface"]).arg(&path); + let output = cmd.stdout_lossy(); + + let s = r#"// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.4; + +interface Interface { + type SpendAssetsHandleType is uint8; + + function getIntegrationManager() external view returns (address integrationManager_); + function lend(address _vaultProxy, bytes memory, bytes memory _assetData) external; + function parseAssetsForAction(address, bytes4 _selector, bytes memory _actionData) + external + view + returns ( + SpendAssetsHandleType spendAssetsHandleType_, + address[] memory spendAssets_, + uint256[] memory spendAssetAmounts_, + address[] memory incomingAssets_, + uint256[] memory minIncomingAssetAmounts_ + ); + function redeem(address _vaultProxy, bytes memory, bytes memory _assetData) external; +}"#; + assert_eq!(output.trim(), s); +}); diff --git a/crates/cast/tests/fixtures/interface.json b/crates/cast/tests/fixtures/interface.json new file mode 100644 index 000000000..73e561886 --- /dev/null +++ b/crates/cast/tests/fixtures/interface.json @@ -0,0 +1,141 @@ +[ + { + "type": "constructor", + "inputs": [ + { + "name": "_integrationManager", + "type": "address", + "internalType": "address" + }, + { + "name": "_addressListRegistry", + "type": "address", + "internalType": "address" + }, + { + "name": "_aTokenListId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "_pool", + "type": "address", + "internalType": "address" + }, + { + "name": "_referralCode", + "type": "uint16", + "internalType": "uint16" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "getIntegrationManager", + "inputs": [], + "outputs": [ + { + "name": "integrationManager_", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "lend", + "inputs": [ + { + "name": "_vaultProxy", + "type": "address", + "internalType": "address" + }, + { + "name": "", + "type": "bytes", + "internalType": "bytes" + }, + { + "name": "_assetData", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "parseAssetsForAction", + "inputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + }, + { + "name": "_selector", + "type": "bytes4", + "internalType": "bytes4" + }, + { + "name": "_actionData", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [ + { + "name": "spendAssetsHandleType_", + "type": "uint8", + "internalType": "enum IIntegrationManager.SpendAssetsHandleType" + }, + { + "name": "spendAssets_", + "type": "address[]", + "internalType": "address[]" + }, + { + "name": "spendAssetAmounts_", + "type": "uint256[]", + "internalType": "uint256[]" + }, + { + "name": "incomingAssets_", + "type": "address[]", + "internalType": "address[]" + }, + { + "name": "minIncomingAssetAmounts_", + "type": "uint256[]", + "internalType": "uint256[]" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "redeem", + "inputs": [ + { + "name": "_vaultProxy", + "type": "address", + "internalType": "address" + }, + { + "name": "", + "type": "bytes", + "internalType": "bytes" + }, + { + "name": "_assetData", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + } +] \ No newline at end of file From 9fde758a97a828efdcc4937ecfea4369b9850be8 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 8 Mar 2024 15:16:13 +0100 Subject: [PATCH 046/430] chore(deps): bump revm to 6.0 (#7125) * migrate revm * inspector * more inspectors * port more things * port more * some simplifications * chore: add error variant back * use original bytecode * bump deps * feat: migrate evm backend * chore: port more stuf * fix compile * chore: make compile again * fix * more fixes * fix anvil * fix cast * chore(clippy): make clippy happy * fix: make it fucking compile * nits, clippies * fix: try early return None from inspector * chore: finish data -> context renaming * chore: really finish rename * chore: rename context to ecx Shorter * feat: simpler methods * fix anvil test * fix env bug * chore: rename functions * chore: migrate disallow mem write * Update Cargo.toml Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix: update exc env after transact * no clone * no clone --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- Cargo.lock | 62 +-- Cargo.toml | 49 +- crates/anvil/src/config.rs | 25 +- crates/anvil/src/eth/api.rs | 6 +- crates/anvil/src/eth/backend/executor.rs | 81 ++-- crates/anvil/src/eth/backend/mem/inspector.rs | 105 ++--- crates/anvil/src/eth/backend/mem/mod.rs | 147 +++--- crates/anvil/src/eth/backend/validate.rs | 6 +- crates/anvil/src/eth/error.rs | 6 +- crates/anvil/src/eth/util.rs | 33 +- crates/cast/bin/cmd/run.rs | 4 + crates/cheatcodes/src/error.rs | 8 + crates/cheatcodes/src/evm.rs | 92 ++-- crates/cheatcodes/src/evm/fork.rs | 76 +-- crates/cheatcodes/src/evm/mock.rs | 7 +- crates/cheatcodes/src/evm/prank.rs | 4 +- crates/cheatcodes/src/inspector.rs | 436 ++++++++++-------- crates/cheatcodes/src/lib.rs | 25 +- crates/cheatcodes/src/script.rs | 10 +- crates/cheatcodes/src/test.rs | 2 +- crates/cheatcodes/src/test/expect.rs | 48 +- crates/chisel/src/dispatcher.rs | 2 +- crates/chisel/src/executor.rs | 2 +- crates/chisel/src/runner.rs | 4 +- crates/evm/core/Cargo.toml | 1 + crates/evm/core/src/backend/error.rs | 14 + crates/evm/core/src/backend/fuzz.rs | 41 +- crates/evm/core/src/backend/mod.rs | 118 +++-- crates/evm/core/src/opts.rs | 3 +- crates/evm/core/src/utils.rs | 94 ++-- crates/evm/coverage/src/inspector.rs | 21 +- crates/evm/evm/Cargo.toml | 1 - crates/evm/evm/src/executors/builder.rs | 12 +- crates/evm/evm/src/executors/mod.rs | 59 ++- crates/evm/evm/src/executors/tracing.rs | 7 +- crates/evm/evm/src/inspectors/access_list.rs | 79 ---- crates/evm/evm/src/inspectors/chisel_state.rs | 11 +- crates/evm/evm/src/inspectors/debugger.rs | 102 ++-- crates/evm/evm/src/inspectors/logs.rs | 40 +- crates/evm/evm/src/inspectors/mod.rs | 3 +- crates/evm/evm/src/inspectors/printer.rs | 23 +- crates/evm/evm/src/inspectors/stack.rs | 352 +++++++------- crates/evm/fuzz/src/inspector.rs | 35 +- crates/script/src/runner.rs | 2 +- testdata/cheats/MemSafety.t.sol | 27 ++ 45 files changed, 1138 insertions(+), 1147 deletions(-) delete mode 100644 crates/evm/evm/src/inspectors/access_list.rs diff --git a/Cargo.lock b/Cargo.lock index 67bca0286..a23a5d778 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -90,7 +90,7 @@ dependencies = [ [[package]] name = "alloy-consensus" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" +source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" dependencies = [ "alloy-eips", "alloy-network", @@ -122,7 +122,7 @@ dependencies = [ [[package]] name = "alloy-eips" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" +source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -133,7 +133,7 @@ dependencies = [ [[package]] name = "alloy-genesis" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" +source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -155,7 +155,7 @@ dependencies = [ [[package]] name = "alloy-json-rpc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" +source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" dependencies = [ "alloy-primitives", "serde", @@ -166,7 +166,7 @@ dependencies = [ [[package]] name = "alloy-network" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" +source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" dependencies = [ "alloy-eips", "alloy-json-rpc", @@ -205,7 +205,7 @@ dependencies = [ [[package]] name = "alloy-providers" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" +source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" dependencies = [ "alloy-network", "alloy-primitives", @@ -224,7 +224,7 @@ dependencies = [ [[package]] name = "alloy-pubsub" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" +source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -263,7 +263,7 @@ dependencies = [ [[package]] name = "alloy-rpc-client" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" +source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -280,7 +280,7 @@ dependencies = [ [[package]] name = "alloy-rpc-trace-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" +source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -291,7 +291,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" +source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -304,7 +304,7 @@ dependencies = [ [[package]] name = "alloy-signer" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" +source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" dependencies = [ "alloy-network", "alloy-primitives", @@ -365,7 +365,7 @@ dependencies = [ [[package]] name = "alloy-transport" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" +source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" dependencies = [ "alloy-json-rpc", "base64 0.21.7", @@ -382,7 +382,7 @@ dependencies = [ [[package]] name = "alloy-transport-http" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" +source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -395,7 +395,7 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" +source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -413,7 +413,7 @@ dependencies = [ [[package]] name = "alloy-transport-ws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#52bf7125d944e8a389371be503035724dfde5657" +source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -3333,7 +3333,6 @@ dependencies = [ "alloy-dyn-abi", "alloy-json-abi", "alloy-primitives", - "alloy-rpc-types", "alloy-sol-types", "const-hex", "eyre", @@ -3369,6 +3368,7 @@ dependencies = [ "alloy-rpc-types", "alloy-sol-types", "alloy-transport", + "auto_impl", "const-hex", "derive_more", "eyre", @@ -4578,9 +4578,9 @@ dependencies = [ [[package]] name = "k256" -version = "0.13.1" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" dependencies = [ "cfg-if", "ecdsa", @@ -6234,10 +6234,12 @@ dependencies = [ [[package]] name = "revm" -version = "3.5.0" -source = "git+https://github.com/bluealloy/revm?branch=reth_freeze#ba28a42393604beeb2da5a339ac47d3d5d3f2271" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d35316fc02d99e42831356c71e882f5d385c77b78f64a44ae82f2f9a4b8b72f" dependencies = [ "auto_impl", + "cfg-if", "revm-interpreter", "revm-precompile", "serde", @@ -6247,20 +6249,23 @@ dependencies = [ [[package]] name = "revm-inspectors" version = "0.1.0" -source = "git+https://github.com/paradigmxyz/evm-inspectors?rev=e90052361276aebcdc67cb24d8e2c4d907b6d299#e90052361276aebcdc67cb24d8e2c4d907b6d299" +source = "git+https://github.com/paradigmxyz/evm-inspectors?rev=5d560be#5d560be4cf022912f4f53f4e5ea71f81253b3c3d" dependencies = [ "alloy-primitives", "alloy-rpc-trace-types", "alloy-rpc-types", "alloy-sol-types", + "anstyle", + "colorchoice", "revm", "serde", ] [[package]] name = "revm-interpreter" -version = "1.3.0" -source = "git+https://github.com/bluealloy/revm?branch=reth_freeze#ba28a42393604beeb2da5a339ac47d3d5d3f2271" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fa10c2dc1e8f4934bdc763a2c09371bcec29e50c22e55e3eb325ee0cba09064" dependencies = [ "revm-primitives", "serde", @@ -6268,8 +6273,9 @@ dependencies = [ [[package]] name = "revm-precompile" -version = "2.2.0" -source = "git+https://github.com/bluealloy/revm?branch=reth_freeze#ba28a42393604beeb2da5a339ac47d3d5d3f2271" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db828d49d329560a70809d9d1fa0c74695edb49f50c5332db3eb24483076deac" dependencies = [ "aurora-engine-modexp", "c-kzg", @@ -6284,11 +6290,11 @@ dependencies = [ [[package]] name = "revm-primitives" -version = "1.3.0" -source = "git+https://github.com/bluealloy/revm?branch=reth_freeze#ba28a42393604beeb2da5a339ac47d3d5d3f2271" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fecd125aad58e135e2ca5771ed6e4e7b1f05fa3a64e0dfb9cc643b7a800a8435" dependencies = [ "alloy-primitives", - "alloy-rlp", "auto_impl", "bitflags 2.4.2", "bitvec", diff --git a/Cargo.toml b/Cargo.toml index 68240fde2..f14e8d891 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -142,9 +142,11 @@ foundry-compilers = { version = "0.3.9", default-features = false } ## revm # no default features to avoid c-kzg -revm = { version = "3", default-features = false } -revm-primitives = { version = "1", default-features = false } -revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "e90052361276aebcdc67cb24d8e2c4d907b6d299", default-features = false } +revm = { version = "6.1", default-features = false } +revm-primitives = { version = "2", default-features = false } +revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "5d560be", features = [ + "serde", +] } ## ethers ethers = { version = "2.0.14", default-features = false } @@ -157,22 +159,22 @@ ethers-middleware = { version = "2.0.14", default-features = false } ethers-solc = { version = "2.0.14", default-features = false } ## alloy -alloy-consensus = { git = "https://github.com/alloy-rs/alloy" } -alloy-eips = { git = "https://github.com/alloy-rs/alloy" } -alloy-genesis = { git = "https://github.com/alloy-rs/alloy" } -alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy" } -alloy-network = { git = "https://github.com/alloy-rs/alloy" } -alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy" } -alloy-providers = { git = "https://github.com/alloy-rs/alloy" } -alloy-pubsub = { git = "https://github.com/alloy-rs/alloy" } -alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy" } -alloy-rpc-trace-types = { git = "https://github.com/alloy-rs/alloy" } -alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy" } -alloy-signer = { git = "https://github.com/alloy-rs/alloy" } -alloy-transport = { git = "https://github.com/alloy-rs/alloy" } -alloy-transport-http = { git = "https://github.com/alloy-rs/alloy" } -alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy" } -alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy" } +alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } +alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } +alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } +alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } +alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } +alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } +alloy-providers = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } +alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } +alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } +alloy-rpc-trace-types = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } +alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } +alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } +alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } +alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } +alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } +alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } alloy-primitives = { version = "0.6.3", features = ["getrandom"] } alloy-dyn-abi = "0.6.3" alloy-json-abi = "0.6.3" @@ -203,17 +205,10 @@ tracing = "0.1" tracing-subscriber = "0.3" evm-disassembler = "0.4" vergen = { version = "8", default-features = false } -# TODO: bumping to >=0.13.2 breaks ecrecover: https://github.com/foundry-rs/foundry/pull/6969 -# TODO: unpin on next revm release: https://github.com/bluealloy/revm/pull/870 -k256 = "=0.13.1" +k256 = "0.13" axum = "0.6" hyper = "0.14" tower = "0.4" tower-http = "0.4" -[patch.crates-io] -revm = { git = "https://github.com/bluealloy/revm", branch = "reth_freeze" } -revm-primitives = { git = "https://github.com/bluealloy/revm", branch = "reth_freeze" } -revm-interpreter = { git = "https://github.com/bluealloy/revm", branch = "reth_freeze" } -revm-precompile = { git = "https://github.com/bluealloy/revm", branch = "reth_freeze" } diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 7c1098ec7..14021e289 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -34,7 +34,7 @@ use foundry_evm::{ constants::DEFAULT_CREATE2_DEPLOYER, fork::{BlockchainDb, BlockchainDbMeta, SharedBackend}, revm, - revm::primitives::{BlockEnv, CfgEnv, SpecId, TxEnv}, + revm::primitives::{BlockEnv, CfgEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, SpecId, TxEnv}, utils::apply_chain_and_block_specific_env_changes, }; use parking_lot::RwLock; @@ -820,8 +820,8 @@ impl NodeConfig { pub(crate) async fn setup(&mut self) -> mem::Backend { // configure the revm environment - let mut cfg = CfgEnv::default(); - cfg.spec_id = self.get_hardfork().into(); + let mut cfg = + CfgEnvWithHandlerCfg::new_with_spec_id(CfgEnv::default(), self.get_hardfork().into()); cfg.chain_id = self.get_chain_id(); cfg.limit_contract_code_size = self.code_size_limit; // EIP-3607 rejects transactions from senders with deployed code. @@ -829,10 +829,10 @@ impl NodeConfig { // caller is a contract. So we disable the check by default. cfg.disable_eip3607 = true; cfg.disable_block_gas_limit = self.disable_block_gas_limit; - cfg.optimism = self.enable_optimism; + cfg.handler_cfg.is_optimism = self.enable_optimism; - let mut env = revm::primitives::Env { - cfg, + let env = revm::primitives::Env { + cfg: cfg.cfg_env, block: BlockEnv { gas_limit: self.gas_limit, basefee: self.get_base_fee(), @@ -840,7 +840,10 @@ impl NodeConfig { }, tx: TxEnv { chain_id: self.get_chain_id().into(), ..Default::default() }, }; - let fees = FeeManager::new(env.cfg.spec_id, self.get_base_fee(), self.get_gas_price()); + let mut env = EnvWithHandlerCfg::new(Box::new(env), cfg.handler_cfg); + + let fees = + FeeManager::new(cfg.handler_cfg.spec_id, self.get_base_fee(), self.get_gas_price()); let (db, fork): (Arc>>, Option) = if let Some(eth_rpc_url) = self.eth_rpc_url.clone() { @@ -910,7 +913,7 @@ impl NodeConfig { pub async fn setup_fork_db( &mut self, eth_rpc_url: String, - env: &mut revm::primitives::Env, + env: &mut EnvWithHandlerCfg, fees: &FeeManager, ) -> (Arc>>, Option) { let (db, config) = self.setup_fork_db_config(eth_rpc_url, env, fees).await; @@ -932,7 +935,7 @@ impl NodeConfig { pub async fn setup_fork_db_config( &mut self, eth_rpc_url: String, - env: &mut revm::primitives::Env, + env: &mut EnvWithHandlerCfg, fees: &FeeManager, ) -> (ForkedDatabase, ClientForkConfig) { // TODO make provider agnostic @@ -961,7 +964,7 @@ impl NodeConfig { provider.get_chain_id().await.expect("Failed to fetch network chain ID"); if alloy_chains::NamedChain::Mainnet == chain_id.to::() { let hardfork: Hardfork = fork_block_number.into(); - env.cfg.spec_id = hardfork.into(); + env.handler_cfg.spec_id = hardfork.into(); self.hardfork = Some(hardfork); } Some(U256::from(chain_id)) @@ -1070,7 +1073,7 @@ latest block number: {latest_block}" }; let override_chain_id = self.chain_id; - let meta = BlockchainDbMeta::new(env.clone(), eth_rpc_url.clone()); + let meta = BlockchainDbMeta::new(*env.env.clone(), eth_rpc_url.clone()); let block_chain_db = if self.fork_chain_id.is_some() { BlockchainDb::new_skip_check(meta, self.block_cache_path(fork_block_number)) } else { diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index e9ba3ff84..f12904477 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -1733,7 +1733,7 @@ impl EthApi { current_block_number: U64::from(self.backend.best_number()), current_block_timestamp: env.block.timestamp.try_into().unwrap_or(u64::MAX), current_block_hash: self.backend.best_hash(), - hard_fork: env.cfg.spec_id, + hard_fork: env.handler_cfg.spec_id, transaction_order: match *tx_order { TransactionOrder::Fifo => "fifo".to_string(), TransactionOrder::Fees => "fees".to_string(), @@ -2259,7 +2259,7 @@ impl EthApi { return_ok!() => { // succeeded } - InstructionResult::OutOfGas | InstructionResult::OutOfFund => { + InstructionResult::OutOfGas | InstructionResult::OutOfFunds => { return Err(InvalidTransactionError::BasicOutOfGas(gas_limit).into()) } // need to check if the revert was due to lack of gas or unrelated reason @@ -2340,7 +2340,7 @@ impl EthApi { // gas). InstructionResult::Revert | InstructionResult::OutOfGas | - InstructionResult::OutOfFund | + InstructionResult::OutOfFunds | // we're also checking for InvalidFEOpcode here because this can be used to trigger an error common usage in openzeppelin InstructionResult::InvalidFEOpcode => { lowest_gas_limit = mid_gas_limit; diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index d4ba69264..45fc1f112 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -16,13 +16,14 @@ use anvil_core::eth::{ use foundry_evm::{ backend::DatabaseError, inspectors::{TracingInspector, TracingInspectorConfig}, - revm, revm::{ interpreter::InstructionResult, - primitives::{BlockEnv, CfgEnv, EVMError, Env, ExecutionResult, Output, SpecId}, + primitives::{ + BlockEnv, CfgEnvWithHandlerCfg, EVMError, EnvWithHandlerCfg, ExecutionResult, Output, + SpecId, + }, }, traces::CallTraceNode, - utils::{eval_to_instruction_result, halt_to_instruction_result}, }; use std::sync::Arc; @@ -108,7 +109,8 @@ pub struct TransactionExecutor<'a, Db: ?Sized, Validator: TransactionValidator> /// all pending transactions pub pending: std::vec::IntoIter>, pub block_env: BlockEnv, - pub cfg_env: CfgEnv, + /// The configuration environment and spec id + pub cfg_env: CfgEnvWithHandlerCfg, pub parent_hash: B256, /// Cumulative gas used by all executed transactions pub gas_used: U256, @@ -131,7 +133,7 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' let difficulty = self.block_env.difficulty; let beneficiary = self.block_env.coinbase; let timestamp = self.block_env.timestamp.to::(); - let base_fee = if (self.cfg_env.spec_id as u8) >= (SpecId::LONDON as u8) { + let base_fee = if (self.cfg_env.handler_cfg.spec_id as u8) >= (SpecId::LONDON as u8) { Some(self.block_env.basefee) } else { None @@ -220,8 +222,12 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' ExecutedTransactions { block, included, invalid } } - fn env_for(&self, tx: &PendingTransaction) -> Env { - Env { cfg: self.cfg_env.clone(), block: self.block_env.clone(), tx: tx.to_revm_tx_env() } + fn env_for(&self, tx: &PendingTransaction) -> EnvWithHandlerCfg { + EnvWithHandlerCfg::new_with_cfg_env( + self.cfg_env.clone(), + self.block_env.clone(), + tx.to_revm_tx_env(), + ) } } @@ -269,33 +275,40 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator let nonce = account.nonce; - let mut evm = revm::EVM::new(); - evm.env = env; - evm.database(&mut self.db); - // records all call and step traces let mut inspector = Inspector::default().with_tracing(); if self.enable_steps_tracing { inspector = inspector.with_steps_tracing(); } - trace!(target: "backend", "[{:?}] executing", transaction.hash()); - // transact and commit the transaction - let exec_result = match evm.inspect_commit(&mut inspector) { - Ok(exec_result) => exec_result, - Err(err) => { - warn!(target: "backend", "[{:?}] failed to execute: {:?}", transaction.hash(), err); - match err { - EVMError::Database(err) => { - return Some(TransactionExecutionOutcome::DatabaseError(transaction, err)) - } - EVMError::Transaction(err) => { - return Some(TransactionExecutionOutcome::Invalid(transaction, err.into())) - } - // This will correspond to prevrandao not set, and it should never happen. - // If it does, it's a bug. - e => { - panic!("Failed to execute transaction. This is a bug.\n {:?}", e) + let exec_result = { + let mut evm = + foundry_evm::utils::new_evm_with_inspector(&mut *self.db, env, &mut inspector); + + trace!(target: "backend", "[{:?}] executing", transaction.hash()); + // transact and commit the transaction + match evm.transact_commit() { + Ok(exec_result) => exec_result, + Err(err) => { + warn!(target: "backend", "[{:?}] failed to execute: {:?}", transaction.hash(), err); + match err { + EVMError::Database(err) => { + return Some(TransactionExecutionOutcome::DatabaseError( + transaction, + err, + )) + } + EVMError::Transaction(err) => { + return Some(TransactionExecutionOutcome::Invalid( + transaction, + err.into(), + )) + } + // This will correspond to prevrandao not set, and it should never happen. + // If it does, it's a bug. + e => { + panic!("Failed to execute transaction. This is a bug.\n {:?}", e) + } } } } @@ -304,14 +317,12 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator let (exit_reason, gas_used, out, logs) = match exec_result { ExecutionResult::Success { reason, gas_used, logs, output, .. } => { - (eval_to_instruction_result(reason), gas_used, Some(output), Some(logs)) + (reason.into(), gas_used, Some(output), Some(logs)) } ExecutionResult::Revert { gas_used, output } => { (InstructionResult::Revert, gas_used, Some(Output::Call(output)), None) } - ExecutionResult::Halt { reason, gas_used } => { - (halt_to_instruction_result(reason), gas_used, None, None) - } + ExecutionResult::Halt { reason, gas_used } => (reason.into(), gas_used, None, None), }; if exit_reason == InstructionResult::OutOfGas { @@ -330,11 +341,7 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator exit_reason, out, gas_used, - logs: logs - .unwrap_or_default() - .into_iter() - .map(|log| Log::new_unchecked(log.address, log.topics, log.data)) - .collect(), + logs: logs.unwrap_or_default(), traces: inspector .tracer .unwrap_or(TracingInspector::new(TracingInspectorConfig::all())) diff --git a/crates/anvil/src/eth/backend/mem/inspector.rs b/crates/anvil/src/eth/backend/mem/inspector.rs index 6072e4f93..6e38ac61d 100644 --- a/crates/anvil/src/eth/backend/mem/inspector.rs +++ b/crates/anvil/src/eth/backend/mem/inspector.rs @@ -1,16 +1,16 @@ //! Anvil specific [`revm::Inspector`] implementation use crate::{eth::macros::node_info, revm::Database}; -use alloy_primitives::Log; +use alloy_primitives::{Address, Log}; use foundry_evm::{ call_inspectors, decode::decode_console_logs, inspectors::{LogCollector, TracingInspector}, revm, revm::{ - interpreter::{CallInputs, CreateInputs, Gas, InstructionResult, Interpreter}, - primitives::{Address, Bytes, B256}, - EVMData, + interpreter::{CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter}, + primitives::U256, + EvmContext, }, traces::TracingInspectorConfig, }; @@ -48,94 +48,91 @@ impl Inspector { impl revm::Inspector for Inspector { #[inline] - fn initialize_interp(&mut self, interp: &mut Interpreter<'_>, data: &mut EVMData<'_, DB>) { + fn initialize_interp(&mut self, interp: &mut Interpreter, ecx: &mut EvmContext) { call_inspectors!([&mut self.tracer], |inspector| { - inspector.initialize_interp(interp, data); + inspector.initialize_interp(interp, ecx); }); } #[inline] - fn step(&mut self, interp: &mut Interpreter<'_>, data: &mut EVMData<'_, DB>) { + fn step(&mut self, interp: &mut Interpreter, ecx: &mut EvmContext) { call_inspectors!([&mut self.tracer], |inspector| { - inspector.step(interp, data); + inspector.step(interp, ecx); }); } #[inline] - fn log( - &mut self, - evm_data: &mut EVMData<'_, DB>, - address: &Address, - topics: &[B256], - data: &Bytes, - ) { - call_inspectors!([&mut self.tracer, Some(&mut self.log_collector)], |inspector| { - inspector.log(evm_data, address, topics, data); + fn step_end(&mut self, interp: &mut Interpreter, ecx: &mut EvmContext) { + call_inspectors!([&mut self.tracer], |inspector| { + inspector.step_end(interp, ecx); }); } #[inline] - fn step_end(&mut self, interp: &mut Interpreter<'_>, data: &mut EVMData<'_, DB>) { - call_inspectors!([&mut self.tracer], |inspector| { - inspector.step_end(interp, data); + fn log(&mut self, ecx: &mut EvmContext, log: &Log) { + call_inspectors!([&mut self.tracer, Some(&mut self.log_collector)], |inspector| { + inspector.log(ecx, log); }); } #[inline] - fn call( - &mut self, - data: &mut EVMData<'_, DB>, - call: &mut CallInputs, - ) -> (InstructionResult, Gas, Bytes) { + fn call(&mut self, ecx: &mut EvmContext, inputs: &mut CallInputs) -> Option { call_inspectors!([&mut self.tracer, Some(&mut self.log_collector)], |inspector| { - inspector.call(data, call); + if let Some(outcome) = inspector.call(ecx, inputs) { + return Some(outcome); + } }); - (InstructionResult::Continue, Gas::new(call.gas_limit), Bytes::new()) + None } #[inline] fn call_end( &mut self, - data: &mut EVMData<'_, DB>, + ecx: &mut EvmContext, inputs: &CallInputs, - remaining_gas: Gas, - ret: InstructionResult, - out: Bytes, - ) -> (InstructionResult, Gas, Bytes) { - call_inspectors!([&mut self.tracer], |inspector| { - inspector.call_end(data, inputs, remaining_gas, ret, out.clone()); - }); - (ret, remaining_gas, out) + outcome: CallOutcome, + ) -> CallOutcome { + if let Some(tracer) = &mut self.tracer { + return tracer.call_end(ecx, inputs, outcome); + } + + outcome } #[inline] fn create( &mut self, - data: &mut EVMData<'_, DB>, - call: &mut CreateInputs, - ) -> (InstructionResult, Option
, Gas, Bytes) { - call_inspectors!([&mut self.tracer], |inspector| { - inspector.create(data, call); - }); - - (InstructionResult::Continue, None, Gas::new(call.gas_limit), Bytes::new()) + ecx: &mut EvmContext, + inputs: &mut CreateInputs, + ) -> Option { + if let Some(tracer) = &mut self.tracer { + if let Some(out) = tracer.create(ecx, inputs) { + return Some(out); + } + } + None } #[inline] fn create_end( &mut self, - data: &mut EVMData<'_, DB>, + ecx: &mut EvmContext, inputs: &CreateInputs, - status: InstructionResult, - address: Option
, - gas: Gas, - retdata: Bytes, - ) -> (InstructionResult, Option
, Gas, Bytes) { - call_inspectors!([&mut self.tracer], |inspector| { - inspector.create_end(data, inputs, status, address, gas, retdata.clone()); - }); - (status, address, gas, retdata) + outcome: CreateOutcome, + ) -> CreateOutcome { + if let Some(tracer) = &mut self.tracer { + return tracer.create_end(ecx, inputs, outcome); + } + + outcome + } + + #[inline] + fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) { + if let Some(tracer) = &mut self.tracer { + revm::Inspector::::selfdestruct(tracer, contract, target, value); + } } } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 64e694255..8fb868dcd 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -63,22 +63,22 @@ use foundry_evm::{ backend::{DatabaseError, DatabaseResult, RevertSnapshotAction}, constants::DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE, decode::RevertDecoder, - inspectors::AccessListTracer, + inspectors::AccessListInspector, revm::{ self, db::CacheDB, interpreter::InstructionResult, primitives::{ - BlockEnv, CreateScheme, EVMError, Env, ExecutionResult, InvalidHeader, Output, SpecId, - TransactTo, TxEnv, KECCAK_EMPTY, + BlockEnv, CfgEnvWithHandlerCfg, CreateScheme, EnvWithHandlerCfg, ExecutionResult, + Output, SpecId, TransactTo, TxEnv, KECCAK_EMPTY, }, }, - traces::{TracingInspector, TracingInspectorConfig}, - utils::{eval_to_instruction_result, halt_to_instruction_result}, + utils::new_evm_with_inspector_ref, }; use futures::channel::mpsc::{unbounded, UnboundedSender}; use hash_db::HashDB; use parking_lot::{Mutex, RwLock}; +use revm::primitives::ResultAndState; use std::{ collections::{BTreeMap, HashMap}, io::{Read, Write}, @@ -147,7 +147,7 @@ pub struct Backend { /// Historic states of previous blocks states: Arc>, /// env data of the chain - env: Arc>, + env: Arc>, /// this is set if this is currently forked off another client fork: Arc>>, /// provides time related info, like timestamp @@ -177,7 +177,7 @@ impl Backend { #[allow(clippy::too_many_arguments)] pub async fn with_genesis( db: Arc>>, - env: Arc>, + env: Arc>, genesis: GenesisConfig, fees: FeeManager, fork: Arc>>, @@ -357,7 +357,7 @@ impl Backend { } pub fn precompiles(&self) -> Vec
{ - get_precompiles_for(self.env.read().cfg.spec_id) + get_precompiles_for(self.env.read().handler_cfg.spec_id) } /// Resets the fork to a fresh state @@ -480,7 +480,7 @@ impl Backend { } /// The env data of the blockchain - pub fn env(&self) -> &Arc> { + pub fn env(&self) -> &Arc> { &self.env } @@ -556,7 +556,7 @@ impl Backend { /// Returns the configured specid pub fn spec_id(&self) -> SpecId { - self.env.read().cfg.spec_id + self.env.read().handler_cfg.spec_id } /// Returns true for post London @@ -576,7 +576,7 @@ impl Backend { /// Returns true if op-stack deposits are active pub fn is_optimism(&self) -> bool { - self.env.read().cfg.optimism + self.env.read().handler_cfg.is_optimism } /// Returns an error if EIP1559 is not active (pre Berlin) @@ -771,7 +771,7 @@ impl Backend { } /// Returns the environment for the next block - fn next_env(&self) -> Env { + fn next_env(&self) -> EnvWithHandlerCfg { let mut env = self.env.read().clone(); // increase block number for this block env.block.number = env.block.number.saturating_add(rU256::from(1)); @@ -793,24 +793,16 @@ impl Backend { let db = self.db.read().await; let mut inspector = Inspector::default(); - let mut evm = revm::EVM::new(); - evm.env = env; - evm.database(&*db); - let result_and_state = match evm.inspect_ref(&mut inspector) { - Ok(res) => res, - Err(e) => return Err(e.into()), - }; - let state = result_and_state.state; - let (exit_reason, gas_used, out, logs) = match result_and_state.result { + let ResultAndState { result, state } = + new_evm_with_inspector_ref(&*db, env, &mut inspector).transact()?; + let (exit_reason, gas_used, out, logs) = match result { ExecutionResult::Success { reason, gas_used, logs, output, .. } => { - (eval_to_instruction_result(reason), gas_used, Some(output), Some(logs)) + (reason.into(), gas_used, Some(output), Some(logs)) } ExecutionResult::Revert { gas_used, output } => { (InstructionResult::Revert, gas_used, Some(Output::Call(output)), None) } - ExecutionResult::Halt { reason, gas_used } => { - (halt_to_instruction_result(reason), gas_used, None, None) - } + ExecutionResult::Halt { reason, gas_used } => (reason.into(), gas_used, None, None), }; inspector.print_logs(); @@ -843,12 +835,13 @@ impl Backend { let storage = self.blockchain.storage.read(); + let cfg_env = CfgEnvWithHandlerCfg::new(env.cfg.clone(), env.handler_cfg); let executor = TransactionExecutor { db: &mut cache_db, validator: self, pending: pool_transactions.into_iter(), block_env: env.block.clone(), - cfg_env: env.cfg, + cfg_env, parent_hash: storage.best_hash, gas_used: U256::ZERO, enable_steps_tracing: self.enable_steps_tracing, @@ -907,7 +900,7 @@ impl Backend { validator: self, pending: pool_transactions.into_iter(), block_env: env.block.clone(), - cfg_env: env.cfg.clone(), + cfg_env: CfgEnvWithHandlerCfg::new(env.cfg.clone(), env.handler_cfg), parent_hash: best_hash, gas_used: U256::ZERO, enable_steps_tracing: self.enable_steps_tracing, @@ -1049,7 +1042,7 @@ impl Backend { request: TransactionRequest, fee_details: FeeDetails, block_env: BlockEnv, - ) -> Env { + ) -> EnvWithHandlerCfg { let TransactionRequest { from, to, gas, value, input, nonce, access_list, .. } = request; let FeeDetails { gas_price, max_fee_per_gas, max_priority_fee_per_gas } = fee_details; @@ -1105,37 +1098,18 @@ impl Backend { D: DatabaseRef, { let mut inspector = Inspector::default(); - let mut evm = revm::EVM::new(); - evm.env = self.build_call_env(request, fee_details, block_env); - evm.database(state); - let result_and_state = match evm.inspect_ref(&mut inspector) { - Ok(result_and_state) => result_and_state, - Err(e) => match e { - EVMError::Transaction(invalid_tx) => { - return Err(BlockchainError::InvalidTransaction(invalid_tx.into())) - } - EVMError::Database(e) => return Err(BlockchainError::DatabaseError(e)), - EVMError::Header(e) => match e { - InvalidHeader::ExcessBlobGasNotSet => { - return Err(BlockchainError::ExcessBlobGasNotSet) - } - InvalidHeader::PrevrandaoNotSet => { - return Err(BlockchainError::PrevrandaoNotSet) - } - }, - }, - }; - let state = result_and_state.state; - let (exit_reason, gas_used, out) = match result_and_state.result { + + let env = self.build_call_env(request, fee_details, block_env); + let ResultAndState { result, state } = + new_evm_with_inspector_ref(state, env, &mut inspector).transact()?; + let (exit_reason, gas_used, out) = match result { ExecutionResult::Success { reason, gas_used, output, .. } => { - (eval_to_instruction_result(reason), gas_used, Some(output)) + (reason.into(), gas_used, Some(output)) } ExecutionResult::Revert { gas_used, output } => { (InstructionResult::Revert, gas_used, Some(Output::Call(output))) } - ExecutionResult::Halt { reason, gas_used } => { - (halt_to_instruction_result(reason), gas_used, None) - } + ExecutionResult::Halt { reason, gas_used } => (reason.into(), gas_used, None), }; inspector.print_logs(); Ok((exit_reason, out, gas_used, state)) @@ -1151,30 +1125,23 @@ impl Backend { self.with_database_at(block_request, |state, block| { let mut inspector = Inspector::default().with_steps_tracing(); let block_number = block.number; - let mut evm = revm::EVM::new(); - evm.env = self.build_call_env(request, fee_details, block); - evm.database(state); - let result_and_state = - match evm.inspect_ref(&mut inspector) { - Ok(result_and_state) => result_and_state, - Err(e) => return Err(e.into()), - }; - let (exit_reason, gas_used, out, ) = match result_and_state.result { + + let env = self.build_call_env(request, fee_details, block); + let ResultAndState { result, state: _ } = + new_evm_with_inspector_ref(state, env, &mut inspector).transact()?; + let (exit_reason, gas_used, out) = match result { ExecutionResult::Success { reason, gas_used, output, .. } => { - (eval_to_instruction_result(reason), gas_used, Some(output), ) - }, - ExecutionResult::Revert { gas_used, output} => { + (reason.into(), gas_used, Some(output)) + } + ExecutionResult::Revert { gas_used, output } => { (InstructionResult::Revert, gas_used, Some(Output::Call(output))) - }, - ExecutionResult::Halt { reason, gas_used } => { - (halt_to_instruction_result(reason), gas_used, None) - }, + } + ExecutionResult::Halt { reason, gas_used } => (reason.into(), gas_used, None), }; - let res = inspector.tracer.unwrap_or(TracingInspector::new(TracingInspectorConfig::all())).into_geth_builder().geth_traces(gas_used, match &out { - Some(out) => out.data().clone(), - None => Bytes::new() - }, opts); - trace!(target: "backend", "trace call return {:?} out: {:?} gas {} on block {}", exit_reason, out, gas_used, block_number); + let tracer = inspector.tracer.expect("tracer disappeared"); + let return_value = out.as_ref().map(|o| o.data().clone()).unwrap_or_default(); + let res = tracer.into_geth_builder().geth_traces(gas_used, return_value, opts); + trace!(target: "backend", ?exit_reason, ?out, %gas_used, %block_number, "trace call"); Ok(res) }) .await? @@ -1198,32 +1165,26 @@ impl Backend { from.create(nonce) }; - let mut tracer = AccessListTracer::new( + let mut inspector = AccessListInspector::new( request.access_list.clone().unwrap_or_default(), from, to, self.precompiles(), ); - let mut evm = revm::EVM::new(); - evm.env = self.build_call_env(request, fee_details, block_env); - evm.database(state); - let result_and_state = match evm.inspect_ref(&mut tracer) { - Ok(result_and_state) => result_and_state, - Err(e) => return Err(e.into()), - }; - let (exit_reason, gas_used, out) = match result_and_state.result { + let env = self.build_call_env(request, fee_details, block_env); + let ResultAndState { result, state: _ } = + new_evm_with_inspector_ref(state, env, &mut inspector).transact()?; + let (exit_reason, gas_used, out) = match result { ExecutionResult::Success { reason, gas_used, output, .. } => { - (eval_to_instruction_result(reason), gas_used, Some(output)) + (reason.into(), gas_used, Some(output)) } ExecutionResult::Revert { gas_used, output } => { (InstructionResult::Revert, gas_used, Some(Output::Call(output))) } - ExecutionResult::Halt { reason, gas_used } => { - (halt_to_instruction_result(reason), gas_used, None) - } + ExecutionResult::Halt { reason, gas_used } => (reason.into(), gas_used, None), }; - let access_list = tracer.access_list(); + let access_list = inspector.access_list(); Ok((exit_reason, out, gas_used, access_list)) } @@ -2320,7 +2281,7 @@ impl TransactionValidator for Backend { &self, pending: &PendingTransaction, account: &AccountInfo, - env: &Env, + env: &EnvWithHandlerCfg, ) -> Result<(), InvalidTransactionError> { let tx = &pending.transaction; @@ -2329,7 +2290,7 @@ impl TransactionValidator for Backend { if chain_id.to::() != tx_chain_id { if let Some(legacy) = tx.as_legacy() { // - if env.cfg.spec_id >= SpecId::SPURIOUS_DRAGON && + if env.handler_cfg.spec_id >= SpecId::SPURIOUS_DRAGON && !meets_eip155(chain_id.to::(), legacy.signature().v()) { warn!(target: "backend", ?chain_id, ?tx_chain_id, "incompatible EIP155-based V"); @@ -2365,7 +2326,7 @@ impl TransactionValidator for Backend { return Err(InvalidTransactionError::NonceTooLow); } - if (env.cfg.spec_id as u8) >= (SpecId::LONDON as u8) { + if (env.handler_cfg.spec_id as u8) >= (SpecId::LONDON as u8) { if tx.gas_price() < env.block.basefee && !is_deposit_tx { warn!(target: "backend", "max fee per gas={}, too low, block basefee={}",tx.gas_price(), env.block.basefee); return Err(InvalidTransactionError::FeeCapTooLow); @@ -2400,7 +2361,7 @@ impl TransactionValidator for Backend { &self, tx: &PendingTransaction, account: &AccountInfo, - env: &Env, + env: &EnvWithHandlerCfg, ) -> Result<(), InvalidTransactionError> { self.validate_pool_transaction_for(tx, account, env)?; if tx.nonce().to::() > account.nonce { diff --git a/crates/anvil/src/eth/backend/validate.rs b/crates/anvil/src/eth/backend/validate.rs index 3ea666f15..650ce24a5 100644 --- a/crates/anvil/src/eth/backend/validate.rs +++ b/crates/anvil/src/eth/backend/validate.rs @@ -2,7 +2,7 @@ use crate::eth::error::{BlockchainError, InvalidTransactionError}; use anvil_core::eth::transaction::PendingTransaction; -use foundry_evm::revm::primitives::{AccountInfo, Env}; +use foundry_evm::revm::primitives::{AccountInfo, EnvWithHandlerCfg}; /// A trait for validating transactions #[async_trait::async_trait] @@ -22,7 +22,7 @@ pub trait TransactionValidator { &self, tx: &PendingTransaction, account: &AccountInfo, - env: &Env, + env: &EnvWithHandlerCfg, ) -> Result<(), InvalidTransactionError>; /// Validates the transaction against a specific account @@ -32,6 +32,6 @@ pub trait TransactionValidator { &self, tx: &PendingTransaction, account: &AccountInfo, - env: &Env, + env: &EnvWithHandlerCfg, ) -> Result<(), InvalidTransactionError>; } diff --git a/crates/anvil/src/eth/error.rs b/crates/anvil/src/eth/error.rs index cc7af40ac..39e1f52a4 100644 --- a/crates/anvil/src/eth/error.rs +++ b/crates/anvil/src/eth/error.rs @@ -85,6 +85,8 @@ pub enum BlockchainError { DepositTransactionUnsupported, #[error("Excess blob gas not set.")] ExcessBlobGasNotSet, + #[error("{0}")] + Message(String), } impl From for BlockchainError { @@ -105,6 +107,7 @@ where InvalidHeader::PrevrandaoNotSet => BlockchainError::PrevrandaoNotSet, }, EVMError::Database(err) => err.into(), + EVMError::Custom(err) => BlockchainError::Message(err), } } } @@ -233,7 +236,7 @@ impl From for InvalidTransactionError { InvalidTransaction::NonceOverflowInTransaction => { InvalidTransactionError::NonceMaxValue } - InvalidTransaction::CreateInitcodeSizeLimit => { + InvalidTransaction::CreateInitCodeSizeLimit => { InvalidTransactionError::MaxInitCodeSizeExceeded } InvalidTransaction::NonceTooHigh { .. } => InvalidTransactionError::NonceTooHigh, @@ -411,6 +414,7 @@ impl ToRpcResponseResult for Result { err @ BlockchainError::ExcessBlobGasNotSet => { RpcError::invalid_params(err.to_string()) } + err @ BlockchainError::Message(_) => RpcError::internal_error_with(err.to_string()), } .into(), } diff --git a/crates/anvil/src/eth/util.rs b/crates/anvil/src/eth/util.rs index 5153178f5..6bcde67d5 100644 --- a/crates/anvil/src/eth/util.rs +++ b/crates/anvil/src/eth/util.rs @@ -1,9 +1,12 @@ use alloy_primitives::Address; -use foundry_evm::revm::{self, precompile::Precompiles, primitives::SpecId}; +use foundry_evm::revm::{ + precompile::{PrecompileSpecId, Precompiles}, + primitives::SpecId, +}; use std::fmt; pub fn get_precompiles_for(spec_id: SpecId) -> Vec
{ - Precompiles::new(to_precompile_id(spec_id)).addresses().into_iter().copied().collect() + Precompiles::new(PrecompileSpecId::from_spec_id(spec_id)).addresses().copied().collect() } /// wrapper type that displays byte as hex @@ -53,29 +56,3 @@ impl<'a> fmt::Debug for HexDisplay<'a> { Ok(()) } } - -pub fn to_precompile_id(spec_id: SpecId) -> revm::precompile::SpecId { - match spec_id { - SpecId::FRONTIER | - SpecId::FRONTIER_THAWING | - SpecId::HOMESTEAD | - SpecId::DAO_FORK | - SpecId::TANGERINE | - SpecId::SPURIOUS_DRAGON => revm::precompile::SpecId::HOMESTEAD, - SpecId::BYZANTIUM | SpecId::CONSTANTINOPLE | SpecId::PETERSBURG => { - revm::precompile::SpecId::BYZANTIUM - } - SpecId::ISTANBUL | SpecId::MUIR_GLACIER => revm::precompile::SpecId::ISTANBUL, - SpecId::BERLIN | - SpecId::LONDON | - SpecId::ARROW_GLACIER | - SpecId::GRAY_GLACIER | - SpecId::MERGE | - SpecId::SHANGHAI | - SpecId::CANCUN | - SpecId::BEDROCK | - SpecId::REGOLITH | - SpecId::CANYON | - SpecId::LATEST => revm::precompile::SpecId::BERLIN, - } -} diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index ca83c084d..a9e074962 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -1,6 +1,7 @@ use alloy_primitives::U256; use alloy_providers::provider::TempProvider; use alloy_rpc_types::BlockTransactions; +use cast::revm::primitives::EnvWithHandlerCfg; use clap::Parser; use eyre::{Result, WrapErr}; use foundry_cli::{ @@ -135,6 +136,9 @@ impl RunArgs { env.block.gas_limit = block.header.gas_limit; } + let mut env = + EnvWithHandlerCfg::new_with_spec_id(Box::new(env.clone()), executor.spec_id()); + // Set the state to the moment right before the transaction if !self.quick { println!("Executing previous transactions from the block."); diff --git a/crates/cheatcodes/src/error.rs b/crates/cheatcodes/src/error.rs index 66796026d..8b5f8102d 100644 --- a/crates/cheatcodes/src/error.rs +++ b/crates/cheatcodes/src/error.rs @@ -7,6 +7,7 @@ use foundry_config::UnresolvedEnvVarError; use foundry_evm_core::backend::DatabaseError; use foundry_wallets::error::WalletSignerError; use k256::ecdsa::signature::Error as SignatureError; +use revm::primitives::EVMError; use std::{borrow::Cow, fmt}; /// Cheatcode result type. @@ -302,6 +303,13 @@ impl_from!( WalletSignerError, ); +impl From> for Error { + #[inline] + fn from(err: EVMError) -> Self { + Self::display(DatabaseError::from(err)) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index 2fec64503..fd45200f0 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -12,7 +12,7 @@ use foundry_evm_core::{ }; use revm::{ primitives::{Account, Bytecode, SpecId, KECCAK_EMPTY}, - EVMData, + EvmContext, }; use std::{collections::HashMap, path::Path}; @@ -60,8 +60,8 @@ impl Cheatcode for loadCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { target, slot } = *self; ensure_not_precompile!(&target, ccx); - ccx.data.journaled_state.load_account(target, ccx.data.db)?; - let (val, _) = ccx.data.journaled_state.sload(target, slot.into(), ccx.data.db)?; + ccx.ecx.load_account(target)?; + let (val, _) = ccx.ecx.sload(target, slot.into())?; Ok(val.abi_encode()) } } @@ -84,9 +84,9 @@ impl Cheatcode for loadAllocsCall { }; // Then, load the allocs into the database. - ccx.data + ccx.ecx .db - .load_allocs(&allocs, &mut ccx.data.journaled_state) + .load_allocs(&allocs, &mut ccx.ecx.journaled_state) .map(|()| Vec::default()) .map_err(|e| fmt_err!("failed to load allocs: {e}")) } @@ -109,7 +109,7 @@ impl Cheatcode for dumpStateCall { }; let alloc = ccx - .data + .ecx .journaled_state .state() .into_iter() @@ -214,7 +214,7 @@ impl Cheatcode for chainIdCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newChainId } = self; ensure!(*newChainId <= U256::from(u64::MAX), "chain ID must be less than 2^64 - 1"); - ccx.data.env.cfg.chain_id = newChainId.to(); + ccx.ecx.env.cfg.chain_id = newChainId.to(); Ok(Default::default()) } } @@ -222,7 +222,7 @@ impl Cheatcode for chainIdCall { impl Cheatcode for coinbaseCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newCoinbase } = self; - ccx.data.env.block.coinbase = *newCoinbase; + ccx.ecx.env.block.coinbase = *newCoinbase; Ok(Default::default()) } } @@ -231,11 +231,11 @@ impl Cheatcode for difficultyCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newDifficulty } = self; ensure!( - ccx.data.env.cfg.spec_id < SpecId::MERGE, + ccx.ecx.spec_id() < SpecId::MERGE, "`difficulty` is not supported after the Paris hard fork, use `prevrandao` instead; \ see EIP-4399: https://eips.ethereum.org/EIPS/eip-4399" ); - ccx.data.env.block.difficulty = *newDifficulty; + ccx.ecx.env.block.difficulty = *newDifficulty; Ok(Default::default()) } } @@ -243,7 +243,7 @@ impl Cheatcode for difficultyCall { impl Cheatcode for feeCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newBasefee } = self; - ccx.data.env.block.basefee = *newBasefee; + ccx.ecx.env.block.basefee = *newBasefee; Ok(Default::default()) } } @@ -252,11 +252,11 @@ impl Cheatcode for prevrandaoCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newPrevrandao } = self; ensure!( - ccx.data.env.cfg.spec_id >= SpecId::MERGE, + ccx.ecx.spec_id() >= SpecId::MERGE, "`prevrandao` is not supported before the Paris hard fork, use `difficulty` instead; \ see EIP-4399: https://eips.ethereum.org/EIPS/eip-4399" ); - ccx.data.env.block.prevrandao = Some(*newPrevrandao); + ccx.ecx.env.block.prevrandao = Some(*newPrevrandao); Ok(Default::default()) } } @@ -264,7 +264,7 @@ impl Cheatcode for prevrandaoCall { impl Cheatcode for rollCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newHeight } = self; - ccx.data.env.block.number = *newHeight; + ccx.ecx.env.block.number = *newHeight; Ok(Default::default()) } } @@ -272,14 +272,14 @@ impl Cheatcode for rollCall { impl Cheatcode for getBlockNumberCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; - Ok(ccx.data.env.block.number.abi_encode()) + Ok(ccx.ecx.env.block.number.abi_encode()) } } impl Cheatcode for txGasPriceCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newGasPrice } = self; - ccx.data.env.tx.gas_price = *newGasPrice; + ccx.ecx.env.tx.gas_price = *newGasPrice; Ok(Default::default()) } } @@ -287,7 +287,7 @@ impl Cheatcode for txGasPriceCall { impl Cheatcode for warpCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newTimestamp } = self; - ccx.data.env.block.timestamp = *newTimestamp; + ccx.ecx.env.block.timestamp = *newTimestamp; Ok(Default::default()) } } @@ -295,14 +295,14 @@ impl Cheatcode for warpCall { impl Cheatcode for getBlockTimestampCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; - Ok(ccx.data.env.block.timestamp.abi_encode()) + Ok(ccx.ecx.env.block.timestamp.abi_encode()) } } impl Cheatcode for dealCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account: address, newBalance: new_balance } = *self; - let account = journaled_account(ccx.data, address)?; + let account = journaled_account(ccx.ecx, address)?; let old_balance = std::mem::replace(&mut account.info.balance, new_balance); let record = DealRecord { address, old_balance, new_balance }; ccx.state.eth_deals.push(record); @@ -314,9 +314,9 @@ impl Cheatcode for etchCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { target, newRuntimeBytecode } = self; ensure_not_precompile!(target, ccx); - ccx.data.journaled_state.load_account(*target, ccx.data.db)?; + ccx.ecx.load_account(*target)?; let bytecode = Bytecode::new_raw(Bytes::copy_from_slice(newRuntimeBytecode)).to_checked(); - ccx.data.journaled_state.set_code(*target, bytecode); + ccx.ecx.journaled_state.set_code(*target, bytecode); Ok(Default::default()) } } @@ -324,7 +324,7 @@ impl Cheatcode for etchCall { impl Cheatcode for resetNonceCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account } = self; - let account = journaled_account(ccx.data, *account)?; + let account = journaled_account(ccx.ecx, *account)?; // Per EIP-161, EOA nonces start at 0, but contract nonces // start at 1. Comparing by code_hash instead of code // to avoid hitting the case where account's code is None. @@ -339,7 +339,7 @@ impl Cheatcode for resetNonceCall { impl Cheatcode for setNonceCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account, newNonce } = *self; - let account = journaled_account(ccx.data, account)?; + let account = journaled_account(ccx.ecx, account)?; // nonce must increment only let current = account.info.nonce; ensure!( @@ -355,7 +355,7 @@ impl Cheatcode for setNonceCall { impl Cheatcode for setNonceUnsafeCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account, newNonce } = *self; - let account = journaled_account(ccx.data, account)?; + let account = journaled_account(ccx.ecx, account)?; account.info.nonce = newNonce; Ok(Default::default()) } @@ -366,8 +366,8 @@ impl Cheatcode for storeCall { let Self { target, slot, value } = *self; ensure_not_precompile!(&target, ccx); // ensure the account is touched - let _ = journaled_account(ccx.data, target)?; - ccx.data.journaled_state.sstore(target, slot.into(), value.into(), ccx.data.db)?; + let _ = journaled_account(ccx.ecx, target)?; + ccx.ecx.sstore(target, slot.into(), value.into())?; Ok(Default::default()) } } @@ -375,7 +375,7 @@ impl Cheatcode for storeCall { impl Cheatcode for coolCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { target } = self; - if let Some(account) = ccx.data.journaled_state.state.get_mut(target) { + if let Some(account) = ccx.ecx.journaled_state.state.get_mut(target) { account.unmark_touch(); account.storage.clear(); } @@ -386,28 +386,28 @@ impl Cheatcode for coolCall { impl Cheatcode for readCallersCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; - read_callers(ccx.state, &ccx.data.env.tx.caller) + read_callers(ccx.state, &ccx.ecx.env.tx.caller) } } impl Cheatcode for snapshotCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; - Ok(ccx.data.db.snapshot(&ccx.data.journaled_state, ccx.data.env).abi_encode()) + Ok(ccx.ecx.db.snapshot(&ccx.ecx.journaled_state, &ccx.ecx.env).abi_encode()) } } impl Cheatcode for revertToCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { snapshotId } = self; - let result = if let Some(journaled_state) = ccx.data.db.revert( + let result = if let Some(journaled_state) = ccx.ecx.db.revert( *snapshotId, - &ccx.data.journaled_state, - ccx.data.env, + &ccx.ecx.journaled_state, + &mut ccx.ecx.env, RevertSnapshotAction::RevertKeep, ) { // we reset the evm's journaled_state to the state of the snapshot previous state - ccx.data.journaled_state = journaled_state; + ccx.ecx.journaled_state = journaled_state; true } else { false @@ -419,14 +419,14 @@ impl Cheatcode for revertToCall { impl Cheatcode for revertToAndDeleteCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { snapshotId } = self; - let result = if let Some(journaled_state) = ccx.data.db.revert( + let result = if let Some(journaled_state) = ccx.ecx.db.revert( *snapshotId, - &ccx.data.journaled_state, - ccx.data.env, + &ccx.ecx.journaled_state, + &mut ccx.ecx.env, RevertSnapshotAction::RevertRemove, ) { // we reset the evm's journaled_state to the state of the snapshot previous state - ccx.data.journaled_state = journaled_state; + ccx.ecx.journaled_state = journaled_state; true } else { false @@ -438,14 +438,14 @@ impl Cheatcode for revertToAndDeleteCall { impl Cheatcode for deleteSnapshotCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { snapshotId } = self; - let result = ccx.data.db.delete_snapshot(*snapshotId); + let result = ccx.ecx.db.delete_snapshot(*snapshotId); Ok(result.abi_encode()) } } impl Cheatcode for deleteSnapshotsCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; - ccx.data.db.delete_snapshots(); + ccx.ecx.db.delete_snapshots(); Ok(Default::default()) } } @@ -467,7 +467,7 @@ impl Cheatcode for stopAndReturnStateDiffCall { pub(super) fn get_nonce(ccx: &mut CheatsCtxt, address: &Address) -> Result { super::script::correct_sender_nonce(ccx)?; - let (account, _) = ccx.data.journaled_state.load_account(*address, ccx.data.db)?; + let (account, _) = ccx.ecx.journaled_state.load_account(*address, &mut ccx.ecx.db)?; Ok(account.info.nonce.abi_encode()) } @@ -520,13 +520,13 @@ fn read_callers(state: &Cheatcodes, default_sender: &Address) -> Result { } /// Ensures the `Account` is loaded and touched. -pub(super) fn journaled_account<'a, DB: DatabaseExt>( - data: &'a mut EVMData<'_, DB>, +pub(super) fn journaled_account( + ecx: &mut EvmContext, addr: Address, -) -> Result<&'a mut Account> { - data.journaled_state.load_account(addr, data.db)?; - data.journaled_state.touch(&addr); - Ok(data.journaled_state.state.get_mut(&addr).expect("account is loaded")) +) -> Result<&mut Account> { + ecx.load_account(addr)?; + ecx.journaled_state.touch(&addr); + Ok(ecx.journaled_state.state.get_mut(&addr).expect("account is loaded")) } /// Consumes recorded account accesses and returns them as an abi encoded diff --git a/crates/cheatcodes/src/evm/fork.rs b/crates/cheatcodes/src/evm/fork.rs index 1d8fc2fe4..4b3fdc6b3 100644 --- a/crates/cheatcodes/src/evm/fork.rs +++ b/crates/cheatcodes/src/evm/fork.rs @@ -11,7 +11,7 @@ use foundry_evm_core::fork::CreateFork; impl Cheatcode for activeForkCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; - ccx.data + ccx.ecx .db .active_fork_id() .map(|id| id.abi_encode()) @@ -64,7 +64,7 @@ impl Cheatcode for createSelectFork_2Call { impl Cheatcode for rollFork_0Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { blockNumber } = self; - ccx.data.db.roll_fork(None, *blockNumber, ccx.data.env, &mut ccx.data.journaled_state)?; + ccx.ecx.db.roll_fork(None, *blockNumber, &mut ccx.ecx.env, &mut ccx.ecx.journaled_state)?; Ok(Default::default()) } } @@ -72,11 +72,11 @@ impl Cheatcode for rollFork_0Call { impl Cheatcode for rollFork_1Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { txHash } = self; - ccx.data.db.roll_fork_to_transaction( + ccx.ecx.db.roll_fork_to_transaction( None, *txHash, - ccx.data.env, - &mut ccx.data.journaled_state, + &mut ccx.ecx.env, + &mut ccx.ecx.journaled_state, )?; Ok(Default::default()) } @@ -85,11 +85,11 @@ impl Cheatcode for rollFork_1Call { impl Cheatcode for rollFork_2Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { forkId, blockNumber } = self; - ccx.data.db.roll_fork( + ccx.ecx.db.roll_fork( Some(*forkId), *blockNumber, - ccx.data.env, - &mut ccx.data.journaled_state, + &mut ccx.ecx.env, + &mut ccx.ecx.journaled_state, )?; Ok(Default::default()) } @@ -98,11 +98,11 @@ impl Cheatcode for rollFork_2Call { impl Cheatcode for rollFork_3Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { forkId, txHash } = self; - ccx.data.db.roll_fork_to_transaction( + ccx.ecx.db.roll_fork_to_transaction( Some(*forkId), *txHash, - ccx.data.env, - &mut ccx.data.journaled_state, + &mut ccx.ecx.env, + &mut ccx.ecx.journaled_state, )?; Ok(Default::default()) } @@ -117,7 +117,7 @@ impl Cheatcode for selectForkCall { // fork. ccx.state.corrected_nonce = true; - ccx.data.db.select_fork(*forkId, ccx.data.env, &mut ccx.data.journaled_state)?; + ccx.ecx.db.select_fork(*forkId, &mut ccx.ecx.env, &mut ccx.ecx.journaled_state)?; Ok(Default::default()) } } @@ -125,11 +125,11 @@ impl Cheatcode for selectForkCall { impl Cheatcode for transact_0Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { txHash } = *self; - ccx.data.db.transact( + ccx.ecx.db.transact( None, txHash, - ccx.data.env, - &mut ccx.data.journaled_state, + &mut ccx.ecx.env, + &mut ccx.ecx.journaled_state, ccx.state, )?; Ok(Default::default()) @@ -139,11 +139,11 @@ impl Cheatcode for transact_0Call { impl Cheatcode for transact_1Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { forkId, txHash } = *self; - ccx.data.db.transact( + ccx.ecx.db.transact( Some(forkId), txHash, - ccx.data.env, - &mut ccx.data.journaled_state, + &mut ccx.ecx.env, + &mut ccx.ecx.journaled_state, ccx.state, )?; Ok(Default::default()) @@ -153,7 +153,7 @@ impl Cheatcode for transact_1Call { impl Cheatcode for allowCheatcodesCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account } = self; - ccx.data.db.allow_cheatcode_access(*account); + ccx.ecx.db.allow_cheatcode_access(*account); Ok(Default::default()) } } @@ -161,7 +161,7 @@ impl Cheatcode for allowCheatcodesCall { impl Cheatcode for makePersistent_0Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account } = self; - ccx.data.db.add_persistent_account(*account); + ccx.ecx.db.add_persistent_account(*account); Ok(Default::default()) } } @@ -169,8 +169,8 @@ impl Cheatcode for makePersistent_0Call { impl Cheatcode for makePersistent_1Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account0, account1 } = self; - ccx.data.db.add_persistent_account(*account0); - ccx.data.db.add_persistent_account(*account1); + ccx.ecx.db.add_persistent_account(*account0); + ccx.ecx.db.add_persistent_account(*account1); Ok(Default::default()) } } @@ -178,9 +178,9 @@ impl Cheatcode for makePersistent_1Call { impl Cheatcode for makePersistent_2Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account0, account1, account2 } = self; - ccx.data.db.add_persistent_account(*account0); - ccx.data.db.add_persistent_account(*account1); - ccx.data.db.add_persistent_account(*account2); + ccx.ecx.db.add_persistent_account(*account0); + ccx.ecx.db.add_persistent_account(*account1); + ccx.ecx.db.add_persistent_account(*account2); Ok(Default::default()) } } @@ -188,7 +188,7 @@ impl Cheatcode for makePersistent_2Call { impl Cheatcode for makePersistent_3Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { accounts } = self; - ccx.data.db.extend_persistent_accounts(accounts.iter().copied()); + ccx.ecx.db.extend_persistent_accounts(accounts.iter().copied()); Ok(Default::default()) } } @@ -196,7 +196,7 @@ impl Cheatcode for makePersistent_3Call { impl Cheatcode for revokePersistent_0Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account } = self; - ccx.data.db.remove_persistent_account(account); + ccx.ecx.db.remove_persistent_account(account); Ok(Default::default()) } } @@ -204,7 +204,7 @@ impl Cheatcode for revokePersistent_0Call { impl Cheatcode for revokePersistent_1Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { accounts } = self; - ccx.data.db.remove_persistent_accounts(accounts.iter().copied()); + ccx.ecx.db.remove_persistent_accounts(accounts.iter().copied()); Ok(Default::default()) } } @@ -212,7 +212,7 @@ impl Cheatcode for revokePersistent_1Call { impl Cheatcode for isPersistentCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account } = self; - Ok(ccx.data.db.is_persistent(account).abi_encode()) + Ok(ccx.ecx.db.is_persistent(account).abi_encode()) } } @@ -220,7 +220,7 @@ impl Cheatcode for rpcCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { method, params } = self; let url = - ccx.data.db.active_fork_url().ok_or_else(|| fmt_err!("no active fork URL found"))?; + ccx.ecx.db.active_fork_url().ok_or_else(|| fmt_err!("no active fork URL found"))?; let provider = ProviderBuilder::new(&url).build()?; let method: &'static str = Box::new(method.clone()).leak(); let params_json: serde_json::Value = serde_json::from_str(params)?; @@ -248,7 +248,7 @@ impl Cheatcode for eth_getLogsCall { } let url = - ccx.data.db.active_fork_url().ok_or_else(|| fmt_err!("no active fork URL found"))?; + ccx.ecx.db.active_fork_url().ok_or_else(|| fmt_err!("no active fork URL found"))?; let provider = ProviderBuilder::new(&url).build()?; let mut filter = Filter::new().address(*target).from_block(from_block).to_block(to_block); for (i, topic) in topics.iter().enumerate() { @@ -302,7 +302,7 @@ fn create_select_fork( ccx.state.corrected_nonce = true; let fork = create_fork_request(ccx, url_or_alias, block)?; - let id = ccx.data.db.create_select_fork(fork, ccx.data.env, &mut ccx.data.journaled_state)?; + let id = ccx.ecx.db.create_select_fork(fork, &mut ccx.ecx.env, &mut ccx.ecx.journaled_state)?; Ok(id.abi_encode()) } @@ -313,7 +313,7 @@ fn create_fork( block: Option, ) -> Result { let fork = create_fork_request(ccx, url_or_alias, block)?; - let id = ccx.data.db.create_fork(fork)?; + let id = ccx.ecx.db.create_fork(fork)?; Ok(id.abi_encode()) } @@ -329,10 +329,10 @@ fn create_select_fork_at_transaction( ccx.state.corrected_nonce = true; let fork = create_fork_request(ccx, url_or_alias, None)?; - let id = ccx.data.db.create_select_fork_at_transaction( + let id = ccx.ecx.db.create_select_fork_at_transaction( fork, - ccx.data.env, - &mut ccx.data.journaled_state, + &mut ccx.ecx.env, + &mut ccx.ecx.journaled_state, *transaction, )?; Ok(id.abi_encode()) @@ -345,7 +345,7 @@ fn create_fork_at_transaction( transaction: &B256, ) -> Result { let fork = create_fork_request(ccx, url_or_alias, None)?; - let id = ccx.data.db.create_fork_at_transaction(fork, *transaction)?; + let id = ccx.ecx.db.create_fork_at_transaction(fork, *transaction)?; Ok(id.abi_encode()) } @@ -361,7 +361,7 @@ fn create_fork_request( let fork = CreateFork { enable_caching: ccx.state.config.rpc_storage_caching.enable_for_endpoint(&url), url, - env: ccx.data.env.clone(), + env: (*ccx.ecx.env).clone(), evm_opts, }; Ok(fork) diff --git a/crates/cheatcodes/src/evm/mock.rs b/crates/cheatcodes/src/evm/mock.rs index 1fa55539e..645c14c58 100644 --- a/crates/cheatcodes/src/evm/mock.rs +++ b/crates/cheatcodes/src/evm/mock.rs @@ -49,14 +49,15 @@ impl Cheatcode for clearMockedCallsCall { impl Cheatcode for mockCall_0Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { callee, data, returnData } = self; - let (acc, _) = ccx.data.journaled_state.load_account(*callee, ccx.data.db)?; + // TODO: use ecx.load_account + let (acc, _) = ccx.ecx.journaled_state.load_account(*callee, &mut ccx.ecx.db)?; // Etches a single byte onto the account if it is empty to circumvent the `extcodesize` // check Solidity might perform. let empty_bytecode = acc.info.code.as_ref().map_or(true, Bytecode::is_empty); if empty_bytecode { let code = Bytecode::new_raw(Bytes::from_static(&[0u8])).to_checked(); - ccx.data.journaled_state.set_code(*callee, code); + ccx.ecx.journaled_state.set_code(*callee, code); } mock_call(ccx.state, callee, data, None, returnData, InstructionResult::Return); @@ -67,7 +68,7 @@ impl Cheatcode for mockCall_0Call { impl Cheatcode for mockCall_1Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { callee, msgValue, data, returnData } = self; - ccx.data.journaled_state.load_account(*callee, ccx.data.db)?; + ccx.ecx.load_account(*callee)?; mock_call(ccx.state, callee, data, Some(msgValue), returnData, InstructionResult::Return); Ok(Default::default()) } diff --git a/crates/cheatcodes/src/evm/prank.rs b/crates/cheatcodes/src/evm/prank.rs index 73269b23d..3e1452d42 100644 --- a/crates/cheatcodes/src/evm/prank.rs +++ b/crates/cheatcodes/src/evm/prank.rs @@ -96,10 +96,10 @@ fn prank( ) -> Result { let prank = Prank::new( ccx.caller, - ccx.data.env.tx.caller, + ccx.ecx.env.tx.caller, *new_caller, new_origin.copied(), - ccx.data.journaled_state.depth(), + ccx.ecx.journaled_state.depth(), single_call, ); diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index f6627114f..3396fc876 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -15,7 +15,7 @@ use crate::{ CheatsConfig, CheatsCtxt, Error, Result, Vm, Vm::AccountAccess, }; -use alloy_primitives::{Address, Bytes, B256, U256, U64}; +use alloy_primitives::{Address, Bytes, Log, B256, U256, U64}; use alloy_rpc_types::request::{TransactionInput, TransactionRequest}; use alloy_sol_types::{SolInterface, SolValue}; use foundry_common::{evm::Breakpoints, provider::alloy::RpcUrl}; @@ -26,10 +26,11 @@ use foundry_evm_core::{ use itertools::Itertools; use revm::{ interpreter::{ - opcode, CallInputs, CallScheme, CreateInputs, Gas, InstructionResult, Interpreter, + opcode, CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, Gas, + InstructionResult, Interpreter, InterpreterAction, InterpreterResult, }, primitives::{BlockEnv, CreateScheme, TransactTo}, - EVMData, Inspector, + EvmContext, Inspector, }; use serde_json::Value; use std::{ @@ -216,7 +217,7 @@ impl Cheatcodes { fn apply_cheatcode( &mut self, - data: &mut EVMData<'_, DB>, + ecx: &mut EvmContext, call: &CallInputs, ) -> Result { // decode the cheatcode call @@ -225,9 +226,9 @@ impl Cheatcodes { // ensure the caller is allowed to execute cheatcodes, // but only if the backend is in forking mode - data.db.ensure_cheatcode_access_forking_mode(&caller)?; + ecx.db.ensure_cheatcode_access_forking_mode(&caller)?; - apply_dispatch(&decoded, &mut CheatsCtxt { state: self, data, caller }) + apply_dispatch(&decoded, &mut CheatsCtxt { state: self, ecx, caller }) } /// Determines the address of the contract and marks it as allowed @@ -237,10 +238,10 @@ impl Cheatcodes { /// automatically we need to determine the new address fn allow_cheatcodes_on_create( &self, - data: &mut EVMData<'_, DB>, + ecx: &mut EvmContext, inputs: &CreateInputs, ) -> Address { - let old_nonce = data + let old_nonce = ecx .journaled_state .state .get(&inputs.caller) @@ -248,13 +249,13 @@ impl Cheatcodes { .unwrap_or_default(); let created_address = inputs.created_address(old_nonce); - if data.journaled_state.depth > 1 && !data.db.has_cheatcode_access(&inputs.caller) { + if ecx.journaled_state.depth > 1 && !ecx.db.has_cheatcode_access(&inputs.caller) { // we only grant cheat code access for new contracts if the caller also has // cheatcode access and the new contract is created in top most call return created_address; } - data.db.allow_cheatcode_access(created_address); + ecx.db.allow_cheatcode_access(created_address); created_address } @@ -263,7 +264,7 @@ impl Cheatcodes { /// /// Cleanup any previously applied cheatcodes that altered the state in such a way that revm's /// revert would run into issues. - pub fn on_revert(&mut self, data: &mut EVMData<'_, DB>) { + pub fn on_revert(&mut self, ecx: &mut EvmContext) { trace!(deals=?self.eth_deals.len(), "rolling back deals"); // Delay revert clean up until expected revert is handled, if set. @@ -272,7 +273,7 @@ impl Cheatcodes { } // we only want to apply cleanup top level - if data.journaled_state.depth() > 0 { + if ecx.journaled_state.depth() > 0 { return; } @@ -280,7 +281,7 @@ impl Cheatcodes { // This will prevent overflow issues in revm's [`JournaledState::journal_revert`] routine // which rolls back any transfers. while let Some(record) = self.eth_deals.pop() { - if let Some(acc) = data.journaled_state.state.get_mut(&record.address) { + if let Some(acc) = ecx.journaled_state.state.get_mut(&record.address) { acc.info.balance = record.old_balance; } } @@ -289,18 +290,18 @@ impl Cheatcodes { impl Inspector for Cheatcodes { #[inline] - fn initialize_interp(&mut self, _: &mut Interpreter<'_>, data: &mut EVMData<'_, DB>) { + fn initialize_interp(&mut self, _: &mut Interpreter, ecx: &mut EvmContext) { // When the first interpreter is initialized we've circumvented the balance and gas checks, // so we apply our actual block data with the correct fees and all. if let Some(block) = self.block.take() { - data.env.block = block; + ecx.env.block = block; } if let Some(gas_price) = self.gas_price.take() { - data.env.tx.gas_price = gas_price; + ecx.env.tx.gas_price = gas_price; } } - fn step(&mut self, interpreter: &mut Interpreter<'_>, data: &mut EVMData<'_, DB>) { + fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext) { self.pc = interpreter.program_counter(); // reset gas if gas metering is turned off @@ -395,26 +396,25 @@ impl Inspector for Cheatcodes { if interpreter.current_opcode() == opcode::SELFDESTRUCT { let target = try_or_continue!(interpreter.stack().peek(0)); // load balance of this account - let value = if let Ok((account, _)) = - data.journaled_state.load_account(interpreter.contract().address, data.db) + let value = ecx + .balance(interpreter.contract().address) + .map(|(b, _)| b) + .unwrap_or(U256::ZERO); + let account = Address::from_word(B256::from(target)); + // get previous balance and initialized status of the target account + // TODO: use load_account_exists + let (initialized, old_balance) = if let Ok((account, _)) = + ecx.journaled_state.load_account(account, &mut ecx.db) { - account.info.balance + (account.info.exists(), account.info.balance) } else { - U256::ZERO + (false, U256::ZERO) }; - let account = Address::from_word(B256::from(target)); - // get previous balance and initialized status of the target account - let (initialized, old_balance) = - if let Ok((account, _)) = data.journaled_state.load_account(account, data.db) { - (account.info.exists(), account.info.balance) - } else { - (false, U256::ZERO) - }; // register access for the target account let access = crate::Vm::AccountAccess { chainInfo: crate::Vm::ChainInfo { - forkId: data.db.active_fork_id().unwrap_or_default(), - chainId: U256::from(data.env.cfg.chain_id), + forkId: ecx.db.active_fork_id().unwrap_or_default(), + chainId: U256::from(ecx.env.cfg.chain_id), }, accessor: interpreter.contract().address, account, @@ -427,7 +427,7 @@ impl Inspector for Cheatcodes { reverted: false, deployedCode: vec![], storageAccesses: vec![], - depth: data.journaled_state.depth(), + depth: ecx.journaled_state.depth(), }; // Ensure that we're not selfdestructing a context recording was initiated on if let Some(last) = account_accesses.last_mut() { @@ -447,9 +447,8 @@ impl Inspector for Cheatcodes { // it's not set (zero value) let mut present_value = U256::ZERO; // Try to load the account and the slot's present value - if data.journaled_state.load_account(address, data.db).is_ok() { - if let Ok((previous, _)) = data.journaled_state.sload(address, key, data.db) - { + if ecx.load_account(address).is_ok() { + if let Ok((previous, _)) = ecx.sload(address, key) { present_value = previous; } } @@ -464,7 +463,7 @@ impl Inspector for Cheatcodes { append_storage_access( recorded_account_diffs_stack, access, - data.journaled_state.depth(), + ecx.journaled_state.depth(), ); } opcode::SSTORE => { @@ -474,9 +473,8 @@ impl Inspector for Cheatcodes { // Try to load the account and the slot's previous value, otherwise, assume it's // not set (zero value) let mut previous_value = U256::ZERO; - if data.journaled_state.load_account(address, data.db).is_ok() { - if let Ok((previous, _)) = data.journaled_state.sload(address, key, data.db) - { + if ecx.load_account(address).is_ok() { + if let Ok((previous, _)) = ecx.sload(address, key) { previous_value = previous; } } @@ -492,7 +490,7 @@ impl Inspector for Cheatcodes { append_storage_access( recorded_account_diffs_stack, access, - data.journaled_state.depth(), + ecx.journaled_state.depth(), ); } // Record account accesses via the EXT family of opcodes @@ -512,7 +510,8 @@ impl Inspector for Cheatcodes { .peek(0)))); let balance; let initialized; - if let Ok((acc, _)) = data.journaled_state.load_account(address, data.db) { + // TODO: use ecx.load_account + if let Ok((acc, _)) = ecx.journaled_state.load_account(address, &mut ecx.db) { initialized = acc.info.exists(); balance = acc.info.balance; } else { @@ -521,8 +520,8 @@ impl Inspector for Cheatcodes { } let account_access = crate::Vm::AccountAccess { chainInfo: crate::Vm::ChainInfo { - forkId: data.db.active_fork_id().unwrap_or_default(), - chainId: U256::from(data.env.cfg.chain_id), + forkId: ecx.db.active_fork_id().unwrap_or_default(), + chainId: U256::from(ecx.env.cfg.chain_id), }, accessor: interpreter.contract().address, account: address, @@ -535,7 +534,7 @@ impl Inspector for Cheatcodes { reverted: false, deployedCode: vec![], storageAccesses: vec![], - depth: data.journaled_state.depth(), + depth: ecx.journaled_state.depth(), }; // Record the EXT* call as an account access at the current depth // (future storage accesses will be recorded in a new "Resume" context) @@ -553,7 +552,7 @@ impl Inspector for Cheatcodes { // if the current opcode can either mutate directly or expand memory. If the opcode at // the current program counter is a match, check if the modified memory lies within the // allowed ranges. If not, revert and fail the test. - if let Some(ranges) = self.allowed_mem_writes.get(&data.journaled_state.depth()) { + if let Some(ranges) = self.allowed_mem_writes.get(&ecx.journaled_state.depth()) { // The `mem_opcode_match` macro is used to match the current opcode against a list of // opcodes that can mutate memory (either directly or expansion via reading). If the // opcode is a match, the memory offsets that are being written to are checked to be @@ -579,7 +578,6 @@ impl Inspector for Cheatcodes { range.contains(&offset) && range.contains(&(offset + 31)) }) { disallowed_mem_write(offset, 32, interpreter, ranges); - interpreter.instruction_result = InstructionResult::Revert; return } } @@ -591,7 +589,6 @@ impl Inspector for Cheatcodes { // unexpectedly mutated. if !ranges.iter().any(|range| range.contains(&offset)) { disallowed_mem_write(offset, 1, interpreter, ranges); - interpreter.instruction_result = InstructionResult::Revert; return } } @@ -611,7 +608,6 @@ impl Inspector for Cheatcodes { range.contains(&offset) && range.contains(&(offset + 31)) }) { disallowed_mem_write(offset, 32, interpreter, ranges); - interpreter.instruction_result = InstructionResult::Revert; return } } @@ -643,7 +639,6 @@ impl Inspector for Cheatcodes { // that gives information about the allowed ranges and revert. if fail_cond { disallowed_mem_write(dest_offset, size, interpreter, ranges); - interpreter.instruction_result = InstructionResult::Revert; return } })* @@ -682,37 +677,47 @@ impl Inspector for Cheatcodes { } } - fn log(&mut self, _: &mut EVMData<'_, DB>, address: &Address, topics: &[B256], data: &Bytes) { + fn log(&mut self, _context: &mut EvmContext, log: &Log) { if !self.expected_emits.is_empty() { - expect::handle_expect_emit(self, address, topics, data); + expect::handle_expect_emit(self, log); } // Stores this log if `recordLogs` has been called if let Some(storage_recorded_logs) = &mut self.recorded_logs { storage_recorded_logs.push(Vm::Log { - topics: topics.to_vec(), - data: data.to_vec(), - emitter: *address, + topics: log.data.topics().to_vec(), + data: log.data.data.to_vec(), + emitter: log.address, }); } } - fn call( - &mut self, - data: &mut EVMData<'_, DB>, - call: &mut CallInputs, - ) -> (InstructionResult, Gas, Bytes) { + fn call(&mut self, ecx: &mut EvmContext, call: &mut CallInputs) -> Option { let gas = Gas::new(call.gas_limit); if call.contract == CHEATCODE_ADDRESS { - return match self.apply_cheatcode(data, call) { - Ok(retdata) => (InstructionResult::Return, gas, retdata.into()), - Err(err) => (InstructionResult::Revert, gas, err.abi_encode().into()), + return match self.apply_cheatcode(ecx, call) { + Ok(retdata) => Some(CallOutcome { + result: InterpreterResult { + result: InstructionResult::Return, + output: retdata.into(), + gas, + }, + memory_offset: call.return_memory_offset.clone(), + }), + Err(err) => Some(CallOutcome { + result: InterpreterResult { + result: InstructionResult::Revert, + output: err.abi_encode().into(), + gas, + }, + memory_offset: call.return_memory_offset.clone(), + }), }; } if call.contract == HARDHAT_CONSOLE_ADDRESS { - return (InstructionResult::Continue, gas, Bytes::new()); + return None } // Handle expected calls @@ -755,19 +760,26 @@ impl Inspector for Cheatcodes { }) .map(|(_, v)| v) }) { - return (return_data.ret_type, gas, return_data.data.clone()); + return Some(CallOutcome { + result: InterpreterResult { + result: return_data.ret_type, + output: return_data.data.clone(), + gas, + }, + memory_offset: call.return_memory_offset.clone(), + }) } } // Apply our prank if let Some(prank) = &self.prank { - if data.journaled_state.depth() >= prank.depth && + if ecx.journaled_state.depth() >= prank.depth && call.context.caller == prank.prank_caller { let mut prank_applied = false; // At the target depth we set `msg.sender` - if data.journaled_state.depth() == prank.depth { + if ecx.journaled_state.depth() == prank.depth { call.context.caller = prank.new_caller; call.transfer.source = prank.new_caller; prank_applied = true; @@ -775,7 +787,7 @@ impl Inspector for Cheatcodes { // At the target depth, or deeper, we set `tx.origin` if let Some(new_origin) = prank.new_origin { - data.env.tx.caller = new_origin; + ecx.env.tx.caller = new_origin; prank_applied = true; } @@ -794,13 +806,13 @@ impl Inspector for Cheatcodes { // // We do this because any subsequent contract calls *must* exist on chain and // we only want to grab *this* call, not internal ones - if data.journaled_state.depth() == broadcast.depth && + if ecx.journaled_state.depth() == broadcast.depth && call.context.caller == broadcast.original_caller { // At the target depth we set `msg.sender` & tx.origin. // We are simulating the caller as being an EOA, so *both* must be set to the // broadcast.origin. - data.env.tx.caller = broadcast.new_origin; + ecx.env.tx.caller = broadcast.new_origin; call.context.caller = broadcast.new_origin; call.transfer.source = broadcast.new_origin; @@ -809,19 +821,24 @@ impl Inspector for Cheatcodes { // into 1559, in the cli package, relatively easily once we // know the target chain supports EIP-1559. if !call.is_static { - if let Err(err) = - data.journaled_state.load_account(broadcast.new_origin, data.db) - { - return (InstructionResult::Revert, gas, Error::encode(err)); + if let Err(err) = ecx.load_account(broadcast.new_origin) { + return Some(CallOutcome { + result: InterpreterResult { + result: InstructionResult::Revert, + output: Error::encode(err), + gas, + }, + memory_offset: call.return_memory_offset.clone(), + }) } - let is_fixed_gas_limit = check_if_fixed_gas_limit(data, call.gas_limit); + let is_fixed_gas_limit = check_if_fixed_gas_limit(ecx, call.gas_limit); let account = - data.journaled_state.state().get_mut(&broadcast.new_origin).unwrap(); + ecx.journaled_state.state().get_mut(&broadcast.new_origin).unwrap(); self.broadcastable_transactions.push_back(BroadcastableTransaction { - rpc: data.db.active_fork_url(), + rpc: ecx.db.active_fork_url(), transaction: TransactionRequest { from: Some(broadcast.new_origin), to: Some(call.contract), @@ -846,7 +863,14 @@ impl Inspector for Cheatcodes { debug!(target: "cheatcodes", address=%broadcast.new_origin, nonce=prev+1, prev, "incremented nonce"); } else if broadcast.single_call { let msg = "`staticcall`s are not allowed after `broadcast`; use `startBroadcast` instead"; - return (InstructionResult::Revert, Gas::new(0), Error::encode(msg)); + return Some(CallOutcome { + result: InterpreterResult { + result: InstructionResult::Revert, + output: Error::encode(msg), + gas, + }, + memory_offset: call.return_memory_offset.clone(), + }) } } } @@ -857,7 +881,8 @@ impl Inspector for Cheatcodes { // nonce, a non-zero KECCAK_EMPTY codehash, or non-empty code let initialized; let old_balance; - if let Ok((acc, _)) = data.journaled_state.load_account(call.contract, data.db) { + // TODO: use ecx.load_account + if let Ok((acc, _)) = ecx.journaled_state.load_account(call.contract, &mut ecx.db) { initialized = acc.info.exists(); old_balance = acc.info.balance; } else { @@ -877,8 +902,8 @@ impl Inspector for Cheatcodes { // as "warm" if the call from which they were accessed is reverted recorded_account_diffs_stack.push(vec![AccountAccess { chainInfo: crate::Vm::ChainInfo { - forkId: data.db.active_fork_id().unwrap_or_default(), - chainId: U256::from(data.env.cfg.chain_id), + forkId: ecx.db.active_fork_id().unwrap_or_default(), + chainId: U256::from(ecx.env.cfg.chain_id), }, accessor: call.context.caller, account: call.contract, @@ -891,21 +916,19 @@ impl Inspector for Cheatcodes { reverted: false, deployedCode: vec![], storageAccesses: vec![], // updated on step - depth: data.journaled_state.depth(), + depth: ecx.journaled_state.depth(), }]); } - (InstructionResult::Continue, gas, Bytes::new()) + None } fn call_end( &mut self, - data: &mut EVMData<'_, DB>, + ecx: &mut EvmContext, call: &CallInputs, - remaining_gas: Gas, - status: InstructionResult, - retdata: Bytes, - ) -> (InstructionResult, Gas, Bytes) { + mut outcome: CallOutcome, + ) -> CallOutcome { let cheatcode_call = call.contract == CHEATCODE_ADDRESS || call.contract == HARDHAT_CONSOLE_ADDRESS; @@ -915,8 +938,8 @@ impl Inspector for Cheatcodes { if !cheatcode_call { // Clean up pranks if let Some(prank) = &self.prank { - if data.journaled_state.depth() == prank.depth { - data.env.tx.caller = prank.prank_origin; + if ecx.journaled_state.depth() == prank.depth { + ecx.env.tx.caller = prank.prank_origin; // Clean single-call prank once we have returned to the original depth if prank.single_call { @@ -927,8 +950,8 @@ impl Inspector for Cheatcodes { // Clean up broadcast if let Some(broadcast) = &self.broadcast { - if data.journaled_state.depth() == broadcast.depth { - data.env.tx.caller = broadcast.original_origin; + if ecx.journaled_state.depth() == broadcast.depth { + ecx.env.tx.caller = broadcast.original_origin; // Clean single-call broadcast once we have returned to the original depth if broadcast.single_call { @@ -940,7 +963,7 @@ impl Inspector for Cheatcodes { // Handle expected reverts if let Some(expected_revert) = &self.expected_revert { - if data.journaled_state.depth() <= expected_revert.depth { + if ecx.journaled_state.depth() <= expected_revert.depth { let needs_processing: bool = match expected_revert.kind { ExpectedRevertKind::Default => !cheatcode_call, // `pending_processing` == true means that we're in the `call_end` hook for @@ -955,14 +978,20 @@ impl Inspector for Cheatcodes { return match expect::handle_expect_revert( false, expected_revert.reason.as_deref(), - status, - retdata, + outcome.result.result, + outcome.result.output.clone(), ) { Err(error) => { - trace!(expected=?expected_revert, ?error, ?status, "Expected revert mismatch"); - (InstructionResult::Revert, remaining_gas, error.abi_encode().into()) + trace!(expected=?expected_revert, ?error, status=?outcome.result.result, "Expected revert mismatch"); + outcome.result.result = InstructionResult::Revert; + outcome.result.output = error.abi_encode().into(); + outcome + } + Ok((_, retdata)) => { + outcome.result.result = InstructionResult::Return; + outcome.result.output = retdata; + outcome } - Ok((_, retdata)) => (InstructionResult::Return, remaining_gas, retdata), }; } @@ -981,19 +1010,19 @@ impl Inspector for Cheatcodes { // Exit early for calls to cheatcodes as other logic is not relevant for cheatcode // invocations if cheatcode_call { - return (status, remaining_gas, retdata); + return outcome; } // If `startStateDiffRecording` has been called, update the `reverted` status of the // previous call depth's recorded accesses, if any if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack { // The root call cannot be recorded. - if data.journaled_state.depth() > 0 { + if ecx.journaled_state.depth() > 0 { let mut last_recorded_depth = recorded_account_diffs_stack.pop().expect("missing CALL account accesses"); // Update the reverted status of all deeper calls if this call reverted, in // accordance with EVM behavior - if status.is_revert() { + if outcome.result.is_revert() { last_recorded_depth.iter_mut().for_each(|element| { element.reverted = true; element @@ -1006,8 +1035,10 @@ impl Inspector for Cheatcodes { // Assert that we're at the correct depth before recording post-call state changes. // Depending on the depth the cheat was called at, there may not be any pending // calls to update if execution has percolated up to a higher depth. - if call_access.depth == data.journaled_state.depth() { - if let Ok((acc, _)) = data.journaled_state.load_account(call.contract, data.db) + if call_access.depth == ecx.journaled_state.depth() { + // TODO: use ecx.load_account + if let Ok((acc, _)) = + ecx.journaled_state.load_account(call.contract, &mut ecx.db) { debug_assert!(access_is_call(call_access.kind)); call_access.newBalance = acc.info.balance; @@ -1038,17 +1069,15 @@ impl Inspector for Cheatcodes { let should_check_emits = self .expected_emits .iter() - .any(|expected| expected.depth == data.journaled_state.depth()) && + .any(|expected| expected.depth == ecx.journaled_state.depth()) && // Ignore staticcalls !call.is_static; if should_check_emits { // Not all emits were matched. if self.expected_emits.iter().any(|expected| !expected.found) { - return ( - InstructionResult::Revert, - remaining_gas, - "log != expected log".abi_encode().into(), - ); + outcome.result.result = InstructionResult::Revert; + outcome.result.output = "log != expected log".abi_encode().into(); + return outcome } else { // All emits were found, we're good. // Clear the queue, as we expect the user to declare more events for the next call @@ -1063,33 +1092,34 @@ impl Inspector for Cheatcodes { // if there's a revert and a previous call was diagnosed as fork related revert then we can // return a better error here - if status == InstructionResult::Revert { + if outcome.result.is_revert() { if let Some(err) = diag { - return (status, remaining_gas, Error::encode(err.to_error_msg(&self.labels))); + outcome.result.output = Error::encode(err.to_error_msg(&self.labels)); + return outcome } } // try to diagnose reverts in multi-fork mode where a call is made to an address that does // not exist - if let TransactTo::Call(test_contract) = data.env.tx.transact_to { + if let TransactTo::Call(test_contract) = ecx.env.tx.transact_to { // if a call to a different contract than the original test contract returned with // `Stop` we check if the contract actually exists on the active fork - if data.db.is_forked_mode() && - status == InstructionResult::Stop && + if ecx.db.is_forked_mode() && + outcome.result.result == InstructionResult::Stop && call.contract != test_contract { self.fork_revert_diagnostic = - data.db.diagnose_revert(call.contract, &data.journaled_state); + ecx.db.diagnose_revert(call.contract, &ecx.journaled_state); } } // If the depth is 0, then this is the root call terminating - if data.journaled_state.depth() == 0 { + if ecx.journaled_state.depth() == 0 { // If we already have a revert, we shouldn't run the below logic as it can obfuscate an // earlier error that happened first with unrelated information about // another error when using cheatcodes. - if status == InstructionResult::Revert { - return (status, remaining_gas, retdata); + if outcome.result.is_revert() { + return outcome; } // If there's not a revert, we can continue on to run the last logic for expect* @@ -1121,7 +1151,7 @@ impl Inspector for Cheatcodes { .into_iter() .flatten() .join(", "); - let but = if status.is_ok() { + let but = if outcome.result.is_ok() { let s = if *actual_count == 1 { "" } else { "s" }; format!("was called {actual_count} time{s}") } else { @@ -1134,7 +1164,10 @@ impl Inspector for Cheatcodes { "expected call to {address} with {expected_values} \ to be called {count} time{s}, but {but}" ); - return (InstructionResult::Revert, remaining_gas, Error::encode(msg)); + outcome.result.result = InstructionResult::Revert; + outcome.result.output = Error::encode(msg); + + return outcome; } } } @@ -1144,64 +1177,75 @@ impl Inspector for Cheatcodes { self.expected_emits.retain(|expected| !expected.found); // If not empty, we got mismatched emits if !self.expected_emits.is_empty() { - let msg = if status.is_ok() { + let msg = if outcome.result.is_ok() { "expected an emit, but no logs were emitted afterwards. \ you might have mismatched events or not enough events were emitted" } else { "expected an emit, but the call reverted instead. \ ensure you're testing the happy path when using `expectEmit`" }; - return (InstructionResult::Revert, remaining_gas, Error::encode(msg)); + outcome.result.result = InstructionResult::Revert; + outcome.result.output = Error::encode(msg); + return outcome; } } - (status, remaining_gas, retdata) + outcome } fn create( &mut self, - data: &mut EVMData<'_, DB>, + ecx: &mut EvmContext, call: &mut CreateInputs, - ) -> (InstructionResult, Option
, Gas, Bytes) { + ) -> Option { let gas = Gas::new(call.gas_limit); // Apply our prank if let Some(prank) = &self.prank { - if data.journaled_state.depth() >= prank.depth && call.caller == prank.prank_caller { + if ecx.journaled_state.depth() >= prank.depth && call.caller == prank.prank_caller { // At the target depth we set `msg.sender` - if data.journaled_state.depth() == prank.depth { + if ecx.journaled_state.depth() == prank.depth { call.caller = prank.new_caller; } // At the target depth, or deeper, we set `tx.origin` if let Some(new_origin) = prank.new_origin { - data.env.tx.caller = new_origin; + ecx.env.tx.caller = new_origin; } } } // Apply our broadcast if let Some(broadcast) = &self.broadcast { - if data.journaled_state.depth() >= broadcast.depth && + if ecx.journaled_state.depth() >= broadcast.depth && call.caller == broadcast.original_caller { - if let Err(err) = data.journaled_state.load_account(broadcast.new_origin, data.db) { - return (InstructionResult::Revert, None, gas, Error::encode(err)); + if let Err(err) = + ecx.journaled_state.load_account(broadcast.new_origin, &mut ecx.db) + { + return Some(CreateOutcome { + result: InterpreterResult { + result: InstructionResult::Revert, + output: Error::encode(err), + gas, + }, + address: None, + }) } - data.env.tx.caller = broadcast.new_origin; + ecx.env.tx.caller = broadcast.new_origin; - if data.journaled_state.depth() == broadcast.depth { + if ecx.journaled_state.depth() == broadcast.depth { let (bytecode, to, nonce) = process_broadcast_create( broadcast.new_origin, call.init_code.clone(), - data, + ecx, call, ); - let is_fixed_gas_limit = check_if_fixed_gas_limit(data, call.gas_limit); + let is_fixed_gas_limit = check_if_fixed_gas_limit(ecx, call.gas_limit); self.broadcastable_transactions.push_back(BroadcastableTransaction { - rpc: data.db.active_fork_url(), + rpc: ecx.db.active_fork_url(), transaction: TransactionRequest { from: Some(broadcast.new_origin), to, @@ -1228,26 +1272,35 @@ impl Inspector for Cheatcodes { // Apply the Create2 deployer if self.broadcast.is_some() || self.config.always_use_create_2_factory { match apply_create2_deployer( - data, + ecx, call, self.prank.as_ref(), self.broadcast.as_ref(), self.recorded_account_diffs_stack.as_mut(), ) { Ok(_) => {} - Err(err) => return (InstructionResult::Revert, None, gas, Error::encode(err)), + Err(err) => { + return Some(CreateOutcome { + result: InterpreterResult { + result: InstructionResult::Revert, + output: Error::encode(err), + gas, + }, + address: None, + }) + } }; } // allow cheatcodes from the address of the new contract // Compute the address *after* any possible broadcast updates, so it's based on the updated // call inputs - let address = self.allow_cheatcodes_on_create(data, call); + let address = self.allow_cheatcodes_on_create(ecx, call); // If `recordAccountAccesses` has been called, record the create if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack { // If the create scheme is create2, and the caller is the DEFAULT_CREATE2_DEPLOYER then // we must add 1 to the depth to account for the call to the create2 factory. - let mut depth = data.journaled_state.depth(); + let mut depth = ecx.journaled_state.depth(); if let CreateScheme::Create2 { salt: _ } = call.scheme { if call.caller == DEFAULT_CREATE2_DEPLOYER { depth += 1; @@ -1258,8 +1311,8 @@ impl Inspector for Cheatcodes { // subsequent account accesses recorded_account_diffs_stack.push(vec![AccountAccess { chainInfo: crate::Vm::ChainInfo { - forkId: data.db.active_fork_id().unwrap_or_default(), - chainId: U256::from(data.env.cfg.chain_id), + forkId: ecx.db.active_fork_id().unwrap_or_default(), + chainId: U256::from(ecx.env.cfg.chain_id), }, accessor: call.caller, account: address, @@ -1276,22 +1329,19 @@ impl Inspector for Cheatcodes { }]); } - (InstructionResult::Continue, None, gas, Bytes::new()) + None } fn create_end( &mut self, - data: &mut EVMData<'_, DB>, - _: &CreateInputs, - status: InstructionResult, - address: Option
, - remaining_gas: Gas, - retdata: Bytes, - ) -> (InstructionResult, Option
, Gas, Bytes) { + ecx: &mut EvmContext, + _call: &CreateInputs, + mut outcome: CreateOutcome, + ) -> CreateOutcome { // Clean up pranks if let Some(prank) = &self.prank { - if data.journaled_state.depth() == prank.depth { - data.env.tx.caller = prank.prank_origin; + if ecx.journaled_state.depth() == prank.depth { + ecx.env.tx.caller = prank.prank_origin; // Clean single-call prank once we have returned to the original depth if prank.single_call { @@ -1302,8 +1352,8 @@ impl Inspector for Cheatcodes { // Clean up broadcasts if let Some(broadcast) = &self.broadcast { - if data.journaled_state.depth() == broadcast.depth { - data.env.tx.caller = broadcast.original_origin; + if ecx.journaled_state.depth() == broadcast.depth { + ecx.env.tx.caller = broadcast.original_origin; // Clean single-call broadcast once we have returned to the original depth if broadcast.single_call { @@ -1314,21 +1364,26 @@ impl Inspector for Cheatcodes { // Handle expected reverts if let Some(expected_revert) = &self.expected_revert { - if data.journaled_state.depth() <= expected_revert.depth && + if ecx.journaled_state.depth() <= expected_revert.depth && matches!(expected_revert.kind, ExpectedRevertKind::Default) { let expected_revert = std::mem::take(&mut self.expected_revert).unwrap(); return match expect::handle_expect_revert( true, expected_revert.reason.as_deref(), - status, - retdata, + outcome.result.result, + outcome.result.output.clone(), ) { Ok((address, retdata)) => { - (InstructionResult::Return, address, remaining_gas, retdata) + outcome.result.result = InstructionResult::Return; + outcome.result.output = retdata; + outcome.address = address; + outcome } Err(err) => { - (InstructionResult::Revert, None, remaining_gas, err.abi_encode().into()) + outcome.result.result = InstructionResult::Revert; + outcome.result.output = err.abi_encode().into(); + outcome } }; } @@ -1338,12 +1393,12 @@ impl Inspector for Cheatcodes { // previous call depth's recorded accesses, if any if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack { // The root call cannot be recorded. - if data.journaled_state.depth() > 0 { + if ecx.journaled_state.depth() > 0 { let mut last_depth = recorded_account_diffs_stack.pop().expect("missing CREATE account accesses"); // Update the reverted status of all deeper calls if this call reverted, in // accordance with EVM behavior - if status.is_revert() { + if outcome.result.is_revert() { last_depth.iter_mut().for_each(|element| { element.reverted = true; element @@ -1357,14 +1412,14 @@ impl Inspector for Cheatcodes { // changes. Depending on what depth the cheat was called at, there // may not be any pending calls to update if execution has // percolated up to a higher depth. - if create_access.depth == data.journaled_state.depth() { + if create_access.depth == ecx.journaled_state.depth() { debug_assert_eq!( create_access.kind as u8, crate::Vm::AccountAccessKind::Create as u8 ); - if let Some(address) = address { + if let Some(address) = outcome.address { if let Ok((created_acc, _)) = - data.journaled_state.load_account(address, data.db) + ecx.journaled_state.load_account(address, &mut ecx.db) { create_access.newBalance = created_acc.info.balance; create_access.deployedCode = created_acc @@ -1388,36 +1443,36 @@ impl Inspector for Cheatcodes { } } - (status, address, remaining_gas, retdata) + outcome } } /// Helper that expands memory, stores a revert string pertaining to a disallowed memory write, /// and sets the return range to the revert string's location in memory. +/// +/// This will set the interpreter's next action to a return with the revert string as the output. +/// And trigger a revert. fn disallowed_mem_write( dest_offset: u64, size: u64, - interpreter: &mut Interpreter<'_>, + interpreter: &mut Interpreter, ranges: &[Range], ) { let revert_string = format!( "memory write at offset 0x{:02X} of size 0x{:02X} not allowed; safe range: {}", dest_offset, size, - ranges.iter().map(|r| format!("(0x{:02X}, 0x{:02X}]", r.start, r.end)).join(" ∪ ") - ) - .abi_encode(); - mstore_revert_string(interpreter, &revert_string); -} - -/// Expands memory, stores a revert string, and sets the return range to the revert -/// string's location in memory. -fn mstore_revert_string(interpreter: &mut Interpreter<'_>, bytes: &[u8]) { - let starting_offset = interpreter.shared_memory.len(); - interpreter.shared_memory.resize(starting_offset + bytes.len()); - interpreter.shared_memory.set_data(starting_offset, 0, bytes.len(), bytes); - interpreter.return_offset = starting_offset; - interpreter.return_len = interpreter.shared_memory.len() - starting_offset + ranges.iter().map(|r| format!("(0x{:02X}, 0x{:02X}]", r.start, r.end)).join(" U ") + ); + + interpreter.instruction_result = InstructionResult::Revert; + interpreter.next_action = InterpreterAction::Return { + result: InterpreterResult { + output: Error::encode(revert_string), + gas: interpreter.gas, + result: InstructionResult::Revert, + }, + }; } /// Applies the default CREATE2 deployer for contract creation. @@ -1430,7 +1485,7 @@ fn mstore_revert_string(interpreter: &mut Interpreter<'_>, bytes: &[u8]) { /// Returns a `DatabaseError::MissingCreate2Deployer` if the `DEFAULT_CREATE2_DEPLOYER` account is /// not found or if it does not have any associated bytecode. fn apply_create2_deployer( - data: &mut EVMData<'_, DB>, + ecx: &mut EvmContext, call: &mut CreateInputs, prank: Option<&Prank>, broadcast: Option<&Broadcast>, @@ -1446,14 +1501,14 @@ fn apply_create2_deployer( // If the create scheme is Create2 and the depth equals the broadcast/prank/default // depth, then use the default create2 factory as the deployer - if data.journaled_state.depth() == base_depth { + if ecx.journaled_state.depth() == base_depth { // Record the call to the create2 factory in the state diff if let Some(recorded_account_diffs_stack) = diffs_stack { let calldata = [&salt.to_be_bytes::<32>()[..], &call.init_code[..]].concat(); recorded_account_diffs_stack.push(vec![AccountAccess { chainInfo: crate::Vm::ChainInfo { - forkId: data.db.active_fork_id().unwrap_or_default(), - chainId: U256::from(data.env.cfg.chain_id), + forkId: ecx.db.active_fork_id().unwrap_or_default(), + chainId: U256::from(ecx.env.cfg.chain_id), }, accessor: call.caller, account: DEFAULT_CREATE2_DEPLOYER, @@ -1466,16 +1521,17 @@ fn apply_create2_deployer( reverted: false, deployedCode: vec![], // updated on create_end storageAccesses: vec![], // updated on create_end - depth: data.journaled_state.depth(), + depth: ecx.journaled_state.depth(), }]) } // Sanity checks for our CREATE2 deployer + // TODO: use ecx.load_account let info = - &data.journaled_state.load_account(DEFAULT_CREATE2_DEPLOYER, data.db)?.0.info; + &ecx.journaled_state.load_account(DEFAULT_CREATE2_DEPLOYER, &mut ecx.db)?.0.info; match &info.code { Some(code) if code.is_empty() => return Err(DatabaseError::MissingCreate2Deployer), - None if data.db.code_by_hash(info.code_hash)?.is_empty() => { + None if ecx.db.code_by_hash(info.code_hash)?.is_empty() => { return Err(DatabaseError::MissingCreate2Deployer) } _ => {} @@ -1499,18 +1555,18 @@ fn apply_create2_deployer( fn process_broadcast_create( broadcast_sender: Address, bytecode: Bytes, - data: &mut EVMData<'_, DB>, + ecx: &mut EvmContext, call: &mut CreateInputs, ) -> (Bytes, Option
, u64) { call.caller = broadcast_sender; match call.scheme { CreateScheme::Create => { - (bytecode, None, data.journaled_state.account(broadcast_sender).info.nonce) + (bytecode, None, ecx.journaled_state.account(broadcast_sender).info.nonce) } CreateScheme::Create2 { salt } => { // We have to increment the nonce of the user address, since this create2 will be done // by the create2_deployer - let account = data.journaled_state.state().get_mut(&broadcast_sender).unwrap(); + let account = ecx.journaled_state.state().get_mut(&broadcast_sender).unwrap(); let prev = account.info.nonce; // Touch account to ensure that incremented nonce is committed account.mark_touch(); @@ -1525,7 +1581,7 @@ fn process_broadcast_create( // Determines if the gas limit on a given call was manually set in the script and should therefore // not be overwritten by later estimations -fn check_if_fixed_gas_limit(data: &EVMData<'_, DB>, call_gas_limit: u64) -> bool { +fn check_if_fixed_gas_limit(data: &EvmContext, call_gas_limit: u64) -> bool { // If the gas limit was not set in the source code it is set to the estimated gas left at the // time of the call, which should be rather close to configured gas limit. // TODO: Find a way to reliably make this determination. diff --git a/crates/cheatcodes/src/lib.rs b/crates/cheatcodes/src/lib.rs index a2654e953..a9dda80f9 100644 --- a/crates/cheatcodes/src/lib.rs +++ b/crates/cheatcodes/src/lib.rs @@ -11,25 +11,22 @@ pub extern crate foundry_cheatcodes_spec as spec; extern crate tracing; use alloy_primitives::Address; -use foundry_evm_core::backend::DatabaseExt; -use revm::EVMData; +use revm::EvmContext; +pub use config::CheatsConfig; +pub use error::{Error, ErrorKind, Result}; +use foundry_evm_core::backend::DatabaseExt; +pub use inspector::{BroadcastableTransaction, BroadcastableTransactions, Cheatcodes, Context}; pub use spec::{CheatcodeDef, Vm}; #[macro_use] mod error; -pub use error::{Error, ErrorKind, Result}; - -mod config; -pub use config::CheatsConfig; - -mod inspector; -pub use inspector::{BroadcastableTransaction, BroadcastableTransactions, Cheatcodes, Context}; - mod base64; +mod config; mod env; mod evm; mod fs; +mod inspector; mod json; mod script; mod string; @@ -112,18 +109,18 @@ impl DynCheatcode for T { } /// The cheatcode context, used in [`Cheatcode`]. -pub(crate) struct CheatsCtxt<'a, 'b, 'c, DB: DatabaseExt> { +pub(crate) struct CheatsCtxt<'a, 'b, DB: DatabaseExt> { /// The cheatcodes inspector state. pub(crate) state: &'a mut Cheatcodes, /// The EVM data. - pub(crate) data: &'b mut EVMData<'c, DB>, + pub(crate) ecx: &'b mut EvmContext, /// The original `msg.sender`. pub(crate) caller: Address, } -impl CheatsCtxt<'_, '_, '_, DB> { +impl CheatsCtxt<'_, '_, DB> { #[inline] pub(crate) fn is_precompile(&self, address: &Address) -> bool { - self.data.precompiles.contains(address) + self.ecx.precompiles.contains(address) } } diff --git a/crates/cheatcodes/src/script.rs b/crates/cheatcodes/src/script.rs index 62a751837..4b3e6ba48 100644 --- a/crates/cheatcodes/src/script.rs +++ b/crates/cheatcodes/src/script.rs @@ -145,10 +145,10 @@ fn broadcast( } let broadcast = Broadcast { - new_origin: new_origin.unwrap_or(ccx.data.env.tx.caller), + new_origin: new_origin.unwrap_or(ccx.ecx.env.tx.caller), original_caller: ccx.caller, - original_origin: ccx.data.env.tx.caller, - depth: ccx.data.journaled_state.depth(), + original_origin: ccx.ecx.env.tx.caller, + depth: ccx.ecx.journaled_state.depth(), single_call, }; debug!(target: "cheatcodes", ?broadcast, "started"); @@ -181,9 +181,9 @@ fn broadcast_key( /// That leads to its nonce being incremented by `call_raw`. In a `broadcast` scenario this is /// undesirable. Therefore, we make sure to fix the sender's nonce **once**. pub(super) fn correct_sender_nonce(ccx: &mut CheatsCtxt) -> Result<()> { - let sender = ccx.data.env.tx.caller; + let sender = ccx.ecx.env.tx.caller; if !ccx.state.corrected_nonce && sender != Config::DEFAULT_SENDER { - let account = super::evm::journaled_account(ccx.data, sender)?; + let account = super::evm::journaled_account(ccx.ecx, sender)?; let prev = account.info.nonce; account.info.nonce = prev.saturating_sub(1); debug!(target: "cheatcodes", %sender, nonce=account.info.nonce, prev, "corrected nonce"); diff --git a/crates/cheatcodes/src/test.rs b/crates/cheatcodes/src/test.rs index 0d557db22..18487c0aa 100644 --- a/crates/cheatcodes/src/test.rs +++ b/crates/cheatcodes/src/test.rs @@ -69,7 +69,7 @@ impl Cheatcode for skipCall { if skipTest { // Skip should not work if called deeper than at test level. // Since we're not returning the magic skip bytes, this will cause a test failure. - ensure!(ccx.data.journaled_state.depth() <= 1, "`skip` can only be used at test level"); + ensure!(ccx.ecx.journaled_state.depth() <= 1, "`skip` can only be used at test level"); Err(MAGIC_SKIP.into()) } else { Ok(Default::default()) diff --git a/crates/cheatcodes/src/test/expect.rs b/crates/cheatcodes/src/test/expect.rs index e4cc34986..02c747adb 100644 --- a/crates/cheatcodes/src/test/expect.rs +++ b/crates/cheatcodes/src/test/expect.rs @@ -1,5 +1,5 @@ use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Result, Vm::*}; -use alloy_primitives::{address, Address, Bytes, LogData as RawLog, B256, U256}; +use alloy_primitives::{address, Address, Bytes, LogData as RawLog, U256}; use alloy_sol_types::{SolError, SolValue}; use revm::interpreter::{return_ok, InstructionResult}; use spec::Vm; @@ -201,7 +201,7 @@ impl Cheatcode for expectEmit_0Call { let Self { checkTopic1, checkTopic2, checkTopic3, checkData } = *self; expect_emit( ccx.state, - ccx.data.journaled_state.depth(), + ccx.ecx.journaled_state.depth(), [checkTopic1, checkTopic2, checkTopic3, checkData], None, ) @@ -213,7 +213,7 @@ impl Cheatcode for expectEmit_1Call { let Self { checkTopic1, checkTopic2, checkTopic3, checkData, emitter } = *self; expect_emit( ccx.state, - ccx.data.journaled_state.depth(), + ccx.ecx.journaled_state.depth(), [checkTopic1, checkTopic2, checkTopic3, checkData], Some(emitter), ) @@ -223,69 +223,69 @@ impl Cheatcode for expectEmit_1Call { impl Cheatcode for expectEmit_2Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; - expect_emit(ccx.state, ccx.data.journaled_state.depth(), [true; 4], None) + expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 4], None) } } impl Cheatcode for expectEmit_3Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { emitter } = *self; - expect_emit(ccx.state, ccx.data.journaled_state.depth(), [true; 4], Some(emitter)) + expect_emit(ccx.state, ccx.ecx.journaled_state.depth(), [true; 4], Some(emitter)) } } impl Cheatcode for expectRevert_0Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; - expect_revert(ccx.state, None, ccx.data.journaled_state.depth(), false) + expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), false) } } impl Cheatcode for expectRevert_1Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { revertData } = self; - expect_revert(ccx.state, Some(revertData.as_ref()), ccx.data.journaled_state.depth(), false) + expect_revert(ccx.state, Some(revertData.as_ref()), ccx.ecx.journaled_state.depth(), false) } } impl Cheatcode for expectRevert_2Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { revertData } = self; - expect_revert(ccx.state, Some(revertData), ccx.data.journaled_state.depth(), false) + expect_revert(ccx.state, Some(revertData), ccx.ecx.journaled_state.depth(), false) } } impl Cheatcode for _expectCheatcodeRevert_0Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { - expect_revert(ccx.state, None, ccx.data.journaled_state.depth(), true) + expect_revert(ccx.state, None, ccx.ecx.journaled_state.depth(), true) } } impl Cheatcode for _expectCheatcodeRevert_1Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { revertData } = self; - expect_revert(ccx.state, Some(revertData.as_ref()), ccx.data.journaled_state.depth(), true) + expect_revert(ccx.state, Some(revertData.as_ref()), ccx.ecx.journaled_state.depth(), true) } } impl Cheatcode for _expectCheatcodeRevert_2Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { revertData } = self; - expect_revert(ccx.state, Some(revertData), ccx.data.journaled_state.depth(), true) + expect_revert(ccx.state, Some(revertData), ccx.ecx.journaled_state.depth(), true) } } impl Cheatcode for expectSafeMemoryCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { min, max } = *self; - expect_safe_memory(ccx.state, min, max, ccx.data.journaled_state.depth()) + expect_safe_memory(ccx.state, min, max, ccx.ecx.journaled_state.depth()) } } impl Cheatcode for stopExpectSafeMemoryCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self {} = self; - ccx.state.allowed_mem_writes.remove(&ccx.data.journaled_state.depth()); + ccx.state.allowed_mem_writes.remove(&ccx.ecx.journaled_state.depth()); Ok(Default::default()) } } @@ -293,7 +293,7 @@ impl Cheatcode for stopExpectSafeMemoryCall { impl Cheatcode for expectSafeMemoryCallCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { min, max } = *self; - expect_safe_memory(ccx.state, min, max, ccx.data.journaled_state.depth() + 1) + expect_safe_memory(ccx.state, min, max, ccx.ecx.journaled_state.depth() + 1) } } @@ -398,12 +398,7 @@ fn expect_emit( Ok(Default::default()) } -pub(crate) fn handle_expect_emit( - state: &mut Cheatcodes, - address: &Address, - topics: &[B256], - data: &Bytes, -) { +pub(crate) fn handle_expect_emit(state: &mut Cheatcodes, log: &alloy_primitives::Log) { // Fill or check the expected emits. // We expect for emit checks to be filled as they're declared (from oldest to newest), // so we fill them and push them to the back of the queue. @@ -431,20 +426,21 @@ pub(crate) fn handle_expect_emit( let Some(expected) = &event_to_fill_or_check.log else { // Fill the event. - event_to_fill_or_check.log = Some(RawLog::new_unchecked(topics.to_vec(), data.clone())); + event_to_fill_or_check.log = Some(log.data.clone()); state.expected_emits.push_back(event_to_fill_or_check); return }; let expected_topic_0 = expected.topics().first(); - let log_topic_0 = topics.first(); + let log_topic_0 = log.topics().first(); if expected_topic_0 .zip(log_topic_0) - .map_or(false, |(a, b)| a == b && expected.topics().len() == topics.len()) + .map_or(false, |(a, b)| a == b && expected.topics().len() == log.topics().len()) { // Match topics - event_to_fill_or_check.found = topics + event_to_fill_or_check.found = log + .topics() .iter() .skip(1) .enumerate() @@ -453,12 +449,12 @@ pub(crate) fn handle_expect_emit( // Maybe match source address if let Some(addr) = event_to_fill_or_check.address { - event_to_fill_or_check.found &= addr == *address; + event_to_fill_or_check.found &= addr == log.address; } // Maybe match data if event_to_fill_or_check.checks[3] { - event_to_fill_or_check.found &= expected.data == *data; + event_to_fill_or_check.found &= expected.data.as_ref() == log.data.data.as_ref(); } } diff --git a/crates/chisel/src/dispatcher.rs b/crates/chisel/src/dispatcher.rs index b6a4411ac..cba66f7aa 100644 --- a/crates/chisel/src/dispatcher.rs +++ b/crates/chisel/src/dispatcher.rs @@ -438,7 +438,7 @@ impl ChiselDispatcher { println!( "{}: {}", Paint::yellow(format!("[{}]", stack.len() - i - 1)), - Paint::cyan(format!("0x{:02x}", stack.data()[i])) + Paint::cyan(format!("0x{:02x}", stack[i])) ); }); } diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index 6d76d5b0e..69e35f947 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -236,7 +236,7 @@ impl SessionSource { // the file compiled correctly, thus the last stack item must be the memory offset of // the `bytes memory inspectoor` value - let mut offset = stack.data().last().unwrap().to::(); + let mut offset = stack.last().unwrap().to::(); let mem_offset = &memory[offset..offset + 32]; let len = U256::try_from_be_slice(mem_offset).unwrap().to::(); offset += 32; diff --git a/crates/chisel/src/runner.rs b/crates/chisel/src/runner.rs index dfbc30189..6ffcb6770 100644 --- a/crates/chisel/src/runner.rs +++ b/crates/chisel/src/runner.rs @@ -49,7 +49,7 @@ pub struct ChiselResult { /// Called address pub address: Option
, /// EVM State at the final instruction of the `run()` function - pub state: Option<(revm::interpreter::Stack, Vec, InstructionResult)>, + pub state: Option<(Vec, Vec, InstructionResult)>, } /// ChiselRunner implementation @@ -153,7 +153,7 @@ impl ChiselRunner { match res.exit_reason { InstructionResult::Revert | InstructionResult::OutOfGas | - InstructionResult::OutOfFund => { + InstructionResult::OutOfFunds => { lowest_gas_limit = mid_gas_limit; } _ => { diff --git a/crates/evm/core/Cargo.toml b/crates/evm/core/Cargo.toml index 04403a9f8..814a967c9 100644 --- a/crates/evm/core/Cargo.toml +++ b/crates/evm/core/Cargo.toml @@ -39,6 +39,7 @@ revm = { workspace = true, default-features = false, features = [ revm-inspectors.workspace = true derive_more.workspace = true +auto_impl = "1" eyre = "0.6" futures = "0.3" hex.workspace = true diff --git a/crates/evm/core/src/backend/error.rs b/crates/evm/core/src/backend/error.rs index a5b3d0286..f1f9733d4 100644 --- a/crates/evm/core/src/backend/error.rs +++ b/crates/evm/core/src/backend/error.rs @@ -1,6 +1,7 @@ use alloy_primitives::{Address, B256, U256}; use alloy_rpc_types::BlockId; use futures::channel::mpsc::{SendError, TrySendError}; +use revm::primitives::EVMError; use std::{ convert::Infallible, sync::{mpsc::RecvError, Arc}, @@ -46,6 +47,8 @@ pub enum DatabaseError { For a test environment, you can use `etch` to place the required bytecode at that address." )] MissingCreate2Deployer, + #[error("{0}")] + Other(String), } impl DatabaseError { @@ -76,6 +79,7 @@ impl DatabaseError { Self::BlockNotFound(_) | Self::TransactionNotFound(_) | Self::MissingCreate2Deployer => None, + DatabaseError::Other(_) => None, } } @@ -107,3 +111,13 @@ impl From for DatabaseError { match value {} } } + +// Note: this is mostly necessary to use some revm internals that return an [EVMError] +impl From> for DatabaseError { + fn from(err: EVMError) -> Self { + match err { + EVMError::Database(err) => err, + err => DatabaseError::Other(err.to_string()), + } + } +} diff --git a/crates/evm/core/src/backend/fuzz.rs b/crates/evm/core/src/backend/fuzz.rs index 11857fa4a..69bcac72b 100644 --- a/crates/evm/core/src/backend/fuzz.rs +++ b/crates/evm/core/src/backend/fuzz.rs @@ -9,9 +9,13 @@ use crate::{ }; use alloy_genesis::GenesisAccount; use alloy_primitives::{Address, B256, U256}; +use eyre::WrapErr; use revm::{ db::DatabaseRef, - primitives::{Account, AccountInfo, Bytecode, Env, HashMap as Map, ResultAndState}, + primitives::{ + Account, AccountInfo, Bytecode, Env, EnvWithHandlerCfg, HashMap as Map, ResultAndState, + SpecId, + }, Database, DatabaseCommit, Inspector, JournaledState, }; use std::{borrow::Cow, collections::HashMap}; @@ -40,29 +44,35 @@ pub struct FuzzBackendWrapper<'a> { pub backend: Cow<'a, Backend>, /// Keeps track of whether the backed is already initialized is_initialized: bool, + /// The [SpecId] of the current backend. + spec_id: SpecId, } impl<'a> FuzzBackendWrapper<'a> { pub fn new(backend: &'a Backend) -> Self { - Self { backend: Cow::Borrowed(backend), is_initialized: false } + Self { backend: Cow::Borrowed(backend), is_initialized: false, spec_id: SpecId::LATEST } } /// Executes the configured transaction of the `env` without committing state changes - pub fn inspect_ref( - &mut self, - env: &mut Env, - mut inspector: INSP, - ) -> eyre::Result - where - INSP: Inspector, - { + /// + /// Note: in case there are any cheatcodes executed that modify the environment, this will + /// update the given `env` with the new values. + pub fn inspect<'b, I: Inspector<&'b mut Self>>( + &'b mut self, + env: &mut EnvWithHandlerCfg, + inspector: I, + ) -> eyre::Result { // this is a new call to inspect with a new env, so even if we've cloned the backend // already, we reset the initialized state self.is_initialized = false; - match revm::evm_inner::(env, self, Some(&mut inspector)).transact() { - Ok(result) => Ok(result), - Err(e) => eyre::bail!("fuzz: failed to inspect: {e}"), - } + self.spec_id = env.handler_cfg.spec_id; + let mut evm = crate::utils::new_evm_with_inspector(self, env.clone(), inspector); + + let res = evm.transact().wrap_err("backend: failed while inspecting")?; + + env.env = evm.context.evm.env; + + Ok(res) } /// Returns whether there was a snapshot failure in the fuzz backend. @@ -78,7 +88,8 @@ impl<'a> FuzzBackendWrapper<'a> { fn backend_mut(&mut self, env: &Env) -> &mut Backend { if !self.is_initialized { let backend = self.backend.to_mut(); - backend.initialize(env); + let env = EnvWithHandlerCfg::new_with_spec_id(Box::new(env.clone()), self.spec_id); + backend.initialize(&env); self.is_initialized = true; return backend } diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index 5e2c8fdad..e3b7ab339 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -9,16 +9,17 @@ use crate::{ use alloy_genesis::GenesisAccount; use alloy_primitives::{b256, keccak256, Address, B256, U256, U64}; use alloy_rpc_types::{Block, BlockNumberOrTag, BlockTransactions, Transaction}; +use eyre::Context; use foundry_common::{is_known_system_sender, SYSTEM_TRANSACTION_TYPE}; use revm::{ db::{CacheDB, DatabaseRef}, inspectors::NoOpInspector, - precompile::{Precompiles, SpecId}, + precompile::{PrecompileSpecId, Precompiles}, primitives::{ - Account, AccountInfo, Bytecode, CreateScheme, Env, HashMap as Map, Log, ResultAndState, - StorageSlot, TransactTo, KECCAK_EMPTY, + Account, AccountInfo, Bytecode, CreateScheme, Env, EnvWithHandlerCfg, HashMap as Map, Log, + ResultAndState, SpecId, StorageSlot, TransactTo, KECCAK_EMPTY, }, - Database, DatabaseCommit, Inspector, JournaledState, EVM, + Database, DatabaseCommit, Inspector, JournaledState, }; use std::{ collections::{HashMap, HashSet}, @@ -62,6 +63,7 @@ const GLOBAL_FAILURE_SLOT: B256 = b256!("6661696c65640000000000000000000000000000000000000000000000000000"); /// An extension trait that allows us to easily extend the `revm::Inspector` capabilities +#[auto_impl::auto_impl(&mut)] pub trait DatabaseExt: Database { /// Creates a new snapshot at the current point of execution. /// @@ -193,7 +195,9 @@ pub trait DatabaseExt: Database { env: &mut Env, journaled_state: &mut JournaledState, inspector: &mut I, - ) -> eyre::Result<()>; + ) -> eyre::Result<()> + where + Self: Sized; /// Returns the `ForkId` that's currently used in the database, if fork mode is on fn active_fork_id(&self) -> Option; @@ -272,14 +276,20 @@ pub trait DatabaseExt: Database { fn add_persistent_account(&mut self, account: Address) -> bool; /// Removes persistent status from all given accounts - fn remove_persistent_accounts(&mut self, accounts: impl IntoIterator) { + fn remove_persistent_accounts(&mut self, accounts: impl IntoIterator) + where + Self: Sized, + { for acc in accounts { self.remove_persistent_account(&acc); } } /// Extends the persistent accounts with the accounts the iterator yields. - fn extend_persistent_accounts(&mut self, accounts: impl IntoIterator) { + fn extend_persistent_accounts(&mut self, accounts: impl IntoIterator) + where + Self: Sized, + { for acc in accounts { self.add_persistent_account(acc); } @@ -318,6 +328,8 @@ pub trait DatabaseExt: Database { } } +struct _ObjectSafe(dyn DatabaseExt); + /// Provides the underlying `revm::Database` implementation. /// /// A `Backend` can be initialised in two forms: @@ -545,8 +557,8 @@ impl Backend { /// Sets the current spec id pub fn set_spec_id(&mut self, spec_id: SpecId) -> &mut Self { - trace!("setting precompile id"); - self.inner.precompile_id = spec_id; + trace!(?spec_id, "setting spec ID"); + self.inner.spec_id = spec_id; self } @@ -742,9 +754,9 @@ impl Backend { /// Initializes settings we need to keep track of. /// /// We need to track these mainly to prevent issues when switching between different evms - pub(crate) fn initialize(&mut self, env: &Env) { + pub(crate) fn initialize(&mut self, env: &EnvWithHandlerCfg) { self.set_caller(env.tx.caller); - self.set_spec_id(SpecId::from_spec_id(env.cfg.spec_id)); + self.set_spec_id(env.handler_cfg.spec_id); let test_contract = match env.tx.transact_to { TransactTo::Call(to) => to, @@ -759,21 +771,28 @@ impl Backend { self.set_test_contract(test_contract); } - /// Executes the configured test call of the `env` without committing state changes - pub fn inspect_ref( - &mut self, - env: &mut Env, - mut inspector: INSP, - ) -> eyre::Result - where - INSP: Inspector, - { + /// Returns the `EnvWithHandlerCfg` with the current `spec_id` set. + fn env_with_handler_cfg(&self, env: Env) -> EnvWithHandlerCfg { + EnvWithHandlerCfg::new_with_spec_id(Box::new(env), self.inner.spec_id) + } + + /// Executes the configured test call of the `env` without committing state changes. + /// + /// Note: in case there are any cheatcodes executed that modify the environment, this will + /// update the given `env` with the new values. + pub fn inspect<'a, I: Inspector<&'a mut Self>>( + &'a mut self, + env: &mut EnvWithHandlerCfg, + inspector: I, + ) -> eyre::Result { self.initialize(env); + let mut evm = crate::utils::new_evm_with_inspector(self, env.clone(), inspector); - match revm::evm_inner::(env, self, Some(&mut inspector)).transact() { - Ok(res) => Ok(res), - Err(e) => eyre::bail!("backend: failed while inspecting: {e}"), - } + let res = evm.transact().wrap_err("backend: failed while inspecting")?; + + env.env = evm.context.evm.env; + + Ok(res) } /// Returns true if the address is a precompile @@ -876,6 +895,7 @@ impl Backend { let fork_id = self.ensure_fork_id(id)?.clone(); + let env = self.env_with_handler_cfg(env); let fork = self.inner.get_fork_by_id_mut(id)?; let full_block = fork.db.db.get_full_block(env.block.number.to::())?; @@ -902,7 +922,7 @@ impl Backend { journaled_state, fork, &fork_id, - NoOpInspector, + &mut NoOpInspector, )?; } } @@ -1232,6 +1252,7 @@ impl DatabaseExt for Backend { let mut env = env.clone(); update_env_block(&mut env, fork_block, &block); + let env = self.env_with_handler_cfg(env); let fork = self.inner.get_fork_by_id_mut(id)?; commit_transaction(tx, env, journaled_state, fork, &fork_id, inspector) } @@ -1551,8 +1572,8 @@ pub struct BackendInner { /// /// See also [`clone_data()`] pub persistent_accounts: HashSet
, - /// The configured precompile spec id - pub precompile_id: revm::precompile::SpecId, + /// The configured spec id + pub spec_id: SpecId, /// All accounts that are allowed to execute cheatcodes pub cheatcode_access_accounts: HashSet
, } @@ -1715,29 +1736,12 @@ impl BackendInner { } pub fn precompiles(&self) -> &'static Precompiles { - Precompiles::new(self.precompile_id) + Precompiles::new(PrecompileSpecId::from_spec_id(self.spec_id)) } /// Returns a new, empty, `JournaledState` with set precompiles pub fn new_journaled_state(&self) -> JournaledState { - /// Helper function to convert from a `revm::precompile::SpecId` into a - /// `revm::primitives::SpecId` This only matters if the spec is Cancun or later, or - /// pre-Spurious Dragon. - fn precompiles_spec_id_to_primitives_spec_id(spec: SpecId) -> revm::primitives::SpecId { - match spec { - SpecId::HOMESTEAD => revm::primitives::SpecId::HOMESTEAD, - SpecId::BYZANTIUM => revm::primitives::SpecId::BYZANTIUM, - SpecId::ISTANBUL => revm::primitives::ISTANBUL, - SpecId::BERLIN => revm::primitives::BERLIN, - SpecId::CANCUN => revm::primitives::CANCUN, - // Point latest to berlin for now, as we don't wanna accidentally point to Cancun. - SpecId::LATEST => revm::primitives::BERLIN, - } - } - JournaledState::new( - precompiles_spec_id_to_primitives_spec_id(self.precompile_id), - self.precompiles().addresses().into_iter().copied().collect(), - ) + JournaledState::new(self.spec_id, self.precompiles().addresses().copied().collect()) } } @@ -1754,7 +1758,7 @@ impl Default for BackendInner { caller: None, next_fork_id: Default::default(), persistent_accounts: Default::default(), - precompile_id: revm::precompile::SpecId::LATEST, + spec_id: SpecId::LATEST, // grant the cheatcode,default test and caller address access to execute cheatcodes // itself cheatcode_access_accounts: HashSet::from([ @@ -1865,32 +1869,26 @@ fn update_env_block(env: &mut Env, fork_block: U64, block: &Block) { /// state, with an optional inspector fn commit_transaction>( tx: Transaction, - mut env: Env, + mut env: EnvWithHandlerCfg, journaled_state: &mut JournaledState, fork: &mut Fork, fork_id: &ForkId, inspector: I, ) -> eyre::Result<()> { - configure_tx_env(&mut env, &tx); + configure_tx_env(&mut env.env, &tx); let now = Instant::now(); - let state = { - let mut evm = EVM::new(); - evm.env = env; - + let res = { let fork = fork.clone(); let journaled_state = journaled_state.clone(); let db = Backend::new_with_fork(fork_id, fork, journaled_state); - evm.database(db); - - match evm.inspect(inspector) { - Ok(res) => res.state, - Err(e) => eyre::bail!("backend: failed committing transaction: {e}"), - } + crate::utils::new_evm_with_inspector(db, env, inspector) + .transact() + .wrap_err("backend: failed committing transaction")? }; trace!(elapsed = ?now.elapsed(), "transacted transaction"); - apply_state_changeset(state, journaled_state, fork); + apply_state_changeset(res.state, journaled_state, fork); Ok(()) } diff --git a/crates/evm/core/src/opts.rs b/crates/evm/core/src/opts.rs index 2dee3a9b5..786252bba 100644 --- a/crates/evm/core/src/opts.rs +++ b/crates/evm/core/src/opts.rs @@ -10,7 +10,7 @@ use foundry_common::{ }; use foundry_compilers::utils::RuntimeOrHandle; use foundry_config::{Chain, Config}; -use revm::primitives::{BlockEnv, CfgEnv, SpecId, TxEnv}; +use revm::primitives::{BlockEnv, CfgEnv, TxEnv}; use serde::{Deserialize, Deserializer, Serialize}; #[derive(Clone, Debug, Default, Serialize, Deserialize)] @@ -111,7 +111,6 @@ impl EvmOpts { pub fn local_evm_env(&self) -> revm::primitives::Env { let mut cfg = CfgEnv::default(); cfg.chain_id = self.env.chain_id.unwrap_or(foundry_common::DEV_CHAIN_ID); - cfg.spec_id = SpecId::MERGE; cfg.limit_contract_code_size = self.env.code_size_limit.or(Some(usize::MAX)); cfg.memory_limit = self.memory_limit; // EIP-3607 rejects transactions from senders with deployed code. diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index e0f0f1ee8..48e8808e4 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -4,8 +4,8 @@ use alloy_rpc_types::{Block, Transaction}; use eyre::ContextCompat; use foundry_config::NamedChain; use revm::{ - interpreter::InstructionResult, - primitives::{Eval, Halt, SpecId, TransactTo}, + db::WrapDatabaseRef, + primitives::{SpecId, TransactTo}, }; pub use foundry_compilers::utils::RuntimeOrHandle; @@ -13,43 +13,6 @@ pub use revm::primitives::State as StateChangeset; pub use crate::ic::*; -/// Small helper function to convert an Eval into an InstructionResult -#[inline] -pub fn eval_to_instruction_result(eval: Eval) -> InstructionResult { - match eval { - Eval::Return => InstructionResult::Return, - Eval::Stop => InstructionResult::Stop, - Eval::SelfDestruct => InstructionResult::SelfDestruct, - } -} - -/// Small helper function to convert a Halt into an InstructionResult -#[inline] -pub fn halt_to_instruction_result(halt: Halt) -> InstructionResult { - match halt { - Halt::OutOfGas(_) => InstructionResult::OutOfGas, - Halt::OpcodeNotFound => InstructionResult::OpcodeNotFound, - Halt::InvalidFEOpcode => InstructionResult::InvalidFEOpcode, - Halt::InvalidJump => InstructionResult::InvalidJump, - Halt::NotActivated => InstructionResult::NotActivated, - Halt::StackOverflow => InstructionResult::StackOverflow, - Halt::StackUnderflow => InstructionResult::StackUnderflow, - Halt::OutOfOffset => InstructionResult::OutOfOffset, - Halt::CreateCollision => InstructionResult::CreateCollision, - Halt::PrecompileError => InstructionResult::PrecompileError, - Halt::NonceOverflow => InstructionResult::NonceOverflow, - Halt::CreateContractSizeLimit => InstructionResult::CreateContractSizeLimit, - Halt::CreateContractStartingWithEF => InstructionResult::CreateContractStartingWithEF, - Halt::CreateInitcodeSizeLimit => InstructionResult::CreateInitcodeSizeLimit, - Halt::OverflowPayment => InstructionResult::OverflowPayment, - Halt::StateChangeDuringStaticCall => InstructionResult::StateChangeDuringStaticCall, - Halt::CallNotAllowedInsideStatic => InstructionResult::CallNotAllowedInsideStatic, - Halt::OutOfFund => InstructionResult::OutOfFund, - Halt::CallTooDeep => InstructionResult::CallTooDeep, - Halt::FailedDeposit => InstructionResult::Return, - } -} - /// Depending on the configured chain id and block number this should apply any specific changes /// /// This checks for: @@ -133,3 +96,56 @@ pub fn gas_used(spec: SpecId, spent: u64, refunded: u64) -> u64 { let refund_quotient = if SpecId::enabled(spec, SpecId::LONDON) { 5 } else { 2 }; spent - (refunded).min(spent / refund_quotient) } + +/// Creates a new EVM with the given inspector. +pub fn new_evm_with_inspector<'a, DB, I>( + db: DB, + env: revm::primitives::EnvWithHandlerCfg, + inspector: I, +) -> revm::Evm<'a, I, DB> +where + DB: revm::Database, + I: revm::Inspector, +{ + // NOTE: We could use `revm::Evm::builder()` here, but on the current patch it has some + // performance issues. + let revm::primitives::EnvWithHandlerCfg { env, handler_cfg } = env; + let context = revm::Context::new(revm::EvmContext::new_with_env(db, env), inspector); + let mut handler = revm::Handler::new(handler_cfg); + handler.append_handler_register_plain(revm::inspector_handle_register); + revm::Evm::new(context, handler) +} + +/// Creates a new EVM with the given inspector and wraps the database in a `WrapDatabaseRef`. +pub fn new_evm_with_inspector_ref<'a, DB, I>( + db: DB, + env: revm::primitives::EnvWithHandlerCfg, + inspector: I, +) -> revm::Evm<'a, I, WrapDatabaseRef> +where + DB: revm::DatabaseRef, + I: revm::Inspector>, +{ + new_evm_with_inspector(WrapDatabaseRef(db), env, inspector) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn build_evm() { + let mut db = revm::db::EmptyDB::default(); + + let env = Box::::default(); + let spec = SpecId::LATEST; + let handler_cfg = revm::primitives::HandlerCfg::new(spec); + let cfg = revm::primitives::EnvWithHandlerCfg::new(env, handler_cfg); + + let mut inspector = revm::inspectors::NoOpInspector; + + let mut evm = new_evm_with_inspector(&mut db, cfg, &mut inspector); + let result = evm.transact().unwrap(); + assert!(result.result.is_success()); + } +} diff --git a/crates/evm/coverage/src/inspector.rs b/crates/evm/coverage/src/inspector.rs index 8d75e0e94..3c3af1ba2 100644 --- a/crates/evm/coverage/src/inspector.rs +++ b/crates/evm/coverage/src/inspector.rs @@ -1,6 +1,5 @@ use crate::{HitMap, HitMaps}; -use alloy_primitives::Bytes; -use revm::{interpreter::Interpreter, Database, EVMData, Inspector}; +use revm::{interpreter::Interpreter, Database, EvmContext, Inspector}; #[derive(Clone, Debug, Default)] pub struct CoverageCollector { @@ -10,18 +9,16 @@ pub struct CoverageCollector { impl Inspector for CoverageCollector { #[inline] - fn initialize_interp(&mut self, interpreter: &mut Interpreter<'_>, _: &mut EVMData<'_, DB>) { - let hash = interpreter.contract.hash; - self.maps.entry(hash).or_insert_with(|| { - HitMap::new(Bytes::copy_from_slice( - interpreter.contract.bytecode.original_bytecode_slice(), - )) - }); + fn initialize_interp(&mut self, interp: &mut Interpreter, _context: &mut EvmContext) { + let hash = interp.contract.hash; + self.maps + .entry(hash) + .or_insert_with(|| HitMap::new(interp.contract.bytecode.original_bytecode())); } #[inline] - fn step(&mut self, interpreter: &mut Interpreter<'_>, _: &mut EVMData<'_, DB>) { - let hash = interpreter.contract.hash; - self.maps.entry(hash).and_modify(|map| map.hit(interpreter.program_counter())); + fn step(&mut self, interp: &mut Interpreter, _context: &mut EvmContext) { + let hash = interp.contract.hash; + self.maps.entry(hash).and_modify(|map| map.hit(interp.program_counter())); } } diff --git a/crates/evm/evm/Cargo.toml b/crates/evm/evm/Cargo.toml index 33e491e11..08b1500b4 100644 --- a/crates/evm/evm/Cargo.toml +++ b/crates/evm/evm/Cargo.toml @@ -24,7 +24,6 @@ alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } alloy-json-abi.workspace = true alloy-primitives = { workspace = true, features = ["serde", "getrandom", "arbitrary", "rlp"] } alloy-sol-types.workspace = true -alloy-rpc-types.workspace = true hashbrown = { version = "0.14", features = ["serde"] } revm = { workspace = true, default-features = false, features = [ "std", diff --git a/crates/evm/evm/src/executors/builder.rs b/crates/evm/evm/src/executors/builder.rs index d7cca61a7..370c3bcf3 100644 --- a/crates/evm/evm/src/executors/builder.rs +++ b/crates/evm/evm/src/executors/builder.rs @@ -1,7 +1,7 @@ use crate::{executors::Executor, inspectors::InspectorStackBuilder}; use alloy_primitives::U256; use foundry_evm_core::backend::Backend; -use revm::primitives::{Env, SpecId}; +use revm::primitives::{Env, EnvWithHandlerCfg, SpecId}; /// The builder that allows to configure an evm [`Executor`] which a stack of optional /// [`revm::Inspector`]s, such as [`Cheatcodes`]. @@ -63,12 +63,16 @@ impl ExecutorBuilder { /// Builds the executor as configured. #[inline] - pub fn build(self, mut env: Env, db: Backend) -> Executor { + pub fn build(self, env: Env, db: Backend) -> Executor { let Self { mut stack, gas_limit, spec_id } = self; - env.cfg.spec_id = spec_id; stack.block = Some(env.block.clone()); stack.gas_price = Some(env.tx.gas_price); let gas_limit = gas_limit.unwrap_or(env.block.gas_limit); - Executor::new(db, env, stack.build(), gas_limit) + Executor::new( + db, + EnvWithHandlerCfg::new_with_spec_id(Box::new(env), spec_id), + stack.build(), + gas_limit, + ) } } diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 628bc502e..f1076925f 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -20,15 +20,16 @@ use foundry_evm_core::{ }, debug::DebugArena, decode::RevertDecoder, - utils::{eval_to_instruction_result, halt_to_instruction_result, StateChangeset}, + utils::StateChangeset, }; use foundry_evm_coverage::HitMaps; use foundry_evm_traces::CallTraceArena; use revm::{ db::{DatabaseCommit, DatabaseRef}, - interpreter::{return_ok, CreateScheme, InstructionResult, Stack}, + interpreter::{return_ok, CreateScheme, InstructionResult}, primitives::{ - BlockEnv, Bytecode, Env, ExecutionResult, Output, ResultAndState, SpecId, TransactTo, TxEnv, + BlockEnv, Bytecode, Env, EnvWithHandlerCfg, ExecutionResult, Output, ResultAndState, + SpecId, TransactTo, TxEnv, }, }; use std::collections::HashMap; @@ -62,7 +63,7 @@ pub struct Executor { // so the performance difference should be negligible. pub backend: Backend, /// The EVM environment. - pub env: Env, + pub env: EnvWithHandlerCfg, /// The Revm inspector stack. pub inspector: InspectorStack, /// The gas limit for calls and deployments. This is different from the gas limit imposed by @@ -73,7 +74,12 @@ pub struct Executor { impl Executor { #[inline] - pub fn new(mut backend: Backend, env: Env, inspector: InspectorStack, gas_limit: U256) -> Self { + pub fn new( + mut backend: Backend, + env: EnvWithHandlerCfg, + inspector: InspectorStack, + gas_limit: U256, + ) -> Self { // Need to create a non-empty contract on the cheatcodes address so `extcodesize` checks // does not fail backend.insert_account_info( @@ -87,6 +93,11 @@ impl Executor { Executor { backend, env, inspector, gas_limit } } + /// Returns the spec id of the executor + pub fn spec_id(&self) -> SpecId { + self.env.handler_cfg.spec_id + } + /// Creates the default CREATE2 Contract Deployer for local tests and scripts. pub fn deploy_create2_deployer(&mut self) -> eyre::Result<()> { trace!("deploying local create2 deployer"); @@ -299,7 +310,7 @@ impl Executor { // Build VM let mut env = self.build_test_env(from, TransactTo::Call(to), calldata, value); let mut db = FuzzBackendWrapper::new(&self.backend); - let result = db.inspect_ref(&mut env, &mut inspector)?; + let result = db.inspect(&mut env, &mut inspector)?; // Persist the snapshot failure recorded on the fuzz backend wrapper. let has_snapshot_failure = db.has_snapshot_failure(); @@ -307,17 +318,17 @@ impl Executor { } /// Execute the transaction configured in `env.tx` and commit the changes - pub fn commit_tx_with_env(&mut self, env: Env) -> eyre::Result { + pub fn commit_tx_with_env(&mut self, env: EnvWithHandlerCfg) -> eyre::Result { let mut result = self.call_raw_with_env(env)?; self.commit(&mut result); Ok(result) } /// Execute the transaction configured in `env.tx` - pub fn call_raw_with_env(&mut self, mut env: Env) -> eyre::Result { + pub fn call_raw_with_env(&mut self, mut env: EnvWithHandlerCfg) -> eyre::Result { // execute the call let mut inspector = self.inspector.clone(); - let result = self.backend.inspect_ref(&mut env, &mut inspector)?; + let result = self.backend.inspect(&mut env, &mut inspector)?; convert_executed_result(env, inspector, result, self.backend.has_snapshot_failure()) } @@ -349,7 +360,7 @@ impl Executor { /// database pub fn deploy_with_env( &mut self, - env: Env, + env: EnvWithHandlerCfg, rd: Option<&RevertDecoder>, ) -> Result { debug_assert!( @@ -549,8 +560,8 @@ impl Executor { transact_to: TransactTo, data: Bytes, value: U256, - ) -> Env { - Env { + ) -> EnvWithHandlerCfg { + let env = Env { cfg: self.env.cfg.clone(), // We always set the gas price to 0 so we can execute the transaction regardless of // network conditions - the actual gas price is kept in `self.block` and is applied by @@ -571,7 +582,9 @@ impl Executor { gas_limit: self.gas_limit.to(), ..self.env.tx.clone() }, - } + }; + + EnvWithHandlerCfg::new_with_spec_id(Box::new(env), self.env.handler_cfg.spec_id) } } @@ -624,7 +637,7 @@ pub struct DeployResult { /// The debug nodes of the call pub debug: Option, /// The `revm::Env` after deployment - pub env: Env, + pub env: EnvWithHandlerCfg, /// The coverage info collected during the deployment pub coverage: Option, } @@ -661,7 +674,7 @@ pub struct CallResult { /// used `call` and `call_raw` not `call_committing` or `call_raw_committing`). pub state_changeset: Option, /// The `revm::Env` after the call - pub env: Env, + pub env: EnvWithHandlerCfg, /// breakpoints pub breakpoints: Breakpoints, } @@ -704,13 +717,13 @@ pub struct RawCallResult { /// used `call` and `call_raw` not `call_committing` or `call_raw_committing`). pub state_changeset: Option, /// The `revm::Env` after the call - pub env: Env, + pub env: EnvWithHandlerCfg, /// The cheatcode states after execution pub cheatcodes: Option, /// The raw output of the execution pub out: Option, /// The chisel state - pub chisel_state: Option<(Stack, Vec, InstructionResult)>, + pub chisel_state: Option<(Vec, Vec, InstructionResult)>, } impl Default for RawCallResult { @@ -730,7 +743,7 @@ impl Default for RawCallResult { debug: None, transactions: None, state_changeset: None, - env: Default::default(), + env: EnvWithHandlerCfg::new_with_spec_id(Box::default(), SpecId::LATEST), cheatcodes: Default::default(), out: None, chisel_state: None, @@ -746,7 +759,7 @@ fn calc_stipend(calldata: &[u8], spec: SpecId) -> u64 { /// Converts the data aggregated in the `inspector` and `call` to a `RawCallResult` fn convert_executed_result( - env: Env, + env: EnvWithHandlerCfg, inspector: InspectorStack, result: ResultAndState, has_snapshot_failure: bool, @@ -754,17 +767,15 @@ fn convert_executed_result( let ResultAndState { result: exec_result, state: state_changeset } = result; let (exit_reason, gas_refunded, gas_used, out) = match exec_result { ExecutionResult::Success { reason, gas_used, gas_refunded, output, .. } => { - (eval_to_instruction_result(reason), gas_refunded, gas_used, Some(output)) + (reason.into(), gas_refunded, gas_used, Some(output)) } ExecutionResult::Revert { gas_used, output } => { // Need to fetch the unused gas (InstructionResult::Revert, 0_u64, gas_used, Some(Output::Call(output))) } - ExecutionResult::Halt { reason, gas_used } => { - (halt_to_instruction_result(reason), 0_u64, gas_used, None) - } + ExecutionResult::Halt { reason, gas_used } => (reason.into(), 0_u64, gas_used, None), }; - let stipend = calc_stipend(&env.tx.data, env.cfg.spec_id); + let stipend = calc_stipend(&env.tx.data, env.handler_cfg.spec_id); let result = match &out { Some(Output::Call(data)) => data.clone(), diff --git a/crates/evm/evm/src/executors/tracing.rs b/crates/evm/evm/src/executors/tracing.rs index 5beabdd1d..08979bc16 100644 --- a/crates/evm/evm/src/executors/tracing.rs +++ b/crates/evm/evm/src/executors/tracing.rs @@ -2,7 +2,7 @@ use crate::executors::{Executor, ExecutorBuilder}; use foundry_compilers::EvmVersion; use foundry_config::{utils::evm_spec_id, Chain, Config}; use foundry_evm_core::{backend::Backend, fork::CreateFork, opts::EvmOpts}; -use revm::primitives::Env; +use revm::primitives::{Env, SpecId}; use std::ops::{Deref, DerefMut}; /// A default executor with tracing enabled @@ -28,6 +28,11 @@ impl TracingExecutor { } } + /// Returns the spec id of the executor + pub fn spec_id(&self) -> SpecId { + self.executor.spec_id() + } + /// uses the fork block number from the config pub async fn get_fork_material( config: &Config, diff --git a/crates/evm/evm/src/inspectors/access_list.rs b/crates/evm/evm/src/inspectors/access_list.rs deleted file mode 100644 index ea43336a7..000000000 --- a/crates/evm/evm/src/inspectors/access_list.rs +++ /dev/null @@ -1,79 +0,0 @@ -use alloy_primitives::{Address, B256}; -use alloy_rpc_types::{AccessList, AccessListItem}; -use hashbrown::{HashMap, HashSet}; -use revm::{ - interpreter::{opcode, Interpreter}, - Database, EVMData, Inspector, -}; - -/// An inspector that collects touched accounts and storage slots. -#[derive(Debug, Default)] -pub struct AccessListTracer { - excluded: HashSet
, - access_list: HashMap>, -} - -impl AccessListTracer { - pub fn new( - access_list: AccessList, - from: Address, - to: Address, - precompiles: Vec
, - ) -> Self { - AccessListTracer { - excluded: [from, to].iter().chain(precompiles.iter()).copied().collect(), - access_list: access_list - .0 - .iter() - .map(|v| (v.address, v.storage_keys.iter().copied().collect())) - .collect(), - } - } - - pub fn access_list(&self) -> AccessList { - AccessList( - self.access_list - .iter() - .map(|(address, slots)| AccessListItem { - address: *address, - storage_keys: slots.iter().copied().collect(), - }) - .collect::>(), - ) - } -} - -impl Inspector for AccessListTracer { - #[inline] - fn step(&mut self, interpreter: &mut Interpreter<'_>, _data: &mut EVMData<'_, DB>) { - match interpreter.current_opcode() { - opcode::SLOAD | opcode::SSTORE => { - if let Ok(slot) = interpreter.stack().peek(0) { - let cur_contract = interpreter.contract.address; - self.access_list.entry(cur_contract).or_default().insert(slot.into()); - } - } - opcode::EXTCODECOPY | - opcode::EXTCODEHASH | - opcode::EXTCODESIZE | - opcode::BALANCE | - opcode::SELFDESTRUCT => { - if let Ok(slot) = interpreter.stack().peek(0) { - let addr: Address = Address::from_word(slot.into()); - if !self.excluded.contains(&addr) { - self.access_list.entry(addr).or_default(); - } - } - } - opcode::DELEGATECALL | opcode::CALL | opcode::STATICCALL | opcode::CALLCODE => { - if let Ok(slot) = interpreter.stack().peek(1) { - let addr: Address = Address::from_word(slot.into()); - if !self.excluded.contains(&addr) { - self.access_list.entry(addr).or_default(); - } - } - } - _ => (), - } - } -} diff --git a/crates/evm/evm/src/inspectors/chisel_state.rs b/crates/evm/evm/src/inspectors/chisel_state.rs index a4d3a1895..9aa933d38 100644 --- a/crates/evm/evm/src/inspectors/chisel_state.rs +++ b/crates/evm/evm/src/inspectors/chisel_state.rs @@ -1,6 +1,7 @@ +use alloy_primitives::U256; use revm::{ - interpreter::{InstructionResult, Interpreter, Stack}, - Database, Inspector, + interpreter::{InstructionResult, Interpreter}, + Database, EvmContext, Inspector, }; /// An inspector for Chisel @@ -9,7 +10,7 @@ pub struct ChiselState { /// The PC of the final instruction pub final_pc: usize, /// The final state of the REPL contract call - pub state: Option<(Stack, Vec, InstructionResult)>, + pub state: Option<(Vec, Vec, InstructionResult)>, } impl ChiselState { @@ -22,12 +23,12 @@ impl ChiselState { impl Inspector for ChiselState { #[inline] - fn step_end(&mut self, interp: &mut Interpreter<'_>, _: &mut revm::EVMData<'_, DB>) { + fn step_end(&mut self, interp: &mut Interpreter, _context: &mut EvmContext) { // If we are at the final pc of the REPL contract execution, set the state. // Subtraction can't overflow because `pc` is always at least 1 in `step_end`. if self.final_pc == interp.program_counter() - 1 { self.state = Some(( - interp.stack().clone(), + interp.stack.data().clone(), interp.shared_memory.context_memory().to_vec(), interp.instruction_result, )) diff --git a/crates/evm/evm/src/inspectors/debugger.rs b/crates/evm/evm/src/inspectors/debugger.rs index bb327b36e..222e6de3b 100644 --- a/crates/evm/evm/src/inspectors/debugger.rs +++ b/crates/evm/evm/src/inspectors/debugger.rs @@ -1,4 +1,4 @@ -use alloy_primitives::{Address, Bytes}; +use alloy_primitives::Address; use foundry_common::{ErrorExt, SELECTOR_LEN}; use foundry_evm_core::{ backend::DatabaseExt, @@ -9,9 +9,10 @@ use foundry_evm_core::{ use revm::{ interpreter::{ opcode::{self, spec_opcode_gas}, - CallInputs, CreateInputs, Gas, InstructionResult, Interpreter, + CallInputs, CallOutcome, CreateInputs, CreateOutcome, Gas, InstructionResult, Interpreter, + InterpreterResult, }, - EVMData, Inspector, + EvmContext, Inspector, }; use revm_inspectors::tracing::types::CallKind; @@ -46,12 +47,12 @@ impl Debugger { impl Inspector for Debugger { #[inline] - fn step(&mut self, interpreter: &mut Interpreter<'_>, data: &mut EVMData<'_, DB>) { - let pc = interpreter.program_counter(); - let op = interpreter.current_opcode(); + fn step(&mut self, interp: &mut Interpreter, ecx: &mut EvmContext) { + let pc = interp.program_counter(); + let op = interp.current_opcode(); // Get opcode information - let opcode_infos = spec_opcode_gas(data.env.cfg.spec_id); + let opcode_infos = spec_opcode_gas(ecx.spec_id()); let opcode_info = &opcode_infos[op as usize]; // Extract the push bytes @@ -61,22 +62,22 @@ impl Inspector for Debugger { n => { let start = pc + 1; let end = start + n; - Some(interpreter.contract.bytecode.bytecode()[start..end].to_vec()) + Some(interp.contract.bytecode.bytecode()[start..end].to_vec()) } }; let total_gas_used = gas_used( - data.env.cfg.spec_id, - interpreter.gas.limit().saturating_sub(interpreter.gas.remaining()), - interpreter.gas.refunded() as u64, + ecx.spec_id(), + interp.gas.limit().saturating_sub(interp.gas.remaining()), + interp.gas.refunded() as u64, ); self.arena.arena[self.head].steps.push(DebugStep { pc, - stack: interpreter.stack().data().clone(), - memory: interpreter.shared_memory.context_memory().to_vec(), - calldata: interpreter.contract().input.to_vec(), - returndata: interpreter.return_data_buffer.to_vec(), + stack: interp.stack().data().clone(), + memory: interp.shared_memory.context_memory().to_vec(), + calldata: interp.contract().input.to_vec(), + returndata: interp.return_data_buffer.to_vec(), instruction: Instruction::OpCode(op), push_bytes, total_gas_used, @@ -84,19 +85,15 @@ impl Inspector for Debugger { } #[inline] - fn call( - &mut self, - data: &mut EVMData<'_, DB>, - call: &mut CallInputs, - ) -> (InstructionResult, Gas, Bytes) { + fn call(&mut self, ecx: &mut EvmContext, inputs: &mut CallInputs) -> Option { self.enter( - data.journaled_state.depth() as usize, - call.context.code_address, - call.context.scheme.into(), + ecx.journaled_state.depth() as usize, + inputs.context.code_address, + inputs.context.scheme.into(), ); - if call.contract == CHEATCODE_ADDRESS { - if let Some(selector) = call.input.get(..SELECTOR_LEN) { + if inputs.contract == CHEATCODE_ADDRESS { + if let Some(selector) = inputs.input.get(..SELECTOR_LEN) { self.arena.arena[self.head].steps.push(DebugStep { instruction: Instruction::Cheatcode(selector.try_into().unwrap()), ..Default::default() @@ -104,57 +101,58 @@ impl Inspector for Debugger { } } - (InstructionResult::Continue, Gas::new(call.gas_limit), Bytes::new()) + None } #[inline] fn call_end( &mut self, - _: &mut EVMData<'_, DB>, - _: &CallInputs, - gas: Gas, - status: InstructionResult, - retdata: Bytes, - ) -> (InstructionResult, Gas, Bytes) { + _context: &mut EvmContext, + _inputs: &CallInputs, + outcome: CallOutcome, + ) -> CallOutcome { self.exit(); - (status, gas, retdata) + outcome } #[inline] fn create( &mut self, - data: &mut EVMData<'_, DB>, - call: &mut CreateInputs, - ) -> (InstructionResult, Option
, Gas, Bytes) { - // TODO: Does this increase gas cost? - if let Err(err) = data.journaled_state.load_account(call.caller, data.db) { - let gas = Gas::new(call.gas_limit); - return (InstructionResult::Revert, None, gas, err.abi_encode_revert()); + ecx: &mut EvmContext, + inputs: &mut CreateInputs, + ) -> Option { + if let Err(err) = ecx.load_account(inputs.caller) { + let gas = Gas::new(inputs.gas_limit); + return Some(CreateOutcome::new( + InterpreterResult { + result: InstructionResult::Revert, + output: err.abi_encode_revert(), + gas, + }, + None, + )); } - let nonce = data.journaled_state.account(call.caller).info.nonce; + let nonce = ecx.journaled_state.account(inputs.caller).info.nonce; self.enter( - data.journaled_state.depth() as usize, - call.created_address(nonce), + ecx.journaled_state.depth() as usize, + inputs.created_address(nonce), CallKind::Create, ); - (InstructionResult::Continue, None, Gas::new(call.gas_limit), Bytes::new()) + None } #[inline] fn create_end( &mut self, - _: &mut EVMData<'_, DB>, - _: &CreateInputs, - status: InstructionResult, - address: Option
, - gas: Gas, - retdata: Bytes, - ) -> (InstructionResult, Option
, Gas, Bytes) { + _context: &mut EvmContext, + _inputs: &CreateInputs, + outcome: CreateOutcome, + ) -> CreateOutcome { self.exit(); - (status, address, gas, retdata) + outcome } } diff --git a/crates/evm/evm/src/inspectors/logs.rs b/crates/evm/evm/src/inspectors/logs.rs index b5324f576..a8e4a063c 100644 --- a/crates/evm/evm/src/inspectors/logs.rs +++ b/crates/evm/evm/src/inspectors/logs.rs @@ -1,4 +1,4 @@ -use alloy_primitives::{Address, Bytes, Log, B256}; +use alloy_primitives::{Address, Bytes, Log}; use alloy_sol_types::{SolEvent, SolInterface, SolValue}; use foundry_common::{fmt::ConsoleFmt, ErrorExt}; use foundry_evm_core::{ @@ -6,8 +6,8 @@ use foundry_evm_core::{ constants::HARDHAT_CONSOLE_ADDRESS, }; use revm::{ - interpreter::{CallInputs, Gas, InstructionResult}, - Database, EVMData, Inspector, + interpreter::{CallInputs, CallOutcome, Gas, InstructionResult, InterpreterResult}, + Database, EvmContext, Inspector, }; /// An inspector that collects logs during execution. @@ -38,23 +38,31 @@ impl LogCollector { } impl Inspector for LogCollector { - fn log(&mut self, _: &mut EVMData<'_, DB>, address: &Address, topics: &[B256], data: &Bytes) { - if let Some(log) = Log::new(*address, topics.to_vec(), data.clone()) { - self.logs.push(log); - } + fn log(&mut self, _context: &mut EvmContext, log: &Log) { + self.logs.push(log.clone()); } + #[inline] fn call( &mut self, - _: &mut EVMData<'_, DB>, - call: &mut CallInputs, - ) -> (InstructionResult, Gas, Bytes) { - let (status, reason) = if call.contract == HARDHAT_CONSOLE_ADDRESS { - self.hardhat_log(call.input.to_vec()) - } else { - (InstructionResult::Continue, Bytes::new()) - }; - (status, Gas::new(call.gas_limit), reason) + _context: &mut EvmContext, + inputs: &mut CallInputs, + ) -> Option { + if inputs.contract == HARDHAT_CONSOLE_ADDRESS { + let (res, out) = self.hardhat_log(inputs.input.to_vec()); + if res != InstructionResult::Continue { + return Some(CallOutcome { + result: InterpreterResult { + result: res, + output: out, + gas: Gas::new(inputs.gas_limit), + }, + memory_offset: inputs.return_memory_offset.clone(), + }) + } + } + + None } } diff --git a/crates/evm/evm/src/inspectors/mod.rs b/crates/evm/evm/src/inspectors/mod.rs index 20dd340d3..059033889 100644 --- a/crates/evm/evm/src/inspectors/mod.rs +++ b/crates/evm/evm/src/inspectors/mod.rs @@ -5,8 +5,7 @@ pub use foundry_evm_coverage::CoverageCollector; pub use foundry_evm_fuzz::Fuzzer; pub use foundry_evm_traces::{StackSnapshotType, TracingInspector, TracingInspectorConfig}; -mod access_list; -pub use access_list::AccessListTracer; +pub use revm_inspectors::access_list::AccessListInspector; mod chisel_state; pub use chisel_state::ChiselState; diff --git a/crates/evm/evm/src/inspectors/printer.rs b/crates/evm/evm/src/inspectors/printer.rs index 02a47fda1..81110669f 100644 --- a/crates/evm/evm/src/inspectors/printer.rs +++ b/crates/evm/evm/src/inspectors/printer.rs @@ -1,7 +1,6 @@ -use alloy_primitives::{Address, Bytes}; use revm::{ - interpreter::{opcode, CallInputs, CreateInputs, Gas, InstructionResult, Interpreter}, - Database, EVMData, Inspector, + interpreter::{opcode, CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter}, + Database, EvmContext, Inspector, }; #[derive(Clone, Debug, Default)] @@ -11,13 +10,13 @@ pub struct TracePrinter; impl Inspector for TracePrinter { // get opcode by calling `interp.contract.opcode(interp.program_counter())`. // all other information can be obtained from interp. - fn step(&mut self, interp: &mut Interpreter<'_>, data: &mut EVMData<'_, DB>) { + fn step(&mut self, interp: &mut Interpreter, ecx: &mut EvmContext) { let opcode = interp.current_opcode(); let opcode_str = opcode::OPCODE_JUMPMAP[opcode as usize]; let gas_remaining = interp.gas.remaining(); println!( "depth:{}, PC:{}, gas:{:#x}({}), OPCODE: {:?}({:?}) refund:{:#x}({}) Stack:{:?}, Data size:{}, Data: 0x{}", - data.journaled_state.depth(), + ecx.journaled_state.depth(), interp.program_counter(), gas_remaining, gas_remaining, @@ -31,11 +30,12 @@ impl Inspector for TracePrinter { ); } + #[inline] fn call( &mut self, - _data: &mut EVMData<'_, DB>, + _context: &mut EvmContext, inputs: &mut CallInputs, - ) -> (InstructionResult, Gas, Bytes) { + ) -> Option { println!( "SM CALL: {},context:{:?}, is_static:{:?}, transfer:{:?}, input_size:{:?}", inputs.contract, @@ -44,14 +44,15 @@ impl Inspector for TracePrinter { inputs.transfer, inputs.input.len(), ); - (InstructionResult::Continue, Gas::new(0), Bytes::new()) + None } + #[inline] fn create( &mut self, - _data: &mut EVMData<'_, DB>, + _context: &mut EvmContext, inputs: &mut CreateInputs, - ) -> (InstructionResult, Option
, Gas, Bytes) { + ) -> Option { println!( "CREATE CALL: caller:{}, scheme:{:?}, value:{:?}, init_code:{:?}, gas:{:?}", inputs.caller, @@ -60,6 +61,6 @@ impl Inspector for TracePrinter { hex::encode(&inputs.init_code), inputs.gas_limit ); - (InstructionResult::Continue, None, Gas::new(0), Bytes::new()) + None } } diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 94152a444..c118bcd3d 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -2,22 +2,17 @@ use super::{ Cheatcodes, CheatsConfig, ChiselState, CoverageCollector, Debugger, Fuzzer, LogCollector, StackSnapshotType, TracePrinter, TracingInspector, TracingInspectorConfig, }; -use alloy_primitives::{Address, Bytes, Log, B256, U256}; -use foundry_evm_core::{ - backend::DatabaseExt, - debug::DebugArena, - utils::{eval_to_instruction_result, halt_to_instruction_result}, -}; +use alloy_primitives::{Address, Bytes, Log, U256}; +use foundry_evm_core::{backend::DatabaseExt, debug::DebugArena}; use foundry_evm_coverage::HitMaps; use foundry_evm_traces::CallTraceArena; use revm::{ - evm_inner, interpreter::{ - return_revert, CallInputs, CallScheme, CreateInputs, Gas, InstructionResult, Interpreter, - Stack, + CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, Gas, InstructionResult, + Interpreter, InterpreterResult, }, - primitives::{BlockEnv, Env, ExecutionResult, Output, State, TransactTo}, - DatabaseCommit, EVMData, Inspector, + primitives::{BlockEnv, Env, EnvWithHandlerCfg, ExecutionResult, Output, State, TransactTo}, + DatabaseCommit, EvmContext, Inspector, }; use std::{collections::HashMap, sync::Arc}; @@ -241,7 +236,7 @@ pub struct InspectorData { pub debug: Option, pub coverage: Option, pub cheatcodes: Option, - pub chisel_state: Option<(Stack, Vec, InstructionResult)>, + pub chisel_state: Option<(Vec, Vec, InstructionResult)>, } /// Contains data about the state of outer/main EVM which created and invoked the inner EVM context. @@ -403,12 +398,11 @@ impl InspectorStack { fn do_call_end( &mut self, - data: &mut EVMData<'_, DB>, - call: &CallInputs, - remaining_gas: Gas, - status: InstructionResult, - retdata: Bytes, - ) -> (InstructionResult, Gas, Bytes) { + ecx: &mut EvmContext<&mut DB>, + inputs: &CallInputs, + outcome: CallOutcome, + ) -> CallOutcome { + let result = outcome.result.result; call_inspectors_adjust_depth!( [ &mut self.fuzzer, @@ -420,93 +414,106 @@ impl InspectorStack { &mut self.printer ], |inspector| { - let (new_status, new_gas, new_retdata) = - inspector.call_end(data, call, remaining_gas, status, retdata.clone()); + let new_outcome = inspector.call_end(ecx, inputs, outcome.clone()); // If the inspector returns a different status or a revert with a non-empty message, // we assume it wants to tell us something - if new_status != status || - (new_status == InstructionResult::Revert && new_retdata != retdata) + if new_outcome.result.result != result || + (new_outcome.result.result == InstructionResult::Revert && + new_outcome.output() != outcome.output()) { - Some((new_status, new_gas, new_retdata)) + Some(new_outcome) } else { None } }, self, - data + ecx ); - (status, remaining_gas, retdata) + + outcome } fn transact_inner( &mut self, - data: &mut EVMData<'_, DB>, + ecx: &mut EvmContext<&mut DB>, transact_to: TransactTo, caller: Address, input: Bytes, gas_limit: u64, value: U256, - ) -> (InstructionResult, Option
, Gas, Bytes) { - data.db.commit(data.journaled_state.state.clone()); + ) -> (InterpreterResult, Option
) { + ecx.db.commit(ecx.journaled_state.state.clone()); - let nonce = data + let nonce = ecx .journaled_state - .load_account(caller, data.db) + .load_account(caller, &mut ecx.db) .expect("failed to load caller") .0 .info .nonce; - let cached_env = data.env.clone(); + let cached_env = ecx.env.clone(); - data.env.block.basefee = U256::ZERO; - data.env.tx.caller = caller; - data.env.tx.transact_to = transact_to.clone(); - data.env.tx.data = input; - data.env.tx.value = value; - data.env.tx.nonce = Some(nonce); + ecx.env.block.basefee = U256::ZERO; + ecx.env.tx.caller = caller; + ecx.env.tx.transact_to = transact_to.clone(); + ecx.env.tx.data = input; + ecx.env.tx.value = value; + ecx.env.tx.nonce = Some(nonce); // Add 21000 to the gas limit to account for the base cost of transaction. - data.env.tx.gas_limit = gas_limit + 21000; + ecx.env.tx.gas_limit = gas_limit + 21000; // If we haven't disabled gas limit checks, ensure that transaction gas limit will not // exceed block gas limit. - if !data.env.cfg.disable_block_gas_limit { - data.env.tx.gas_limit = - std::cmp::min(data.env.tx.gas_limit, data.env.block.gas_limit.to()); + if !ecx.env.cfg.disable_block_gas_limit { + ecx.env.tx.gas_limit = + std::cmp::min(ecx.env.tx.gas_limit, ecx.env.block.gas_limit.to()); } - data.env.tx.gas_price = U256::ZERO; + ecx.env.tx.gas_price = U256::ZERO; self.inner_context_data = Some(InnerContextData { - sender: data.env.tx.caller, + sender: ecx.env.tx.caller, original_origin: cached_env.tx.caller, original_sender_nonce: nonce, is_create: matches!(transact_to, TransactTo::Create(_)), }); self.in_inner_context = true; - let res = evm_inner(data.env, data.db, Some(self)).transact(); + + let env = EnvWithHandlerCfg::new_with_spec_id(ecx.env.clone(), ecx.spec_id()); + let res = { + let mut evm = crate::utils::new_evm_with_inspector(&mut *ecx.db, env, &mut *self); + let res = evm.transact(); + + // need to reset the env in case it was modified via cheatcodes during execution + ecx.env = evm.context.evm.env; + res + }; + self.in_inner_context = false; self.inner_context_data = None; - data.env.tx = cached_env.tx; - data.env.block.basefee = cached_env.block.basefee; + ecx.env.tx = cached_env.tx; + ecx.env.block.basefee = cached_env.block.basefee; let mut gas = Gas::new(gas_limit); let Ok(mut res) = res else { // Should we match, encode and propagate error as a revert reason? - return (InstructionResult::Revert, None, gas, Bytes::new()); + let result = + InterpreterResult { result: InstructionResult::Revert, output: Bytes::new(), gas }; + return (result, None) }; // Commit changes after transaction - data.db.commit(res.state.clone()); + ecx.db.commit(res.state.clone()); // Update both states with new DB data after commit. - update_state(&mut data.journaled_state.state, data.db); - update_state(&mut res.state, data.db); + update_state(&mut ecx.journaled_state.state, &mut ecx.db); + update_state(&mut res.state, &mut ecx.db); // Merge transaction journal into the active journal. for (addr, acc) in res.state { - if let Some(acc_mut) = data.journaled_state.state.get_mut(&addr) { + if let Some(acc_mut) = ecx.journaled_state.state.get_mut(&addr) { acc_mut.status |= acc.status; for (key, val) in acc.storage { if !acc_mut.storage.contains_key(&key) { @@ -514,11 +521,11 @@ impl InspectorStack { } } } else { - data.journaled_state.state.insert(addr, acc); + ecx.journaled_state.state.insert(addr, acc); } } - match res.result { + let (result, address, output) = match res.result { ExecutionResult::Success { reason, gas_used, gas_refunded, logs: _, output } => { gas.set_refund(gas_refunded as i64); gas.record_cost(gas_used); @@ -526,27 +533,31 @@ impl InspectorStack { Output::Create(_, address) => address, Output::Call(_) => None, }; - (eval_to_instruction_result(reason), address, gas, output.into_data()) + (reason.into(), address, output.into_data()) } ExecutionResult::Halt { reason, gas_used } => { gas.record_cost(gas_used); - (halt_to_instruction_result(reason), None, gas, Bytes::new()) + (reason.into(), None, Bytes::new()) } ExecutionResult::Revert { gas_used, output } => { gas.record_cost(gas_used); - (InstructionResult::Revert, None, gas, output) + (InstructionResult::Revert, None, output) } - } + }; + (InterpreterResult { result, output, gas }, address) } /// Adjusts the EVM data for the inner EVM context. /// Should be called on the top-level call of inner context (depth == 0 && /// self.in_inner_context) Decreases sender nonce for CALLs to keep backwards compatibility /// Updates tx.origin to the value before entering inner context - fn adjust_evm_data_for_inner_context(&mut self, data: &mut EVMData<'_, DB>) { + fn adjust_evm_data_for_inner_context( + &mut self, + ecx: &mut EvmContext<&mut DB>, + ) { let inner_context_data = self.inner_context_data.as_ref().expect("should be called in inner context"); - let sender_acc = data + let sender_acc = ecx .journaled_state .state .get_mut(&inner_context_data.sender) @@ -554,13 +565,17 @@ impl InspectorStack { if !inner_context_data.is_create { sender_acc.info.nonce = inner_context_data.original_sender_nonce; } - data.env.tx.caller = inner_context_data.original_origin; + ecx.env.tx.caller = inner_context_data.original_origin; } } -impl Inspector for InspectorStack { - fn initialize_interp(&mut self, interpreter: &mut Interpreter<'_>, data: &mut EVMData<'_, DB>) { - let res = interpreter.instruction_result; +// NOTE: `&mut DB` is required because we recurse inside of `transact_inner` and we need to use the +// same reference to the DB, otherwise there's infinite recursion and Rust fails to instatiate this +// implementation. This currently works because internally we only use `&mut DB` anyways, but if +// this ever needs to be changed, this can be reverted back to using just `DB`, and instead using +// dynamic dispatch (`&mut dyn ...`) in `transact_inner`. +impl Inspector<&mut DB> for InspectorStack { + fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<&mut DB>) { call_inspectors_adjust_depth!( [ &mut self.debugger, @@ -571,22 +586,15 @@ impl Inspector for InspectorStack { &mut self.printer ], |inspector| { - inspector.initialize_interp(interpreter, data); - - // Allow inspectors to exit early - if interpreter.instruction_result != res { - Some(()) - } else { - None - } + inspector.initialize_interp(interpreter, ecx); + None::<()> }, self, - data + ecx ); } - fn step(&mut self, interpreter: &mut Interpreter<'_>, data: &mut EVMData<'_, DB>) { - let res = interpreter.instruction_result; + fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<&mut DB>) { call_inspectors_adjust_depth!( [ &mut self.fuzzer, @@ -598,40 +606,15 @@ impl Inspector for InspectorStack { &mut self.printer ], |inspector| { - inspector.step(interpreter, data); - - // Allow inspectors to exit early - if interpreter.instruction_result != res { - Some(()) - } else { - None - } - }, - self, - data - ); - } - - fn log( - &mut self, - evm_data: &mut EVMData<'_, DB>, - address: &Address, - topics: &[B256], - data: &Bytes, - ) { - call_inspectors_adjust_depth!( - [&mut self.tracer, &mut self.log_collector, &mut self.cheatcodes, &mut self.printer], - |inspector| { - inspector.log(evm_data, address, topics, data); - None + inspector.step(interpreter, ecx); + None::<()> }, self, - evm_data + ecx ); } - fn step_end(&mut self, interpreter: &mut Interpreter<'_>, data: &mut EVMData<'_, DB>) { - let res = interpreter.instruction_result; + fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<&mut DB>) { call_inspectors_adjust_depth!( [ &mut self.debugger, @@ -642,28 +625,34 @@ impl Inspector for InspectorStack { &mut self.chisel_state ], |inspector| { - inspector.step_end(interpreter, data); + inspector.step_end(interpreter, ecx); + None::<()> + }, + self, + ecx + ); + } - // Allow inspectors to exit early - if interpreter.instruction_result != res { - Some(()) - } else { - None - } + fn log(&mut self, ecx: &mut EvmContext<&mut DB>, log: &Log) { + call_inspectors_adjust_depth!( + [&mut self.tracer, &mut self.log_collector, &mut self.cheatcodes, &mut self.printer], + |inspector| { + inspector.log(ecx, log); + None::<()> }, self, - data + ecx ); } fn call( &mut self, - data: &mut EVMData<'_, DB>, + ecx: &mut EvmContext<&mut DB>, call: &mut CallInputs, - ) -> (InstructionResult, Gas, Bytes) { - if self.in_inner_context && data.journaled_state.depth == 0 { - self.adjust_evm_data_for_inner_context(data); - return (InstructionResult::Continue, Gas::new(call.gas_limit), Bytes::new()); + ) -> Option { + if self.in_inner_context && ecx.journaled_state.depth == 0 { + self.adjust_evm_data_for_inner_context(ecx); + return None; } call_inspectors_adjust_depth!( @@ -677,73 +666,70 @@ impl Inspector for InspectorStack { &mut self.printer ], |inspector| { - let (status, gas, retdata) = inspector.call(data, call); - - // Allow inspectors to exit early - if status != InstructionResult::Continue { - Some((status, gas, retdata)) - } else { - None + let mut out = None; + if let Some(output) = inspector.call(ecx, call) { + if output.result.result != InstructionResult::Continue { + out = Some(Some(output)); + } } + out }, self, - data + ecx ); if self.enable_isolation && call.context.scheme == CallScheme::Call && !self.in_inner_context && - data.journaled_state.depth == 1 + ecx.journaled_state.depth == 1 { - let (res, _, gas, output) = self.transact_inner( - data, + let (result, _) = self.transact_inner( + ecx, TransactTo::Call(call.contract), call.context.caller, call.input.clone(), call.gas_limit, call.transfer.value, ); - return (res, gas, output); + return Some(CallOutcome { result, memory_offset: call.return_memory_offset.clone() }) } - (InstructionResult::Continue, Gas::new(call.gas_limit), Bytes::new()) + None } fn call_end( &mut self, - data: &mut EVMData<'_, DB>, - call: &CallInputs, - remaining_gas: Gas, - status: InstructionResult, - retdata: Bytes, - ) -> (InstructionResult, Gas, Bytes) { + ecx: &mut EvmContext<&mut DB>, + inputs: &CallInputs, + outcome: CallOutcome, + ) -> CallOutcome { // Inner context calls with depth 0 are being dispatched as top-level calls with depth 1. // Avoid processing twice. - if self.in_inner_context && data.journaled_state.depth == 0 { - return (status, remaining_gas, retdata); + if self.in_inner_context && ecx.journaled_state.depth == 0 { + return outcome } - let res = self.do_call_end(data, call, remaining_gas, status, retdata); - if matches!(res.0, return_revert!()) { + let outcome = self.do_call_end(ecx, inputs, outcome); + if outcome.result.is_revert() { // Encountered a revert, since cheatcodes may have altered the evm state in such a way // that violates some constraints, e.g. `deal`, we need to manually roll back on revert // before revm reverts the state itself if let Some(cheats) = self.cheatcodes.as_mut() { - cheats.on_revert(data); + cheats.on_revert(ecx); } } - res + outcome } fn create( &mut self, - data: &mut EVMData<'_, DB>, - call: &mut CreateInputs, - ) -> (InstructionResult, Option
, Gas, Bytes) { - if self.in_inner_context && data.journaled_state.depth == 0 { - self.adjust_evm_data_for_inner_context(data); - return (InstructionResult::Continue, None, Gas::new(call.gas_limit), Bytes::new()); + ecx: &mut EvmContext<&mut DB>, + create: &mut CreateInputs, + ) -> Option { + if self.in_inner_context && ecx.journaled_state.depth == 0 { + self.adjust_evm_data_for_inner_context(ecx); + return None; } call_inspectors_adjust_depth!( @@ -755,48 +741,40 @@ impl Inspector for InspectorStack { &mut self.cheatcodes, &mut self.printer ], - |inspector| { - let (status, addr, gas, retdata) = inspector.create(data, call); - - // Allow inspectors to exit early - if status != InstructionResult::Continue { - Some((status, addr, gas, retdata)) - } else { - None - } - }, + |inspector| { inspector.create(ecx, create).map(Some) }, self, - data + ecx ); - if self.enable_isolation && !self.in_inner_context && data.journaled_state.depth == 1 { - return self.transact_inner( - data, - TransactTo::Create(call.scheme), - call.caller, - call.init_code.clone(), - call.gas_limit, - call.value, + if self.enable_isolation && !self.in_inner_context && ecx.journaled_state.depth == 1 { + let (result, address) = self.transact_inner( + ecx, + TransactTo::Create(create.scheme), + create.caller, + create.init_code.clone(), + create.gas_limit, + create.value, ); + return Some(CreateOutcome { result, address }) } - (InstructionResult::Continue, None, Gas::new(call.gas_limit), Bytes::new()) + None } fn create_end( &mut self, - data: &mut EVMData<'_, DB>, + ecx: &mut EvmContext<&mut DB>, call: &CreateInputs, - status: InstructionResult, - address: Option
, - remaining_gas: Gas, - retdata: Bytes, - ) -> (InstructionResult, Option
, Gas, Bytes) { + outcome: CreateOutcome, + ) -> CreateOutcome { // Inner context calls with depth 0 are being dispatched as top-level calls with depth 1. // Avoid processing twice. - if self.in_inner_context && data.journaled_state.depth == 0 { - return (status, address, remaining_gas, retdata); + if self.in_inner_context && ecx.journaled_state.depth == 0 { + return outcome } + + let result = outcome.result.result; + call_inspectors_adjust_depth!( [ &mut self.debugger, @@ -807,26 +785,24 @@ impl Inspector for InspectorStack { &mut self.printer ], |inspector| { - let (new_status, new_address, new_gas, new_retdata) = inspector.create_end( - data, - call, - status, - address, - remaining_gas, - retdata.clone(), - ); - - if new_status != status { - Some((new_status, new_address, new_gas, new_retdata)) + let new_outcome = inspector.create_end(ecx, call, outcome.clone()); + + // If the inspector returns a different status or a revert with a non-empty message, + // we assume it wants to tell us something + if new_outcome.result.result != result || + (new_outcome.result.result == InstructionResult::Revert && + new_outcome.output() != outcome.output()) + { + Some(new_outcome) } else { None } }, self, - data + ecx ); - (status, address, remaining_gas, retdata) + outcome } fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) { diff --git a/crates/evm/fuzz/src/inspector.rs b/crates/evm/fuzz/src/inspector.rs index 65c6e5ec9..e58afb214 100644 --- a/crates/evm/fuzz/src/inspector.rs +++ b/crates/evm/fuzz/src/inspector.rs @@ -1,8 +1,7 @@ use crate::{invariant::RandomCallGenerator, strategies::EvmFuzzState}; -use alloy_primitives::Bytes; use revm::{ - interpreter::{CallInputs, CallScheme, Gas, InstructionResult, Interpreter}, - Database, EVMData, Inspector, + interpreter::{CallInputs, CallOutcome, CallScheme, Interpreter}, + Database, EvmContext, Inspector, }; /// An inspector that can fuzz and collect data for that effect. @@ -18,41 +17,35 @@ pub struct Fuzzer { impl Inspector for Fuzzer { #[inline] - fn step(&mut self, interpreter: &mut Interpreter<'_>, _: &mut EVMData<'_, DB>) { + fn step(&mut self, interp: &mut Interpreter, _context: &mut EvmContext) { // We only collect `stack` and `memory` data before and after calls. if self.collect { - self.collect_data(interpreter); + self.collect_data(interp); self.collect = false; } } #[inline] - fn call( - &mut self, - data: &mut EVMData<'_, DB>, - call: &mut CallInputs, - ) -> (InstructionResult, Gas, Bytes) { + fn call(&mut self, ecx: &mut EvmContext, inputs: &mut CallInputs) -> Option { // We don't want to override the very first call made to the test contract. - if self.call_generator.is_some() && data.env.tx.caller != call.context.caller { - self.override_call(call); + if self.call_generator.is_some() && ecx.env.tx.caller != inputs.context.caller { + self.override_call(inputs); } // We only collect `stack` and `memory` data before and after calls. // this will be turned off on the next `step` self.collect = true; - (InstructionResult::Continue, Gas::new(call.gas_limit), Bytes::new()) + None } #[inline] fn call_end( &mut self, - _: &mut EVMData<'_, DB>, - _: &CallInputs, - remaining_gas: Gas, - status: InstructionResult, - retdata: Bytes, - ) -> (InstructionResult, Gas, Bytes) { + _context: &mut EvmContext, + _inputs: &CallInputs, + outcome: CallOutcome, + ) -> CallOutcome { if let Some(ref mut call_generator) = self.call_generator { call_generator.used = false; } @@ -61,13 +54,13 @@ impl Inspector for Fuzzer { // this will be turned off on the next `step` self.collect = true; - (status, remaining_gas, retdata) + outcome } } impl Fuzzer { /// Collects `stack` and `memory` values into the fuzz dictionary. - fn collect_data(&mut self, interpreter: &Interpreter<'_>) { + fn collect_data(&mut self, interpreter: &Interpreter) { let mut state = self.fuzz_state.write(); for slot in interpreter.stack().data() { diff --git a/crates/script/src/runner.rs b/crates/script/src/runner.rs index 59f6402d3..d91af9dc6 100644 --- a/crates/script/src/runner.rs +++ b/crates/script/src/runner.rs @@ -311,7 +311,7 @@ impl ScriptRunner { match res.exit_reason { InstructionResult::Revert | InstructionResult::OutOfGas | - InstructionResult::OutOfFund => { + InstructionResult::OutOfFunds => { lowest_gas_limit = mid_gas_limit; } _ => { diff --git a/testdata/cheats/MemSafety.t.sol b/testdata/cheats/MemSafety.t.sol index 48205233b..eb00e77de 100644 --- a/testdata/cheats/MemSafety.t.sol +++ b/testdata/cheats/MemSafety.t.sol @@ -411,6 +411,22 @@ contract MemSafetyTest is DSTest { uint256 b = a + 1; } + /// @dev Tests that expanding memory outside of the range given to `expectSafeMemory` + /// will cause the test to fail while using the `MLOAD` opcode. + function testExpectSafeMemory_MLOAD_REVERT() public { + vm.expectSafeMemory(0x80, 0x100); + + vm.expectRevert(); + + // This should revert. Ugly hack to make sure the mload isn't optimized + // out. + uint256 a; + assembly { + a := mload(0x100) + } + uint256 b = a + 1; + } + /// @dev Tests that expanding memory outside of the range given to `expectSafeMemory` /// will cause the test to fail while using the `MLOAD` opcode. function testFailExpectSafeMemory_MLOAD() public { @@ -486,6 +502,17 @@ contract MemSafetyTest is DSTest { } } + /// @dev Tests that expanding memory outside of the range given to `expectSafeMemory` + /// will cause the test to fail while using the `LOG0` opcode. + function testExpectSafeMemory_LOG0_REVERT() public { + vm.expectSafeMemory(0x80, 0x100); + vm.expectRevert(); + // This should revert. + assembly { + log0(0x100, 0x20) + } + } + //////////////////////////////////////////////////////////////// // CREATE/CREATE2 (Read Expansion) // //////////////////////////////////////////////////////////////// From 55c30ddac8d314c16dc3b1f5fa269a84e0a1d046 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 8 Mar 2024 15:17:12 +0100 Subject: [PATCH 047/430] fix: insert genesis hash into db (#7325) --- crates/anvil/src/config.rs | 7 ++++++- crates/anvil/src/eth/backend/fork.rs | 12 +++++------- crates/anvil/src/eth/backend/mem/mod.rs | 4 ++++ 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 14021e289..be18d2827 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -1104,7 +1104,12 @@ latest block number: {latest_block}" total_difficulty: block.header.total_difficulty.unwrap_or_default(), }; - (ForkedDatabase::new(backend, block_chain_db), config) + let mut db = ForkedDatabase::new(backend, block_chain_db); + + // need to insert the forked block's hash + db.insert_block_hash(U256::from(config.block_number), config.block_hash); + + (db, config) } } diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index c763f4c9a..71dadf40e 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -79,15 +79,13 @@ impl ClientFork { let base_fee = block.header.base_fee_per_gas; let total_difficulty = block.header.total_difficulty.unwrap_or_default(); - self.config.write().update_block( - block.header.number.ok_or(BlockchainError::BlockNotFound)?.to::(), - block_hash, - timestamp, - base_fee, - total_difficulty, - ); + let number = block.header.number.ok_or(BlockchainError::BlockNotFound)?.to::(); + self.config.write().update_block(number, block_hash, timestamp, base_fee, total_difficulty); self.clear_cached_storage(); + + self.database.write().await.insert_block_hash(U256::from(number), block_hash); + Ok(()) } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 8fb868dcd..0eba1474d 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -302,6 +302,10 @@ impl Backend { for (account, info) in self.genesis.account_infos() { db.insert_account(account, info); } + + // insert the new genesis hash to the database so it's available for the next block in + // the evm + db.insert_block_hash(U256::from(self.best_number()), self.best_hash()); } let db = self.db.write().await; From 0ab9e3c6fbeaa921c503c2ba1f319834b23f424b Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 8 Mar 2024 15:30:34 +0100 Subject: [PATCH 048/430] chore: unpin nightly in ci, clippy (#7345) --- .github/workflows/test.yml | 2 -- crates/config/src/lib.rs | 2 +- crates/evm/core/src/backend/snapshot.rs | 2 +- crates/evm/evm/src/executors/invariant/mod.rs | 2 +- crates/evm/traces/src/decoder/mod.rs | 2 +- crates/script/src/lib.rs | 1 - crates/script/src/sequence.rs | 2 +- 7 files changed, 5 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 13e2d4367..ffef82ca5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -133,8 +133,6 @@ jobs: steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@clippy - with: - toolchain: nightly-2024-02-03 - uses: Swatinem/rust-cache@v2 with: cache-on-failure: true diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 7a1d4195d..cfee46312 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -963,7 +963,7 @@ impl Config { (Ok(mut config), Some(key)) => { // we update the key, because if an etherscan_api_key is set, it should take // precedence over the entry, since this is usually set via env var or CLI args. - config.key = key.clone(); + config.key.clone_from(key); return Ok(Some(config)) } (Ok(config), None) => return Ok(Some(config)), diff --git a/crates/evm/core/src/backend/snapshot.rs b/crates/evm/core/src/backend/snapshot.rs index 35ca9222b..53ce6f7dd 100644 --- a/crates/evm/core/src/backend/snapshot.rs +++ b/crates/evm/core/src/backend/snapshot.rs @@ -39,7 +39,7 @@ impl BackendSnapshot { /// journaled_state includes the same logs, we can simply replace use that See also /// `DatabaseExt::revert` pub fn merge(&mut self, current: &JournaledState) { - self.journaled_state.logs = current.logs.clone(); + self.journaled_state.logs.clone_from(¤t.logs); } } diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 2ec627b11..0a7b81943 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -236,7 +236,7 @@ impl<'a> InvariantExecutor<'a> { ); if !can_continue || current_run == self.config.depth - 1 { - *last_run_calldata.borrow_mut() = inputs.clone(); + last_run_calldata.borrow_mut().clone_from(&inputs); } if !can_continue { diff --git a/crates/evm/traces/src/decoder/mod.rs b/crates/evm/traces/src/decoder/mod.rs index 78193a5f9..2eac34d00 100644 --- a/crates/evm/traces/src/decoder/mod.rs +++ b/crates/evm/traces/src/decoder/mod.rs @@ -189,7 +189,7 @@ impl CallTraceDecoder { let default_labels = &Self::new().labels; if self.labels.len() > default_labels.len() { - self.labels = default_labels.clone(); + self.labels.clone_from(default_labels); } self.receive_contracts.clear(); diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 6d8fb87b8..ef579716f 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -605,7 +605,6 @@ impl ScriptConfig { #[cfg(test)] mod tests { use super::*; - use foundry_cli::utils::LoadConfig; use foundry_config::{NamedChain, UnresolvedEnvVarError}; use std::fs; use tempfile::tempdir; diff --git a/crates/script/src/sequence.rs b/crates/script/src/sequence.rs index 6a78d1ac4..9fdee7b02 100644 --- a/crates/script/src/sequence.rs +++ b/crates/script/src/sequence.rs @@ -365,7 +365,7 @@ impl ScriptSequence { self.transactions .iter_mut() .enumerate() - .for_each(|(i, tx)| tx.rpc = sensitive.transactions[i].rpc.clone()); + .for_each(|(i, tx)| tx.rpc.clone_from(&sensitive.transactions[i].rpc)); } } From 2c6955c167ec38ecc8b514b130afb7862951d25f Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 8 Mar 2024 19:01:36 +0100 Subject: [PATCH 049/430] chore: rename `FuzzBackendWrapper` to `CowBackend` (#7349) Name what it is not what it's used for, or something like that --- .../evm/core/src/backend/{fuzz.rs => cow.rs} | 31 +++++++------------ crates/evm/core/src/backend/mod.rs | 4 +-- crates/evm/evm/src/executors/mod.rs | 21 +++++++------ crates/forge/src/multi_runner.rs | 2 +- 4 files changed, 26 insertions(+), 32 deletions(-) rename crates/evm/core/src/backend/{fuzz.rs => cow.rs} (89%) diff --git a/crates/evm/core/src/backend/fuzz.rs b/crates/evm/core/src/backend/cow.rs similarity index 89% rename from crates/evm/core/src/backend/fuzz.rs rename to crates/evm/core/src/backend/cow.rs index 69bcac72b..d8ea27c0d 100644 --- a/crates/evm/core/src/backend/fuzz.rs +++ b/crates/evm/core/src/backend/cow.rs @@ -31,16 +31,16 @@ use std::{borrow::Cow, collections::HashMap}; /// function via immutable raw (no state changes) calls. /// /// **N.B.**: we're assuming cheatcodes that alter the state (like multi fork swapping) are niche. -/// If they executed during fuzzing, it will require a clone of the initial input database. This way -/// we can support these cheatcodes in fuzzing cheaply without adding overhead for fuzz tests that +/// If they executed, it will require a clone of the initial input database. +/// This way we can support these cheatcodes cheaply without adding overhead for tests that /// don't make use of them. Alternatively each test case would require its own `Backend` clone, /// which would add significant overhead for large fuzz sets even if the Database is not big after /// setup. #[derive(Clone, Debug)] -pub struct FuzzBackendWrapper<'a> { - /// The underlying immutable `Backend` +pub struct CowBackend<'a> { + /// The underlying `Backend`. /// - /// No calls on the `FuzzBackendWrapper` will ever persistently modify the `backend`'s state. + /// No calls on the `CowBackend` will ever persistently modify the `backend`'s state. pub backend: Cow<'a, Backend>, /// Keeps track of whether the backed is already initialized is_initialized: bool, @@ -48,7 +48,8 @@ pub struct FuzzBackendWrapper<'a> { spec_id: SpecId, } -impl<'a> FuzzBackendWrapper<'a> { +impl<'a> CowBackend<'a> { + /// Creates a new `CowBackend` with the given `Backend`. pub fn new(backend: &'a Backend) -> Self { Self { backend: Cow::Borrowed(backend), is_initialized: false, spec_id: SpecId::LATEST } } @@ -75,7 +76,7 @@ impl<'a> FuzzBackendWrapper<'a> { Ok(res) } - /// Returns whether there was a snapshot failure in the fuzz backend. + /// Returns whether there was a snapshot failure in the backend. /// /// This is bubbled up from the underlying Copy-On-Write backend when a revert occurs. pub fn has_snapshot_failure(&self) -> bool { @@ -105,9 +106,8 @@ impl<'a> FuzzBackendWrapper<'a> { } } -impl<'a> DatabaseExt for FuzzBackendWrapper<'a> { +impl<'a> DatabaseExt for CowBackend<'a> { fn snapshot(&mut self, journaled_state: &JournaledState, env: &Env) -> U256 { - trace!("fuzz: create snapshot"); self.backend_mut(env).snapshot(journaled_state, env) } @@ -118,7 +118,6 @@ impl<'a> DatabaseExt for FuzzBackendWrapper<'a> { current: &mut Env, action: RevertSnapshotAction, ) -> Option { - trace!(?id, "fuzz: revert snapshot"); self.backend_mut(current).revert(id, journaled_state, current, action) } @@ -137,7 +136,6 @@ impl<'a> DatabaseExt for FuzzBackendWrapper<'a> { } fn create_fork(&mut self, fork: CreateFork) -> eyre::Result { - trace!("fuzz: create fork"); self.backend.to_mut().create_fork(fork) } @@ -146,7 +144,6 @@ impl<'a> DatabaseExt for FuzzBackendWrapper<'a> { fork: CreateFork, transaction: B256, ) -> eyre::Result { - trace!(?transaction, "fuzz: create fork at"); self.backend.to_mut().create_fork_at_transaction(fork, transaction) } @@ -156,7 +153,6 @@ impl<'a> DatabaseExt for FuzzBackendWrapper<'a> { env: &mut Env, journaled_state: &mut JournaledState, ) -> eyre::Result<()> { - trace!(?id, "fuzz: select fork"); self.backend_mut(env).select_fork(id, env, journaled_state) } @@ -167,7 +163,6 @@ impl<'a> DatabaseExt for FuzzBackendWrapper<'a> { env: &mut Env, journaled_state: &mut JournaledState, ) -> eyre::Result<()> { - trace!(?id, ?block_number, "fuzz: roll fork"); self.backend_mut(env).roll_fork(id, block_number, env, journaled_state) } @@ -178,7 +173,6 @@ impl<'a> DatabaseExt for FuzzBackendWrapper<'a> { env: &mut Env, journaled_state: &mut JournaledState, ) -> eyre::Result<()> { - trace!(?id, ?transaction, "fuzz: roll fork to transaction"); self.backend_mut(env).roll_fork_to_transaction(id, transaction, env, journaled_state) } @@ -190,7 +184,6 @@ impl<'a> DatabaseExt for FuzzBackendWrapper<'a> { journaled_state: &mut JournaledState, inspector: &mut I, ) -> eyre::Result<()> { - trace!(?id, ?transaction, "fuzz: execute transaction"); self.backend_mut(env).transact(id, transaction, env, journaled_state, inspector) } @@ -251,7 +244,7 @@ impl<'a> DatabaseExt for FuzzBackendWrapper<'a> { } } -impl<'a> DatabaseRef for FuzzBackendWrapper<'a> { +impl<'a> DatabaseRef for CowBackend<'a> { type Error = DatabaseError; fn basic_ref(&self, address: Address) -> Result, Self::Error> { @@ -271,7 +264,7 @@ impl<'a> DatabaseRef for FuzzBackendWrapper<'a> { } } -impl<'a> Database for FuzzBackendWrapper<'a> { +impl<'a> Database for CowBackend<'a> { type Error = DatabaseError; fn basic(&mut self, address: Address) -> Result, Self::Error> { @@ -291,7 +284,7 @@ impl<'a> Database for FuzzBackendWrapper<'a> { } } -impl<'a> DatabaseCommit for FuzzBackendWrapper<'a> { +impl<'a> DatabaseCommit for CowBackend<'a> { fn commit(&mut self, changes: Map) { self.backend.to_mut().commit(changes) } diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index e3b7ab339..1ad5ec481 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -32,8 +32,8 @@ pub use diagnostic::RevertDiagnostic; mod error; pub use error::{DatabaseError, DatabaseResult}; -mod fuzz; -pub use fuzz::FuzzBackendWrapper; +mod cow; +pub use cow::CowBackend; mod in_memory_db; pub use in_memory_db::{EmptyDBWrapper, FoundryEvmInMemoryDB, MemDb}; diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index f1076925f..a62d070a5 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -14,7 +14,7 @@ use alloy_json_abi::Function; use alloy_primitives::{Address, Bytes, Log, U256}; use foundry_common::{abi::IntoFunction, evm::Breakpoints}; use foundry_evm_core::{ - backend::{Backend, DatabaseError, DatabaseExt, DatabaseResult, FuzzBackendWrapper}, + backend::{Backend, CowBackend, DatabaseError, DatabaseExt, DatabaseResult}, constants::{ CALLER, CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, DEFAULT_CREATE2_DEPLOYER_CODE, }, @@ -309,7 +309,7 @@ impl Executor { let mut inspector = self.inspector.clone(); // Build VM let mut env = self.build_test_env(from, TransactTo::Call(to), calldata, value); - let mut db = FuzzBackendWrapper::new(&self.backend); + let mut db = CowBackend::new(&self.backend); let result = db.inspect(&mut env, &mut inspector)?; // Persist the snapshot failure recorded on the fuzz backend wrapper. @@ -483,15 +483,16 @@ impl Executor { /// /// ## Background /// - /// Executing and failure checking [Executor::ensure_success] are two steps, for ds-test + /// Executing and failure checking [`Executor::ensure_success`] are two steps, for ds-test /// legacy reasons failures can be stored in a global variables and needs to be called via a - /// solidity call `failed()(bool)`. For fuzz tests we’re using the - /// `FuzzBackendWrapper` which is a Cow of the executor’s backend which lazily clones the - /// backend when it’s mutated via cheatcodes like `snapshot`. Snapshots make it even - /// more complicated because now we also need to keep track of that global variable when we - /// revert to a snapshot (because it is stored in state). Now, the problem is that - /// the `FuzzBackendWrapper` is dropped after every call, so we need to keep track of the - /// snapshot failure in the [RawCallResult] instead. + /// solidity call `failed()(bool)`. + /// + /// For fuzz tests we’re using the `CowBackend` which is a Cow of the executor’s backend which + /// lazily clones the backend when it’s mutated via cheatcodes like `snapshot`. Snapshots + /// make it even more complicated because now we also need to keep track of that global + /// variable when we revert to a snapshot (because it is stored in state). Now, the problem + /// is that the `CowBackend` is dropped after every call, so we need to keep track of the + /// snapshot failure in the [`RawCallResult`] instead. pub fn is_raw_call_success( &self, address: Address, diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 36d2f2fd8..7712a7ee6 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -345,7 +345,7 @@ impl MultiContractRunnerBuilder { } if let Some(bytes) = linked_contract.get_deployed_bytecode_bytes() { - known_contracts.insert(id.clone(), (abi.clone(), bytes.to_vec())); + known_contracts.insert(id.clone(), (abi.clone(), bytes.into_owned().into())); } } From 9ec42d6f03bafbd3b9bb8e258ca67d7887b1f2e7 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Fri, 8 Mar 2024 22:01:47 +0400 Subject: [PATCH 050/430] fix(forge): correctly write build info (#7347) fix --- crates/config/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index cfee46312..dd0bf9807 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -679,7 +679,7 @@ impl Config { .set_auto_detect(self.is_auto_detect()) .set_offline(self.offline) .set_cached(cached) - .set_build_info(cached && self.build_info) + .set_build_info(!no_artifacts && self.build_info) .set_no_artifacts(no_artifacts) .build()?; From 9f6bb3bb47de9d5a1f2a6c38cbc57e0f4f5508c2 Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Sat, 9 Mar 2024 16:28:12 +0100 Subject: [PATCH 051/430] fix(cheatcodes): add repro test for once reported issues around empty JSON arrays (#7348) * add repro case, fortunately issue has been resolved over time * revert debug line --- crates/forge/tests/it/repros.rs | 3 ++ testdata/fixtures/Json/Issue4402.json | 4 ++ testdata/fixtures/Toml/Issue4402.toml | 2 + testdata/repros/Issue4402.t.sol | 64 +++++++++++++++++++++++++++ 4 files changed, 73 insertions(+) create mode 100644 testdata/fixtures/Json/Issue4402.json create mode 100644 testdata/fixtures/Toml/Issue4402.toml create mode 100644 testdata/repros/Issue4402.t.sol diff --git a/crates/forge/tests/it/repros.rs b/crates/forge/tests/it/repros.rs index d8b4447cf..108434e50 100644 --- a/crates/forge/tests/it/repros.rs +++ b/crates/forge/tests/it/repros.rs @@ -176,6 +176,9 @@ test_repro!(3753); // https://github.com/foundry-rs/foundry/issues/3792 test_repro!(3792); +// https://github.com/foundry-rs/foundry/issues/4402 +test_repro!(4402); + // https://github.com/foundry-rs/foundry/issues/4586 test_repro!(4586); diff --git a/testdata/fixtures/Json/Issue4402.json b/testdata/fixtures/Json/Issue4402.json new file mode 100644 index 000000000..e981817fe --- /dev/null +++ b/testdata/fixtures/Json/Issue4402.json @@ -0,0 +1,4 @@ +{ + "tokens": ["0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"], + "empty": [] +} \ No newline at end of file diff --git a/testdata/fixtures/Toml/Issue4402.toml b/testdata/fixtures/Toml/Issue4402.toml new file mode 100644 index 000000000..8f7d11023 --- /dev/null +++ b/testdata/fixtures/Toml/Issue4402.toml @@ -0,0 +1,2 @@ +tokens = ["0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"] +empty = [] diff --git a/testdata/repros/Issue4402.t.sol b/testdata/repros/Issue4402.t.sol new file mode 100644 index 000000000..c69285b7f --- /dev/null +++ b/testdata/repros/Issue4402.t.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "../cheats/Vm.sol"; + +// https://github.com/foundry-rs/foundry/issues/4402 +contract Issue4402Test is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + function testReadNonEmptyArray() public { + string memory path = "fixtures/Json/Issue4402.json"; + string memory json = vm.readFile(path); + address[] memory tokens = vm.parseJsonAddressArray(json, ".tokens"); + assertEq(tokens.length, 1); + + path = "fixtures/Toml/Issue4402.toml"; + string memory toml = vm.readFile(path); + tokens = vm.parseTomlAddressArray(toml, ".tokens"); + assertEq(tokens.length, 1); + } + + function testReadEmptyArray() public { + string memory path = "fixtures/Json/Issue4402.json"; + string memory json = vm.readFile(path); + + // Every one of these used to causes panic + address[] memory emptyAddressArray = vm.parseJsonAddressArray(json, ".empty"); + bool[] memory emptyBoolArray = vm.parseJsonBoolArray(json, ".empty"); + bytes[] memory emptyBytesArray = vm.parseJsonBytesArray(json, ".empty"); + bytes32[] memory emptyBytes32Array = vm.parseJsonBytes32Array(json, ".empty"); + string[] memory emptyStringArray = vm.parseJsonStringArray(json, ".empty"); + int256[] memory emptyIntArray = vm.parseJsonIntArray(json, ".empty"); + uint256[] memory emptyUintArray = vm.parseJsonUintArray(json, ".empty"); + + assertEq(emptyAddressArray.length, 0); + assertEq(emptyBoolArray.length, 0); + assertEq(emptyBytesArray.length, 0); + assertEq(emptyBytes32Array.length, 0); + assertEq(emptyStringArray.length, 0); + assertEq(emptyIntArray.length, 0); + assertEq(emptyUintArray.length, 0); + + path = "fixtures/Toml/Issue4402.toml"; + string memory toml = vm.readFile(path); + + // Every one of these used to causes panic + emptyAddressArray = vm.parseTomlAddressArray(toml, ".empty"); + emptyBoolArray = vm.parseTomlBoolArray(toml, ".empty"); + emptyBytesArray = vm.parseTomlBytesArray(toml, ".empty"); + emptyBytes32Array = vm.parseTomlBytes32Array(toml, ".empty"); + emptyStringArray = vm.parseTomlStringArray(toml, ".empty"); + emptyIntArray = vm.parseTomlIntArray(toml, ".empty"); + emptyUintArray = vm.parseTomlUintArray(toml, ".empty"); + + assertEq(emptyAddressArray.length, 0); + assertEq(emptyBoolArray.length, 0); + assertEq(emptyBytesArray.length, 0); + assertEq(emptyBytes32Array.length, 0); + assertEq(emptyStringArray.length, 0); + assertEq(emptyIntArray.length, 0); + assertEq(emptyUintArray.length, 0); + } +} From 15c8d119d4af426e4c4369fd08a9676b676d767d Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 11 Mar 2024 16:22:53 +0400 Subject: [PATCH 052/430] feat(test): include data from fuzz/invariant runs in gas reports (#7324) feat(test): include data from fuzz/invariant runs when building gas reports --- crates/config/src/fuzz.rs | 3 + crates/config/src/invariant.rs | 3 + crates/evm/evm/src/executors/fuzz/mod.rs | 22 +++-- .../evm/evm/src/executors/invariant/error.rs | 3 + crates/evm/evm/src/executors/invariant/mod.rs | 17 ++++ crates/evm/fuzz/src/lib.rs | 4 + crates/forge/bin/cmd/test/mod.rs | 88 ++++++++++++++++++- crates/forge/src/gas_report.rs | 16 ++-- crates/forge/src/result.rs | 12 ++- crates/forge/src/runner.rs | 36 ++++---- crates/forge/tests/it/test_helpers.rs | 2 + 11 files changed, 173 insertions(+), 33 deletions(-) diff --git a/crates/config/src/fuzz.rs b/crates/config/src/fuzz.rs index 11d214670..13e8d34d3 100644 --- a/crates/config/src/fuzz.rs +++ b/crates/config/src/fuzz.rs @@ -22,6 +22,8 @@ pub struct FuzzConfig { /// The fuzz dictionary configuration #[serde(flatten)] pub dictionary: FuzzDictionaryConfig, + /// Number of runs to execute and include in the gas report. + pub gas_report_samples: u32, } impl Default for FuzzConfig { @@ -31,6 +33,7 @@ impl Default for FuzzConfig { max_test_rejects: 65536, seed: None, dictionary: FuzzDictionaryConfig::default(), + gas_report_samples: 256, } } } diff --git a/crates/config/src/invariant.rs b/crates/config/src/invariant.rs index 0eb96bdee..13c203d39 100644 --- a/crates/config/src/invariant.rs +++ b/crates/config/src/invariant.rs @@ -35,6 +35,8 @@ pub struct InvariantConfig { /// The maximum number of rejects via `vm.assume` which can be encountered during a single /// invariant run. pub max_assume_rejects: u32, + /// Number of runs to execute and include in the gas report. + pub gas_report_samples: u32, } impl Default for InvariantConfig { @@ -49,6 +51,7 @@ impl Default for InvariantConfig { shrink_run_limit: 2usize.pow(18_u32), preserve_state: false, max_assume_rejects: 65536, + gas_report_samples: 256, } } } diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index ab8989f30..ec980c01a 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -71,8 +71,11 @@ impl FuzzedExecutor { // Stores the result and calldata of the last failed call, if any. let counterexample: RefCell<(Bytes, RawCallResult)> = RefCell::default(); - // Stores the last successful call trace - let traces: RefCell> = RefCell::default(); + // We want to collect at least one trace which will be displayed to user. + let max_traces_to_collect = std::cmp::max(1, self.config.gas_report_samples) as usize; + + // Stores up to `max_traces_to_collect` traces. + let traces: RefCell> = RefCell::default(); // Stores coverage information for all fuzz cases let coverage: RefCell> = RefCell::default(); @@ -103,8 +106,12 @@ impl FuzzedExecutor { if first_case.is_none() { first_case.replace(case.case); } - - traces.replace(case.traces); + if let Some(call_traces) = case.traces { + if traces.borrow().len() == max_traces_to_collect { + traces.borrow_mut().pop(); + } + traces.borrow_mut().push(call_traces); + } if let Some(prev) = coverage.take() { // Safety: If `Option::or` evaluates to `Some`, then `call.coverage` must @@ -137,6 +144,10 @@ impl FuzzedExecutor { }); let (calldata, call) = counterexample.into_inner(); + + let mut traces = traces.into_inner(); + let last_run_traces = if run_result.is_ok() { traces.pop() } else { call.traces.clone() }; + let mut result = FuzzTestResult { first_case: first_case.take().unwrap_or_default(), gas_by_case: gas_by_case.take(), @@ -146,7 +157,8 @@ impl FuzzedExecutor { decoded_logs: decode_console_logs(&call.logs), logs: call.logs, labeled_addresses: call.labels, - traces: if run_result.is_ok() { traces.into_inner() } else { call.traces.clone() }, + traces: last_run_traces, + gas_report_traces: traces, coverage: coverage.into_inner(), }; diff --git a/crates/evm/evm/src/executors/invariant/error.rs b/crates/evm/evm/src/executors/invariant/error.rs index 8acc67f6a..da29e657e 100644 --- a/crates/evm/evm/src/executors/invariant/error.rs +++ b/crates/evm/evm/src/executors/invariant/error.rs @@ -50,6 +50,9 @@ pub struct InvariantFuzzTestResult { /// The entire inputs of the last run of the invariant campaign, used for /// replaying the run for collecting traces. pub last_run_inputs: Vec, + + /// Additional traces used for gas report construction. + pub gas_report_traces: Vec>, } #[derive(Clone, Debug)] diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 0a7b81943..591fa7cf4 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -23,6 +23,7 @@ use foundry_evm_fuzz::{ }, FuzzCase, FuzzedCases, }; +use foundry_evm_traces::CallTraceArena; use parking_lot::{Mutex, RwLock}; use proptest::{ strategy::{BoxedStrategy, Strategy, ValueTree}, @@ -123,6 +124,9 @@ impl<'a> InvariantExecutor<'a> { // Stores the calldata in the last run. let last_run_calldata: RefCell> = RefCell::new(vec![]); + // Stores additional traces for gas report. + let gas_report_traces: RefCell>> = RefCell::default(); + // Let's make sure the invariant is sound before actually starting the run: // We'll assert the invariant in its initial state, and if it fails, we'll // already know if we can early exit the invariant run. @@ -162,6 +166,9 @@ impl<'a> InvariantExecutor<'a> { // Created contracts during a run. let mut created_contracts = vec![]; + // Traces of each call of the sequence. + let mut run_traces = Vec::new(); + let mut current_run = 0; let mut assume_rejects_counter = 0; @@ -233,6 +240,7 @@ impl<'a> InvariantExecutor<'a> { self.config.fail_on_revert, self.config.shrink_sequence, self.config.shrink_run_limit, + &mut run_traces, ); if !can_continue || current_run == self.config.depth - 1 { @@ -265,6 +273,9 @@ impl<'a> InvariantExecutor<'a> { } } + if gas_report_traces.borrow().len() < self.config.gas_report_samples as usize { + gas_report_traces.borrow_mut().push(run_traces); + } fuzz_cases.borrow_mut().push(FuzzedCases::new(fuzz_runs)); Ok(()) @@ -280,6 +291,7 @@ impl<'a> InvariantExecutor<'a> { cases: fuzz_cases.into_inner(), reverts, last_run_inputs: last_run_calldata.take(), + gas_report_traces: gas_report_traces.into_inner(), }) } @@ -764,6 +776,7 @@ fn can_continue( fail_on_revert: bool, shrink_sequence: bool, shrink_run_limit: usize, + run_traces: &mut Vec, ) -> RichInvariantResults { let mut call_results = None; @@ -775,6 +788,10 @@ fn can_continue( // Assert invariants IFF the call did not revert and the handlers did not fail. if !call_result.reverted && !handlers_failed { + if let Some(traces) = call_result.traces { + run_traces.push(traces); + } + call_results = assert_invariants( invariant_contract, executor, diff --git a/crates/evm/fuzz/src/lib.rs b/crates/evm/fuzz/src/lib.rs index ed3b50178..f8928ea3e 100644 --- a/crates/evm/fuzz/src/lib.rs +++ b/crates/evm/fuzz/src/lib.rs @@ -150,6 +150,10 @@ pub struct FuzzTestResult { /// `num(fuzz_cases)` traces, one for each run, which is neither helpful nor performant. pub traces: Option, + /// Additional traces used for gas report construction. + /// Those traces should not be displayed. + pub gas_report_traces: Vec, + /// Raw coverage info pub coverage: Option, } diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 3e7d14848..1cea3b818 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -143,6 +143,10 @@ impl TestArgs { // Explicitly enable isolation for gas reports for more correct gas accounting if self.gas_report { evm_opts.isolate = true; + } else { + // Do not collect gas report traces if gas report is not enabled. + config.fuzz.gas_report_samples = 0; + config.invariant.gas_report_samples = 0; } // Set up the project. @@ -409,7 +413,26 @@ impl TestArgs { } if let Some(gas_report) = &mut gas_report { - gas_report.analyze(&result.traces, &decoder).await; + gas_report + .analyze(result.traces.iter().map(|(_, arena)| arena), &decoder) + .await; + + for trace in result.gas_report_traces.iter() { + decoder.clear_addresses(); + + // Re-execute setup and deployment traces to collect identities created in + // setUp and constructor. + for (kind, arena) in &result.traces { + if !matches!(kind, TraceKind::Execution) { + decoder.identify(arena, &mut local_identifier); + } + } + + for arena in trace { + decoder.identify(arena, &mut local_identifier); + gas_report.analyze([arena], &decoder).await; + } + } } } @@ -431,7 +454,9 @@ impl TestArgs { outcome.decoder = Some(decoder); if let Some(gas_report) = gas_report { - shell::println(gas_report.finalize())?; + let finalized = gas_report.finalize(); + shell::println(&finalized)?; + outcome.gas_report = Some(finalized); } if !outcome.results.is_empty() { @@ -528,6 +553,7 @@ fn list( mod tests { use super::*; use foundry_config::Chain; + use foundry_test_utils::forgetest_async; #[test] fn watch_parse() { @@ -561,4 +587,62 @@ mod tests { test("--chain-id=1", Chain::mainnet()); test("--chain-id=42", Chain::from_id(42)); } + + forgetest_async!(gas_report_fuzz_invariant, |prj, _cmd| { + prj.insert_ds_test(); + prj.add_source( + "Contracts.sol", + r#" +//SPDX-license-identifier: MIT + +import "./test.sol"; + +contract Foo { + function foo() public {} +} + +contract Bar { + function bar() public {} +} + + +contract FooBarTest is DSTest { + Foo public targetContract; + + function setUp() public { + targetContract = new Foo(); + } + + function invariant_dummy() public { + assertTrue(true); + } + + function testFuzz_bar(uint256 _val) public { + (new Bar()).bar(); + } +} + "#, + ) + .unwrap(); + + let args = TestArgs::parse_from([ + "foundry-cli", + "--gas-report", + "--root", + &prj.root().to_string_lossy(), + "--silent", + ]); + + let outcome = args.run().await.unwrap(); + let gas_report = outcome.gas_report.unwrap(); + + assert_eq!(gas_report.contracts.len(), 3); + let call_cnts = gas_report + .contracts + .values() + .flat_map(|c| c.functions.values().flat_map(|f| f.values().map(|v| v.calls.len()))) + .collect::>(); + // assert that all functions were called at least 100 times + assert!(call_cnts.iter().all(|c| *c > 100)); + }); } diff --git a/crates/forge/src/gas_report.rs b/crates/forge/src/gas_report.rs index f6c269d70..de36b871c 100644 --- a/crates/forge/src/gas_report.rs +++ b/crates/forge/src/gas_report.rs @@ -3,7 +3,7 @@ use crate::{ constants::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS}, hashbrown::HashSet, - traces::{CallTraceArena, CallTraceDecoder, CallTraceNode, DecodedCallData, TraceKind}, + traces::{CallTraceArena, CallTraceDecoder, CallTraceNode, DecodedCallData}, }; use comfy_table::{presets::ASCII_MARKDOWN, *}; use foundry_common::{calc, TestFunctionExt}; @@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize}; use std::{collections::BTreeMap, fmt::Display}; /// Represents the gas report for a set of contracts. -#[derive(Debug, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct GasReport { /// Whether to report any contracts. report_any: bool, @@ -22,7 +22,7 @@ pub struct GasReport { ignore: HashSet, /// All contracts that were analyzed grouped by their identifier /// ``test/Counter.t.sol:CounterTest - contracts: BTreeMap, + pub contracts: BTreeMap, } impl GasReport { @@ -61,10 +61,10 @@ impl GasReport { /// Analyzes the given traces and generates a gas report. pub async fn analyze( &mut self, - traces: &[(TraceKind, CallTraceArena)], + arenas: impl IntoIterator, decoder: &CallTraceDecoder, ) { - for node in traces.iter().flat_map(|(_, arena)| arena.nodes()) { + for node in arenas.into_iter().flat_map(|arena| arena.nodes()) { self.analyze_node(node, decoder).await; } } @@ -78,7 +78,7 @@ impl GasReport { // Only include top-level calls which accout for calldata and base (21.000) cost. // Only include Calls and Creates as only these calls are isolated in inspector. - if trace.depth != 1 && + if trace.depth > 1 && (trace.kind == CallKind::Call || trace.kind == CallKind::Create || trace.kind == CallKind::Create2) @@ -186,7 +186,7 @@ impl Display for GasReport { } } -#[derive(Debug, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct ContractInfo { pub gas: u64, pub size: usize, @@ -194,7 +194,7 @@ pub struct ContractInfo { pub functions: BTreeMap>, } -#[derive(Debug, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct GasInfo { pub calls: Vec, pub min: u64, diff --git a/crates/forge/src/result.rs b/crates/forge/src/result.rs index 1605f72fe..94c6d58fc 100644 --- a/crates/forge/src/result.rs +++ b/crates/forge/src/result.rs @@ -7,7 +7,7 @@ use foundry_evm::{ debug::DebugArena, executors::EvmError, fuzz::{CounterExample, FuzzCase}, - traces::{CallTraceDecoder, TraceKind, Traces}, + traces::{CallTraceArena, CallTraceDecoder, TraceKind, Traces}, }; use serde::{Deserialize, Serialize}; use std::{ @@ -17,6 +17,8 @@ use std::{ }; use yansi::Paint; +use crate::gas_report::GasReport; + /// The aggregated result of a test run. #[derive(Clone, Debug)] pub struct TestOutcome { @@ -32,12 +34,14 @@ pub struct TestOutcome { /// /// Note that `Address` fields only contain the last executed test case's data. pub decoder: Option, + /// The gas report, if requested. + pub gas_report: Option, } impl TestOutcome { /// Creates a new test outcome with the given results. pub fn new(results: BTreeMap, allow_failure: bool) -> Self { - Self { results, allow_failure, decoder: None } + Self { results, allow_failure, decoder: None, gas_report: None } } /// Creates a new empty test outcome. @@ -358,6 +362,10 @@ pub struct TestResult { #[serde(skip)] pub traces: Traces, + /// Additional traces to use for gas report. + #[serde(skip)] + pub gas_report_traces: Vec>, + /// Raw coverage info #[serde(skip)] pub coverage: Option, diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 29507de8d..0164cd7df 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -439,6 +439,7 @@ impl<'a> ContractRunner<'a> { debug: debug_arena, breakpoints, duration, + gas_report_traces: Vec::new(), } } @@ -491,23 +492,24 @@ impl<'a> ContractRunner<'a> { let invariant_contract = InvariantContract { address, invariant_function: func, abi: self.contract }; - let InvariantFuzzTestResult { error, cases, reverts, last_run_inputs } = match evm - .invariant_fuzz(invariant_contract.clone()) - { - Ok(x) => x, - Err(e) => { - return TestResult { - status: TestStatus::Failure, - reason: Some(format!("failed to set up invariant testing environment: {e}")), - decoded_logs: decode_console_logs(&logs), - traces, - labeled_addresses, - kind: TestKind::Invariant { runs: 0, calls: 0, reverts: 0 }, - duration: start.elapsed(), - ..Default::default() + let InvariantFuzzTestResult { error, cases, reverts, last_run_inputs, gas_report_traces } = + match evm.invariant_fuzz(invariant_contract.clone()) { + Ok(x) => x, + Err(e) => { + return TestResult { + status: TestStatus::Failure, + reason: Some(format!( + "failed to set up invariant testing environment: {e}" + )), + decoded_logs: decode_console_logs(&logs), + traces, + labeled_addresses, + kind: TestKind::Invariant { runs: 0, calls: 0, reverts: 0 }, + duration: start.elapsed(), + ..Default::default() + } } - } - }; + }; let mut counterexample = None; let mut logs = logs.clone(); @@ -571,6 +573,7 @@ impl<'a> ContractRunner<'a> { traces, labeled_addresses: labeled_addresses.clone(), duration: start.elapsed(), + gas_report_traces, ..Default::default() // TODO collect debug traces on the last run or error } } @@ -698,6 +701,7 @@ impl<'a> ContractRunner<'a> { debug, breakpoints, duration, + gas_report_traces: result.gas_report_traces.into_iter().map(|t| vec![t]).collect(), } } } diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index 609181f1a..e615e2785 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -94,6 +94,7 @@ pub static TEST_OPTS: Lazy = Lazy::new(|| { max_fuzz_dictionary_values: 10_000, max_calldata_fuzz_dictionary_addresses: 0, }, + gas_report_samples: 256, }) .invariant(InvariantConfig { runs: 256, @@ -112,6 +113,7 @@ pub static TEST_OPTS: Lazy = Lazy::new(|| { shrink_run_limit: 2usize.pow(18u32), preserve_state: false, max_assume_rejects: 65536, + gas_report_samples: 256, }) .build(&COMPILED, &PROJECT.paths.root) .expect("Config loaded") From e74d6324f74ba7dcdca61364d56bc3241dcd514c Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 11 Mar 2024 19:09:40 +0400 Subject: [PATCH 053/430] fix: ci (#7363) * fix: ci * fmt --- crates/forge/bin/cmd/test/mod.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 1cea3b818..11ae1aee7 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -299,9 +299,12 @@ impl TestArgs { // Set up trace identifiers. let known_contracts = runner.known_contracts.clone(); let remote_chain_id = runner.evm_opts.get_remote_chain_id(); - let mut identifier = TraceIdentifiers::new() - .with_local(&known_contracts) - .with_etherscan(&config, remote_chain_id)?; + let mut identifier = TraceIdentifiers::new().with_local(&known_contracts); + + // Avoid using etherscan for gas report as we decode more traces and this will be expensive. + if !self.gas_report { + identifier = identifier.with_etherscan(&config, remote_chain_id)?; + } // Run tests. let (tx, rx) = channel::<(String, SuiteResult)>(); @@ -424,12 +427,12 @@ impl TestArgs { // setUp and constructor. for (kind, arena) in &result.traces { if !matches!(kind, TraceKind::Execution) { - decoder.identify(arena, &mut local_identifier); + decoder.identify(arena, &mut identifier); } } for arena in trace { - decoder.identify(arena, &mut local_identifier); + decoder.identify(arena, &mut identifier); gas_report.analyze([arena], &decoder).await; } } From ef81e23259f32b4e221f1a97c4a644dc9ed9950f Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 11 Mar 2024 19:23:23 +0400 Subject: [PATCH 054/430] fix: disable cache if build-info is requested (#7358) --- crates/config/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index dd0bf9807..000778455 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -678,7 +678,7 @@ impl Config { }) .set_auto_detect(self.is_auto_detect()) .set_offline(self.offline) - .set_cached(cached) + .set_cached(cached && !self.build_info) .set_build_info(!no_artifacts && self.build_info) .set_no_artifacts(no_artifacts) .build()?; From 7545c7a2857a873fa1909ec4174032c4e4702116 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 11 Mar 2024 19:11:02 +0100 Subject: [PATCH 055/430] chore(deps): bump all dependencies, revm 7 (#7365) * chore(deps): bump revm to 7.1 * chore: general cargo update ```text Updating crates.io index Updating git repository `https://github.com/alloy-rs/alloy` Updating git repository `https://github.com/paradigmxyz/evm-inspectors` Updating ahash v0.8.10 -> v0.8.11 Updating bumpalo v3.15.3 -> v3.15.4 Updating cc v1.0.88 -> v1.0.90 Adding cfg_aliases v0.1.1 (latest: v0.2.0) Updating chrono v0.4.34 -> v0.4.35 Updating clap v4.5.1 -> v4.5.2 Updating clap_builder v4.5.1 -> v4.5.2 Updating const-hex v1.11.1 -> v1.11.2 Updating ctrlc v3.4.2 -> v3.4.4 Updating enr v0.10.0 -> v0.10.1 Updating env_logger v0.11.2 -> v0.11.3 Updating foundry-compilers v0.3.9 -> v0.3.10 Updating http v0.2.11 -> v0.2.12 (latest: v1.1.0) Adding jobserver v0.1.28 Updating js-sys v0.3.68 -> v0.3.69 Updating nix v0.27.1 -> v0.28.0 Updating pin-project v1.1.4 -> v1.1.5 Updating pin-project-internal v1.1.4 -> v1.1.5 Adding proc-macro-crate v3.1.0 Updating regex-automata v0.4.5 -> v0.4.6 Updating reqwest v0.11.24 -> v0.11.25 Updating serde_path_to_error v0.1.15 -> v0.1.16 Updating strum v0.26.1 -> v0.26.2 Updating strum_macros v0.26.1 -> v0.26.2 Updating system-configuration v0.5.1 -> v0.6.0 Updating system-configuration-sys v0.5.0 -> v0.6.0 Updating wasm-bindgen v0.2.91 -> v0.2.92 Updating wasm-bindgen-backend v0.2.91 -> v0.2.92 Updating wasm-bindgen-futures v0.4.41 -> v0.4.42 Updating wasm-bindgen-macro v0.2.91 -> v0.2.92 Updating wasm-bindgen-macro-support v0.2.91 -> v0.2.92 Updating wasm-bindgen-shared v0.2.91 -> v0.2.92 Updating web-sys v0.3.68 -> v0.3.69 Updating yansi v1.0.0-rc.1 -> v1.0.0 ``` * chore: chrono deprecation * chore: no default features --- .gitignore | 1 + Cargo.lock | 279 +++++++++++++++--------- Cargo.toml | 43 ++-- crates/anvil/src/config.rs | 2 +- crates/anvil/src/eth/backend/fork.rs | 2 +- crates/anvil/src/eth/backend/mem/mod.rs | 6 +- crates/anvil/src/eth/backend/time.rs | 7 +- crates/anvil/src/pubsub.rs | 15 +- crates/anvil/tests/it/api.rs | 2 +- crates/anvil/tests/it/fork.rs | 2 +- crates/anvil/tests/it/genesis.rs | 2 +- crates/anvil/tests/it/wsapi.rs | 2 +- crates/cast/bin/cmd/run.rs | 2 +- crates/cast/src/lib.rs | 5 +- crates/cheatcodes/src/evm.rs | 4 +- crates/cheatcodes/src/evm/fork.rs | 2 +- crates/cheatcodes/src/inspector.rs | 34 ++- crates/cheatcodes/src/lib.rs | 46 ++-- crates/common/src/provider/alloy.rs | 12 +- crates/evm/core/Cargo.toml | 1 - crates/evm/core/src/backend/cow.rs | 2 +- crates/evm/core/src/backend/mod.rs | 2 +- crates/evm/core/src/fork/backend.rs | 2 +- crates/evm/core/src/fork/init.rs | 2 +- crates/evm/core/src/fork/multi.rs | 6 +- crates/evm/core/src/opts.rs | 2 +- crates/evm/evm/src/inspectors/stack.rs | 5 +- 27 files changed, 291 insertions(+), 199 deletions(-) diff --git a/.gitignore b/.gitignore index 19f666e45..14e811f2d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ out/ out.json .idea .vscode +bloat* diff --git a/Cargo.lock b/Cargo.lock index a23a5d778..113f04de8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -51,9 +51,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b79b82693f705137f8fb9b37871d99e4f9a7df12b917eed79c3d3954830a60b" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "once_cell", @@ -84,13 +84,13 @@ checksum = "e96c81b05c893348760f232c4cc6a6a77fd91cfb09885d4eaad25cd03bd7732e" dependencies = [ "num_enum", "serde", - "strum 0.26.1", + "strum 0.26.2", ] [[package]] name = "alloy-consensus" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" +source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" dependencies = [ "alloy-eips", "alloy-network", @@ -122,7 +122,7 @@ dependencies = [ [[package]] name = "alloy-eips" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" +source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -133,7 +133,7 @@ dependencies = [ [[package]] name = "alloy-genesis" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" +source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -155,7 +155,7 @@ dependencies = [ [[package]] name = "alloy-json-rpc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" +source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" dependencies = [ "alloy-primitives", "serde", @@ -166,7 +166,7 @@ dependencies = [ [[package]] name = "alloy-network" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" +source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" dependencies = [ "alloy-eips", "alloy-json-rpc", @@ -205,7 +205,7 @@ dependencies = [ [[package]] name = "alloy-providers" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" +source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" dependencies = [ "alloy-network", "alloy-primitives", @@ -214,17 +214,22 @@ dependencies = [ "alloy-rpc-types", "alloy-transport", "alloy-transport-http", + "async-stream", "async-trait", "auto_impl", + "futures", + "lru", "reqwest", "serde", "thiserror", + "tokio", + "tracing", ] [[package]] name = "alloy-pubsub" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" +source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -234,6 +239,7 @@ dependencies = [ "serde", "serde_json", "tokio", + "tokio-stream", "tower", "tracing", ] @@ -263,7 +269,7 @@ dependencies = [ [[package]] name = "alloy-rpc-client" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" +source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -271,7 +277,10 @@ dependencies = [ "futures", "pin-project", "reqwest", + "serde", "serde_json", + "tokio", + "tokio-stream", "tower", "tracing", "url", @@ -280,7 +289,7 @@ dependencies = [ [[package]] name = "alloy-rpc-trace-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" +source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -291,7 +300,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" +source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -304,7 +313,7 @@ dependencies = [ [[package]] name = "alloy-signer" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" +source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" dependencies = [ "alloy-network", "alloy-primitives", @@ -365,10 +374,10 @@ dependencies = [ [[package]] name = "alloy-transport" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" +source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" dependencies = [ "alloy-json-rpc", - "base64 0.21.7", + "base64 0.22.0", "futures-util", "serde", "serde_json", @@ -382,7 +391,7 @@ dependencies = [ [[package]] name = "alloy-transport-http" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" +source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -395,7 +404,7 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" +source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -413,7 +422,7 @@ dependencies = [ [[package]] name = "alloy-transport-ws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" +source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -835,6 +844,28 @@ dependencies = [ "syn 2.0.52", ] +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + [[package]] name = "async-task" version = "4.7.0" @@ -996,6 +1027,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" + [[package]] name = "base64ct" version = "1.6.0" @@ -1121,7 +1158,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" dependencies = [ "memchr", - "regex-automata 0.4.5", + "regex-automata 0.4.6", "serde", ] @@ -1142,9 +1179,9 @@ checksum = "b4ae4235e6dac0694637c763029ecea1a2ec9e4e06ec2729bd21ba4d9c863eb7" [[package]] name = "bumpalo" -version = "3.15.3" +version = "3.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" [[package]] name = "byte-slice-cast" @@ -1310,10 +1347,11 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.88" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" dependencies = [ + "jobserver", "libc", ] @@ -1323,6 +1361,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + [[package]] name = "chisel" version = "0.2.0" @@ -1352,7 +1396,7 @@ dependencies = [ "serde_json", "serial_test", "solang-parser", - "strum 0.26.1", + "strum 0.26.2", "time", "tokio", "tracing", @@ -1363,9 +1407,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.34" +version = "0.4.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" +checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" dependencies = [ "android-tzdata", "iana-time-zone", @@ -1413,9 +1457,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.1" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" +checksum = "b230ab84b0ffdf890d5a10abdbc8b83ae1c4918275daea1ab8801f71536b2651" dependencies = [ "clap_builder", "clap_derive", @@ -1423,9 +1467,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.1" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstream", "anstyle", @@ -1653,9 +1697,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.11.1" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbd12d49ab0eaf8193ba9175e45f56bbc2e4b27d57b8cfe62aa47942a46b9a9" +checksum = "b37dae8c8ded08d5ec72caa1b4204a5344047cd4a2c7387e3d150020abfbc1c9" dependencies = [ "cfg-if", "cpufeatures", @@ -1886,11 +1930,11 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.4.2" +version = "3.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b467862cc8610ca6fc9a1532d7777cee0804e678ab45410897b9396495994a0b" +checksum = "672465ae37dc1bc6380a6547a8883d5dd397b0f1faaad4f265726cc7042a5345" dependencies = [ - "nix 0.27.1", + "nix 0.28.0", "windows-sys 0.52.0", ] @@ -2236,17 +2280,17 @@ checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" [[package]] name = "enr" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a3d8dc56e02f954cac8eb489772c552c473346fc34f67412bb6244fd647f7e4" +checksum = "2dc3eabaca59dc39ea5ed15062e4abc5bba9723b1cff7a4fea3faae0647f04c0" dependencies = [ + "alloy-rlp", "base64 0.21.7", "bytes", "hex", "k256", "log", "rand 0.8.5", - "rlp", "serde", "sha3", "zeroize", @@ -2275,9 +2319,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c012a26a7f605efc424dd53697843a72be7dc86ad2d01f7814337794a12231d" +checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" dependencies = [ "anstream", "anstyle", @@ -2515,7 +2559,7 @@ dependencies = [ "rlp", "serde", "serde_json", - "strum 0.26.1", + "strum 0.26.2", "syn 2.0.52", "tempfile", "thiserror", @@ -2958,7 +3002,7 @@ dependencies = [ "serde_json", "similar", "solang-parser", - "strum 0.26.1", + "strum 0.26.2", "svm-rs", "tempfile", "thiserror", @@ -3179,7 +3223,7 @@ dependencies = [ "regex", "serde", "strsim 0.10.0", - "strum 0.26.1", + "strum 0.26.2", "tempfile", "tokio", "tracing", @@ -3241,9 +3285,9 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1b77c95e79bff02ddaa38426fc6809a3a438dce0e6a2eb212dac97da7c157b4" +checksum = "7f5a6dffd81de844c96fea0aaf825cd2a9760c4a768ae3f5786d431ef3c57d69" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3367,7 +3411,6 @@ dependencies = [ "alloy-providers", "alloy-rpc-types", "alloy-sol-types", - "alloy-transport", "auto_impl", "const-hex", "derive_more", @@ -3968,7 +4011,7 @@ dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata 0.4.5", + "regex-automata 0.4.6", "regex-syntax 0.8.2", ] @@ -4068,7 +4111,7 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ - "ahash 0.8.10", + "ahash 0.8.11", "allocator-api2", "serde", ] @@ -4165,9 +4208,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", @@ -4324,7 +4367,7 @@ dependencies = [ "globset", "log", "memchr", - "regex-automata 0.4.5", + "regex-automata 0.4.6", "same-file", "walkdir", "winapi-util", @@ -4542,11 +4585,20 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +[[package]] +name = "jobserver" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -4667,7 +4719,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" dependencies = [ - "regex-automata 0.4.5", + "regex-automata 0.4.6", ] [[package]] @@ -4993,12 +5045,13 @@ dependencies = [ [[package]] name = "nix" -version = "0.27.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ "bitflags 2.4.2", "cfg-if", + "cfg_aliases", "libc", ] @@ -5162,7 +5215,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ - "proc-macro-crate 2.0.0", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", "syn 2.0.52", @@ -5467,7 +5520,7 @@ checksum = "4ccca0f6c17acc81df8e242ed473ec144cbf5c98037e69aa6d144780aad103c8" dependencies = [ "inlinable_string", "pear_codegen", - "yansi 1.0.0-rc.1", + "yansi 1.0.0", ] [[package]] @@ -5663,18 +5716,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", @@ -5834,6 +5887,15 @@ dependencies = [ "toml_edit 0.20.7", ] +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit 0.21.1", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -5877,7 +5939,7 @@ dependencies = [ "quote", "syn 2.0.52", "version_check", - "yansi 1.0.0-rc.1", + "yansi 1.0.0", ] [[package]] @@ -6151,7 +6213,7 @@ checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.5", + "regex-automata 0.4.6", "regex-syntax 0.8.2", ] @@ -6166,9 +6228,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", @@ -6189,9 +6251,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.24" +version = "0.11.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" +checksum = "0eea5a9eb898d3783f17c6407670e3592fd174cb81a10e51d4c37f49450b9946" dependencies = [ "base64 0.21.7", "bytes", @@ -6234,12 +6296,13 @@ dependencies = [ [[package]] name = "revm" -version = "6.1.0" +version = "7.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d35316fc02d99e42831356c71e882f5d385c77b78f64a44ae82f2f9a4b8b72f" +checksum = "217d21144d329f21d5245b8e6a46e0d6d0a527d9917d7a087f225b161e529169" dependencies = [ "auto_impl", "cfg-if", + "dyn-clone", "revm-interpreter", "revm-precompile", "serde", @@ -6249,7 +6312,7 @@ dependencies = [ [[package]] name = "revm-inspectors" version = "0.1.0" -source = "git+https://github.com/paradigmxyz/evm-inspectors?rev=5d560be#5d560be4cf022912f4f53f4e5ea71f81253b3c3d" +source = "git+https://github.com/paradigmxyz/evm-inspectors?rev=ba0b6ab#ba0b6ab695802c752601f17f5c941b62a067ad64" dependencies = [ "alloy-primitives", "alloy-rpc-trace-types", @@ -6259,13 +6322,15 @@ dependencies = [ "colorchoice", "revm", "serde", + "serde_json", + "thiserror", ] [[package]] name = "revm-interpreter" -version = "3.1.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fa10c2dc1e8f4934bdc763a2c09371bcec29e50c22e55e3eb325ee0cba09064" +checksum = "776848391ed76d5103ca1aa1632cd21b521e2870afb30b63723da862d69efd0f" dependencies = [ "revm-primitives", "serde", @@ -6273,9 +6338,9 @@ dependencies = [ [[package]] name = "revm-precompile" -version = "4.1.0" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db828d49d329560a70809d9d1fa0c74695edb49f50c5332db3eb24483076deac" +checksum = "e3fd1856a7cb09197a02669d779e1afb5a627b0888a24814ba2b6a1ad4c3ff8d" dependencies = [ "aurora-engine-modexp", "c-kzg", @@ -6290,9 +6355,9 @@ dependencies = [ [[package]] name = "revm-primitives" -version = "2.1.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fecd125aad58e135e2ca5771ed6e4e7b1f05fa3a64e0dfb9cc643b7a800a8435" +checksum = "2a4d7d3e793e907dc0797a9d3b43abfdf5226d133855214db9bd27d4cee33ebd" dependencies = [ "alloy-primitives", "auto_impl", @@ -6300,6 +6365,7 @@ dependencies = [ "bitvec", "c-kzg", "cfg-if", + "dyn-clone", "enumn", "hashbrown 0.14.3", "hex", @@ -6909,9 +6975,9 @@ dependencies = [ [[package]] name = "serde_path_to_error" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd154a240de39fdebcf5775d2675c204d7c13cf39a4c697be6493c8e734337c" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" dependencies = [ "itoa", "serde", @@ -7261,11 +7327,11 @@ dependencies = [ [[package]] name = "strum" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "723b93e8addf9aa965ebe2d11da6d7540fa2283fcea14b3371ff055f7ba13f5f" +checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" dependencies = [ - "strum_macros 0.26.1", + "strum_macros 0.26.2", ] [[package]] @@ -7283,9 +7349,9 @@ dependencies = [ [[package]] name = "strum_macros" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a3417fc93d76740d974a01654a09777cb500428cc874ca9f45edfe0c4d4cd18" +checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" dependencies = [ "heck", "proc-macro2", @@ -7413,20 +7479,20 @@ dependencies = [ [[package]] name = "system-configuration" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "658bc6ee10a9b4fcf576e9b0819d95ec16f4d2c02d39fd83ac1c8789785c4a42" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.2", "core-foundation", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" dependencies = [ "core-foundation-sys", "libc", @@ -7677,6 +7743,7 @@ dependencies = [ "futures-core", "pin-project-lite", "tokio", + "tokio-util", ] [[package]] @@ -8213,9 +8280,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -8223,9 +8290,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", @@ -8238,9 +8305,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", @@ -8250,9 +8317,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -8260,9 +8327,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", @@ -8273,9 +8340,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "watchexec" @@ -8327,9 +8394,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", @@ -8680,9 +8747,9 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "yansi" -version = "1.0.0-rc.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1367295b8f788d371ce2dbc842c7b709c73ee1364d30351dd300ec2203b12377" +checksum = "6c2861d76f58ec8fc95708b9b1e417f7b12fd72ad33c01fa6886707092dea0d3" [[package]] name = "zerocopy" diff --git a/Cargo.toml b/Cargo.toml index f14e8d891..f56a5cfd7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -142,9 +142,9 @@ foundry-compilers = { version = "0.3.9", default-features = false } ## revm # no default features to avoid c-kzg -revm = { version = "6.1", default-features = false } -revm-primitives = { version = "2", default-features = false } -revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "5d560be", features = [ +revm = { version = "7.1", default-features = false } +revm-primitives = { version = "3", default-features = false } +revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "ba0b6ab", features = [ "serde", ] } @@ -159,22 +159,22 @@ ethers-middleware = { version = "2.0.14", default-features = false } ethers-solc = { version = "2.0.14", default-features = false } ## alloy -alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } -alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } -alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } -alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } -alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } -alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } -alloy-providers = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } -alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } -alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } -alloy-rpc-trace-types = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } -alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } -alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } -alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } -alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } -alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } -alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } +alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } +alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } +alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } +alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } +alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } +alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } +alloy-providers = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } +alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } +alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } +alloy-rpc-trace-types = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } +alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } +alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } +alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } +alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } +alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } +alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } alloy-primitives = { version = "0.6.3", features = ["getrandom"] } alloy-dyn-abi = "0.6.3" alloy-json-abi = "0.6.3" @@ -212,3 +212,8 @@ hyper = "0.14" tower = "0.4" tower-http = "0.4" +# [patch.crates-io] +# revm = { path = "../../danipopes/revm/crates/revm" } +# revm-interpreter = { path = "../../danipopes/revm/crates/interpreter" } +# revm-primitives = { path = "../../danipopes/revm/crates/primitives" } +# revm-precompile = { path = "../../danipopes/revm/crates/precompile" } diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index be18d2827..5c61c0824 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -17,7 +17,7 @@ use crate::{ }; use alloy_genesis::Genesis; use alloy_primitives::{hex, utils::Unit, U256}; -use alloy_providers::provider::TempProvider; +use alloy_providers::tmp::TempProvider; use alloy_rpc_types::BlockNumberOrTag; use alloy_signer::{ coins_bip39::{English, Mnemonic}, diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index 71dadf40e..04ea0c70f 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -2,7 +2,7 @@ use crate::eth::{backend::db::Db, error::BlockchainError}; use alloy_primitives::{Address, Bytes, StorageValue, B256, U256, U64}; -use alloy_providers::provider::TempProvider; +use alloy_providers::tmp::TempProvider; use alloy_rpc_trace_types::{ geth::{GethDebugTracingOptions, GethTrace}, parity::LocalizedTransactionTrace as Trace, diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 0eba1474d..2b0bf292f 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -1257,14 +1257,14 @@ impl Backend { }; let mut is_match: bool = true; if !filter.address.is_empty() && filter.has_topics() { - if !params.filter_address(&log) || !params.filter_topics(&log) { + if !params.filter_address(&log.address) || !params.filter_topics(&log.topics) { is_match = false; } } else if !filter.address.is_empty() { - if !params.filter_address(&log) { + if !params.filter_address(&log.address) { is_match = false; } - } else if filter.has_topics() && !params.filter_topics(&log) { + } else if filter.has_topics() && !params.filter_topics(&log.topics) { is_match = false; } diff --git a/crates/anvil/src/eth/backend/time.rs b/crates/anvil/src/eth/backend/time.rs index f51ff93fe..ce6900eb4 100644 --- a/crates/anvil/src/eth/backend/time.rs +++ b/crates/anvil/src/eth/backend/time.rs @@ -1,16 +1,13 @@ //! Manages the block time use crate::eth::error::BlockchainError; -use chrono::{DateTime, NaiveDateTime, Utc}; +use chrono::{DateTime, Utc}; use parking_lot::RwLock; use std::{sync::Arc, time::Duration}; /// Returns the `Utc` datetime for the given seconds since unix epoch pub fn utc_from_secs(secs: u64) -> DateTime { - DateTime::::from_naive_utc_and_offset( - NaiveDateTime::from_timestamp_opt(secs as i64, 0).unwrap(), - Utc, - ) + DateTime::from_timestamp(secs as i64, 0).unwrap() } /// Manages block time diff --git a/crates/anvil/src/pubsub.rs b/crates/anvil/src/pubsub.rs index ce2f7e245..b0b8b33bb 100644 --- a/crates/anvil/src/pubsub.rs +++ b/crates/anvil/src/pubsub.rs @@ -152,23 +152,12 @@ pub fn filter_logs( ) -> Vec { /// Determines whether to add this log fn add_log(block_hash: B256, l: &Log, block: &Block, params: &FilteredParams) -> bool { - let log = AlloyLog { - address: l.address, - topics: l.topics().to_vec(), - data: l.data.data.clone(), - block_hash: None, - block_number: None, - transaction_hash: None, - transaction_index: None, - log_index: None, - removed: false, - }; if params.filter.is_some() { let block_number = block.header.number; if !params.filter_block_range(block_number) || !params.filter_block_hash(block_hash) || - !params.filter_address(&log) || - !params.filter_topics(&log) + !params.filter_address(&l.address) || + !params.filter_topics(l.topics()) { return false; } diff --git a/crates/anvil/tests/it/api.rs b/crates/anvil/tests/it/api.rs index fb3c3742b..216e95813 100644 --- a/crates/anvil/tests/it/api.rs +++ b/crates/anvil/tests/it/api.rs @@ -5,7 +5,7 @@ use crate::{ utils::ethers_http_provider, }; use alloy_primitives::{Address as rAddress, B256, U256 as rU256}; -use alloy_providers::provider::TempProvider; +use alloy_providers::tmp::TempProvider; use alloy_rpc_types::{ request::{TransactionInput as CallInput, TransactionRequest as CallRequest}, state::{AccountOverride, StateOverride}, diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index 8399dc85a..a23cc7052 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -5,7 +5,7 @@ use crate::{ utils::{self, ethers_http_provider}, }; use alloy_primitives::{address, U256 as rU256}; -use alloy_providers::provider::TempProvider; +use alloy_providers::tmp::TempProvider; use alloy_rpc_types::{ request::{TransactionInput, TransactionRequest as CallRequest}, BlockNumberOrTag, diff --git a/crates/anvil/tests/it/genesis.rs b/crates/anvil/tests/it/genesis.rs index 9b822aff7..f5f5fec9c 100644 --- a/crates/anvil/tests/it/genesis.rs +++ b/crates/anvil/tests/it/genesis.rs @@ -4,7 +4,7 @@ use std::str::FromStr; use alloy_genesis::Genesis; use alloy_primitives::{Address, U256, U64}; -use alloy_providers::provider::TempProvider; +use alloy_providers::tmp::TempProvider; use anvil::{spawn, NodeConfig}; #[tokio::test(flavor = "multi_thread")] diff --git a/crates/anvil/tests/it/wsapi.rs b/crates/anvil/tests/it/wsapi.rs index 7d31bdbca..c2073f561 100644 --- a/crates/anvil/tests/it/wsapi.rs +++ b/crates/anvil/tests/it/wsapi.rs @@ -1,6 +1,6 @@ //! general eth api tests with websocket provider -use alloy_providers::provider::TempProvider; +use alloy_providers::tmp::TempProvider; use anvil::{spawn, NodeConfig}; use ethers::types::U256; use foundry_common::types::ToAlloy; diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index a9e074962..8de966906 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -1,5 +1,5 @@ use alloy_primitives::U256; -use alloy_providers::provider::TempProvider; +use alloy_providers::tmp::TempProvider; use alloy_rpc_types::BlockTransactions; use cast::revm::primitives::EnvWithHandlerCfg; use clap::Parser; diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index 8a0e34c94..38498786e 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -6,7 +6,7 @@ use alloy_primitives::{ }; use alloy_rlp::Decodable; use base::{Base, NumberWithBase, ToBase}; -use chrono::NaiveDateTime; +use chrono::DateTime; use ethers_core::{ types::{ transaction::eip2718::TypedTransaction, BlockId, BlockNumber, Filter, NameOrAddress, @@ -395,8 +395,7 @@ where pub async fn age>(&self, block: T) -> Result { let timestamp_str = Cast::block_field_as_num(self, block, String::from("timestamp")).await?.to_string(); - let datetime = - NaiveDateTime::from_timestamp_opt(timestamp_str.parse::().unwrap(), 0).unwrap(); + let datetime = DateTime::from_timestamp(timestamp_str.parse::().unwrap(), 0).unwrap(); Ok(datetime.format("%a %b %e %H:%M:%S %Y").to_string()) } diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index fd45200f0..601c97ad0 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -12,7 +12,7 @@ use foundry_evm_core::{ }; use revm::{ primitives::{Account, Bytecode, SpecId, KECCAK_EMPTY}, - EvmContext, + InnerEvmContext, }; use std::{collections::HashMap, path::Path}; @@ -521,7 +521,7 @@ fn read_callers(state: &Cheatcodes, default_sender: &Address) -> Result { /// Ensures the `Account` is loaded and touched. pub(super) fn journaled_account( - ecx: &mut EvmContext, + ecx: &mut InnerEvmContext, addr: Address, ) -> Result<&mut Account> { ecx.load_account(addr)?; diff --git a/crates/cheatcodes/src/evm/fork.rs b/crates/cheatcodes/src/evm/fork.rs index 4b3fdc6b3..f5402a0d3 100644 --- a/crates/cheatcodes/src/evm/fork.rs +++ b/crates/cheatcodes/src/evm/fork.rs @@ -1,6 +1,6 @@ use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Result, Vm::*}; use alloy_primitives::{B256, U256}; -use alloy_providers::provider::TempProvider; +use alloy_providers::tmp::TempProvider; use alloy_rpc_types::Filter; use alloy_sol_types::SolValue; use eyre::WrapErr; diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 3396fc876..9b26e7754 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -30,7 +30,7 @@ use revm::{ InstructionResult, Interpreter, InterpreterAction, InterpreterResult, }, primitives::{BlockEnv, CreateScheme, TransactTo}, - EvmContext, Inspector, + EvmContext, InnerEvmContext, Inspector, }; use serde_json::Value; use std::{ @@ -228,7 +228,15 @@ impl Cheatcodes { // but only if the backend is in forking mode ecx.db.ensure_cheatcode_access_forking_mode(&caller)?; - apply_dispatch(&decoded, &mut CheatsCtxt { state: self, ecx, caller }) + apply_dispatch( + &decoded, + &mut CheatsCtxt { + state: self, + ecx: &mut ecx.inner, + precompiles: &mut ecx.precompiles, + caller, + }, + ) } /// Determines the address of the contract and marks it as allowed @@ -238,7 +246,7 @@ impl Cheatcodes { /// automatically we need to determine the new address fn allow_cheatcodes_on_create( &self, - ecx: &mut EvmContext, + ecx: &mut InnerEvmContext, inputs: &CreateInputs, ) -> Address { let old_nonce = ecx @@ -302,6 +310,7 @@ impl Inspector for Cheatcodes { } fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext) { + let ecx = &mut ecx.inner; self.pc = interpreter.program_counter(); // reset gas if gas metering is turned off @@ -716,6 +725,8 @@ impl Inspector for Cheatcodes { }; } + let ecx = &mut ecx.inner; + if call.contract == HARDHAT_CONSOLE_ADDRESS { return None } @@ -929,6 +940,7 @@ impl Inspector for Cheatcodes { call: &CallInputs, mut outcome: CallOutcome, ) -> CallOutcome { + let ecx = &mut ecx.inner; let cheatcode_call = call.contract == CHEATCODE_ADDRESS || call.contract == HARDHAT_CONSOLE_ADDRESS; @@ -1198,6 +1210,7 @@ impl Inspector for Cheatcodes { ecx: &mut EvmContext, call: &mut CreateInputs, ) -> Option { + let ecx = &mut ecx.inner; let gas = Gas::new(call.gas_limit); // Apply our prank @@ -1338,6 +1351,8 @@ impl Inspector for Cheatcodes { _call: &CreateInputs, mut outcome: CreateOutcome, ) -> CreateOutcome { + let ecx = &mut ecx.inner; + // Clean up pranks if let Some(prank) = &self.prank { if ecx.journaled_state.depth() == prank.depth { @@ -1485,7 +1500,7 @@ fn disallowed_mem_write( /// Returns a `DatabaseError::MissingCreate2Deployer` if the `DEFAULT_CREATE2_DEPLOYER` account is /// not found or if it does not have any associated bytecode. fn apply_create2_deployer( - ecx: &mut EvmContext, + ecx: &mut InnerEvmContext, call: &mut CreateInputs, prank: Option<&Prank>, broadcast: Option<&Broadcast>, @@ -1555,7 +1570,7 @@ fn apply_create2_deployer( fn process_broadcast_create( broadcast_sender: Address, bytecode: Bytes, - ecx: &mut EvmContext, + ecx: &mut InnerEvmContext, call: &mut CreateInputs, ) -> (Bytes, Option
, u64) { call.caller = broadcast_sender; @@ -1581,13 +1596,16 @@ fn process_broadcast_create( // Determines if the gas limit on a given call was manually set in the script and should therefore // not be overwritten by later estimations -fn check_if_fixed_gas_limit(data: &EvmContext, call_gas_limit: u64) -> bool { +fn check_if_fixed_gas_limit( + ecx: &InnerEvmContext, + call_gas_limit: u64, +) -> bool { // If the gas limit was not set in the source code it is set to the estimated gas left at the // time of the call, which should be rather close to configured gas limit. // TODO: Find a way to reliably make this determination. // For example by generating it in the compilation or EVM simulation process - U256::from(data.env.tx.gas_limit) > data.env.block.gas_limit && - U256::from(call_gas_limit) <= data.env.block.gas_limit + U256::from(ecx.env.tx.gas_limit) > ecx.env.block.gas_limit && + U256::from(call_gas_limit) <= ecx.env.block.gas_limit // Transfers in forge scripts seem to be estimated at 2300 by revm leading to "Intrinsic // gas too low" failure when simulated on chain && call_gas_limit > 2300 diff --git a/crates/cheatcodes/src/lib.rs b/crates/cheatcodes/src/lib.rs index a9dda80f9..2fa866b1a 100644 --- a/crates/cheatcodes/src/lib.rs +++ b/crates/cheatcodes/src/lib.rs @@ -11,11 +11,11 @@ pub extern crate foundry_cheatcodes_spec as spec; extern crate tracing; use alloy_primitives::Address; -use revm::EvmContext; +use foundry_evm_core::backend::DatabaseExt; +use revm::{ContextPrecompiles, InnerEvmContext}; pub use config::CheatsConfig; pub use error::{Error, ErrorKind, Result}; -use foundry_evm_core::backend::DatabaseExt; pub use inspector::{BroadcastableTransaction, BroadcastableTransactions, Cheatcodes, Context}; pub use spec::{CheatcodeDef, Vm}; @@ -57,15 +57,14 @@ pub(crate) trait Cheatcode: CheatcodeDef + DynCheatcode { #[inline] fn apply_traced(&self, ccx: &mut CheatsCtxt) -> Result { - let span = trace_span(self); - let _enter = span.enter(); - trace_call(); + let _span = trace_span_and_call(self); let result = self.apply_full(ccx); trace_return(&result); return result; // Separate and non-generic functions to avoid inline and monomorphization bloat. - fn trace_span(cheat: &dyn DynCheatcode) -> tracing::Span { + #[inline(never)] + fn trace_span_and_call(cheat: &dyn DynCheatcode) -> tracing::span::EnteredSpan { let span = debug_span!(target: "cheatcodes", "apply"); if !span.is_disabled() { if enabled!(tracing::Level::TRACE) { @@ -74,13 +73,12 @@ pub(crate) trait Cheatcode: CheatcodeDef + DynCheatcode { span.record("id", cheat.cheatcode().func.id); } } - span - } - - fn trace_call() { + let entered = span.entered(); trace!(target: "cheatcodes", "applying"); + entered } + #[inline(never)] fn trace_return(result: &Result) { trace!( target: "cheatcodes", @@ -109,18 +107,36 @@ impl DynCheatcode for T { } /// The cheatcode context, used in [`Cheatcode`]. -pub(crate) struct CheatsCtxt<'a, 'b, DB: DatabaseExt> { +pub(crate) struct CheatsCtxt<'cheats, 'evm, DB: DatabaseExt> { /// The cheatcodes inspector state. - pub(crate) state: &'a mut Cheatcodes, + pub(crate) state: &'cheats mut Cheatcodes, /// The EVM data. - pub(crate) ecx: &'b mut EvmContext, + pub(crate) ecx: &'evm mut InnerEvmContext, + /// The precompiles context. + pub(crate) precompiles: &'evm mut ContextPrecompiles, /// The original `msg.sender`. pub(crate) caller: Address, } -impl CheatsCtxt<'_, '_, DB> { +impl<'cheats, 'evm, DB: DatabaseExt> std::ops::Deref for CheatsCtxt<'cheats, 'evm, DB> { + type Target = InnerEvmContext; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + self.ecx + } +} + +impl<'cheats, 'evm, DB: DatabaseExt> std::ops::DerefMut for CheatsCtxt<'cheats, 'evm, DB> { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut *self.ecx + } +} + +impl<'cheats, 'evm, DB: DatabaseExt> CheatsCtxt<'cheats, 'evm, DB> { #[inline] pub(crate) fn is_precompile(&self, address: &Address) -> bool { - self.ecx.precompiles.contains(address) + self.precompiles.contains_key(address) } } diff --git a/crates/common/src/provider/alloy.rs b/crates/common/src/provider/alloy.rs index d51be9f9a..7208c7953 100644 --- a/crates/common/src/provider/alloy.rs +++ b/crates/common/src/provider/alloy.rs @@ -4,9 +4,8 @@ use crate::{ provider::runtime_transport::RuntimeTransportBuilder, ALCHEMY_FREE_TIER_CUPS, REQUEST_TIMEOUT, }; use alloy_primitives::U256; -use alloy_providers::provider::{Provider, TempProvider}; +use alloy_providers::tmp::{Provider, TempProvider}; use alloy_rpc_client::ClientBuilder; -use alloy_transport::BoxTransport; use ethers_middleware::gas_oracle::{GasCategory, GasOracle, Polygon}; use eyre::{Result, WrapErr}; use foundry_common::types::ToAlloy; @@ -18,10 +17,13 @@ use std::{ }; use url::ParseError; -use super::tower::RetryBackoffLayer; +use super::{ + runtime_transport::RuntimeTransport, + tower::{RetryBackoffLayer, RetryBackoffService}, +}; /// Helper type alias for a retry provider -pub type RetryProvider = Provider; +pub type RetryProvider = Provider>; /// Helper type alias for a rpc url pub type RpcUrl = String; @@ -232,7 +234,7 @@ impl ProviderBuilder { let client = ClientBuilder::default().layer(retry_layer).transport(transport, false); // todo: provider polling interval - Ok(Provider::new_with_client(client.boxed())) + Ok(Provider::new_with_client(client)) } } diff --git a/crates/evm/core/Cargo.toml b/crates/evm/core/Cargo.toml index 814a967c9..347d0489c 100644 --- a/crates/evm/core/Cargo.toml +++ b/crates/evm/core/Cargo.toml @@ -24,7 +24,6 @@ alloy-genesis.workspace = true alloy-providers.workspace = true alloy-rpc-types.workspace = true alloy-sol-types.workspace = true -alloy-transport.workspace = true revm = { workspace = true, default-features = false, features = [ "std", diff --git a/crates/evm/core/src/backend/cow.rs b/crates/evm/core/src/backend/cow.rs index d8ea27c0d..c0ff88ea4 100644 --- a/crates/evm/core/src/backend/cow.rs +++ b/crates/evm/core/src/backend/cow.rs @@ -71,7 +71,7 @@ impl<'a> CowBackend<'a> { let res = evm.transact().wrap_err("backend: failed while inspecting")?; - env.env = evm.context.evm.env; + env.env = evm.context.evm.inner.env; Ok(res) } diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index 1ad5ec481..f55259f4d 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -790,7 +790,7 @@ impl Backend { let res = evm.transact().wrap_err("backend: failed while inspecting")?; - env.env = evm.context.evm.env; + env.env = evm.context.evm.inner.env; Ok(res) } diff --git a/crates/evm/core/src/fork/backend.rs b/crates/evm/core/src/fork/backend.rs index ccf64fd10..6613c0f13 100644 --- a/crates/evm/core/src/fork/backend.rs +++ b/crates/evm/core/src/fork/backend.rs @@ -4,7 +4,7 @@ use crate::{ fork::{cache::FlushJsonBlockCacheDB, BlockchainDb}, }; use alloy_primitives::{keccak256, Address, Bytes, B256, U256}; -use alloy_providers::provider::TempProvider; +use alloy_providers::tmp::TempProvider; use alloy_rpc_types::{Block, BlockId, Transaction}; use eyre::WrapErr; use foundry_common::NON_ARCHIVE_NODE_WARNING; diff --git a/crates/evm/core/src/fork/init.rs b/crates/evm/core/src/fork/init.rs index 84830d5a2..abde7cb22 100644 --- a/crates/evm/core/src/fork/init.rs +++ b/crates/evm/core/src/fork/init.rs @@ -1,6 +1,6 @@ use crate::utils::apply_chain_and_block_specific_env_changes; use alloy_primitives::{Address, U256}; -use alloy_providers::provider::TempProvider; +use alloy_providers::tmp::TempProvider; use alloy_rpc_types::{Block, BlockNumberOrTag}; use eyre::WrapErr; use foundry_common::NON_ARCHIVE_NODE_WARNING; diff --git a/crates/evm/core/src/fork/multi.rs b/crates/evm/core/src/fork/multi.rs index f84af067c..4e1cb66e5 100644 --- a/crates/evm/core/src/fork/multi.rs +++ b/crates/evm/core/src/fork/multi.rs @@ -4,9 +4,7 @@ //! concurrently active pairs at once. use crate::fork::{BackendHandler, BlockchainDb, BlockchainDbMeta, CreateFork, SharedBackend}; -use alloy_providers::provider::Provider; -use alloy_transport::BoxTransport; -use foundry_common::provider::alloy::ProviderBuilder; +use foundry_common::provider::alloy::{ProviderBuilder, RetryProvider}; use foundry_config::Config; use futures::{ channel::mpsc::{channel, Receiver, Sender}, @@ -169,7 +167,7 @@ impl MultiFork { } } -type Handler = BackendHandler>>; +type Handler = BackendHandler>; type CreateFuture = Pin> + Send>>; diff --git a/crates/evm/core/src/opts.rs b/crates/evm/core/src/opts.rs index 786252bba..89fdcf4e3 100644 --- a/crates/evm/core/src/opts.rs +++ b/crates/evm/core/src/opts.rs @@ -1,7 +1,7 @@ use super::fork::environment; use crate::fork::CreateFork; use alloy_primitives::{Address, B256, U256}; -use alloy_providers::provider::TempProvider; +use alloy_providers::tmp::TempProvider; use alloy_rpc_types::Block; use eyre::WrapErr; use foundry_common::{ diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index c118bcd3d..67fbb3fa7 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -370,7 +370,6 @@ impl InspectorStack { record_stack_snapshots: StackSnapshotType::None, record_state_diff: false, exclude_precompile_calls: false, - record_call_return_data: true, record_logs: true, }) }); @@ -443,6 +442,8 @@ impl InspectorStack { gas_limit: u64, value: U256, ) -> (InterpreterResult, Option
) { + let ecx = &mut ecx.inner; + ecx.db.commit(ecx.journaled_state.state.clone()); let nonce = ecx @@ -485,7 +486,7 @@ impl InspectorStack { let res = evm.transact(); // need to reset the env in case it was modified via cheatcodes during execution - ecx.env = evm.context.evm.env; + ecx.env = evm.context.evm.inner.env; res }; From d75219c55c00f158651feb3cbb8405bf5ad790b8 Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Tue, 12 Mar 2024 08:56:28 +0100 Subject: [PATCH 056/430] chore: remove repetitive words (#7371) --- crates/cast/bin/cmd/create2.rs | 2 +- crates/fmt/src/string.rs | 2 +- crates/script/src/transaction.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/cast/bin/cmd/create2.rs b/crates/cast/bin/cmd/create2.rs index 6474d52e5..4494d5d68 100644 --- a/crates/cast/bin/cmd/create2.rs +++ b/crates/cast/bin/cmd/create2.rs @@ -203,7 +203,7 @@ impl Create2Args { #[allow(clippy::needless_borrows_for_generic_args)] let addr = deployer.create2(&salt.0, init_code_hash); - // Check if the the regex matches the calculated address' checksum. + // Check if the regex matches the calculated address' checksum. let _ = addr.to_checksum_raw(&mut checksum, None); // SAFETY: stripping 2 ASCII bytes ("0x") off of an already valid UTF-8 string // is safe. diff --git a/crates/fmt/src/string.rs b/crates/fmt/src/string.rs index 6ffc0b959..607c890e7 100644 --- a/crates/fmt/src/string.rs +++ b/crates/fmt/src/string.rs @@ -65,7 +65,7 @@ impl<'a> Iterator for QuoteStateCharIndices<'a> { } } -/// An iterator over the the indices of quoted string locations +/// An iterator over the indices of quoted string locations pub struct QuotedRanges<'a>(QuoteStateCharIndices<'a>); impl<'a> QuotedRanges<'a> { diff --git a/crates/script/src/transaction.rs b/crates/script/src/transaction.rs index 3f92e2f31..80a281420 100644 --- a/crates/script/src/transaction.rs +++ b/crates/script/src/transaction.rs @@ -397,7 +397,7 @@ pub mod wrapper { pub cumulative_gas_used: U256, /// Gas used by this transaction alone. /// - /// Gas used is `None` if the the client is running in light client mode. + /// Gas used is `None` if the client is running in light client mode. #[serde(rename = "gasUsed")] pub gas_used: Option, /// Contract address created, or `None` if not a deployment. From a3cec878c2938f60855ab928acc0c07439e9c7c5 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 12 Mar 2024 12:59:07 +0100 Subject: [PATCH 057/430] chore: update tests after new forge-std (#7374) --- crates/forge/tests/cli/cmd.rs | 2 +- crates/forge/tests/cli/config.rs | 22 ++++++++----------- .../can_create_template_contract.stdout | 2 +- .../fixtures/can_create_using_unlocked.stdout | 2 +- .../can_create_with_constructor_args.stdout | 2 +- .../tests/fixtures/can_test_repeatedly.stdout | 2 +- .../can_use_libs_in_multi_fork.stdout | 2 +- crates/forge/tests/fixtures/repro_6531.stdout | 4 ++-- 8 files changed, 17 insertions(+), 21 deletions(-) diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 48e86d9df..e081b3d61 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -393,7 +393,7 @@ forgetest!(can_init_vscode, |prj, cmd| { let remappings = prj.root().join("remappings.txt"); assert!(remappings.is_file()); let content = std::fs::read_to_string(remappings).unwrap(); - assert_eq!(content, "ds-test/=lib/forge-std/lib/ds-test/src/\nforge-std/=lib/forge-std/src/",); + assert_eq!(content, "forge-std/=lib/forge-std/src/",); }); // checks that forge can init with template diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 20bbb8e1b..fa57e4077 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -150,14 +150,14 @@ forgetest_init!(can_override_config, |prj, cmd| { let profile = Config::load_with_root(prj.root()); // ensure that the auto-generated internal remapping for forge-std's ds-test exists - assert_eq!(profile.remappings.len(), 2); - assert_eq!("ds-test/=lib/forge-std/lib/ds-test/src/", profile.remappings[0].to_string()); + assert_eq!(profile.remappings.len(), 1); + assert_eq!("forge-std/=lib/forge-std/src/", profile.remappings[0].to_string()); // ensure remappings contain test - assert_eq!("ds-test/=lib/forge-std/lib/ds-test/src/", profile.remappings[0].to_string()); + assert_eq!("forge-std/=lib/forge-std/src/", profile.remappings[0].to_string()); // the loaded config has resolved, absolute paths assert_eq!( - "ds-test/=lib/forge-std/lib/ds-test/src/", + "forge-std/=lib/forge-std/src/", Remapping::from(profile.remappings[0].clone()).to_string() ); @@ -221,12 +221,12 @@ forgetest_init!(can_parse_remappings_correctly, |prj, cmd| { let profile = Config::load_with_root(prj.root()); // ensure that the auto-generated internal remapping for forge-std's ds-test exists - assert_eq!(profile.remappings.len(), 2); - let [r, _] = &profile.remappings[..] else { unreachable!() }; - assert_eq!("ds-test/=lib/forge-std/lib/ds-test/src/", r.to_string()); + assert_eq!(profile.remappings.len(), 1); + let r = &profile.remappings[0]; + assert_eq!("forge-std/=lib/forge-std/src/", r.to_string()); // the loaded config has resolved, absolute paths - assert_eq!("ds-test/=lib/forge-std/lib/ds-test/src/", Remapping::from(r.clone()).to_string()); + assert_eq!("forge-std/=lib/forge-std/src/", Remapping::from(r.clone()).to_string()); cmd.arg("config"); let expected = profile.to_string_pretty().unwrap(); @@ -432,11 +432,11 @@ forgetest!(can_set_gas_price, |prj, cmd| { forgetest_init!(can_detect_lib_foundry_toml, |prj, cmd| { let config = cmd.config(); let remappings = config.remappings.iter().cloned().map(Remapping::from).collect::>(); + dbg!(&remappings); pretty_assertions::assert_eq!( remappings, vec![ // global - "ds-test/=lib/forge-std/lib/ds-test/src/".parse().unwrap(), "forge-std/=lib/forge-std/src/".parse().unwrap(), ] ); @@ -455,7 +455,6 @@ forgetest_init!(can_detect_lib_foundry_toml, |prj, cmd| { remappings, vec![ // default - "ds-test/=lib/forge-std/lib/ds-test/src/".parse().unwrap(), "forge-std/=lib/forge-std/src/".parse().unwrap(), // remapping is local to the lib "nested-lib/=lib/nested-lib/src/".parse().unwrap(), @@ -481,7 +480,6 @@ forgetest_init!(can_detect_lib_foundry_toml, |prj, cmd| { // local to the lib "another-lib/=lib/nested-lib/lib/another-lib/src/".parse().unwrap(), // global - "ds-test/=lib/forge-std/lib/ds-test/src/".parse().unwrap(), "forge-std/=lib/forge-std/src/".parse().unwrap(), "nested-lib/=lib/nested-lib/src/".parse().unwrap(), // remappings local to the lib @@ -500,7 +498,6 @@ forgetest_init!(can_detect_lib_foundry_toml, |prj, cmd| { // local to the lib "another-lib/=lib/nested-lib/lib/another-lib/custom-source-dir/".parse().unwrap(), // global - "ds-test/=lib/forge-std/lib/ds-test/src/".parse().unwrap(), "forge-std/=lib/forge-std/src/".parse().unwrap(), "nested-lib/=lib/nested-lib/src/".parse().unwrap(), // remappings local to the lib @@ -529,7 +526,6 @@ forgetest_init!(can_prioritise_closer_lib_remappings, |prj, cmd| { remappings, vec![ "dep1/=lib/dep1/src/".parse().unwrap(), - "ds-test/=lib/forge-std/lib/ds-test/src/".parse().unwrap(), "forge-std/=lib/forge-std/src/".parse().unwrap() ] ); diff --git a/crates/forge/tests/fixtures/can_create_template_contract.stdout b/crates/forge/tests/fixtures/can_create_template_contract.stdout index 622c81ac4..adb787a44 100644 --- a/crates/forge/tests/fixtures/can_create_template_contract.stdout +++ b/crates/forge/tests/fixtures/can_create_template_contract.stdout @@ -1,4 +1,4 @@ -Compiling 24 files with 0.8.23 +Compiling 27 files with 0.8.23 Solc 0.8.23 finished in 2.27s Compiler run successful! Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 diff --git a/crates/forge/tests/fixtures/can_create_using_unlocked.stdout b/crates/forge/tests/fixtures/can_create_using_unlocked.stdout index a4132c617..34a5fb9f7 100644 --- a/crates/forge/tests/fixtures/can_create_using_unlocked.stdout +++ b/crates/forge/tests/fixtures/can_create_using_unlocked.stdout @@ -1,4 +1,4 @@ -Compiling 24 files with 0.8.23 +Compiling 27 files with 0.8.23 Solc 0.8.23 finished in 1.95s Compiler run successful! Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 diff --git a/crates/forge/tests/fixtures/can_create_with_constructor_args.stdout b/crates/forge/tests/fixtures/can_create_with_constructor_args.stdout index 8cb09c22c..0fb83d06f 100644 --- a/crates/forge/tests/fixtures/can_create_with_constructor_args.stdout +++ b/crates/forge/tests/fixtures/can_create_with_constructor_args.stdout @@ -1,4 +1,4 @@ -Compiling 25 files with 0.8.23 +Compiling 28 files with 0.8.23 Solc 0.8.23 finished in 2.82s Compiler run successful! Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 diff --git a/crates/forge/tests/fixtures/can_test_repeatedly.stdout b/crates/forge/tests/fixtures/can_test_repeatedly.stdout index dbab28125..d792e8096 100644 --- a/crates/forge/tests/fixtures/can_test_repeatedly.stdout +++ b/crates/forge/tests/fixtures/can_test_repeatedly.stdout @@ -2,7 +2,7 @@ No files changed, compilation skipped Ran 2 tests for test/Counter.t.sol:CounterTest [PASS] testFuzz_SetNumber(uint256) (runs: 256, μ: 26521, ~: 28387) -[PASS] test_Increment() (gas: 28379) +[PASS] test_Increment() (gas: 31225) Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 9.42ms Ran 1 test suite: 2 tests passed, 0 failed, 0 skipped (2 total tests) diff --git a/crates/forge/tests/fixtures/can_use_libs_in_multi_fork.stdout b/crates/forge/tests/fixtures/can_use_libs_in_multi_fork.stdout index 70c72887a..87a5aa753 100644 --- a/crates/forge/tests/fixtures/can_use_libs_in_multi_fork.stdout +++ b/crates/forge/tests/fixtures/can_use_libs_in_multi_fork.stdout @@ -3,7 +3,7 @@ Solc 0.8.23 finished in 1.95s Compiler run successful! Ran 1 test for test/Contract.t.sol:ContractTest -[PASS] test() (gas: 70360) +[PASS] test() (gas: 70404) Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.21s Ran 1 test suite: 1 tests passed, 0 failed, 0 skipped (1 total tests) diff --git a/crates/forge/tests/fixtures/repro_6531.stdout b/crates/forge/tests/fixtures/repro_6531.stdout index 159c6476e..e8e474ba7 100644 --- a/crates/forge/tests/fixtures/repro_6531.stdout +++ b/crates/forge/tests/fixtures/repro_6531.stdout @@ -3,9 +3,9 @@ Compiling 1 files with 0.8.23 Compiler run successful! Ran 1 test for test/Contract.t.sol:USDCCallingTest -[PASS] test() (gas: 16799) +[PASS] test() (gas: 16821) Traces: - [16799] USDCCallingTest::test() + [16821] USDCCallingTest::test() ├─ [0] VM::createSelectFork("") │ └─ ← 0 ├─ [10350] 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48::name() [staticcall] From dbddd081a59238bb2ffb95ae04a6e2176a721a40 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 12 Mar 2024 14:01:10 +0200 Subject: [PATCH 058/430] fix(anvil): return correct block number for Arbitrum fork (#7360) * fix(anvil): return correct block number for Arbitrum fork * Backward compatibility with existing state files --- crates/anvil/src/config.rs | 5 +- crates/anvil/src/eth/backend/db.rs | 16 ++++-- crates/anvil/src/eth/backend/mem/fork_db.rs | 14 ++++-- .../anvil/src/eth/backend/mem/in_memory_db.rs | 16 ++++-- crates/anvil/src/eth/backend/mem/mod.rs | 16 ++++-- crates/anvil/tests/it/fork.rs | 49 +++++++++++++++++++ crates/evm/core/src/utils.rs | 5 +- 7 files changed, 101 insertions(+), 20 deletions(-) diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 5c61c0824..354bf2c58 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -1026,9 +1026,6 @@ latest block number: {latest_block}" ..Default::default() }; - // apply changes such as difficulty -> prevrandao - apply_chain_and_block_specific_env_changes(env, &block); - // if not set explicitly we use the base fee of the latest block if self.base_fee.is_none() { if let Some(base_fee) = block.header.base_fee_per_gas { @@ -1072,6 +1069,8 @@ latest block number: {latest_block}" chain_id }; let override_chain_id = self.chain_id; + // apply changes such as difficulty -> prevrandao and chain specifics for current chain id + apply_chain_and_block_specific_env_changes(env, &block); let meta = BlockchainDbMeta::new(*env.env.clone(), eth_rpc_url.clone()); let block_chain_db = if self.fork_chain_id.is_some() { diff --git a/crates/anvil/src/eth/backend/db.rs b/crates/anvil/src/eth/backend/db.rs index 02e7db113..2630d19a7 100644 --- a/crates/anvil/src/eth/backend/db.rs +++ b/crates/anvil/src/eth/backend/db.rs @@ -1,7 +1,7 @@ //! Helper types for working with [revm](foundry_evm::revm) use crate::{mem::state::trie_hash_db, revm::primitives::AccountInfo}; -use alloy_primitives::{keccak256, Address, Bytes, B256, U256}; +use alloy_primitives::{keccak256, Address, Bytes, B256, U256, U64}; use alloy_rpc_types::BlockId; use anvil_core::eth::trie::KeccakHasher; use foundry_common::errors::FsPathError; @@ -126,7 +126,11 @@ pub trait Db: fn insert_block_hash(&mut self, number: U256, hash: B256); /// Write all chain data to serialized bytes buffer - fn dump_state(&self, at: BlockEnv) -> DatabaseResult>; + fn dump_state( + &self, + at: BlockEnv, + best_number: U64, + ) -> DatabaseResult>; /// Deserialize and add all chain data to the backend storage fn load_state(&mut self, state: SerializableState) -> DatabaseResult { @@ -196,7 +200,11 @@ impl + Send + Sync + Clone + fmt::Debug> D self.block_hashes.insert(number, hash); } - fn dump_state(&self, _at: BlockEnv) -> DatabaseResult> { + fn dump_state( + &self, + _at: BlockEnv, + _best_number: U64, + ) -> DatabaseResult> { Ok(None) } @@ -329,6 +337,8 @@ pub struct SerializableState { /// Note: This is an Option for backwards compatibility: pub block: Option, pub accounts: BTreeMap, + /// The best block number of the state, can be different from block number (Arbitrum chain). + pub best_block_number: Option, } // === impl SerializableState === diff --git a/crates/anvil/src/eth/backend/mem/fork_db.rs b/crates/anvil/src/eth/backend/mem/fork_db.rs index 354971ce5..7f9262fd6 100644 --- a/crates/anvil/src/eth/backend/mem/fork_db.rs +++ b/crates/anvil/src/eth/backend/mem/fork_db.rs @@ -5,7 +5,7 @@ use crate::{ }, revm::primitives::AccountInfo, }; -use alloy_primitives::{Address, B256, U256}; +use alloy_primitives::{Address, B256, U256, U64}; use alloy_rpc_types::BlockId; use foundry_evm::{ backend::{DatabaseResult, RevertSnapshotAction, StateSnapshot}, @@ -32,7 +32,11 @@ impl Db for ForkedDatabase { self.inner().block_hashes().write().insert(number, hash); } - fn dump_state(&self, at: BlockEnv) -> DatabaseResult> { + fn dump_state( + &self, + at: BlockEnv, + best_number: U64, + ) -> DatabaseResult> { let mut db = self.database().clone(); let accounts = self .database() @@ -57,7 +61,11 @@ impl Db for ForkedDatabase { )) }) .collect::>()?; - Ok(Some(SerializableState { block: Some(at), accounts })) + Ok(Some(SerializableState { + block: Some(at), + accounts, + best_block_number: Some(best_number), + })) } fn snapshot(&mut self) -> U256 { diff --git a/crates/anvil/src/eth/backend/mem/in_memory_db.rs b/crates/anvil/src/eth/backend/mem/in_memory_db.rs index e1c7deb8e..c6ebc8a91 100644 --- a/crates/anvil/src/eth/backend/mem/in_memory_db.rs +++ b/crates/anvil/src/eth/backend/mem/in_memory_db.rs @@ -8,7 +8,7 @@ use crate::{ mem::state::{state_merkle_trie_root, storage_trie_db, trie_hash_db}, revm::primitives::AccountInfo, }; -use alloy_primitives::{Address, B256, U256}; +use alloy_primitives::{Address, B256, U256, U64}; use alloy_rpc_types::BlockId; use foundry_evm::{ backend::{DatabaseResult, StateSnapshot}, @@ -32,7 +32,11 @@ impl Db for MemDb { self.inner.block_hashes.insert(number, hash); } - fn dump_state(&self, at: BlockEnv) -> DatabaseResult> { + fn dump_state( + &self, + at: BlockEnv, + best_number: U64, + ) -> DatabaseResult> { let accounts = self .inner .accounts @@ -57,7 +61,11 @@ impl Db for MemDb { }) .collect::>()?; - Ok(Some(SerializableState { block: Some(at), accounts })) + Ok(Some(SerializableState { + block: Some(at), + accounts, + best_block_number: Some(best_number), + })) } /// Creates a new snapshot @@ -160,7 +168,7 @@ mod tests { dump_db.set_storage_at(test_addr, U256::from(1234567), U256::from(1)).unwrap(); - let state = dump_db.dump_state(Default::default()).unwrap().unwrap(); + let state = dump_db.dump_state(Default::default(), U64::ZERO).unwrap().unwrap(); let mut load_db = MemDb::default(); diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 2b0bf292f..709932438 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -495,7 +495,7 @@ impl Backend { /// Returns the current best number of the chain pub fn best_number(&self) -> u64 { - self.env.read().block.number.try_into().unwrap_or(u64::MAX) + self.blockchain.storage.read().best_number.try_into().unwrap_or(u64::MAX) } /// Sets the block number @@ -721,7 +721,8 @@ impl Backend { /// Get the current state. pub async fn serialized_state(&self) -> Result { let at = self.env.read().block.clone(); - let state = self.db.read().await.dump_state(at)?; + let best_number = self.blockchain.storage.read().best_number; + let state = self.db.read().await.dump_state(at, best_number)?; state.ok_or_else(|| { RpcError::invalid_params("Dumping state not supported with the current configuration") .into() @@ -742,7 +743,12 @@ impl Backend { pub async fn load_state(&self, state: SerializableState) -> Result { // reset the block env if let Some(block) = state.block.clone() { - self.env.write().block = block; + self.env.write().block = block.clone(); + + // Set the current best block number. + // Defaults to block number for compatibility with existing state files. + self.blockchain.storage.write().best_number = + state.best_block_number.unwrap_or(block.number.to::()); } if !self.db.write().await.load_state(state)? { @@ -922,8 +928,9 @@ impl Backend { let ExecutedTransactions { block, included, invalid } = executed_tx; let BlockInfo { block, transactions, receipts } = block; + let mut storage = self.blockchain.storage.write(); let header = block.header.clone(); - let block_number: U64 = env.block.number.to::(); + let block_number = storage.best_number.saturating_add(U64::from(1)); trace!( target: "backend", @@ -933,7 +940,6 @@ impl Backend { transactions.iter().map(|tx| tx.transaction_hash).collect::>() ); - let mut storage = self.blockchain.storage.write(); // update block metadata storage.best_number = block_number; storage.best_hash = block_hash; diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index a23cc7052..39946383e 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -1123,6 +1123,55 @@ async fn test_arbitrum_fork_dev_balance() { } } +// +#[tokio::test(flavor = "multi_thread")] +async fn test_arbitrum_fork_block_number() { + // fork to get initial block for test + let (_, handle) = spawn( + fork_config() + .with_fork_block_number(None::) + .with_eth_rpc_url(Some("https://arb1.arbitrum.io/rpc".to_string())), + ) + .await; + let provider = ethers_http_provider(&handle.http_endpoint()); + let initial_block_number = provider.get_block_number().await.unwrap().as_u64(); + + // fork again at block number returned by `eth_blockNumber` + // if wrong block number returned (e.g. L1) then fork will fail with error code -32000: missing + // trie node + let (api, _) = spawn( + fork_config() + .with_fork_block_number(Some(initial_block_number)) + .with_eth_rpc_url(Some("https://arb1.arbitrum.io/rpc".to_string())), + ) + .await; + let block_number = api.block_number().unwrap().to::(); + assert_eq!(block_number, initial_block_number); + + // take snapshot at initial block number + let snapshot = api.evm_snapshot().await.unwrap(); + + // mine new block and check block number returned by `eth_blockNumber` + api.mine_one().await; + let block_number = api.block_number().unwrap().to::(); + assert_eq!(block_number, initial_block_number + 1); + + // revert to recorded snapshot and check block number + assert!(api.evm_revert(snapshot).await.unwrap()); + let block_number = api.block_number().unwrap().to::(); + assert_eq!(block_number, initial_block_number); + + // reset fork to different block number and compare with block returned by `eth_blockNumber` + api.anvil_reset(Some(Forking { + json_rpc_url: Some("https://arb1.arbitrum.io/rpc".to_string()), + block_number: Some(initial_block_number - 2), + })) + .await + .unwrap(); + let block_number = api.block_number().unwrap().to::(); + assert_eq!(block_number, initial_block_number - 2); +} + // #[tokio::test(flavor = "multi_thread")] async fn test_fork_execution_reverted() { diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index 48e8808e4..7ef8bb47f 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -15,8 +15,9 @@ pub use crate::ic::*; /// Depending on the configured chain id and block number this should apply any specific changes /// -/// This checks for: -/// - prevrandao mixhash after merge +/// - checks for prevrandao mixhash after merge +/// - applies chain specifics: on Arbitrum `block.number` is the L1 block +/// Should be called with proper chain id (retrieved from provider if not provided). pub fn apply_chain_and_block_specific_env_changes(env: &mut revm::primitives::Env, block: &Block) { if let Ok(chain) = NamedChain::try_from(env.cfg.chain_id) { let block_number = block.header.number.unwrap_or_default(); From d3b8d154ef135421c699f3ba8668a2ccbf2d3c26 Mon Sep 17 00:00:00 2001 From: Krishang Shah <93703995+kamuik16@users.noreply.github.com> Date: Tue, 12 Mar 2024 17:47:56 +0530 Subject: [PATCH 059/430] fix: compile contracts before generating docs (#7369) * fix: compile before doc * run forge compile silently --------- Co-authored-by: Matthias Seitz --- crates/forge/bin/cmd/doc/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/forge/bin/cmd/doc/mod.rs b/crates/forge/bin/cmd/doc/mod.rs index d594a0e56..30de31e5a 100644 --- a/crates/forge/bin/cmd/doc/mod.rs +++ b/crates/forge/bin/cmd/doc/mod.rs @@ -4,6 +4,7 @@ use forge_doc::{ ContractInheritance, Deployments, DocBuilder, GitSource, InferInlineHyperlinks, Inheritdoc, }; use foundry_cli::opts::GH_REPO_PREFIX_REGEX; +use foundry_common::compile::ProjectCompiler; use foundry_config::{find_project_root_path, load_config_with_root}; use std::{path::PathBuf, process::Command}; @@ -64,6 +65,9 @@ impl DocArgs { pub fn run(self) -> Result<()> { let root = self.root.clone().unwrap_or(find_project_root_path(None)?); let config = load_config_with_root(Some(root.clone())); + let project = config.project()?; + let compiler = ProjectCompiler::new().quiet(true); + let _output = compiler.compile(&project)?; let mut doc_config = config.doc.clone(); if let Some(out) = self.out { From 5fe9143385231ebf67af670a9f001e0f4fab4a33 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 12 Mar 2024 16:52:28 +0200 Subject: [PATCH 060/430] feat(test): add fuzz tests failure persistence (#7336) * feat(forge): add fuzz tests failure persistence * Enable inline file failure config * New config not needed to be Option * Persist failures in proj cache dir * Make persist dirs option, remove foundry_fuzz_cache_dir fn --------- Co-authored-by: Matthias Seitz --- crates/config/src/fuzz.rs | 30 ++++++++++++-- crates/config/src/lib.rs | 2 +- crates/forge/bin/cmd/test/mod.rs | 9 +++- crates/forge/src/lib.rs | 29 +++++++++---- crates/forge/src/runner.rs | 10 +++-- crates/forge/tests/cli/config.rs | 2 + crates/forge/tests/it/fuzz.rs | 57 ++++++++++++++++++++++++-- crates/forge/tests/it/test_helpers.rs | 4 +- testdata/fuzz/FuzzFailurePersist.t.sol | 29 +++++++++++++ 9 files changed, 153 insertions(+), 19 deletions(-) create mode 100644 testdata/fuzz/FuzzFailurePersist.t.sol diff --git a/crates/config/src/fuzz.rs b/crates/config/src/fuzz.rs index 13e8d34d3..3b0e13bcd 100644 --- a/crates/config/src/fuzz.rs +++ b/crates/config/src/fuzz.rs @@ -5,9 +5,10 @@ use crate::inline::{ }; use alloy_primitives::U256; use serde::{Deserialize, Serialize}; +use std::path::PathBuf; /// Contains for fuzz testing -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct FuzzConfig { /// The number of test cases that must execute for each property test pub runs: u32, @@ -24,6 +25,10 @@ pub struct FuzzConfig { pub dictionary: FuzzDictionaryConfig, /// Number of runs to execute and include in the gas report. pub gas_report_samples: u32, + /// Path where fuzz failures are recorded and replayed. + pub failure_persist_dir: Option, + /// Name of the file to record fuzz failures, defaults to `failures`. + pub failure_persist_file: Option, } impl Default for FuzzConfig { @@ -34,6 +39,23 @@ impl Default for FuzzConfig { seed: None, dictionary: FuzzDictionaryConfig::default(), gas_report_samples: 256, + failure_persist_dir: None, + failure_persist_file: None, + } + } +} + +impl FuzzConfig { + /// Creates fuzz configuration to write failures in `{PROJECT_ROOT}/cache/fuzz` dir. + pub fn new(cache_dir: PathBuf) -> Self { + FuzzConfig { + 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()), } } } @@ -50,8 +72,7 @@ impl InlineConfigParser for FuzzConfig { return Ok(None) } - // self is Copy. We clone it with dereference. - let mut conf_clone = *self; + let mut conf_clone = self.clone(); for pair in overrides { let key = pair.0; @@ -62,6 +83,7 @@ impl InlineConfigParser for FuzzConfig { "dictionary-weight" => { conf_clone.dictionary.dictionary_weight = parse_config_u32(key, value)? } + "failure-persist-file" => conf_clone.failure_persist_file = Some(value), _ => Err(InlineConfigParserError::InvalidConfigProperty(key))?, } } @@ -130,11 +152,13 @@ mod tests { 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] diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 000778455..4f27b58b8 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -1869,7 +1869,7 @@ impl Default for Config { contract_pattern_inverse: None, path_pattern: None, path_pattern_inverse: None, - fuzz: Default::default(), + fuzz: FuzzConfig::new("cache/fuzz".into()), invariant: Default::default(), always_use_create_2_factory: false, ffi: false, diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 11ae1aee7..dfb1d8cf5 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -97,6 +97,10 @@ pub struct TestArgs { #[arg(long, env = "FOUNDRY_FUZZ_RUNS", value_name = "RUNS")] pub fuzz_runs: Option, + /// File to rerun fuzz failures from. + #[arg(long)] + pub fuzz_input_file: Option, + #[command(flatten)] filter: FilterArgs, @@ -176,7 +180,7 @@ impl TestArgs { let profiles = get_available_profiles(toml)?; let test_options: TestOptions = TestOptionsBuilder::default() - .fuzz(config.fuzz) + .fuzz(config.clone().fuzz) .invariant(config.invariant) .profiles(profiles) .build(&output, project_root)?; @@ -518,6 +522,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_input_file) = self.fuzz_input_file.clone() { + fuzz_dict.insert("failure_persist_file".to_string(), fuzz_input_file.into()); + } dict.insert("fuzz".to_string(), fuzz_dict.into()); if let Some(etherscan_api_key) = diff --git a/crates/forge/src/lib.rs b/crates/forge/src/lib.rs index f6b9a54a8..a430204e1 100644 --- a/crates/forge/src/lib.rs +++ b/crates/forge/src/lib.rs @@ -6,7 +6,9 @@ use foundry_config::{ validate_profiles, Config, FuzzConfig, InlineConfig, InlineConfigError, InlineConfigParser, InvariantConfig, NatSpec, }; -use proptest::test_runner::{RngAlgorithm, TestRng, TestRunner}; +use proptest::test_runner::{ + FailurePersistence, FileFailurePersistence, RngAlgorithm, TestRng, TestRunner, +}; use std::path::Path; pub mod coverage; @@ -93,8 +95,18 @@ impl TestOptions { where S: Into, { - let fuzz = self.fuzz_config(contract_id, test_fn); - self.fuzzer_with_cases(fuzz.runs) + let fuzz_config = self.fuzz_config(contract_id, test_fn).clone(); + let failure_persist_path = fuzz_config + .failure_persist_dir + .unwrap() + .join(fuzz_config.failure_persist_file.unwrap()) + .into_os_string() + .into_string() + .unwrap(); + self.fuzzer_with_cases( + fuzz_config.runs, + Some(Box::new(FileFailurePersistence::Direct(failure_persist_path.leak()))), + ) } /// Returns an "invariant" test runner instance. Parameters are used to select tight scoped fuzz @@ -109,7 +121,7 @@ impl TestOptions { S: Into, { let invariant = self.invariant_config(contract_id, test_fn); - self.fuzzer_with_cases(invariant.runs) + self.fuzzer_with_cases(invariant.runs, None) } /// Returns a "fuzz" configuration setup. Parameters are used to select tight scoped fuzz @@ -140,10 +152,13 @@ impl TestOptions { self.inline_invariant.get(contract_id, test_fn).unwrap_or(&self.invariant) } - pub fn fuzzer_with_cases(&self, cases: u32) -> TestRunner { - // TODO: Add Options to modify the persistence + pub fn fuzzer_with_cases( + &self, + cases: u32, + file_failure_persistence: Option>, + ) -> TestRunner { let config = proptest::test_runner::Config { - failure_persistence: None, + failure_persistence: file_failure_persistence, cases, max_global_rejects: self.fuzz.max_test_rejects, ..Default::default() diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 0164cd7df..3b1a1a207 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -294,7 +294,7 @@ impl<'a> ContractRunner<'a> { debug_assert!(func.is_test()); 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) + self.run_fuzz_test(func, should_fail, runner, setup, fuzz_config.clone()) } else { debug_assert!(func.is_test()); self.run_test(func, should_fail, setup) @@ -604,8 +604,12 @@ impl<'a> ContractRunner<'a> { // Run fuzz test let start = Instant::now(); - let fuzzed_executor = - FuzzedExecutor::new(self.executor.clone(), runner.clone(), self.sender, fuzz_config); + let fuzzed_executor = FuzzedExecutor::new( + self.executor.clone(), + runner.clone(), + self.sender, + fuzz_config.clone(), + ); let state = fuzzed_executor.build_fuzz_state(); let result = fuzzed_executor.fuzz(func, address, should_fail, self.revert_decoder); diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index fa57e4077..6714df59d 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -65,6 +65,8 @@ forgetest!(can_extract_config_values, |prj, cmd| { runs: 1000, max_test_rejects: 100203, seed: Some(U256::from(1000)), + failure_persist_dir: Some("test-cache/fuzz".into()), + failure_persist_file: Some("failures".to_string()), ..Default::default() }, invariant: InvariantConfig { runs: 256, ..Default::default() }, diff --git a/crates/forge/tests/it/fuzz.rs b/crates/forge/tests/it/fuzz.rs index 14a27d6b4..9f59a7f42 100644 --- a/crates/forge/tests/it/fuzz.rs +++ b/crates/forge/tests/it/fuzz.rs @@ -1,10 +1,14 @@ //! Fuzz tests. -use crate::config::*; -use alloy_primitives::U256; +use std::collections::BTreeMap; + +use alloy_primitives::{Bytes, U256}; +use forge::fuzz::CounterExample; + use forge::result::{SuiteResult, TestStatus}; use foundry_test_utils::Filter; -use std::collections::BTreeMap; + +use crate::config::*; #[tokio::test(flavor = "multi_thread")] async fn test_fuzz() { @@ -103,3 +107,50 @@ 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 = runner(); + runner.test_options.fuzz.runs = 1000; + + macro_rules! get_failure_result { + () => { + runner + .test_collect(&filter) + .get("fuzz/FuzzFailurePersist.t.sol:FuzzFailurePersistTest") + .unwrap() + .test_results + .get("test_persist_fuzzed_failure(uint256,int256,address,bool,string,(address,uint256),address[])") + .unwrap() + .counterexample + .clone() + }; + } + + // record initial counterexample calldata + let intial_counterexample = get_failure_result!(); + let initial_calldata = match intial_counterexample { + Some(CounterExample::Single(counterexample)) => counterexample.calldata, + _ => Bytes::new(), + }; + + // run several times and compare counterexamples calldata + for _ in 0..10 { + let new_calldata = match get_failure_result!() { + Some(CounterExample::Single(counterexample)) => counterexample.calldata, + _ => Bytes::new(), + }; + // calldata should be the same with the initial one + assert_eq!(initial_calldata, new_calldata); + } + + // write new failure in different file + runner.test_options.fuzz.failure_persist_file = Some("failure1".to_string()); + let new_calldata = match get_failure_result!() { + Some(CounterExample::Single(counterexample)) => counterexample.calldata, + _ => Bytes::new(), + }; + // empty file is used to load failure so new calldata is generated + assert_ne!(initial_calldata, new_calldata); +} diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index e615e2785..969e67357 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -95,6 +95,8 @@ pub static TEST_OPTS: Lazy = Lazy::new(|| { max_calldata_fuzz_dictionary_addresses: 0, }, gas_report_samples: 256, + failure_persist_dir: Some(tempfile::tempdir().unwrap().into_path()), + failure_persist_file: Some("testfailure".to_string()), }) .invariant(InvariantConfig { runs: 256, @@ -126,6 +128,6 @@ pub fn fuzz_executor(executor: Executor) -> FuzzedExecutor { executor, proptest::test_runner::TestRunner::new(cfg), CALLER, - TEST_OPTS.fuzz, + TEST_OPTS.fuzz.clone(), ) } diff --git a/testdata/fuzz/FuzzFailurePersist.t.sol b/testdata/fuzz/FuzzFailurePersist.t.sol new file mode 100644 index 000000000..1f7c4829c --- /dev/null +++ b/testdata/fuzz/FuzzFailurePersist.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"; + +struct TestTuple { + address user; + uint256 amount; +} + +contract FuzzFailurePersistTest is DSTest { + Vm vm = Vm(HEVM_ADDRESS); + + function test_persist_fuzzed_failure( + uint256 x, + int256 y, + address addr, + bool cond, + string calldata test, + TestTuple calldata tuple, + address[] calldata addresses + ) public { + // dummy assume to trigger runs + vm.assume(x > 1 && x < 1111111111111111111111111111); + vm.assume(y > 1 && y < 1111111111111111111111111111); + require(false); + } +} From f218563dcc1d1f006ef85224403513a139072745 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Tue, 12 Mar 2024 19:36:01 +0400 Subject: [PATCH 061/430] fix(forge): do not re-execute script on resume when possible (#7361) * fix(forge): do not re-execute script on resume when possible * fmt * skip broadcasted --- crates/cheatcodes/src/script.rs | 5 ++ crates/forge/tests/cli/multi_script.rs | 1 + crates/script/src/broadcast.rs | 11 +-- crates/script/src/build.rs | 112 ++++++++++++++++++++++++- crates/script/src/lib.rs | 79 ++++++++--------- crates/script/src/resume.rs | 106 ----------------------- crates/script/src/simulate.rs | 2 - crates/script/src/verify.rs | 9 +- 8 files changed, 159 insertions(+), 166 deletions(-) delete mode 100644 crates/script/src/resume.rs diff --git a/crates/cheatcodes/src/script.rs b/crates/cheatcodes/src/script.rs index 4b3e6ba48..ff4e3a148 100644 --- a/crates/cheatcodes/src/script.rs +++ b/crates/cheatcodes/src/script.rs @@ -111,6 +111,11 @@ impl ScriptWallets { self.inner.lock().multi_wallet.add_signer(WalletSigner::from_private_key(private_key)?); Ok(Default::default()) } + + /// Locks inner Mutex and returns all signer addresses in the [MultiWallet]. + pub fn signers(&self) -> Result> { + Ok(self.inner.lock().multi_wallet.signers()?.keys().cloned().collect()) + } } /// Sets up broadcasting from a script using `new_origin` as the sender. diff --git a/crates/forge/tests/cli/multi_script.rs b/crates/forge/tests/cli/multi_script.rs index 121fa9862..d6f7628da 100644 --- a/crates/forge/tests/cli/multi_script.rs +++ b/crates/forge/tests/cli/multi_script.rs @@ -61,5 +61,6 @@ forgetest_async!(can_resume_multi_chain_script, |prj, cmd| { .broadcast(ScriptOutcome::MissingWallet) .load_private_keys(&[0, 1]) .await + .arg("--multi") .resume(ScriptOutcome::OkBroadcast); }); diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index fb21276c2..224bcc44c 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -1,9 +1,6 @@ use crate::{ - build::LinkedBuildData, - execute::{ExecutionArtifacts, ExecutionData}, - sequence::ScriptSequenceKind, - verify::BroadcastedState, - ScriptArgs, ScriptConfig, + build::LinkedBuildData, sequence::ScriptSequenceKind, verify::BroadcastedState, ScriptArgs, + ScriptConfig, }; use super::receipts; @@ -170,8 +167,6 @@ pub struct BundledState { pub script_config: ScriptConfig, pub script_wallets: ScriptWallets, pub build_data: LinkedBuildData, - pub execution_data: ExecutionData, - pub execution_artifacts: ExecutionArtifacts, pub sequence: ScriptSequenceKind, } @@ -408,8 +403,6 @@ impl BundledState { args: self.args, script_config: self.script_config, build_data: self.build_data, - execution_data: self.execution_data, - execution_artifacts: self.execution_artifacts, sequence: self.sequence, }) } diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index 4dc78b0cd..41e898d96 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -1,11 +1,20 @@ -use crate::{execute::LinkedState, ScriptArgs, ScriptConfig}; +use crate::{ + broadcast::BundledState, + execute::LinkedState, + multi_sequence::MultiChainSequence, + sequence::{ScriptSequence, ScriptSequenceKind}, + ScriptArgs, ScriptConfig, +}; use alloy_primitives::{Address, Bytes}; +use ethers_providers::Middleware; use eyre::{Context, OptionExt, Result}; use foundry_cheatcodes::ScriptWallets; use foundry_cli::utils::get_cached_entry_by_name; use foundry_common::{ compile::{self, ContractSources, ProjectCompiler}, + provider::ethers::try_get_http_provider, + types::ToAlloy, ContractsByArtifact, }; use foundry_compilers::{ @@ -16,7 +25,7 @@ use foundry_compilers::{ ArtifactId, }; use foundry_linking::{LinkOutput, Linker}; -use std::str::FromStr; +use std::{str::FromStr, sync::Arc}; /// Container for the compiled contracts. pub struct BuildData { @@ -245,4 +254,103 @@ impl CompiledState { Ok(LinkedState { args, script_config, script_wallets, build_data }) } + + /// Tries loading the resumed state from the cache files, skipping simulation stage. + pub async fn resume(self) -> Result { + let chain = if self.args.multi { + None + } else { + let fork_url = self.script_config.evm_opts.fork_url.clone().ok_or_eyre("Missing --fork-url field, if you were trying to broadcast a multi-chain sequence, please use --multi flag")?; + let provider = Arc::new(try_get_http_provider(fork_url)?); + Some(provider.get_chainid().await?.as_u64()) + }; + + let sequence = match self.try_load_sequence(chain, false) { + Ok(sequence) => sequence, + Err(_) => { + // If the script was simulated, but there was no attempt to broadcast yet, + // try to read the script sequence from the `dry-run/` folder + let mut sequence = self.try_load_sequence(chain, true)?; + + // If sequence was in /dry-run, Update its paths so it is not saved into /dry-run + // this time as we are about to broadcast it. + sequence.update_paths_to_broadcasted( + &self.script_config.config, + &self.args.sig, + &self.build_data.target, + )?; + + sequence.save(true, true)?; + sequence + } + }; + + let (args, build_data, script_wallets, script_config) = if !self.args.unlocked { + let mut froms = sequence.sequences().iter().flat_map(|s| { + s.transactions + .iter() + .skip(s.receipts.len()) + .map(|t| t.transaction.from().expect("from is missing in script artifact")) + }); + + let available_signers = self + .script_wallets + .signers() + .map_err(|e| eyre::eyre!("Failed to get available signers: {}", e))?; + + if !froms.all(|from| available_signers.contains(&from.to_alloy())) { + // IF we are missing required signers, execute script as we might need to collect + // private keys from the execution. + let executed = self.link()?.prepare_execution().await?.execute().await?; + ( + executed.args, + executed.build_data.build_data, + executed.script_wallets, + executed.script_config, + ) + } else { + (self.args, self.build_data, self.script_wallets, self.script_config) + } + } else { + (self.args, self.build_data, self.script_wallets, self.script_config) + }; + + // Collect libraries from sequence and link contracts with them. + let libraries = match sequence { + ScriptSequenceKind::Single(ref seq) => Libraries::parse(&seq.libraries)?, + // Library linking is not supported for multi-chain sequences + ScriptSequenceKind::Multi(_) => Libraries::default(), + }; + + let linked_build_data = build_data.link_with_libraries(libraries)?; + + Ok(BundledState { + args, + script_config, + script_wallets, + build_data: linked_build_data, + sequence, + }) + } + + fn try_load_sequence(&self, chain: Option, dry_run: bool) -> Result { + if let Some(chain) = chain { + let sequence = ScriptSequence::load( + &self.script_config.config, + &self.args.sig, + &self.build_data.target, + chain, + dry_run, + )?; + Ok(ScriptSequenceKind::Single(sequence)) + } else { + let sequence = MultiChainSequence::load( + &self.script_config.config, + &self.args.sig, + &self.build_data.target, + dry_run, + )?; + Ok(ScriptSequenceKind::Multi(sequence)) + } + } } diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index ef579716f..010b5bbe9 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -58,7 +58,6 @@ mod execute; mod multi_sequence; mod providers; mod receipts; -mod resume; mod runner; mod sequence; mod simulate; @@ -219,49 +218,51 @@ impl ScriptArgs { pub async fn run_script(self) -> Result<()> { trace!(target: "script", "executing script command"); - // Drive state machine to point at which we have everything needed for simulation/resuming. - let pre_simulation = self - .preprocess() - .await? - .compile()? - .link()? - .prepare_execution() - .await? - .execute() - .await? - .prepare_simulation() - .await?; - - if pre_simulation.args.debug { - pre_simulation.run_debugger()?; - } + let compiled = self.preprocess().await?.compile()?; - if pre_simulation.args.json { - pre_simulation.show_json()?; + // Move from `CompiledState` to `BundledState` either by resuming or executing and + // simulating script. + let bundled = if compiled.args.resume || (compiled.args.verify && !compiled.args.broadcast) + { + compiled.resume().await? } else { - pre_simulation.show_traces().await?; - } + // Drive state machine to point at which we have everything needed for simulation. + let pre_simulation = compiled + .link()? + .prepare_execution() + .await? + .execute() + .await? + .prepare_simulation() + .await?; + + if pre_simulation.args.debug { + pre_simulation.run_debugger()?; + } - // Ensure that we have transactions to simulate/broadcast, otherwise exit early to avoid - // hard error. - if pre_simulation.execution_result.transactions.as_ref().map_or(true, |txs| txs.is_empty()) - { - return Ok(()); - } + if pre_simulation.args.json { + pre_simulation.show_json()?; + } else { + pre_simulation.show_traces().await?; + } - // Check if there are any missing RPCs and exit early to avoid hard error. - if pre_simulation.execution_artifacts.rpc_data.missing_rpc { - shell::println("\nIf you wish to simulate on-chain transactions pass a RPC URL.")?; - return Ok(()); - } + // Ensure that we have transactions to simulate/broadcast, otherwise exit early to avoid + // hard error. + if pre_simulation + .execution_result + .transactions + .as_ref() + .map_or(true, |txs| txs.is_empty()) + { + return Ok(()); + } + + // Check if there are any missing RPCs and exit early to avoid hard error. + if pre_simulation.execution_artifacts.rpc_data.missing_rpc { + shell::println("\nIf you wish to simulate on-chain transactions pass a RPC URL.")?; + return Ok(()); + } - // Move from `PreSimulationState` to `BundledState` either by resuming or simulating - // transactions. - let bundled = if pre_simulation.args.resume || - (pre_simulation.args.verify && !pre_simulation.args.broadcast) - { - pre_simulation.resume().await? - } else { pre_simulation.args.check_contract_sizes( &pre_simulation.execution_result, &pre_simulation.build_data.highlevel_known_contracts, diff --git a/crates/script/src/resume.rs b/crates/script/src/resume.rs deleted file mode 100644 index 4f704ed60..000000000 --- a/crates/script/src/resume.rs +++ /dev/null @@ -1,106 +0,0 @@ -use crate::{broadcast::BundledState, simulate::PreSimulationState}; - -use super::{ - multi_sequence::MultiChainSequence, - sequence::{ScriptSequence, ScriptSequenceKind}, -}; -use ethers_providers::Middleware; -use eyre::Result; -use foundry_common::provider::ethers::try_get_http_provider; -use foundry_compilers::artifacts::Libraries; -use std::sync::Arc; - -impl PreSimulationState { - /// Tries loading the resumed state from the cache files, skipping simulation stage. - pub async fn resume(mut self) -> Result { - if self.execution_artifacts.rpc_data.missing_rpc { - eyre::bail!("Missing `--fork-url` field.") - } - - let chain = match self.execution_artifacts.rpc_data.total_rpcs.len() { - 2.. => None, - 1 => { - let fork_url = self.execution_artifacts.rpc_data.total_rpcs.iter().next().unwrap(); - - let provider = Arc::new(try_get_http_provider(fork_url)?); - Some(provider.get_chainid().await?.as_u64()) - } - 0 => eyre::bail!("No RPC URLs"), - }; - - let sequence = match self.try_load_sequence(chain, false) { - Ok(sequence) => sequence, - Err(_) => { - // If the script was simulated, but there was no attempt to broadcast yet, - // try to read the script sequence from the `dry-run/` folder - let mut sequence = self.try_load_sequence(chain, true)?; - - // If sequence was in /dry-run, Update its paths so it is not saved into /dry-run - // this time as we are about to broadcast it. - sequence.update_paths_to_broadcasted( - &self.script_config.config, - &self.args.sig, - &self.build_data.build_data.target, - )?; - - sequence.save(true, true)?; - sequence - } - }; - - match sequence { - ScriptSequenceKind::Single(ref seq) => { - // We might have predeployed libraries from the broadcasting, so we need to - // relink the contracts with them, since their mapping is not included in the solc - // cache files. - self.build_data = self - .build_data - .build_data - .link_with_libraries(Libraries::parse(&seq.libraries)?)?; - } - // Library linking is not supported for multi-chain sequences - ScriptSequenceKind::Multi(_) => {} - } - - let Self { - args, - script_config, - script_wallets, - build_data, - execution_data, - execution_result: _, - execution_artifacts, - } = self; - - Ok(BundledState { - args, - script_config, - script_wallets, - build_data, - execution_data, - execution_artifacts, - sequence, - }) - } - - fn try_load_sequence(&self, chain: Option, dry_run: bool) -> Result { - if let Some(chain) = chain { - let sequence = ScriptSequence::load( - &self.script_config.config, - &self.args.sig, - &self.build_data.build_data.target, - chain, - dry_run, - )?; - Ok(ScriptSequenceKind::Single(sequence)) - } else { - let sequence = MultiChainSequence::load( - &self.script_config.config, - &self.args.sig, - &self.build_data.build_data.target, - dry_run, - )?; - Ok(ScriptSequenceKind::Multi(sequence)) - } - } -} diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index 99bd39ad9..07e962ddf 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -400,8 +400,6 @@ impl FilledTransactionsState { script_config: self.script_config, script_wallets: self.script_wallets, build_data: self.build_data, - execution_data: self.execution_data, - execution_artifacts: self.execution_artifacts, sequence, }) } diff --git a/crates/script/src/verify.rs b/crates/script/src/verify.rs index be5825dfc..217d880b0 100644 --- a/crates/script/src/verify.rs +++ b/crates/script/src/verify.rs @@ -1,9 +1,4 @@ -use crate::{ - build::LinkedBuildData, - execute::{ExecutionArtifacts, ExecutionData}, - sequence::ScriptSequenceKind, - ScriptArgs, ScriptConfig, -}; +use crate::{build::LinkedBuildData, sequence::ScriptSequenceKind, ScriptArgs, ScriptConfig}; use alloy_primitives::Address; use eyre::Result; @@ -21,8 +16,6 @@ pub struct BroadcastedState { pub args: ScriptArgs, pub script_config: ScriptConfig, pub build_data: LinkedBuildData, - pub execution_data: ExecutionData, - pub execution_artifacts: ExecutionArtifacts, pub sequence: ScriptSequenceKind, } From edb3a4b125510c1b24e40b69218aada87b73489d Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 12 Mar 2024 17:10:56 +0100 Subject: [PATCH 062/430] feat(anvil): support sub-second block time granularity (#7380) --- crates/anvil/src/anvil.rs | 2 +- crates/anvil/src/cmd.rs | 16 ++++++++++++---- crates/config/src/fix.rs | 6 +++++- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/crates/anvil/src/anvil.rs b/crates/anvil/src/anvil.rs index 5ac169c3a..8ce75751c 100644 --- a/crates/anvil/src/anvil.rs +++ b/crates/anvil/src/anvil.rs @@ -30,7 +30,7 @@ pub enum AnvilSubcommand { } #[tokio::main] -async fn main() -> Result<(), Box> { +async fn main() -> eyre::Result<()> { utils::load_dotenv(); let mut app = Anvil::parse(); diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index c16328ee2..508c3dc8e 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -83,8 +83,8 @@ pub struct NodeArgs { pub hardfork: Option, /// Block time in seconds for interval mining. - #[arg(short, long, visible_alias = "blockTime", value_name = "SECONDS")] - pub block_time: Option, + #[arg(short, long, visible_alias = "blockTime", value_name = "SECONDS", value_parser = duration_from_secs_f64)] + pub block_time: Option, /// Slots in an epoch #[arg(long, value_name = "SLOTS_IN_AN_EPOCH", default_value_t = 32)] @@ -198,7 +198,7 @@ impl NodeArgs { .disable_block_gas_limit(self.evm_opts.disable_block_gas_limit) .with_gas_price(self.evm_opts.gas_price.map(U256::from)) .with_hardfork(self.hardfork) - .with_blocktime(self.block_time.map(Duration::from_secs)) + .with_blocktime(self.block_time) .with_no_mining(self.no_mining) .with_account_generator(self.account_generator()) .with_genesis_balance(genesis_balance) @@ -269,7 +269,7 @@ impl NodeArgs { /// Starts the node /// /// See also [crate::spawn()] - pub async fn run(self) -> Result<(), Box> { + pub async fn run(self) -> eyre::Result<()> { let dump_state = self.dump_state_path(); let dump_interval = self.state_interval.map(Duration::from_secs).unwrap_or(DEFAULT_DUMP_INTERVAL); @@ -677,6 +677,14 @@ fn read_genesis_file(path: &str) -> Result { foundry_common::fs::read_json_file(path.as_ref()).map_err(|err| err.to_string()) } +fn duration_from_secs_f64(s: &str) -> Result { + let s = s.parse::().map_err(|e| e.to_string())?; + if s == 0.0 { + return Err("Duration must be greater than 0".to_string()); + } + Duration::try_from_secs_f64(s).map_err(|e| e.to_string()) +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/config/src/fix.rs b/crates/config/src/fix.rs index 086fbb7db..a0ce9fbc1 100644 --- a/crates/config/src/fix.rs +++ b/crates/config/src/fix.rs @@ -15,20 +15,24 @@ struct TomlFile { } impl TomlFile { - fn open(path: impl AsRef) -> Result> { + fn open(path: impl AsRef) -> eyre::Result { let path = path.as_ref().to_owned(); let doc = fs::read_to_string(&path)?.parse()?; Ok(Self { doc, path }) } + fn doc(&self) -> &toml_edit::Document { &self.doc } + fn doc_mut(&mut self) -> &mut toml_edit::Document { &mut self.doc } + fn path(&self) -> &Path { self.path.as_ref() } + fn save(&self) -> io::Result<()> { fs::write(self.path(), self.doc().to_string()) } From eef87de35375311f134fb4261c18455ced8022a5 Mon Sep 17 00:00:00 2001 From: Enrique Date: Tue, 12 Mar 2024 13:28:08 -0400 Subject: [PATCH 063/430] feat(`anvil`): add support for EIP4844 types (#7202) * feat(anvil-core): EIP4844 variant support * chore: proper support when converting txs * feat: add more type support * chore: lock * feat: missing type conversions, decoding test * use correct eip check * force no blob hashes for eip1559 * feat: support sidecar with 4844 types * fmt * feat: use main branch for consensus, update * chore: rename * lockfile * fmt * fmt --------- Co-authored-by: Matthias Seitz --- Cargo.lock | 4 + crates/anvil/Cargo.toml | 2 +- crates/anvil/core/Cargo.toml | 3 +- crates/anvil/core/src/eth/transaction/mod.rs | 224 ++++++++++++++++++- crates/anvil/src/eth/api.rs | 5 + crates/anvil/src/eth/backend/executor.rs | 8 + crates/anvil/src/eth/backend/mem/mod.rs | 18 ++ crates/anvil/src/eth/error.rs | 5 + crates/anvil/src/eth/fees.rs | 9 + crates/anvil/src/eth/sign.rs | 5 + 10 files changed, 274 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 113f04de8..8d5f0d37b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -96,6 +96,9 @@ dependencies = [ "alloy-network", "alloy-primitives", "alloy-rlp", + "c-kzg", + "sha2 0.10.8", + "thiserror", ] [[package]] @@ -595,6 +598,7 @@ dependencies = [ "alloy-rpc-types", "anvil-core", "bytes", + "c-kzg", "foundry-common", "foundry-evm", "hash-db", diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index 36607d126..430c9e906 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -40,7 +40,7 @@ trie-db = "0.23" hash-db = "0.15" memory-db = "0.29" alloy-primitives = { workspace = true, features = ["serde"] } -alloy-consensus.workspace = true +alloy-consensus = { workspace = true, features = ["k256", "kzg"] } alloy-network.workspace = true alloy-rlp.workspace = true alloy-signer = { workspace = true, features = ["eip712", "mnemonic"] } diff --git a/crates/anvil/core/Cargo.toml b/crates/anvil/core/Cargo.toml index 3b164a9d8..6b6041ce5 100644 --- a/crates/anvil/core/Cargo.toml +++ b/crates/anvil/core/Cargo.toml @@ -20,12 +20,13 @@ alloy-rpc-trace-types.workspace = true alloy-rlp.workspace = true alloy-eips.workspace = true alloy-network = { workspace = true, features = ["k256"] } -alloy-consensus.workspace = true +alloy-consensus = { workspace = true, features = ["k256", "kzg"]} alloy-dyn-abi = { workspace = true, features = ["std", "eip712"] } serde = { workspace = true, optional = true } serde_json.workspace = true bytes = "1.4" +c-kzg = { version = "0.4.2", features = ["serde"] } # trie hash-db = { version = "0.15", default-features = false } diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index afc16b928..72a88c547 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -4,7 +4,10 @@ use crate::eth::{ transaction::optimism::{DepositTransaction, DepositTransactionRequest}, utils::eip_to_revm_access_list, }; -use alloy_consensus::{ReceiptWithBloom, TxEip1559, TxEip2930, TxLegacy}; +use alloy_consensus::{ + BlobTransactionSidecar, ReceiptWithBloom, TxEip1559, TxEip2930, TxEip4844, TxEip4844Variant, + TxEip4844WithSidecar, TxLegacy, +}; use alloy_network::{Signed, Transaction, TxKind}; use alloy_primitives::{Address, Bloom, Bytes, Log, Signature, TxHash, B256, U128, U256, U64}; use alloy_rlp::{Decodable, Encodable}; @@ -39,11 +42,14 @@ pub fn transaction_request_to_typed(tx: TransactionRequest) -> Option Option { + (Some(0), _, None, None, None, None, None, None) | + (None, Some(_), None, None, None, None, None, None) => { Some(TypedTransactionRequest::Legacy(TxLegacy { nonce: nonce.unwrap_or_default().to::(), gas_price: gas_price.unwrap_or_default().to::(), @@ -87,7 +97,8 @@ pub fn transaction_request_to_typed(tx: TransactionRequest) -> Option { + (Some(1), _, None, None, _, None, None, None) | + (None, _, None, None, Some(_), None, None, None) => { Some(TypedTransactionRequest::EIP2930(TxEip2930 { nonce: nonce.unwrap_or_default().to::(), gas_price: gas_price.unwrap_or_default().to(), @@ -103,10 +114,10 @@ pub fn transaction_request_to_typed(tx: TransactionRequest) -> Option { + (Some(2), None, _, _, _, _, None, None) | + (None, None, Some(_), _, _, _, None, None) | + (None, None, _, Some(_), _, _, None, None) | + (None, None, None, None, None, _, None, None) => { // Empty fields fall back to the canonical transaction schema. Some(TypedTransactionRequest::EIP1559(TxEip1559 { nonce: nonce.unwrap_or_default().to::(), @@ -123,6 +134,45 @@ pub fn transaction_request_to_typed(tx: TransactionRequest) -> Option { + let tx = TxEip4844 { + nonce: nonce.unwrap_or_default().to::(), + max_fee_per_gas: max_fee_per_gas.unwrap_or_default().to::(), + max_priority_fee_per_gas: max_priority_fee_per_gas.unwrap_or_default().to::(), + max_fee_per_blob_gas: max_fee_per_blob_gas.unwrap_or_default().to::(), + gas_limit: gas.unwrap_or_default().to::(), + value: value.unwrap_or(U256::ZERO), + input: input.into_input().unwrap_or_default(), + to: match to { + Some(to) => TxKind::Call(to), + None => TxKind::Create, + }, + chain_id: 0, + access_list: to_eip_access_list(access_list.unwrap_or_default()), + blob_versioned_hashes: blob_versioned_hashes.unwrap_or_default(), + }; + let blob_sidecar = BlobTransactionSidecar { + blobs: sidecar + .blobs + .into_iter() + .map(|b| c_kzg::Blob::from_bytes(b.as_slice()).unwrap()) + .collect(), + commitments: sidecar + .commitments + .into_iter() + .map(|c| c_kzg::Bytes48::from_bytes(c.as_slice()).unwrap()) + .collect(), + proofs: sidecar + .proofs + .into_iter() + .map(|p| c_kzg::Bytes48::from_bytes(p.as_slice()).unwrap()) + .collect(), + }; + Some(TypedTransactionRequest::EIP4844(TxEip4844Variant::TxEip4844WithSidecar( + TxEip4844WithSidecar::from_tx_and_sidecar(tx, blob_sidecar), + ))) + } _ => None, } } @@ -132,6 +182,7 @@ pub enum TypedTransactionRequest { Legacy(TxLegacy), EIP2930(TxEip2930), EIP1559(TxEip1559), + EIP4844(TxEip4844Variant), Deposit(DepositTransactionRequest), } @@ -316,6 +367,33 @@ pub fn to_alloy_transaction_with_hash_and_sender( blob_versioned_hashes: vec![], other: Default::default(), }, + TypedTransaction::EIP4844(t) => RpcTransaction { + hash, + nonce: U64::from(t.tx().tx().nonce), + block_hash: None, + block_number: None, + transaction_index: None, + from, + to: None, + value: t.tx().tx().value, + gas_price: Some(U128::from(t.tx().tx().max_fee_per_gas)), + max_fee_per_gas: Some(U128::from(t.tx().tx().max_fee_per_gas)), + max_priority_fee_per_gas: Some(U128::from(t.tx().tx().max_priority_fee_per_gas)), + gas: U256::from(t.tx().tx().gas_limit), + input: t.tx().tx().input.clone(), + chain_id: Some(U64::from(t.tx().tx().chain_id)), + signature: Some(RpcSignature { + r: t.signature().r(), + s: t.signature().s(), + v: U256::from(t.signature().v().y_parity_byte()), + y_parity: Some(alloy_rpc_types::Parity::from(t.signature().v().y_parity())), + }), + access_list: Some(from_eip_to_alloy_access_list(t.tx().tx().access_list.clone()).0), + transaction_type: Some(U64::from(3)), + max_fee_per_blob_gas: Some(U128::from(t.tx().tx().max_fee_per_blob_gas)), + blob_versioned_hashes: t.tx().tx().blob_versioned_hashes.clone(), + other: Default::default(), + }, TypedTransaction::Deposit(t) => RpcTransaction { hash, nonce: U64::from(t.nonce), @@ -463,6 +541,37 @@ impl PendingTransaction { ..Default::default() } } + TypedTransaction::EIP4844(tx) => { + let TxEip4844 { + chain_id, + nonce, + max_fee_per_blob_gas, + max_fee_per_gas, + max_priority_fee_per_gas, + gas_limit, + to, + value, + input, + access_list, + blob_versioned_hashes, + .. + } = tx.tx().tx(); + TxEnv { + caller, + transact_to: transact_to(to), + data: alloy_primitives::Bytes(input.0.clone()), + chain_id: Some(*chain_id), + nonce: Some(*nonce), + value: *value, + gas_price: U256::from(*max_fee_per_gas), + gas_priority_fee: Some(U256::from(*max_priority_fee_per_gas)), + max_fee_per_blob_gas: Some(U256::from(*max_fee_per_blob_gas)), + blob_hashes: blob_versioned_hashes.clone(), + gas_limit: *gas_limit, + access_list: eip_to_revm_access_list(access_list.0.clone()), + ..Default::default() + } + } TypedTransaction::Deposit(tx) => { let chain_id = tx.chain_id(); let DepositTransaction { @@ -509,6 +618,8 @@ pub enum TypedTransaction { EIP2930(Signed), /// EIP-1559 transaction EIP1559(Signed), + /// EIP-4844 transaction + EIP4844(Signed), /// op-stack deposit transaction Deposit(DepositTransaction), } @@ -524,6 +635,7 @@ impl TypedTransaction { TypedTransaction::Legacy(tx) => tx.gas_price, TypedTransaction::EIP2930(tx) => tx.gas_price, TypedTransaction::EIP1559(tx) => tx.max_fee_per_gas, + TypedTransaction::EIP4844(tx) => tx.tx().tx().max_fee_per_blob_gas, TypedTransaction::Deposit(_) => 0, }) } @@ -533,6 +645,7 @@ impl TypedTransaction { TypedTransaction::Legacy(tx) => tx.gas_limit, TypedTransaction::EIP2930(tx) => tx.gas_limit, TypedTransaction::EIP1559(tx) => tx.gas_limit, + TypedTransaction::EIP4844(tx) => tx.tx().tx().gas_limit, TypedTransaction::Deposit(tx) => tx.gas_limit.to::(), }) } @@ -542,6 +655,7 @@ impl TypedTransaction { TypedTransaction::Legacy(tx) => tx.value, TypedTransaction::EIP2930(tx) => tx.value, TypedTransaction::EIP1559(tx) => tx.value, + TypedTransaction::EIP4844(tx) => tx.tx().tx().value, TypedTransaction::Deposit(tx) => tx.value, }) } @@ -551,6 +665,7 @@ impl TypedTransaction { TypedTransaction::Legacy(tx) => &tx.input, TypedTransaction::EIP2930(tx) => &tx.input, TypedTransaction::EIP1559(tx) => &tx.input, + TypedTransaction::EIP4844(tx) => &tx.tx().tx().input, TypedTransaction::Deposit(tx) => &tx.input, } } @@ -561,6 +676,7 @@ impl TypedTransaction { TypedTransaction::Legacy(_) => None, TypedTransaction::EIP2930(_) => Some(1), TypedTransaction::EIP1559(_) => Some(2), + TypedTransaction::EIP4844(_) => Some(3), TypedTransaction::Deposit(_) => Some(0x7E), } } @@ -581,6 +697,8 @@ impl TypedTransaction { gas_price: Some(U256::from(t.tx().gas_price)), max_fee_per_gas: None, max_priority_fee_per_gas: None, + max_fee_per_blob_gas: None, + blob_versioned_hashes: None, value: t.value, chain_id: t.tx().chain_id, access_list: Default::default(), @@ -593,6 +711,8 @@ impl TypedTransaction { gas_price: Some(U256::from(t.tx().gas_price)), max_fee_per_gas: None, max_priority_fee_per_gas: None, + max_fee_per_blob_gas: None, + blob_versioned_hashes: None, value: t.value, chain_id: Some(t.chain_id), access_list: to_alloy_access_list(t.access_list.clone()), @@ -605,10 +725,26 @@ impl TypedTransaction { gas_price: None, max_fee_per_gas: Some(U256::from(t.max_fee_per_gas)), max_priority_fee_per_gas: Some(U256::from(t.max_priority_fee_per_gas)), + max_fee_per_blob_gas: None, + blob_versioned_hashes: None, value: t.value, chain_id: Some(t.chain_id), access_list: to_alloy_access_list(t.access_list.clone()), }, + TypedTransaction::EIP4844(t) => TransactionEssentials { + kind: t.tx().tx().to, + input: t.tx().tx().input.clone(), + nonce: U256::from(t.tx().tx().nonce), + gas_limit: U256::from(t.tx().tx().gas_limit), + gas_price: Some(U256::from(t.tx().tx().max_fee_per_blob_gas)), + max_fee_per_gas: Some(U256::from(t.tx().tx().max_fee_per_gas)), + max_priority_fee_per_gas: Some(U256::from(t.tx().tx().max_priority_fee_per_gas)), + max_fee_per_blob_gas: Some(U256::from(t.tx().tx().max_fee_per_blob_gas)), + blob_versioned_hashes: Some(t.tx().tx().blob_versioned_hashes.clone()), + value: t.tx().tx().value, + chain_id: Some(t.tx().tx().chain_id), + access_list: to_alloy_access_list(t.tx().tx().access_list.clone()), + }, TypedTransaction::Deposit(t) => TransactionEssentials { kind: t.kind, input: t.input.clone(), @@ -617,6 +753,8 @@ impl TypedTransaction { gas_price: Some(U256::from(0)), max_fee_per_gas: None, max_priority_fee_per_gas: None, + max_fee_per_blob_gas: None, + blob_versioned_hashes: None, value: t.value, chain_id: t.chain_id(), access_list: Default::default(), @@ -629,6 +767,7 @@ impl TypedTransaction { TypedTransaction::Legacy(t) => U256::from(t.nonce), TypedTransaction::EIP2930(t) => U256::from(t.nonce), TypedTransaction::EIP1559(t) => U256::from(t.nonce), + TypedTransaction::EIP4844(t) => U256::from(t.tx().tx().nonce), TypedTransaction::Deposit(t) => U256::from(t.nonce), } } @@ -638,6 +777,7 @@ impl TypedTransaction { TypedTransaction::Legacy(t) => t.chain_id, TypedTransaction::EIP2930(t) => Some(t.chain_id), TypedTransaction::EIP1559(t) => Some(t.chain_id), + TypedTransaction::EIP4844(t) => Some(t.tx().tx().chain_id), TypedTransaction::Deposit(t) => t.chain_id(), } } @@ -659,6 +799,16 @@ impl TypedTransaction { matches!(self, TypedTransaction::EIP1559(_)) } + /// Returns true whether this tx is a EIP2930 transaction + pub fn is_eip2930(&self) -> bool { + matches!(self, TypedTransaction::EIP2930(_)) + } + + /// Returns true whether this tx is a EIP4844 transaction + pub fn is_eip4844(&self) -> bool { + matches!(self, TypedTransaction::EIP4844(_)) + } + /// Returns the hash of the transaction. /// /// Note: If this transaction has the Impersonated signature then this returns a modified unique @@ -668,6 +818,7 @@ impl TypedTransaction { TypedTransaction::Legacy(t) => *t.hash(), TypedTransaction::EIP2930(t) => *t.hash(), TypedTransaction::EIP1559(t) => *t.hash(), + TypedTransaction::EIP4844(t) => *t.hash(), TypedTransaction::Deposit(t) => t.hash(), } } @@ -695,6 +846,7 @@ impl TypedTransaction { TypedTransaction::Legacy(tx) => tx.recover_signer(), TypedTransaction::EIP2930(tx) => tx.recover_signer(), TypedTransaction::EIP1559(tx) => tx.recover_signer(), + TypedTransaction::EIP4844(tx) => tx.recover_signer(), TypedTransaction::Deposit(tx) => tx.recover(), } } @@ -705,6 +857,7 @@ impl TypedTransaction { TypedTransaction::Legacy(tx) => &tx.to, TypedTransaction::EIP2930(tx) => &tx.to, TypedTransaction::EIP1559(tx) => &tx.to, + TypedTransaction::EIP4844(tx) => &tx.tx().tx().to, TypedTransaction::Deposit(tx) => &tx.kind, } } @@ -720,6 +873,7 @@ impl TypedTransaction { TypedTransaction::Legacy(tx) => *tx.signature(), TypedTransaction::EIP2930(tx) => *tx.signature(), TypedTransaction::EIP1559(tx) => *tx.signature(), + TypedTransaction::EIP4844(tx) => *tx.signature(), TypedTransaction::Deposit(_) => Signature::from_scalars_and_parity( B256::with_last_byte(1), B256::with_last_byte(1), @@ -736,6 +890,7 @@ impl Encodable for TypedTransaction { TypedTransaction::Legacy(tx) => tx.encode(out), TypedTransaction::EIP2930(tx) => tx.encode(out), TypedTransaction::EIP1559(tx) => tx.encode(out), + TypedTransaction::EIP4844(tx) => tx.encode(out), TypedTransaction::Deposit(tx) => tx.encode(out), } } @@ -774,6 +929,10 @@ impl Decodable for TypedTransaction { } else if tx_type == 0x02 { buf.advance(1); as Decodable>::decode(buf).map(TypedTransaction::EIP1559) + } else if tx_type == 0x03 { + buf.advance(1); + as Decodable>::decode(buf) + .map(TypedTransaction::EIP4844) } else if tx_type == 0x7E { buf.advance(1); ::decode(buf).map(TypedTransaction::Deposit) @@ -800,6 +959,8 @@ pub struct TransactionEssentials { pub gas_price: Option, pub max_fee_per_gas: Option, pub max_priority_fee_per_gas: Option, + pub max_fee_per_blob_gas: Option, + pub blob_versioned_hashes: Option>, pub value: U256, pub chain_id: Option, pub access_list: AccessList, @@ -826,6 +987,7 @@ pub enum TypedReceipt { Legacy(ReceiptWithBloom), EIP2930(ReceiptWithBloom), EIP1559(ReceiptWithBloom), + EIP4844(ReceiptWithBloom), Deposit(ReceiptWithBloom), } @@ -835,6 +997,7 @@ impl TypedReceipt { TypedReceipt::Legacy(r) | TypedReceipt::EIP1559(r) | TypedReceipt::EIP2930(r) | + TypedReceipt::EIP4844(r) | TypedReceipt::Deposit(r) => U256::from(r.receipt.cumulative_gas_used), } } @@ -844,6 +1007,7 @@ impl TypedReceipt { TypedReceipt::Legacy(r) | TypedReceipt::EIP1559(r) | TypedReceipt::EIP2930(r) | + TypedReceipt::EIP4844(r) | TypedReceipt::Deposit(r) => &r.bloom, } } @@ -855,6 +1019,7 @@ impl From for ReceiptWithBloom { TypedReceipt::Legacy(r) | TypedReceipt::EIP1559(r) | TypedReceipt::EIP2930(r) | + TypedReceipt::EIP4844(r) | TypedReceipt::Deposit(r) => r, } } @@ -870,6 +1035,7 @@ impl Encodable for TypedReceipt { let payload_len = match receipt { TypedReceipt::EIP2930(r) => r.length() + 1, TypedReceipt::EIP1559(r) => r.length() + 1, + TypedReceipt::EIP4844(r) => r.length() + 1, TypedReceipt::Deposit(r) => r.length() + 1, _ => unreachable!("receipt already matched"), }; @@ -885,6 +1051,11 @@ impl Encodable for TypedReceipt { 2u8.encode(out); r.encode(out); } + TypedReceipt::EIP4844(r) => { + Header { list: true, payload_length: payload_len }.encode(out); + 3u8.encode(out); + r.encode(out); + } TypedReceipt::Deposit(r) => { Header { list: true, payload_length: payload_len }.encode(out); 0x7Eu8.encode(out); @@ -923,6 +1094,9 @@ impl Decodable for TypedReceipt { } else if receipt_type == 0x02 { buf.advance(1); ::decode(buf).map(TypedReceipt::EIP1559) + } else if receipt_type == 0x03 { + buf.advance(1); + ::decode(buf).map(TypedReceipt::EIP4844) } else if receipt_type == 0x7E { buf.advance(1); ::decode(buf).map(TypedReceipt::Deposit) @@ -1038,6 +1212,42 @@ mod tests { ); } + // Test vector from https://sepolia.etherscan.io/tx/0x9a22ccb0029bc8b0ddd073be1a1d923b7ae2b2ea52100bae0db4424f9107e9c0 + // Blobscan: https://sepolia.blobscan.com/tx/0x9a22ccb0029bc8b0ddd073be1a1d923b7ae2b2ea52100bae0db4424f9107e9c0 + #[test] + fn test_decode_live_4844_tx() { + use alloy_primitives::{address, b256}; + + // https://sepolia.etherscan.io/getRawTx?tx=0x9a22ccb0029bc8b0ddd073be1a1d923b7ae2b2ea52100bae0db4424f9107e9c0 + let raw_tx = alloy_primitives::hex::decode("0x03f9011d83aa36a7820fa28477359400852e90edd0008252089411e9ca82a3a762b4b5bd264d4173a242e7a770648080c08504a817c800f8a5a0012ec3d6f66766bedb002a190126b3549fce0047de0d4c25cffce0dc1c57921aa00152d8e24762ff22b1cfd9f8c0683786a7ca63ba49973818b3d1e9512cd2cec4a0013b98c6c83e066d5b14af2b85199e3d4fc7d1e778dd53130d180f5077e2d1c7a001148b495d6e859114e670ca54fb6e2657f0cbae5b08063605093a4b3dc9f8f1a0011ac212f13c5dff2b2c6b600a79635103d6f580a4221079951181b25c7e654901a0c8de4cced43169f9aa3d36506363b2d2c44f6c49fc1fd91ea114c86f3757077ea01e11fdd0d1934eda0492606ee0bb80a7bf8f35cc5f86ec60fe5031ba48bfd544").unwrap(); + let res = TypedTransaction::decode(&mut raw_tx.as_slice()).unwrap(); + assert_eq!(res.r#type(), Some(3)); + + let tx = match res { + TypedTransaction::EIP4844(tx) => tx, + _ => unreachable!(), + }; + + assert_eq!( + tx.tx().tx().to, + TxKind::Call(address!("11E9CA82A3a762b4B5bd264d4173a242e7a77064")) + ); + + assert_eq!( + tx.tx().tx().blob_versioned_hashes, + vec![ + b256!("012ec3d6f66766bedb002a190126b3549fce0047de0d4c25cffce0dc1c57921a"), + b256!("0152d8e24762ff22b1cfd9f8c0683786a7ca63ba49973818b3d1e9512cd2cec4"), + b256!("013b98c6c83e066d5b14af2b85199e3d4fc7d1e778dd53130d180f5077e2d1c7"), + b256!("01148b495d6e859114e670ca54fb6e2657f0cbae5b08063605093a4b3dc9f8f1"), + b256!("011ac212f13c5dff2b2c6b600a79635103d6f580a4221079951181b25c7e6549") + ] + ); + + let from = tx.recover_signer().unwrap(); + assert_eq!(from, address!("A83C816D4f9b2783761a22BA6FADB0eB0606D7B2")); + } + #[test] fn can_recover_sender_not_normalized() { let bytes = hex::decode("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap(); diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index f12904477..a9b2cd8ab 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -2585,6 +2585,7 @@ impl EthApi { match &tx { TypedTransaction::EIP2930(_) => self.backend.ensure_eip2930_active(), TypedTransaction::EIP1559(_) => self.backend.ensure_eip1559_active(), + TypedTransaction::EIP4844(_) => self.backend.ensure_eip4844_active(), TypedTransaction::Deposit(_) => self.backend.ensure_op_deposits_active(), TypedTransaction::Legacy(_) => Ok(()), } @@ -2674,6 +2675,10 @@ fn determine_base_gas_by_kind(request: &TransactionRequest) -> U256 { TxKind::Call(_) => MIN_TRANSACTION_GAS, TxKind::Create => MIN_CREATE_GAS, }, + TypedTransactionRequest::EIP4844(req) => match req.tx().to { + TxKind::Call(_) => MIN_TRANSACTION_GAS, + TxKind::Create => MIN_CREATE_GAS, + }, TypedTransactionRequest::Deposit(req) => match req.kind { TxKind::Call(_) => MIN_TRANSACTION_GAS, TxKind::Create => MIN_CREATE_GAS, diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index 45fc1f112..320d46884 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -76,6 +76,14 @@ impl ExecutedTransaction { }, bloom, }), + TypedTransaction::EIP4844(_) => TypedReceipt::EIP4844(ReceiptWithBloom { + receipt: Receipt { + success: status_code == 1, + cumulative_gas_used: used_gas.to::(), + logs, + }, + bloom, + }), TypedTransaction::Deposit(_) => TypedReceipt::Deposit(ReceiptWithBloom { receipt: Receipt { success: status_code == 1, diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 709932438..837b6482d 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -578,6 +578,11 @@ impl Backend { (self.spec_id() as u8) >= (SpecId::BERLIN as u8) } + /// Returns true for post Cancun + pub fn is_eip4844(&self) -> bool { + (self.spec_id() as u8) >= (SpecId::CANCUN as u8) + } + /// Returns true if op-stack deposits are active pub fn is_optimism(&self) -> bool { self.env.read().handler_cfg.is_optimism @@ -599,6 +604,13 @@ impl Backend { Err(BlockchainError::EIP2930TransactionUnsupportedAtHardfork) } + pub fn ensure_eip4844_active(&self) -> Result<(), BlockchainError> { + if self.is_eip4844() { + return Ok(()); + } + Err(BlockchainError::EIP4844TransactionUnsupportedAtHardfork) + } + /// Returns an error if op-stack deposits are not active pub fn ensure_op_deposits_active(&self) -> Result<(), BlockchainError> { if self.is_optimism() { @@ -1972,6 +1984,12 @@ impl Backend { .map_or(self.base_fee().to::(), |b| b as u128) .checked_add(t.max_priority_fee_per_gas) .unwrap_or(u128::MAX), + TypedTransaction::EIP4844(t) => block + .header + .base_fee_per_gas + .map_or(self.base_fee().to::(), |b| b as u128) + .checked_add(t.tx().tx().max_priority_fee_per_gas) + .unwrap_or(u128::MAX), TypedTransaction::Deposit(_) => 0_u128, }; diff --git a/crates/anvil/src/eth/error.rs b/crates/anvil/src/eth/error.rs index 39e1f52a4..574365039 100644 --- a/crates/anvil/src/eth/error.rs +++ b/crates/anvil/src/eth/error.rs @@ -81,6 +81,8 @@ pub enum BlockchainError { EIP1559TransactionUnsupportedAtHardfork, #[error("Access list received but is not supported by the current hardfork.\n\nYou can use it by running anvil with '--hardfork berlin' or later.")] EIP2930TransactionUnsupportedAtHardfork, + #[error("EIP-4844 fields received but is not supported by the current hardfork.\n\nYou can use it by running anvil with '--hardfork cancun' or later.")] + EIP4844TransactionUnsupportedAtHardfork, #[error("op-stack deposit tx received but is not supported.\n\nYou can use it by running anvil with '--optimism'.")] DepositTransactionUnsupported, #[error("Excess blob gas not set.")] @@ -408,6 +410,9 @@ impl ToRpcResponseResult for Result { err @ BlockchainError::EIP2930TransactionUnsupportedAtHardfork => { RpcError::invalid_params(err.to_string()) } + err @ BlockchainError::EIP4844TransactionUnsupportedAtHardfork => { + RpcError::invalid_params(err.to_string()) + } err @ BlockchainError::DepositTransactionUnsupported => { RpcError::invalid_params(err.to_string()) } diff --git a/crates/anvil/src/eth/fees.rs b/crates/anvil/src/eth/fees.rs index 4f341858b..ef2d38eca 100644 --- a/crates/anvil/src/eth/fees.rs +++ b/crates/anvil/src/eth/fees.rs @@ -259,6 +259,15 @@ impl FeeHistoryService { .min(U256::from(t.max_fee_per_gas).saturating_sub(base_fee)) .to::() } + // TODO: This probably needs to be extended to extract 4844 info. + Some(TypedTransaction::EIP4844(t)) => { + U256::from(t.tx().tx().max_priority_fee_per_gas) + .min( + U256::from(t.tx().tx().max_fee_per_gas) + .saturating_sub(base_fee), + ) + .to::() + } Some(TypedTransaction::Deposit(_)) => 0, None => 0, }; diff --git a/crates/anvil/src/eth/sign.rs b/crates/anvil/src/eth/sign.rs index 34b3fa285..4886d96f0 100644 --- a/crates/anvil/src/eth/sign.rs +++ b/crates/anvil/src/eth/sign.rs @@ -107,6 +107,7 @@ impl Signer for DevSigner { TypedTransactionRequest::Legacy(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?), TypedTransactionRequest::EIP2930(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?), TypedTransactionRequest::EIP1559(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?), + TypedTransactionRequest::EIP4844(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?), TypedTransactionRequest::Deposit(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?), } } @@ -134,6 +135,10 @@ pub fn build_typed_transaction( let sighash = tx.signature_hash(); TypedTransaction::EIP1559(Signed::new_unchecked(tx, signature, sighash)) } + TypedTransactionRequest::EIP4844(tx) => { + let sighash = tx.signature_hash(); + TypedTransaction::EIP4844(Signed::new_unchecked(tx, signature, sighash)) + } TypedTransactionRequest::Deposit(tx) => { let DepositTransactionRequest { from, From 4fa0fa1671d3955984b96dd1fa3eae09233c550f Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 12 Mar 2024 22:59:47 +0100 Subject: [PATCH 064/430] chore: use Bytes to store calldata (#7383) --- crates/debugger/src/tui/draw.rs | 6 +++--- crates/evm/core/src/debug.rs | 4 ++-- crates/evm/evm/src/inspectors/debugger.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/debugger/src/tui/draw.rs b/crates/debugger/src/tui/draw.rs index d06e5c231..41eca5e76 100644 --- a/crates/debugger/src/tui/draw.rs +++ b/crates/debugger/src/tui/draw.rs @@ -503,9 +503,9 @@ impl DebuggerContext<'_> { fn draw_buffer(&self, f: &mut Frame<'_>, area: Rect) { let step = self.current_step(); let buf = match self.active_buffer { - BufferKind::Memory => &step.memory, - BufferKind::Calldata => &step.calldata, - BufferKind::Returndata => &step.returndata, + BufferKind::Memory => step.memory.as_ref(), + BufferKind::Calldata => step.calldata.as_ref(), + BufferKind::Returndata => step.returndata.as_ref(), }; let min_len = hex_digits(buf.len()); diff --git a/crates/evm/core/src/debug.rs b/crates/evm/core/src/debug.rs index d6f459339..3866dfe0c 100644 --- a/crates/evm/core/src/debug.rs +++ b/crates/evm/core/src/debug.rs @@ -1,4 +1,4 @@ -use alloy_primitives::{Address, U256}; +use alloy_primitives::{Address, Bytes, U256}; use revm::interpreter::OpCode; use revm_inspectors::tracing::types::CallKind; use serde::{Deserialize, Serialize}; @@ -170,7 +170,7 @@ pub struct DebugStep { /// Memory *prior* to running the associated opcode pub memory: Vec, /// Calldata *prior* to running the associated opcode - pub calldata: Vec, + pub calldata: Bytes, /// Returndata *prior* to running the associated opcode pub returndata: Vec, /// Opcode to be executed diff --git a/crates/evm/evm/src/inspectors/debugger.rs b/crates/evm/evm/src/inspectors/debugger.rs index 222e6de3b..8d7c7b116 100644 --- a/crates/evm/evm/src/inspectors/debugger.rs +++ b/crates/evm/evm/src/inspectors/debugger.rs @@ -76,7 +76,7 @@ impl Inspector for Debugger { pc, stack: interp.stack().data().clone(), memory: interp.shared_memory.context_memory().to_vec(), - calldata: interp.contract().input.to_vec(), + calldata: interp.contract().input.clone(), returndata: interp.return_data_buffer.to_vec(), instruction: Instruction::OpCode(op), push_bytes, From 46889b1ab0fb50a73537c5292df7cdeb22202553 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 12 Mar 2024 23:11:50 +0100 Subject: [PATCH 065/430] chore: retry 429 errors (#7384) --- crates/common/src/provider/retry.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/common/src/provider/retry.rs b/crates/common/src/provider/retry.rs index 2137dedbd..e7277bd4b 100644 --- a/crates/common/src/provider/retry.rs +++ b/crates/common/src/provider/retry.rs @@ -79,6 +79,12 @@ fn should_retry_transport_level_error(error: &TransportErrorKind) -> bool { match error { // Missing batch response errors can be retried. TransportErrorKind::MissingBatchResponse(_) => true, + TransportErrorKind::Custom(err) => { + // currently http error responses are not standard in alloy + let msg = err.to_string(); + msg.contains("429 Too Many Requests") + } + // If the backend is gone, or there's a completely custom error, we should assume it's not // retryable. _ => false, From b2f9346ef75810a48f776a36ad1bfb3b873c3514 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 12 Mar 2024 23:37:20 +0100 Subject: [PATCH 066/430] chore: reuse unmodified step memory (#7385) * chore: only record changed memory * chore: only record changed memory * chore: use returndata Bytes * clippy * clippy --- crates/evm/core/src/debug.rs | 21 +++++++++++++++++-- crates/evm/core/src/lib.rs | 1 + crates/evm/core/src/opcodes.rs | 25 +++++++++++++++++++++++ crates/evm/evm/src/inspectors/debugger.rs | 20 ++++++++++++++++-- 4 files changed, 63 insertions(+), 4 deletions(-) create mode 100644 crates/evm/core/src/opcodes.rs diff --git a/crates/evm/core/src/debug.rs b/crates/evm/core/src/debug.rs index 3866dfe0c..084d36b90 100644 --- a/crates/evm/core/src/debug.rs +++ b/crates/evm/core/src/debug.rs @@ -1,3 +1,4 @@ +use crate::opcodes; use alloy_primitives::{Address, Bytes, U256}; use revm::interpreter::OpCode; use revm_inspectors::tracing::types::CallKind; @@ -168,11 +169,11 @@ pub struct DebugStep { /// Stack *prior* to running the associated opcode pub stack: Vec, /// Memory *prior* to running the associated opcode - pub memory: Vec, + pub memory: Bytes, /// Calldata *prior* to running the associated opcode pub calldata: Bytes, /// Returndata *prior* to running the associated opcode - pub returndata: Vec, + pub returndata: Bytes, /// Opcode to be executed pub instruction: Instruction, /// Optional bytes that are being pushed onto the stack @@ -210,6 +211,11 @@ impl DebugStep { self.instruction.to_string() } } + + /// Returns `true` if the opcode modifies memory. + pub fn opcode_modifies_memory(&self) -> bool { + self.instruction.opcode().and_then(OpCode::new).map_or(false, opcodes::modifies_memory) + } } #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] @@ -249,3 +255,14 @@ impl Display for Instruction { } } } + +impl Instruction { + /// Returns the opcode of the instruction, if it is an opcode. + #[inline] + pub fn opcode(&self) -> Option { + match self { + Instruction::OpCode(op) => Some(*op), + _ => None, + } + } +} diff --git a/crates/evm/core/src/lib.rs b/crates/evm/core/src/lib.rs index caafa8e0f..9d26c9421 100644 --- a/crates/evm/core/src/lib.rs +++ b/crates/evm/core/src/lib.rs @@ -15,6 +15,7 @@ pub mod constants; pub mod debug; pub mod decode; pub mod fork; +pub mod opcodes; pub mod opts; pub mod snapshot; pub mod utils; diff --git a/crates/evm/core/src/opcodes.rs b/crates/evm/core/src/opcodes.rs new file mode 100644 index 000000000..3251036c7 --- /dev/null +++ b/crates/evm/core/src/opcodes.rs @@ -0,0 +1,25 @@ +//! Opcode utils + +use revm::interpreter::OpCode; + +/// Returns true if the opcode modifies memory. +/// +/// +#[inline] +pub const fn modifies_memory(opcode: OpCode) -> bool { + matches!( + opcode, + OpCode::EXTCODECOPY | + OpCode::MLOAD | + OpCode::MSTORE | + OpCode::MSTORE8 | + OpCode::MCOPY | + OpCode::CODECOPY | + OpCode::CALLDATACOPY | + OpCode::RETURNDATACOPY | + OpCode::CALL | + OpCode::CALLCODE | + OpCode::DELEGATECALL | + OpCode::STATICCALL + ) +} diff --git a/crates/evm/evm/src/inspectors/debugger.rs b/crates/evm/evm/src/inspectors/debugger.rs index 8d7c7b116..5ec952ac2 100644 --- a/crates/evm/evm/src/inspectors/debugger.rs +++ b/crates/evm/evm/src/inspectors/debugger.rs @@ -72,12 +72,28 @@ impl Inspector for Debugger { interp.gas.refunded() as u64, ); + // if the previous opcode does __not__ modify memory, we can reuse the memory of + // that step + let memory = self.arena.arena[self.head] + .steps + .last() + .and_then(|step| { + if !step.opcode_modifies_memory() { + // reuse the memory from the previous step, because its opcode did not modify + // memory + Some(step.memory.clone()) + } else { + None + } + }) + .unwrap_or_else(|| interp.shared_memory.context_memory().to_vec().into()); + self.arena.arena[self.head].steps.push(DebugStep { pc, stack: interp.stack().data().clone(), - memory: interp.shared_memory.context_memory().to_vec(), + memory, calldata: interp.contract().input.clone(), - returndata: interp.return_data_buffer.to_vec(), + returndata: interp.return_data_buffer.clone(), instruction: Instruction::OpCode(op), push_bytes, total_gas_used, From ed8dec54a0cf4f292ea7ee3e3934443d34cf65d9 Mon Sep 17 00:00:00 2001 From: Krishang Shah <93703995+kamuik16@users.noreply.github.com> Date: Wed, 13 Mar 2024 18:12:50 +0530 Subject: [PATCH 067/430] feat: supports socket address as --rpc-url input (#7389) * feat: supports socket address * cargo fmt and clippy --- crates/common/src/provider/alloy.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/crates/common/src/provider/alloy.rs b/crates/common/src/provider/alloy.rs index 7208c7953..54c9085a1 100644 --- a/crates/common/src/provider/alloy.rs +++ b/crates/common/src/provider/alloy.rs @@ -12,7 +12,9 @@ use foundry_common::types::ToAlloy; use foundry_config::NamedChain; use reqwest::Url; use std::{ + net::SocketAddr, path::{Path, PathBuf}, + str::FromStr, time::Duration, }; use url::ParseError; @@ -93,12 +95,16 @@ impl ProviderBuilder { let url = Url::parse(url_str) .or_else(|err| match err { ParseError::RelativeUrlWithoutBase => { - let path = Path::new(url_str); - - if let Ok(path) = resolve_path(path) { - Url::parse(&format!("file://{}", path.display())) + if SocketAddr::from_str(url_str).is_ok() { + Url::parse(&format!("http://{}", url_str)) } else { - Err(err) + let path = Path::new(url_str); + + if let Ok(path) = resolve_path(path) { + Url::parse(&format!("file://{}", path.display())) + } else { + Err(err) + } } } _ => Err(err), From bd03d2b9bd082e7b503b855a32a560eddf496399 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 13 Mar 2024 14:52:36 +0100 Subject: [PATCH 068/430] chore: simplify get transaction nonce (#7392) * chore: simplify get transaction nonce * chore: rm option * chore: cleanup --- crates/anvil/src/eth/api.rs | 2 +- crates/anvil/src/eth/backend/mem/mod.rs | 29 ++++++++++--------------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index a9b2cd8ab..83754480b 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -2535,7 +2535,7 @@ impl EthApi { } } - let nonce = self.backend.get_nonce(address, Some(block_request)).await?; + let nonce = self.backend.get_nonce(address, block_request).await?; Ok(nonce) } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 837b6482d..013c303e3 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -1789,19 +1789,19 @@ impl Backend { pub async fn get_nonce( &self, address: Address, - block_request: Option, + block_request: BlockRequest, ) -> Result { - if let Some(BlockRequest::Pending(pool_transactions)) = block_request.as_ref() { + if let BlockRequest::Pending(pool_transactions) = &block_request { if let Some(value) = get_pool_transactions_nonce(pool_transactions, address) { return Ok(value); } } let final_block_request = match block_request { - Some(BlockRequest::Pending(_)) => Some(BlockRequest::Number(self.best_number())), - Some(BlockRequest::Number(bn)) => Some(BlockRequest::Number(bn)), - None => None, + BlockRequest::Pending(_) => BlockRequest::Number(self.best_number()), + BlockRequest::Number(bn) => BlockRequest::Number(bn), }; - self.with_database_at(final_block_request, |db, _| { + + self.with_database_at(Some(final_block_request), |db, _| { trace!(target: "backend", "get nonce for {:?}", address); Ok(U256::from(db.basic_ref(address)?.unwrap_or_default().nonce)) }) @@ -2276,19 +2276,14 @@ fn get_pool_transactions_nonce( pool_transactions: &[Arc], address: Address, ) -> Option { - let highest_nonce_tx = pool_transactions + if let Some(highest_nonce) = pool_transactions .iter() .filter(|tx| *tx.pending_transaction.sender() == address) - .reduce(|accum, item| { - let nonce = item.pending_transaction.nonce(); - if nonce > accum.pending_transaction.nonce() { - item - } else { - accum - } - }); - if let Some(highest_nonce_tx) = highest_nonce_tx { - return Some(highest_nonce_tx.pending_transaction.nonce().saturating_add(U256::from(1))); + .map(|tx| tx.pending_transaction.nonce()) + .max() + { + let tx_count = highest_nonce.saturating_add(U256::from(1)); + return Some(tx_count) } None } From b6d31869344c820515554ed20ee698232f3c42c3 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 13 Mar 2024 17:38:01 +0100 Subject: [PATCH 069/430] perf: use fxhash in maps when the key is small (#7393) --- Cargo.lock | 121 +++++++++++++++------------- Cargo.toml | 10 +-- crates/cheatcodes/Cargo.toml | 1 + crates/cheatcodes/src/inspector.rs | 3 +- crates/common/Cargo.toml | 1 + crates/common/src/compile.rs | 3 +- crates/common/src/evm.rs | 4 +- crates/evm/core/Cargo.toml | 3 +- crates/evm/core/src/fork/backend.rs | 3 +- crates/evm/core/src/ic.rs | 11 +-- crates/evm/coverage/Cargo.toml | 1 + crates/evm/coverage/src/analysis.rs | 9 ++- crates/evm/evm/src/executors/mod.rs | 6 +- crates/forge/Cargo.toml | 17 +--- crates/forge/bin/cmd/coverage.rs | 5 +- crates/forge/src/runner.rs | 2 +- 16 files changed, 104 insertions(+), 96 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8d5f0d37b..5ee659cab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -564,7 +564,7 @@ dependencies = [ "futures", "hash-db", "hyper", - "itertools 0.11.0", + "itertools 0.12.1", "k256", "memory-db", "parking_lot", @@ -645,9 +645,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.80" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" +checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" [[package]] name = "arbitrary" @@ -1195,9 +1195,9 @@ checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" [[package]] name = "bytemuck" -version = "1.14.3" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2ef034f05691a48569bd920a96c81b9d91bbad1ab5ac7c4616c1f6ef36cb79f" +checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" [[package]] name = "byteorder" @@ -1326,7 +1326,7 @@ dependencies = [ "foundry-wallets", "futures", "indicatif", - "itertools 0.11.0", + "itertools 0.12.1", "rand 0.8.5", "rayon", "regex", @@ -1701,9 +1701,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.11.2" +version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b37dae8c8ded08d5ec72caa1b4204a5344047cd4a2c7387e3d150020abfbc1c9" +checksum = "5ba00838774b4ab0233e355d26710fbfc8327a05c017f6dc4873f876d1f79f78" dependencies = [ "cfg-if", "cpufeatures", @@ -2521,7 +2521,7 @@ dependencies = [ "serde", "serde_json", "syn 2.0.52", - "toml 0.8.10", + "toml 0.8.11", "walkdir", ] @@ -2878,7 +2878,7 @@ dependencies = [ "pear", "serde", "tempfile", - "toml 0.8.10", + "toml 0.8.11", "uncased", "version_check", ] @@ -2989,7 +2989,7 @@ dependencies = [ "globset", "hyper", "indicatif", - "itertools 0.11.0", + "itertools 0.12.1", "once_cell", "opener", "parking_lot", @@ -3001,6 +3001,7 @@ dependencies = [ "regex", "reqwest", "revm-inspectors", + "rustc-hash", "semver 1.0.22", "serde", "serde_json", @@ -3031,7 +3032,7 @@ dependencies = [ "foundry-common", "foundry-compilers", "foundry-config", - "itertools 0.11.0", + "itertools 0.12.1", "mdbook", "once_cell", "rayon", @@ -3040,7 +3041,7 @@ dependencies = [ "serde_json", "solang-parser", "thiserror", - "toml 0.8.10", + "toml 0.8.11", "tracing", ] @@ -3051,11 +3052,11 @@ dependencies = [ "alloy-primitives", "ariadne", "foundry-config", - "itertools 0.11.0", + "itertools 0.12.1", "pretty_assertions", "solang-parser", "thiserror", - "toml 0.8.10", + "toml 0.8.11", "tracing", "tracing-subscriber", ] @@ -3089,7 +3090,7 @@ dependencies = [ "foundry-wallets", "futures", "indicatif", - "itertools 0.11.0", + "itertools 0.12.1", "parking_lot", "revm-inspectors", "semver 1.0.22", @@ -3169,7 +3170,7 @@ dependencies = [ "alloy-rpc-types", "alloy-signer", "alloy-sol-types", - "base64 0.21.7", + "base64 0.22.0", "const-hex", "eyre", "foundry-cheatcodes-spec", @@ -3178,15 +3179,16 @@ dependencies = [ "foundry-config", "foundry-evm-core", "foundry-wallets", - "itertools 0.11.0", + "itertools 0.12.1", "jsonpath_lib", "k256", "p256", "parking_lot", "revm", + "rustc-hash", "serde_json", "thiserror", - "toml 0.8.10", + "toml 0.8.11", "tracing", "walkdir", ] @@ -3274,6 +3276,7 @@ dependencies = [ "pretty_assertions", "rand 0.8.5", "reqwest", + "rustc-hash", "semver 1.0.22", "serde", "serde_json", @@ -3289,9 +3292,9 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f5a6dffd81de844c96fea0aaf825cd2a9760c4a768ae3f5786d431ef3c57d69" +checksum = "1cc03b422b28e49d6dd61b8442bcee16c22edd975be1764de5a39c4037ecb478" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3351,7 +3354,7 @@ dependencies = [ "serde_regex", "tempfile", "thiserror", - "toml 0.8.10", + "toml 0.8.11", "toml_edit 0.21.1", "tracing", "walkdir", @@ -3393,7 +3396,7 @@ dependencies = [ "foundry-evm-fuzz", "foundry-evm-traces", "hashbrown 0.14.3", - "itertools 0.11.0", + "itertools 0.12.1", "parking_lot", "proptest", "rand 0.8.5", @@ -3425,11 +3428,12 @@ dependencies = [ "foundry-config", "foundry-macros", "futures", - "itertools 0.11.0", + "itertools 0.12.1", "once_cell", "parking_lot", "revm", "revm-inspectors", + "rustc-hash", "serde", "serde_json", "thiserror", @@ -3448,6 +3452,7 @@ dependencies = [ "foundry-compilers", "foundry-evm-core", "revm", + "rustc-hash", "semver 1.0.22", "tracing", ] @@ -3468,7 +3473,7 @@ dependencies = [ "foundry-evm-coverage", "foundry-evm-traces", "hashbrown 0.14.3", - "itertools 0.11.0", + "itertools 0.12.1", "parking_lot", "proptest", "rand 0.8.5", @@ -3495,7 +3500,7 @@ dependencies = [ "foundry-evm-core", "futures", "hashbrown 0.14.3", - "itertools 0.11.0", + "itertools 0.12.1", "once_cell", "revm-inspectors", "serde", @@ -3562,7 +3567,7 @@ dependencies = [ "eyre", "foundry-common", "foundry-config", - "itertools 0.11.0", + "itertools 0.12.1", "rpassword", "rusoto_core", "rusoto_kms", @@ -5524,7 +5529,7 @@ checksum = "4ccca0f6c17acc81df8e242ed473ec144cbf5c98037e69aa6d144780aad103c8" dependencies = [ "inlinable_string", "pear_codegen", - "yansi 1.0.0", + "yansi 1.0.1", ] [[package]] @@ -5926,9 +5931,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] @@ -5943,7 +5948,7 @@ dependencies = [ "quote", "syn 2.0.52", "version_check", - "yansi 1.0.0", + "yansi 1.0.1", ] [[package]] @@ -6255,9 +6260,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.25" +version = "0.11.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eea5a9eb898d3783f17c6407670e3592fd174cb81a10e51d4c37f49450b9946" +checksum = "78bf93c4af7a8bb7d879d51cebe797356ff10ae8516ace542b5182d9dcac10b2" dependencies = [ "base64 0.21.7", "bytes", @@ -6470,9 +6475,9 @@ dependencies = [ [[package]] name = "ruint" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b1d9521f889713d1221270fdd63370feca7e5c71a18745343402fa86e4f04f" +checksum = "8f308135fef9fc398342da5472ce7c484529df23743fb7c734e0f3d472971e62" dependencies = [ "alloy-rlp", "arbitrary", @@ -6598,6 +6603,12 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc-hex" version = "2.1.0" @@ -6757,9 +6768,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7d66a1128282b7ef025a8ead62a4a9fcf017382ec53b8ffbf4d7bf77bd3c60" +checksum = "2ef2175c2907e7c8bc0a9c3f86aeb5ec1f3b275300ad58a44d0c3ae379a5e52e" dependencies = [ "cfg-if", "derive_more", @@ -6769,9 +6780,9 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19" +checksum = "634d9b8eb8fd61c5cdd3390d9b2132300a7e7618955b98b8416f118c1b4e144f" dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", @@ -7483,20 +7494,20 @@ dependencies = [ [[package]] name = "system-configuration" -version = "0.6.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bc6ee10a9b4fcf576e9b0819d95ec16f4d2c02d39fd83ac1c8789785c4a42" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ - "bitflags 2.4.2", + "bitflags 1.3.2", "core-foundation", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" -version = "0.6.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" dependencies = [ "core-foundation-sys", "libc", @@ -7567,18 +7578,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.57" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.57" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", @@ -7793,15 +7804,15 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" +checksum = "af06656561d28735e9c1cd63dfd57132c8155426aa6af24f36a00a351f88c48e" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.6", + "toml_edit 0.22.7", ] [[package]] @@ -7848,9 +7859,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.6" +version = "0.22.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1b5fd4128cc8d3e0cb74d4ed9a9cc7c7284becd4df68f5f940e1ad123606f6" +checksum = "18769cd1cec395d70860ceb4d932812a0b4d06b1a4bb336745a4d21b9496e992" dependencies = [ "indexmap", "serde", @@ -8751,9 +8762,9 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "yansi" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c2861d76f58ec8fc95708b9b1e417f7b12fd72ad33c01fa6886707092dea0d3" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "zerocopy" diff --git a/Cargo.toml b/Cargo.toml index f56a5cfd7..1831efc1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -186,26 +186,26 @@ alloy-rlp = "0.3.3" solang-parser = "=0.3.3" ## misc +base64 = "0.22" chrono = { version = "0.4", default-features = false, features = ["clock", "std"] } color-eyre = "0.6" derive_more = "0.99" +evm-disassembler = "0.4" eyre = "0.6" hex = { package = "const-hex", version = "1.6", features = ["hex"] } -itertools = "0.11" +itertools = "0.12" jsonpath_lib = "0.3" +k256 = "0.13" pretty_assertions = "1.4" -protobuf = "=3.2.0" rand = "0.8" +rustc-hash = "1.1" serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["arbitrary_precision"] } -base64 = "0.21" strum = "0.26" toml = "0.8" tracing = "0.1" tracing-subscriber = "0.3" -evm-disassembler = "0.4" vergen = { version = "8", default-features = false } -k256 = "0.13" axum = "0.6" hyper = "0.14" diff --git a/crates/cheatcodes/Cargo.toml b/crates/cheatcodes/Cargo.toml index 7524afb63..57b39966e 100644 --- a/crates/cheatcodes/Cargo.toml +++ b/crates/cheatcodes/Cargo.toml @@ -42,3 +42,4 @@ k256.workspace = true walkdir = "2" p256 = "0.13.2" thiserror = "1" +rustc-hash.workspace = true diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 9b26e7754..bf39684b4 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -32,6 +32,7 @@ use revm::{ primitives::{BlockEnv, CreateScheme, TransactTo}, EvmContext, InnerEvmContext, Inspector, }; +use rustc_hash::FxHashMap; use serde_json::Value; use std::{ collections::{BTreeMap, HashMap, VecDeque}, @@ -154,7 +155,7 @@ pub struct Cheatcodes { pub expected_emits: VecDeque, /// Map of context depths to memory offset ranges that may be written to within the call depth. - pub allowed_mem_writes: HashMap>>, + pub allowed_mem_writes: FxHashMap>>, /// Current broadcasting information pub broadcast: Option, diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 49be9df68..cf6e53040 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -57,6 +57,7 @@ tracing.workspace = true url = "2" walkdir = "2" yansi = "0.5" +rustc-hash.workspace = true [dev-dependencies] foundry-macros.workspace = true diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index 96574f460..3f159f069 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -11,6 +11,7 @@ use foundry_compilers::{ Artifact, ArtifactId, FileFilter, Graph, Project, ProjectCompileOutput, ProjectPathsConfig, Solc, SolcConfig, }; +use rustc_hash::FxHashMap; use std::{ collections::{BTreeMap, HashMap}, convert::Infallible, @@ -278,7 +279,7 @@ pub struct ContractSources { /// Map over artifacts' contract names -> vector of file IDs pub ids_by_name: HashMap>, /// Map over file_id -> (source code, contract) - pub sources_by_id: HashMap, + pub sources_by_id: FxHashMap, } impl ContractSources { diff --git a/crates/common/src/evm.rs b/crates/common/src/evm.rs index 4b2a94f43..7b9158913 100644 --- a/crates/common/src/evm.rs +++ b/crates/common/src/evm.rs @@ -11,11 +11,11 @@ use foundry_config::{ }, Chain, Config, }; +use rustc_hash::FxHashMap; use serde::Serialize; -use std::collections::HashMap; /// Map keyed by breakpoints char to their location (contract address, pc) -pub type Breakpoints = HashMap; +pub type Breakpoints = FxHashMap; /// `EvmArgs` and `EnvArgs` take the highest precedence in the Config/Figment hierarchy. /// All vars are opt-in, their default values are expected to be set by the diff --git a/crates/evm/core/Cargo.toml b/crates/evm/core/Cargo.toml index 347d0489c..5e01c9114 100644 --- a/crates/evm/core/Cargo.toml +++ b/crates/evm/core/Cargo.toml @@ -37,14 +37,15 @@ revm = { workspace = true, default-features = false, features = [ ] } revm-inspectors.workspace = true -derive_more.workspace = true auto_impl = "1" +derive_more.workspace = true eyre = "0.6" futures = "0.3" hex.workspace = true itertools.workspace = true once_cell = "1" parking_lot = "0.12" +rustc-hash.workspace = true serde = "1" serde_json = "1" thiserror = "1" diff --git a/crates/evm/core/src/fork/backend.rs b/crates/evm/core/src/fork/backend.rs index 6613c0f13..8e720a250 100644 --- a/crates/evm/core/src/fork/backend.rs +++ b/crates/evm/core/src/fork/backend.rs @@ -18,6 +18,7 @@ use revm::{ db::DatabaseRef, primitives::{AccountInfo, Bytecode, KECCAK_EMPTY}, }; +use rustc_hash::FxHashMap; use std::{ collections::{hash_map::Entry, HashMap, VecDeque}, pin::Pin, @@ -86,7 +87,7 @@ pub struct BackendHandler

{ /// Listeners that wait for a `get_storage_at` response storage_requests: HashMap<(Address, U256), Vec>, /// Listeners that wait for a `get_block` response - block_requests: HashMap>, + block_requests: FxHashMap>, /// Incoming commands. incoming: Receiver, /// unprocessed queued requests diff --git a/crates/evm/core/src/ic.rs b/crates/evm/core/src/ic.rs index 479a50b0a..7bb2179c5 100644 --- a/crates/evm/core/src/ic.rs +++ b/crates/evm/core/src/ic.rs @@ -1,20 +1,21 @@ use revm::{ interpreter::{opcode, opcode::spec_opcode_gas}, - primitives::{HashMap, SpecId}, + primitives::SpecId, }; +use rustc_hash::FxHashMap; /// Maps from program counter to instruction counter. /// /// Inverse of [`IcPcMap`]. pub struct PcIcMap { - pub inner: HashMap, + pub inner: FxHashMap, } impl PcIcMap { /// Creates a new `PcIcMap` for the given code. pub fn new(spec: SpecId, code: &[u8]) -> Self { let opcode_infos = spec_opcode_gas(spec); - let mut map = HashMap::new(); + let mut map = FxHashMap::default(); let mut i = 0; let mut cumulative_push_size = 0; @@ -44,14 +45,14 @@ impl PcIcMap { /// /// Inverse of [`PcIcMap`]. pub struct IcPcMap { - pub inner: HashMap, + pub inner: FxHashMap, } impl IcPcMap { /// Creates a new `IcPcMap` for the given code. pub fn new(spec: SpecId, code: &[u8]) -> Self { let opcode_infos = spec_opcode_gas(spec); - let mut map = HashMap::new(); + let mut map = FxHashMap::default(); let mut i = 0; let mut cumulative_push_size = 0; diff --git a/crates/evm/coverage/Cargo.toml b/crates/evm/coverage/Cargo.toml index 4a9a39276..6a3dc92ff 100644 --- a/crates/evm/coverage/Cargo.toml +++ b/crates/evm/coverage/Cargo.toml @@ -20,3 +20,4 @@ eyre = "0.6" revm.workspace = true semver = "1" tracing = "0.1" +rustc-hash.workspace = true diff --git a/crates/evm/coverage/src/analysis.rs b/crates/evm/coverage/src/analysis.rs index 9af93cb68..1926d2b4e 100644 --- a/crates/evm/coverage/src/analysis.rs +++ b/crates/evm/coverage/src/analysis.rs @@ -1,6 +1,7 @@ use super::{ContractId, CoverageItem, CoverageItemKind, SourceLocation}; use foundry_common::TestFunctionExt; use foundry_compilers::artifacts::ast::{self, Ast, Node, NodeType}; +use rustc_hash::FxHashMap; use semver::Version; use std::collections::{HashMap, HashSet}; @@ -443,9 +444,9 @@ pub struct SourceAnalysis { #[derive(Clone, Debug, Default)] pub struct SourceAnalyzer { /// A map of source IDs to their source code - sources: HashMap, + sources: FxHashMap, /// A map of AST node IDs of contracts to their contract IDs. - contract_ids: HashMap, + contract_ids: FxHashMap, /// A map of contract IDs to their AST nodes. contracts: HashMap, /// A collection of coverage items. @@ -463,8 +464,8 @@ impl SourceAnalyzer { /// (defined by `version`). pub fn new( version: Version, - asts: HashMap, - sources: HashMap, + asts: FxHashMap, + sources: FxHashMap, ) -> eyre::Result { let mut analyzer = SourceAnalyzer { sources, ..Default::default() }; diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index a62d070a5..469d6b6e4 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -838,11 +838,7 @@ fn convert_call_result( .. } = call_result; - let breakpoints = if let Some(c) = call_result.cheatcodes { - c.breakpoints - } else { - std::collections::HashMap::new() - }; + let breakpoints = call_result.cheatcodes.map(|c| c.breakpoints).unwrap_or_default(); match status { return_ok!() => { diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 0a120f975..46e9ffd38 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -15,11 +15,7 @@ name = "forge" path = "bin/main.rs" [build-dependencies] -vergen = { workspace = true, default-features = false, features = [ - "build", - "git", - "gitcl", -] } +vergen = { workspace = true, default-features = false, features = ["build", "git", "gitcl"] } [dependencies] # lib @@ -83,6 +79,7 @@ thiserror = "1" tokio = { version = "1", features = ["time"] } watchexec = "2.3.2" evm-disassembler.workspace = true +rustc-hash.workspace = true # doc server axum = { workspace = true, features = ["ws"] } @@ -99,9 +96,7 @@ globset = "0.4" paste = "1.0" path-slash = "0.2" pretty_assertions.workspace = true -svm = { package = "svm-rs", version = "0.3", default-features = false, features = [ - "rustls", -] } +svm = { package = "svm-rs", version = "0.3", default-features = false, features = ["rustls"] } tempfile = "3" tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] } @@ -113,11 +108,7 @@ rustls = [ "reqwest/rustls-tls", "reqwest/rustls-tls-native-roots", ] -openssl = [ - "foundry-cli/openssl", - "reqwest/default-tls", - "foundry-wallets/openssl", -] +openssl = ["foundry-cli/openssl", "reqwest/default-tls", "foundry-wallets/openssl"] asm-keccak = ["alloy-primitives/asm-keccak"] [[bench]] diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index 7acfa8fdf..e140b8436 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -26,6 +26,7 @@ use foundry_compilers::{ Artifact, Project, ProjectCompileOutput, }; use foundry_config::{Config, SolcReq}; +use rustc_hash::FxHashMap; use semver::Version; use std::{collections::HashMap, path::PathBuf, sync::mpsc::channel}; use yansi::Paint; @@ -155,8 +156,8 @@ impl CoverageArgs { let mut report = CoverageReport::default(); // Collect ASTs and sources - let mut versioned_asts: HashMap> = HashMap::new(); - let mut versioned_sources: HashMap> = HashMap::new(); + let mut versioned_asts: HashMap> = HashMap::new(); + let mut versioned_sources: HashMap> = HashMap::new(); for (path, mut source_file, version) in sources.into_sources_with_version() { report.add_source(version.clone(), source_file.id as usize, path.clone()); diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 3b1a1a207..e8dba45a2 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -383,7 +383,7 @@ impl<'a> ContractRunner<'a> { err.stipend, None, err.state_changeset, - HashMap::new(), + Default::default(), ) } Err(EvmError::SkipError) => { From 37ada9673981053a739e557824544082989b9ed4 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 13 Mar 2024 19:15:26 +0100 Subject: [PATCH 070/430] chore: factor out common ic-pc code (#7396) --- crates/evm/core/src/ic.rs | 67 +++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 38 deletions(-) diff --git a/crates/evm/core/src/ic.rs b/crates/evm/core/src/ic.rs index 7bb2179c5..9f679fc36 100644 --- a/crates/evm/core/src/ic.rs +++ b/crates/evm/core/src/ic.rs @@ -14,25 +14,7 @@ pub struct PcIcMap { impl PcIcMap { /// Creates a new `PcIcMap` for the given code. pub fn new(spec: SpecId, code: &[u8]) -> Self { - let opcode_infos = spec_opcode_gas(spec); - let mut map = FxHashMap::default(); - - let mut i = 0; - let mut cumulative_push_size = 0; - while i < code.len() { - let op = code[i]; - map.insert(i, i - cumulative_push_size); - if opcode_infos[op as usize].is_push() { - // Skip the push bytes. - // - // For more context on the math, see: https://github.com/bluealloy/revm/blob/007b8807b5ad7705d3cacce4d92b89d880a83301/crates/revm/src/interpreter/contract.rs#L114-L115 - i += (op - opcode::PUSH1 + 1) as usize; - cumulative_push_size += (op - opcode::PUSH1 + 1) as usize; - } - i += 1; - } - - Self { inner: map } + Self { inner: make_map::(spec, code) } } /// Returns the instruction counter for the given program counter. @@ -51,25 +33,7 @@ pub struct IcPcMap { impl IcPcMap { /// Creates a new `IcPcMap` for the given code. pub fn new(spec: SpecId, code: &[u8]) -> Self { - let opcode_infos = spec_opcode_gas(spec); - let mut map = FxHashMap::default(); - - let mut i = 0; - let mut cumulative_push_size = 0; - while i < code.len() { - let op = code[i]; - map.insert(i - cumulative_push_size, i); - if opcode_infos[op as usize].is_push() { - // Skip the push bytes. - // - // For more context on the math, see: https://github.com/bluealloy/revm/blob/007b8807b5ad7705d3cacce4d92b89d880a83301/crates/revm/src/interpreter/contract.rs#L114-L115 - i += (op - opcode::PUSH1 + 1) as usize; - cumulative_push_size += (op - opcode::PUSH1 + 1) as usize; - } - i += 1; - } - - Self { inner: map } + Self { inner: make_map::(spec, code) } } /// Returns the program counter for the given instruction counter. @@ -77,3 +41,30 @@ impl IcPcMap { self.inner.get(&ic).copied() } } + +fn make_map(spec: SpecId, code: &[u8]) -> FxHashMap { + let opcode_infos = spec_opcode_gas(spec); + let mut map = FxHashMap::default(); + + let mut pc = 0; + let mut cumulative_push_size = 0; + while pc < code.len() { + let ic = pc - cumulative_push_size; + if PC_FIRST { + map.insert(pc, ic); + } else { + map.insert(ic, pc); + } + + let op = code[pc]; + if opcode_infos[op as usize].is_push() { + // Skip the push bytes. + let push_size = (op - opcode::PUSH0) as usize; + pc += push_size; + cumulative_push_size += push_size; + } + + pc += 1; + } + map +} From 545cd0bf4cc5979f8f97671012dce54440550181 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 13 Mar 2024 19:51:22 +0100 Subject: [PATCH 071/430] perf: use ArrayVec for collecting push bytes (#7397) * perf: use ArrayVec for collecting push bytes * deps --- Cargo.lock | 2 ++ Cargo.toml | 1 + crates/evm/core/Cargo.toml | 1 + crates/evm/core/src/debug.rs | 22 ++++++++++---- crates/evm/evm/Cargo.toml | 1 + crates/evm/evm/src/inspectors/debugger.rs | 36 ++++++++++------------- 6 files changed, 37 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5ee659cab..19a9bbbe1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3385,6 +3385,7 @@ dependencies = [ "alloy-json-abi", "alloy-primitives", "alloy-sol-types", + "arrayvec", "const-hex", "eyre", "foundry-cheatcodes", @@ -3418,6 +3419,7 @@ dependencies = [ "alloy-providers", "alloy-rpc-types", "alloy-sol-types", + "arrayvec", "auto_impl", "const-hex", "derive_more", diff --git a/Cargo.toml b/Cargo.toml index 1831efc1c..0c3917340 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -186,6 +186,7 @@ alloy-rlp = "0.3.3" solang-parser = "=0.3.3" ## misc +arrayvec = "0.7" base64 = "0.22" chrono = { version = "0.4", default-features = false, features = ["clock", "std"] } color-eyre = "0.6" diff --git a/crates/evm/core/Cargo.toml b/crates/evm/core/Cargo.toml index 5e01c9114..7e4ea3c21 100644 --- a/crates/evm/core/Cargo.toml +++ b/crates/evm/core/Cargo.toml @@ -37,6 +37,7 @@ revm = { workspace = true, default-features = false, features = [ ] } revm-inspectors.workspace = true +arrayvec.workspace = true auto_impl = "1" derive_more.workspace = true eyre = "0.6" diff --git a/crates/evm/core/src/debug.rs b/crates/evm/core/src/debug.rs index 084d36b90..70d94ff97 100644 --- a/crates/evm/core/src/debug.rs +++ b/crates/evm/core/src/debug.rs @@ -1,5 +1,6 @@ use crate::opcodes; use alloy_primitives::{Address, Bytes, U256}; +use arrayvec::ArrayVec; use revm::interpreter::OpCode; use revm_inspectors::tracing::types::CallKind; use serde::{Deserialize, Serialize}; @@ -176,8 +177,10 @@ pub struct DebugStep { pub returndata: Bytes, /// Opcode to be executed pub instruction: Instruction, - /// Optional bytes that are being pushed onto the stack - pub push_bytes: Option>, + /// Optional bytes that are being pushed onto the stack. + /// Empty if the opcode is not a push or PUSH0. + #[serde(serialize_with = "hex::serialize", deserialize_with = "deserialize_arrayvec_hex")] + pub push_bytes: ArrayVec, /// The program counter at this step. /// /// Note: To map this step onto source code using a source map, you must convert the program @@ -195,7 +198,7 @@ impl Default for DebugStep { calldata: Default::default(), returndata: Default::default(), instruction: Instruction::OpCode(revm::interpreter::opcode::INVALID), - push_bytes: None, + push_bytes: Default::default(), pc: 0, total_gas_used: 0, } @@ -205,8 +208,8 @@ impl Default for DebugStep { impl DebugStep { /// Pretty print the step's opcode pub fn pretty_opcode(&self) -> String { - if let Some(push_bytes) = &self.push_bytes { - format!("{}(0x{})", self.instruction, hex::encode(push_bytes)) + if !self.push_bytes.is_empty() { + format!("{}(0x{})", self.instruction, hex::encode(&self.push_bytes)) } else { self.instruction.to_string() } @@ -266,3 +269,12 @@ impl Instruction { } } } + +fn deserialize_arrayvec_hex<'de, D: serde::Deserializer<'de>>( + deserializer: D, +) -> Result, D::Error> { + let bytes: Vec = hex::deserialize(deserializer)?; + let mut array = ArrayVec::new(); + array.try_extend_from_slice(&bytes).map_err(serde::de::Error::custom)?; + Ok(array) +} diff --git a/crates/evm/evm/Cargo.toml b/crates/evm/evm/Cargo.toml index 08b1500b4..a1857b303 100644 --- a/crates/evm/evm/Cargo.toml +++ b/crates/evm/evm/Cargo.toml @@ -38,6 +38,7 @@ revm-inspectors.workspace = true itertools.workspace = true +arrayvec.workspace = true eyre = "0.6" hex.workspace = true parking_lot = "0.12" diff --git a/crates/evm/evm/src/inspectors/debugger.rs b/crates/evm/evm/src/inspectors/debugger.rs index 5ec952ac2..f6232bd47 100644 --- a/crates/evm/evm/src/inspectors/debugger.rs +++ b/crates/evm/evm/src/inspectors/debugger.rs @@ -1,4 +1,5 @@ use alloy_primitives::Address; +use arrayvec::ArrayVec; use foundry_common::{ErrorExt, SELECTOR_LEN}; use foundry_evm_core::{ backend::DatabaseExt, @@ -56,15 +57,16 @@ impl Inspector for Debugger { let opcode_info = &opcode_infos[op as usize]; // Extract the push bytes - let push_size = if opcode_info.is_push() { (op - opcode::PUSH1 + 1) as usize } else { 0 }; - let push_bytes = match push_size { - 0 => None, - n => { - let start = pc + 1; - let end = start + n; - Some(interp.contract.bytecode.bytecode()[start..end].to_vec()) - } - }; + let push_size = if opcode_info.is_push() { (op - opcode::PUSH0) as usize } else { 0 }; + let push_bytes = (push_size > 0).then(|| { + let start = pc + 1; + let end = start + push_size; + let slice = &interp.contract.bytecode.bytecode()[start..end]; + assert!(slice.len() <= 32); + let mut array = ArrayVec::new(); + array.try_extend_from_slice(slice).unwrap(); + array + }); let total_gas_used = gas_used( ecx.spec_id(), @@ -72,20 +74,12 @@ impl Inspector for Debugger { interp.gas.refunded() as u64, ); - // if the previous opcode does __not__ modify memory, we can reuse the memory of - // that step + // Reuse the memory from the previous step if the previous opcode did not modify it. let memory = self.arena.arena[self.head] .steps .last() - .and_then(|step| { - if !step.opcode_modifies_memory() { - // reuse the memory from the previous step, because its opcode did not modify - // memory - Some(step.memory.clone()) - } else { - None - } - }) + .filter(|step| !step.opcode_modifies_memory()) + .map(|step| step.memory.clone()) .unwrap_or_else(|| interp.shared_memory.context_memory().to_vec().into()); self.arena.arena[self.head].steps.push(DebugStep { @@ -95,7 +89,7 @@ impl Inspector for Debugger { calldata: interp.contract().input.clone(), returndata: interp.return_data_buffer.clone(), instruction: Instruction::OpCode(op), - push_bytes, + push_bytes: push_bytes.unwrap_or_default(), total_gas_used, }); } From 1001c6975906d7fb06f95d8179def04ad4cbd8f0 Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Thu, 14 Mar 2024 13:40:46 +0100 Subject: [PATCH 072/430] chore: typos (#7403) --- crates/fmt/src/formatter.rs | 2 +- crates/forge/tests/it/fuzz.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/fmt/src/formatter.rs b/crates/fmt/src/formatter.rs index 00beb4d8c..bf6965100 100644 --- a/crates/fmt/src/formatter.rs +++ b/crates/fmt/src/formatter.rs @@ -820,7 +820,7 @@ impl<'a, W: Write> Formatter<'a, W> { Ok(self.transact(fun)?.buffer) } - /// Turn a chunk and its surrounding comments into a a string + /// Turn a chunk and its surrounding comments into a string fn chunk_to_string(&mut self, chunk: &Chunk) -> Result { self.simulate_to_string(|fmt| fmt.write_chunk(chunk)) } diff --git a/crates/forge/tests/it/fuzz.rs b/crates/forge/tests/it/fuzz.rs index 9f59a7f42..47bc7745d 100644 --- a/crates/forge/tests/it/fuzz.rs +++ b/crates/forge/tests/it/fuzz.rs @@ -129,8 +129,8 @@ async fn test_persist_fuzz_failure() { } // record initial counterexample calldata - let intial_counterexample = get_failure_result!(); - let initial_calldata = match intial_counterexample { + let initial_counterexample = get_failure_result!(); + let initial_calldata = match initial_counterexample { Some(CounterExample::Single(counterexample)) => counterexample.calldata, _ => Bytes::new(), }; From 3fa02706ca732c994715ba42d923605692062375 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 14 Mar 2024 18:06:40 +0100 Subject: [PATCH 073/430] chore: set cancun block (#7404) --- crates/anvil/src/hardfork.rs | 15 ++++++--------- crates/anvil/tests/it/anvil_api.rs | 2 +- crates/anvil/tests/it/traces.rs | 4 ++-- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/crates/anvil/src/hardfork.rs b/crates/anvil/src/hardfork.rs index a5e92284c..8fabace32 100644 --- a/crates/anvil/src/hardfork.rs +++ b/crates/anvil/src/hardfork.rs @@ -44,10 +44,8 @@ impl Hardfork { Hardfork::ArrowGlacier => 13773000, Hardfork::GrayGlacier => 15050000, Hardfork::Paris => 15537394, - Hardfork::Shanghai | Hardfork::Latest => 17034870, - - // TODO: set block number after activation - Hardfork::Cancun => unreachable!(), + Hardfork::Shanghai => 17034870, + Hardfork::Cancun | Hardfork::Latest => 19426587, } } @@ -94,7 +92,7 @@ impl Hardfork { Hardfork::Paris => ForkId { hash: ForkHash([0x4f, 0xb8, 0xa8, 0x72]), next: 17034870 }, Hardfork::Shanghai | Hardfork::Latest => { // update `next` when another fork block num is known - ForkId { hash: ForkHash([0xc1, 0xfd, 0xf1, 0x81]), next: 0 } + ForkId { hash: ForkHash([0xc1, 0xfd, 0xf1, 0x81]), next: 19426587 } } Hardfork::Cancun => { // TODO: set fork hash once known @@ -152,10 +150,8 @@ impl From for SpecId { Hardfork::ArrowGlacier => SpecId::LONDON, Hardfork::GrayGlacier => SpecId::GRAY_GLACIER, Hardfork::Paris => SpecId::MERGE, - Hardfork::Shanghai | Hardfork::Latest => SpecId::SHANGHAI, - - // TODO: switch to latest after activation - Hardfork::Cancun => SpecId::CANCUN, + Hardfork::Shanghai => SpecId::SHANGHAI, + Hardfork::Cancun | Hardfork::Latest => SpecId::CANCUN, } } } @@ -182,6 +178,7 @@ impl> From for Hardfork { _i if num < 13_773_000 => Hardfork::London, _i if num < 15_050_000 => Hardfork::ArrowGlacier, _i if num < 17_034_870 => Hardfork::Paris, + _i if num < 19_426_587 => Hardfork::Shanghai, _ => Hardfork::Latest, } } diff --git a/crates/anvil/tests/it/anvil_api.rs b/crates/anvil/tests/it/anvil_api.rs index e487033cc..91ba92d77 100644 --- a/crates/anvil/tests/it/anvil_api.rs +++ b/crates/anvil/tests/it/anvil_api.rs @@ -441,7 +441,7 @@ async fn can_get_node_info() { current_block_number: U64([0]).to_alloy(), current_block_timestamp: 1, current_block_hash: block.hash.unwrap().to_alloy(), - hard_fork: SpecId::SHANGHAI, + hard_fork: SpecId::CANCUN, transaction_order: "fees".to_owned(), environment: NodeEnvironment { base_fee: U256::from_str("0x3b9aca00").unwrap().to_alloy(), diff --git a/crates/anvil/tests/it/traces.rs b/crates/anvil/tests/it/traces.rs index e7d42051b..309621ac5 100644 --- a/crates/anvil/tests/it/traces.rs +++ b/crates/anvil/tests/it/traces.rs @@ -3,7 +3,7 @@ use crate::{ utils::{ethers_http_provider, ethers_ws_provider}, }; use alloy_primitives::U256; -use anvil::{spawn, NodeConfig}; +use anvil::{spawn, Hardfork, NodeConfig}; use ethers::{ contract::ContractInstance, prelude::{ @@ -76,7 +76,7 @@ contract Contract { let contract = compiled.remove_first("Contract").unwrap(); let (abi, bytecode, _) = contract.into_contract_bytecode().into_parts(); - let (_api, handle) = spawn(NodeConfig::test()).await; + let (_api, handle) = spawn(NodeConfig::test().with_hardfork(Some(Hardfork::Shanghai))).await; let provider = ethers_ws_provider(&handle.ws_endpoint()); let wallets = handle.dev_wallets().collect::>().to_ethers(); let client = Arc::new(SignerMiddleware::new(provider, wallets[0].clone())); From 42da94276892f63afefd0dc743e862b058a4b4c2 Mon Sep 17 00:00:00 2001 From: christn Date: Fri, 15 Mar 2024 15:46:39 +0100 Subject: [PATCH 074/430] Bump evm-disassembler dependency to support Cancun opcodes (#7409) * Bump evm-disassembler dependency to support Cancun opcodes * bump lock --------- Co-authored-by: Matthias Seitz --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 19a9bbbe1..45d72746c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2768,9 +2768,9 @@ dependencies = [ [[package]] name = "evm-disassembler" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f6fc9c732a3210153e6aa26746f0abd8773dbf204c64ae3e824309b32b384c5" +checksum = "ded685d9f07315ff689ba56e7d84e6f1e782db19b531a46c34061a733bba7258" dependencies = [ "eyre", "hex", diff --git a/Cargo.toml b/Cargo.toml index 0c3917340..108bbe333 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -191,7 +191,7 @@ base64 = "0.22" chrono = { version = "0.4", default-features = false, features = ["clock", "std"] } color-eyre = "0.6" derive_more = "0.99" -evm-disassembler = "0.4" +evm-disassembler = "0.5" eyre = "0.6" hex = { package = "const-hex", version = "1.6", features = ["hex"] } itertools = "0.12" From 63066ab3389557a81279d7899d5226ea902f2a1e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 17 Mar 2024 11:06:22 +0100 Subject: [PATCH 075/430] chore(deps): weekly `cargo update` (#7416) Updating git repository `https://github.com/alloy-rs/alloy` Updating git repository `https://github.com/paradigmxyz/evm-inspectors` Updating async-trait v0.1.77 -> v0.1.78 Updating clap v4.5.2 -> v4.5.3 Updating clap_derive v4.5.0 -> v4.5.3 Updating color-eyre v0.6.2 -> v0.6.3 Updating figment v0.10.14 -> v0.10.15 Updating gix-trace v0.1.7 -> v0.1.8 Updating gix-utils v0.1.10 -> v0.1.11 Updating h2 v0.3.24 -> v0.3.25 (latest: v0.4.3) Adding heck v0.5.0 Updating new_debug_unreachable v1.0.4 -> v1.0.6 Updating syn v2.0.52 -> v2.0.53 Updating tokio-stream v0.1.14 -> v0.1.15 note: pass `--verbose` to see 182 unchanged dependencies behind latest Co-authored-by: mattsse --- Cargo.lock | 140 ++++++++++++++++++++++++++++------------------------- 1 file changed, 73 insertions(+), 67 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 45d72746c..0e3bf6070 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -266,7 +266,7 @@ checksum = "1a047897373be4bbb0224c1afdabca92648dc57a9c9ef6e7b0be3aff7a859c83" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -341,13 +341,13 @@ dependencies = [ "alloy-json-abi", "const-hex", "dunce", - "heck", + "heck 0.4.1", "indexmap", "proc-macro-error", "proc-macro2", "quote", "serde_json", - "syn 2.0.52", + "syn 2.0.53", "syn-solidity", "tiny-keccak", ] @@ -845,7 +845,7 @@ checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -867,7 +867,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -878,13 +878,13 @@ checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" [[package]] name = "async-trait" -version = "0.1.77" +version = "0.1.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +checksum = "461abc97219de0eaaf81fe3ef974a540158f3d079c2ab200f891f1a2ef201e85" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -937,7 +937,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -1461,9 +1461,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.2" +version = "4.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b230ab84b0ffdf890d5a10abdbc8b83ae1c4918275daea1ab8801f71536b2651" +checksum = "949626d00e063efc93b6dca932419ceb5432f99769911c0b995f7e884c778813" dependencies = [ "clap_builder", "clap_derive", @@ -1505,14 +1505,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.0" +version = "4.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" +checksum = "90239a040c80f5e14809ca132ddc4176ab33d5e17e49691793296e3fcb34d72f" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -1622,9 +1622,9 @@ dependencies = [ [[package]] name = "color-eyre" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" +checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" dependencies = [ "backtrace", "color-spantrace", @@ -1963,7 +1963,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -1974,7 +1974,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -2035,7 +2035,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -2056,7 +2056,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -2066,7 +2066,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" dependencies = [ "derive_builder_core", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -2308,7 +2308,7 @@ checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -2520,7 +2520,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "syn 2.0.52", + "syn 2.0.53", "toml 0.8.11", "walkdir", ] @@ -2538,7 +2538,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -2564,7 +2564,7 @@ dependencies = [ "serde", "serde_json", "strum 0.26.2", - "syn 2.0.52", + "syn 2.0.53", "tempfile", "thiserror", "tiny-keccak", @@ -2822,7 +2822,7 @@ dependencies = [ "bytes", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -2869,9 +2869,9 @@ dependencies = [ [[package]] name = "figment" -version = "0.10.14" +version = "0.10.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b6e5bc7bd59d60d0d45a6ccab6cf0f4ce28698fb4e81e750ddf229c9b824026" +checksum = "7270677e7067213e04f323b55084586195f18308cd7546cfac9f873344ccceb6" dependencies = [ "atomic", "parking_lot", @@ -3529,7 +3529,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -3696,7 +3696,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -3983,15 +3983,15 @@ dependencies = [ [[package]] name = "gix-trace" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b202d766a7fefc596e2cc6a89cda8ad8ad733aed82da635ac120691112a9b1" +checksum = "9b838b2db8f62c9447d483a4c28d251b67fee32741a82cb4d35e9eb4e9fdc5ab" [[package]] name = "gix-utils" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60157a15b9f14b11af1c6817ad7a93b10b50b4e5136d98a127c46a37ff16eeb6" +checksum = "0066432d4c277f9877f091279a597ea5331f68ca410efc874f0bdfb1cd348f92" dependencies = [ "fastrand", "unicode-normalization", @@ -4051,9 +4051,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +checksum = "4fbd2820c5e49886948654ab546d0688ff24530286bdcf8fca3cefb16d4618eb" dependencies = [ "bytes", "fnv", @@ -4142,6 +4142,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.3.9" @@ -4962,7 +4968,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -5028,9 +5034,9 @@ dependencies = [ [[package]] name = "new_debug_unreachable" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "nibble_vec" @@ -5229,7 +5235,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -5333,7 +5339,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -5543,7 +5549,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -5601,7 +5607,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -5704,7 +5710,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -5742,7 +5748,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -5853,7 +5859,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" dependencies = [ "proc-macro2", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -5948,7 +5954,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", "version_check", "yansi 1.0.1", ] @@ -6964,7 +6970,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -7018,7 +7024,7 @@ checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -7064,7 +7070,7 @@ checksum = "b93fb4adc70021ac1b47f7d45e8cc4169baaa7ea58483bc5b721d19a26202212" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -7357,11 +7363,11 @@ version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "rustversion", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -7370,11 +7376,11 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "rustversion", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -7455,9 +7461,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.52" +version = "2.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" dependencies = [ "proc-macro2", "quote", @@ -7473,7 +7479,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -7595,7 +7601,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -7717,7 +7723,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -7753,9 +7759,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ "futures-core", "pin-project-lite", @@ -7951,7 +7957,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -8316,7 +8322,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", "wasm-bindgen-shared", ] @@ -8350,7 +8356,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -8785,7 +8791,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -8805,7 +8811,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] From 42a9d349d6f950ffb9d45e2bb9495d4060d68ea7 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sun, 17 Mar 2024 17:07:17 +0400 Subject: [PATCH 076/430] chore: fix race condition on test project initialization (#7415) * chore: fix race condition on test initialization * fix for windows * fmt --- crates/test-utils/src/util.rs | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index 6a6f15d0f..ede7ca8c0 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -13,9 +13,8 @@ use regex::Regex; use std::{ env, ffi::OsStr, - fs, - fs::File, - io::{BufWriter, IsTerminal, Write}, + fs::{self, File}, + io::{BufWriter, IsTerminal, Read, Seek, Write}, path::{Path, PathBuf}, process::{ChildStdin, Command, Output, Stdio}, sync::{ @@ -237,21 +236,30 @@ pub fn initialize(target: &Path) { // Release the read lock and acquire a write lock, initializing the lock file. _read = None; + let mut write = lock.write().unwrap(); - write.write_all(b"1").unwrap(); - // Initialize and build. - let (prj, mut cmd) = setup_forge("template", foundry_compilers::PathStyle::Dapptools); - eprintln!("- initializing template dir in {}", prj.root().display()); + let mut data = String::new(); + write.read_to_string(&mut data).unwrap(); + + if data != "1" { + write.set_len(0).unwrap(); + write.seek(std::io::SeekFrom::Start(0)).unwrap(); + write.write_all(b"1").unwrap(); - cmd.args(["init", "--force"]).assert_success(); - cmd.forge_fuse().args(["build", "--use", SOLC_VERSION]).assert_success(); + // Initialize and build. + let (prj, mut cmd) = setup_forge("template", foundry_compilers::PathStyle::Dapptools); + eprintln!("- initializing template dir in {}", prj.root().display()); - // Remove the existing template, if any. - let _ = fs::remove_dir_all(tpath); + cmd.args(["init", "--force"]).assert_success(); + cmd.forge_fuse().args(["build", "--use", SOLC_VERSION]).assert_success(); - // Copy the template to the global template path. - pretty_err(tpath, copy_dir(prj.root(), tpath)); + // Remove the existing template, if any. + let _ = fs::remove_dir_all(tpath); + + // Copy the template to the global template path. + pretty_err(tpath, copy_dir(prj.root(), tpath)); + } // Release the write lock and acquire a new read lock. drop(write); From f6863914cab448faabd3be11d092430b325b8812 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 18 Mar 2024 05:43:00 +0400 Subject: [PATCH 077/430] chore: fix test project initialization (#7418) fix initialization --- crates/test-utils/src/util.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index ede7ca8c0..d392b953e 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -243,10 +243,6 @@ pub fn initialize(target: &Path) { write.read_to_string(&mut data).unwrap(); if data != "1" { - write.set_len(0).unwrap(); - write.seek(std::io::SeekFrom::Start(0)).unwrap(); - write.write_all(b"1").unwrap(); - // Initialize and build. let (prj, mut cmd) = setup_forge("template", foundry_compilers::PathStyle::Dapptools); eprintln!("- initializing template dir in {}", prj.root().display()); @@ -259,6 +255,11 @@ pub fn initialize(target: &Path) { // Copy the template to the global template path. pretty_err(tpath, copy_dir(prj.root(), tpath)); + + // Update lockfile to mark that template is initialized. + write.set_len(0).unwrap(); + write.seek(std::io::SeekFrom::Start(0)).unwrap(); + write.write_all(b"1").unwrap(); } // Release the write lock and acquire a new read lock. From db8ea58ba607075ce0f39bb975a38da6b4889ea1 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 18 Mar 2024 03:45:30 +0100 Subject: [PATCH 078/430] test: update fixtures (#7421) --- crates/forge/tests/cli/test_cmd.rs | 4 ++-- .../forge/tests/fixtures/can_test_repeatedly.stdout | 2 +- .../tests/fixtures/can_use_libs_in_multi_fork.stdout | 2 +- crates/forge/tests/fixtures/repro_6531.stdout | 12 +++++------- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index f449a0ac0..5aad1848a 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -358,10 +358,10 @@ interface IERC20 { function name() external view returns (string memory); } -contract USDCCallingTest is Test { +contract USDTCallingTest is Test { function test() public { vm.createSelectFork(""); - IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48).name(); + IERC20(0xdAC17F958D2ee523a2206206994597C13D831ec7).name(); } } "# diff --git a/crates/forge/tests/fixtures/can_test_repeatedly.stdout b/crates/forge/tests/fixtures/can_test_repeatedly.stdout index d792e8096..7095a50f0 100644 --- a/crates/forge/tests/fixtures/can_test_repeatedly.stdout +++ b/crates/forge/tests/fixtures/can_test_repeatedly.stdout @@ -2,7 +2,7 @@ No files changed, compilation skipped Ran 2 tests for test/Counter.t.sol:CounterTest [PASS] testFuzz_SetNumber(uint256) (runs: 256, μ: 26521, ~: 28387) -[PASS] test_Increment() (gas: 31225) +[PASS] test_Increment() (gas: 31325) Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 9.42ms Ran 1 test suite: 2 tests passed, 0 failed, 0 skipped (2 total tests) diff --git a/crates/forge/tests/fixtures/can_use_libs_in_multi_fork.stdout b/crates/forge/tests/fixtures/can_use_libs_in_multi_fork.stdout index 87a5aa753..70c72887a 100644 --- a/crates/forge/tests/fixtures/can_use_libs_in_multi_fork.stdout +++ b/crates/forge/tests/fixtures/can_use_libs_in_multi_fork.stdout @@ -3,7 +3,7 @@ Solc 0.8.23 finished in 1.95s Compiler run successful! Ran 1 test for test/Contract.t.sol:ContractTest -[PASS] test() (gas: 70404) +[PASS] test() (gas: 70360) Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.21s Ran 1 test suite: 1 tests passed, 0 failed, 0 skipped (1 total tests) diff --git a/crates/forge/tests/fixtures/repro_6531.stdout b/crates/forge/tests/fixtures/repro_6531.stdout index e8e474ba7..01f282bf7 100644 --- a/crates/forge/tests/fixtures/repro_6531.stdout +++ b/crates/forge/tests/fixtures/repro_6531.stdout @@ -2,16 +2,14 @@ Compiling 1 files with 0.8.23 Compiler run successful! -Ran 1 test for test/Contract.t.sol:USDCCallingTest -[PASS] test() (gas: 16821) +Ran 1 test for test/Contract.t.sol:USDTCallingTest +[PASS] test() (gas: 9559) Traces: - [16821] USDCCallingTest::test() + [9559] USDTCallingTest::test() ├─ [0] VM::createSelectFork("") │ └─ ← 0 - ├─ [10350] 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48::name() [staticcall] - │ ├─ [3061] 0x43506849D7C04F9138D1A2050bbF3A0c054402dd::name() [delegatecall] - │ │ └─ ← "USD Coin" - │ └─ ← "USD Coin" + ├─ [3110] 0xdAC17F958D2ee523a2206206994597C13D831ec7::name() [staticcall] + │ └─ ← "Tether USD" └─ ← () Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.43s From a08a7da50b9fcaecb33bd25e927b6777bd768035 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 18 Mar 2024 12:06:28 +0100 Subject: [PATCH 079/430] fix: ignore forge-std artifacts in build --sizes (#7414) --- crates/common/src/compile.rs | 12 +++++++++++- crates/forge/tests/cli/build.rs | 9 +++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index 3f159f069..8272dece3 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -245,7 +245,16 @@ impl ProjectCompiler { } let mut size_report = SizeReport { contracts: BTreeMap::new() }; - let artifacts: BTreeMap<_, _> = output.artifacts().collect(); + + let artifacts: BTreeMap<_, _> = output + .artifact_ids() + .filter(|(id, _)| { + // filter out forge-std specific contracts + !id.source.to_string_lossy().contains("/forge-std/src/") + }) + .map(|(id, artifact)| (id.name, artifact)) + .collect(); + for (name, artifact) in artifacts { let size = deployed_contract_size(artifact).unwrap_or_default(); @@ -381,6 +390,7 @@ impl Display for SizeReport { Cell::new("Margin (kB)").add_attribute(Attribute::Bold).fg(Color::Blue), ]); + // filters out non dev contracts (Test or Script) let contracts = self.contracts.iter().filter(|(_, c)| !c.is_dev_contract && c.size > 0); for (name, contract) in contracts { let margin = CONTRACT_SIZE_LIMIT as isize - contract.size as isize; diff --git a/crates/forge/tests/cli/build.rs b/crates/forge/tests/cli/build.rs index 0002ec145..668e4be5e 100644 --- a/crates/forge/tests/cli/build.rs +++ b/crates/forge/tests/cli/build.rs @@ -31,3 +31,12 @@ forgetest_init!(exact_build_output, |prj, cmd| { let stdout = cmd.stdout_lossy(); assert!(stdout.contains("Compiling"), "\n{stdout}"); }); + +// tests build output is as expected +forgetest_init!(build_sizes_no_forge_std, |prj, cmd| { + cmd.args(["build", "--sizes"]); + let stdout = cmd.stdout_lossy(); + assert!(!stdout.contains("console"), "\n{stdout}"); + assert!(!stdout.contains("std"), "\n{stdout}"); + assert!(stdout.contains("Counter"), "\n{stdout}"); +}); From 3865e57a3ba6b8ff3650d45bdd39fbd64e16819a Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 18 Mar 2024 16:22:46 +0100 Subject: [PATCH 080/430] chore: solc 0.8.25 (#7424) * chore: solc 0.8.25 * replace ethers-solc --- Cargo.lock | 66 +++++++++++++++--------------- Cargo.toml | 3 +- crates/anvil/Cargo.toml | 4 +- crates/anvil/tests/it/ganache.rs | 13 +++--- crates/anvil/tests/it/geth.rs | 11 +++-- crates/anvil/tests/it/otterscan.rs | 20 +++++---- crates/anvil/tests/it/revert.rs | 2 +- crates/anvil/tests/it/traces.rs | 14 ++++--- crates/anvil/tests/it/utils.rs | 53 ++++++++++++++++++++++++ crates/forge/Cargo.toml | 2 +- crates/forge/tests/cli/svm.rs | 2 +- 11 files changed, 128 insertions(+), 62 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0e3bf6070..dea2c7c4d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -528,6 +528,7 @@ dependencies = [ "alloy-consensus", "alloy-dyn-abi", "alloy-genesis", + "alloy-json-abi", "alloy-network", "alloy-primitives", "alloy-providers", @@ -553,12 +554,12 @@ dependencies = [ "ethereum-forkid", "ethers", "ethers-core", - "ethers-solc", "eyre", "fdlimit", "flate2", "foundry-cli", "foundry-common", + "foundry-compilers", "foundry-config", "foundry-evm", "futures", @@ -2691,25 +2692,19 @@ dependencies = [ "dirs 5.0.1", "dunce", "ethers-core", - "fs_extra", - "futures-util", "glob", "home", "md-5 0.10.6", "num_cpus", "once_cell", "path-slash", - "rand 0.8.5", "rayon", "regex", "semver 1.0.22", "serde", "serde_json", - "sha2 0.10.8", "solang-parser", - "svm-rs", - "svm-rs-builds 0.2.3", - "tempfile", + "svm-rs 0.3.6", "thiserror", "tiny-keccak", "tokio", @@ -3008,7 +3003,7 @@ dependencies = [ "similar", "solang-parser", "strum 0.26.2", - "svm-rs", + "svm-rs 0.4.0", "tempfile", "thiserror", "tokio", @@ -3292,9 +3287,9 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.3.11" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cc03b422b28e49d6dd61b8442bcee16c22edd975be1764de5a39c4037ecb478" +checksum = "079ada1a2093e0fec67caa15ccf018a2d1b5747c16ba1c11a28df53530eb1a5f" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3317,8 +3312,8 @@ dependencies = [ "serde_json", "sha2 0.10.8", "solang-parser", - "svm-rs", - "svm-rs-builds 0.3.5", + "svm-rs 0.4.0", + "svm-rs-builds", "tempfile", "thiserror", "tokio", @@ -3580,13 +3575,13 @@ dependencies = [ ] [[package]] -name = "fs2" -version = "0.4.3" +name = "fs4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +checksum = "29f9df8a11882c4e3335eb2d18a0137c505d9ca927470b0cac9c6f0ae07d28f7" dependencies = [ - "libc", - "winapi", + "rustix", + "windows-sys 0.48.0", ] [[package]] @@ -7404,13 +7399,13 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "svm-rs" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11297baafe5fa0c99d5722458eac6a5e25c01eb1b8e5cd137f54079093daa7a4" +checksum = "f9b34cc1af809be8d20575428638da2c6a1eb12b1da06dae79c1b82a4ddf83ac" dependencies = [ + "const-hex", "dirs 5.0.1", - "fs2", - "hex", + "fs4", "once_cell", "reqwest", "semver 1.0.22", @@ -7423,29 +7418,36 @@ dependencies = [ ] [[package]] -name = "svm-rs-builds" -version = "0.2.3" +name = "svm-rs" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa64b5e8eecd3a8af7cfc311e29db31a268a62d5953233d3e8243ec77a71c4e3" +checksum = "cde88464d5718a437e9f6be54cf3771837a111bed305c4f4f9d3497471e96249" dependencies = [ - "build_const", - "hex", + "const-hex", + "dirs 5.0.1", + "fs4", + "once_cell", + "reqwest", "semver 1.0.22", + "serde", "serde_json", - "svm-rs", + "sha2 0.10.8", + "thiserror", + "url", + "zip", ] [[package]] name = "svm-rs-builds" -version = "0.3.5" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8d3c94c4d3337336f58493471b98d712c267c66977b0fbe48efd6cbf69ffd0" +checksum = "2ff43323737122457c266fe28a454635edd9cd15f8b6277251eb4703ef54f829" dependencies = [ "build_const", - "hex", + "const-hex", "semver 1.0.22", "serde_json", - "svm-rs", + "svm-rs 0.4.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 108bbe333..860175871 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -138,7 +138,7 @@ foundry-linking = { path = "crates/linking" } # solc & compilation utilities foundry-block-explorers = { version = "0.2.3", default-features = false } -foundry-compilers = { version = "0.3.9", default-features = false } +foundry-compilers = { version = "0.3.13", default-features = false } ## revm # no default features to avoid c-kzg @@ -156,7 +156,6 @@ ethers-contract-abigen = { version = "2.0.14", default-features = false } ethers-providers = { version = "2.0.14", default-features = false } ethers-signers = { version = "2.0.14", default-features = false } ethers-middleware = { version = "2.0.14", default-features = false } -ethers-solc = { version = "2.0.14", default-features = false } ## alloy alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index 430c9e906..18682e5d2 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -95,9 +95,11 @@ clap_complete_fig = "4" ethereum-forkid = "0.12" [dev-dependencies] +alloy-json-abi.workspace = true ethers = { workspace = true, features = ["abigen"] } ethers-core = { workspace = true, features = ["optimism"] } -ethers-solc = { workspace = true, features = ["project-util", "full"] } +foundry-compilers = { workspace = true, features = ["project-util", "full"] } + pretty_assertions = "1.3.0" tokio = { version = "1", features = ["full"] } crc = "3.0.1" diff --git a/crates/anvil/tests/it/ganache.rs b/crates/anvil/tests/it/ganache.rs index 3ea0e84f5..b95b55433 100644 --- a/crates/anvil/tests/it/ganache.rs +++ b/crates/anvil/tests/it/ganache.rs @@ -1,6 +1,9 @@ //! tests against local ganache for local debug purposes #![allow(unused)] -use crate::init_tracing; +use crate::{ + init_tracing, + utils::{ContractInstanceCompat, DeploymentTxFactoryCompat}, +}; use ethers::{ abi::Address, contract::{Contract, ContractFactory, ContractInstance}, @@ -11,7 +14,7 @@ use ethers::{ types::{BlockNumber, U256}, utils::hex, }; -use ethers_solc::{project_util::TempProject, Artifact}; +use foundry_compilers::{project_util::TempProject, Artifact}; use std::sync::Arc; // the mnemonic used to start the local ganache instance @@ -115,7 +118,7 @@ contract Contract { let (abi, bytecode, _) = contract.into_contract_bytecode().into_parts(); - let factory = ContractFactory::new(abi.unwrap(), bytecode.unwrap(), Arc::clone(&client)); + let factory = ContractFactory::new_compat(abi.unwrap(), bytecode.unwrap(), Arc::clone(&client)); let contract = factory.deploy(()).unwrap().legacy().send().await; contract.unwrap_err(); } @@ -153,13 +156,13 @@ contract Contract { let client = Arc::new(http_client()); // deploy successfully - let factory = ContractFactory::new(abi.clone().unwrap(), bytecode.unwrap(), client); + let factory = ContractFactory::new_compat(abi.clone().unwrap(), bytecode.unwrap(), client); let contract = factory.deploy(()).unwrap().legacy().send().await.unwrap(); let provider = SignerMiddleware::new( Provider::::try_from("http://127.0.0.1:8545").unwrap(), ganache_wallet2(), ); - let contract = ContractInstance::new(contract.address(), abi.unwrap(), provider); + let contract = ContractInstance::new_compat(contract.address(), abi.unwrap(), provider); let resp = contract.method::<_, U256>("getSecret", ()).unwrap().legacy().call().await; resp.unwrap_err(); diff --git a/crates/anvil/tests/it/geth.rs b/crates/anvil/tests/it/geth.rs index 31429d0bd..e1c9bc7b8 100644 --- a/crates/anvil/tests/it/geth.rs +++ b/crates/anvil/tests/it/geth.rs @@ -1,6 +1,9 @@ //! tests against local geth for local debug purposes -use crate::abi::VENDING_MACHINE_CONTRACT; +use crate::{ + abi::VENDING_MACHINE_CONTRACT, + utils::{ContractInstanceCompat, DeploymentTxFactoryCompat}, +}; use ethers::{ abi::Address, contract::{Contract, ContractFactory}, @@ -9,7 +12,7 @@ use ethers::{ types::U256, utils::WEI_IN_ETHER, }; -use ethers_solc::{project_util::TempProject, Artifact}; +use foundry_compilers::{project_util::TempProject, Artifact}; use futures::StreamExt; use std::sync::Arc; use tokio::time::timeout; @@ -52,7 +55,7 @@ async fn test_geth_revert_transaction() { // deploy successfully let factory = - ContractFactory::new(abi.clone().unwrap(), bytecode.unwrap(), Arc::clone(&client)); + ContractFactory::new_compat(abi.clone().unwrap(), bytecode.unwrap(), Arc::clone(&client)); let mut tx = factory.deploy(()).unwrap().tx; tx.set_from(account); @@ -60,7 +63,7 @@ async fn test_geth_revert_transaction() { let resp = client.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); let contract = - Contract::>::new(resp.contract_address.unwrap(), abi.unwrap(), client); + Contract::>::new_compat(resp.contract_address.unwrap(), abi.unwrap(), client); let ten = WEI_IN_ETHER.saturating_mul(10u64.into()); let call = contract.method::<_, ()>("buyRevert", ten).unwrap().value(ten).from(account); diff --git a/crates/anvil/tests/it/otterscan.rs b/crates/anvil/tests/it/otterscan.rs index 8dca5f7a2..6bbd987a8 100644 --- a/crates/anvil/tests/it/otterscan.rs +++ b/crates/anvil/tests/it/otterscan.rs @@ -1,7 +1,9 @@ //! tests for otterscan endpoints use crate::{ abi::MulticallContract, - utils::{ethers_http_provider, ethers_ws_provider}, + utils::{ + ethers_http_provider, ethers_ws_provider, ContractInstanceCompat, DeploymentTxFactoryCompat, + }, }; use alloy_primitives::U256 as rU256; use alloy_rpc_types::{BlockNumberOrTag, BlockTransactions}; @@ -19,8 +21,8 @@ use ethers::{ types::{Bytes, TransactionRequest}, utils::get_contract_address, }; -use ethers_solc::{project_util::TempProject, Artifact}; use foundry_common::types::{ToAlloy, ToEthers}; +use foundry_compilers::{project_util::TempProject, Artifact}; use std::{collections::VecDeque, str::FromStr, sync::Arc}; #[tokio::test(flavor = "multi_thread")] @@ -136,10 +138,10 @@ contract Contract { let client = Arc::new(SignerMiddleware::new(provider, wallets[0].clone())); // deploy successfully - let factory = ContractFactory::new(abi.clone().unwrap(), bytecode.unwrap(), client); + let factory = ContractFactory::new_compat(abi.clone().unwrap(), bytecode.unwrap(), client); let contract = factory.deploy(()).unwrap().send().await.unwrap(); - let contract = ContractInstance::new( + let contract = ContractInstance::new_compat( contract.address(), abi.unwrap(), SignerMiddleware::new(ethers_http_provider(&handle.http_endpoint()), wallets[1].clone()), @@ -194,10 +196,10 @@ contract Contract { let client = Arc::new(SignerMiddleware::new(provider, wallets[0].clone())); // deploy successfully - let factory = ContractFactory::new(abi.clone().unwrap(), bytecode.unwrap(), client); + let factory = ContractFactory::new_compat(abi.clone().unwrap(), bytecode.unwrap(), client); let contract = factory.deploy(()).unwrap().send().await.unwrap(); - let contract = ContractInstance::new( + let contract = ContractInstance::new_compat( contract.address(), abi.unwrap(), SignerMiddleware::new(ethers_http_provider(&handle.http_endpoint()), wallets[1].clone()), @@ -307,10 +309,10 @@ contract Contract { let client = Arc::new(SignerMiddleware::new(provider, wallets[0].clone())); // deploy successfully - let factory = ContractFactory::new(abi.clone().unwrap(), bytecode.unwrap(), client); + let factory = ContractFactory::new_compat(abi.clone().unwrap(), bytecode.unwrap(), client); let contract = factory.deploy(()).unwrap().send().await.unwrap(); - let contract = ContractInstance::new( + let contract = ContractInstance::new_compat( contract.address(), abi.unwrap(), SignerMiddleware::new(ethers_http_provider(&handle.http_endpoint()), wallets[1].clone()), @@ -397,7 +399,7 @@ contract Contract { let client = Arc::new(SignerMiddleware::new(provider, wallet)); // deploy successfully - let factory = ContractFactory::new(abi.clone().unwrap(), bytecode.unwrap(), client); + let factory = ContractFactory::new_compat(abi.clone().unwrap(), bytecode.unwrap(), client); let contract = factory.deploy(()).unwrap().send().await.unwrap(); let call = contract.method::<_, ()>("trigger_revert", ()).unwrap().gas(150_000u64); diff --git a/crates/anvil/tests/it/revert.rs b/crates/anvil/tests/it/revert.rs index 45da5eb2e..6ba4a67f7 100644 --- a/crates/anvil/tests/it/revert.rs +++ b/crates/anvil/tests/it/revert.rs @@ -6,7 +6,7 @@ use ethers::{ types::U256, utils::WEI_IN_ETHER, }; -use ethers_solc::{project_util::TempProject, Artifact}; +use foundry_compilers::{project_util::TempProject, Artifact}; use std::sync::Arc; #[tokio::test(flavor = "multi_thread")] diff --git a/crates/anvil/tests/it/traces.rs b/crates/anvil/tests/it/traces.rs index 309621ac5..8b506f3cd 100644 --- a/crates/anvil/tests/it/traces.rs +++ b/crates/anvil/tests/it/traces.rs @@ -1,6 +1,8 @@ use crate::{ fork::fork_config, - utils::{ethers_http_provider, ethers_ws_provider}, + utils::{ + ethers_http_provider, ethers_ws_provider, ContractInstanceCompat, DeploymentTxFactoryCompat, + }, }; use alloy_primitives::U256; use anvil::{spawn, Hardfork, NodeConfig}; @@ -13,8 +15,8 @@ use ethers::{ types::{ActionType, Address, GethDebugTracingCallOptions, Trace}, utils::hex, }; -use ethers_solc::{project_util::TempProject, Artifact}; use foundry_common::types::{ToAlloy, ToEthers}; +use foundry_compilers::{project_util::TempProject, Artifact}; use std::sync::Arc; #[tokio::test(flavor = "multi_thread")] @@ -82,10 +84,10 @@ contract Contract { let client = Arc::new(SignerMiddleware::new(provider, wallets[0].clone())); // deploy successfully - let factory = ContractFactory::new(abi.clone().unwrap(), bytecode.unwrap(), client); + let factory = ContractFactory::new_compat(abi.clone().unwrap(), bytecode.unwrap(), client); let contract = factory.deploy(()).unwrap().send().await.unwrap(); - let contract = ContractInstance::new( + let contract = ContractInstance::new_compat( contract.address(), abi.unwrap(), SignerMiddleware::new(ethers_http_provider(&handle.http_endpoint()), wallets[1].clone()), @@ -132,10 +134,10 @@ contract Contract { let client = Arc::new(SignerMiddleware::new(provider, wallets[0].clone())); // deploy successfully - let factory = ContractFactory::new(abi.clone().unwrap(), bytecode.unwrap(), client); + let factory = ContractFactory::new_compat(abi.clone().unwrap(), bytecode.unwrap(), client); let contract = factory.deploy(()).unwrap().send().await.unwrap(); - let contract = ContractInstance::new( + let contract = ContractInstance::new_compat( contract.address(), abi.unwrap(), SignerMiddleware::new(ethers_http_provider(&handle.http_endpoint()), wallets[1].clone()), diff --git a/crates/anvil/tests/it/utils.rs b/crates/anvil/tests/it/utils.rs index 7fdd90823..9aa077094 100644 --- a/crates/anvil/tests/it/utils.rs +++ b/crates/anvil/tests/it/utils.rs @@ -1,8 +1,14 @@ +use alloy_json_abi::JsonAbi; +use alloy_primitives::Bytes; use ethers::{ addressbook::contract, + contract::ContractInstance, + middleware::Middleware, + prelude::DeploymentTxFactory, types::{Address, Chain}, }; use foundry_common::provider::ethers::{ProviderBuilder, RetryProvider}; +use std::borrow::Borrow; /// Returns a set of various contract addresses pub fn contract_addresses(chain: Chain) -> Vec

{ @@ -29,3 +35,50 @@ pub fn ethers_ws_provider(ws_endpoint: &str) -> RetryProvider { pub fn ethers_ipc_provider(ipc_endpoint: Option) -> Option { ProviderBuilder::new(&ipc_endpoint?).build().ok() } + +/// Temporary helper trait for compatibility with ethers +pub trait ContractInstanceCompat +where + B: Borrow, + M: Middleware, +{ + fn new_compat(address: Address, abi: JsonAbi, client: B) -> Self; +} + +impl ContractInstanceCompat for ContractInstance +where + B: Borrow, + M: Middleware, +{ + fn new_compat(address: Address, abi: JsonAbi, client: B) -> Self { + let json = serde_json::to_string(&abi).unwrap(); + ContractInstance::new( + address, + serde_json::from_str::(&json).unwrap(), + client, + ) + } +} + +pub trait DeploymentTxFactoryCompat +where + B: Borrow + Clone, + M: Middleware, +{ + fn new_compat(abi: JsonAbi, bytecode: Bytes, client: B) -> Self; +} + +impl DeploymentTxFactoryCompat for DeploymentTxFactory +where + B: Borrow + Clone, + M: Middleware, +{ + fn new_compat(abi: JsonAbi, bytecode: Bytes, client: B) -> Self { + let json = serde_json::to_string(&abi).unwrap(); + DeploymentTxFactory::new( + serde_json::from_str::(&json).unwrap(), + bytecode.as_ref().to_vec().into(), + client, + ) + } +} diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 46e9ffd38..84abd4637 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -96,7 +96,7 @@ globset = "0.4" paste = "1.0" path-slash = "0.2" pretty_assertions.workspace = true -svm = { package = "svm-rs", version = "0.3", default-features = false, features = ["rustls"] } +svm = { package = "svm-rs", version = "0.4", default-features = false, features = ["rustls"] } tempfile = "3" tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] } diff --git a/crates/forge/tests/cli/svm.rs b/crates/forge/tests/cli/svm.rs index 431a36a1c..cbdd56f9d 100644 --- a/crates/forge/tests/cli/svm.rs +++ b/crates/forge/tests/cli/svm.rs @@ -11,7 +11,7 @@ use svm::Platform; /// 3. svm bumped in foundry-compilers /// 4. foundry-compilers update with any breaking changes /// 5. upgrade the `LATEST_SOLC` -const LATEST_SOLC: Version = Version::new(0, 8, 24); +const LATEST_SOLC: Version = Version::new(0, 8, 25); macro_rules! ensure_svm_releases { ($($test:ident => $platform:ident),* $(,)?) => {$( From 71ad565051ead9bd2a11cbaa6a86b971b9efe6a6 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 18 Mar 2024 18:33:09 +0100 Subject: [PATCH 081/430] chore: don't create a backend unnecessarily in ensure_success (#7429) * chore: don't create a backend unnecessarily in ensure_success * chore: cow it up --- crates/evm/evm/src/executors/fuzz/mod.rs | 19 +++++---- .../evm/evm/src/executors/invariant/error.rs | 4 +- .../evm/evm/src/executors/invariant/funcs.rs | 15 ++++--- crates/evm/evm/src/executors/invariant/mod.rs | 13 +++--- crates/evm/evm/src/executors/mod.rs | 41 ++++++++++--------- crates/evm/fuzz/src/error.rs | 2 - crates/forge/src/runner.rs | 3 +- 7 files changed, 48 insertions(+), 49 deletions(-) diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index ec980c01a..dfb7b6c60 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -18,7 +18,7 @@ use foundry_evm_fuzz::{ }; use foundry_evm_traces::CallTraceArena; use proptest::test_runner::{TestCaseError, TestError, TestRunner}; -use std::cell::RefCell; +use std::{borrow::Cow, cell::RefCell}; mod types; pub use types::{CaseOutcome, CounterExampleOutcome, FuzzOutcome}; @@ -208,19 +208,16 @@ impl FuzzedExecutor { should_fail: bool, calldata: alloy_primitives::Bytes, ) -> Result { - let call = self + let mut call = self .executor .call_raw(self.sender, address, calldata.clone(), U256::ZERO) .map_err(|_| TestCaseError::fail(FuzzError::FailedContractCall))?; - let state_changeset = call - .state_changeset - .as_ref() - .ok_or_else(|| TestCaseError::fail(FuzzError::EmptyChangeset))?; + let state_changeset = call.state_changeset.take().unwrap(); // Build fuzzer state collect_state_from_call( &call.logs, - state_changeset, + &state_changeset, state.clone(), &self.config.dictionary, ); @@ -235,8 +232,12 @@ impl FuzzedExecutor { .as_ref() .map_or_else(Default::default, |cheats| cheats.breakpoints.clone()); - let success = - self.executor.is_raw_call_success(address, state_changeset.clone(), &call, should_fail); + let success = self.executor.is_raw_call_success( + address, + Cow::Owned(state_changeset), + &call, + should_fail, + ); if success { Ok(FuzzOutcome::Case(CaseOutcome { diff --git a/crates/evm/evm/src/executors/invariant/error.rs b/crates/evm/evm/src/executors/invariant/error.rs index da29e657e..47ebc8776 100644 --- a/crates/evm/evm/src/executors/invariant/error.rs +++ b/crates/evm/evm/src/executors/invariant/error.rs @@ -13,7 +13,7 @@ use proptest::test_runner::TestError; use rand::{seq, thread_rng, Rng}; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use revm::primitives::U256; -use std::sync::Arc; +use std::{borrow::Cow, sync::Arc}; /// Stores information about failures and reverts of the invariant tests. #[derive(Clone, Default)] @@ -244,7 +244,7 @@ impl FailedInvariantCaseData { .expect("bad call to evm"); let is_success = executor.is_raw_call_success( self.addr, - call_result.state_changeset.take().unwrap(), + Cow::Owned(call_result.state_changeset.take().unwrap()), &call_result, false, ); diff --git a/crates/evm/evm/src/executors/invariant/funcs.rs b/crates/evm/evm/src/executors/invariant/funcs.rs index 237b4dad8..7f4694cdb 100644 --- a/crates/evm/evm/src/executors/invariant/funcs.rs +++ b/crates/evm/evm/src/executors/invariant/funcs.rs @@ -9,6 +9,7 @@ use foundry_evm_coverage::HitMaps; use foundry_evm_fuzz::invariant::{BasicTxDetails, InvariantContract}; use foundry_evm_traces::{load_contracts, TraceKind, Traces}; use revm::primitives::U256; +use std::borrow::Cow; /// Given the executor state, asserts that no invariant has been broken. Otherwise, it fills the /// external `invariant_failures.failed_invariant` map and returns a generic error. @@ -39,14 +40,12 @@ pub fn assert_invariants( ) .expect("EVM error"); - // This will panic and get caught by the executor - let is_err = call_result.reverted || - !executor.is_raw_call_success( - invariant_contract.address, - call_result.state_changeset.take().expect("we should have a state changeset"), - &call_result, - false, - ); + let is_err = !executor.is_raw_call_success( + invariant_contract.address, + Cow::Owned(call_result.state_changeset.take().unwrap()), + &call_result, + false, + ); if is_err { // We only care about invariants which we haven't broken yet. if invariant_failures.error.is_none() { diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 591fa7cf4..00877a00d 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -30,7 +30,7 @@ use proptest::{ test_runner::{TestCaseError, TestRunner}, }; use revm::{primitives::HashMap, DatabaseCommit}; -use std::{cell::RefCell, collections::BTreeMap, sync::Arc}; +use std::{borrow::Cow, cell::RefCell, collections::BTreeMap, sync::Arc}; mod error; pub use error::{InvariantFailures, InvariantFuzzError, InvariantFuzzTestResult}; @@ -236,7 +236,7 @@ impl<'a> InvariantExecutor<'a> { &inputs, &mut failures.borrow_mut(), &targeted_contracts, - state_changeset, + &state_changeset, self.config.fail_on_revert, self.config.shrink_sequence, self.config.shrink_run_limit, @@ -772,7 +772,7 @@ fn can_continue( calldata: &[BasicTxDetails], failures: &mut InvariantFailures, targeted_contracts: &FuzzRunIdentifiedContracts, - state_changeset: StateChangeset, + state_changeset: &StateChangeset, fail_on_revert: bool, shrink_sequence: bool, shrink_run_limit: usize, @@ -781,10 +781,9 @@ fn can_continue( let mut call_results = None; // Detect handler assertion failures first. - let handlers_failed = targeted_contracts - .lock() - .iter() - .any(|contract| !executor.is_success(*contract.0, false, state_changeset.clone(), false)); + let handlers_failed = targeted_contracts.lock().iter().any(|contract| { + !executor.is_success(*contract.0, false, Cow::Borrowed(state_changeset), false) + }); // Assert invariants IFF the call did not revert and the handlers did not fail. if !call_result.reverted && !handlers_failed { diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 469d6b6e4..51048002d 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -32,7 +32,7 @@ use revm::{ SpecId, TransactTo, TxEnv, }, }; -use std::collections::HashMap; +use std::{borrow::Cow, collections::HashMap}; mod builder; pub use builder::ExecutorBuilder; @@ -197,7 +197,7 @@ impl Executor { match res.state_changeset.as_ref() { Some(changeset) => { let success = self - .ensure_success(to, res.reverted, changeset.clone(), false) + .ensure_success(to, res.reverted, Cow::Borrowed(changeset), false) .map_err(|err| EvmError::Eyre(eyre::eyre!(err.to_string())))?; if success { Ok(res) @@ -472,7 +472,7 @@ impl Executor { &self, address: Address, reverted: bool, - state_changeset: StateChangeset, + state_changeset: Cow<'_, StateChangeset>, should_fail: bool, ) -> bool { self.ensure_success(address, reverted, state_changeset, should_fail).unwrap_or_default() @@ -496,7 +496,7 @@ impl Executor { pub fn is_raw_call_success( &self, address: Address, - state_changeset: StateChangeset, + state_changeset: Cow<'_, StateChangeset>, call_result: &RawCallResult, should_fail: bool, ) -> bool { @@ -511,7 +511,7 @@ impl Executor { &self, address: Address, reverted: bool, - state_changeset: StateChangeset, + state_changeset: Cow<'_, StateChangeset>, should_fail: bool, ) -> Result { if self.backend.has_snapshot_failure() { @@ -519,23 +519,24 @@ impl Executor { return Ok(should_fail) } - // Construct a new VM with the state changeset - let mut backend = self.backend.clone_empty(); - - // we only clone the test contract and cheatcode accounts, that's all we need to evaluate - // success - for addr in [address, CHEATCODE_ADDRESS] { - let acc = self.backend.basic_ref(addr)?.unwrap_or_default(); - backend.insert_account_info(addr, acc); - } - - // If this test failed any asserts, then this changeset will contain changes `false -> true` - // for the contract's `failed` variable and the `globalFailure` flag in the state of the - // cheatcode address which are both read when we call `"failed()(bool)"` in the next step - backend.commit(state_changeset); - let mut success = !reverted; if success { + // Construct a new bare-bones backend to evaluate success. + let mut backend = self.backend.clone_empty(); + + // We only clone the test contract and cheatcode accounts, + // that's all we need to evaluate success. + for addr in [address, CHEATCODE_ADDRESS] { + let acc = self.backend.basic_ref(addr)?.unwrap_or_default(); + backend.insert_account_info(addr, acc); + } + + // If this test failed any asserts, then this changeset will contain changes + // `false -> true` for the contract's `failed` variable and the `globalFailure` flag + // in the state of the cheatcode address, + // which are both read when we call `"failed()(bool)"` in the next step. + backend.commit(state_changeset.into_owned()); + // Check if a DSTest assertion failed let executor = Executor::new(backend, self.env.clone(), self.inspector.clone(), self.gas_limit); diff --git a/crates/evm/fuzz/src/error.rs b/crates/evm/fuzz/src/error.rs index 145afa5e7..1371f4969 100644 --- a/crates/evm/fuzz/src/error.rs +++ b/crates/evm/fuzz/src/error.rs @@ -8,8 +8,6 @@ pub enum FuzzError { UnknownContract, #[error("Failed contract call")] FailedContractCall, - #[error("Empty state changeset")] - EmptyChangeset, #[error("`vm.assume` reject")] AssumeReject, #[error("The `vm.assume` cheatcode rejected too many inputs ({0} allowed)")] diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index e8dba45a2..9ee3cf689 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -28,6 +28,7 @@ use foundry_evm::{ use proptest::test_runner::TestRunner; use rayon::prelude::*; use std::{ + borrow::Cow, collections::{BTreeMap, HashMap}, time::Instant, }; @@ -415,7 +416,7 @@ impl<'a> ContractRunner<'a> { let success = executor.is_success( setup.address, reverted, - state_changeset.expect("we should have a state changeset"), + Cow::Owned(state_changeset.unwrap()), should_fail, ); From dfefc0fff0304bfb00b568069fa6c899df8498af Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 18 Mar 2024 19:54:29 +0100 Subject: [PATCH 082/430] feat(cast): print config in create2 (#7427) --- crates/cast/bin/cmd/create2.rs | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/crates/cast/bin/cmd/create2.rs b/crates/cast/bin/cmd/create2.rs index 4494d5d68..dae91c642 100644 --- a/crates/cast/bin/cmd/create2.rs +++ b/crates/cast/bin/cmd/create2.rs @@ -135,14 +135,12 @@ impl Create2Args { let regex = RegexSetBuilder::new(regexs).case_insensitive(!case_sensitive).build()?; let init_code_hash = if let Some(init_code_hash) = init_code_hash { - let mut hash: [u8; 32] = [0; 32]; - hex::decode_to_slice(init_code_hash, &mut hash)?; - hash.into() + hex::FromHex::from_hex(init_code_hash) } else if let Some(init_code) = init_code { - keccak256(hex::decode(init_code)?) + hex::decode(init_code).map(keccak256) } else { unreachable!(); - }; + }?; let mut n_threads = std::thread::available_parallelism().map_or(1, |n| n.get()); if let Some(jobs) = jobs { @@ -168,7 +166,11 @@ impl Create2Args { rng.fill_bytes(remaining); } - println!("Starting to generate deterministic contract address..."); + println!("Configuration:"); + println!("Init code hash: {init_code_hash}"); + println!("Regex patterns: {:?}", regex.patterns()); + println!(); + println!("Starting to generate deterministic contract address with {n_threads} threads..."); let mut handles = Vec::with_capacity(n_threads); let found = Arc::new(AtomicBool::new(false)); let timer = Instant::now(); @@ -201,7 +203,7 @@ impl Create2Args { // Calculate the `CREATE2` address. #[allow(clippy::needless_borrows_for_generic_args)] - let addr = deployer.create2(&salt.0, init_code_hash); + let addr = deployer.create2(&salt.0, &init_code_hash); // Check if the regex matches the calculated address' checksum. let _ = addr.to_checksum_raw(&mut checksum, None); @@ -211,7 +213,7 @@ impl Create2Args { if regex.matches(s).into_iter().count() == regex_len { // Notify other threads that we found a result. found.store(true, Ordering::Relaxed); - break Some((salt.0, addr)); + break Some((addr, salt.0)); } // Increment the salt for the next iteration. @@ -221,15 +223,11 @@ impl Create2Args { } let results = handles.into_iter().filter_map(|h| h.join().unwrap()).collect::>(); - println!("Successfully found contract address(es) in {:?}", timer.elapsed()); - for (i, (salt, address)) in results.iter().enumerate() { - if i > 0 { - println!("---"); - } - println!("Address: {address}\nSalt: {salt} ({})", U256::from_be_bytes(salt.0)); - } + let (address, salt) = results.into_iter().next().unwrap(); + println!("Successfully found contract address in {:?}", timer.elapsed()); + println!("Address: {address}"); + println!("Salt: {salt} ({})", U256::from_be_bytes(salt.0)); - let (salt, address) = results.into_iter().next().unwrap(); Ok(Create2Output { address, salt }) } } From 04fca21441a90301f77daae5f54b40c3dfc220a5 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 18 Mar 2024 20:49:36 +0100 Subject: [PATCH 083/430] refactor(evm): executor API, merge evm result types (#7422) * refactor(evm): executor API, merge evm result types * chore: MSRV * fix: persist cheatcode state * log * fix: setup revert --- Cargo.toml | 2 +- clippy.toml | 2 +- crates/cli/src/utils/cmd.rs | 7 +- crates/common/src/abi.rs | 35 -- crates/config/src/macros.rs | 18 +- crates/evm/evm/src/executors/invariant/mod.rs | 263 ++++------ crates/evm/evm/src/executors/mod.rs | 456 ++++++++---------- crates/forge/src/result.rs | 6 +- crates/forge/src/runner.rs | 87 ++-- crates/script/src/runner.rs | 48 +- 10 files changed, 381 insertions(+), 543 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 860175871..54364afa6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ resolver = "2" [workspace.package] version = "0.2.0" edition = "2021" -rust-version = "1.75" # Remember to update clippy.toml as well +rust-version = "1.76" # Remember to update clippy.toml as well authors = ["Foundry Contributors"] license = "MIT OR Apache-2.0" homepage = "https://github.com/foundry-rs/foundry" diff --git a/clippy.toml b/clippy.toml index cc4ad18b1..472818efe 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1 +1 @@ -msrv = "1.75" +msrv = "1.76" diff --git a/crates/cli/src/utils/cmd.rs b/crates/cli/src/utils/cmd.rs index 1ea54d3f6..e58fd937d 100644 --- a/crates/cli/src/utils/cmd.rs +++ b/crates/cli/src/utils/cmd.rs @@ -13,7 +13,7 @@ use foundry_config::{error::ExtractConfigError, figment::Figment, Chain, Config, use foundry_debugger::Debugger; use foundry_evm::{ debug::DebugArena, - executors::{DeployResult, EvmError, ExecutionErr, RawCallResult}, + executors::{DeployResult, EvmError, RawCallResult}, opts::EvmOpts, traces::{ identifier::{EtherscanIdentifier, SignaturesIdentifier}, @@ -339,8 +339,7 @@ impl From for TraceResult { impl From for TraceResult { fn from(result: DeployResult) -> Self { - let DeployResult { gas_used, traces, debug, .. } = result; - + let RawCallResult { gas_used, traces, debug, .. } = result.raw; Self { success: true, traces: vec![(TraceKind::Execution, traces.expect("traces is None"))], @@ -356,7 +355,7 @@ impl TryFrom for TraceResult { fn try_from(err: EvmError) -> Result { match err { EvmError::Execution(err) => { - let ExecutionErr { reverted, gas_used, traces, debug: run_debug, .. } = *err; + let RawCallResult { reverted, gas_used, traces, debug: run_debug, .. } = err.raw; Ok(TraceResult { success: !reverted, traces: vec![(TraceKind::Execution, traces.expect("traces is None"))], diff --git a/crates/common/src/abi.rs b/crates/common/src/abi.rs index 66a7c9891..b08620d88 100644 --- a/crates/common/src/abi.rs +++ b/crates/common/src/abi.rs @@ -68,41 +68,6 @@ pub fn abi_decode_calldata( Ok(res) } -/// Helper trait for converting types to Functions. Helpful for allowing the `call` -/// function on the EVM to be generic over `String`, `&str` and `Function`. -pub trait IntoFunction { - /// Consumes self and produces a function - /// - /// # Panics - /// - /// This function does not return a Result, so it is expected that the consumer - /// uses it correctly so that it does not panic. - fn into(self) -> Function; -} - -impl IntoFunction for Function { - fn into(self) -> Function { - self - } -} - -impl IntoFunction for String { - #[track_caller] - fn into(self) -> Function { - IntoFunction::into(self.as_str()) - } -} - -impl<'a> IntoFunction for &'a str { - #[track_caller] - fn into(self) -> Function { - match get_func(self) { - Ok(func) => func, - Err(e) => panic!("could not parse function: {e}"), - } - } -} - /// Given a function signature string, it tries to parse it as a `Function` pub fn get_func(sig: &str) -> Result { Function::parse(sig).wrap_err("could not parse function signature") diff --git a/crates/config/src/macros.rs b/crates/config/src/macros.rs index 95b111bdb..b84876b8e 100644 --- a/crates/config/src/macros.rs +++ b/crates/config/src/macros.rs @@ -59,12 +59,10 @@ macro_rules! impl_figment_convert { ($name:ty) => { impl<'a> From<&'a $name> for $crate::figment::Figment { fn from(args: &'a $name) -> Self { - if let Some(root) = args.root.clone() { - $crate::Config::figment_with_root(root) - } else { - $crate::Config::figment_with_root($crate::find_project_root_path(None).unwrap()) - } - .merge(args) + let root = args.root.clone() + .unwrap_or_else(|| $crate::find_project_root_path(None) + .unwrap_or_else(|e| panic!("could not find project root: {e}"))); + $crate::Config::figment_with_root(root).merge(args) } } @@ -79,8 +77,8 @@ macro_rules! impl_figment_convert { impl<'a> From<&'a $name> for $crate::figment::Figment { fn from(args: &'a $name) -> Self { let mut figment: $crate::figment::Figment = From::from(&args.$start); - $ ( - figment = figment.merge(&args.$more); + $( + figment = figment.merge(&args.$more); )* figment } @@ -97,8 +95,8 @@ macro_rules! impl_figment_convert { impl<'a> From<&'a $name> for $crate::figment::Figment { fn from(args: &'a $name) -> Self { let mut figment: $crate::figment::Figment = From::from(&args.$start); - $ ( - figment = figment.merge(&args.$more); + $( + figment = figment.merge(&args.$more); )* figment = figment.merge(args); figment diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 00877a00d..622b94bf7 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -2,9 +2,8 @@ use crate::{ executors::{Executor, RawCallResult}, inspectors::Fuzzer, }; -use alloy_dyn_abi::DynSolValue; -use alloy_json_abi::JsonAbi; use alloy_primitives::{Address, FixedBytes, U256}; +use alloy_sol_types::{sol, SolCall}; use eyre::{eyre, ContextCompat, Result}; use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; use foundry_config::{FuzzDictionaryConfig, InvariantConfig}; @@ -19,7 +18,7 @@ use foundry_evm_fuzz::{ }, strategies::{ build_initial_state, collect_created_contracts, collect_state_from_call, invariant_strat, - override_call_strat, EvmFuzzState, + override_call_strat, CalldataFuzzDictionary, EvmFuzzState, }, FuzzCase, FuzzedCases, }; @@ -33,13 +32,60 @@ use revm::{primitives::HashMap, DatabaseCommit}; use std::{borrow::Cow, cell::RefCell, collections::BTreeMap, sync::Arc}; mod error; +use self::error::FailedInvariantCaseData; pub use error::{InvariantFailures, InvariantFuzzError, InvariantFuzzTestResult}; -use foundry_evm_fuzz::strategies::CalldataFuzzDictionary; mod funcs; pub use funcs::{assert_invariants, replay_run}; -use self::error::FailedInvariantCaseData; +sol! { + interface IInvariantTest { + #[derive(Default)] + struct FuzzSelector { + address addr; + bytes4[] selectors; + } + + #[derive(Default)] + struct FuzzAbiSelector { + string contract_abi; + bytes4[] selectors; + } + + #[derive(Default)] + struct FuzzInterface { + address addr; + string[] artifacts; + } + + #[derive(Default)] + function excludeArtifacts() public view returns (string[] memory excludedArtifacts); + + #[derive(Default)] + function excludeContracts() public view returns (address[] memory excludedContracts); + + #[derive(Default)] + function excludeSenders() public view returns (address[] memory excludedSenders); + + #[derive(Default)] + function targetArtifacts() public view returns (string[] memory targetedArtifacts); + + #[derive(Default)] + function targetArtifactSelectors() public view returns (FuzzAbiSelector[] memory targetedArtifactSelectors); + + #[derive(Default)] + function targetContracts() public view returns (address[] memory targetedContracts); + + #[derive(Default)] + function targetSelectors() public view returns (FuzzSelector[] memory targetedSelectors); + + #[derive(Default)] + function targetSenders() public view returns (address[] memory targetedSenders); + + #[derive(Default)] + function targetInterfaces() public view returns (FuzzInterface[] memory targetedInterfaces); + } +} /// Alias for (Dictionary for fuzzing, initial contracts to fuzz and an InvariantStrategy). type InvariantPreparation = ( @@ -304,9 +350,9 @@ impl<'a> InvariantExecutor<'a> { invariant_contract: &InvariantContract<'_>, ) -> eyre::Result { // Finds out the chosen deployed contracts and/or senders. - self.select_contract_artifacts(invariant_contract.address, invariant_contract.abi)?; + self.select_contract_artifacts(invariant_contract.address)?; let (targeted_senders, targeted_contracts) = - self.select_contracts_and_senders(invariant_contract.address, invariant_contract.abi)?; + self.select_contracts_and_senders(invariant_contract.address)?; if targeted_contracts.is_empty() { eyre::bail!("No contracts to fuzz."); @@ -369,68 +415,25 @@ impl<'a> InvariantExecutor<'a> { /// Priority: /// /// targetArtifactSelectors > excludeArtifacts > targetArtifacts - pub fn select_contract_artifacts( - &mut self, - invariant_address: Address, - abi: &JsonAbi, - ) -> eyre::Result<()> { - // targetArtifactSelectors -> (string, bytes4[])[]. - let targeted_abi = self - .get_list::<(String, Vec>)>( - invariant_address, - abi, - "targetArtifactSelectors", - |v| { - if let Some(list) = v.as_array() { - list.iter().map(|val| { - if let Some((_, _str, elements)) = val.as_custom_struct() { - let name = elements[0].as_str().unwrap().to_string(); - let selectors = elements[1] - .as_array() - .unwrap() - .iter() - .map(|selector| { - FixedBytes::<4>::from_slice(&selector.as_fixed_bytes().unwrap().0[0..4]) - }) - .collect::>(); - (name, selectors) - } else { - panic!("Could not decode inner value of targetArtifactSelectors. This is a bug.") - } - }).collect::>() - } else { - panic!("Could not decode targetArtifactSelectors as array. This is a bug.") - } - }, - ) - .into_iter() - .collect::>(); + pub fn select_contract_artifacts(&mut self, invariant_address: Address) -> eyre::Result<()> { + let result = self + .call_sol_default(invariant_address, &IInvariantTest::targetArtifactSelectorsCall {}); // Insert them into the executor `targeted_abi`. - for (contract, selectors) in targeted_abi { - let identifier = self.validate_selected_contract(contract, &selectors.to_vec())?; - - self.artifact_filters - .targeted - .entry(identifier) - .or_default() - .extend(selectors.to_vec()); + for IInvariantTest::FuzzAbiSelector { contract_abi, selectors } in + result.targetedArtifactSelectors + { + let identifier = self.validate_selected_contract(contract_abi, &selectors)?; + self.artifact_filters.targeted.entry(identifier).or_default().extend(selectors); } - // targetArtifacts -> string[] - // excludeArtifacts -> string[]. - let [selected_abi, excluded_abi] = ["targetArtifacts", "excludeArtifacts"].map(|method| { - self.get_list::(invariant_address, abi, method, |v| { - if let Some(list) = v.as_array() { - list.iter().map(|v| v.as_str().unwrap().to_string()).collect::>() - } else { - panic!("targetArtifacts should be an array") - } - }) - }); + let selected = + self.call_sol_default(invariant_address, &IInvariantTest::targetArtifactsCall {}); + let excluded = + self.call_sol_default(invariant_address, &IInvariantTest::excludeArtifactsCall {}); // Insert `excludeArtifacts` into the executor `excluded_abi`. - for contract in excluded_abi { + for contract in excluded.excludedArtifacts { let identifier = self.validate_selected_contract(contract, &[])?; if !self.artifact_filters.excluded.contains(&identifier) { @@ -459,7 +462,7 @@ impl<'a> InvariantExecutor<'a> { // Insert `targetArtifacts` into the executor `targeted_abi`, if they have not been seen // before. - for contract in selected_abi { + for contract in selected.targetedArtifacts { let identifier = self.validate_selected_contract(contract, &[])?; if !self.artifact_filters.targeted.contains_key(&identifier) && @@ -497,28 +500,23 @@ impl<'a> InvariantExecutor<'a> { /// `targetContracts() -> address[]` and `excludeContracts() -> address[]`. pub fn select_contracts_and_senders( &self, - invariant_address: Address, - abi: &JsonAbi, + to: Address, ) -> eyre::Result<(SenderFilters, TargetedContracts)> { - let [targeted_senders, excluded_senders, selected, excluded] = - ["targetSenders", "excludeSenders", "targetContracts", "excludeContracts"].map( - |method| { - self.get_list::
(invariant_address, abi, method, |v| { - if let Some(list) = v.as_array() { - list.iter().map(|v| v.as_address().unwrap()).collect::>() - } else { - panic!("targetSenders should be an array") - } - }) - }, - ); + let targeted_senders = + self.call_sol_default(to, &IInvariantTest::targetSendersCall {}).targetedSenders; + let excluded_senders = + self.call_sol_default(to, &IInvariantTest::excludeSendersCall {}).excludedSenders; + let selected = + self.call_sol_default(to, &IInvariantTest::targetContractsCall {}).targetedContracts; + let excluded = + self.call_sol_default(to, &IInvariantTest::excludeContractsCall {}).excludedContracts; let mut contracts: TargetedContracts = self .setup_contracts .clone() .into_iter() .filter(|(addr, (identifier, _))| { - *addr != invariant_address && + *addr != to && *addr != CHEATCODE_ADDRESS && *addr != HARDHAT_CONSOLE_ADDRESS && (selected.is_empty() || selected.contains(addr)) && @@ -531,9 +529,9 @@ impl<'a> InvariantExecutor<'a> { .map(|(addr, (identifier, abi))| (addr, (identifier, abi, vec![]))) .collect(); - self.target_interfaces(invariant_address, abi, &mut contracts)?; + self.target_interfaces(to, &mut contracts)?; - self.select_selectors(invariant_address, abi, &mut contracts)?; + self.select_selectors(to, &mut contracts)?; Ok((SenderFilters::new(targeted_senders, excluded_senders), contracts)) } @@ -545,36 +543,11 @@ impl<'a> InvariantExecutor<'a> { pub fn target_interfaces( &self, invariant_address: Address, - abi: &JsonAbi, targeted_contracts: &mut TargetedContracts, ) -> eyre::Result<()> { - let interfaces = self.get_list::<(Address, Vec)>( - invariant_address, - abi, - "targetInterfaces", - |v| { - if let Some(l) = v.as_array() { - l.iter() - .map(|v| { - if let Some((_, _names, elements)) = v.as_custom_struct() { - let addr = elements[0].as_address().unwrap(); - let interfaces = elements[1] - .as_array() - .unwrap() - .iter() - .map(|v| v.as_str().unwrap().to_string()) - .collect::>(); - (addr, interfaces) - } else { - panic!("targetInterfaces should be a tuple array") - } - }) - .collect::>() - } else { - panic!("targetInterfaces should be a tuple array") - } - }, - ); + let interfaces = self + .call_sol_default(invariant_address, &IInvariantTest::targetInterfacesCall {}) + .targetedInterfaces; // Since `targetInterfaces` returns a tuple array there is no guarantee // that the addresses are unique this map is used to merge functions of @@ -585,9 +558,9 @@ impl<'a> InvariantExecutor<'a> { // Loop through each address and its associated artifact identifiers. // We're borrowing here to avoid taking full ownership. - for (addr, identifiers) in &interfaces { + for IInvariantTest::FuzzInterface { addr, artifacts } in &interfaces { // Identifiers are specified as an array, so we loop through them. - for identifier in identifiers { + for identifier in artifacts { // Try to find the contract by name or identifier in the project's contracts. if let Some((_, (abi, _))) = self.project_contracts.find_by_name_or_identifier(identifier)? @@ -618,10 +591,8 @@ impl<'a> InvariantExecutor<'a> { pub fn select_selectors( &self, address: Address, - abi: &JsonAbi, targeted_contracts: &mut TargetedContracts, ) -> eyre::Result<()> { - // `targetArtifactSelectors() -> (string, bytes4[])[]`. let some_abi_selectors = self .artifact_filters .targeted @@ -639,37 +610,9 @@ impl<'a> InvariantExecutor<'a> { } } - // `targetSelectors() -> (address, bytes4[])[]`. - let selectors = - self.get_list::<(Address, Vec>)>(address, abi, "targetSelectors", |v| { - if let Some(l) = v.as_array() { - l.iter() - .map(|val| { - if let Some((_, _str, elements)) = val.as_custom_struct() { - let name = elements[0].as_address().unwrap(); - let selectors = elements[1] - .as_array() - .unwrap() - .iter() - .map(|selector| { - FixedBytes::<4>::from_slice( - &selector.as_fixed_bytes().unwrap().0[0..4], - ) - }) - .collect::>(); - (name, selectors) - } else { - panic!("targetSelectors should be a tuple array2") - } - }) - .collect::>() - } else { - panic!("targetSelectors should be a tuple array") - } - }); - - for (address, bytes4_array) in selectors.into_iter() { - self.add_address_with_functions(address, bytes4_array, targeted_contracts)?; + let selectors = self.call_sol_default(address, &IInvariantTest::targetSelectorsCall {}); + for IInvariantTest::FuzzSelector { addr, selectors } in selectors.targetedSelectors { + self.add_address_with_functions(addr, selectors, targeted_contracts)?; } Ok(()) } @@ -704,29 +647,15 @@ impl<'a> InvariantExecutor<'a> { Ok(()) } - /// Get the function output by calling the contract `method_name` function, encoded as a - /// [DynSolValue]. - fn get_list( - &self, - address: Address, - abi: &JsonAbi, - method_name: &str, - f: fn(DynSolValue) -> Vec, - ) -> Vec { - if let Some(func) = abi.functions().find(|func| func.name == method_name) { - if let Ok(call_result) = - self.executor.call::<_, _>(CALLER, address, func.clone(), vec![], U256::ZERO, None) - { - return f(call_result.result) - } else { - warn!( - "The function {} was found but there was an error querying its data.", - method_name - ); - } - }; - - Vec::new() + fn call_sol_default(&self, to: Address, args: &C) -> C::Return + where + C::Return: Default, + { + self.executor + .call_sol(CALLER, to, args, U256::ZERO, None) + .map(|c| c.decoded_result) + .inspect_err(|e| warn!(target: "forge::test", "failed calling {:?}: {e}", C::SIGNATURE)) + .unwrap_or_default() } } diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 51048002d..45abbfc9c 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -12,7 +12,7 @@ use crate::inspectors::{ use alloy_dyn_abi::{DynSolValue, FunctionExt, JsonAbiExt}; use alloy_json_abi::Function; use alloy_primitives::{Address, Bytes, Log, U256}; -use foundry_common::{abi::IntoFunction, evm::Breakpoints}; +use alloy_sol_types::{sol, SolCall}; use foundry_evm_core::{ backend::{Backend, CowBackend, DatabaseError, DatabaseExt, DatabaseResult}, constants::{ @@ -46,6 +46,13 @@ pub use invariant::InvariantExecutor; mod tracing; pub use tracing::TracingExecutor; +sol! { + interface ITest { + function setUp() external; + function failed() external view returns (bool); + } +} + /// A type that can execute calls /// /// The executor can be configured with various `revm::Inspector`s, like `Cheatcodes`. @@ -182,61 +189,52 @@ impl Executor { /// /// Ayn changes made during the setup call to env's block environment are persistent, for /// example `vm.chainId()` will change the `block.chainId` for all subsequent test calls. - pub fn setup(&mut self, from: Option
, to: Address) -> Result { + pub fn setup( + &mut self, + from: Option
, + to: Address, + rd: Option<&RevertDecoder>, + ) -> Result { trace!(?from, ?to, "setting up contract"); let from = from.unwrap_or(CALLER); self.backend.set_test_contract(to).set_caller(from); - let res = self.call_committing::<_, _>(from, to, "setUp()", vec![], U256::ZERO, None)?; + let calldata = Bytes::from_static(&ITest::setUpCall::SELECTOR); + let mut res = self.call_raw_committing(from, to, calldata, U256::ZERO)?; + res = res.into_result(rd)?; // record any changes made to the block's environment during setup self.env.block = res.env.block.clone(); // and also the chainid, which can be set manually self.env.cfg.chain_id = res.env.cfg.chain_id; - match res.state_changeset.as_ref() { - Some(changeset) => { - let success = self - .ensure_success(to, res.reverted, Cow::Borrowed(changeset), false) - .map_err(|err| EvmError::Eyre(eyre::eyre!(err.to_string())))?; - if success { - Ok(res) - } else { - Err(EvmError::Execution(Box::new(ExecutionErr { - reverted: res.reverted, - reason: "execution error".to_owned(), - traces: res.traces, - gas_used: res.gas_used, - gas_refunded: res.gas_refunded, - stipend: res.stipend, - logs: res.logs, - debug: res.debug, - labels: res.labels, - state_changeset: None, - transactions: None, - }))) - } + if let Some(changeset) = res.state_changeset.as_ref() { + let success = self + .ensure_success(to, res.reverted, Cow::Borrowed(changeset), false) + .map_err(|err| EvmError::Eyre(eyre::eyre!(err)))?; + if !success { + return Err(res.into_execution_error("execution error".to_string()).into()); } - None => Ok(res), } + + Ok(res) } /// Performs a call to an account on the current state of the VM. /// /// The state after the call is persisted. - pub fn call_committing>, F: IntoFunction>( + pub fn call_committing( &mut self, from: Address, to: Address, - func: F, - args: T, + func: &Function, + args: &[DynSolValue], value: U256, rd: Option<&RevertDecoder>, ) -> Result { - let func = func.into(); - let calldata = Bytes::from(func.abi_encode_input(&args.into())?); + let calldata = Bytes::from(func.abi_encode_input(args)?); let result = self.call_raw_committing(from, to, calldata, value)?; - convert_call_result(rd, &func, result) + result.into_decoded_result(func, rd) } /// Performs a raw call to an account on the current state of the VM. @@ -256,40 +254,55 @@ impl Executor { } /// Executes the test function call - pub fn execute_test>, F: IntoFunction>( + pub fn execute_test( &mut self, from: Address, test_contract: Address, - func: F, - args: T, + func: &Function, + args: &[DynSolValue], value: U256, rd: Option<&RevertDecoder>, ) -> Result { - let func = func.into(); - let calldata = Bytes::from(func.abi_encode_input(&args.into())?.to_vec()); + let calldata = Bytes::from(func.abi_encode_input(args)?); // execute the call let env = self.build_test_env(from, TransactTo::Call(test_contract), calldata, value); - let call_result = self.call_raw_with_env(env)?; - convert_call_result(rd, &func, call_result) + let result = self.call_raw_with_env(env)?; + result.into_decoded_result(func, rd) } /// Performs a call to an account on the current state of the VM. /// /// The state after the call is not persisted. - pub fn call>, F: IntoFunction>( + pub fn call( &self, from: Address, to: Address, - func: F, - args: T, + func: &Function, + args: &[DynSolValue], value: U256, rd: Option<&RevertDecoder>, ) -> Result { - let func = func.into(); - let calldata = Bytes::from(func.abi_encode_input(&args.into())?.to_vec()); - let call_result = self.call_raw(from, to, calldata, value)?; - convert_call_result(rd, &func, call_result) + let calldata = Bytes::from(func.abi_encode_input(args)?); + let result = self.call_raw(from, to, calldata, value)?; + result.into_decoded_result(func, rd) + } + + /// Performs a call to an account on the current state of the VM. + /// + /// The state after the call is not persisted. + pub fn call_sol( + &self, + from: Address, + to: Address, + args: &C, + value: U256, + rd: Option<&RevertDecoder>, + ) -> Result, EvmError> { + let calldata = Bytes::from(args.abi_encode()); + let mut raw = self.call_raw(from, to, calldata, value)?; + raw = raw.into_result(rd)?; + Ok(CallResult { decoded_result: C::abi_decode_returns(&raw.result, false)?, raw }) } /// Performs a raw call to an account on the current state of the VM. @@ -335,12 +348,12 @@ impl Executor { /// Commit the changeset to the database and adjust `self.inspector_config` /// values according to the executed call result fn commit(&mut self, result: &mut RawCallResult) { - // Persist changes to db + // Persist changes to db. if let Some(changes) = &result.state_changeset { self.backend.commit(changes.clone()); } - // Persist cheatcode state + // Persist cheatcode state. let mut cheatcodes = result.cheatcodes.take(); if let Some(cheats) = cheatcodes.as_mut() { // Clear broadcastable transactions @@ -352,96 +365,48 @@ impl Executor { } self.inspector.cheatcodes = cheatcodes; - // Persist the changed environment + // Persist the changed environment. self.inspector.set_env(&result.env); } /// Deploys a contract using the given `env` and commits the new state to the underlying - /// database + /// database. + /// + /// # Panics + /// + /// Panics if `env.tx.transact_to` is not `TransactTo::Create(_)`. pub fn deploy_with_env( &mut self, env: EnvWithHandlerCfg, rd: Option<&RevertDecoder>, ) -> Result { - debug_assert!( + assert!( matches!(env.tx.transact_to, TransactTo::Create(_)), - "Expect create transaction" + "Expected create transaction, got {:?}", + env.tx.transact_to ); - trace!(sender=?env.tx.caller, "deploying contract"); + trace!(sender=%env.tx.caller, "deploying contract"); let mut result = self.call_raw_with_env(env)?; self.commit(&mut result); - - let RawCallResult { - exit_reason, - out, - gas_used, - gas_refunded, - logs, - labels, - traces, - debug, - env, - coverage, - .. - } = result; - - let result = match &out { - Some(Output::Create(data, _)) => data.to_owned(), - _ => Bytes::default(), - }; - - let address = match exit_reason { - return_ok!() => { - if let Some(Output::Create(_, Some(addr))) = out { - addr - } else { - return Err(EvmError::Execution(Box::new(ExecutionErr { - reverted: true, - reason: "Deployment succeeded, but no address was returned. This is a bug, please report it".to_string(), - traces, - gas_used, - gas_refunded: 0, - stipend: 0, - logs, - debug, - labels, - state_changeset: None, - transactions: None, - }))); - } - } - _ => { - let reason = rd.unwrap_or_default().decode(&result, Some(exit_reason)); - return Err(EvmError::Execution(Box::new(ExecutionErr { - reverted: true, - reason, - traces, - gas_used, - gas_refunded, - stipend: 0, - logs, - debug, - labels, - state_changeset: None, - transactions: None, - }))) - } + result = result.into_result(rd)?; + let Some(Output::Create(_, Some(address))) = result.out else { + panic!("Deployment succeeded, but no address was returned: {result:#?}"); }; // also mark this library as persistent, this will ensure that the state of the library is // persistent across fork swaps in forking mode self.backend.add_persistent_account(address); - trace!(address=?address, "deployed contract"); + debug!(%address, "deployed contract"); - Ok(DeployResult { address, gas_used, gas_refunded, logs, traces, debug, env, coverage }) + Ok(DeployResult { raw: result, address }) } /// Deploys a contract and commits the new state to the underlying database. /// /// Executes a CREATE transaction with the contract `code` and persistent database state - /// modifications + /// modifications. pub fn deploy( &mut self, from: Address, @@ -540,10 +505,12 @@ impl Executor { // Check if a DSTest assertion failed let executor = Executor::new(backend, self.env.clone(), self.inspector.clone(), self.gas_limit); - let call = executor.call(CALLER, address, "failed()(bool)", vec![], U256::ZERO, None); - if let Ok(CallResult { result: failed, .. }) = call { - debug!(?failed, "DSTest"); - success = !failed.as_bool().unwrap(); + let call = executor.call_sol(CALLER, address, &ITest::failedCall {}, U256::ZERO, None); + if let Ok(CallResult { raw: _, decoded_result: ITest::failedReturn { _0: failed } }) = + call + { + debug!(failed, "DSTest::failed()"); + success = !failed; } } @@ -592,26 +559,35 @@ impl Executor { /// Represents the context after an execution error occurred. #[derive(Debug, thiserror::Error)] -#[error("Execution reverted: {reason} (gas: {gas_used})")] +#[error("execution reverted: {reason} (gas: {})", raw.gas_used)] pub struct ExecutionErr { - pub reverted: bool, + /// The raw result of the call. + pub raw: RawCallResult, + /// The revert reason. pub reason: String, - pub gas_used: u64, - pub gas_refunded: u64, - pub stipend: u64, - pub logs: Vec, - pub traces: Option, - pub debug: Option, - pub labels: HashMap, - pub transactions: Option, - pub state_changeset: Option, +} + +impl std::ops::Deref for ExecutionErr { + type Target = RawCallResult; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.raw + } +} + +impl std::ops::DerefMut for ExecutionErr { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.raw + } } #[derive(Debug, thiserror::Error)] pub enum EvmError { /// Error which occurred during execution of a transaction #[error(transparent)] - Execution(Box), + Execution(#[from] Box), /// Error which occurred during ABI encoding/decoding #[error(transparent)] AbiError(#[from] alloy_dyn_abi::Error), @@ -623,62 +599,41 @@ pub enum EvmError { Eyre(#[from] eyre::Error), } +impl From for EvmError { + fn from(err: ExecutionErr) -> Self { + EvmError::Execution(Box::new(err)) + } +} + +impl From for EvmError { + fn from(err: alloy_sol_types::Error) -> Self { + EvmError::AbiError(err.into()) + } +} + /// The result of a deployment. #[derive(Debug)] pub struct DeployResult { + /// The raw result of the deployment. + pub raw: RawCallResult, /// The address of the deployed contract pub address: Address, - /// The gas cost of the deployment - pub gas_used: u64, - /// The refunded gas - pub gas_refunded: u64, - /// The logs emitted during the deployment - pub logs: Vec, - /// The traces of the deployment - pub traces: Option, - /// The debug nodes of the call - pub debug: Option, - /// The `revm::Env` after deployment - pub env: EnvWithHandlerCfg, - /// The coverage info collected during the deployment - pub coverage: Option, } -/// The result of a call. -#[derive(Debug)] -pub struct CallResult { - pub skipped: bool, - /// Whether the call reverted or not - pub reverted: bool, - /// The decoded result of the call - pub result: DynSolValue, - /// The gas used for the call - pub gas_used: u64, - /// The refunded gas for the call - pub gas_refunded: u64, - /// The initial gas stipend for the transaction - pub stipend: u64, - /// The logs emitted during the call - pub logs: Vec, - /// The labels assigned to addresses during the call - pub labels: HashMap, - /// The traces of the call - pub traces: Option, - /// The coverage info collected during the call - pub coverage: Option, - /// The debug nodes of the call - pub debug: Option, - /// Scripted transactions generated from this call - pub transactions: Option, - /// The changeset of the state. - /// - /// This is only present if the changed state was not committed to the database (i.e. if you - /// used `call` and `call_raw` not `call_committing` or `call_raw_committing`). - pub state_changeset: Option, - /// The `revm::Env` after the call - pub env: EnvWithHandlerCfg, - /// breakpoints - pub breakpoints: Breakpoints, +impl std::ops::Deref for DeployResult { + type Target = RawCallResult; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.raw + } +} + +impl std::ops::DerefMut for DeployResult { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.raw + } } /// The result of a raw call. @@ -693,7 +648,7 @@ pub struct RawCallResult { /// This is tracked separately from revert because a snapshot failure can occur without a /// revert, since assert failures are stored in a global variable (ds-test legacy) pub has_snapshot_failure: bool, - /// The raw result of the call + /// The raw result of the call. pub result: Bytes, /// The gas used for the call pub gas_used: u64, @@ -753,6 +708,77 @@ impl Default for RawCallResult { } } +impl RawCallResult { + /// Converts the result of the call into an `EvmError`. + pub fn into_evm_error(self, rd: Option<&RevertDecoder>) -> EvmError { + if self.result[..] == crate::constants::MAGIC_SKIP[..] { + return EvmError::SkipError; + } + let reason = rd.unwrap_or_default().decode(&self.result, Some(self.exit_reason)); + EvmError::Execution(Box::new(self.into_execution_error(reason))) + } + + /// Converts the result of the call into an `ExecutionErr`. + pub fn into_execution_error(self, reason: String) -> ExecutionErr { + ExecutionErr { raw: self, reason } + } + + /// Returns an `EvmError` if the call failed, otherwise returns `self`. + pub fn into_result(self, rd: Option<&RevertDecoder>) -> Result { + if self.exit_reason.is_ok() { + Ok(self) + } else { + Err(self.into_evm_error(rd)) + } + } + + /// Decodes the result of the call with the given function. + pub fn into_decoded_result( + mut self, + func: &Function, + rd: Option<&RevertDecoder>, + ) -> Result { + self = self.into_result(rd)?; + let mut result = func.abi_decode_output(&self.result, false)?; + let decoded_result = if result.len() == 1 { + result.pop().unwrap() + } else { + // combine results into a tuple + DynSolValue::Tuple(result) + }; + Ok(CallResult { raw: self, decoded_result }) + } + + /// Returns the transactions generated from this call. + pub fn transactions(&self) -> Option<&BroadcastableTransactions> { + self.cheatcodes.as_ref().map(|c| &c.broadcastable_transactions) + } +} + +/// The result of a call. +pub struct CallResult { + /// The raw result of the call. + pub raw: RawCallResult, + /// The decoded result of the call. + pub decoded_result: T, +} + +impl std::ops::Deref for CallResult { + type Target = RawCallResult; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.raw + } +} + +impl std::ops::DerefMut for CallResult { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.raw + } +} + /// Calculates the initial gas stipend for a transaction fn calc_stipend(calldata: &[u8], spec: SpecId) -> u64 { let non_zero_data_cost = if SpecId::enabled(spec, SpecId::ISTANBUL) { 16 } else { 68 }; @@ -815,77 +841,3 @@ fn convert_executed_result( chisel_state, }) } - -fn convert_call_result( - rd: Option<&RevertDecoder>, - func: &Function, - call_result: RawCallResult, -) -> Result { - let RawCallResult { - result, - exit_reason: status, - reverted, - gas_used, - gas_refunded, - stipend, - logs, - labels, - traces, - coverage, - debug, - transactions, - state_changeset, - env, - .. - } = call_result; - - let breakpoints = call_result.cheatcodes.map(|c| c.breakpoints).unwrap_or_default(); - - match status { - return_ok!() => { - let mut result = func.abi_decode_output(&result, false)?; - let res = if result.len() == 1 { - result.pop().unwrap() - } else { - // combine results into a tuple - DynSolValue::Tuple(result) - }; - Ok(CallResult { - reverted, - result: res, - gas_used, - gas_refunded, - stipend, - logs, - labels, - traces, - coverage, - debug, - transactions, - state_changeset, - env, - breakpoints, - skipped: false, - }) - } - _ => { - if &result == crate::constants::MAGIC_SKIP { - return Err(EvmError::SkipError) - } - let reason = rd.unwrap_or_default().decode(&result, Some(status)); - Err(EvmError::Execution(Box::new(ExecutionErr { - reverted, - reason, - gas_used, - gas_refunded, - stipend, - logs, - traces, - debug, - labels, - transactions, - state_changeset, - }))) - } - } -} diff --git a/crates/forge/src/result.rs b/crates/forge/src/result.rs index 94c6d58fc..ad1173381 100644 --- a/crates/forge/src/result.rs +++ b/crates/forge/src/result.rs @@ -534,9 +534,9 @@ impl TestSetup { match error { EvmError::Execution(err) => { // force the tracekind to be setup so a trace is shown. - traces.extend(err.traces.map(|traces| (TraceKind::Setup, traces))); - logs.extend(err.logs); - labeled_addresses.extend(err.labels); + 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( diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 9ee3cf689..494a02d88 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -20,7 +20,7 @@ use foundry_evm::{ executors::{ fuzz::{CaseOutcome, CounterExampleOutcome, FuzzOutcome, FuzzedExecutor}, invariant::{replay_run, InvariantExecutor, InvariantFuzzError, InvariantFuzzTestResult}, - CallResult, EvmError, ExecutionErr, Executor, + EvmError, ExecutionErr, Executor, RawCallResult, }, fuzz::{invariant::InvariantContract, CounterExample}, traces::{load_contracts, TraceKind}, @@ -113,8 +113,8 @@ impl<'a> ContractRunner<'a> { Some(self.revert_decoder), ) { Ok(d) => { - logs.extend(d.logs); - traces.extend(d.traces.map(|traces| (TraceKind::Deployment, traces))); + 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())) @@ -136,8 +136,8 @@ impl<'a> ContractRunner<'a> { Some(self.revert_decoder), ) { Ok(d) => { - logs.extend(d.logs); - traces.extend(d.traces.map(|traces| (TraceKind::Deployment, traces))); + logs.extend(d.raw.logs); + traces.extend(d.raw.traces.map(|traces| (TraceKind::Deployment, traces))); d.address } Err(e) => { @@ -154,21 +154,20 @@ impl<'a> ContractRunner<'a> { // Optionally call the `setUp` function let setup = if setup { trace!("setting up"); - let (setup_logs, setup_traces, labeled_addresses, reason, coverage) = match self - .executor - .setup(None, address) - { - Ok(CallResult { traces, labels, logs, coverage, .. }) => { + 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!(contract=%address, "successfully setUp test"); (logs, traces, labels, None, coverage) } Err(EvmError::Execution(err)) => { - let ExecutionErr { traces, labels, logs, reason, .. } = *err; - error!(reason=%reason, contract=%address, "setUp failed"); - (logs, traces, labels, Some(format!("setup failed: {reason}")), None) + let ExecutionErr { + raw: RawCallResult { traces, labels, logs, coverage, .. }, + reason, + } = *err; + (logs, traces, labels, Some(format!("setup failed: {reason}")), coverage) } Err(err) => { - error!(reason=%err, contract=%address, "setUp failed"); (Vec::new(), None, HashMap::new(), Some(format!("setup failed: {err}")), None) } }; @@ -343,27 +342,30 @@ impl<'a> ContractRunner<'a> { let start = Instant::now(); let debug_arena; let (reverted, reason, gas, stipend, coverage, state_changeset, breakpoints) = - match executor.execute_test::<_, _>( + match executor.execute_test( self.sender, address, - func.clone(), - vec![], + func, + &[], U256::ZERO, Some(self.revert_decoder), ) { - Ok(CallResult { - reverted, - gas_used: gas, - stipend, - logs: execution_logs, - traces: execution_trace, - coverage: execution_coverage, - labels: new_labels, - state_changeset, - debug, - breakpoints, - .. - }) => { + Ok(res) => { + let RawCallResult { + reverted, + gas_used: gas, + stipend, + logs: execution_logs, + traces: execution_trace, + coverage: execution_coverage, + labels: new_labels, + state_changeset, + debug, + cheatcodes, + .. + } = res.raw; + + let breakpoints = cheatcodes.map(|c| c.breakpoints).unwrap_or_default(); traces.extend(execution_trace.map(|traces| (TraceKind::Execution, traces))); labeled_addresses.extend(new_labels); logs.extend(execution_logs); @@ -373,17 +375,18 @@ impl<'a> ContractRunner<'a> { (reverted, None, gas, stipend, coverage, state_changeset, breakpoints) } Err(EvmError::Execution(err)) => { - traces.extend(err.traces.map(|traces| (TraceKind::Execution, traces))); - labeled_addresses.extend(err.labels); - logs.extend(err.logs); - debug_arena = err.debug; + let ExecutionErr { raw, reason } = *err; + traces.extend(raw.traces.map(|traces| (TraceKind::Execution, traces))); + labeled_addresses.extend(raw.labels); + logs.extend(raw.logs); + debug_arena = raw.debug; ( - err.reverted, - Some(err.reason), - err.gas_used, - err.stipend, + raw.reverted, + Some(reason), + raw.gas_used, + raw.stipend, None, - err.state_changeset, + raw.state_changeset, Default::default(), ) } @@ -461,11 +464,11 @@ impl<'a> ContractRunner<'a> { // First, run the test normally to see if it needs to be skipped. let start = Instant::now(); - if let Err(EvmError::SkipError) = self.executor.clone().execute_test::<_, _>( + if let Err(EvmError::SkipError) = self.executor.clone().execute_test( self.sender, address, - func.clone(), - vec![], + func, + &[], U256::ZERO, Some(self.revert_decoder), ) { diff --git a/crates/script/src/runner.rs b/crates/script/src/runner.rs index d91af9dc6..23c25d133 100644 --- a/crates/script/src/runner.rs +++ b/crates/script/src/runner.rs @@ -4,7 +4,7 @@ use eyre::Result; use foundry_config::Config; use foundry_evm::{ constants::CALLER, - executors::{CallResult, DeployResult, EvmError, ExecutionErr, Executor, RawCallResult}, + executors::{DeployResult, EvmError, ExecutionErr, Executor, RawCallResult}, revm::interpreter::{return_ok, InstructionResult}, traces::{TraceKind, Traces}, }; @@ -55,12 +55,11 @@ impl ScriptRunner { let mut traces: Traces = libraries .iter() .filter_map(|code| { - let DeployResult { traces, .. } = self - .executor + self.executor .deploy(self.sender, code.clone(), U256::ZERO, None) - .expect("couldn't deploy library"); - - traces + .expect("couldn't deploy library") + .raw + .traces }) .map(|traces| (TraceKind::Deployment, traces)) .collect(); @@ -74,10 +73,8 @@ impl ScriptRunner { // Deploy an instance of the contract let DeployResult { address, - mut logs, - traces: constructor_traces, - debug: constructor_debug, - .. + raw: + RawCallResult { mut logs, traces: constructor_traces, debug: constructor_debug, .. }, } = self .executor .deploy(CALLER, code, U256::ZERO, None) @@ -90,8 +87,8 @@ impl ScriptRunner { self.executor.backend.set_test_contract(address); (true, 0, Default::default(), None, vec![constructor_debug].into_iter().collect()) } else { - match self.executor.setup(Some(self.sender), address) { - Ok(CallResult { + match self.executor.setup(Some(self.sender), address, None) { + Ok(RawCallResult { reverted, traces: setup_traces, labels, @@ -115,7 +112,7 @@ impl ScriptRunner { ) } Err(EvmError::Execution(err)) => { - let ExecutionErr { + let RawCallResult { reverted, traces: setup_traces, labels, @@ -124,7 +121,7 @@ impl ScriptRunner { gas_used, transactions, .. - } = *err; + } = err.raw; traces.extend(setup_traces.map(|traces| (TraceKind::Setup, traces))); logs.extend_from_slice(&setup_logs); @@ -193,20 +190,18 @@ impl ScriptRunner { if let Some(to) = to { self.call(from, to, calldata.unwrap_or_default(), value.unwrap_or(U256::ZERO), true) } else if to.is_none() { - let (address, gas_used, logs, traces, debug) = match self.executor.deploy( + let res = self.executor.deploy( from, calldata.expect("No data for create transaction"), value.unwrap_or(U256::ZERO), None, - ) { - Ok(DeployResult { address, gas_used, logs, traces, debug, .. }) => { - (address, gas_used, logs, traces, debug) - } + ); + let (address, RawCallResult { gas_used, logs, traces, debug, .. }) = match res { + Ok(DeployResult { address, raw }) => (address, raw), Err(EvmError::Execution(err)) => { - let ExecutionErr { reason, traces, gas_used, logs, debug, .. } = *err; + let ExecutionErr { raw, reason } = *err; println!("{}", Paint::red(format!("\nFailed with `{reason}`:\n"))); - - (Address::ZERO, gas_used, logs, traces, debug) + (Address::ZERO, raw) } Err(e) => eyre::bail!("Failed deploying contract: {e:?}"), }; @@ -216,14 +211,11 @@ impl ScriptRunner { success: address != Address::ZERO, gas_used, logs, + // Manually adjust gas for the trace to add back the stipend/real used gas traces: traces - .map(|traces| { - // Manually adjust gas for the trace to add back the stipend/real used gas - - vec![(TraceKind::Execution, traces)] - }) + .map(|traces| vec![(TraceKind::Execution, traces)]) .unwrap_or_default(), - debug: vec![debug].into_iter().collect(), + debug: debug.map(|debug| vec![debug]), address: Some(address), ..Default::default() }) From 6dfc6e7975308f9753acc410250099847efaed14 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 18 Mar 2024 21:06:36 +0100 Subject: [PATCH 084/430] feat: parse inline config with solang (#7431) * feat: parse inline config with solang * fix: multiple single docs are merged * fix: dont merge lines * fix: read absolute path * perf: fast path with src.contains * fix: split on dots * chore: clippy --- Cargo.lock | 1 + Cargo.toml | 1 + crates/config/Cargo.toml | 2 + crates/config/src/inline/mod.rs | 28 +-- crates/config/src/inline/natspec.rs | 310 +++++++++++++++++++++------- crates/forge/src/lib.rs | 20 +- crates/forge/tests/it/inline.rs | 44 ++-- 7 files changed, 267 insertions(+), 139 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dea2c7c4d..bd7a9fd78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3347,6 +3347,7 @@ dependencies = [ "serde", "serde_json", "serde_regex", + "solang-parser", "tempfile", "thiserror", "toml 0.8.11", diff --git a/Cargo.toml b/Cargo.toml index 54364afa6..ab21bddfc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,7 @@ debug = 1 # Solc and artifacts foundry-compilers.opt-level = 3 solang-parser.opt-level = 3 +lalrpop-util.opt-level = 3 serde_json.opt-level = 3 # EVM diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml index b3f4c514a..4f42da66d 100644 --- a/crates/config/Cargo.toml +++ b/crates/config/Cargo.toml @@ -18,6 +18,8 @@ alloy-chains = { workspace = true, features = ["serde"] } alloy-primitives = { workspace = true, features = ["serde"] } revm-primitives = { workspace = true, default-features = false, features = ["std"] } +solang-parser.workspace = true + dirs-next = "2" dunce = "1" eyre.workspace = true diff --git a/crates/config/src/inline/mod.rs b/crates/config/src/inline/mod.rs index af2cbe7e3..9989d5b76 100644 --- a/crates/config/src/inline/mod.rs +++ b/crates/config/src/inline/mod.rs @@ -3,7 +3,7 @@ pub use conf_parser::{parse_config_bool, parse_config_u32, validate_profiles, In pub use error::{InlineConfigError, InlineConfigParserError}; pub use natspec::NatSpec; use once_cell::sync::Lazy; -use std::{borrow::Cow, collections::HashMap}; +use std::collections::HashMap; mod conf_parser; mod error; @@ -25,22 +25,14 @@ static INLINE_CONFIG_PREFIX_SELECTED_PROFILE: Lazy = Lazy::new(|| { pub struct InlineConfig { /// Maps a (test-contract, test-function) pair /// to a specific configuration provided by the user. - configs: HashMap, T>, + configs: HashMap<(String, String), T>, } 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: C, fn_name: F) -> Option<&T> - where - C: Into, - F: Into, - { - // TODO use borrow - let key = InlineConfigKey { - contract: Cow::Owned(contract_id.into()), - function: Cow::Owned(fn_name.into()), - }; + pub fn get(&self, contract_id: &str, fn_name: &str) -> Option<&T> { + let key = (contract_id.to_string(), fn_name.to_string()); self.configs.get(&key) } @@ -51,21 +43,11 @@ impl InlineConfig { C: Into, F: Into, { - let key = InlineConfigKey { - contract: Cow::Owned(contract_id.into()), - function: Cow::Owned(fn_name.into()), - }; + let key = (contract_id.into(), fn_name.into()); self.configs.insert(key, config); } } -/// Represents a (test-contract, test-function) pair -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -struct InlineConfigKey<'a> { - contract: Cow<'a, str>, - function: Cow<'a, str>, -} - pub(crate) fn remove_whitespaces(s: &str) -> String { s.chars().filter(|c| !c.is_whitespace()).collect() } diff --git a/crates/config/src/inline/natspec.rs b/crates/config/src/inline/natspec.rs index 11557fd7a..3b62d40cc 100644 --- a/crates/config/src/inline/natspec.rs +++ b/crates/config/src/inline/natspec.rs @@ -4,10 +4,11 @@ use foundry_compilers::{ ProjectCompileOutput, }; use serde_json::Value; +use solang_parser::pt; use std::{collections::BTreeMap, path::Path}; /// Convenient struct to hold in-line per-test configurations -#[derive(Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct NatSpec { /// The parent contract of the natspec pub contract: String, @@ -28,14 +29,28 @@ impl NatSpec { pub fn parse(output: &ProjectCompileOutput, root: &Path) -> Vec { let mut natspecs: Vec = vec![]; + let solc = SolcParser::new(); + let solang = SolangParser::new(); for (id, artifact) in output.artifact_ids() { - let Some(ast) = &artifact.ast else { continue }; - let path = id.source.as_path(); - let path = path.strip_prefix(root).unwrap_or(path); - // id.identifier + let abs_path = id.source.as_path(); + let path = abs_path.strip_prefix(root).unwrap_or(abs_path); + let contract_name = id.name.split('.').next().unwrap(); + // `id.identifier` but with the stripped path. let contract = format!("{}:{}", path.display(), id.name); - let Some(node) = contract_root_node(&ast.nodes, &contract) else { continue }; - apply(&mut natspecs, &contract, node) + + let mut used_solc_ast = false; + if let Some(ast) = &artifact.ast { + if let Some(node) = solc.contract_root_node(&ast.nodes, &contract) { + solc.parse(&mut natspecs, &contract, node); + used_solc_ast = true; + } + } + + if !used_solc_ast { + if let Ok(src) = std::fs::read_to_string(abs_path) { + solang.parse(&mut natspecs, &src, &contract, contract_name); + } + } } natspecs @@ -63,89 +78,246 @@ impl NatSpec { /// Returns a list of all the configuration lines available in the natspec pub fn config_lines(&self) -> impl Iterator + '_ { - self.docs.lines().map(remove_whitespaces).filter(|line| line.contains(INLINE_CONFIG_PREFIX)) + self.docs.lines().filter(|line| line.contains(INLINE_CONFIG_PREFIX)).map(remove_whitespaces) } } -/// Given a list of nodes, find a "ContractDefinition" node that matches -/// the provided contract_id. -fn contract_root_node<'a>(nodes: &'a [Node], contract_id: &'a str) -> Option<&'a Node> { - for n in nodes.iter() { - if let NodeType::ContractDefinition = n.node_type { - let contract_data = &n.other; - if let Value::String(contract_name) = contract_data.get("name")? { - if contract_id.ends_with(contract_name) { - return Some(n) +struct SolcParser { + _private: (), +} + +impl SolcParser { + fn new() -> Self { + Self { _private: () } + } + + /// Given a list of nodes, find a "ContractDefinition" node that matches + /// the provided contract_id. + fn contract_root_node<'a>(&self, nodes: &'a [Node], contract_id: &str) -> Option<&'a Node> { + for n in nodes.iter() { + if let NodeType::ContractDefinition = n.node_type { + let contract_data = &n.other; + if let Value::String(contract_name) = contract_data.get("name")? { + if contract_id.ends_with(contract_name) { + return Some(n) + } } } } + None } - None -} -/// Implements a DFS over a compiler output node and its children. -/// If a natspec is found it is added to `natspecs` -fn apply(natspecs: &mut Vec, contract: &str, node: &Node) { - for n in node.nodes.iter() { - if let Some((function, docs, line)) = get_fn_data(n) { - natspecs.push(NatSpec { contract: contract.into(), function, line, docs }) + /// Implements a DFS over a compiler output node and its children. + /// If a natspec is found it is added to `natspecs` + fn parse(&self, natspecs: &mut Vec, contract: &str, node: &Node) { + for n in node.nodes.iter() { + if let Some((function, docs, line)) = self.get_fn_data(n) { + natspecs.push(NatSpec { contract: contract.into(), function, line, docs }) + } + self.parse(natspecs, contract, n); + } + } + + /// Given a compilation output node, if it is a function definition + /// that also contains a natspec then return a tuple of: + /// - Function name + /// - Natspec text + /// - Natspec position with format "row:col:length" + /// + /// Return None otherwise. + fn get_fn_data(&self, node: &Node) -> Option<(String, String, String)> { + if let NodeType::FunctionDefinition = node.node_type { + let fn_data = &node.other; + let fn_name: String = self.get_fn_name(fn_data)?; + let (fn_docs, docs_src_line): (String, String) = self.get_fn_docs(fn_data)?; + return Some((fn_name, fn_docs, docs_src_line)) + } + + None + } + + /// Given a dictionary of function data returns the name of the function. + fn get_fn_name(&self, fn_data: &BTreeMap) -> Option { + match fn_data.get("name")? { + Value::String(fn_name) => Some(fn_name.into()), + _ => None, + } + } + + /// Inspects Solc compiler output for documentation comments. Returns: + /// - `Some((String, String))` in case the function has natspec comments. First item is a + /// textual natspec representation, the second item is the natspec src line, in the form + /// "raw:col:length". + /// - `None` in case the function has not natspec comments. + fn get_fn_docs(&self, fn_data: &BTreeMap) -> Option<(String, String)> { + if let Value::Object(fn_docs) = fn_data.get("documentation")? { + if let Value::String(comment) = fn_docs.get("text")? { + if comment.contains(INLINE_CONFIG_PREFIX) { + let mut src_line = fn_docs + .get("src") + .map(|src| src.to_string()) + .unwrap_or_else(|| String::from("")); + + src_line.retain(|c| c != '"'); + return Some((comment.into(), src_line)) + } + } } - apply(natspecs, contract, n); + None } } -/// Given a compilation output node, if it is a function definition -/// that also contains a natspec then return a tuple of: -/// - Function name -/// - Natspec text -/// - Natspec position with format "row:col:length" -/// -/// Return None otherwise. -fn get_fn_data(node: &Node) -> Option<(String, String, String)> { - if let NodeType::FunctionDefinition = node.node_type { - let fn_data = &node.other; - let fn_name: String = get_fn_name(fn_data)?; - let (fn_docs, docs_src_line): (String, String) = get_fn_docs(fn_data)?; - return Some((fn_name, fn_docs, docs_src_line)) - } - - None +struct SolangParser { + _private: (), } -/// Given a dictionary of function data returns the name of the function. -fn get_fn_name(fn_data: &BTreeMap) -> Option { - match fn_data.get("name")? { - Value::String(fn_name) => Some(fn_name.into()), - _ => None, +impl SolangParser { + fn new() -> Self { + Self { _private: () } } -} -/// Inspects Solc compiler output for documentation comments. Returns: -/// - `Some((String, String))` in case the function has natspec comments. First item is a textual -/// natspec representation, the second item is the natspec src line, in the form "raw:col:length". -/// - `None` in case the function has not natspec comments. -fn get_fn_docs(fn_data: &BTreeMap) -> Option<(String, String)> { - if let Value::Object(fn_docs) = fn_data.get("documentation")? { - if let Value::String(comment) = fn_docs.get("text")? { - if comment.contains(INLINE_CONFIG_PREFIX) { - let mut src_line = fn_docs - .get("src") - .map(|src| src.to_string()) - .unwrap_or_else(|| String::from("")); - - src_line.retain(|c| c != '"'); - return Some((comment.into(), src_line)) + fn parse( + &self, + natspecs: &mut Vec, + src: &str, + contract_id: &str, + contract_name: &str, + ) { + // Fast path to avoid parsing the file. + if !src.contains(INLINE_CONFIG_PREFIX) { + return; + } + + let Ok((pt, comments)) = solang_parser::parse(src, 0) else { return }; + let mut prev_end = 0; + for item in &pt.0 { + let pt::SourceUnitPart::ContractDefinition(c) = item else { continue }; + let Some(id) = c.name.as_ref() else { continue }; + if id.name != contract_name { + continue + }; + for part in &c.parts { + let pt::ContractPart::FunctionDefinition(f) = part else { continue }; + let start = f.loc.start(); + // Parse doc comments in between the previous function and the current one. + let docs = solang_parser::doccomment::parse_doccomments(&comments, prev_end, start); + let docs = docs + .into_iter() + .flat_map(|doc| doc.into_comments()) + .filter(|doc| doc.value.contains(INLINE_CONFIG_PREFIX)); + for doc in docs { + natspecs.push(NatSpec { + contract: contract_id.to_string(), + function: f.name.as_ref().map(|id| id.to_string()).unwrap_or_default(), + line: "0:0:0".to_string(), + docs: doc.value, + }); + } + prev_end = f.loc.end(); } + prev_end = c.loc.end(); } } - None } #[cfg(test)] mod tests { - use crate::{inline::natspec::get_fn_docs, NatSpec}; - use serde_json::{json, Value}; - use std::collections::BTreeMap; + use super::*; + use serde_json::json; + + #[test] + fn parse_solang() { + let src = " +contract C { /// forge-config: default.fuzz.runs = 600 + +\t\t\t\t /// forge-config: default.fuzz.runs = 601 + + function f1() {} + /** forge-config: default.fuzz.runs = 700 */ +function f2() {} /** forge-config: default.fuzz.runs = 800 */ function f3() {} + +/** + * forge-config: default.fuzz.runs = 1024 + * forge-config: default.fuzz.max-test-rejects = 500 + */ + function f4() {} +} +"; + let mut natspecs = vec![]; + let solang = SolangParser::new(); + let id = || "path.sol:C".to_string(); + let default_line = || "0:0:0".to_string(); + solang.parse(&mut natspecs, src, &id(), "C"); + assert_eq!( + natspecs, + [ + // f1 + NatSpec { + contract: id(), + function: "f1".to_string(), + line: default_line(), + docs: "forge-config: default.fuzz.runs = 600\nforge-config: default.fuzz.runs = 601".to_string(), + }, + // f2 + NatSpec { + contract: id(), + function: "f2".to_string(), + line: default_line(), + docs: "forge-config: default.fuzz.runs = 700".to_string(), + }, + // f3 + NatSpec { + contract: id(), + function: "f3".to_string(), + line: default_line(), + docs: "forge-config: default.fuzz.runs = 800".to_string(), + }, + // f4 + NatSpec { + contract: id(), + function: "f4".to_string(), + line: default_line(), + docs: "forge-config: default.fuzz.runs = 1024\nforge-config: default.fuzz.max-test-rejects = 500".to_string(), + }, + ] + ); + } + + #[test] + fn parse_solang_2() { + let src = r#" +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity >=0.8.0; + +import "ds-test/test.sol"; + +contract FuzzInlineConf is DSTest { + /** + * forge-config: default.fuzz.runs = 1024 + * forge-config: default.fuzz.max-test-rejects = 500 + */ + function testInlineConfFuzz(uint8 x) public { + require(true, "this is not going to revert"); + } +} + "#; + let mut natspecs = vec![]; + let solang = SolangParser::new(); + let id = || "inline/FuzzInlineConf.t.sol:FuzzInlineConf".to_string(); + let default_line = || "0:0:0".to_string(); + solang.parse(&mut natspecs, src, &id(), "FuzzInlineConf"); + assert_eq!( + natspecs, + [ + NatSpec { + contract: id(), + function: "testInlineConfFuzz".to_string(), + line: default_line(), + docs: "forge-config: default.fuzz.runs = 1024\nforge-config: default.fuzz.max-test-rejects = 500".to_string(), + }, + ] + ); + } #[test] fn config_lines() { @@ -195,7 +367,7 @@ mod tests { let mut fn_data: BTreeMap = BTreeMap::new(); let doc_without_src_field = json!({ "text": "forge-config:default.fuzz.runs=600" }); fn_data.insert("documentation".into(), doc_without_src_field); - let (_, src_line) = get_fn_docs(&fn_data).expect("Some docs"); + let (_, src_line) = SolcParser::new().get_fn_docs(&fn_data).expect("Some docs"); assert_eq!(src_line, "".to_string()); } @@ -205,7 +377,7 @@ mod tests { let doc_without_src_field = json!({ "text": "forge-config:default.fuzz.runs=600", "src": "73:21:12" }); fn_data.insert("documentation".into(), doc_without_src_field); - let (_, src_line) = get_fn_docs(&fn_data).expect("Some docs"); + let (_, src_line) = SolcParser::new().get_fn_docs(&fn_data).expect("Some docs"); assert_eq!(src_line, "73:21:12".to_string()); } diff --git a/crates/forge/src/lib.rs b/crates/forge/src/lib.rs index a430204e1..d163f819c 100644 --- a/crates/forge/src/lib.rs +++ b/crates/forge/src/lib.rs @@ -91,10 +91,7 @@ impl TestOptions { /// - `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: S, test_fn: S) -> TestRunner - where - S: Into, - { + 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 .failure_persist_dir @@ -116,10 +113,7 @@ impl TestOptions { /// - `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: S, test_fn: S) -> TestRunner - where - S: Into, - { + 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, None) } @@ -131,10 +125,7 @@ impl TestOptions { /// - `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: S, test_fn: S) -> &FuzzConfig - where - S: Into, - { + pub fn fuzz_config(&self, contract_id: &str, test_fn: &str) -> &FuzzConfig { self.inline_fuzz.get(contract_id, test_fn).unwrap_or(&self.fuzz) } @@ -145,10 +136,7 @@ impl TestOptions { /// - `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: S, test_fn: S) -> &InvariantConfig - where - S: Into, - { + pub fn invariant_config(&self, contract_id: &str, test_fn: &str) -> &InvariantConfig { self.inline_invariant.get(contract_id, test_fn).unwrap_or(&self.invariant) } diff --git a/crates/forge/tests/it/inline.rs b/crates/forge/tests/it/inline.rs index 3e7b46163..0a2f4d593 100644 --- a/crates/forge/tests/it/inline.rs +++ b/crates/forge/tests/it/inline.rs @@ -4,10 +4,7 @@ use crate::{ config::runner, test_helpers::{COMPILED, PROJECT}, }; -use forge::{ - result::{SuiteResult, TestKind, TestResult}, - TestOptions, TestOptionsBuilder, -}; +use forge::{result::TestKind, TestOptions, TestOptionsBuilder}; use foundry_config::{FuzzConfig, InvariantConfig}; use foundry_test_utils::Filter; @@ -16,17 +13,11 @@ async fn inline_config_run_fuzz() { let filter = Filter::new(".*", ".*", ".*inline/FuzzInlineConf.t.sol"); let mut runner = runner(); let result = runner.test_collect(&filter); - let suite_result: &SuiteResult = - result.get("inline/FuzzInlineConf.t.sol:FuzzInlineConf").unwrap(); - let test_result: &TestResult = - suite_result.test_results.get("testInlineConfFuzz(uint8)").unwrap(); - match &test_result.kind { - TestKind::Fuzz { runs, .. } => { - assert_eq!(runs, &1024); - } - _ => { - unreachable!() - } + let suite_result = result.get("inline/FuzzInlineConf.t.sol:FuzzInlineConf").unwrap(); + let test_result = suite_result.test_results.get("testInlineConfFuzz(uint8)").unwrap(); + match test_result.kind { + TestKind::Fuzz { runs, .. } => assert_eq!(runs, 1024), + _ => unreachable!(), } } @@ -44,24 +35,15 @@ async fn inline_config_run_invariant() { result.get(&format!("{ROOT}:InvariantInlineConf2")).expect("Result exists"); let test_result_1 = suite_result_1.test_results.get("invariant_neverFalse()").unwrap(); - let test_result_2 = suite_result_2.test_results.get("invariant_neverFalse()").unwrap(); - - match &test_result_1.kind { - TestKind::Invariant { runs, .. } => { - assert_eq!(runs, &333); - } - _ => { - unreachable!() - } + match test_result_1.kind { + TestKind::Invariant { runs, .. } => assert_eq!(runs, 333), + _ => unreachable!(), } - match &test_result_2.kind { - TestKind::Invariant { runs, .. } => { - assert_eq!(runs, &42); - } - _ => { - unreachable!() - } + let test_result_2 = suite_result_2.test_results.get("invariant_neverFalse()").unwrap(); + match test_result_2.kind { + TestKind::Invariant { runs, .. } => assert_eq!(runs, 42), + _ => unreachable!(), } } From bc821ef1530cd006b46446b494e8996c95afbf32 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 18 Mar 2024 21:06:41 +0100 Subject: [PATCH 085/430] chore: don't panic when EVM fails in invariants (#7419) --- .../evm/evm/src/executors/invariant/error.rs | 83 +++++++++---------- .../evm/evm/src/executors/invariant/funcs.rs | 58 ++++++------- crates/evm/evm/src/executors/invariant/mod.rs | 15 ++-- crates/forge/src/runner.rs | 6 +- 4 files changed, 74 insertions(+), 88 deletions(-) diff --git a/crates/evm/evm/src/executors/invariant/error.rs b/crates/evm/evm/src/executors/invariant/error.rs index 47ebc8776..d46d6da2c 100644 --- a/crates/evm/evm/src/executors/invariant/error.rs +++ b/crates/evm/evm/src/executors/invariant/error.rs @@ -150,7 +150,7 @@ impl FailedInvariantCaseData { }; if self.shrink { - calls = self.try_shrinking(&calls, &executor).into_iter().cloned().collect(); + calls = self.try_shrinking(&calls, &executor)?.into_iter().cloned().collect(); } else { trace!(target: "forge::test", "Shrinking disabled."); } @@ -162,9 +162,8 @@ impl FailedInvariantCaseData { // Replay each call from the sequence until we break the invariant. for (sender, (addr, bytes)) in calls.iter() { - let call_result = executor - .call_raw_committing(*sender, *addr, bytes.clone(), U256::ZERO) - .expect("bad call to evm"); + let call_result = + executor.call_raw_committing(*sender, *addr, bytes.clone(), U256::ZERO)?; logs.extend(call_result.logs); traces.push((TraceKind::Execution, call_result.traces.clone().unwrap())); @@ -185,9 +184,8 @@ impl FailedInvariantCaseData { // Checks the invariant. if let Some(func) = &self.func { - let error_call_result = executor - .call_raw(CALLER, self.addr, func.clone(), U256::ZERO) - .expect("bad call to evm"); + let error_call_result = + executor.call_raw(CALLER, self.addr, func.clone(), U256::ZERO)?; traces.push((TraceKind::Execution, error_call_result.traces.clone().unwrap())); @@ -210,10 +208,10 @@ impl FailedInvariantCaseData { calls: &[BasicTxDetails], use_calls: &[usize], curr_seq: Arc>>, - ) { + ) -> eyre::Result<()> { if curr_seq.read().len() == 1 { // if current sequence is already the smallest possible, just return - return; + return Ok(()); } let mut new_sequence = Vec::with_capacity(calls.len()); @@ -226,22 +224,19 @@ impl FailedInvariantCaseData { // If the new sequence is already longer than the known best, skip execution if new_sequence.len() >= curr_seq.read().len() { - return + return Ok(()); } } for (seq_idx, call_index) in new_sequence.iter().enumerate() { let (sender, (addr, bytes)) = &calls[*call_index]; - executor - .call_raw_committing(*sender, *addr, bytes.clone(), U256::ZERO) - .expect("bad call to evm"); + executor.call_raw_committing(*sender, *addr, bytes.clone(), U256::ZERO)?; // Checks the invariant. If we revert or fail before the last call, all the better. if let Some(func) = &self.func { - let mut call_result = executor - .call_raw(CALLER, self.addr, func.clone(), U256::ZERO) - .expect("bad call to evm"); + let mut call_result = + executor.call_raw(CALLER, self.addr, func.clone(), U256::ZERO)?; let is_success = executor.is_raw_call_success( self.addr, Cow::Owned(call_result.state_changeset.take().unwrap()), @@ -257,6 +252,7 @@ impl FailedInvariantCaseData { } } } + Ok(()) } /// Tries to shrink the failure case to its smallest sequence of calls. @@ -266,21 +262,20 @@ impl FailedInvariantCaseData { &self, calls: &'a [BasicTxDetails], executor: &Executor, - ) -> Vec<&'a BasicTxDetails> { + ) -> eyre::Result> { trace!(target: "forge::test", "Shrinking."); // Special case test: the invariant is *unsatisfiable* - it took 0 calls to // break the invariant -- consider emitting a warning. if let Some(func) = &self.func { - let error_call_result = executor - .call_raw(CALLER, self.addr, func.clone(), U256::ZERO) - .expect("bad call to evm"); + let error_call_result = + executor.call_raw(CALLER, self.addr, func.clone(), U256::ZERO)?; if error_call_result.reverted { - return vec![]; + return Ok(vec![]); } } - let shrunk_call_indices = self.try_shrinking_recurse(calls, executor, 0, 0); + let shrunk_call_indices = self.try_shrinking_recurse(calls, executor, 0, 0)?; // We recreate the call sequence in the same order as they reproduce the failure, // otherwise we could end up with inverted sequence. @@ -289,7 +284,7 @@ impl FailedInvariantCaseData { // 2. Bob calls transferOwnership to Alice // 3. Alice calls acceptOwnership and test fails // we shrink to indices of [2, 1] and we recreate call sequence in same order. - shrunk_call_indices.iter().map(|idx| &calls[*idx]).collect() + Ok(shrunk_call_indices.iter().map(|idx| &calls[*idx]).collect()) } /// We try to construct a [powerset](https://en.wikipedia.org/wiki/Power_set) of the sequence if @@ -310,7 +305,7 @@ impl FailedInvariantCaseData { executor: &Executor, runs: usize, retries: usize, - ) -> Vec { + ) -> eyre::Result> { // Construct a ArcRwLock vector of indices of `calls` let shrunk_call_indices = Arc::new(RwLock::new((0..calls.len()).collect())); let shrink_limit = self.shrink_run_limit - runs; @@ -319,7 +314,7 @@ impl FailedInvariantCaseData { // We construct either a full powerset (this guarantees we maximally shrunk for the given // calls) or a random subset let (set_of_indices, is_powerset): (Vec<_>, bool) = if calls.len() <= 64 && - 2_usize.pow(calls.len() as u32) <= shrink_limit + (1 << calls.len() as u32) <= shrink_limit { // We add the last tx always because thats ultimately what broke the invariant let powerset = (0..upper_bound) @@ -357,14 +352,17 @@ impl FailedInvariantCaseData { let new_runs = set_of_indices.len(); // just try all of them in parallel - set_of_indices.par_iter().for_each(|use_calls| { - self.set_fails_successfully( - executor.clone(), - calls, - use_calls, - Arc::clone(&shrunk_call_indices), - ); - }); + set_of_indices + .par_iter() + .map(|use_calls| { + self.set_fails_successfully( + executor.clone(), + calls, + use_calls, + Arc::clone(&shrunk_call_indices), + ) + }) + .collect::>()?; // There are no more live references to shrunk_call_indices as the parallel execution is // finished, so it is fine to get the inner value via `Arc::unwrap`. @@ -372,7 +370,7 @@ impl FailedInvariantCaseData { if is_powerset { // A powerset is guaranteed to be smallest local subset, so we return early. - return shrunk_call_indices + return Ok(shrunk_call_indices); } let computation_budget_not_hit = new_runs + runs < self.shrink_run_limit; @@ -399,13 +397,8 @@ impl FailedInvariantCaseData { let new_calls: Vec<_> = calls .iter() .enumerate() - .filter_map(|(i, call)| { - if shrunk_call_indices.contains(&i) { - Some(call.clone()) - } else { - None - } - }) + .filter(|(i, _)| shrunk_call_indices.contains(i)) + .map(|(_, call)| call.clone()) .collect(); // We rerun this algorithm as if the new smaller subset above were the original @@ -415,13 +408,13 @@ impl FailedInvariantCaseData { // returns [1]. This means `call3` is all that is required to break // the invariant. let new_calls_idxs = - self.try_shrinking_recurse(&new_calls, executor, runs + new_runs, 0); + self.try_shrinking_recurse(&new_calls, executor, runs + new_runs, 0)?; // Notably, the indices returned above are relative to `new_calls`, *not* the // originally passed in `calls`. So we map back by filtering // `new_calls` by index if the index was returned above, and finding the position // of the `new_call` in the passed in `call` - new_calls + Ok(new_calls .iter() .enumerate() .filter_map(|(idx, new_call)| { @@ -431,12 +424,12 @@ impl FailedInvariantCaseData { calls.iter().position(|r| r == new_call) } }) - .collect() + .collect()) } _ => { // The computation budget has been hit or no retries remaining, stop trying to make // progress - shrunk_call_indices + Ok(shrunk_call_indices) } } } diff --git a/crates/evm/evm/src/executors/invariant/funcs.rs b/crates/evm/evm/src/executors/invariant/funcs.rs index 7f4694cdb..218f97e92 100644 --- a/crates/evm/evm/src/executors/invariant/funcs.rs +++ b/crates/evm/evm/src/executors/invariant/funcs.rs @@ -21,7 +21,7 @@ pub fn assert_invariants( invariant_failures: &mut InvariantFailures, shrink_sequence: bool, shrink_run_limit: usize, -) -> Option { +) -> eyre::Result> { let mut inner_sequence = vec![]; if let Some(fuzzer) = &executor.inspector.fuzzer { @@ -31,14 +31,12 @@ pub fn assert_invariants( } let func = invariant_contract.invariant_function; - let mut call_result = executor - .call_raw( - CALLER, - invariant_contract.address, - func.abi_encode_input(&[]).expect("invariant should have no inputs").into(), - U256::ZERO, - ) - .expect("EVM error"); + let mut call_result = executor.call_raw( + CALLER, + invariant_contract.address, + func.abi_encode_input(&[]).expect("invariant should have no inputs").into(), + U256::ZERO, + )?; let is_err = !executor.is_raw_call_success( invariant_contract.address, @@ -59,11 +57,11 @@ pub fn assert_invariants( shrink_run_limit, ); invariant_failures.error = Some(InvariantFuzzError::BrokenInvariant(case_data)); - return None + return Ok(None); } } - Some(call_result) + Ok(Some(call_result)) } /// Replays the provided invariant run for collecting the logs and traces from all depths. @@ -78,7 +76,7 @@ pub fn replay_run( coverage: &mut Option, func: Function, inputs: Vec, -) { +) -> eyre::Result<()> { // We want traces for a failed case. executor.set_tracing(true); @@ -86,25 +84,18 @@ pub fn replay_run( // Replay each call from the sequence until we break the invariant. for (sender, (addr, bytes)) in inputs.iter() { - let call_result = executor - .call_raw_committing(*sender, *addr, bytes.clone(), U256::ZERO) - .expect("bad call to evm"); + let call_result = + executor.call_raw_committing(*sender, *addr, bytes.clone(), U256::ZERO)?; logs.extend(call_result.logs); traces.push((TraceKind::Execution, call_result.traces.clone().unwrap())); - let old_coverage = std::mem::take(coverage); - match (old_coverage, call_result.coverage) { - (Some(old_coverage), Some(call_coverage)) => { - *coverage = Some(old_coverage.merge(call_coverage)); - } - (None, Some(call_coverage)) => { - *coverage = Some(call_coverage); + if let Some(new_coverage) = call_result.coverage { + if let Some(old_coverage) = coverage { + *coverage = Some(std::mem::take(old_coverage).merge(new_coverage)); + } else { + *coverage = Some(new_coverage); } - (Some(old_coverage), None) => { - *coverage = Some(old_coverage); - } - (None, None) => {} } // Identify newly generated contracts, if they exist. @@ -114,17 +105,16 @@ pub fn replay_run( )); // Checks the invariant. - let error_call_result = executor - .call_raw( - CALLER, - invariant_contract.address, - func.abi_encode_input(&[]).expect("invariant should have no inputs").into(), - U256::ZERO, - ) - .expect("bad call to evm"); + let error_call_result = executor.call_raw( + CALLER, + invariant_contract.address, + func.abi_encode_input(&[]).expect("invariant should have no inputs").into(), + U256::ZERO, + )?; traces.push((TraceKind::Execution, error_call_result.traces.clone().unwrap())); logs.extend(error_call_result.logs); } + Ok(()) } diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 622b94bf7..3d3f18200 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -184,7 +184,7 @@ impl<'a> InvariantExecutor<'a> { &mut failures.borrow_mut(), self.config.shrink_sequence, self.config.shrink_run_limit, - )); + )?); if last_call_results.borrow().is_none() { fuzz_cases.borrow_mut().push(FuzzedCases::new(vec![])); @@ -287,7 +287,8 @@ impl<'a> InvariantExecutor<'a> { self.config.shrink_sequence, self.config.shrink_run_limit, &mut run_traces, - ); + ) + .map_err(|e| TestCaseError::fail(e.to_string()))?; if !can_continue || current_run == self.config.depth - 1 { last_run_calldata.borrow_mut().clone_from(&inputs); @@ -706,7 +707,7 @@ fn can_continue( shrink_sequence: bool, shrink_run_limit: usize, run_traces: &mut Vec, -) -> RichInvariantResults { +) -> eyre::Result { let mut call_results = None; // Detect handler assertion failures first. @@ -727,9 +728,9 @@ fn can_continue( failures, shrink_sequence, shrink_run_limit, - ); + )?; if call_results.is_none() { - return RichInvariantResults::new(false, None) + return Ok(RichInvariantResults::new(false, None)); } } else { // Increase the amount of reverts. @@ -749,8 +750,8 @@ fn can_continue( let error = InvariantFuzzError::Revert(case_data); failures.error = Some(error); - return RichInvariantResults::new(false, None) + return Ok(RichInvariantResults::new(false, None)); } } - RichInvariantResults::new(true, call_results) + Ok(RichInvariantResults::new(true, call_results)) } diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 494a02d88..3c9eee1dd 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -545,7 +545,7 @@ impl<'a> ContractRunner<'a> { // If invariants ran successfully, replay the last run to collect logs and // traces. _ => { - replay_run( + if let Err(err) = replay_run( &invariant_contract, self.executor.clone(), known_contracts, @@ -555,7 +555,9 @@ impl<'a> ContractRunner<'a> { &mut coverage, func.clone(), last_run_inputs.clone(), - ); + ) { + error!(%err, "Failed to replay last invariant run"); + } } } From 63ea108478cb8372e578c2d6343e8bf243fccebf Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 18 Mar 2024 21:06:46 +0100 Subject: [PATCH 086/430] refactor: simplify fuzzing code (#7420) * refactor: simplify fuzzing code * chore: use shl * chore: unlock earlier --- Cargo.lock | 1 - crates/evm/evm/src/executors/fuzz/mod.rs | 23 +-- crates/evm/evm/src/executors/invariant/mod.rs | 6 +- crates/evm/fuzz/Cargo.toml | 1 - crates/evm/fuzz/src/strategies/calldata.rs | 60 +++--- crates/evm/fuzz/src/strategies/invariants.rs | 151 ++++++--------- crates/evm/fuzz/src/strategies/param.rs | 183 ++++++++---------- crates/evm/fuzz/src/strategies/state.rs | 26 +-- 8 files changed, 185 insertions(+), 266 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bd7a9fd78..ab4caf50e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3462,7 +3462,6 @@ dependencies = [ "alloy-dyn-abi", "alloy-json-abi", "alloy-primitives", - "arbitrary", "eyre", "foundry-common", "foundry-compilers", diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index dfb7b6c60..41beb7e43 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -82,19 +82,11 @@ impl FuzzedExecutor { let state = self.build_fuzz_state(); - let mut weights = vec![]; let dictionary_weight = self.config.dictionary.dictionary_weight.min(100); - if self.config.dictionary.dictionary_weight < 100 { - weights.push((100 - dictionary_weight, fuzz_calldata(func.clone()))); - } - if dictionary_weight > 0 { - weights.push(( - self.config.dictionary.dictionary_weight, - fuzz_calldata_from_state(func.clone(), state.clone()), - )); - } - - let strat = proptest::strategy::Union::new_weighted(weights); + let strat = proptest::prop_oneof![ + 100 - dictionary_weight => fuzz_calldata(func.clone()), + dictionary_weight => fuzz_calldata_from_state(func.clone(), &state), + ]; debug!(func=?func.name, should_fail, "fuzzing"); let run_result = self.runner.clone().run(&strat, |calldata| { let fuzz_res = self.single_fuzz(&state, address, should_fail, calldata)?; @@ -215,12 +207,7 @@ impl FuzzedExecutor { let state_changeset = call.state_changeset.take().unwrap(); // Build fuzzer state - collect_state_from_call( - &call.logs, - &state_changeset, - state.clone(), - &self.config.dictionary, - ); + collect_state_from_call(&call.logs, &state_changeset, state, &self.config.dictionary); // When the `assume` cheatcode is called it returns a special string if call.result.as_ref() == MAGIC_ASSUME { diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 3d3f18200..5ed7998ac 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -250,7 +250,7 @@ impl<'a> InvariantExecutor<'a> { &mut state_changeset, sender, &call_result, - fuzz_state.clone(), + &fuzz_state, &self.config.dictionary, ); @@ -369,7 +369,7 @@ impl<'a> InvariantExecutor<'a> { Arc::new(Mutex::new(targeted_contracts)); let calldata_fuzz_config = - CalldataFuzzDictionary::new(&self.config.dictionary, fuzz_state.clone()); + CalldataFuzzDictionary::new(&self.config.dictionary, &fuzz_state); // Creates the invariant strategy. let strat = invariant_strat( @@ -667,7 +667,7 @@ fn collect_data( state_changeset: &mut HashMap, sender: &Address, call_result: &RawCallResult, - fuzz_state: EvmFuzzState, + fuzz_state: &EvmFuzzState, config: &FuzzDictionaryConfig, ) { // Verify it has no code. diff --git a/crates/evm/fuzz/Cargo.toml b/crates/evm/fuzz/Cargo.toml index 47b578728..d8c68428e 100644 --- a/crates/evm/fuzz/Cargo.toml +++ b/crates/evm/fuzz/Cargo.toml @@ -31,7 +31,6 @@ revm = { workspace = true, default-features = false, features = [ "arbitrary", ] } -arbitrary = "1.3.1" eyre = "0.6" hashbrown = { version = "0.14", features = ["serde"] } itertools.workspace = true diff --git a/crates/evm/fuzz/src/strategies/calldata.rs b/crates/evm/fuzz/src/strategies/calldata.rs index cb56f3330..cf5030203 100644 --- a/crates/evm/fuzz/src/strategies/calldata.rs +++ b/crates/evm/fuzz/src/strategies/calldata.rs @@ -4,33 +4,27 @@ use alloy_json_abi::Function; use alloy_primitives::{Address, Bytes}; use foundry_config::FuzzDictionaryConfig; use hashbrown::HashSet; -use proptest::prelude::{BoxedStrategy, Strategy}; -use std::{fmt, sync::Arc}; +use proptest::prelude::Strategy; +use std::sync::Arc; /// Clonable wrapper around [CalldataFuzzDictionary]. -#[derive(Debug, Clone)] +#[derive(Clone, Debug)] pub struct CalldataFuzzDictionary { pub inner: Arc, } impl CalldataFuzzDictionary { - pub fn new(config: &FuzzDictionaryConfig, state: EvmFuzzState) -> Self { + pub fn new(config: &FuzzDictionaryConfig, state: &EvmFuzzState) -> Self { Self { inner: Arc::new(CalldataFuzzDictionaryConfig::new(config, state)) } } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct CalldataFuzzDictionaryConfig { /// Addresses that can be used for fuzzing calldata. pub addresses: Vec
, } -impl fmt::Debug for CalldataFuzzDictionaryConfig { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("CalldataFuzzDictionaryConfig").field("addresses", &self.addresses).finish() - } -} - /// Represents custom configuration for invariant fuzzed calldata strategies. /// /// At the moment only the dictionary of addresses to be used for a fuzzed `function(address)` can @@ -41,30 +35,23 @@ impl CalldataFuzzDictionaryConfig { /// The set of addresses contains a number of `max_calldata_fuzz_dictionary_addresses` random /// addresses plus all addresses that already had their PUSH bytes collected (retrieved from /// `EvmFuzzState`, if `include_push_bytes` config enabled). - pub fn new(config: &FuzzDictionaryConfig, state: EvmFuzzState) -> Self { - let mut addresses: HashSet
= HashSet::new(); - let dict_size = config.max_calldata_fuzz_dictionary_addresses; + pub fn new(config: &FuzzDictionaryConfig, state: &EvmFuzzState) -> Self { + let mut addresses = HashSet::
::new(); + let dict_size = config.max_calldata_fuzz_dictionary_addresses; if dict_size > 0 { - loop { - if addresses.len() == dict_size { - break - } - addresses.insert(Address::random()); - } - + addresses.extend(std::iter::repeat_with(Address::random).take(dict_size)); // Add all addresses that already had their PUSH bytes collected. - let mut state = state.write(); - addresses.extend(state.addresses()); + addresses.extend(state.read().addresses()); } - Self { addresses: Vec::from_iter(addresses) } + Self { addresses: addresses.into_iter().collect() } } } /// Given a function, it returns a strategy which generates valid calldata /// for that function's input types. -pub fn fuzz_calldata(func: Function) -> BoxedStrategy { +pub fn fuzz_calldata(func: Function) -> impl Strategy { fuzz_calldata_with_config(func, None) } @@ -72,20 +59,23 @@ pub fn fuzz_calldata(func: Function) -> BoxedStrategy { /// for that function's input types, following custom configuration rules. pub fn fuzz_calldata_with_config( func: Function, - config: Option, -) -> BoxedStrategy { + config: Option<&CalldataFuzzDictionary>, +) -> impl Strategy { // We need to compose all the strategies generated for each parameter in all // possible combinations let strats = func .inputs .iter() - .map(|input| fuzz_param(&input.selector_type().parse().unwrap(), config.clone())) + .map(|input| fuzz_param(&input.selector_type().parse().unwrap(), config)) .collect::>(); - - strats - .prop_map(move |tokens| { - trace!(input=?tokens); - func.abi_encode_input(&tokens).unwrap().into() - }) - .boxed() + strats.prop_map(move |values| { + func.abi_encode_input(&values) + .unwrap_or_else(|_| { + panic!( + "Fuzzer generated invalid arguments for function `{}` with inputs {:?}: {:?}", + func.name, func.inputs, values + ) + }) + .into() + }) } diff --git a/crates/evm/fuzz/src/strategies/invariants.rs b/crates/evm/fuzz/src/strategies/invariants.rs index e6dedc9cd..5fd766bb4 100644 --- a/crates/evm/fuzz/src/strategies/invariants.rs +++ b/crates/evm/fuzz/src/strategies/invariants.rs @@ -17,27 +17,18 @@ pub fn override_call_strat( calldata_fuzz_config: CalldataFuzzDictionary, ) -> SBoxedStrategy<(Address, Bytes)> { let contracts_ref = contracts.clone(); - - let random_contract = any::() - .prop_map(move |selector| *selector.select(contracts_ref.lock().keys())); - let target = any::().prop_map(move |_| *target.read()); - - proptest::strategy::Union::new_weighted(vec![ - (80, target.sboxed()), - (20, random_contract.sboxed()), - ]) + proptest::prop_oneof![ + 80 => proptest::strategy::LazyJust::new(move || *target.read()), + 20 => any::() + .prop_map(move |selector| *selector.select(contracts_ref.lock().keys())), + ] .prop_flat_map(move |target_address| { let fuzz_state = fuzz_state.clone(); let calldata_fuzz_config = calldata_fuzz_config.clone(); - let (_, abi, functions) = contracts.lock().get(&target_address).unwrap().clone(); + let (_, abi, functions) = &contracts.lock()[&target_address]; let func = select_random_function(abi, functions); func.prop_flat_map(move |func| { - fuzz_contract_with_calldata( - fuzz_state.clone(), - calldata_fuzz_config.clone(), - target_address, - func, - ) + fuzz_contract_with_calldata(&fuzz_state, &calldata_fuzz_config, target_address, func) }) }) .sboxed() @@ -74,26 +65,27 @@ fn generate_call( dictionary_weight: u32, calldata_fuzz_config: CalldataFuzzDictionary, ) -> BoxedStrategy { - let random_contract = select_random_contract(contracts); let senders = Rc::new(senders); - random_contract - .prop_flat_map(move |(contract, abi, functions)| { - let func = select_random_function(abi, functions); + any::() + .prop_flat_map(move |selector| { + let (contract, func) = { + let contracts = contracts.lock(); + let contracts = + contracts.iter().filter(|(_, (_, abi, _))| !abi.functions.is_empty()); + let (&contract, (_, abi, functions)) = selector.select(contracts); + + let func = select_random_function(abi, functions); + (contract, func) + }; + let senders = senders.clone(); let fuzz_state = fuzz_state.clone(); let calldata_fuzz_config = calldata_fuzz_config.clone(); func.prop_flat_map(move |func| { - let sender = - select_random_sender(fuzz_state.clone(), senders.clone(), dictionary_weight); - ( - sender, - fuzz_contract_with_calldata( - fuzz_state.clone(), - calldata_fuzz_config.clone(), - contract, - func, - ), - ) + let sender = select_random_sender(&fuzz_state, senders.clone(), dictionary_weight); + let contract = + fuzz_contract_with_calldata(&fuzz_state, &calldata_fuzz_config, contract, func); + (sender, contract) }) }) .boxed() @@ -103,78 +95,55 @@ fn generate_call( /// * If `senders` is empty, then it's either a random address (10%) or from the dictionary (90%). /// * If `senders` is not empty, a random address is chosen from the list of senders. fn select_random_sender( - fuzz_state: EvmFuzzState, + fuzz_state: &EvmFuzzState, senders: Rc, dictionary_weight: u32, ) -> BoxedStrategy
{ - let senders_ref = senders.clone(); - let fuzz_strategy = proptest::strategy::Union::new_weighted(vec![ - ( - 100 - dictionary_weight, - fuzz_param(&alloy_dyn_abi::DynSolType::Address, None) - .prop_map(move |addr| addr.as_address().unwrap()) - .boxed(), - ), - ( - dictionary_weight, - fuzz_param_from_state(&alloy_dyn_abi::DynSolType::Address, fuzz_state) - .prop_map(move |addr| addr.as_address().unwrap()) - .boxed(), - ), - ]) - // Too many exclusions can slow down testing. - .prop_filter("senders not allowed", move |addr| !senders_ref.excluded.contains(addr)) - .boxed(); if !senders.targeted.is_empty() { any::() - .prop_map(move |selector| *selector.select(&*senders.targeted)) + .prop_map(move |selector| *selector.select(&senders.targeted)) .boxed() } else { - fuzz_strategy + proptest::prop_oneof![ + 100 - dictionary_weight => fuzz_param(&alloy_dyn_abi::DynSolType::Address, None) + .prop_map(move |addr| addr.as_address().unwrap()) + .boxed(), + dictionary_weight => fuzz_param_from_state(&alloy_dyn_abi::DynSolType::Address, fuzz_state) + .prop_map(move |addr| addr.as_address().unwrap()) + .boxed(), + ] + // Too many exclusions can slow down testing. + .prop_filter("excluded sender", move |addr| !senders.excluded.contains(addr)) + .boxed() } } -/// Strategy to randomly select a contract from the `contracts` list that has at least 1 function -fn select_random_contract( - contracts: FuzzRunIdentifiedContracts, -) -> impl Strategy)> { - let selectors = any::(); - selectors.prop_map(move |selector| { - let contracts = contracts.lock(); - let (addr, (_, abi, functions)) = - selector.select(contracts.iter().filter(|(_, (_, abi, _))| !abi.functions.is_empty())); - (*addr, abi.clone(), functions.clone()) - }) -} - /// Strategy to select a random mutable function from the abi. /// /// If `targeted_functions` is not empty, select one from it. Otherwise, take any /// of the available abi functions. fn select_random_function( - abi: JsonAbi, - targeted_functions: Vec, + abi: &JsonAbi, + targeted_functions: &[Function], ) -> BoxedStrategy { - let selectors = any::(); - let possible_funcs: Vec = abi - .functions() - .filter(|func| { - !matches!( - func.state_mutability, - alloy_json_abi::StateMutability::Pure | alloy_json_abi::StateMutability::View - ) - }) - .cloned() - .collect(); - let total_random = selectors.prop_map(move |selector| { - let func = selector.select(&possible_funcs); - func.clone() - }); if !targeted_functions.is_empty() { + let targeted_functions = targeted_functions.to_vec(); let selector = any::() - .prop_map(move |selector| selector.select(targeted_functions.clone())); + .prop_map(move |selector| selector.select(&targeted_functions).clone()); selector.boxed() } else { + let possible_funcs: Vec = abi + .functions() + .filter(|&func| { + !matches!( + func.state_mutability, + alloy_json_abi::StateMutability::Pure | alloy_json_abi::StateMutability::View + ) + }) + .cloned() + .collect(); + let total_random = any::() + .prop_map(move |selector| selector.select(&possible_funcs).clone()); total_random.boxed() } } @@ -182,20 +151,20 @@ fn select_random_function( /// Given a function, it returns a proptest strategy which generates valid abi-encoded calldata /// for that function's input types. pub fn fuzz_contract_with_calldata( - fuzz_state: EvmFuzzState, - calldata_fuzz_config: CalldataFuzzDictionary, + fuzz_state: &EvmFuzzState, + calldata_fuzz_config: &CalldataFuzzDictionary, contract: Address, func: Function, ) -> impl Strategy { - // We need to compose all the strategies generated for each parameter in all - // possible combinations - // `prop_oneof!` / `TupleUnion` `Arc`s for cheap cloning + // We need to compose all the strategies generated for each parameter in all possible + // combinations. + // `prop_oneof!` / `TupleUnion` `Arc`s for cheap cloning. #[allow(clippy::arc_with_non_send_sync)] - let strats = prop_oneof![ + prop_oneof![ 60 => fuzz_calldata_with_config(func.clone(), Some(calldata_fuzz_config)), 40 => fuzz_calldata_from_state(func, fuzz_state), - ]; - strats.prop_map(move |calldata| { + ] + .prop_map(move |calldata| { trace!(input=?calldata); (contract, calldata) }) diff --git a/crates/evm/fuzz/src/strategies/param.rs b/crates/evm/fuzz/src/strategies/param.rs index 4c8dc03ec..f287c75df 100644 --- a/crates/evm/fuzz/src/strategies/param.rs +++ b/crates/evm/fuzz/src/strategies/param.rs @@ -1,8 +1,7 @@ use super::state::EvmFuzzState; use crate::strategies::calldata::CalldataFuzzDictionary; use alloy_dyn_abi::{DynSolType, DynSolValue}; -use alloy_primitives::{Address, FixedBytes, I256, U256}; -use arbitrary::Unstructured; +use alloy_primitives::{Address, B256, I256, U256}; use proptest::prelude::*; /// The max length of arrays we fuzz for is 256. @@ -13,79 +12,67 @@ const MAX_ARRAY_LEN: usize = 256; /// Works with ABI Encoder v2 tuples. pub fn fuzz_param( param: &DynSolType, - config: Option, + config: Option<&CalldataFuzzDictionary>, ) -> BoxedStrategy { - let param = param.to_owned(); - match param { + match *param { DynSolType::Address => { - if config.is_some() { - let fuzz_config = config.unwrap().inner; - let address_dict_len = fuzz_config.addresses.len(); - if address_dict_len > 0 { + if let Some(config) = config { + let len = config.inner.addresses.len(); + if len > 0 { + let dict = config.inner.clone(); // Create strategy to return random address from configured dictionary. return any::() - .prop_map(move |index| index.index(address_dict_len)) .prop_map(move |index| { - DynSolValue::Address(fuzz_config.addresses.get(index).cloned().unwrap()) + let index = index.index(len); + DynSolValue::Address(dict.addresses[index]) }) - .boxed() + .boxed(); } } // If no config for addresses dictionary then create unbounded addresses strategy. - any::<[u8; 32]>() - .prop_map(|x| DynSolValue::Address(Address::from_word(x.into()))) - .boxed() + any::
().prop_map(DynSolValue::Address).boxed() } - DynSolType::Int(n) => { - let strat = super::IntStrategy::new(n, vec![]); - let strat = strat.prop_map(move |x| DynSolValue::Int(x, n)); - strat.boxed() + DynSolType::Int(n @ 8..=256) => { + super::IntStrategy::new(n, vec![]).prop_map(move |x| DynSolValue::Int(x, n)).boxed() } - DynSolType::Uint(n) => { - let strat = super::UintStrategy::new(n, vec![]); - let strat = strat.prop_map(move |x| DynSolValue::Uint(x, n)); - strat.boxed() + DynSolType::Uint(n @ 8..=256) => { + super::UintStrategy::new(n, vec![]).prop_map(move |x| DynSolValue::Uint(x, n)).boxed() } DynSolType::Function | DynSolType::Bool | DynSolType::Bytes => { - DynSolValue::type_strategy(¶m).boxed() + DynSolValue::type_strategy(param).boxed() } - DynSolType::FixedBytes(size) => prop::collection::vec(any::(), size) + DynSolType::FixedBytes(size @ 1..=32) => any::() .prop_map(move |mut v| { - v.reverse(); - while v.len() < 32 { - v.push(0); - } - DynSolValue::FixedBytes(FixedBytes::from_slice(&v), size) + v[size..].fill(0); + DynSolValue::FixedBytes(v, size) }) .boxed(), - DynSolType::String => DynSolValue::type_strategy(¶m) + DynSolType::String => DynSolValue::type_strategy(param) .prop_map(move |value| { DynSolValue::String( - String::from_utf8_lossy(value.as_str().unwrap().as_bytes()) - .trim() - .trim_end_matches('\0') - .to_string(), + value.as_str().unwrap().trim().trim_end_matches('\0').to_string(), ) }) .boxed(), - DynSolType::Tuple(params) => params + + DynSolType::Tuple(ref params) => params .iter() - .map(|p| fuzz_param(p, config.clone())) + .map(|p| fuzz_param(p, config)) .collect::>() .prop_map(DynSolValue::Tuple) .boxed(), - DynSolType::FixedArray(param, size) => { - proptest::collection::vec(fuzz_param(¶m, config), size) + DynSolType::FixedArray(ref param, size) => { + proptest::collection::vec(fuzz_param(param, config), size) .prop_map(DynSolValue::FixedArray) .boxed() } - DynSolType::Array(param) => { - proptest::collection::vec(fuzz_param(¶m, config), 0..MAX_ARRAY_LEN) + DynSolType::Array(ref param) => { + proptest::collection::vec(fuzz_param(param, config), 0..MAX_ARRAY_LEN) .prop_map(DynSolValue::Array) .boxed() } - DynSolType::CustomStruct { .. } => panic!("unsupported type"), + _ => panic!("unsupported fuzz param type: {param}"), } } @@ -95,99 +82,93 @@ pub fn fuzz_param( /// Works with ABI Encoder v2 tuples. pub fn fuzz_param_from_state( param: &DynSolType, - arc_state: EvmFuzzState, + state: &EvmFuzzState, ) -> BoxedStrategy { - // These are to comply with lifetime requirements - let state_len = arc_state.read().values().len(); - - // Select a value from the state - let st = arc_state.clone(); - let value = any::() - .prop_map(move |index| index.index(state_len)) - .prop_map(move |index| *st.read().values().iter().nth(index).unwrap()); - let param = param.to_owned(); + // Value strategy that uses the state. + let value = || { + let state = state.clone(); + // Use `Index` instead of `Selector` to not iterate over the entire dictionary. + any::().prop_map(move |index| { + let state = state.read(); + let values = state.values(); + let index = index.index(values.len()); + *values.iter().nth(index).unwrap() + }) + }; // Convert the value based on the parameter type - match param { - DynSolType::Address => value + match *param { + DynSolType::Address => value() .prop_map(move |value| DynSolValue::Address(Address::from_word(value.into()))) .boxed(), - DynSolType::FixedBytes(size) => value - .prop_map(move |v| { - let mut buf: [u8; 32] = [0; 32]; - - for b in v[..size].iter().enumerate() { - buf[b.0] = *b.1 - } - - let mut unstructured_v = Unstructured::new(v.as_slice()); - DynSolValue::arbitrary_from_type(¶m, &mut unstructured_v) - .unwrap_or(DynSolValue::FixedBytes(FixedBytes::from_slice(&buf), size)) + DynSolType::Function => value() + .prop_map(move |value| { + DynSolValue::Function(alloy_primitives::Function::from_word(value.into())) + }) + .boxed(), + DynSolType::FixedBytes(size @ 1..=32) => value() + .prop_map(move |mut v| { + v[size..].fill(0); + DynSolValue::FixedBytes(B256::from(v), size) }) .boxed(), - DynSolType::Function | DynSolType::Bool => DynSolValue::type_strategy(¶m).boxed(), - DynSolType::String => DynSolValue::type_strategy(¶m) + DynSolType::Bool => DynSolValue::type_strategy(param).boxed(), + DynSolType::String => DynSolValue::type_strategy(param) .prop_map(move |value| { DynSolValue::String( - String::from_utf8_lossy(value.as_str().unwrap().as_bytes()) - .trim() - .trim_end_matches('\0') - .to_string(), + value.as_str().unwrap().trim().trim_end_matches('\0').to_string(), ) }) .boxed(), - DynSolType::Int(n) => match n / 8 { - 32 => value + DynSolType::Bytes => { + value().prop_map(move |value| DynSolValue::Bytes(value.into())).boxed() + } + DynSolType::Int(n @ 8..=256) => match n / 8 { + 32 => value() .prop_map(move |value| { DynSolValue::Int(I256::from_raw(U256::from_be_bytes(value)), 256) }) .boxed(), - y @ 1..=31 => value + 1..=31 => value() .prop_map(move |value| { // Generate a uintN in the correct range, then shift it to the range of intN // by subtracting 2^(N-1) - let uint = - U256::from_be_bytes(value) % U256::from(2usize).pow(U256::from(y * 8)); - let max_int_plus1 = U256::from(2usize).pow(U256::from(y * 8 - 1)); - let num = I256::from_raw(uint.overflowing_sub(max_int_plus1).0); - DynSolValue::Int(num, y * 8) + let uint = U256::from_be_bytes(value) % U256::from(1).wrapping_shl(n); + let max_int_plus1 = U256::from(1).wrapping_shl(n - 1); + let num = I256::from_raw(uint.wrapping_sub(max_int_plus1)); + DynSolValue::Int(num, n) }) .boxed(), - _ => panic!("unsupported solidity type int{n}"), + _ => unreachable!(), }, - DynSolType::Uint(n) => match n / 8 { - 32 => value + DynSolType::Uint(n @ 8..=256) => match n / 8 { + 32 => value() .prop_map(move |value| DynSolValue::Uint(U256::from_be_bytes(value), 256)) .boxed(), - y @ 1..=31 => value + 1..=31 => value() .prop_map(move |value| { - DynSolValue::Uint( - U256::from_be_bytes(value) % U256::from(2).pow(U256::from(y * 8)), - y * 8, - ) + DynSolValue::Uint(U256::from_be_bytes(value) % U256::from(1).wrapping_shl(n), n) }) .boxed(), - _ => panic!("unsupported solidity type uint{n}"), + _ => unreachable!(), }, - DynSolType::Tuple(params) => params + DynSolType::Tuple(ref params) => params .iter() - .map(|p| fuzz_param_from_state(p, arc_state.clone())) + .map(|p| fuzz_param_from_state(p, state)) .collect::>() .prop_map(DynSolValue::Tuple) .boxed(), - DynSolType::Bytes => value.prop_map(move |value| DynSolValue::Bytes(value.into())).boxed(), - DynSolType::FixedArray(param, size) => { - let fixed_size = size; - proptest::collection::vec(fuzz_param_from_state(¶m, arc_state), fixed_size) + DynSolType::FixedArray(ref param, size) => { + proptest::collection::vec(fuzz_param_from_state(param, state), size) .prop_map(DynSolValue::FixedArray) .boxed() } - DynSolType::Array(param) => { - proptest::collection::vec(fuzz_param_from_state(¶m, arc_state), 0..MAX_ARRAY_LEN) + DynSolType::Array(ref param) => { + proptest::collection::vec(fuzz_param_from_state(param, state), 0..MAX_ARRAY_LEN) .prop_map(DynSolValue::Array) .boxed() } - DynSolType::CustomStruct { .. } => panic!("unsupported type"), + _ => panic!("unsupported fuzz param type: {param}"), } } @@ -204,10 +185,10 @@ mod tests { let func = get_func(f).unwrap(); let db = CacheDB::new(EmptyDB::default()); let state = build_initial_state(&db, &FuzzDictionaryConfig::default()); - let strat = proptest::strategy::Union::new_weighted(vec![ - (60, fuzz_calldata(func.clone())), - (40, fuzz_calldata_from_state(func, state)), - ]); + let strat = proptest::prop_oneof![ + 60 => fuzz_calldata(func.clone()), + 40 => fuzz_calldata_from_state(func, &state), + ]; let cfg = proptest::test_runner::Config { failure_persistence: None, ..Default::default() }; let mut runner = proptest::test_runner::TestRunner::new(cfg); let _ = runner.run(&strat, |_| Ok(())); diff --git a/crates/evm/fuzz/src/strategies/state.rs b/crates/evm/fuzz/src/strategies/state.rs index cf2f6ec6e..1d959ccc7 100644 --- a/crates/evm/fuzz/src/strategies/state.rs +++ b/crates/evm/fuzz/src/strategies/state.rs @@ -1,6 +1,6 @@ use super::fuzz_param_from_state; use crate::invariant::{ArtifactFilters, FuzzRunIdentifiedContracts}; -use alloy_dyn_abi::{DynSolType, JsonAbiExt}; +use alloy_dyn_abi::JsonAbiExt; use alloy_json_abi::Function; use alloy_primitives::{Address, Bytes, Log, B256, U256}; use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; @@ -14,7 +14,7 @@ use revm::{ interpreter::opcode::{self, spec_opcode_gas}, primitives::SpecId, }; -use std::{fmt, str::FromStr, sync::Arc}; +use std::{fmt, sync::Arc}; /// A set of arbitrary 32 byte data from the VM used to generate values for the strategy. /// @@ -50,7 +50,7 @@ impl FuzzDictionary { } #[inline] - pub fn addresses(&mut self) -> &HashSet
{ + pub fn addresses(&self) -> &HashSet
{ &self.addresses } @@ -62,25 +62,19 @@ impl FuzzDictionary { /// Given a function and some state, it returns a strategy which generated valid calldata for the /// given function's input types, based on state taken from the EVM. -pub fn fuzz_calldata_from_state(func: Function, state: EvmFuzzState) -> BoxedStrategy { +pub fn fuzz_calldata_from_state(func: Function, state: &EvmFuzzState) -> BoxedStrategy { let strats = func .inputs .iter() - .map(|input| { - fuzz_param_from_state( - &DynSolType::from_str(&input.selector_type()).unwrap(), - state.clone(), - ) - }) + .map(|input| fuzz_param_from_state(&input.selector_type().parse().unwrap(), state)) .collect::>(); - strats - .prop_map(move |tokens| { - func.abi_encode_input(&tokens) + .prop_map(move |values| { + func.abi_encode_input(&values) .unwrap_or_else(|_| { panic!( - "Fuzzer generated invalid tokens for function `{}` with inputs {:?}: {:?}", - func.name, func.inputs, tokens + "Fuzzer generated invalid arguments for function `{}` with inputs {:?}: {:?}", + func.name, func.inputs, values ) }) .into() @@ -145,7 +139,7 @@ pub fn build_initial_state( pub fn collect_state_from_call( logs: &[Log], state_changeset: &StateChangeset, - state: EvmFuzzState, + state: &EvmFuzzState, config: &FuzzDictionaryConfig, ) { let mut state = state.write(); From 125988ce28b365eb59c64a032bf3369f90db3a96 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 18 Mar 2024 23:14:07 +0100 Subject: [PATCH 087/430] fix: check for cancun in cast run (#7434) --- crates/cast/bin/cmd/run.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index 8de966906..83cbf5743 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -117,16 +117,18 @@ impl RunArgs { .ok_or_else(|| eyre::eyre!("tx may still be pending: {:?}", tx_hash))? .to::(); + // fetch the block the transaction was mined in + let block = provider.get_block(tx_block_number.into(), true).await?; + // we need to fork off the parent block config.fork_block_number = Some(tx_block_number - 1); let (mut env, fork, chain) = TracingExecutor::get_fork_material(&config, evm_opts).await?; - let mut executor = TracingExecutor::new(env.clone(), fork, self.evm_version, self.debug); + let mut evm_version = self.evm_version; env.block.number = U256::from(tx_block_number); - let block = provider.get_block(tx_block_number.into(), true).await?; if let Some(ref block) = block { env.block.timestamp = block.header.timestamp; env.block.coinbase = block.header.miner; @@ -134,8 +136,18 @@ impl RunArgs { env.block.prevrandao = Some(block.header.mix_hash.unwrap_or_default()); env.block.basefee = block.header.base_fee_per_gas.unwrap_or_default(); env.block.gas_limit = block.header.gas_limit; + + // TODO: we need a smarter way to map the block to the corresponding evm_version for + // commonly used chains + if evm_version.is_none() { + // if the block has the excess_blob_gas field, we assume it's a Cancun block + if block.header.excess_blob_gas.is_some() { + evm_version = Some(EvmVersion::Cancun); + } + } } + let mut executor = TracingExecutor::new(env.clone(), fork, evm_version, self.debug); let mut env = EnvWithHandlerCfg::new_with_spec_id(Box::new(env.clone()), executor.spec_id()); From 0026488754512acf0fd902a0d2c90cf8a09367b0 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 18 Mar 2024 23:26:49 +0100 Subject: [PATCH 088/430] fix: allow fork related cli args without forkurl (#7432) --- crates/common/src/evm.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/crates/common/src/evm.rs b/crates/common/src/evm.rs index 7b9158913..a0cc4d4b8 100644 --- a/crates/common/src/evm.rs +++ b/crates/common/src/evm.rs @@ -118,13 +118,7 @@ pub struct EvmArgs { /// default value: 330 /// /// See also --fork-url and https://docs.alchemy.com/reference/compute-units#what-are-cups-compute-units-per-second - #[arg( - long, - requires = "fork_url", - alias = "cups", - value_name = "CUPS", - help_heading = "Fork config" - )] + #[arg(long, alias = "cups", value_name = "CUPS", help_heading = "Fork config")] pub compute_units_per_second: Option, /// Disables rate limiting for this node's provider. @@ -132,7 +126,6 @@ pub struct EvmArgs { /// See also --fork-url and https://docs.alchemy.com/reference/compute-units#what-are-cups-compute-units-per-second #[arg( long, - requires = "fork_url", value_name = "NO_RATE_LIMITS", help_heading = "Fork config", visible_alias = "no-rate-limit" From af8685f49e8c00f2302875cb5cfbfedf96e50042 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 19 Mar 2024 18:36:29 +0100 Subject: [PATCH 089/430] fix: ignore build info in forge bind (#7444) --- crates/forge/bin/cmd/bind.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/forge/bin/cmd/bind.rs b/crates/forge/bin/cmd/bind.rs index ca76aafa0..5be0f262a 100644 --- a/crates/forge/bin/cmd/bind.rs +++ b/crates/forge/bin/cmd/bind.rs @@ -143,7 +143,7 @@ impl BindArgs { "console[2]?", "CommonBase", "Components", - "[Ss]td(Chains|Math|Error|Json|Utils|Cheats|Style|Invariant|Assertions|Storage(Safe)?)", + "[Ss]td(Chains|Math|Error|Json|Utils|Cheats|Style|Invariant|Assertions|Toml|Storage(Safe)?)", "[Vv]m.*", ]) .extend_names(["IMulticall3"]) @@ -155,6 +155,10 @@ impl BindArgs { let abigens = json_files(artifacts.as_ref()) .into_iter() .filter_map(|path| { + if path.to_string_lossy().contains("/build-info/") { + // ignore the build info json + return None + } // we don't want `.metadata.json files let stem = path.file_stem()?; if stem.to_str()?.ends_with(".metadata") { From a064b63e7f7ea6c312a1e8aebf06379b5c2ab9bf Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 19 Mar 2024 19:04:39 +0100 Subject: [PATCH 090/430] perf: load TLS certs only for https (#7450) --- .../common/src/provider/runtime_transport.rs | 4 ++- crates/config/src/etherscan.rs | 36 ++++++++++--------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/crates/common/src/provider/runtime_transport.rs b/crates/common/src/provider/runtime_transport.rs index 5c2eededf..48411a321 100644 --- a/crates/common/src/provider/runtime_transport.rs +++ b/crates/common/src/provider/runtime_transport.rs @@ -144,7 +144,9 @@ impl RuntimeTransport { /// Connects to an HTTP [alloy_transport_http::Http] transport. async fn connect_http(&self) -> Result { - let mut client_builder = reqwest::Client::builder().timeout(self.timeout); + let mut client_builder = reqwest::Client::builder() + .timeout(self.timeout) + .tls_built_in_root_certs(self.url.scheme() == "https"); let mut headers = reqwest::header::HeaderMap::new(); // If there's a JWT, add it to the headers if we can decode it. diff --git a/crates/config/src/etherscan.rs b/crates/config/src/etherscan.rs index 6e2030cb9..fb254a635 100644 --- a/crates/config/src/etherscan.rs +++ b/crates/config/src/etherscan.rs @@ -306,32 +306,29 @@ impl ResolvedEtherscanConfig { let (mainnet_api, mainnet_url) = NamedChain::Mainnet.etherscan_urls().expect("exist; qed"); let cache = chain - .or_else(|| { - if api_url == mainnet_api { - // try to match against mainnet, which is usually the most common target - Some(NamedChain::Mainnet.into()) - } else { - None - } - }) + // try to match against mainnet, which is usually the most common target + .or_else(|| (api_url == mainnet_api).then(Chain::mainnet)) .and_then(Config::foundry_etherscan_chain_cache_dir); - if let Some(ref cache_path) = cache { + if let Some(cache_path) = &cache { // we also create the `sources` sub dir here if let Err(err) = std::fs::create_dir_all(cache_path.join("sources")) { warn!("could not create etherscan cache dir: {:?}", err); } } + let api_url = into_url(&api_url)?; + let client = reqwest::Client::builder() + .user_agent(ETHERSCAN_USER_AGENT) + .tls_built_in_root_certs(api_url.scheme() == "https") + .build()?; foundry_block_explorers::Client::builder() - .with_client(reqwest::Client::builder().user_agent(ETHERSCAN_USER_AGENT).build()?) + .with_client(client) .with_api_key(api_key) - .with_api_url(api_url.as_str())? - .with_url( - // the browser url is not used/required by the client so we can simply set the - // mainnet browser url here - browser_url.as_deref().unwrap_or(mainnet_url), - )? + .with_api_url(api_url)? + // the browser url is not used/required by the client so we can simply set the + // mainnet browser url here + .with_url(browser_url.as_deref().unwrap_or(mainnet_url))? .with_cache(cache, Duration::from_secs(24 * 60 * 60)) .build() } @@ -419,6 +416,13 @@ impl fmt::Display for EtherscanApiKey { } } +/// This is a hack to work around `IntoUrl`'s sealed private functions, which can't be called +/// normally. +#[inline] +fn into_url(url: impl reqwest::IntoUrl) -> std::result::Result { + url.into_url() +} + #[cfg(test)] mod tests { use super::*; From ffed0deb6377f3682c6261fd52f24a6d203d0fa5 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 19 Mar 2024 19:13:44 +0100 Subject: [PATCH 091/430] chore: provide a better error message for unknown cheatcodes (#7436) --- crates/cheatcodes/src/inspector.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index bf39684b4..699840d93 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -222,7 +222,17 @@ impl Cheatcodes { call: &CallInputs, ) -> Result { // decode the cheatcode call - let decoded = Vm::VmCalls::abi_decode(&call.input, false)?; + let decoded = Vm::VmCalls::abi_decode(&call.input, false).map_err(|e| { + if let alloy_sol_types::Error::UnknownSelector { name: _, selector } = e { + let msg = format!( + "unknown cheatcode with selector {selector}; \ + you may have a mismatch between the `Vm` interface (likely in `forge-std`) \ + and the `forge` version" + ); + return alloy_sol_types::Error::Other(std::borrow::Cow::Owned(msg)); + } + e + })?; let caller = call.context.caller; // ensure the caller is allowed to execute cheatcodes, From a527c1c622e6929f67c5c71c082a79957de9103b Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 19 Mar 2024 19:50:59 +0100 Subject: [PATCH 092/430] chore: abstract away hashbrown (#7395) * chore: abstract away hashbrown * deps * fix * log * fix: use indexset * test * test --- Cargo.lock | 5 ++-- Cargo.toml | 11 ++----- crates/anvil/src/eth/backend/cheats.rs | 3 +- crates/anvil/src/eth/backend/db.rs | 4 +-- crates/anvil/src/eth/backend/mem/state.rs | 13 ++++---- crates/config/Cargo.toml | 2 +- crates/evm/core/src/backend/snapshot.rs | 8 ++--- crates/evm/evm/Cargo.toml | 8 ++--- crates/evm/evm/src/lib.rs | 8 ++++- crates/evm/fuzz/Cargo.toml | 3 +- crates/evm/fuzz/src/strategies/calldata.rs | 3 +- crates/evm/fuzz/src/strategies/state.rs | 18 ++++++----- crates/evm/traces/Cargo.toml | 1 - .../evm/traces/src/identifier/signatures.rs | 7 +++-- crates/forge/bin/cmd/remappings.rs | 9 +++--- crates/forge/src/gas_report.rs | 6 ++-- crates/forge/tests/it/fuzz.rs | 4 +-- crates/forge/tests/it/invariant.rs | 30 ++++++++++--------- crates/verify/src/etherscan/mod.rs | 3 +- 19 files changed, 76 insertions(+), 70 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ab4caf50e..29154610a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3392,7 +3392,6 @@ dependencies = [ "foundry-evm-coverage", "foundry-evm-fuzz", "foundry-evm-traces", - "hashbrown 0.14.3", "itertools 0.12.1", "parking_lot", "proptest", @@ -3469,12 +3468,13 @@ dependencies = [ "foundry-evm-core", "foundry-evm-coverage", "foundry-evm-traces", - "hashbrown 0.14.3", + "indexmap", "itertools 0.12.1", "parking_lot", "proptest", "rand 0.8.5", "revm", + "rustc-hash", "serde", "thiserror", "tracing", @@ -3496,7 +3496,6 @@ dependencies = [ "foundry-config", "foundry-evm-core", "futures", - "hashbrown 0.14.3", "itertools 0.12.1", "once_cell", "revm-inspectors", diff --git a/Cargo.toml b/Cargo.toml index ab21bddfc..e8dac00db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -143,8 +143,8 @@ foundry-compilers = { version = "0.3.13", default-features = false } ## revm # no default features to avoid c-kzg -revm = { version = "7.1", default-features = false } -revm-primitives = { version = "3", default-features = false } +revm = { version = "7.1", default-features = false, features = ["std"] } +revm-primitives = { version = "3", default-features = false, features = ["std"] } revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "ba0b6ab", features = [ "serde", ] } @@ -207,14 +207,9 @@ toml = "0.8" tracing = "0.1" tracing-subscriber = "0.3" vergen = { version = "8", default-features = false } +indexmap = "2.2" axum = "0.6" hyper = "0.14" tower = "0.4" tower-http = "0.4" - -# [patch.crates-io] -# revm = { path = "../../danipopes/revm/crates/revm" } -# revm-interpreter = { path = "../../danipopes/revm/crates/interpreter" } -# revm-primitives = { path = "../../danipopes/revm/crates/primitives" } -# revm-precompile = { path = "../../danipopes/revm/crates/precompile" } diff --git a/crates/anvil/src/eth/backend/cheats.rs b/crates/anvil/src/eth/backend/cheats.rs index 949ea10e7..0dc3189b0 100644 --- a/crates/anvil/src/eth/backend/cheats.rs +++ b/crates/anvil/src/eth/backend/cheats.rs @@ -2,9 +2,8 @@ use alloy_primitives::{Address, Signature}; use anvil_core::eth::transaction::impersonated_signature; -use foundry_evm::hashbrown::HashSet; use parking_lot::RwLock; -use std::sync::Arc; +use std::{collections::HashSet, sync::Arc}; /// Manages user modifications that may affect the node's behavior /// diff --git a/crates/anvil/src/eth/backend/db.rs b/crates/anvil/src/eth/backend/db.rs index 2630d19a7..31e60ddbe 100644 --- a/crates/anvil/src/eth/backend/db.rs +++ b/crates/anvil/src/eth/backend/db.rs @@ -8,10 +8,9 @@ use foundry_common::errors::FsPathError; use foundry_evm::{ backend::{DatabaseError, DatabaseResult, MemDb, RevertSnapshotAction, StateSnapshot}, fork::BlockchainDb, - hashbrown::HashMap, revm::{ db::{CacheDB, DatabaseRef, DbAccount}, - primitives::{BlockEnv, Bytecode, KECCAK_EMPTY}, + primitives::{BlockEnv, Bytecode, HashMap, KECCAK_EMPTY}, Database, DatabaseCommit, }, }; @@ -225,6 +224,7 @@ impl> MaybeHashDatabase for CacheDB { fn maybe_as_hash_db(&self) -> Option<(AsHashDB, B256)> { Some(trie_hash_db(&self.accounts)) } + fn clear_into_snapshot(&mut self) -> StateSnapshot { let db_accounts = std::mem::take(&mut self.accounts); let mut accounts = HashMap::new(); diff --git a/crates/anvil/src/eth/backend/mem/state.rs b/crates/anvil/src/eth/backend/mem/state.rs index af936578d..29a774f18 100644 --- a/crates/anvil/src/eth/backend/mem/state.rs +++ b/crates/anvil/src/eth/backend/mem/state.rs @@ -7,17 +7,16 @@ use alloy_rpc_types::state::StateOverride; use anvil_core::eth::trie::RefSecTrieDBMut; use foundry_evm::{ backend::DatabaseError, - hashbrown::HashMap as Map, revm::{ db::{CacheDB, DatabaseRef, DbAccount}, - primitives::{AccountInfo, Bytecode}, + primitives::{AccountInfo, Bytecode, HashMap}, }, }; use memory_db::HashKey; use trie_db::TrieMut; /// Returns storage trie of an account as `HashDB` -pub fn storage_trie_db(storage: &Map) -> (AsHashDB, B256) { +pub fn storage_trie_db(storage: &HashMap) -> (AsHashDB, B256) { // Populate DB with full trie from entries. let (db, root) = { let mut db = , _>>::default(); @@ -38,7 +37,7 @@ pub fn storage_trie_db(storage: &Map) -> (AsHashDB, B256) { } /// Returns the account data as `HashDB` -pub fn trie_hash_db(accounts: &Map) -> (AsHashDB, B256) { +pub fn trie_hash_db(accounts: &HashMap) -> (AsHashDB, B256) { let accounts = trie_accounts(accounts); // Populate DB with full trie from entries. @@ -58,7 +57,7 @@ pub fn trie_hash_db(accounts: &Map) -> (AsHashDB, B256) { } /// Returns all RLP-encoded Accounts -pub fn trie_accounts(accounts: &Map) -> Vec<(Address, Bytes)> { +pub fn trie_accounts(accounts: &HashMap) -> Vec<(Address, Bytes)> { accounts .iter() .map(|(address, account)| { @@ -68,12 +67,12 @@ pub fn trie_accounts(accounts: &Map) -> Vec<(Address, Bytes) .collect() } -pub fn state_merkle_trie_root(accounts: &Map) -> B256 { +pub fn state_merkle_trie_root(accounts: &HashMap) -> B256 { trie_hash_db(accounts).1 } /// Returns the RLP for this account. -pub fn trie_account_rlp(info: &AccountInfo, storage: &Map) -> Bytes { +pub fn trie_account_rlp(info: &AccountInfo, storage: &HashMap) -> Bytes { let mut out: Vec = Vec::new(); let list: [&dyn Encodable; 4] = [&info.nonce, &info.balance, &storage_trie_db(storage).1, &info.code_hash]; diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml index 4f42da66d..0504bb369 100644 --- a/crates/config/Cargo.toml +++ b/crates/config/Cargo.toml @@ -16,7 +16,7 @@ foundry-compilers = { workspace = true, features = ["svm-solc"] } alloy-chains = { workspace = true, features = ["serde"] } alloy-primitives = { workspace = true, features = ["serde"] } -revm-primitives = { workspace = true, default-features = false, features = ["std"] } +revm-primitives.workspace = true solang-parser.workspace = true diff --git a/crates/evm/core/src/backend/snapshot.rs b/crates/evm/core/src/backend/snapshot.rs index 53ce6f7dd..4c0c665f2 100644 --- a/crates/evm/core/src/backend/snapshot.rs +++ b/crates/evm/core/src/backend/snapshot.rs @@ -1,6 +1,6 @@ use alloy_primitives::{Address, B256, U256}; use revm::{ - primitives::{AccountInfo, Env, HashMap as Map}, + primitives::{AccountInfo, Env, HashMap}, JournaledState, }; use serde::{Deserialize, Serialize}; @@ -8,9 +8,9 @@ use serde::{Deserialize, Serialize}; /// A minimal abstraction of a state at a certain point in time #[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct StateSnapshot { - pub accounts: Map, - pub storage: Map>, - pub block_hashes: Map, + pub accounts: HashMap, + pub storage: HashMap>, + pub block_hashes: HashMap, } /// Represents a snapshot taken during evm execution diff --git a/crates/evm/evm/Cargo.toml b/crates/evm/evm/Cargo.toml index a1857b303..9336ffff7 100644 --- a/crates/evm/evm/Cargo.toml +++ b/crates/evm/evm/Cargo.toml @@ -24,7 +24,6 @@ alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } alloy-json-abi.workspace = true alloy-primitives = { workspace = true, features = ["serde", "getrandom", "arbitrary", "rlp"] } alloy-sol-types.workspace = true -hashbrown = { version = "0.14", features = ["serde"] } revm = { workspace = true, default-features = false, features = [ "std", "serde", @@ -36,14 +35,13 @@ revm = { workspace = true, default-features = false, features = [ ] } revm-inspectors.workspace = true -itertools.workspace = true - arrayvec.workspace = true eyre = "0.6" hex.workspace = true +itertools.workspace = true parking_lot = "0.12" proptest = "1" +rand.workspace = true +rayon = "1" thiserror = "1" tracing = "0.1" -rayon = "1" -rand.workspace = true diff --git a/crates/evm/evm/src/lib.rs b/crates/evm/evm/src/lib.rs index 7c69c1823..aa5386b3e 100644 --- a/crates/evm/evm/src/lib.rs +++ b/crates/evm/evm/src/lib.rs @@ -17,4 +17,10 @@ pub use foundry_evm_traces as traces; // TODO: We should probably remove these, but it's a pretty big breaking change. #[doc(hidden)] -pub use {hashbrown, revm}; +pub use revm; + +#[doc(hidden)] +#[deprecated = "use `{hash_map, hash_set, HashMap, HashSet}` in `std::collections` or `revm::primitives` instead"] +pub mod hashbrown { + pub use revm::primitives::{hash_map, hash_set, HashMap, HashSet}; +} diff --git a/crates/evm/fuzz/Cargo.toml b/crates/evm/fuzz/Cargo.toml index d8c68428e..3c20b029a 100644 --- a/crates/evm/fuzz/Cargo.toml +++ b/crates/evm/fuzz/Cargo.toml @@ -32,7 +32,6 @@ revm = { workspace = true, default-features = false, features = [ ] } eyre = "0.6" -hashbrown = { version = "0.14", features = ["serde"] } itertools.workspace = true parking_lot = "0.12" proptest = "1" @@ -40,3 +39,5 @@ rand.workspace = true serde = "1" thiserror = "1" tracing = "0.1" +rustc-hash.workspace = true +indexmap.workspace = true diff --git a/crates/evm/fuzz/src/strategies/calldata.rs b/crates/evm/fuzz/src/strategies/calldata.rs index cf5030203..b3d41bfad 100644 --- a/crates/evm/fuzz/src/strategies/calldata.rs +++ b/crates/evm/fuzz/src/strategies/calldata.rs @@ -3,9 +3,8 @@ use alloy_dyn_abi::JsonAbiExt; use alloy_json_abi::Function; use alloy_primitives::{Address, Bytes}; use foundry_config::FuzzDictionaryConfig; -use hashbrown::HashSet; use proptest::prelude::Strategy; -use std::sync::Arc; +use std::{collections::HashSet, sync::Arc}; /// Clonable wrapper around [CalldataFuzzDictionary]. #[derive(Clone, Debug)] diff --git a/crates/evm/fuzz/src/strategies/state.rs b/crates/evm/fuzz/src/strategies/state.rs index 1d959ccc7..1cf1d3788 100644 --- a/crates/evm/fuzz/src/strategies/state.rs +++ b/crates/evm/fuzz/src/strategies/state.rs @@ -6,7 +6,6 @@ use alloy_primitives::{Address, Bytes, Log, B256, U256}; use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; use foundry_config::FuzzDictionaryConfig; use foundry_evm_core::utils::StateChangeset; -use hashbrown::HashSet; use parking_lot::RwLock; use proptest::prelude::{BoxedStrategy, Strategy}; use revm::{ @@ -16,6 +15,11 @@ use revm::{ }; use std::{fmt, sync::Arc}; +// We're using `IndexSet` to have a stable element order when restoring persisted state, as well as +// for performance when iterating over the sets. +type FxIndexSet = + indexmap::set::IndexSet>; + /// A set of arbitrary 32 byte data from the VM used to generate values for the strategy. /// /// Wrapped in a shareable container. @@ -24,9 +28,9 @@ pub type EvmFuzzState = Arc>; #[derive(Default)] pub struct FuzzDictionary { /// Collected state values. - state_values: HashSet<[u8; 32]>, + state_values: FxIndexSet<[u8; 32]>, /// Addresses that already had their PUSH bytes collected. - addresses: HashSet
, + addresses: FxIndexSet
, } impl fmt::Debug for FuzzDictionary { @@ -40,22 +44,22 @@ impl fmt::Debug for FuzzDictionary { impl FuzzDictionary { #[inline] - pub fn values(&self) -> &HashSet<[u8; 32]> { + pub fn values(&self) -> &FxIndexSet<[u8; 32]> { &self.state_values } #[inline] - pub fn values_mut(&mut self) -> &mut HashSet<[u8; 32]> { + pub fn values_mut(&mut self) -> &mut FxIndexSet<[u8; 32]> { &mut self.state_values } #[inline] - pub fn addresses(&self) -> &HashSet
{ + pub fn addresses(&self) -> &FxIndexSet
{ &self.addresses } #[inline] - pub fn addresses_mut(&mut self) -> &mut HashSet
{ + pub fn addresses_mut(&mut self) -> &mut FxIndexSet
{ &mut self.addresses } } diff --git a/crates/evm/traces/Cargo.toml b/crates/evm/traces/Cargo.toml index 94dd36b63..be3cecb36 100644 --- a/crates/evm/traces/Cargo.toml +++ b/crates/evm/traces/Cargo.toml @@ -25,7 +25,6 @@ revm-inspectors.workspace = true eyre = "0.6" futures = "0.3" -hashbrown = "0.14" hex.workspace = true itertools.workspace = true once_cell = "1" diff --git a/crates/evm/traces/src/identifier/signatures.rs b/crates/evm/traces/src/identifier/signatures.rs index b1f3124cd..976e9ea69 100644 --- a/crates/evm/traces/src/identifier/signatures.rs +++ b/crates/evm/traces/src/identifier/signatures.rs @@ -4,9 +4,12 @@ use foundry_common::{ fs, selectors::{SelectorType, SignEthClient}, }; -use hashbrown::HashSet; use serde::{Deserialize, Serialize}; -use std::{collections::BTreeMap, path::PathBuf, sync::Arc}; +use std::{ + collections::{BTreeMap, HashSet}, + path::PathBuf, + sync::Arc, +}; use tokio::sync::RwLock; pub type SingleSignaturesIdentifier = Arc>; diff --git a/crates/forge/bin/cmd/remappings.rs b/crates/forge/bin/cmd/remappings.rs index 6728f0ae1..b33f3442c 100644 --- a/crates/forge/bin/cmd/remappings.rs +++ b/crates/forge/bin/cmd/remappings.rs @@ -2,8 +2,7 @@ use clap::{Parser, ValueHint}; use eyre::Result; use foundry_cli::utils::LoadConfig; use foundry_config::impl_figment_convert_basic; -use foundry_evm::hashbrown::HashMap; -use std::path::PathBuf; +use std::{collections::BTreeMap, path::PathBuf}; /// CLI arguments for `forge remappings`. #[derive(Clone, Debug, Parser)] @@ -25,7 +24,7 @@ impl RemappingArgs { let config = self.try_load_config_emit_warnings()?; if self.pretty { - let mut groups = HashMap::<_, Vec<_>>::with_capacity(config.remappings.len()); + let mut groups = BTreeMap::<_, Vec<_>>::new(); for remapping in config.remappings { groups.entry(remapping.context.clone()).or_default().push(remapping); } @@ -36,14 +35,14 @@ impl RemappingArgs { println!("Global:"); } - for mut remapping in remappings.into_iter() { + for mut remapping in remappings { remapping.context = None; // avoid writing context twice println!("- {remapping}"); } println!(); } } else { - for remapping in config.remappings.into_iter() { + for remapping in config.remappings { println!("{remapping}"); } } diff --git a/crates/forge/src/gas_report.rs b/crates/forge/src/gas_report.rs index de36b871c..2bdd6d4da 100644 --- a/crates/forge/src/gas_report.rs +++ b/crates/forge/src/gas_report.rs @@ -2,14 +2,16 @@ use crate::{ constants::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS}, - hashbrown::HashSet, traces::{CallTraceArena, CallTraceDecoder, CallTraceNode, DecodedCallData}, }; use comfy_table::{presets::ASCII_MARKDOWN, *}; use foundry_common::{calc, TestFunctionExt}; use foundry_evm::traces::CallKind; use serde::{Deserialize, Serialize}; -use std::{collections::BTreeMap, fmt::Display}; +use std::{ + collections::{BTreeMap, HashSet}, + fmt::Display, +}; /// Represents the gas report for a set of contracts. #[derive(Clone, Debug, Default, Serialize, Deserialize)] diff --git a/crates/forge/tests/it/fuzz.rs b/crates/forge/tests/it/fuzz.rs index 47bc7745d..50ce05d4a 100644 --- a/crates/forge/tests/it/fuzz.rs +++ b/crates/forge/tests/it/fuzz.rs @@ -136,13 +136,13 @@ async fn test_persist_fuzz_failure() { }; // run several times and compare counterexamples calldata - for _ in 0..10 { + for i in 0..10 { let new_calldata = match get_failure_result!() { Some(CounterExample::Single(counterexample)) => counterexample.calldata, _ => Bytes::new(), }; // calldata should be the same with the initial one - assert_eq!(initial_calldata, new_calldata); + assert_eq!(initial_calldata, new_calldata, "run {i}"); } // write new failure in different file diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index 99b9ad962..9339a54c3 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -235,22 +235,24 @@ async fn test_invariant_shrink() { CounterExample::Single(_) => panic!("CounterExample should be a sequence."), // `fuzz_seed` at 119 makes this sequence shrinkable from 4 to 2. CounterExample::Sequence(sequence) => { - assert_eq!(sequence.len(), 2); + assert!(sequence.len() <= 3); - // call order should always be preserved - let create_fren_sequence = sequence[0].clone(); - assert_eq!( - create_fren_sequence.contract_name.unwrap(), - "fuzz/invariant/common/InvariantInnerContract.t.sol:Jesus" - ); - assert_eq!(create_fren_sequence.signature.unwrap(), "create_fren()"); + if sequence.len() == 2 { + // call order should always be preserved + let create_fren_sequence = sequence[0].clone(); + assert_eq!( + create_fren_sequence.contract_name.unwrap(), + "fuzz/invariant/common/InvariantInnerContract.t.sol:Jesus" + ); + assert_eq!(create_fren_sequence.signature.unwrap(), "create_fren()"); - let betray_sequence = sequence[1].clone(); - assert_eq!( - betray_sequence.contract_name.unwrap(), - "fuzz/invariant/common/InvariantInnerContract.t.sol:Judas" - ); - assert_eq!(betray_sequence.signature.unwrap(), "betray()"); + let betray_sequence = sequence[1].clone(); + assert_eq!( + betray_sequence.contract_name.unwrap(), + "fuzz/invariant/common/InvariantInnerContract.t.sol:Judas" + ); + assert_eq!(betray_sequence.signature.unwrap(), "betray()"); + } } }; } diff --git a/crates/verify/src/etherscan/mod.rs b/crates/verify/src/etherscan/mod.rs index 42986a160..7e79321ef 100644 --- a/crates/verify/src/etherscan/mod.rs +++ b/crates/verify/src/etherscan/mod.rs @@ -18,12 +18,13 @@ use foundry_compilers::{ Artifact, Project, Solc, }; use foundry_config::{Chain, Config, SolcReq}; -use foundry_evm::{constants::DEFAULT_CREATE2_DEPLOYER, hashbrown::HashSet}; +use foundry_evm::constants::DEFAULT_CREATE2_DEPLOYER; use futures::FutureExt; use once_cell::sync::Lazy; use regex::Regex; use semver::{BuildMetadata, Version}; use std::{ + collections::HashSet, fmt::Debug, path::{Path, PathBuf}, }; From 03b60c9da408aec10ea8a5f20b6d0dc56566bdaa Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 19 Mar 2024 23:43:20 +0100 Subject: [PATCH 093/430] chore(evm): remove trace printer, trim inspector stack (#7437) --- crates/cast/bin/cmd/run.rs | 12 +- crates/evm/evm/src/executors/mod.rs | 6 - crates/evm/evm/src/inspectors/mod.rs | 3 - crates/evm/evm/src/inspectors/printer.rs | 66 --------- crates/evm/evm/src/inspectors/stack.rs | 175 ++++++++--------------- 5 files changed, 64 insertions(+), 198 deletions(-) delete mode 100644 crates/evm/evm/src/inspectors/printer.rs diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index 83cbf5743..3fc09565c 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -30,7 +30,8 @@ pub struct RunArgs { debug: bool, /// Print out opcode traces. - #[arg(long, short)] + #[deprecated] + #[arg(long, short, hide = true)] trace_printer: bool, /// Executes the transaction only with the state from the previous block. @@ -82,6 +83,11 @@ impl RunArgs { /// /// Note: This executes the transaction(s) as is: Cheatcodes are disabled pub async fn run(self) -> Result<()> { + #[allow(deprecated)] + if self.trace_printer { + eprintln!("WARNING: --trace-printer is deprecated and has no effect\n"); + } + let figment = Config::figment_with_root(find_project_root_path(None).unwrap()).merge(self.rpc); let evm_opts = figment.extract::()?; @@ -129,7 +135,7 @@ impl RunArgs { env.block.number = U256::from(tx_block_number); - if let Some(ref block) = block { + if let Some(block) = &block { env.block.timestamp = block.header.timestamp; env.block.coinbase = block.header.miner; env.block.difficulty = block.header.difficulty; @@ -213,8 +219,6 @@ impl RunArgs { // Execute our transaction let result = { - executor.set_trace_printer(self.trace_printer); - configure_tx_env(&mut env, &tx); if let Some(to) = tx.to { diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 45abbfc9c..76f67abd9 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -171,12 +171,6 @@ impl Executor { self } - #[inline] - pub fn set_trace_printer(&mut self, trace_printer: bool) -> &mut Self { - self.inspector.print(trace_printer); - self - } - #[inline] pub fn set_gas_limit(&mut self, gas_limit: U256) -> &mut Self { self.gas_limit = gas_limit; diff --git a/crates/evm/evm/src/inspectors/mod.rs b/crates/evm/evm/src/inspectors/mod.rs index 059033889..786786b28 100644 --- a/crates/evm/evm/src/inspectors/mod.rs +++ b/crates/evm/evm/src/inspectors/mod.rs @@ -16,8 +16,5 @@ pub use debugger::Debugger; mod logs; pub use logs::LogCollector; -mod printer; -pub use printer::TracePrinter; - mod stack; pub use stack::{InspectorData, InspectorStack, InspectorStackBuilder}; diff --git a/crates/evm/evm/src/inspectors/printer.rs b/crates/evm/evm/src/inspectors/printer.rs deleted file mode 100644 index 81110669f..000000000 --- a/crates/evm/evm/src/inspectors/printer.rs +++ /dev/null @@ -1,66 +0,0 @@ -use revm::{ - interpreter::{opcode, CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter}, - Database, EvmContext, Inspector, -}; - -#[derive(Clone, Debug, Default)] -#[non_exhaustive] -pub struct TracePrinter; - -impl Inspector for TracePrinter { - // get opcode by calling `interp.contract.opcode(interp.program_counter())`. - // all other information can be obtained from interp. - fn step(&mut self, interp: &mut Interpreter, ecx: &mut EvmContext) { - let opcode = interp.current_opcode(); - let opcode_str = opcode::OPCODE_JUMPMAP[opcode as usize]; - let gas_remaining = interp.gas.remaining(); - println!( - "depth:{}, PC:{}, gas:{:#x}({}), OPCODE: {:?}({:?}) refund:{:#x}({}) Stack:{:?}, Data size:{}, Data: 0x{}", - ecx.journaled_state.depth(), - interp.program_counter(), - gas_remaining, - gas_remaining, - opcode_str.unwrap_or(""), - opcode, - interp.gas.refunded(), - interp.gas.refunded(), - interp.stack.data(), - interp.shared_memory.len(), - hex::encode(interp.shared_memory.context_memory()), - ); - } - - #[inline] - fn call( - &mut self, - _context: &mut EvmContext, - inputs: &mut CallInputs, - ) -> Option { - println!( - "SM CALL: {},context:{:?}, is_static:{:?}, transfer:{:?}, input_size:{:?}", - inputs.contract, - inputs.context, - inputs.is_static, - inputs.transfer, - inputs.input.len(), - ); - None - } - - #[inline] - fn create( - &mut self, - _context: &mut EvmContext, - inputs: &mut CreateInputs, - ) -> Option { - println!( - "CREATE CALL: caller:{}, scheme:{:?}, value:{:?}, init_code:{:?}, gas:{:?}", - inputs.caller, - inputs.scheme, - inputs.value, - hex::encode(&inputs.init_code), - inputs.gas_limit - ); - None - } -} diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 67fbb3fa7..ea0af6bbc 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -1,6 +1,6 @@ use super::{ Cheatcodes, CheatsConfig, ChiselState, CoverageCollector, Debugger, Fuzzer, LogCollector, - StackSnapshotType, TracePrinter, TracingInspector, TracingInspectorConfig, + StackSnapshotType, TracingInspector, TracingInspectorConfig, }; use alloy_primitives::{Address, Bytes, Log, U256}; use foundry_evm_core::{backend::DatabaseExt, debug::DebugArena}; @@ -41,8 +41,6 @@ pub struct InspectorStackBuilder { pub logs: Option, /// Whether coverage info should be collected. pub coverage: Option, - /// Whether to print all opcode traces into the console. Useful for debugging the EVM. - pub print: Option, /// The chisel state inspector. pub chisel_state: Option, /// Whether to enable call isolation. @@ -114,13 +112,6 @@ impl InspectorStackBuilder { self } - /// Set whether to enable the trace printer. - #[inline] - pub fn print(mut self, yes: bool) -> Self { - self.print = Some(yes); - self - } - /// Set whether to enable the tracer. #[inline] pub fn trace(mut self, yes: bool) -> Self { @@ -149,7 +140,6 @@ impl InspectorStackBuilder { debug, logs, coverage, - print, chisel_state, enable_isolation, } = self; @@ -168,7 +158,6 @@ impl InspectorStackBuilder { stack.collect_coverage(coverage.unwrap_or(false)); stack.collect_logs(logs.unwrap_or(true)); stack.enable_debugger(debug.unwrap_or(false)); - stack.print(print.unwrap_or(false)); stack.tracing(trace.unwrap_or(false)); stack.enable_isolation(enable_isolation); @@ -198,24 +187,45 @@ macro_rules! call_inspectors { /// Same as [call_inspectors] macro, but with depth adjustment for isolated execution. macro_rules! call_inspectors_adjust_depth { - ([$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $call:expr, $self:ident, $data:ident $(,)?) => { + (#[no_ret] [$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $call:expr, $self:ident, $data:ident $(,)?) => { if $self.in_inner_context { $data.journaled_state.depth += 1; + $( + if let Some($id) = $inspector { + $call + } + )+ + $data.journaled_state.depth -= 1; + } else { + $( + if let Some($id) = $inspector { + $call + } + )+ } - {$( - if let Some($id) = $inspector { - if let Some(result) = $call { - if $self.in_inner_context { + }; + ([$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $call:expr, $self:ident, $data:ident $(,)?) => { + if $self.in_inner_context { + $data.journaled_state.depth += 1; + $( + if let Some($id) = $inspector { + if let Some(result) = $call { $data.journaled_state.depth -= 1; + return result; } - return result; } - } - )+} - if $self.in_inner_context { + )+ $data.journaled_state.depth -= 1; + } else { + $( + if let Some($id) = $inspector { + if let Some(result) = $call { + return result; + } + } + )+ } - } + }; } /// Helper method which updates data in the state with the data from the database. @@ -269,7 +279,6 @@ pub struct InspectorStack { pub debugger: Option, pub fuzzer: Option, pub log_collector: Option, - pub printer: Option, pub tracer: Option, pub enable_isolation: bool, @@ -354,12 +363,6 @@ impl InspectorStack { self.log_collector = yes.then(Default::default); } - /// Set whether to enable the trace printer. - #[inline] - pub fn print(&mut self, yes: bool) { - self.printer = yes.then(Default::default); - } - /// Set whether to enable the tracer. #[inline] pub fn tracing(&mut self, yes: bool) { @@ -403,28 +406,16 @@ impl InspectorStack { ) -> CallOutcome { let result = outcome.result.result; call_inspectors_adjust_depth!( - [ - &mut self.fuzzer, - &mut self.debugger, - &mut self.tracer, - &mut self.coverage, - &mut self.log_collector, - &mut self.cheatcodes, - &mut self.printer - ], + [&mut self.fuzzer, &mut self.debugger, &mut self.tracer, &mut self.cheatcodes], |inspector| { let new_outcome = inspector.call_end(ecx, inputs, outcome.clone()); // If the inspector returns a different status or a revert with a non-empty message, // we assume it wants to tell us something - if new_outcome.result.result != result || + let different = new_outcome.result.result != result || (new_outcome.result.result == InstructionResult::Revert && - new_outcome.output() != outcome.output()) - { - Some(new_outcome) - } else { - None - } + new_outcome.output() != outcome.output()); + different.then_some(new_outcome) }, self, ecx @@ -578,18 +569,9 @@ impl InspectorStack { impl Inspector<&mut DB> for InspectorStack { fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<&mut DB>) { call_inspectors_adjust_depth!( - [ - &mut self.debugger, - &mut self.coverage, - &mut self.tracer, - &mut self.log_collector, - &mut self.cheatcodes, - &mut self.printer - ], - |inspector| { - inspector.initialize_interp(interpreter, ecx); - None::<()> - }, + #[no_ret] + [&mut self.coverage, &mut self.tracer, &mut self.cheatcodes], + |inspector| inspector.initialize_interp(interpreter, ecx), self, ecx ); @@ -597,19 +579,15 @@ impl Inspector<&mut DB> for InspectorStack { fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<&mut DB>) { call_inspectors_adjust_depth!( + #[no_ret] [ &mut self.fuzzer, &mut self.debugger, &mut self.tracer, &mut self.coverage, - &mut self.log_collector, &mut self.cheatcodes, - &mut self.printer ], - |inspector| { - inspector.step(interpreter, ecx); - None::<()> - }, + |inspector| inspector.step(interpreter, ecx), self, ecx ); @@ -617,18 +595,9 @@ impl Inspector<&mut DB> for InspectorStack { fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<&mut DB>) { call_inspectors_adjust_depth!( - [ - &mut self.debugger, - &mut self.tracer, - &mut self.log_collector, - &mut self.cheatcodes, - &mut self.printer, - &mut self.chisel_state - ], - |inspector| { - inspector.step_end(interpreter, ecx); - None::<()> - }, + #[no_ret] + [&mut self.tracer, &mut self.cheatcodes, &mut self.chisel_state], + |inspector| inspector.step_end(interpreter, ecx), self, ecx ); @@ -636,11 +605,9 @@ impl Inspector<&mut DB> for InspectorStack { fn log(&mut self, ecx: &mut EvmContext<&mut DB>, log: &Log) { call_inspectors_adjust_depth!( - [&mut self.tracer, &mut self.log_collector, &mut self.cheatcodes, &mut self.printer], - |inspector| { - inspector.log(ecx, log); - None::<()> - }, + #[no_ret] + [&mut self.tracer, &mut self.log_collector, &mut self.cheatcodes], + |inspector| inspector.log(ecx, log), self, ecx ); @@ -661,10 +628,8 @@ impl Inspector<&mut DB> for InspectorStack { &mut self.fuzzer, &mut self.debugger, &mut self.tracer, - &mut self.coverage, &mut self.log_collector, &mut self.cheatcodes, - &mut self.printer ], |inspector| { let mut out = None; @@ -734,15 +699,8 @@ impl Inspector<&mut DB> for InspectorStack { } call_inspectors_adjust_depth!( - [ - &mut self.debugger, - &mut self.tracer, - &mut self.coverage, - &mut self.log_collector, - &mut self.cheatcodes, - &mut self.printer - ], - |inspector| { inspector.create(ecx, create).map(Some) }, + [&mut self.debugger, &mut self.tracer, &mut self.coverage, &mut self.cheatcodes], + |inspector| inspector.create(ecx, create).map(Some), self, ecx ); @@ -777,27 +735,16 @@ impl Inspector<&mut DB> for InspectorStack { let result = outcome.result.result; call_inspectors_adjust_depth!( - [ - &mut self.debugger, - &mut self.tracer, - &mut self.coverage, - &mut self.log_collector, - &mut self.cheatcodes, - &mut self.printer - ], + [&mut self.debugger, &mut self.tracer, &mut self.cheatcodes], |inspector| { let new_outcome = inspector.create_end(ecx, call, outcome.clone()); // If the inspector returns a different status or a revert with a non-empty message, // we assume it wants to tell us something - if new_outcome.result.result != result || + let different = new_outcome.result.result != result || (new_outcome.result.result == InstructionResult::Revert && - new_outcome.output() != outcome.output()) - { - Some(new_outcome) - } else { - None - } + new_outcome.output() != outcome.output()); + different.then_some(new_outcome) }, self, ecx @@ -807,18 +754,8 @@ impl Inspector<&mut DB> for InspectorStack { } fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) { - call_inspectors!( - [ - &mut self.debugger, - &mut self.tracer, - &mut self.log_collector, - &mut self.cheatcodes, - &mut self.printer, - &mut self.chisel_state - ], - |inspector| { - Inspector::::selfdestruct(inspector, contract, target, value); - } - ); + call_inspectors!([&mut self.tracer], |inspector| Inspector::::selfdestruct( + inspector, contract, target, value + )); } } From a7f1b3b907b76454eb9e315992b8140b6d292e00 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Wed, 20 Mar 2024 02:05:31 +0200 Subject: [PATCH 094/430] fix(fuzz): prevent int strategy to overflow when complicate (#7447) --- crates/evm/fuzz/src/strategies/int.rs | 28 +++++++++++++++++++++++++- crates/evm/fuzz/src/strategies/uint.rs | 16 +++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/crates/evm/fuzz/src/strategies/int.rs b/crates/evm/fuzz/src/strategies/int.rs index e92c2d464..f772d97c0 100644 --- a/crates/evm/fuzz/src/strategies/int.rs +++ b/crates/evm/fuzz/src/strategies/int.rs @@ -67,7 +67,11 @@ impl ValueTree for IntValueTree { return false } - self.lo = self.curr + if self.hi.is_negative() { I256::MINUS_ONE } else { I256::ONE }; + self.lo = if self.curr != I256::MIN && self.curr != I256::MAX { + self.curr + if self.hi.is_negative() { I256::MINUS_ONE } else { I256::ONE } + } else { + self.curr + }; self.reposition() } @@ -192,3 +196,25 @@ impl Strategy for IntStrategy { } } } + +#[cfg(test)] +mod tests { + use crate::strategies::int::IntValueTree; + use alloy_primitives::I256; + use proptest::strategy::ValueTree; + + #[test] + fn test_int_tree_complicate_should_not_overflow() { + let mut int_tree = IntValueTree::new(I256::MAX, false); + assert_eq!(int_tree.hi, I256::MAX); + assert_eq!(int_tree.curr, I256::MAX); + int_tree.complicate(); + assert_eq!(int_tree.lo, I256::MAX); + + let mut int_tree = IntValueTree::new(I256::MIN, false); + assert_eq!(int_tree.hi, I256::MIN); + assert_eq!(int_tree.curr, I256::MIN); + int_tree.complicate(); + assert_eq!(int_tree.lo, I256::MIN); + } +} diff --git a/crates/evm/fuzz/src/strategies/uint.rs b/crates/evm/fuzz/src/strategies/uint.rs index e1d745526..7b9aac1d4 100644 --- a/crates/evm/fuzz/src/strategies/uint.rs +++ b/crates/evm/fuzz/src/strategies/uint.rs @@ -167,3 +167,19 @@ impl Strategy for UintStrategy { } } } + +#[cfg(test)] +mod tests { + use crate::strategies::uint::UintValueTree; + use alloy_primitives::U256; + use proptest::strategy::ValueTree; + + #[test] + fn test_uint_tree_complicate_max() { + let mut uint_tree = UintValueTree::new(U256::MAX, false); + assert_eq!(uint_tree.hi, U256::MAX); + assert_eq!(uint_tree.curr, U256::MAX); + uint_tree.complicate(); + assert_eq!(uint_tree.lo, U256::MIN); + } +} From 1a4960d0d888200d696ea97b2e38f83db8eaee02 Mon Sep 17 00:00:00 2001 From: Darshan Kathiriya <8559992+lakshya-sky@users.noreply.github.com> Date: Tue, 19 Mar 2024 20:05:59 -0400 Subject: [PATCH 095/430] use correct deserializer for `ots_getBlockDetails` (#7453) use correct deserializer * block_number in ots_getBlockDetails is a list so appropriate deserializer would be `lenient_block_number_seq`. --- crates/anvil/core/src/eth/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/anvil/core/src/eth/mod.rs b/crates/anvil/core/src/eth/mod.rs index 87c02d9b8..203e8532a 100644 --- a/crates/anvil/core/src/eth/mod.rs +++ b/crates/anvil/core/src/eth/mod.rs @@ -676,7 +676,7 @@ pub enum EthRequest { OtsGetBlockDetails( #[cfg_attr( feature = "serde", - serde(deserialize_with = "lenient_block_number::lenient_block_number", default) + serde(deserialize_with = "lenient_block_number::lenient_block_number_seq", default) )] BlockNumber, ), From 319398fe6e61f1a9e8f88356944dbb1f6179726c Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 20 Mar 2024 06:45:07 +0400 Subject: [PATCH 096/430] chore: refactor tests layout (#7410) * [wip] chore: refactor tests layout * fix script tests * more path fixes * more fixes + enable ast * fmt * forge fmt * docs * move to ForgeTestData * fix * fix --- .github/workflows/test.yml | 2 +- crates/forge/tests/it/cheats.rs | 8 +- crates/forge/tests/it/config.rs | 92 +---- crates/forge/tests/it/core.rs | 49 +-- crates/forge/tests/it/fork.rs | 28 +- crates/forge/tests/it/fs.rs | 12 +- crates/forge/tests/it/fuzz.rs | 25 +- crates/forge/tests/it/inline.rs | 34 +- crates/forge/tests/it/invariant.rs | 80 ++-- crates/forge/tests/it/repros.rs | 34 +- crates/forge/tests/it/spec.rs | 7 +- crates/forge/tests/it/test_helpers.rs | 343 +++++++++++++----- crates/test-utils/src/script.rs | 14 +- crates/test-utils/src/util.rs | 2 +- testdata/{ => default}/cheats/Addr.t.sol | 2 +- testdata/{ => default}/cheats/Assert.t.sol | 2 +- testdata/{ => default}/cheats/Assume.t.sol | 2 +- testdata/{ => default}/cheats/Bank.t.sol | 2 +- testdata/{ => default}/cheats/Base64.t.sol | 2 +- testdata/{ => default}/cheats/Broadcast.t.sol | 2 +- testdata/{ => default}/cheats/ChainId.t.sol | 2 +- testdata/{ => default}/cheats/Cool.t.sol | 4 +- testdata/{ => default}/cheats/Deal.t.sol | 2 +- testdata/{ => default}/cheats/Derive.t.sol | 2 +- testdata/{ => default}/cheats/Env.t.sol | 2 +- testdata/{ => default}/cheats/Etch.t.sol | 2 +- .../{ => default}/cheats/ExpectCall.t.sol | 2 +- .../{ => default}/cheats/ExpectEmit.t.sol | 2 +- .../{ => default}/cheats/ExpectRevert.t.sol | 2 +- testdata/{ => default}/cheats/Fee.t.sol | 2 +- testdata/{ => default}/cheats/Ffi.t.sol | 2 +- testdata/{ => default}/cheats/Fork.t.sol | 2 +- testdata/{ => default}/cheats/Fork2.t.sol | 2 +- testdata/{ => default}/cheats/Fs.t.sol | 2 +- .../{ => default}/cheats/GasMetering.t.sol | 2 +- .../cheats/GetBlockTimestamp.t.sol | 2 +- testdata/{ => default}/cheats/GetCode.t.sol | 2 +- .../cheats/GetDeployedCode.t.sol | 2 +- testdata/{ => default}/cheats/GetLabel.t.sol | 2 +- testdata/{ => default}/cheats/GetNonce.t.sol | 2 +- testdata/{ => default}/cheats/Json.t.sol | 2 +- testdata/{ => default}/cheats/Label.t.sol | 2 +- testdata/{ => default}/cheats/Load.t.sol | 2 +- testdata/{ => default}/cheats/Mapping.t.sol | 2 +- testdata/{ => default}/cheats/MemSafety.t.sol | 2 +- testdata/{ => default}/cheats/MockCall.t.sol | 2 +- testdata/{ => default}/cheats/Parse.t.sol | 2 +- testdata/{ => default}/cheats/Prank.t.sol | 2 +- .../{ => default}/cheats/Prevrandao.t.sol | 2 +- .../{ => default}/cheats/ProjectRoot.t.sol | 2 +- .../{ => default}/cheats/ReadCallers.t.sol | 2 +- testdata/{ => default}/cheats/Record.t.sol | 2 +- .../cheats/RecordAccountAccesses.t.sol | 2 +- .../{ => default}/cheats/RecordLogs.t.sol | 2 +- testdata/{ => default}/cheats/Remember.t.sol | 2 +- .../{ => default}/cheats/ResetNonce.t.sol | 2 +- testdata/{ => default}/cheats/Roll.t.sol | 2 +- testdata/{ => default}/cheats/RpcUrls.t.sol | 2 +- testdata/{ => default}/cheats/SetNonce.t.sol | 2 +- .../{ => default}/cheats/SetNonceUnsafe.t.sol | 2 +- testdata/{ => default}/cheats/Setup.t.sol | 2 +- testdata/{ => default}/cheats/Sign.t.sol | 2 +- testdata/{ => default}/cheats/SignP256.t.sol | 2 +- testdata/{ => default}/cheats/Skip.t.sol | 2 +- testdata/{ => default}/cheats/Sleep.t.sol | 2 +- testdata/{ => default}/cheats/Snapshots.t.sol | 2 +- testdata/{ => default}/cheats/Store.t.sol | 2 +- .../{ => default}/cheats/StringUtils.t.sol | 2 +- testdata/{ => default}/cheats/ToString.t.sol | 2 +- testdata/{ => default}/cheats/Toml.t.sol | 2 +- testdata/{ => default}/cheats/Travel.t.sol | 2 +- testdata/{ => default}/cheats/TryFfi.sol | 2 +- testdata/{ => default}/cheats/UnixTime.t.sol | 2 +- testdata/{ => default}/cheats/Wallet.t.sol | 2 +- testdata/{ => default}/cheats/Warp.t.sol | 2 +- testdata/{ => default}/cheats/dumpState.t.sol | 2 +- .../{ => default}/cheats/getBlockNumber.t.sol | 2 +- .../{ => default}/cheats/loadAllocs.t.sol | 2 +- testdata/{ => default}/core/Abstract.t.sol | 0 .../core/ContractEnvironment.t.sol | 0 testdata/{ => default}/core/DSStyle.t.sol | 0 .../{ => default}/core/FailingSetup.t.sol | 0 .../core/FailingTestAfterFailedSetup.t.sol | 0 .../{ => default}/core/MultipleSetup.t.sol | 0 .../{ => default}/core/PaymentFailure.t.sol | 2 +- testdata/{ => default}/core/Reverting.t.sol | 0 .../{ => default}/core/SetupConsistency.t.sol | 0 testdata/{ => default}/fork/DssExecLib.sol | 0 testdata/{ => default}/fork/ForkSame_1.t.sol | 2 +- testdata/{ => default}/fork/ForkSame_2.t.sol | 2 +- testdata/{ => default}/fork/LaunchFork.t.sol | 0 testdata/{ => default}/fork/Transact.t.sol | 2 +- testdata/{ => default}/fs/Default.t.sol | 2 +- testdata/{ => default}/fs/Disabled.t.sol | 2 +- testdata/{ => default}/fuzz/Fuzz.t.sol | 0 .../{ => default}/fuzz/FuzzCollection.t.sol | 0 .../fuzz/FuzzFailurePersist.t.sol | 2 +- testdata/{ => default}/fuzz/FuzzInt.t.sol | 0 .../{ => default}/fuzz/FuzzPositive.t.sol | 0 testdata/{ => default}/fuzz/FuzzUint.t.sol | 0 .../invariant/common/InvariantAssume.t.sol | 4 +- .../common/InvariantCalldataDictionary.t.sol | 2 +- .../common/InvariantHandlerFailure.t.sol | 0 .../common/InvariantInnerContract.t.sol | 0 .../common/InvariantPreserveState.t.sol | 2 +- .../common/InvariantReentrancy.t.sol | 0 .../common/InvariantShrinkWithAssert.t.sol | 2 +- .../invariant/common/InvariantTest1.t.sol | 0 .../storage/InvariantStorageTest.t.sol | 0 .../invariant/target/ExcludeContracts.t.sol | 0 .../invariant/target/ExcludeSenders.t.sol | 0 .../invariant/target/TargetContracts.t.sol | 0 .../invariant/target/TargetInterfaces.t.sol | 0 .../invariant/target/TargetSelectors.t.sol | 0 .../fuzz/invariant/target/TargetSenders.t.sol | 0 .../targetAbi/ExcludeArtifacts.t.sol | 2 +- .../targetAbi/TargetArtifactSelectors.t.sol | 2 +- .../targetAbi/TargetArtifactSelectors2.t.sol | 6 +- .../invariant/targetAbi/TargetArtifacts.t.sol | 2 +- .../{ => default}/inline/FuzzInlineConf.t.sol | 0 .../inline/InvariantInlineConf.t.sol | 0 .../{ => default}/linking/cycle/Cycle.t.sol | 0 .../linking/duplicate/Duplicate.t.sol | 0 .../{ => default}/linking/nested/Nested.t.sol | 0 .../{ => default}/linking/simple/Simple.t.sol | 0 testdata/{ => default}/logs/DebugLogs.t.sol | 0 testdata/{ => default}/logs/HardhatLogs.t.sol | 0 testdata/{ => default}/logs/console.sol | 0 testdata/{ => default}/repros/Issue2623.t.sol | 2 +- testdata/{ => default}/repros/Issue2629.t.sol | 2 +- testdata/{ => default}/repros/Issue2723.t.sol | 2 +- testdata/{ => default}/repros/Issue2898.t.sol | 2 +- testdata/{ => default}/repros/Issue2956.t.sol | 2 +- testdata/{ => default}/repros/Issue2984.t.sol | 2 +- testdata/{ => default}/repros/Issue3055.t.sol | 2 +- testdata/{ => default}/repros/Issue3077.t.sol | 2 +- testdata/{ => default}/repros/Issue3110.t.sol | 2 +- testdata/{ => default}/repros/Issue3119.t.sol | 2 +- testdata/{ => default}/repros/Issue3189.t.sol | 2 +- testdata/{ => default}/repros/Issue3190.t.sol | 2 +- testdata/{ => default}/repros/Issue3192.t.sol | 2 +- testdata/{ => default}/repros/Issue3220.t.sol | 2 +- testdata/{ => default}/repros/Issue3221.t.sol | 2 +- testdata/{ => default}/repros/Issue3223.t.sol | 2 +- testdata/{ => default}/repros/Issue3347.t.sol | 2 +- testdata/{ => default}/repros/Issue3437.t.sol | 2 +- testdata/{ => default}/repros/Issue3596.t.sol | 2 +- testdata/{ => default}/repros/Issue3653.t.sol | 2 +- testdata/{ => default}/repros/Issue3661.t.sol | 0 testdata/{ => default}/repros/Issue3674.t.sol | 2 +- testdata/{ => default}/repros/Issue3685.t.sol | 2 +- testdata/{ => default}/repros/Issue3703.t.sol | 2 +- testdata/{ => default}/repros/Issue3708.t.sol | 2 +- testdata/{ => default}/repros/Issue3723.t.sol | 2 +- testdata/{ => default}/repros/Issue3753.t.sol | 2 +- testdata/{ => default}/repros/Issue3792.t.sol | 2 +- testdata/{ => default}/repros/Issue4402.t.sol | 2 +- testdata/{ => default}/repros/Issue4586.t.sol | 2 +- testdata/{ => default}/repros/Issue4630.t.sol | 2 +- testdata/{ => default}/repros/Issue4640.t.sol | 2 +- testdata/{ => default}/repros/Issue4832.t.sol | 2 +- testdata/{ => default}/repros/Issue5038.t.sol | 2 +- testdata/{ => default}/repros/Issue5529.t.sol | 2 +- testdata/{ => default}/repros/Issue5808.t.sol | 2 +- testdata/{ => default}/repros/Issue5929.t.sol | 2 +- testdata/{ => default}/repros/Issue5935.t.sol | 2 +- testdata/{ => default}/repros/Issue5948.t.sol | 2 +- testdata/{ => default}/repros/Issue6006.t.sol | 2 +- testdata/{ => default}/repros/Issue6032.t.sol | 2 +- testdata/{ => default}/repros/Issue6070.t.sol | 2 +- testdata/{ => default}/repros/Issue6115.t.sol | 0 testdata/{ => default}/repros/Issue6170.t.sol | 2 +- testdata/{ => default}/repros/Issue6180.t.sol | 2 +- testdata/{ => default}/repros/Issue6293.t.sol | 2 +- testdata/{ => default}/repros/Issue6355.t.sol | 2 +- testdata/{ => default}/repros/Issue6437.t.sol | 2 +- testdata/{ => default}/repros/Issue6501.t.sol | 0 testdata/{ => default}/repros/Issue6538.t.sol | 2 +- testdata/{ => default}/repros/Issue6554.t.sol | 6 +- testdata/{ => default}/repros/Issue6616.t.sol | 2 +- testdata/{ => default}/repros/Issue6634.t.sol | 2 +- testdata/{ => default}/repros/Issue6759.t.sol | 2 +- testdata/{ => default}/repros/Issue6966.t.sol | 0 .../deploy.sol/31337/run-latest.json | 0 testdata/{ => default}/script/deploy.sol | 4 +- .../{ => default}/spec/ShanghaiCompat.t.sol | 2 +- .../trace/ConflictingSignatures.t.sol | 2 +- testdata/{ => default}/trace/Trace.t.sol | 2 +- 188 files changed, 547 insertions(+), 465 deletions(-) rename testdata/{ => default}/cheats/Addr.t.sol (95%) rename testdata/{ => default}/cheats/Assert.t.sol (99%) rename testdata/{ => default}/cheats/Assume.t.sol (92%) rename testdata/{ => default}/cheats/Bank.t.sol (95%) rename testdata/{ => default}/cheats/Base64.t.sol (96%) rename testdata/{ => default}/cheats/Broadcast.t.sol (99%) rename testdata/{ => default}/cheats/ChainId.t.sol (93%) rename testdata/{ => default}/cheats/Cool.t.sol (95%) rename testdata/{ => default}/cheats/Deal.t.sol (96%) rename testdata/{ => default}/cheats/Derive.t.sol (96%) rename testdata/{ => default}/cheats/Env.t.sol (99%) rename testdata/{ => default}/cheats/Etch.t.sol (96%) rename testdata/{ => default}/cheats/ExpectCall.t.sol (99%) rename testdata/{ => default}/cheats/ExpectEmit.t.sol (99%) rename testdata/{ => default}/cheats/ExpectRevert.t.sol (99%) rename testdata/{ => default}/cheats/Fee.t.sol (94%) rename testdata/{ => default}/cheats/Ffi.t.sol (97%) rename testdata/{ => default}/cheats/Fork.t.sol (99%) rename testdata/{ => default}/cheats/Fork2.t.sol (99%) rename testdata/{ => default}/cheats/Fs.t.sol (99%) rename testdata/{ => default}/cheats/GasMetering.t.sol (98%) rename testdata/{ => default}/cheats/GetBlockTimestamp.t.sol (96%) rename testdata/{ => default}/cheats/GetCode.t.sol (99%) rename testdata/{ => default}/cheats/GetDeployedCode.t.sol (99%) rename testdata/{ => default}/cheats/GetLabel.t.sol (94%) rename testdata/{ => default}/cheats/GetNonce.t.sol (94%) rename testdata/{ => default}/cheats/Json.t.sol (99%) rename testdata/{ => default}/cheats/Label.t.sol (91%) rename testdata/{ => default}/cheats/Load.t.sol (97%) rename testdata/{ => default}/cheats/Mapping.t.sol (99%) rename testdata/{ => default}/cheats/MemSafety.t.sol (99%) rename testdata/{ => default}/cheats/MockCall.t.sol (99%) rename testdata/{ => default}/cheats/Parse.t.sol (99%) rename testdata/{ => default}/cheats/Prank.t.sol (99%) rename testdata/{ => default}/cheats/Prevrandao.t.sol (97%) rename testdata/{ => default}/cheats/ProjectRoot.t.sol (97%) rename testdata/{ => default}/cheats/ReadCallers.t.sol (99%) rename testdata/{ => default}/cheats/Record.t.sol (98%) rename testdata/{ => default}/cheats/RecordAccountAccesses.t.sol (99%) rename testdata/{ => default}/cheats/RecordLogs.t.sol (99%) rename testdata/{ => default}/cheats/Remember.t.sol (96%) rename testdata/{ => default}/cheats/ResetNonce.t.sol (97%) rename testdata/{ => default}/cheats/Roll.t.sol (97%) rename testdata/{ => default}/cheats/RpcUrls.t.sol (98%) rename testdata/{ => default}/cheats/SetNonce.t.sol (96%) rename testdata/{ => default}/cheats/SetNonceUnsafe.t.sol (97%) rename testdata/{ => default}/cheats/Setup.t.sol (97%) rename testdata/{ => default}/cheats/Sign.t.sol (96%) rename testdata/{ => default}/cheats/SignP256.t.sol (96%) rename testdata/{ => default}/cheats/Skip.t.sol (96%) rename testdata/{ => default}/cheats/Sleep.t.sol (98%) rename testdata/{ => default}/cheats/Snapshots.t.sol (99%) rename testdata/{ => default}/cheats/Store.t.sol (98%) rename testdata/{ => default}/cheats/StringUtils.t.sol (98%) rename testdata/{ => default}/cheats/ToString.t.sol (98%) rename testdata/{ => default}/cheats/Toml.t.sol (99%) rename testdata/{ => default}/cheats/Travel.t.sol (95%) rename testdata/{ => default}/cheats/TryFfi.sol (97%) rename testdata/{ => default}/cheats/UnixTime.t.sol (97%) rename testdata/{ => default}/cheats/Wallet.t.sol (99%) rename testdata/{ => default}/cheats/Warp.t.sol (96%) rename testdata/{ => default}/cheats/dumpState.t.sol (99%) rename testdata/{ => default}/cheats/getBlockNumber.t.sol (97%) rename testdata/{ => default}/cheats/loadAllocs.t.sol (99%) rename testdata/{ => default}/core/Abstract.t.sol (100%) rename testdata/{ => default}/core/ContractEnvironment.t.sol (100%) rename testdata/{ => default}/core/DSStyle.t.sol (100%) rename testdata/{ => default}/core/FailingSetup.t.sol (100%) rename testdata/{ => default}/core/FailingTestAfterFailedSetup.t.sol (100%) rename testdata/{ => default}/core/MultipleSetup.t.sol (100%) rename testdata/{ => default}/core/PaymentFailure.t.sol (93%) rename testdata/{ => default}/core/Reverting.t.sol (100%) rename testdata/{ => default}/core/SetupConsistency.t.sol (100%) rename testdata/{ => default}/fork/DssExecLib.sol (100%) rename testdata/{ => default}/fork/ForkSame_1.t.sol (95%) rename testdata/{ => default}/fork/ForkSame_2.t.sol (95%) rename testdata/{ => default}/fork/LaunchFork.t.sol (100%) rename testdata/{ => default}/fork/Transact.t.sol (99%) rename testdata/{ => default}/fs/Default.t.sol (97%) rename testdata/{ => default}/fs/Disabled.t.sol (97%) rename testdata/{ => default}/fuzz/Fuzz.t.sol (100%) rename testdata/{ => default}/fuzz/FuzzCollection.t.sol (100%) rename testdata/{ => default}/fuzz/FuzzFailurePersist.t.sol (96%) rename testdata/{ => default}/fuzz/FuzzInt.t.sol (100%) rename testdata/{ => default}/fuzz/FuzzPositive.t.sol (100%) rename testdata/{ => default}/fuzz/FuzzUint.t.sol (100%) rename testdata/{ => default}/fuzz/invariant/common/InvariantAssume.t.sol (91%) rename testdata/{ => default}/fuzz/invariant/common/InvariantCalldataDictionary.t.sol (98%) rename testdata/{ => default}/fuzz/invariant/common/InvariantHandlerFailure.t.sol (100%) rename testdata/{ => default}/fuzz/invariant/common/InvariantInnerContract.t.sol (100%) rename testdata/{ => default}/fuzz/invariant/common/InvariantPreserveState.t.sol (97%) rename testdata/{ => default}/fuzz/invariant/common/InvariantReentrancy.t.sol (100%) rename testdata/{ => default}/fuzz/invariant/common/InvariantShrinkWithAssert.t.sol (98%) rename testdata/{ => default}/fuzz/invariant/common/InvariantTest1.t.sol (100%) rename testdata/{ => default}/fuzz/invariant/storage/InvariantStorageTest.t.sol (100%) rename testdata/{ => default}/fuzz/invariant/target/ExcludeContracts.t.sol (100%) rename testdata/{ => default}/fuzz/invariant/target/ExcludeSenders.t.sol (100%) rename testdata/{ => default}/fuzz/invariant/target/TargetContracts.t.sol (100%) rename testdata/{ => default}/fuzz/invariant/target/TargetInterfaces.t.sol (100%) rename testdata/{ => default}/fuzz/invariant/target/TargetSelectors.t.sol (100%) rename testdata/{ => default}/fuzz/invariant/target/TargetSenders.t.sol (100%) rename testdata/{ => default}/fuzz/invariant/targetAbi/ExcludeArtifacts.t.sol (91%) rename testdata/{ => default}/fuzz/invariant/targetAbi/TargetArtifactSelectors.t.sol (87%) rename testdata/{ => default}/fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol (84%) rename testdata/{ => default}/fuzz/invariant/targetAbi/TargetArtifacts.t.sol (91%) rename testdata/{ => default}/inline/FuzzInlineConf.t.sol (100%) rename testdata/{ => default}/inline/InvariantInlineConf.t.sol (100%) rename testdata/{ => default}/linking/cycle/Cycle.t.sol (100%) rename testdata/{ => default}/linking/duplicate/Duplicate.t.sol (100%) rename testdata/{ => default}/linking/nested/Nested.t.sol (100%) rename testdata/{ => default}/linking/simple/Simple.t.sol (100%) rename testdata/{ => default}/logs/DebugLogs.t.sol (100%) rename testdata/{ => default}/logs/HardhatLogs.t.sol (100%) rename testdata/{ => default}/logs/console.sol (100%) rename testdata/{ => default}/repros/Issue2623.t.sol (95%) rename testdata/{ => default}/repros/Issue2629.t.sol (96%) rename testdata/{ => default}/repros/Issue2723.t.sol (95%) rename testdata/{ => default}/repros/Issue2898.t.sol (96%) rename testdata/{ => default}/repros/Issue2956.t.sol (97%) rename testdata/{ => default}/repros/Issue2984.t.sol (96%) rename testdata/{ => default}/repros/Issue3055.t.sol (97%) rename testdata/{ => default}/repros/Issue3077.t.sol (97%) rename testdata/{ => default}/repros/Issue3110.t.sol (97%) rename testdata/{ => default}/repros/Issue3119.t.sol (96%) rename testdata/{ => default}/repros/Issue3189.t.sol (96%) rename testdata/{ => default}/repros/Issue3190.t.sol (94%) rename testdata/{ => default}/repros/Issue3192.t.sol (95%) rename testdata/{ => default}/repros/Issue3220.t.sol (97%) rename testdata/{ => default}/repros/Issue3221.t.sol (97%) rename testdata/{ => default}/repros/Issue3223.t.sol (97%) rename testdata/{ => default}/repros/Issue3347.t.sol (91%) rename testdata/{ => default}/repros/Issue3437.t.sol (93%) rename testdata/{ => default}/repros/Issue3596.t.sol (96%) rename testdata/{ => default}/repros/Issue3653.t.sol (95%) rename testdata/{ => default}/repros/Issue3661.t.sol (100%) rename testdata/{ => default}/repros/Issue3674.t.sol (94%) rename testdata/{ => default}/repros/Issue3685.t.sol (97%) rename testdata/{ => default}/repros/Issue3703.t.sol (98%) rename testdata/{ => default}/repros/Issue3708.t.sol (97%) rename testdata/{ => default}/repros/Issue3723.t.sol (93%) rename testdata/{ => default}/repros/Issue3753.t.sol (94%) rename testdata/{ => default}/repros/Issue3792.t.sol (96%) rename testdata/{ => default}/repros/Issue4402.t.sol (99%) rename testdata/{ => default}/repros/Issue4586.t.sol (97%) rename testdata/{ => default}/repros/Issue4630.t.sol (96%) rename testdata/{ => default}/repros/Issue4640.t.sol (94%) rename testdata/{ => default}/repros/Issue4832.t.sol (92%) rename testdata/{ => default}/repros/Issue5038.t.sol (99%) rename testdata/{ => default}/repros/Issue5529.t.sol (97%) rename testdata/{ => default}/repros/Issue5808.t.sol (95%) rename testdata/{ => default}/repros/Issue5929.t.sol (95%) rename testdata/{ => default}/repros/Issue5935.t.sol (97%) rename testdata/{ => default}/repros/Issue5948.t.sol (97%) rename testdata/{ => default}/repros/Issue6006.t.sol (97%) rename testdata/{ => default}/repros/Issue6032.t.sol (98%) rename testdata/{ => default}/repros/Issue6070.t.sol (95%) rename testdata/{ => default}/repros/Issue6115.t.sol (100%) rename testdata/{ => default}/repros/Issue6170.t.sol (95%) rename testdata/{ => default}/repros/Issue6180.t.sol (95%) rename testdata/{ => default}/repros/Issue6293.t.sol (93%) rename testdata/{ => default}/repros/Issue6355.t.sol (96%) rename testdata/{ => default}/repros/Issue6437.t.sol (97%) rename testdata/{ => default}/repros/Issue6501.t.sol (100%) rename testdata/{ => default}/repros/Issue6538.t.sol (95%) rename testdata/{ => default}/repros/Issue6554.t.sol (61%) rename testdata/{ => default}/repros/Issue6616.t.sol (96%) rename testdata/{ => default}/repros/Issue6634.t.sol (99%) rename testdata/{ => default}/repros/Issue6759.t.sol (95%) rename testdata/{ => default}/repros/Issue6966.t.sol (100%) rename testdata/{ => default}/script/broadcast/deploy.sol/31337/run-latest.json (100%) rename testdata/{ => default}/script/deploy.sol (89%) rename testdata/{ => default}/spec/ShanghaiCompat.t.sol (96%) rename testdata/{ => default}/trace/ConflictingSignatures.t.sol (97%) rename testdata/{ => default}/trace/Trace.t.sol (98%) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ffef82ca5..291417506 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -166,7 +166,7 @@ jobs: # so running `forge fmt` with `--root testdata` won't actually check anything run: | shopt -s extglob - cargo run --bin forge -- fmt --check testdata/**/!(Vm).sol + cargo run --bin forge -- fmt --check $(find testdata -name '*.sol' ! -name Vm.sol) crate-checks: runs-on: ubuntu-latest diff --git a/crates/forge/tests/it/cheats.rs b/crates/forge/tests/it/cheats.rs index 8fa7ae31e..fd6e9866e 100644 --- a/crates/forge/tests/it/cheats.rs +++ b/crates/forge/tests/it/cheats.rs @@ -2,9 +2,9 @@ use crate::{ config::*, - test_helpers::{PROJECT, RE_PATH_SEPARATOR}, + test_helpers::{RE_PATH_SEPARATOR, TEST_DATA_DEFAULT}, }; -use foundry_config::{fs_permissions::PathPermission, Config, FsPermissions}; +use foundry_config::{fs_permissions::PathPermission, FsPermissions}; use foundry_test_utils::Filter; /// Executes all cheat code tests but not fork cheat codes @@ -18,9 +18,9 @@ async fn test_cheats_local() { filter = filter.exclude_tests("(Ffi|File|Line|Root)"); } - let mut config = Config::with_root(PROJECT.root()); + let mut config = TEST_DATA_DEFAULT.config.clone(); config.fs_permissions = FsPermissions::new(vec![PathPermission::read_write("./")]); - let runner = runner_with_config(config); + let runner = TEST_DATA_DEFAULT.runner_with_config(config); TestConfig::with_filter(runner, filter).run().await; } diff --git a/crates/forge/tests/it/config.rs b/crates/forge/tests/it/config.rs index bc56b5c1c..1b2a1398d 100644 --- a/crates/forge/tests/it/config.rs +++ b/crates/forge/tests/it/config.rs @@ -1,23 +1,18 @@ //! Test config. -use crate::test_helpers::{COMPILED, EVM_OPTS, PROJECT, TEST_OPTS}; use forge::{ result::{SuiteResult, TestStatus}, - MultiContractRunner, MultiContractRunnerBuilder, -}; -use foundry_config::{ - fs_permissions::PathPermission, Config, FsPermissions, RpcEndpoint, RpcEndpoints, + MultiContractRunner, }; use foundry_evm::{ decode::decode_console_logs, - inspectors::CheatsConfig, revm::primitives::SpecId, traces::{render_trace_arena, CallTraceDecoderBuilder}, }; use foundry_test_utils::{init_tracing, Filter}; use futures::future::join_all; use itertools::Itertools; -use std::{collections::BTreeMap, path::Path}; +use std::collections::BTreeMap; /// How to execute a test run. pub struct TestConfig { @@ -31,10 +26,6 @@ impl TestConfig { Self::with_filter(runner, Filter::matches_all()) } - pub fn filter(filter: Filter) -> Self { - Self::with_filter(runner(), filter) - } - pub fn with_filter(runner: MultiContractRunner, filter: Filter) -> Self { init_tracing(); Self { runner, should_fail: false, filter } @@ -108,85 +99,6 @@ impl TestConfig { } } -pub fn manifest_root() -> &'static Path { - let mut root = Path::new(env!("CARGO_MANIFEST_DIR")); - // need to check here where we're executing the test from, if in `forge` we need to also allow - // `testdata` - if root.ends_with("forge") { - root = root.parent().unwrap(); - } - root -} - -/// Builds a base runner -pub fn base_runner() -> MultiContractRunnerBuilder { - init_tracing(); - MultiContractRunnerBuilder::default() - .sender(EVM_OPTS.sender) - .with_test_options(TEST_OPTS.clone()) -} - -/// Builds a non-tracing runner -pub fn runner() -> MultiContractRunner { - let mut config = Config::with_root(PROJECT.root()); - config.fs_permissions = FsPermissions::new(vec![PathPermission::read_write(manifest_root())]); - runner_with_config(config) -} - -/// Builds a non-tracing runner -pub fn runner_with_config(mut config: Config) -> MultiContractRunner { - config.rpc_endpoints = rpc_endpoints(); - config.allow_paths.push(manifest_root().to_path_buf()); - - let root = &PROJECT.paths.root; - let opts = &*EVM_OPTS; - let env = opts.local_evm_env(); - let output = COMPILED.clone(); - base_runner() - .with_cheats_config(CheatsConfig::new(&config, opts.clone(), None)) - .sender(config.sender) - .build(root, output, env, opts.clone()) - .unwrap() -} - -/// Builds a tracing runner -pub fn tracing_runner() -> MultiContractRunner { - let mut opts = EVM_OPTS.clone(); - opts.verbosity = 5; - base_runner() - .build(&PROJECT.paths.root, (*COMPILED).clone(), EVM_OPTS.local_evm_env(), opts) - .unwrap() -} - -// Builds a runner that runs against forked state -pub async fn forked_runner(rpc: &str) -> MultiContractRunner { - let mut opts = EVM_OPTS.clone(); - - opts.env.chain_id = None; // clear chain id so the correct one gets fetched from the RPC - opts.fork_url = Some(rpc.to_string()); - - let env = opts.evm_env().await.expect("Could not instantiate fork environment"); - let fork = opts.get_fork(&Default::default(), env.clone()); - - base_runner() - .with_fork(fork) - .build(&PROJECT.paths.root, (*COMPILED).clone(), env, opts) - .unwrap() -} - -/// the RPC endpoints used during tests -pub fn rpc_endpoints() -> RpcEndpoints { - RpcEndpoints::new([ - ( - "rpcAlias", - RpcEndpoint::Url( - "https://eth-mainnet.alchemyapi.io/v2/Lc7oIGYeL_QvInzI0Wiu_pOZZDEKBrdf".to_string(), - ), - ), - ("rpcEnvAlias", RpcEndpoint::Env("${RPC_ENV_ALIAS}".to_string())), - ]) -} - /// A helper to assert the outcome of multiple tests with helpful assert messages #[track_caller] #[allow(clippy::type_complexity)] diff --git a/crates/forge/tests/it/core.rs b/crates/forge/tests/it/core.rs index 96a0aedce..34cdc0d17 100644 --- a/crates/forge/tests/it/core.rs +++ b/crates/forge/tests/it/core.rs @@ -1,6 +1,6 @@ //! Forge tests for core functionality. -use crate::config::*; +use crate::{config::*, test_helpers::TEST_DATA_DEFAULT}; use forge::result::SuiteResult; use foundry_evm::traces::TraceKind; use foundry_test_utils::Filter; @@ -9,14 +9,14 @@ use std::{collections::BTreeMap, env}; #[tokio::test(flavor = "multi_thread")] async fn test_core() { let filter = Filter::new(".*", ".*", ".*core"); - let mut runner = runner(); + let mut runner = TEST_DATA_DEFAULT.runner(); let results = runner.test_collect(&filter); assert_multiple( &results, BTreeMap::from([ ( - "core/FailingSetup.t.sol:FailingSetupTest", + "default/core/FailingSetup.t.sol:FailingSetupTest", vec![( "setUp()", false, @@ -26,7 +26,7 @@ async fn test_core() { )], ), ( - "core/MultipleSetup.t.sol:MultipleSetup", + "default/core/MultipleSetup.t.sol:MultipleSetup", vec![( "setUp()", false, @@ -36,34 +36,37 @@ async fn test_core() { )], ), ( - "core/Reverting.t.sol:RevertingTest", + "default/core/Reverting.t.sol:RevertingTest", vec![("testFailRevert()", true, None, None, None)], ), ( - "core/SetupConsistency.t.sol:SetupConsistencyCheck", + "default/core/SetupConsistency.t.sol:SetupConsistencyCheck", vec![ ("testAdd()", true, None, None, None), ("testMultiply()", true, None, None, None), ], ), ( - "core/DSStyle.t.sol:DSStyleTest", + "default/core/DSStyle.t.sol:DSStyleTest", vec![("testFailingAssertions()", true, None, None, None)], ), ( - "core/ContractEnvironment.t.sol:ContractEnvironmentTest", + "default/core/ContractEnvironment.t.sol:ContractEnvironmentTest", vec![ ("testAddresses()", true, None, None, None), ("testEnvironment()", true, None, None, None), ], ), ( - "core/PaymentFailure.t.sol:PaymentFailureTest", + "default/core/PaymentFailure.t.sol:PaymentFailureTest", vec![("testCantPay()", false, Some("EvmError: Revert".to_string()), None, None)], ), - ("core/Abstract.t.sol:AbstractTest", vec![("testSomething()", true, None, None, None)]), ( - "core/FailingTestAfterFailedSetup.t.sol:FailingTestAfterFailedSetupTest", + "default/core/Abstract.t.sol:AbstractTest", + vec![("testSomething()", true, None, None, None)], + ), + ( + "default/core/FailingTestAfterFailedSetup.t.sol:FailingTestAfterFailedSetupTest", vec![( "setUp()", false, @@ -79,25 +82,25 @@ async fn test_core() { #[tokio::test(flavor = "multi_thread")] async fn test_linking() { let filter = Filter::new(".*", ".*", ".*linking"); - let mut runner = runner(); + let mut runner = TEST_DATA_DEFAULT.runner(); let results = runner.test_collect(&filter); assert_multiple( &results, BTreeMap::from([ ( - "linking/simple/Simple.t.sol:SimpleLibraryLinkingTest", + "default/linking/simple/Simple.t.sol:SimpleLibraryLinkingTest", vec![("testCall()", true, None, None, None)], ), ( - "linking/nested/Nested.t.sol:NestedLibraryLinkingTest", + "default/linking/nested/Nested.t.sol:NestedLibraryLinkingTest", vec![ ("testDirect()", true, None, None, None), ("testNested()", true, None, None, None), ], ), ( - "linking/duplicate/Duplicate.t.sol:DuplicateLibraryLinkingTest", + "default/linking/duplicate/Duplicate.t.sol:DuplicateLibraryLinkingTest", vec![ ("testA()", true, None, None, None), ("testB()", true, None, None, None), @@ -113,14 +116,14 @@ async fn test_linking() { #[tokio::test(flavor = "multi_thread")] async fn test_logs() { let filter = Filter::new(".*", ".*", ".*logs"); - let mut runner = runner(); + let mut runner = TEST_DATA_DEFAULT.runner(); let results = runner.test_collect(&filter); assert_multiple( &results, BTreeMap::from([ ( - "logs/DebugLogs.t.sol:DebugLogsTest", + "default/logs/DebugLogs.t.sol:DebugLogsTest", vec![ ( "test1()", @@ -289,7 +292,7 @@ async fn test_logs() { ], ), ( - "logs/HardhatLogs.t.sol:HardhatLogsTest", + "default/logs/HardhatLogs.t.sol:HardhatLogsTest", vec![ ( "testInts()", @@ -678,7 +681,7 @@ async fn test_env_vars() { env::remove_var(env_var_key); let filter = Filter::new("testSetEnv", ".*", ".*"); - let mut runner = runner(); + let mut runner = TEST_DATA_DEFAULT.runner(); let _ = runner.test_collect(&filter); assert_eq!(env::var(env_var_key).unwrap(), env_var_val); @@ -687,16 +690,16 @@ async fn test_env_vars() { #[tokio::test(flavor = "multi_thread")] async fn test_doesnt_run_abstract_contract() { let filter = Filter::new(".*", ".*", ".*Abstract.t.sol".to_string().as_str()); - let mut runner = runner(); + let mut runner = TEST_DATA_DEFAULT.runner(); let results = runner.test_collect(&filter); - assert!(!results.contains_key("core/Abstract.t.sol:AbstractTestBase")); - assert!(results.contains_key("core/Abstract.t.sol:AbstractTest")); + assert!(!results.contains_key("default/core/Abstract.t.sol:AbstractTestBase")); + assert!(results.contains_key("default/core/Abstract.t.sol:AbstractTest")); } #[tokio::test(flavor = "multi_thread")] async fn test_trace() { let filter = Filter::new(".*", ".*", ".*trace"); - let mut runner = tracing_runner(); + let mut runner = TEST_DATA_DEFAULT.tracing_runner(); let suite_result = runner.test_collect(&filter); // TODO: This trace test is very basic - it is probably a good candidate for snapshot diff --git a/crates/forge/tests/it/fork.rs b/crates/forge/tests/it/fork.rs index da103801c..c2751fe91 100644 --- a/crates/forge/tests/it/fork.rs +++ b/crates/forge/tests/it/fork.rs @@ -2,10 +2,10 @@ use crate::{ config::*, - test_helpers::{PROJECT, RE_PATH_SEPARATOR}, + test_helpers::{RE_PATH_SEPARATOR, TEST_DATA_DEFAULT}, }; use forge::result::SuiteResult; -use foundry_config::{fs_permissions::PathPermission, Config, FsPermissions}; +use foundry_config::{fs_permissions::PathPermission, FsPermissions}; use foundry_test_utils::Filter; /// Executes reverting fork test @@ -16,7 +16,7 @@ async fn test_cheats_fork_revert() { ".*", &format!(".*cheats{RE_PATH_SEPARATOR}Fork"), ); - let mut runner = runner(); + let mut runner = TEST_DATA_DEFAULT.runner(); let suite_result = runner.test_collect(&filter); assert_eq!(suite_result.len(), 1); @@ -33,9 +33,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 = Config::with_root(PROJECT.root()); + let mut config = TEST_DATA_DEFAULT.config.clone(); config.fs_permissions = FsPermissions::new(vec![PathPermission::read("./fixtures")]); - let runner = runner_with_config(config); + let runner = TEST_DATA_DEFAULT.runner_with_config(config); let filter = Filter::new(".*", ".*", &format!(".*cheats{RE_PATH_SEPARATOR}Fork")) .exclude_tests(".*Revert"); TestConfig::with_filter(runner, filter).run().await; @@ -44,9 +44,9 @@ async fn test_cheats_fork() { /// Executes eth_getLogs cheatcode #[tokio::test(flavor = "multi_thread")] async fn test_get_logs_fork() { - let mut config = Config::with_root(PROJECT.root()); + let mut config = TEST_DATA_DEFAULT.config.clone(); config.fs_permissions = FsPermissions::new(vec![PathPermission::read("./fixtures")]); - let runner = runner_with_config(config); + let runner = TEST_DATA_DEFAULT.runner_with_config(config); let filter = Filter::new("testEthGetLogs", ".*", &format!(".*cheats{RE_PATH_SEPARATOR}Fork")) .exclude_tests(".*Revert"); TestConfig::with_filter(runner, filter).run().await; @@ -55,9 +55,9 @@ async fn test_get_logs_fork() { /// Executes rpc cheatcode #[tokio::test(flavor = "multi_thread")] async fn test_rpc_fork() { - let mut config = Config::with_root(PROJECT.root()); + let mut config = TEST_DATA_DEFAULT.config.clone(); config.fs_permissions = FsPermissions::new(vec![PathPermission::read("./fixtures")]); - let runner = runner_with_config(config); + let runner = TEST_DATA_DEFAULT.runner_with_config(config); let filter = Filter::new("testRpc", ".*", &format!(".*cheats{RE_PATH_SEPARATOR}Fork")) .exclude_tests(".*Revert"); TestConfig::with_filter(runner, filter).run().await; @@ -67,7 +67,7 @@ async fn test_rpc_fork() { #[tokio::test(flavor = "multi_thread")] async fn test_launch_fork() { let rpc_url = foundry_common::rpc::next_http_archive_rpc_endpoint(); - let runner = forked_runner(&rpc_url).await; + let runner = TEST_DATA_DEFAULT.forked_runner(&rpc_url).await; let filter = Filter::new(".*", ".*", &format!(".*fork{RE_PATH_SEPARATOR}Launch")); TestConfig::with_filter(runner, filter).run().await; } @@ -76,7 +76,7 @@ async fn test_launch_fork() { #[tokio::test(flavor = "multi_thread")] async fn test_launch_fork_ws() { let rpc_url = foundry_common::rpc::next_ws_archive_rpc_endpoint(); - let runner = forked_runner(&rpc_url).await; + let runner = TEST_DATA_DEFAULT.forked_runner(&rpc_url).await; let filter = Filter::new(".*", ".*", &format!(".*fork{RE_PATH_SEPARATOR}Launch")); TestConfig::with_filter(runner, filter).run().await; } @@ -84,13 +84,15 @@ 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 filter = Filter::new(".*", ".*", &format!(".*fork{RE_PATH_SEPARATOR}Transact")); - TestConfig::filter(filter).run().await; + TestConfig::with_filter(runner, filter).run().await; } /// Tests that we can create the same fork (provider,block) concurretnly in different tests #[tokio::test(flavor = "multi_thread")] async fn test_create_same_fork() { + let runner = TEST_DATA_DEFAULT.runner(); let filter = Filter::new(".*", ".*", &format!(".*fork{RE_PATH_SEPARATOR}ForkSame")); - TestConfig::filter(filter).run().await; + TestConfig::with_filter(runner, filter).run().await; } diff --git a/crates/forge/tests/it/fs.rs b/crates/forge/tests/it/fs.rs index 72ae2cb32..5bb0b59fb 100644 --- a/crates/forge/tests/it/fs.rs +++ b/crates/forge/tests/it/fs.rs @@ -1,23 +1,23 @@ //! Filesystem tests. -use crate::{config::*, test_helpers::PROJECT}; -use foundry_config::{fs_permissions::PathPermission, Config, FsPermissions}; +use crate::{config::*, test_helpers::TEST_DATA_DEFAULT}; +use foundry_config::{fs_permissions::PathPermission, FsPermissions}; use foundry_test_utils::Filter; #[tokio::test(flavor = "multi_thread")] async fn test_fs_disabled() { - let mut config = Config::with_root(PROJECT.root()); + let mut config = TEST_DATA_DEFAULT.config.clone(); config.fs_permissions = FsPermissions::new(vec![PathPermission::none("./")]); - let runner = runner_with_config(config); + let runner = TEST_DATA_DEFAULT.runner_with_config(config); 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 = Config::with_root(PROJECT.root()); + let mut config = TEST_DATA_DEFAULT.config.clone(); config.fs_permissions = FsPermissions::new(vec![PathPermission::read("./fixtures")]); - let runner = runner_with_config(config); + let runner = TEST_DATA_DEFAULT.runner_with_config(config); 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 50ce05d4a..c6369e896 100644 --- a/crates/forge/tests/it/fuzz.rs +++ b/crates/forge/tests/it/fuzz.rs @@ -1,21 +1,20 @@ //! Fuzz tests. -use std::collections::BTreeMap; - +use crate::{config::*, test_helpers::TEST_DATA_DEFAULT}; use alloy_primitives::{Bytes, U256}; -use forge::fuzz::CounterExample; - -use forge::result::{SuiteResult, TestStatus}; +use forge::{ + fuzz::CounterExample, + result::{SuiteResult, TestStatus}, +}; use foundry_test_utils::Filter; - -use crate::config::*; +use std::collections::BTreeMap; #[tokio::test(flavor = "multi_thread")] async fn test_fuzz() { let filter = Filter::new(".*", ".*", ".*fuzz/") .exclude_tests(r"invariantCounter|testIncrement\(address\)|testNeedle\(uint256\)|testSuccessChecker\(uint256\)|testSuccessChecker2\(int256\)|testSuccessChecker3\(uint32\)") .exclude_paths("invariant"); - let mut runner = runner(); + let mut runner = TEST_DATA_DEFAULT.runner(); let suite_result = runner.test_collect(&filter); assert!(!suite_result.is_empty()); @@ -52,7 +51,7 @@ async fn test_successful_fuzz_cases() { let filter = Filter::new(".*", ".*", ".*fuzz/FuzzPositive") .exclude_tests(r"invariantCounter|testIncrement\(address\)|testNeedle\(uint256\)") .exclude_paths("invariant"); - let mut runner = runner(); + let mut runner = TEST_DATA_DEFAULT.runner(); let suite_result = runner.test_collect(&filter); assert!(!suite_result.is_empty()); @@ -82,7 +81,7 @@ async fn test_successful_fuzz_cases() { #[ignore] async fn test_fuzz_collection() { let filter = Filter::new(".*", ".*", ".*fuzz/FuzzCollection.t.sol"); - let mut runner = runner(); + 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; @@ -92,7 +91,7 @@ async fn test_fuzz_collection() { assert_multiple( &results, BTreeMap::from([( - "fuzz/FuzzCollection.t.sol:SampleContractTest", + "default/fuzz/FuzzCollection.t.sol:SampleContractTest", vec![ ("invariantCounter", false, Some("broken counter.".into()), None, None), ( @@ -111,14 +110,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 = runner(); + let mut runner = TEST_DATA_DEFAULT.runner(); runner.test_options.fuzz.runs = 1000; macro_rules! get_failure_result { () => { runner .test_collect(&filter) - .get("fuzz/FuzzFailurePersist.t.sol:FuzzFailurePersistTest") + .get("default/fuzz/FuzzFailurePersist.t.sol:FuzzFailurePersistTest") .unwrap() .test_results .get("test_persist_fuzzed_failure(uint256,int256,address,bool,string,(address,uint256),address[])") diff --git a/crates/forge/tests/it/inline.rs b/crates/forge/tests/it/inline.rs index 0a2f4d593..09d4fb323 100644 --- a/crates/forge/tests/it/inline.rs +++ b/crates/forge/tests/it/inline.rs @@ -1,19 +1,16 @@ //! Inline configuration tests. -use crate::{ - config::runner, - test_helpers::{COMPILED, PROJECT}, -}; -use forge::{result::TestKind, TestOptions, TestOptionsBuilder}; +use crate::test_helpers::TEST_DATA_DEFAULT; +use forge::{result::TestKind, TestOptionsBuilder}; use foundry_config::{FuzzConfig, InvariantConfig}; use foundry_test_utils::Filter; #[tokio::test(flavor = "multi_thread")] async fn inline_config_run_fuzz() { let filter = Filter::new(".*", ".*", ".*inline/FuzzInlineConf.t.sol"); - let mut runner = runner(); + let mut runner = TEST_DATA_DEFAULT.runner(); let result = runner.test_collect(&filter); - let suite_result = result.get("inline/FuzzInlineConf.t.sol:FuzzInlineConf").unwrap(); + let suite_result = result.get("default/inline/FuzzInlineConf.t.sol:FuzzInlineConf").unwrap(); let test_result = suite_result.test_results.get("testInlineConfFuzz(uint8)").unwrap(); match test_result.kind { TestKind::Fuzz { runs, .. } => assert_eq!(runs, 1024), @@ -23,11 +20,10 @@ async fn inline_config_run_fuzz() { #[tokio::test(flavor = "multi_thread")] async fn inline_config_run_invariant() { - const ROOT: &str = "inline/InvariantInlineConf.t.sol"; + const ROOT: &str = "default/inline/InvariantInlineConf.t.sol"; let filter = Filter::new(".*", ".*", ".*inline/InvariantInlineConf.t.sol"); - let mut runner = runner(); - runner.test_options = default_test_options(); + let mut runner = TEST_DATA_DEFAULT.runner(); let result = runner.test_collect(&filter); let suite_result_1 = result.get(&format!("{ROOT}:InvariantInlineConf")).expect("Result exists"); @@ -49,38 +45,28 @@ async fn inline_config_run_invariant() { #[test] fn build_test_options() { - let root = &PROJECT.paths.root; + 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(&COMPILED, root); + .build(&TEST_DATA_DEFAULT.output, root); assert!(build_result.is_ok()); } #[test] fn build_test_options_just_one_valid_profile() { - let root = &PROJECT.paths.root; + 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(&COMPILED, root); + .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()); } - -/// Returns the [TestOptions] for the testing [PROJECT]. -pub fn default_test_options() -> TestOptions { - let root = &PROJECT.paths.root; - TestOptionsBuilder::default() - .fuzz(FuzzConfig::default()) - .invariant(InvariantConfig::default()) - .build(&COMPILED, root) - .expect("Config loaded") -} diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index 9339a54c3..e902b6aa9 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -1,6 +1,6 @@ //! Invariant tests. -use crate::{config::*, test_helpers::TEST_OPTS}; +use crate::{config::*, test_helpers::TEST_DATA_DEFAULT}; use alloy_primitives::U256; use forge::{fuzz::CounterExample, result::TestStatus, TestOptions}; use foundry_test_utils::Filter; @@ -9,18 +9,18 @@ use std::collections::BTreeMap; #[tokio::test(flavor = "multi_thread")] async fn test_invariant() { let filter = Filter::new(".*", ".*", ".*fuzz/invariant/(target|targetAbi|common)"); - let mut runner = runner(); + let mut runner = TEST_DATA_DEFAULT.runner(); let results = runner.test_collect(&filter); assert_multiple( &results, BTreeMap::from([ ( - "fuzz/invariant/common/InvariantHandlerFailure.t.sol:InvariantHandlerFailure", + "default/fuzz/invariant/common/InvariantHandlerFailure.t.sol:InvariantHandlerFailure", vec![("statefulFuzz_BrokenInvariant()", true, None, None, None)], ), ( - "fuzz/invariant/common/InvariantInnerContract.t.sol:InvariantInnerContract", + "default/fuzz/invariant/common/InvariantInnerContract.t.sol:InvariantInnerContract", vec![( "invariantHideJesus()", false, @@ -30,11 +30,11 @@ async fn test_invariant() { )], ), ( - "fuzz/invariant/common/InvariantReentrancy.t.sol:InvariantReentrancy", + "default/fuzz/invariant/common/InvariantReentrancy.t.sol:InvariantReentrancy", vec![("invariantNotStolen()", true, None, None, None)], ), ( - "fuzz/invariant/common/InvariantTest1.t.sol:InvariantTest", + "default/fuzz/invariant/common/InvariantTest1.t.sol:InvariantTest", vec![ ("invariant_neverFalse()", false, Some("revert: false".into()), None, None), ( @@ -47,15 +47,15 @@ async fn test_invariant() { ], ), ( - "fuzz/invariant/target/ExcludeContracts.t.sol:ExcludeContracts", + "default/fuzz/invariant/target/ExcludeContracts.t.sol:ExcludeContracts", vec![("invariantTrueWorld()", true, None, None, None)], ), ( - "fuzz/invariant/target/TargetContracts.t.sol:TargetContracts", + "default/fuzz/invariant/target/TargetContracts.t.sol:TargetContracts", vec![("invariantTrueWorld()", true, None, None, None)], ), ( - "fuzz/invariant/target/TargetSenders.t.sol:TargetSenders", + "default/fuzz/invariant/target/TargetSenders.t.sol:TargetSenders", vec![( "invariantTrueWorld()", false, @@ -65,7 +65,7 @@ async fn test_invariant() { )], ), ( - "fuzz/invariant/target/TargetInterfaces.t.sol:TargetWorldInterfaces", + "default/fuzz/invariant/target/TargetInterfaces.t.sol:TargetWorldInterfaces", vec![( "invariantTrueWorld()", false, @@ -75,19 +75,19 @@ async fn test_invariant() { )], ), ( - "fuzz/invariant/target/ExcludeSenders.t.sol:ExcludeSenders", + "default/fuzz/invariant/target/ExcludeSenders.t.sol:ExcludeSenders", vec![("invariantTrueWorld()", true, None, None, None)], ), ( - "fuzz/invariant/target/TargetSelectors.t.sol:TargetSelectors", + "default/fuzz/invariant/target/TargetSelectors.t.sol:TargetSelectors", vec![("invariantTrueWorld()", true, None, None, None)], ), ( - "fuzz/invariant/targetAbi/ExcludeArtifacts.t.sol:ExcludeArtifacts", + "default/fuzz/invariant/targetAbi/ExcludeArtifacts.t.sol:ExcludeArtifacts", vec![("invariantShouldPass()", true, None, None, None)], ), ( - "fuzz/invariant/targetAbi/TargetArtifacts.t.sol:TargetArtifacts", + "default/fuzz/invariant/targetAbi/TargetArtifacts.t.sol:TargetArtifacts", vec![ ("invariantShouldPass()", true, None, None, None), ( @@ -100,11 +100,11 @@ async fn test_invariant() { ], ), ( - "fuzz/invariant/targetAbi/TargetArtifactSelectors.t.sol:TargetArtifactSelectors", + "default/fuzz/invariant/targetAbi/TargetArtifactSelectors.t.sol:TargetArtifactSelectors", vec![("invariantShouldPass()", true, None, None, None)], ), ( - "fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol:TargetArtifactSelectors2", + "default/fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol:TargetArtifactSelectors2", vec![( "invariantShouldFail()", false, @@ -114,7 +114,7 @@ async fn test_invariant() { )], ), ( - "fuzz/invariant/common/InvariantShrinkWithAssert.t.sol:InvariantShrinkWithAssert", + "default/fuzz/invariant/common/InvariantShrinkWithAssert.t.sol:InvariantShrinkWithAssert", vec![( "invariant_with_assert()", false, @@ -124,7 +124,7 @@ async fn test_invariant() { )], ), ( - "fuzz/invariant/common/InvariantShrinkWithAssert.t.sol:InvariantShrinkWithRequire", + "default/fuzz/invariant/common/InvariantShrinkWithAssert.t.sol:InvariantShrinkWithRequire", vec![( "invariant_with_require()", false, @@ -134,15 +134,15 @@ async fn test_invariant() { )], ), ( - "fuzz/invariant/common/InvariantPreserveState.t.sol:InvariantPreserveState", + "default/fuzz/invariant/common/InvariantPreserveState.t.sol:InvariantPreserveState", vec![("invariant_preserve_state()", true, None, None, None)], ), ( - "fuzz/invariant/common/InvariantCalldataDictionary.t.sol:InvariantCalldataDictionary", + "default/fuzz/invariant/common/InvariantCalldataDictionary.t.sol:InvariantCalldataDictionary", vec![("invariant_owner_never_changes()", true, None, None, None)], ), ( - "fuzz/invariant/common/InvariantAssume.t.sol:InvariantAssume", + "default/fuzz/invariant/common/InvariantAssume.t.sol:InvariantAssume", vec![("invariant_dummy()", true, None, None, None)], ), ]), @@ -152,14 +152,14 @@ async fn test_invariant() { #[tokio::test(flavor = "multi_thread")] async fn test_invariant_override() { let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantReentrancy.t.sol"); - let mut runner = runner(); + let mut runner = TEST_DATA_DEFAULT.runner(); runner.test_options.invariant.call_override = true; let results = runner.test_collect(&filter); assert_multiple( &results, BTreeMap::from([( - "fuzz/invariant/common/InvariantReentrancy.t.sol:InvariantReentrancy", + "default/fuzz/invariant/common/InvariantReentrancy.t.sol:InvariantReentrancy", vec![("invariantNotStolen()", false, Some("revert: stolen".into()), None, None)], )]), ); @@ -168,7 +168,7 @@ 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 = runner(); + 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; @@ -177,7 +177,7 @@ async fn test_invariant_fail_on_revert() { assert_multiple( &results, BTreeMap::from([( - "fuzz/invariant/common/InvariantHandlerFailure.t.sol:InvariantHandlerFailure", + "default/fuzz/invariant/common/InvariantHandlerFailure.t.sol:InvariantHandlerFailure", vec![( "statefulFuzz_BrokenInvariant()", false, @@ -193,7 +193,7 @@ 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 = runner(); + 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 results = runner.test_collect(&filter); @@ -201,7 +201,7 @@ async fn test_invariant_storage() { assert_multiple( &results, BTreeMap::from([( - "fuzz/invariant/storage/InvariantStorageTest.t.sol:InvariantStorageTest", + "default/fuzz/invariant/storage/InvariantStorageTest.t.sol:InvariantStorageTest", vec![ ("invariantChangeAddress()", false, Some("changedAddr".to_string()), None, None), ("invariantChangeString()", false, Some("changedString".to_string()), None, None), @@ -216,7 +216,7 @@ async fn test_invariant_storage() { #[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 = runner(); + let mut runner = TEST_DATA_DEFAULT.runner(); runner.test_options.fuzz.seed = Some(U256::from(119u32)); let results = runner.test_collect(&filter); @@ -260,7 +260,7 @@ async fn test_invariant_shrink() { #[tokio::test(flavor = "multi_thread")] #[cfg_attr(windows, ignore = "for some reason there's different rng")] async fn test_invariant_assert_shrink() { - let mut opts = TEST_OPTS.clone(); + let mut opts = TEST_DATA_DEFAULT.test_opts.clone(); opts.fuzz.seed = Some(U256::from(119u32)); // ensure assert and require shrinks to same sequence of 3 or less @@ -274,7 +274,7 @@ async fn test_shrink(opts: TestOptions, contract_pattern: &str) { contract_pattern, ".*fuzz/invariant/common/InvariantShrinkWithAssert.t.sol", ); - let mut runner = runner(); + let mut runner = TEST_DATA_DEFAULT.runner(); runner.test_options = opts.clone(); let results = runner.test_collect(&filter); let results = results.values().last().expect("`InvariantShrinkWithAssert` should be testable."); @@ -303,14 +303,14 @@ async fn test_shrink(opts: TestOptions, contract_pattern: &str) { #[tokio::test(flavor = "multi_thread")] async fn test_invariant_preserve_state() { let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantPreserveState.t.sol"); - let mut runner = runner(); + let mut runner = TEST_DATA_DEFAULT.runner(); // Should not fail with default options. runner.test_options.invariant.fail_on_revert = true; let results = runner.test_collect(&filter); assert_multiple( &results, BTreeMap::from([( - "fuzz/invariant/common/InvariantPreserveState.t.sol:InvariantPreserveState", + "default/fuzz/invariant/common/InvariantPreserveState.t.sol:InvariantPreserveState", vec![("invariant_preserve_state()", true, None, None, None)], )]), ); @@ -322,7 +322,7 @@ async fn test_invariant_preserve_state() { assert_multiple( &results, BTreeMap::from([( - "fuzz/invariant/common/InvariantPreserveState.t.sol:InvariantPreserveState", + "default/fuzz/invariant/common/InvariantPreserveState.t.sol:InvariantPreserveState", vec![( "invariant_preserve_state()", false, @@ -337,7 +337,7 @@ async fn test_invariant_preserve_state() { #[tokio::test(flavor = "multi_thread")] async fn test_invariant_calldata_fuzz_dictionary_addresses() { // should not fail with default options (address dict not finite) - let mut runner = runner(); + let mut runner = TEST_DATA_DEFAULT.runner(); let results = runner.test_collect(&Filter::new( ".*", ".*", @@ -346,7 +346,7 @@ async fn test_invariant_calldata_fuzz_dictionary_addresses() { assert_multiple( &results, BTreeMap::from([( - "fuzz/invariant/common/InvariantCalldataDictionary.t.sol:InvariantCalldataDictionary", + "default/fuzz/invariant/common/InvariantCalldataDictionary.t.sol:InvariantCalldataDictionary", vec![("invariant_owner_never_changes()", true, None, None, None)], )]), ); @@ -362,7 +362,7 @@ async fn test_invariant_calldata_fuzz_dictionary_addresses() { assert_multiple( &results, BTreeMap::from([( - "fuzz/invariant/common/InvariantCalldataDictionary.t.sol:InvariantCalldataDictionary", + "default/fuzz/invariant/common/InvariantCalldataDictionary.t.sol:InvariantCalldataDictionary", vec![( "invariant_owner_never_changes()", false, @@ -377,14 +377,14 @@ async fn test_invariant_calldata_fuzz_dictionary_addresses() { #[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 = runner(); + let mut runner = TEST_DATA_DEFAULT.runner(); // Should not treat vm.assume as revert. runner.test_options.invariant.fail_on_revert = true; let results = runner.test_collect(&filter); assert_multiple( &results, BTreeMap::from([( - "fuzz/invariant/common/InvariantAssume.t.sol:InvariantAssume", + "default/fuzz/invariant/common/InvariantAssume.t.sol:InvariantAssume", vec![("invariant_dummy()", true, None, None, None)], )]), ); @@ -393,13 +393,13 @@ 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 = runner(); + let mut runner = TEST_DATA_DEFAULT.runner(); runner.test_options.invariant.max_assume_rejects = 1; let results = runner.test_collect(&filter); assert_multiple( &results, BTreeMap::from([( - "fuzz/invariant/common/InvariantAssume.t.sol:InvariantAssume", + "default/fuzz/invariant/common/InvariantAssume.t.sol:InvariantAssume", vec![( "invariant_dummy()", false, diff --git a/crates/forge/tests/it/repros.rs b/crates/forge/tests/it/repros.rs index 108434e50..38cf76331 100644 --- a/crates/forge/tests/it/repros.rs +++ b/crates/forge/tests/it/repros.rs @@ -1,11 +1,14 @@ //! Regression tests for previous issues. -use crate::{config::*, test_helpers::PROJECT}; +use crate::{ + config::*, + test_helpers::{ForgeTestData, TEST_DATA_DEFAULT}, +}; use alloy_primitives::{address, Address}; use ethers_core::abi::{Event, EventParam, Log, LogParam, ParamType, RawLog, Token}; use forge::result::TestStatus; use foundry_common::types::ToEthers; -use foundry_config::{fs_permissions::PathPermission, Config, FsPermissions}; +use foundry_config::{fs_permissions::PathPermission, FsPermissions}; use foundry_evm::{ constants::HARDHAT_CONSOLE_ADDRESS, traces::{CallKind, CallTraceDecoder, DecodedCallData, TraceKind}, @@ -24,7 +27,7 @@ macro_rules! test_repro { paste::paste! { #[tokio::test(flavor = "multi_thread")] async fn [< issue_ $issue_number >]() { - repro_config($issue_number, $should_fail, $sender.into()).await.run().await; + repro_config($issue_number, $should_fail, $sender.into(), &*TEST_DATA_DEFAULT).await.run().await; } } }; @@ -32,7 +35,7 @@ macro_rules! test_repro { paste::paste! { #[tokio::test(flavor = "multi_thread")] async fn [< issue_ $issue_number >]() { - let mut $res = repro_config($issue_number, $should_fail, $sender.into()).await.test(); + let mut $res = repro_config($issue_number, $should_fail, $sender.into(), &*TEST_DATA_DEFAULT).await.test(); $e } } @@ -41,7 +44,7 @@ macro_rules! test_repro { paste::paste! { #[tokio::test(flavor = "multi_thread")] async fn [< issue_ $issue_number >]() { - let mut $config = repro_config($issue_number, false, None).await; + let mut $config = repro_config($issue_number, false, None, &*TEST_DATA_DEFAULT).await; $e $config.run().await; } @@ -49,18 +52,23 @@ macro_rules! test_repro { }; } -async fn repro_config(issue: usize, should_fail: bool, sender: Option
) -> TestConfig { +async fn repro_config( + issue: usize, + should_fail: bool, + sender: Option
, + test_data: &ForgeTestData, +) -> TestConfig { foundry_test_utils::init_tracing(); let filter = Filter::path(&format!(".*repros/Issue{issue}.t.sol")); - let mut config = Config::with_root(PROJECT.root()); + 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 = runner_with_config(config); + let runner = TEST_DATA_DEFAULT.runner_with_config(config); TestConfig::with_filter(runner, filter).set_should_fail(should_fail) } @@ -114,7 +122,7 @@ test_repro!(3223, false, address!("F0959944122fb1ed4CfaBA645eA06EED30427BAA")); // https://github.com/foundry-rs/foundry/issues/3347 test_repro!(3347, false, None, |res| { - let mut res = res.remove("repros/Issue3347.t.sol:Issue3347Test").unwrap(); + let mut res = res.remove("default/repros/Issue3347.t.sol:Issue3347Test").unwrap(); let test = res.test_results.remove("test()").unwrap(); assert_eq!(test.logs.len(), 1); let event = Event { @@ -221,7 +229,7 @@ test_repro!(6115); // https://github.com/foundry-rs/foundry/issues/6170 test_repro!(6170, false, None, |res| { - let mut res = res.remove("repros/Issue6170.t.sol:Issue6170Test").unwrap(); + let mut res = res.remove("default/repros/Issue6170.t.sol:Issue6170Test").unwrap(); let test = res.test_results.remove("test()").unwrap(); assert_eq!(test.status, TestStatus::Failure); assert_eq!(test.reason, Some("log != expected log".to_string())); @@ -235,7 +243,7 @@ test_repro!(6180); // https://github.com/foundry-rs/foundry/issues/6355 test_repro!(6355, false, None, |res| { - let mut res = res.remove("repros/Issue6355.t.sol:Issue6355Test").unwrap(); + let mut res = res.remove("default/repros/Issue6355.t.sol:Issue6355Test").unwrap(); let test = res.test_results.remove("test_shouldFail()").unwrap(); assert_eq!(test.status, TestStatus::Failure); @@ -249,7 +257,7 @@ test_repro!(6437); // Test we decode Hardhat console logs AND traces correctly. // https://github.com/foundry-rs/foundry/issues/6501 test_repro!(6501, false, None, |res| { - let mut res = res.remove("repros/Issue6501.t.sol:Issue6501Test").unwrap(); + let mut res = res.remove("default/repros/Issue6501.t.sol:Issue6501Test").unwrap(); let test = res.test_results.remove("test_hhLogs()").unwrap(); assert_eq!(test.status, TestStatus::Success); assert_eq!(test.decoded_logs, ["a".to_string(), "1".to_string(), "b 2".to_string()]); @@ -293,7 +301,7 @@ test_repro!(6538); // https://github.com/foundry-rs/foundry/issues/6554 test_repro!(6554; |config| { let mut cheats_config = config.runner.cheats_config.as_ref().clone(); - let path = cheats_config.root.join("out/Issue6554.t.sol"); + let path = cheats_config.root.join("out/default/Issue6554.t.sol"); cheats_config.fs_permissions.add(PathPermission::read_write(path)); config.runner.cheats_config = std::sync::Arc::new(cheats_config); }); diff --git a/crates/forge/tests/it/spec.rs b/crates/forge/tests/it/spec.rs index 4dd6aeee5..db98a15d1 100644 --- a/crates/forge/tests/it/spec.rs +++ b/crates/forge/tests/it/spec.rs @@ -1,11 +1,14 @@ //! Integration tests for EVM specifications. -use crate::config::*; +use crate::{config::*, test_helpers::TEST_DATA_DEFAULT}; 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::filter(filter).evm_spec(SpecId::SHANGHAI).run().await; + TestConfig::with_filter(TEST_DATA_DEFAULT.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 969e67357..3d1648756 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -1,51 +1,250 @@ //! Test helpers for Forge integration tests. use alloy_primitives::U256; -use forge::{TestOptions, TestOptionsBuilder}; +use forge::{ + inspectors::CheatsConfig, MultiContractRunner, MultiContractRunnerBuilder, TestOptions, + TestOptionsBuilder, +}; use foundry_compilers::{ artifacts::{Libraries, Settings}, - Project, ProjectCompileOutput, ProjectPathsConfig, SolcConfig, + EvmVersion, Project, ProjectCompileOutput, SolcConfig, +}; +use foundry_config::{ + fs_permissions::PathPermission, Config, FsPermissions, FuzzConfig, FuzzDictionaryConfig, + InvariantConfig, RpcEndpoint, RpcEndpoints, }; -use foundry_config::{Config, FuzzConfig, FuzzDictionaryConfig, InvariantConfig}; use foundry_evm::{ constants::CALLER, - executors::{Executor, FuzzedExecutor}, opts::{Env, EvmOpts}, - revm::db::DatabaseRef, }; -use foundry_test_utils::fd_lock; +use foundry_test_utils::{fd_lock, init_tracing}; use once_cell::sync::Lazy; -use std::{env, io::Write}; +use std::{ + env, fmt, + io::Write, + path::{Path, PathBuf}, +}; pub const RE_PATH_SEPARATOR: &str = "/"; - const TESTDATA: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../testdata"); -pub static PROJECT: Lazy = Lazy::new(|| { - let paths = ProjectPathsConfig::builder().root(TESTDATA).sources(TESTDATA).build().unwrap(); +/// Profile for the tests group. Used to configure separate configurations for test runs. +pub enum ForgeTestProfile { + Default, + Cancun, +} + +impl fmt::Display for ForgeTestProfile { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ForgeTestProfile::Default => write!(f, "default"), + ForgeTestProfile::Cancun => write!(f, "cancun"), + } + } +} + +impl ForgeTestProfile { + pub fn root(&self) -> PathBuf { + PathBuf::from(TESTDATA) + } + + /// Configures the solc settings for the test profile. + pub fn solc_config(&self) -> SolcConfig { + let libs = + ["fork/Fork.t.sol:DssExecLib:0xfD88CeE74f7D78697775aBDAE53f9Da1559728E4".to_string()]; - let libs = - ["fork/Fork.t.sol:DssExecLib:0xfD88CeE74f7D78697775aBDAE53f9Da1559728E4".to_string()]; - let settings = Settings { libraries: Libraries::parse(&libs).unwrap(), ..Default::default() }; - let solc_config = SolcConfig::builder().settings(settings).build(); + let mut settings = + Settings { libraries: Libraries::parse(&libs).unwrap(), ..Default::default() }; - Project::builder().paths(paths).solc_config(solc_config).build().unwrap() -}); + if matches!(self, Self::Cancun) { + settings.evm_version = Some(EvmVersion::Cancun); + } -pub static COMPILED: Lazy = Lazy::new(|| { - const LOCK: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../testdata/.lock"); + SolcConfig::builder().settings(settings).build() + } + + 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, + max_calldata_fuzz_dictionary_addresses: 0, + }, + gas_report_samples: 256, + failure_persist_dir: Some(tempfile::tempdir().unwrap().into_path()), + failure_persist_file: Some("testfailure".to_string()), + }) + .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, + max_calldata_fuzz_dictionary_addresses: 0, + }, + shrink_sequence: true, + shrink_run_limit: 2usize.pow(18u32), + preserve_state: false, + max_assume_rejects: 65536, + gas_report_samples: 256, + }) + .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} + /// Project output files are written to testdata/out/{profile_name} + /// Cache is written to testdata/cache/{profile_name} + /// + /// AST output is enabled by default to support inline configs. + pub fn config(&self) -> Config { + let mut config = Config::with_root(self.root()); + + config.ast = true; + config.src = self.root().join(self.to_string()); + config.out = self.root().join("out").join(self.to_string()); + config.cache_path = self.root().join("cache").join(self.to_string()); + config.libraries = vec![ + "fork/Fork.t.sol:DssExecLib:0xfD88CeE74f7D78697775aBDAE53f9Da1559728E4".to_string(), + ]; + + if matches!(self, Self::Cancun) { + config.evm_version = EvmVersion::Cancun; + } + + config + } +} + +/// Container for test data for a specific test profile. +pub struct ForgeTestData { + pub project: Project, + pub output: ProjectCompileOutput, + pub test_opts: TestOptions, + pub evm_opts: EvmOpts, + pub config: Config, +} - let project = &*PROJECT; - assert!(project.cached); +impl ForgeTestData { + /// Builds [ForgeTestData] for the given [ForgeTestProfile]. + /// + /// Uses [get_compiled] to lazily compile the project. + pub fn new(profile: ForgeTestProfile) -> Self { + let project = profile.project(); + let output = get_compiled(&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 } + } + + /// Builds a base runner + pub fn base_runner(&self) -> MultiContractRunnerBuilder { + init_tracing(); + MultiContractRunnerBuilder::default() + .sender(self.evm_opts.sender) + .with_test_options(self.test_opts.clone()) + } + + /// 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) + } + + /// Builds a non-tracing runner + pub fn runner_with_config(&self, mut config: Config) -> MultiContractRunner { + config.rpc_endpoints = rpc_endpoints(); + config.allow_paths.push(manifest_root().to_path_buf()); + + let root = self.project.root(); + let opts = self.evm_opts.clone(); + let env = opts.local_evm_env(); + let output = self.output.clone(); + self.base_runner() + .with_cheats_config(CheatsConfig::new(&config, opts.clone(), None)) + .sender(config.sender) + .with_test_options(self.test_opts.clone()) + .build(root, output, env, opts.clone()) + .unwrap() + } + + /// Builds a tracing runner + pub fn tracing_runner(&self) -> MultiContractRunner { + let mut opts = self.evm_opts.clone(); + opts.verbosity = 5; + self.base_runner() + .build(self.project.root(), self.output.clone(), opts.local_evm_env(), opts) + .unwrap() + } + + /// Builds a runner that runs against forked state + pub async fn forked_runner(&self, rpc: &str) -> MultiContractRunner { + let mut opts = self.evm_opts.clone(); + + opts.env.chain_id = None; // clear chain id so the correct one gets fetched from the RPC + opts.fork_url = Some(rpc.to_string()); + + let env = opts.evm_env().await.expect("Could not instantiate fork environment"); + let fork = opts.get_fork(&Default::default(), env.clone()); + + self.base_runner() + .with_fork(fork) + .build(self.project.root(), self.output.clone(), env, opts) + .unwrap() + } +} + +pub fn get_compiled(project: &Project) -> ProjectCompileOutput { + let lock_file_path = project.sources_path().join(".lock"); // Compile only once per test run. // We need to use a file lock because `cargo-nextest` runs tests in different processes. // This is similar to [`foundry_test_utils::util::initialize`], see its comments for more // details. - let mut lock = fd_lock::new_lock(LOCK); + let mut lock = fd_lock::new_lock(&lock_file_path); let read = lock.read().unwrap(); let out; - if project.cache_path().exists() && std::fs::read(LOCK).unwrap() == b"1" { + if project.cache_path().exists() && std::fs::read(&lock_file_path).unwrap() == b"1" { out = project.compile(); drop(read); } else { @@ -61,73 +260,35 @@ pub static COMPILED: Lazy = Lazy::new(|| { panic!("Compiled with errors:\n{out}"); } out -}); - -pub static EVM_OPTS: Lazy = Lazy::new(|| EvmOpts { - env: Env { - gas_limit: u64::MAX, - chain_id: None, - tx_origin: Config::DEFAULT_SENDER, - block_number: 1, - block_timestamp: 1, - ..Default::default() - }, - sender: Config::DEFAULT_SENDER, - initial_balance: U256::MAX, - ffi: true, - verbosity: 3, - memory_limit: 1 << 26, - ..Default::default() -}); - -pub static TEST_OPTS: Lazy = Lazy::new(|| { - 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, - max_calldata_fuzz_dictionary_addresses: 0, - }, - gas_report_samples: 256, - failure_persist_dir: Some(tempfile::tempdir().unwrap().into_path()), - failure_persist_file: Some("testfailure".to_string()), - }) - .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, - max_calldata_fuzz_dictionary_addresses: 0, - }, - shrink_sequence: true, - shrink_run_limit: 2usize.pow(18u32), - preserve_state: false, - max_assume_rejects: 65536, - gas_report_samples: 256, - }) - .build(&COMPILED, &PROJECT.paths.root) - .expect("Config loaded") -}); - -pub fn fuzz_executor(executor: Executor) -> FuzzedExecutor { - let cfg = proptest::test_runner::Config { failure_persistence: None, ..Default::default() }; - - FuzzedExecutor::new( - executor, - proptest::test_runner::TestRunner::new(cfg), - CALLER, - TEST_OPTS.fuzz.clone(), - ) +} + +/// Default data for the tests group. +pub static TEST_DATA_DEFAULT: Lazy = + Lazy::new(|| ForgeTestData::new(ForgeTestProfile::Default)); + +/// Data for tests requiring Cancun support on Solc and EVM level. +pub static TEST_DATA_CANCUN: Lazy = + Lazy::new(|| ForgeTestData::new(ForgeTestProfile::Cancun)); + +pub fn manifest_root() -> &'static Path { + let mut root = Path::new(env!("CARGO_MANIFEST_DIR")); + // need to check here where we're executing the test from, if in `forge` we need to also allow + // `testdata` + if root.ends_with("forge") { + root = root.parent().unwrap(); + } + root +} + +/// the RPC endpoints used during tests +pub fn rpc_endpoints() -> RpcEndpoints { + RpcEndpoints::new([ + ( + "rpcAlias", + RpcEndpoint::Url( + "https://eth-mainnet.alchemyapi.io/v2/Lc7oIGYeL_QvInzI0Wiu_pOZZDEKBrdf".to_string(), + ), + ), + ("rpcEnvAlias", RpcEndpoint::Env("${RPC_ENV_ALIAS}".to_string())), + ]) } diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index f4449a769..80b06ae76 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -38,6 +38,8 @@ impl ScriptTester { "script", "-R", "ds-test/=lib/", + "-R", + "cheats/=cheats/", target_contract, "--root", project_root.to_str().unwrap(), @@ -75,7 +77,7 @@ impl ScriptTester { // copy the broadcast test fs::copy( - Self::testdata_path().join("cheats/Broadcast.t.sol"), + Self::testdata_path().join("default/cheats/Broadcast.t.sol"), project_root.join(BROADCAST_TEST_PATH), ) .expect("Failed to initialize broadcast contract"); @@ -90,8 +92,11 @@ impl ScriptTester { // copy the broadcast test let testdata = Self::testdata_path(); - fs::copy(testdata.join("cheats/Broadcast.t.sol"), project_root.join(BROADCAST_TEST_PATH)) - .expect("Failed to initialize broadcast contract"); + fs::copy( + testdata.join("default/cheats/Broadcast.t.sol"), + project_root.join(BROADCAST_TEST_PATH), + ) + .expect("Failed to initialize broadcast contract"); Self::new(cmd, None, project_root, &target_contract) } @@ -104,7 +109,8 @@ impl ScriptTester { /// Initialises the test contracts by copying them into the workspace fn copy_testdata(current_dir: &Path) -> Result<()> { let testdata = Self::testdata_path(); - fs::copy(testdata.join("cheats/Vm.sol"), current_dir.join("src/Vm.sol"))?; + fs::create_dir_all(current_dir.join("cheats"))?; + fs::copy(testdata.join("cheats/Vm.sol"), current_dir.join("cheats/Vm.sol"))?; fs::copy(testdata.join("lib/ds-test/src/test.sol"), current_dir.join("lib/test.sol"))?; Ok(()) } diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index d392b953e..dbb872d8a 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -577,7 +577,7 @@ impl TestProject { /// Adds `console.sol` as a source under "console.sol" pub fn insert_console(&self) -> PathBuf { - let s = include_str!("../../../testdata/logs/console.sol"); + let s = include_str!("../../../testdata/default/logs/console.sol"); self.add_source("console.sol", s).unwrap() } diff --git a/testdata/cheats/Addr.t.sol b/testdata/default/cheats/Addr.t.sol similarity index 95% rename from testdata/cheats/Addr.t.sol rename to testdata/default/cheats/Addr.t.sol index 39f14f6aa..432c52e69 100644 --- a/testdata/cheats/Addr.t.sol +++ b/testdata/default/cheats/Addr.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract AddrTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/Assert.t.sol b/testdata/default/cheats/Assert.t.sol similarity index 99% rename from testdata/cheats/Assert.t.sol rename to testdata/default/cheats/Assert.t.sol index 10015a850..d2b0dcb35 100644 --- a/testdata/cheats/Assert.t.sol +++ b/testdata/default/cheats/Assert.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract AssertionsTest is DSTest { string constant errorMessage = "User provided message"; diff --git a/testdata/cheats/Assume.t.sol b/testdata/default/cheats/Assume.t.sol similarity index 92% rename from testdata/cheats/Assume.t.sol rename to testdata/default/cheats/Assume.t.sol index 7520cfd6d..de11d6644 100644 --- a/testdata/cheats/Assume.t.sol +++ b/testdata/default/cheats/Assume.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract AssumeTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/Bank.t.sol b/testdata/default/cheats/Bank.t.sol similarity index 95% rename from testdata/cheats/Bank.t.sol rename to testdata/default/cheats/Bank.t.sol index 31feed498..a02fe1667 100644 --- a/testdata/cheats/Bank.t.sol +++ b/testdata/default/cheats/Bank.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract CoinbaseTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/Base64.t.sol b/testdata/default/cheats/Base64.t.sol similarity index 96% rename from testdata/cheats/Base64.t.sol rename to testdata/default/cheats/Base64.t.sol index 88b792cb2..0d2249395 100644 --- a/testdata/cheats/Base64.t.sol +++ b/testdata/default/cheats/Base64.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; import "../logs/console.sol"; contract Base64Test is DSTest { diff --git a/testdata/cheats/Broadcast.t.sol b/testdata/default/cheats/Broadcast.t.sol similarity index 99% rename from testdata/cheats/Broadcast.t.sol rename to testdata/default/cheats/Broadcast.t.sol index d542c28c0..d44bc9540 100644 --- a/testdata/cheats/Broadcast.t.sol +++ b/testdata/default/cheats/Broadcast.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; library F { function t2() public pure returns (uint256) { diff --git a/testdata/cheats/ChainId.t.sol b/testdata/default/cheats/ChainId.t.sol similarity index 93% rename from testdata/cheats/ChainId.t.sol rename to testdata/default/cheats/ChainId.t.sol index af5312241..aa8fa0a13 100644 --- a/testdata/cheats/ChainId.t.sol +++ b/testdata/default/cheats/ChainId.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract DealTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/Cool.t.sol b/testdata/default/cheats/Cool.t.sol similarity index 95% rename from testdata/cheats/Cool.t.sol rename to testdata/default/cheats/Cool.t.sol index d721a442d..82212f1b1 100644 --- a/testdata/cheats/Cool.t.sol +++ b/testdata/default/cheats/Cool.t.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity 0.8.18; -import "../lib/ds-test/src/test.sol"; -import "./Vm.sol"; +import "lib/ds-test/src/test.sol"; +import "cheats/Vm.sol"; contract CoolTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/Deal.t.sol b/testdata/default/cheats/Deal.t.sol similarity index 96% rename from testdata/cheats/Deal.t.sol rename to testdata/default/cheats/Deal.t.sol index 2729ac73e..ac4776435 100644 --- a/testdata/cheats/Deal.t.sol +++ b/testdata/default/cheats/Deal.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract DealTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/Derive.t.sol b/testdata/default/cheats/Derive.t.sol similarity index 96% rename from testdata/cheats/Derive.t.sol rename to testdata/default/cheats/Derive.t.sol index e2107e80c..fb1443333 100644 --- a/testdata/cheats/Derive.t.sol +++ b/testdata/default/cheats/Derive.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract DeriveTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/Env.t.sol b/testdata/default/cheats/Env.t.sol similarity index 99% rename from testdata/cheats/Env.t.sol rename to testdata/default/cheats/Env.t.sol index ae6c89ec9..523ab34dc 100644 --- a/testdata/cheats/Env.t.sol +++ b/testdata/default/cheats/Env.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract EnvTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/Etch.t.sol b/testdata/default/cheats/Etch.t.sol similarity index 96% rename from testdata/cheats/Etch.t.sol rename to testdata/default/cheats/Etch.t.sol index f93a002b2..6e58fc13b 100644 --- a/testdata/cheats/Etch.t.sol +++ b/testdata/default/cheats/Etch.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract EtchTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/ExpectCall.t.sol b/testdata/default/cheats/ExpectCall.t.sol similarity index 99% rename from testdata/cheats/ExpectCall.t.sol rename to testdata/default/cheats/ExpectCall.t.sol index 3cc9e6c57..86a5290a9 100644 --- a/testdata/cheats/ExpectCall.t.sol +++ b/testdata/default/cheats/ExpectCall.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract Contract { function numberA() public pure returns (uint256) { diff --git a/testdata/cheats/ExpectEmit.t.sol b/testdata/default/cheats/ExpectEmit.t.sol similarity index 99% rename from testdata/cheats/ExpectEmit.t.sol rename to testdata/default/cheats/ExpectEmit.t.sol index b232ab36b..cad184355 100644 --- a/testdata/cheats/ExpectEmit.t.sol +++ b/testdata/default/cheats/ExpectEmit.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract Emitter { uint256 public thing; diff --git a/testdata/cheats/ExpectRevert.t.sol b/testdata/default/cheats/ExpectRevert.t.sol similarity index 99% rename from testdata/cheats/ExpectRevert.t.sol rename to testdata/default/cheats/ExpectRevert.t.sol index 6006f4506..0cc6cac59 100644 --- a/testdata/cheats/ExpectRevert.t.sol +++ b/testdata/default/cheats/ExpectRevert.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract Reverter { error CustomError(); diff --git a/testdata/cheats/Fee.t.sol b/testdata/default/cheats/Fee.t.sol similarity index 94% rename from testdata/cheats/Fee.t.sol rename to testdata/default/cheats/Fee.t.sol index 3d6ea72a8..ad93fed6a 100644 --- a/testdata/cheats/Fee.t.sol +++ b/testdata/default/cheats/Fee.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract FeeTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/Ffi.t.sol b/testdata/default/cheats/Ffi.t.sol similarity index 97% rename from testdata/cheats/Ffi.t.sol rename to testdata/default/cheats/Ffi.t.sol index 2aa2175e2..897783d7e 100644 --- a/testdata/cheats/Ffi.t.sol +++ b/testdata/default/cheats/Ffi.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract FfiTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/Fork.t.sol b/testdata/default/cheats/Fork.t.sol similarity index 99% rename from testdata/cheats/Fork.t.sol rename to testdata/default/cheats/Fork.t.sol index 0b64b9eb1..2ff4a6432 100644 --- a/testdata/cheats/Fork.t.sol +++ b/testdata/default/cheats/Fork.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; interface IWETH { function deposit() external payable; diff --git a/testdata/cheats/Fork2.t.sol b/testdata/default/cheats/Fork2.t.sol similarity index 99% rename from testdata/cheats/Fork2.t.sol rename to testdata/default/cheats/Fork2.t.sol index b3c1008b7..4b4053334 100644 --- a/testdata/cheats/Fork2.t.sol +++ b/testdata/default/cheats/Fork2.t.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; import "../logs/console.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; struct MyStruct { uint256 value; diff --git a/testdata/cheats/Fs.t.sol b/testdata/default/cheats/Fs.t.sol similarity index 99% rename from testdata/cheats/Fs.t.sol rename to testdata/default/cheats/Fs.t.sol index 13093ede6..c48adefec 100644 --- a/testdata/cheats/Fs.t.sol +++ b/testdata/default/cheats/Fs.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract FsTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/GasMetering.t.sol b/testdata/default/cheats/GasMetering.t.sol similarity index 98% rename from testdata/cheats/GasMetering.t.sol rename to testdata/default/cheats/GasMetering.t.sol index e5616634f..54d0a7422 100644 --- a/testdata/cheats/GasMetering.t.sol +++ b/testdata/default/cheats/GasMetering.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract B { function a() public returns (uint256) { diff --git a/testdata/cheats/GetBlockTimestamp.t.sol b/testdata/default/cheats/GetBlockTimestamp.t.sol similarity index 96% rename from testdata/cheats/GetBlockTimestamp.t.sol rename to testdata/default/cheats/GetBlockTimestamp.t.sol index 144d6b56e..383bfa8b0 100644 --- a/testdata/cheats/GetBlockTimestamp.t.sol +++ b/testdata/default/cheats/GetBlockTimestamp.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract GetBlockTimestampTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/GetCode.t.sol b/testdata/default/cheats/GetCode.t.sol similarity index 99% rename from testdata/cheats/GetCode.t.sol rename to testdata/default/cheats/GetCode.t.sol index db8841f60..8f47188d5 100644 --- a/testdata/cheats/GetCode.t.sol +++ b/testdata/default/cheats/GetCode.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract GetCodeTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/GetDeployedCode.t.sol b/testdata/default/cheats/GetDeployedCode.t.sol similarity index 99% rename from testdata/cheats/GetDeployedCode.t.sol rename to testdata/default/cheats/GetDeployedCode.t.sol index 71020af18..fc8ed609e 100644 --- a/testdata/cheats/GetDeployedCode.t.sol +++ b/testdata/default/cheats/GetDeployedCode.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract GetDeployedCodeTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/GetLabel.t.sol b/testdata/default/cheats/GetLabel.t.sol similarity index 94% rename from testdata/cheats/GetLabel.t.sol rename to testdata/default/cheats/GetLabel.t.sol index 784a18cea..dcbe0812c 100644 --- a/testdata/cheats/GetLabel.t.sol +++ b/testdata/default/cheats/GetLabel.t.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.0; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract GetLabelTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/GetNonce.t.sol b/testdata/default/cheats/GetNonce.t.sol similarity index 94% rename from testdata/cheats/GetNonce.t.sol rename to testdata/default/cheats/GetNonce.t.sol index 8d3a19646..7eb53f205 100644 --- a/testdata/cheats/GetNonce.t.sol +++ b/testdata/default/cheats/GetNonce.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract Foo {} diff --git a/testdata/cheats/Json.t.sol b/testdata/default/cheats/Json.t.sol similarity index 99% rename from testdata/cheats/Json.t.sol rename to testdata/default/cheats/Json.t.sol index 3d44dbd2c..b15292e15 100644 --- a/testdata/cheats/Json.t.sol +++ b/testdata/default/cheats/Json.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; import "../logs/console.sol"; contract ParseJsonTest is DSTest { diff --git a/testdata/cheats/Label.t.sol b/testdata/default/cheats/Label.t.sol similarity index 91% rename from testdata/cheats/Label.t.sol rename to testdata/default/cheats/Label.t.sol index b8e29d195..d554f637d 100644 --- a/testdata/cheats/Label.t.sol +++ b/testdata/default/cheats/Label.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract LabelTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/Load.t.sol b/testdata/default/cheats/Load.t.sol similarity index 97% rename from testdata/cheats/Load.t.sol rename to testdata/default/cheats/Load.t.sol index fa5680d71..37a2c80b2 100644 --- a/testdata/cheats/Load.t.sol +++ b/testdata/default/cheats/Load.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract Storage { uint256 slot0 = 10; diff --git a/testdata/cheats/Mapping.t.sol b/testdata/default/cheats/Mapping.t.sol similarity index 99% rename from testdata/cheats/Mapping.t.sol rename to testdata/default/cheats/Mapping.t.sol index 4dec4156b..6cd141fa8 100644 --- a/testdata/cheats/Mapping.t.sol +++ b/testdata/default/cheats/Mapping.t.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.0; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract RecordMapping { int256 length; diff --git a/testdata/cheats/MemSafety.t.sol b/testdata/default/cheats/MemSafety.t.sol similarity index 99% rename from testdata/cheats/MemSafety.t.sol rename to testdata/default/cheats/MemSafety.t.sol index eb00e77de..05444e39a 100644 --- a/testdata/cheats/MemSafety.t.sol +++ b/testdata/default/cheats/MemSafety.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract MemSafetyTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/MockCall.t.sol b/testdata/default/cheats/MockCall.t.sol similarity index 99% rename from testdata/cheats/MockCall.t.sol rename to testdata/default/cheats/MockCall.t.sol index fa7d9f314..a70b3572b 100644 --- a/testdata/cheats/MockCall.t.sol +++ b/testdata/default/cheats/MockCall.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract Mock { uint256 state = 0; diff --git a/testdata/cheats/Parse.t.sol b/testdata/default/cheats/Parse.t.sol similarity index 99% rename from testdata/cheats/Parse.t.sol rename to testdata/default/cheats/Parse.t.sol index a39d32d08..71d49af6f 100644 --- a/testdata/cheats/Parse.t.sol +++ b/testdata/default/cheats/Parse.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract ParseTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/Prank.t.sol b/testdata/default/cheats/Prank.t.sol similarity index 99% rename from testdata/cheats/Prank.t.sol rename to testdata/default/cheats/Prank.t.sol index 0e23ed7b8..f7dd9b714 100644 --- a/testdata/cheats/Prank.t.sol +++ b/testdata/default/cheats/Prank.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract Victim { function assertCallerAndOrigin( diff --git a/testdata/cheats/Prevrandao.t.sol b/testdata/default/cheats/Prevrandao.t.sol similarity index 97% rename from testdata/cheats/Prevrandao.t.sol rename to testdata/default/cheats/Prevrandao.t.sol index 20bab12c4..a356fcd4e 100644 --- a/testdata/cheats/Prevrandao.t.sol +++ b/testdata/default/cheats/Prevrandao.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract PrevrandaoTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/ProjectRoot.t.sol b/testdata/default/cheats/ProjectRoot.t.sol similarity index 97% rename from testdata/cheats/ProjectRoot.t.sol rename to testdata/default/cheats/ProjectRoot.t.sol index 1edfb0e07..31e68e105 100644 --- a/testdata/cheats/ProjectRoot.t.sol +++ b/testdata/default/cheats/ProjectRoot.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract ProjectRootTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/ReadCallers.t.sol b/testdata/default/cheats/ReadCallers.t.sol similarity index 99% rename from testdata/cheats/ReadCallers.t.sol rename to testdata/default/cheats/ReadCallers.t.sol index 82210b6c4..e0da8ed0d 100644 --- a/testdata/cheats/ReadCallers.t.sol +++ b/testdata/default/cheats/ReadCallers.t.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.0; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract Target { function consumeNewCaller() external {} diff --git a/testdata/cheats/Record.t.sol b/testdata/default/cheats/Record.t.sol similarity index 98% rename from testdata/cheats/Record.t.sol rename to testdata/default/cheats/Record.t.sol index 6fdfa627d..152a5ccb5 100644 --- a/testdata/cheats/Record.t.sol +++ b/testdata/default/cheats/Record.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract RecordAccess { function record() public returns (NestedRecordAccess) { diff --git a/testdata/cheats/RecordAccountAccesses.t.sol b/testdata/default/cheats/RecordAccountAccesses.t.sol similarity index 99% rename from testdata/cheats/RecordAccountAccesses.t.sol rename to testdata/default/cheats/RecordAccountAccesses.t.sol index a86361a75..bea20570a 100644 --- a/testdata/cheats/RecordAccountAccesses.t.sol +++ b/testdata/default/cheats/RecordAccountAccesses.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; /// @notice Helper contract with a construction that makes a call to itself then /// optionally reverts if zero-length data is passed diff --git a/testdata/cheats/RecordLogs.t.sol b/testdata/default/cheats/RecordLogs.t.sol similarity index 99% rename from testdata/cheats/RecordLogs.t.sol rename to testdata/default/cheats/RecordLogs.t.sol index 25fbfaeba..728acdb9b 100644 --- a/testdata/cheats/RecordLogs.t.sol +++ b/testdata/default/cheats/RecordLogs.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract Emitter { event LogAnonymous(bytes data) anonymous; diff --git a/testdata/cheats/Remember.t.sol b/testdata/default/cheats/Remember.t.sol similarity index 96% rename from testdata/cheats/Remember.t.sol rename to testdata/default/cheats/Remember.t.sol index 5592081ea..b5487c369 100644 --- a/testdata/cheats/Remember.t.sol +++ b/testdata/default/cheats/Remember.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract RememberTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/ResetNonce.t.sol b/testdata/default/cheats/ResetNonce.t.sol similarity index 97% rename from testdata/cheats/ResetNonce.t.sol rename to testdata/default/cheats/ResetNonce.t.sol index 914577bdc..901433609 100644 --- a/testdata/cheats/ResetNonce.t.sol +++ b/testdata/default/cheats/ResetNonce.t.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.0; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract Foo { function f() external view returns (uint256) { diff --git a/testdata/cheats/Roll.t.sol b/testdata/default/cheats/Roll.t.sol similarity index 97% rename from testdata/cheats/Roll.t.sol rename to testdata/default/cheats/Roll.t.sol index 50011fe87..820cd9887 100644 --- a/testdata/cheats/Roll.t.sol +++ b/testdata/default/cheats/Roll.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract RollTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/RpcUrls.t.sol b/testdata/default/cheats/RpcUrls.t.sol similarity index 98% rename from testdata/cheats/RpcUrls.t.sol rename to testdata/default/cheats/RpcUrls.t.sol index 282c2addd..3d7b298f4 100644 --- a/testdata/cheats/RpcUrls.t.sol +++ b/testdata/default/cheats/RpcUrls.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract RpcUrlTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/SetNonce.t.sol b/testdata/default/cheats/SetNonce.t.sol similarity index 96% rename from testdata/cheats/SetNonce.t.sol rename to testdata/default/cheats/SetNonce.t.sol index 9285ca7e6..7f2e419b9 100644 --- a/testdata/cheats/SetNonce.t.sol +++ b/testdata/default/cheats/SetNonce.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract Foo { function f() external view returns (uint256) { diff --git a/testdata/cheats/SetNonceUnsafe.t.sol b/testdata/default/cheats/SetNonceUnsafe.t.sol similarity index 97% rename from testdata/cheats/SetNonceUnsafe.t.sol rename to testdata/default/cheats/SetNonceUnsafe.t.sol index 1209a2814..723f66ae2 100644 --- a/testdata/cheats/SetNonceUnsafe.t.sol +++ b/testdata/default/cheats/SetNonceUnsafe.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract Foo { function f() external view returns (uint256) { diff --git a/testdata/cheats/Setup.t.sol b/testdata/default/cheats/Setup.t.sol similarity index 97% rename from testdata/cheats/Setup.t.sol rename to testdata/default/cheats/Setup.t.sol index 986b232d8..e94bf34ec 100644 --- a/testdata/cheats/Setup.t.sol +++ b/testdata/default/cheats/Setup.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract Victim { function assertSender(address sender) external { diff --git a/testdata/cheats/Sign.t.sol b/testdata/default/cheats/Sign.t.sol similarity index 96% rename from testdata/cheats/Sign.t.sol rename to testdata/default/cheats/Sign.t.sol index 587d80e5f..e46439b58 100644 --- a/testdata/cheats/Sign.t.sol +++ b/testdata/default/cheats/Sign.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract SignTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/SignP256.t.sol b/testdata/default/cheats/SignP256.t.sol similarity index 96% rename from testdata/cheats/SignP256.t.sol rename to testdata/default/cheats/SignP256.t.sol index ee0363e97..f1b62fe78 100644 --- a/testdata/cheats/SignP256.t.sol +++ b/testdata/default/cheats/SignP256.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract SignTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/Skip.t.sol b/testdata/default/cheats/Skip.t.sol similarity index 96% rename from testdata/cheats/Skip.t.sol rename to testdata/default/cheats/Skip.t.sol index b5f8a019b..fb2deadb4 100644 --- a/testdata/cheats/Skip.t.sol +++ b/testdata/default/cheats/Skip.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract SkipTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/Sleep.t.sol b/testdata/default/cheats/Sleep.t.sol similarity index 98% rename from testdata/cheats/Sleep.t.sol rename to testdata/default/cheats/Sleep.t.sol index 37be632cc..448d34668 100644 --- a/testdata/cheats/Sleep.t.sol +++ b/testdata/default/cheats/Sleep.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract SleepTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/Snapshots.t.sol b/testdata/default/cheats/Snapshots.t.sol similarity index 99% rename from testdata/cheats/Snapshots.t.sol rename to testdata/default/cheats/Snapshots.t.sol index baf82e2e5..8f85fee40 100644 --- a/testdata/cheats/Snapshots.t.sol +++ b/testdata/default/cheats/Snapshots.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; struct Storage { uint256 slot0; diff --git a/testdata/cheats/Store.t.sol b/testdata/default/cheats/Store.t.sol similarity index 98% rename from testdata/cheats/Store.t.sol rename to testdata/default/cheats/Store.t.sol index 08803b92f..059952fca 100644 --- a/testdata/cheats/Store.t.sol +++ b/testdata/default/cheats/Store.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract Storage { uint256 public slot0 = 10; diff --git a/testdata/cheats/StringUtils.t.sol b/testdata/default/cheats/StringUtils.t.sol similarity index 98% rename from testdata/cheats/StringUtils.t.sol rename to testdata/default/cheats/StringUtils.t.sol index 4fe8bba01..471d628be 100644 --- a/testdata/cheats/StringUtils.t.sol +++ b/testdata/default/cheats/StringUtils.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract StringManipulationTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/ToString.t.sol b/testdata/default/cheats/ToString.t.sol similarity index 98% rename from testdata/cheats/ToString.t.sol rename to testdata/default/cheats/ToString.t.sol index c26fdce4c..835c85242 100644 --- a/testdata/cheats/ToString.t.sol +++ b/testdata/default/cheats/ToString.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract ToStringTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/Toml.t.sol b/testdata/default/cheats/Toml.t.sol similarity index 99% rename from testdata/cheats/Toml.t.sol rename to testdata/default/cheats/Toml.t.sol index ccb73ab87..40667743f 100644 --- a/testdata/cheats/Toml.t.sol +++ b/testdata/default/cheats/Toml.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; import "../logs/console.sol"; contract ParseTomlTest is DSTest { diff --git a/testdata/cheats/Travel.t.sol b/testdata/default/cheats/Travel.t.sol similarity index 95% rename from testdata/cheats/Travel.t.sol rename to testdata/default/cheats/Travel.t.sol index 297017852..733559b29 100644 --- a/testdata/cheats/Travel.t.sol +++ b/testdata/default/cheats/Travel.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract ChainIdTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/TryFfi.sol b/testdata/default/cheats/TryFfi.sol similarity index 97% rename from testdata/cheats/TryFfi.sol rename to testdata/default/cheats/TryFfi.sol index 745b65ce0..58d93a48b 100644 --- a/testdata/cheats/TryFfi.sol +++ b/testdata/default/cheats/TryFfi.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract TryFfiTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/UnixTime.t.sol b/testdata/default/cheats/UnixTime.t.sol similarity index 97% rename from testdata/cheats/UnixTime.t.sol rename to testdata/default/cheats/UnixTime.t.sol index 66cbcc395..e128dad24 100644 --- a/testdata/cheats/UnixTime.t.sol +++ b/testdata/default/cheats/UnixTime.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract UnixTimeTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/Wallet.t.sol b/testdata/default/cheats/Wallet.t.sol similarity index 99% rename from testdata/cheats/Wallet.t.sol rename to testdata/default/cheats/Wallet.t.sol index a8ce5cc02..8ecb707ae 100644 --- a/testdata/cheats/Wallet.t.sol +++ b/testdata/default/cheats/Wallet.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract Foo {} diff --git a/testdata/cheats/Warp.t.sol b/testdata/default/cheats/Warp.t.sol similarity index 96% rename from testdata/cheats/Warp.t.sol rename to testdata/default/cheats/Warp.t.sol index 0400099f8..01ebc8e89 100644 --- a/testdata/cheats/Warp.t.sol +++ b/testdata/default/cheats/Warp.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract WarpTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/dumpState.t.sol b/testdata/default/cheats/dumpState.t.sol similarity index 99% rename from testdata/cheats/dumpState.t.sol rename to testdata/default/cheats/dumpState.t.sol index 387865a1b..74ebd3071 100644 --- a/testdata/cheats/dumpState.t.sol +++ b/testdata/default/cheats/dumpState.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract SimpleContract { constructor() { diff --git a/testdata/cheats/getBlockNumber.t.sol b/testdata/default/cheats/getBlockNumber.t.sol similarity index 97% rename from testdata/cheats/getBlockNumber.t.sol rename to testdata/default/cheats/getBlockNumber.t.sol index 774091747..c874e5e2f 100644 --- a/testdata/cheats/getBlockNumber.t.sol +++ b/testdata/default/cheats/getBlockNumber.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract GetBlockNumberTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/cheats/loadAllocs.t.sol b/testdata/default/cheats/loadAllocs.t.sol similarity index 99% rename from testdata/cheats/loadAllocs.t.sol rename to testdata/default/cheats/loadAllocs.t.sol index f219d025e..358608860 100644 --- a/testdata/cheats/loadAllocs.t.sol +++ b/testdata/default/cheats/loadAllocs.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "./Vm.sol"; +import "cheats/Vm.sol"; contract LoadAllocsTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/core/Abstract.t.sol b/testdata/default/core/Abstract.t.sol similarity index 100% rename from testdata/core/Abstract.t.sol rename to testdata/default/core/Abstract.t.sol diff --git a/testdata/core/ContractEnvironment.t.sol b/testdata/default/core/ContractEnvironment.t.sol similarity index 100% rename from testdata/core/ContractEnvironment.t.sol rename to testdata/default/core/ContractEnvironment.t.sol diff --git a/testdata/core/DSStyle.t.sol b/testdata/default/core/DSStyle.t.sol similarity index 100% rename from testdata/core/DSStyle.t.sol rename to testdata/default/core/DSStyle.t.sol diff --git a/testdata/core/FailingSetup.t.sol b/testdata/default/core/FailingSetup.t.sol similarity index 100% rename from testdata/core/FailingSetup.t.sol rename to testdata/default/core/FailingSetup.t.sol diff --git a/testdata/core/FailingTestAfterFailedSetup.t.sol b/testdata/default/core/FailingTestAfterFailedSetup.t.sol similarity index 100% rename from testdata/core/FailingTestAfterFailedSetup.t.sol rename to testdata/default/core/FailingTestAfterFailedSetup.t.sol diff --git a/testdata/core/MultipleSetup.t.sol b/testdata/default/core/MultipleSetup.t.sol similarity index 100% rename from testdata/core/MultipleSetup.t.sol rename to testdata/default/core/MultipleSetup.t.sol diff --git a/testdata/core/PaymentFailure.t.sol b/testdata/default/core/PaymentFailure.t.sol similarity index 93% rename from testdata/core/PaymentFailure.t.sol rename to testdata/default/core/PaymentFailure.t.sol index 21558cf9c..d4751b2d5 100644 --- a/testdata/core/PaymentFailure.t.sol +++ b/testdata/default/core/PaymentFailure.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; contract Payable { function pay() public payable {} diff --git a/testdata/core/Reverting.t.sol b/testdata/default/core/Reverting.t.sol similarity index 100% rename from testdata/core/Reverting.t.sol rename to testdata/default/core/Reverting.t.sol diff --git a/testdata/core/SetupConsistency.t.sol b/testdata/default/core/SetupConsistency.t.sol similarity index 100% rename from testdata/core/SetupConsistency.t.sol rename to testdata/default/core/SetupConsistency.t.sol diff --git a/testdata/fork/DssExecLib.sol b/testdata/default/fork/DssExecLib.sol similarity index 100% rename from testdata/fork/DssExecLib.sol rename to testdata/default/fork/DssExecLib.sol diff --git a/testdata/fork/ForkSame_1.t.sol b/testdata/default/fork/ForkSame_1.t.sol similarity index 95% rename from testdata/fork/ForkSame_1.t.sol rename to testdata/default/fork/ForkSame_1.t.sol index 01c89e6e2..bff9678f6 100644 --- a/testdata/fork/ForkSame_1.t.sol +++ b/testdata/default/fork/ForkSame_1.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; contract ForkTest is DSTest { address constant WETH_TOKEN_ADDR = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; diff --git a/testdata/fork/ForkSame_2.t.sol b/testdata/default/fork/ForkSame_2.t.sol similarity index 95% rename from testdata/fork/ForkSame_2.t.sol rename to testdata/default/fork/ForkSame_2.t.sol index 01c89e6e2..bff9678f6 100644 --- a/testdata/fork/ForkSame_2.t.sol +++ b/testdata/default/fork/ForkSame_2.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; contract ForkTest is DSTest { address constant WETH_TOKEN_ADDR = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; diff --git a/testdata/fork/LaunchFork.t.sol b/testdata/default/fork/LaunchFork.t.sol similarity index 100% rename from testdata/fork/LaunchFork.t.sol rename to testdata/default/fork/LaunchFork.t.sol diff --git a/testdata/fork/Transact.t.sol b/testdata/default/fork/Transact.t.sol similarity index 99% rename from testdata/fork/Transact.t.sol rename to testdata/default/fork/Transact.t.sol index 79c53fa3f..ec803906d 100644 --- a/testdata/fork/Transact.t.sol +++ b/testdata/default/fork/Transact.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; import "../logs/console.sol"; interface IERC20 { diff --git a/testdata/fs/Default.t.sol b/testdata/default/fs/Default.t.sol similarity index 97% rename from testdata/fs/Default.t.sol rename to testdata/default/fs/Default.t.sol index 7ef8c5bd2..5e776e696 100644 --- a/testdata/fs/Default.t.sol +++ b/testdata/default/fs/Default.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; contract DefaultAccessTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/fs/Disabled.t.sol b/testdata/default/fs/Disabled.t.sol similarity index 97% rename from testdata/fs/Disabled.t.sol rename to testdata/default/fs/Disabled.t.sol index 4c818d914..4efe9affc 100644 --- a/testdata/fs/Disabled.t.sol +++ b/testdata/default/fs/Disabled.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; contract DisabledTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/fuzz/Fuzz.t.sol b/testdata/default/fuzz/Fuzz.t.sol similarity index 100% rename from testdata/fuzz/Fuzz.t.sol rename to testdata/default/fuzz/Fuzz.t.sol diff --git a/testdata/fuzz/FuzzCollection.t.sol b/testdata/default/fuzz/FuzzCollection.t.sol similarity index 100% rename from testdata/fuzz/FuzzCollection.t.sol rename to testdata/default/fuzz/FuzzCollection.t.sol diff --git a/testdata/fuzz/FuzzFailurePersist.t.sol b/testdata/default/fuzz/FuzzFailurePersist.t.sol similarity index 96% rename from testdata/fuzz/FuzzFailurePersist.t.sol rename to testdata/default/fuzz/FuzzFailurePersist.t.sol index 1f7c4829c..0823f29fb 100644 --- a/testdata/fuzz/FuzzFailurePersist.t.sol +++ b/testdata/default/fuzz/FuzzFailurePersist.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; struct TestTuple { address user; diff --git a/testdata/fuzz/FuzzInt.t.sol b/testdata/default/fuzz/FuzzInt.t.sol similarity index 100% rename from testdata/fuzz/FuzzInt.t.sol rename to testdata/default/fuzz/FuzzInt.t.sol diff --git a/testdata/fuzz/FuzzPositive.t.sol b/testdata/default/fuzz/FuzzPositive.t.sol similarity index 100% rename from testdata/fuzz/FuzzPositive.t.sol rename to testdata/default/fuzz/FuzzPositive.t.sol diff --git a/testdata/fuzz/FuzzUint.t.sol b/testdata/default/fuzz/FuzzUint.t.sol similarity index 100% rename from testdata/fuzz/FuzzUint.t.sol rename to testdata/default/fuzz/FuzzUint.t.sol diff --git a/testdata/fuzz/invariant/common/InvariantAssume.t.sol b/testdata/default/fuzz/invariant/common/InvariantAssume.t.sol similarity index 91% rename from testdata/fuzz/invariant/common/InvariantAssume.t.sol rename to testdata/default/fuzz/invariant/common/InvariantAssume.t.sol index 3065a70a5..4ac0d085c 100644 --- a/testdata/fuzz/invariant/common/InvariantAssume.t.sol +++ b/testdata/default/fuzz/invariant/common/InvariantAssume.t.sol @@ -2,11 +2,11 @@ pragma solidity ^0.8.0; import "ds-test/test.sol"; -import "../../../cheats/Vm.sol"; +import "cheats/Vm.sol"; contract Handler is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); - + function doSomething(uint256 param) public { vm.assume(param != 0); } diff --git a/testdata/fuzz/invariant/common/InvariantCalldataDictionary.t.sol b/testdata/default/fuzz/invariant/common/InvariantCalldataDictionary.t.sol similarity index 98% rename from testdata/fuzz/invariant/common/InvariantCalldataDictionary.t.sol rename to testdata/default/fuzz/invariant/common/InvariantCalldataDictionary.t.sol index c8f87a600..e1486f963 100644 --- a/testdata/fuzz/invariant/common/InvariantCalldataDictionary.t.sol +++ b/testdata/default/fuzz/invariant/common/InvariantCalldataDictionary.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../../../cheats/Vm.sol"; +import "cheats/Vm.sol"; struct FuzzSelector { address addr; diff --git a/testdata/fuzz/invariant/common/InvariantHandlerFailure.t.sol b/testdata/default/fuzz/invariant/common/InvariantHandlerFailure.t.sol similarity index 100% rename from testdata/fuzz/invariant/common/InvariantHandlerFailure.t.sol rename to testdata/default/fuzz/invariant/common/InvariantHandlerFailure.t.sol diff --git a/testdata/fuzz/invariant/common/InvariantInnerContract.t.sol b/testdata/default/fuzz/invariant/common/InvariantInnerContract.t.sol similarity index 100% rename from testdata/fuzz/invariant/common/InvariantInnerContract.t.sol rename to testdata/default/fuzz/invariant/common/InvariantInnerContract.t.sol diff --git a/testdata/fuzz/invariant/common/InvariantPreserveState.t.sol b/testdata/default/fuzz/invariant/common/InvariantPreserveState.t.sol similarity index 97% rename from testdata/fuzz/invariant/common/InvariantPreserveState.t.sol rename to testdata/default/fuzz/invariant/common/InvariantPreserveState.t.sol index 20f34c68d..546980136 100644 --- a/testdata/fuzz/invariant/common/InvariantPreserveState.t.sol +++ b/testdata/default/fuzz/invariant/common/InvariantPreserveState.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../../../cheats/Vm.sol"; +import "cheats/Vm.sol"; struct FuzzSelector { address addr; diff --git a/testdata/fuzz/invariant/common/InvariantReentrancy.t.sol b/testdata/default/fuzz/invariant/common/InvariantReentrancy.t.sol similarity index 100% rename from testdata/fuzz/invariant/common/InvariantReentrancy.t.sol rename to testdata/default/fuzz/invariant/common/InvariantReentrancy.t.sol diff --git a/testdata/fuzz/invariant/common/InvariantShrinkWithAssert.t.sol b/testdata/default/fuzz/invariant/common/InvariantShrinkWithAssert.t.sol similarity index 98% rename from testdata/fuzz/invariant/common/InvariantShrinkWithAssert.t.sol rename to testdata/default/fuzz/invariant/common/InvariantShrinkWithAssert.t.sol index 0ba6d61c8..c189e2507 100644 --- a/testdata/fuzz/invariant/common/InvariantShrinkWithAssert.t.sol +++ b/testdata/default/fuzz/invariant/common/InvariantShrinkWithAssert.t.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.13; import "ds-test/test.sol"; -import "../../../cheats/Vm.sol"; +import "cheats/Vm.sol"; struct FuzzSelector { address addr; diff --git a/testdata/fuzz/invariant/common/InvariantTest1.t.sol b/testdata/default/fuzz/invariant/common/InvariantTest1.t.sol similarity index 100% rename from testdata/fuzz/invariant/common/InvariantTest1.t.sol rename to testdata/default/fuzz/invariant/common/InvariantTest1.t.sol diff --git a/testdata/fuzz/invariant/storage/InvariantStorageTest.t.sol b/testdata/default/fuzz/invariant/storage/InvariantStorageTest.t.sol similarity index 100% rename from testdata/fuzz/invariant/storage/InvariantStorageTest.t.sol rename to testdata/default/fuzz/invariant/storage/InvariantStorageTest.t.sol diff --git a/testdata/fuzz/invariant/target/ExcludeContracts.t.sol b/testdata/default/fuzz/invariant/target/ExcludeContracts.t.sol similarity index 100% rename from testdata/fuzz/invariant/target/ExcludeContracts.t.sol rename to testdata/default/fuzz/invariant/target/ExcludeContracts.t.sol diff --git a/testdata/fuzz/invariant/target/ExcludeSenders.t.sol b/testdata/default/fuzz/invariant/target/ExcludeSenders.t.sol similarity index 100% rename from testdata/fuzz/invariant/target/ExcludeSenders.t.sol rename to testdata/default/fuzz/invariant/target/ExcludeSenders.t.sol diff --git a/testdata/fuzz/invariant/target/TargetContracts.t.sol b/testdata/default/fuzz/invariant/target/TargetContracts.t.sol similarity index 100% rename from testdata/fuzz/invariant/target/TargetContracts.t.sol rename to testdata/default/fuzz/invariant/target/TargetContracts.t.sol diff --git a/testdata/fuzz/invariant/target/TargetInterfaces.t.sol b/testdata/default/fuzz/invariant/target/TargetInterfaces.t.sol similarity index 100% rename from testdata/fuzz/invariant/target/TargetInterfaces.t.sol rename to testdata/default/fuzz/invariant/target/TargetInterfaces.t.sol diff --git a/testdata/fuzz/invariant/target/TargetSelectors.t.sol b/testdata/default/fuzz/invariant/target/TargetSelectors.t.sol similarity index 100% rename from testdata/fuzz/invariant/target/TargetSelectors.t.sol rename to testdata/default/fuzz/invariant/target/TargetSelectors.t.sol diff --git a/testdata/fuzz/invariant/target/TargetSenders.t.sol b/testdata/default/fuzz/invariant/target/TargetSenders.t.sol similarity index 100% rename from testdata/fuzz/invariant/target/TargetSenders.t.sol rename to testdata/default/fuzz/invariant/target/TargetSenders.t.sol diff --git a/testdata/fuzz/invariant/targetAbi/ExcludeArtifacts.t.sol b/testdata/default/fuzz/invariant/targetAbi/ExcludeArtifacts.t.sol similarity index 91% rename from testdata/fuzz/invariant/targetAbi/ExcludeArtifacts.t.sol rename to testdata/default/fuzz/invariant/targetAbi/ExcludeArtifacts.t.sol index ce81a51cf..bf457ab17 100644 --- a/testdata/fuzz/invariant/targetAbi/ExcludeArtifacts.t.sol +++ b/testdata/default/fuzz/invariant/targetAbi/ExcludeArtifacts.t.sol @@ -35,7 +35,7 @@ contract ExcludeArtifacts is DSTest { function excludeArtifacts() public returns (string[] memory) { string[] memory abis = new string[](1); - abis[0] = "fuzz/invariant/targetAbi/ExcludeArtifacts.t.sol:Excluded"; + abis[0] = "default/fuzz/invariant/targetAbi/ExcludeArtifacts.t.sol:Excluded"; return abis; } diff --git a/testdata/fuzz/invariant/targetAbi/TargetArtifactSelectors.t.sol b/testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors.t.sol similarity index 87% rename from testdata/fuzz/invariant/targetAbi/TargetArtifactSelectors.t.sol rename to testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors.t.sol index e82e7b1a3..d7c8bcdfa 100644 --- a/testdata/fuzz/invariant/targetAbi/TargetArtifactSelectors.t.sol +++ b/testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors.t.sol @@ -31,7 +31,7 @@ contract TargetArtifactSelectors is DSTest { FuzzAbiSelector[] memory targets = new FuzzAbiSelector[](1); bytes4[] memory selectors = new bytes4[](1); selectors[0] = Hi.no_change.selector; - targets[0] = FuzzAbiSelector("fuzz/invariant/targetAbi/TargetArtifactSelectors.t.sol:Hi", selectors); + targets[0] = FuzzAbiSelector("default/fuzz/invariant/targetAbi/TargetArtifactSelectors.t.sol:Hi", selectors); return targets; } diff --git a/testdata/fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol b/testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol similarity index 84% rename from testdata/fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol rename to testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol index 5592aa849..573350c6e 100644 --- a/testdata/fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol +++ b/testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol @@ -51,11 +51,13 @@ contract TargetArtifactSelectors2 is DSTest { bytes4[] memory selectors_child = new bytes4[](1); selectors_child[0] = Child.change_parent.selector; - targets[0] = FuzzAbiSelector("fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol:Child", selectors_child); + targets[0] = + FuzzAbiSelector("default/fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol:Child", selectors_child); bytes4[] memory selectors_parent = new bytes4[](1); selectors_parent[0] = Parent.create.selector; - targets[1] = FuzzAbiSelector("fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol:Parent", selectors_parent); + targets[1] = + FuzzAbiSelector("default/fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol:Parent", selectors_parent); return targets; } diff --git a/testdata/fuzz/invariant/targetAbi/TargetArtifacts.t.sol b/testdata/default/fuzz/invariant/targetAbi/TargetArtifacts.t.sol similarity index 91% rename from testdata/fuzz/invariant/targetAbi/TargetArtifacts.t.sol rename to testdata/default/fuzz/invariant/targetAbi/TargetArtifacts.t.sol index d3eb2cf9d..ea86ab135 100644 --- a/testdata/fuzz/invariant/targetAbi/TargetArtifacts.t.sol +++ b/testdata/default/fuzz/invariant/targetAbi/TargetArtifacts.t.sol @@ -30,7 +30,7 @@ contract TargetArtifacts is DSTest { function targetArtifacts() public returns (string[] memory) { string[] memory abis = new string[](1); - abis[0] = "fuzz/invariant/targetAbi/TargetArtifacts.t.sol:Targeted"; + abis[0] = "default/fuzz/invariant/targetAbi/TargetArtifacts.t.sol:Targeted"; return abis; } diff --git a/testdata/inline/FuzzInlineConf.t.sol b/testdata/default/inline/FuzzInlineConf.t.sol similarity index 100% rename from testdata/inline/FuzzInlineConf.t.sol rename to testdata/default/inline/FuzzInlineConf.t.sol diff --git a/testdata/inline/InvariantInlineConf.t.sol b/testdata/default/inline/InvariantInlineConf.t.sol similarity index 100% rename from testdata/inline/InvariantInlineConf.t.sol rename to testdata/default/inline/InvariantInlineConf.t.sol diff --git a/testdata/linking/cycle/Cycle.t.sol b/testdata/default/linking/cycle/Cycle.t.sol similarity index 100% rename from testdata/linking/cycle/Cycle.t.sol rename to testdata/default/linking/cycle/Cycle.t.sol diff --git a/testdata/linking/duplicate/Duplicate.t.sol b/testdata/default/linking/duplicate/Duplicate.t.sol similarity index 100% rename from testdata/linking/duplicate/Duplicate.t.sol rename to testdata/default/linking/duplicate/Duplicate.t.sol diff --git a/testdata/linking/nested/Nested.t.sol b/testdata/default/linking/nested/Nested.t.sol similarity index 100% rename from testdata/linking/nested/Nested.t.sol rename to testdata/default/linking/nested/Nested.t.sol diff --git a/testdata/linking/simple/Simple.t.sol b/testdata/default/linking/simple/Simple.t.sol similarity index 100% rename from testdata/linking/simple/Simple.t.sol rename to testdata/default/linking/simple/Simple.t.sol diff --git a/testdata/logs/DebugLogs.t.sol b/testdata/default/logs/DebugLogs.t.sol similarity index 100% rename from testdata/logs/DebugLogs.t.sol rename to testdata/default/logs/DebugLogs.t.sol diff --git a/testdata/logs/HardhatLogs.t.sol b/testdata/default/logs/HardhatLogs.t.sol similarity index 100% rename from testdata/logs/HardhatLogs.t.sol rename to testdata/default/logs/HardhatLogs.t.sol diff --git a/testdata/logs/console.sol b/testdata/default/logs/console.sol similarity index 100% rename from testdata/logs/console.sol rename to testdata/default/logs/console.sol diff --git a/testdata/repros/Issue2623.t.sol b/testdata/default/repros/Issue2623.t.sol similarity index 95% rename from testdata/repros/Issue2623.t.sol rename to testdata/default/repros/Issue2623.t.sol index cf91d10a5..8534aeeaf 100644 --- a/testdata/repros/Issue2623.t.sol +++ b/testdata/default/repros/Issue2623.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/2623 contract Issue2623Test is DSTest { diff --git a/testdata/repros/Issue2629.t.sol b/testdata/default/repros/Issue2629.t.sol similarity index 96% rename from testdata/repros/Issue2629.t.sol rename to testdata/default/repros/Issue2629.t.sol index 296ebfc32..a1f430858 100644 --- a/testdata/repros/Issue2629.t.sol +++ b/testdata/default/repros/Issue2629.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/2629 contract Issue2629Test is DSTest { diff --git a/testdata/repros/Issue2723.t.sol b/testdata/default/repros/Issue2723.t.sol similarity index 95% rename from testdata/repros/Issue2723.t.sol rename to testdata/default/repros/Issue2723.t.sol index 6ecd7df8d..c260f9467 100644 --- a/testdata/repros/Issue2723.t.sol +++ b/testdata/default/repros/Issue2723.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/2723 contract Issue2723Test is DSTest { diff --git a/testdata/repros/Issue2898.t.sol b/testdata/default/repros/Issue2898.t.sol similarity index 96% rename from testdata/repros/Issue2898.t.sol rename to testdata/default/repros/Issue2898.t.sol index 6f6eb5e35..23de35bcd 100644 --- a/testdata/repros/Issue2898.t.sol +++ b/testdata/default/repros/Issue2898.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; import "../logs/console.sol"; // https://github.com/foundry-rs/foundry/issues/2898 diff --git a/testdata/repros/Issue2956.t.sol b/testdata/default/repros/Issue2956.t.sol similarity index 97% rename from testdata/repros/Issue2956.t.sol rename to testdata/default/repros/Issue2956.t.sol index 8e9841e2b..f77340da4 100644 --- a/testdata/repros/Issue2956.t.sol +++ b/testdata/default/repros/Issue2956.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/2956 contract Issue2956Test is DSTest { diff --git a/testdata/repros/Issue2984.t.sol b/testdata/default/repros/Issue2984.t.sol similarity index 96% rename from testdata/repros/Issue2984.t.sol rename to testdata/default/repros/Issue2984.t.sol index 223866926..8e55d5dae 100644 --- a/testdata/repros/Issue2984.t.sol +++ b/testdata/default/repros/Issue2984.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/2984 contract Issue2984Test is DSTest { diff --git a/testdata/repros/Issue3055.t.sol b/testdata/default/repros/Issue3055.t.sol similarity index 97% rename from testdata/repros/Issue3055.t.sol rename to testdata/default/repros/Issue3055.t.sol index 0a94e4d2a..cacf5282f 100644 --- a/testdata/repros/Issue3055.t.sol +++ b/testdata/default/repros/Issue3055.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/3055 contract Issue3055Test is DSTest { diff --git a/testdata/repros/Issue3077.t.sol b/testdata/default/repros/Issue3077.t.sol similarity index 97% rename from testdata/repros/Issue3077.t.sol rename to testdata/default/repros/Issue3077.t.sol index 36cfb071c..cc76b57b6 100644 --- a/testdata/repros/Issue3077.t.sol +++ b/testdata/default/repros/Issue3077.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/3077 abstract contract ZeroState is DSTest { diff --git a/testdata/repros/Issue3110.t.sol b/testdata/default/repros/Issue3110.t.sol similarity index 97% rename from testdata/repros/Issue3110.t.sol rename to testdata/default/repros/Issue3110.t.sol index 259a467d3..7a2622427 100644 --- a/testdata/repros/Issue3110.t.sol +++ b/testdata/default/repros/Issue3110.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/3110 abstract contract ZeroState is DSTest { diff --git a/testdata/repros/Issue3119.t.sol b/testdata/default/repros/Issue3119.t.sol similarity index 96% rename from testdata/repros/Issue3119.t.sol rename to testdata/default/repros/Issue3119.t.sol index 80f539660..5c94b4c5f 100644 --- a/testdata/repros/Issue3119.t.sol +++ b/testdata/default/repros/Issue3119.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/3119 contract Issue3119Test is DSTest { diff --git a/testdata/repros/Issue3189.t.sol b/testdata/default/repros/Issue3189.t.sol similarity index 96% rename from testdata/repros/Issue3189.t.sol rename to testdata/default/repros/Issue3189.t.sol index 771b8f514..27ea0ac51 100644 --- a/testdata/repros/Issue3189.t.sol +++ b/testdata/default/repros/Issue3189.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/3189 contract MyContract { diff --git a/testdata/repros/Issue3190.t.sol b/testdata/default/repros/Issue3190.t.sol similarity index 94% rename from testdata/repros/Issue3190.t.sol rename to testdata/default/repros/Issue3190.t.sol index b5d5c70e9..4a9add5f5 100644 --- a/testdata/repros/Issue3190.t.sol +++ b/testdata/default/repros/Issue3190.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; import "../logs/console.sol"; // https://github.com/foundry-rs/foundry/issues/3190 diff --git a/testdata/repros/Issue3192.t.sol b/testdata/default/repros/Issue3192.t.sol similarity index 95% rename from testdata/repros/Issue3192.t.sol rename to testdata/default/repros/Issue3192.t.sol index 36841fd08..0deb22f49 100644 --- a/testdata/repros/Issue3192.t.sol +++ b/testdata/default/repros/Issue3192.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/3192 contract Issue3192Test is DSTest { diff --git a/testdata/repros/Issue3220.t.sol b/testdata/default/repros/Issue3220.t.sol similarity index 97% rename from testdata/repros/Issue3220.t.sol rename to testdata/default/repros/Issue3220.t.sol index acf75352d..b88d997c1 100644 --- a/testdata/repros/Issue3220.t.sol +++ b/testdata/default/repros/Issue3220.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/3220 contract Issue3220Test is DSTest { diff --git a/testdata/repros/Issue3221.t.sol b/testdata/default/repros/Issue3221.t.sol similarity index 97% rename from testdata/repros/Issue3221.t.sol rename to testdata/default/repros/Issue3221.t.sol index cc6f8039e..9fbc51f60 100644 --- a/testdata/repros/Issue3221.t.sol +++ b/testdata/default/repros/Issue3221.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/3221 contract Issue3221Test is DSTest { diff --git a/testdata/repros/Issue3223.t.sol b/testdata/default/repros/Issue3223.t.sol similarity index 97% rename from testdata/repros/Issue3223.t.sol rename to testdata/default/repros/Issue3223.t.sol index 14d46838e..4408d24ee 100644 --- a/testdata/repros/Issue3223.t.sol +++ b/testdata/default/repros/Issue3223.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/3223 contract Issue3223Test is DSTest { diff --git a/testdata/repros/Issue3347.t.sol b/testdata/default/repros/Issue3347.t.sol similarity index 91% rename from testdata/repros/Issue3347.t.sol rename to testdata/default/repros/Issue3347.t.sol index 66657ea62..ed9be5f36 100644 --- a/testdata/repros/Issue3347.t.sol +++ b/testdata/default/repros/Issue3347.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/3347 contract Issue3347Test is DSTest { diff --git a/testdata/repros/Issue3437.t.sol b/testdata/default/repros/Issue3437.t.sol similarity index 93% rename from testdata/repros/Issue3437.t.sol rename to testdata/default/repros/Issue3437.t.sol index acd02ada7..69f56ca82 100644 --- a/testdata/repros/Issue3437.t.sol +++ b/testdata/default/repros/Issue3437.t.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/3437 contract Issue3347Test is DSTest { diff --git a/testdata/repros/Issue3596.t.sol b/testdata/default/repros/Issue3596.t.sol similarity index 96% rename from testdata/repros/Issue3596.t.sol rename to testdata/default/repros/Issue3596.t.sol index 9a942d342..04ee470d7 100644 --- a/testdata/repros/Issue3596.t.sol +++ b/testdata/default/repros/Issue3596.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/3596 contract Issue3596Test is DSTest { diff --git a/testdata/repros/Issue3653.t.sol b/testdata/default/repros/Issue3653.t.sol similarity index 95% rename from testdata/repros/Issue3653.t.sol rename to testdata/default/repros/Issue3653.t.sol index 5022af678..b86f84c3e 100644 --- a/testdata/repros/Issue3653.t.sol +++ b/testdata/default/repros/Issue3653.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/3653 contract Issue3653Test is DSTest { diff --git a/testdata/repros/Issue3661.t.sol b/testdata/default/repros/Issue3661.t.sol similarity index 100% rename from testdata/repros/Issue3661.t.sol rename to testdata/default/repros/Issue3661.t.sol diff --git a/testdata/repros/Issue3674.t.sol b/testdata/default/repros/Issue3674.t.sol similarity index 94% rename from testdata/repros/Issue3674.t.sol rename to testdata/default/repros/Issue3674.t.sol index 57216e805..f13f7f162 100644 --- a/testdata/repros/Issue3674.t.sol +++ b/testdata/default/repros/Issue3674.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/3674 contract Issue3674Test is DSTest { diff --git a/testdata/repros/Issue3685.t.sol b/testdata/default/repros/Issue3685.t.sol similarity index 97% rename from testdata/repros/Issue3685.t.sol rename to testdata/default/repros/Issue3685.t.sol index 748367f65..7e8f886d8 100644 --- a/testdata/repros/Issue3685.t.sol +++ b/testdata/default/repros/Issue3685.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; import "../logs/console.sol"; // https://github.com/foundry-rs/foundry/issues/3685 diff --git a/testdata/repros/Issue3703.t.sol b/testdata/default/repros/Issue3703.t.sol similarity index 98% rename from testdata/repros/Issue3703.t.sol rename to testdata/default/repros/Issue3703.t.sol index c941fe223..06ce6bcbe 100644 --- a/testdata/repros/Issue3703.t.sol +++ b/testdata/default/repros/Issue3703.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/3703 contract Issue3703Test is DSTest { diff --git a/testdata/repros/Issue3708.t.sol b/testdata/default/repros/Issue3708.t.sol similarity index 97% rename from testdata/repros/Issue3708.t.sol rename to testdata/default/repros/Issue3708.t.sol index 7cf57cd0e..f5bdf48bf 100644 --- a/testdata/repros/Issue3708.t.sol +++ b/testdata/default/repros/Issue3708.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/3708 contract Issue3708Test is DSTest { diff --git a/testdata/repros/Issue3723.t.sol b/testdata/default/repros/Issue3723.t.sol similarity index 93% rename from testdata/repros/Issue3723.t.sol rename to testdata/default/repros/Issue3723.t.sol index 0f5d6694b..9ea3fe733 100644 --- a/testdata/repros/Issue3723.t.sol +++ b/testdata/default/repros/Issue3723.t.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/3723 contract Issue3723Test is DSTest { diff --git a/testdata/repros/Issue3753.t.sol b/testdata/default/repros/Issue3753.t.sol similarity index 94% rename from testdata/repros/Issue3753.t.sol rename to testdata/default/repros/Issue3753.t.sol index 1edbb42b8..7af774baf 100644 --- a/testdata/repros/Issue3753.t.sol +++ b/testdata/default/repros/Issue3753.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/3753 contract Issue3753Test is DSTest { diff --git a/testdata/repros/Issue3792.t.sol b/testdata/default/repros/Issue3792.t.sol similarity index 96% rename from testdata/repros/Issue3792.t.sol rename to testdata/default/repros/Issue3792.t.sol index e01671f37..723329f93 100644 --- a/testdata/repros/Issue3792.t.sol +++ b/testdata/default/repros/Issue3792.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; contract Config { address public test = 0xcBa28b38103307Ec8dA98377ffF9816C164f9AFa; diff --git a/testdata/repros/Issue4402.t.sol b/testdata/default/repros/Issue4402.t.sol similarity index 99% rename from testdata/repros/Issue4402.t.sol rename to testdata/default/repros/Issue4402.t.sol index c69285b7f..3bf0f33fb 100644 --- a/testdata/repros/Issue4402.t.sol +++ b/testdata/default/repros/Issue4402.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/4402 contract Issue4402Test is DSTest { diff --git a/testdata/repros/Issue4586.t.sol b/testdata/default/repros/Issue4586.t.sol similarity index 97% rename from testdata/repros/Issue4586.t.sol rename to testdata/default/repros/Issue4586.t.sol index 6eb615c02..a41ba7a04 100644 --- a/testdata/repros/Issue4586.t.sol +++ b/testdata/default/repros/Issue4586.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/4586 contract Issue4586Test is DSTest { diff --git a/testdata/repros/Issue4630.t.sol b/testdata/default/repros/Issue4630.t.sol similarity index 96% rename from testdata/repros/Issue4630.t.sol rename to testdata/default/repros/Issue4630.t.sol index 3979d5072..4b9fe9c9b 100644 --- a/testdata/repros/Issue4630.t.sol +++ b/testdata/default/repros/Issue4630.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/4630 contract Issue4630Test is DSTest { diff --git a/testdata/repros/Issue4640.t.sol b/testdata/default/repros/Issue4640.t.sol similarity index 94% rename from testdata/repros/Issue4640.t.sol rename to testdata/default/repros/Issue4640.t.sol index b16f4d071..a875d000d 100644 --- a/testdata/repros/Issue4640.t.sol +++ b/testdata/default/repros/Issue4640.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/4640 contract Issue4640Test is DSTest { diff --git a/testdata/repros/Issue4832.t.sol b/testdata/default/repros/Issue4832.t.sol similarity index 92% rename from testdata/repros/Issue4832.t.sol rename to testdata/default/repros/Issue4832.t.sol index 72f846873..192d805c1 100644 --- a/testdata/repros/Issue4832.t.sol +++ b/testdata/default/repros/Issue4832.t.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/4832 contract Issue4832Test is DSTest { diff --git a/testdata/repros/Issue5038.t.sol b/testdata/default/repros/Issue5038.t.sol similarity index 99% rename from testdata/repros/Issue5038.t.sol rename to testdata/default/repros/Issue5038.t.sol index bee48f0b7..834f82783 100644 --- a/testdata/repros/Issue5038.t.sol +++ b/testdata/default/repros/Issue5038.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; struct Value { uint256 value; diff --git a/testdata/repros/Issue5529.t.sol b/testdata/default/repros/Issue5529.t.sol similarity index 97% rename from testdata/repros/Issue5529.t.sol rename to testdata/default/repros/Issue5529.t.sol index 35e714001..14ec7cfdb 100644 --- a/testdata/repros/Issue5529.t.sol +++ b/testdata/default/repros/Issue5529.t.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.13; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; contract Counter { uint256 public number; diff --git a/testdata/repros/Issue5808.t.sol b/testdata/default/repros/Issue5808.t.sol similarity index 95% rename from testdata/repros/Issue5808.t.sol rename to testdata/default/repros/Issue5808.t.sol index 1914264bf..66ea82b30 100644 --- a/testdata/repros/Issue5808.t.sol +++ b/testdata/default/repros/Issue5808.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/5808 contract Issue5808Test is DSTest { diff --git a/testdata/repros/Issue5929.t.sol b/testdata/default/repros/Issue5929.t.sol similarity index 95% rename from testdata/repros/Issue5929.t.sol rename to testdata/default/repros/Issue5929.t.sol index 53ca10ae8..f1009f03b 100644 --- a/testdata/repros/Issue5929.t.sol +++ b/testdata/default/repros/Issue5929.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/5929 contract Issue5929Test is DSTest { diff --git a/testdata/repros/Issue5935.t.sol b/testdata/default/repros/Issue5935.t.sol similarity index 97% rename from testdata/repros/Issue5935.t.sol rename to testdata/default/repros/Issue5935.t.sol index 8d6f8687b..95b6f8fd5 100644 --- a/testdata/repros/Issue5935.t.sol +++ b/testdata/default/repros/Issue5935.t.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.0 <0.9.0; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; contract SimpleStorage { uint256 public value; diff --git a/testdata/repros/Issue5948.t.sol b/testdata/default/repros/Issue5948.t.sol similarity index 97% rename from testdata/repros/Issue5948.t.sol rename to testdata/default/repros/Issue5948.t.sol index b496caf66..992099fb1 100644 --- a/testdata/repros/Issue5948.t.sol +++ b/testdata/default/repros/Issue5948.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/5948 contract Issue5948Test is DSTest { diff --git a/testdata/repros/Issue6006.t.sol b/testdata/default/repros/Issue6006.t.sol similarity index 97% rename from testdata/repros/Issue6006.t.sol rename to testdata/default/repros/Issue6006.t.sol index 63e2cd5c6..dac37cd24 100644 --- a/testdata/repros/Issue6006.t.sol +++ b/testdata/default/repros/Issue6006.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/6006 contract Issue6066Test is DSTest { diff --git a/testdata/repros/Issue6032.t.sol b/testdata/default/repros/Issue6032.t.sol similarity index 98% rename from testdata/repros/Issue6032.t.sol rename to testdata/default/repros/Issue6032.t.sol index c9f82f209..fc230c47e 100644 --- a/testdata/repros/Issue6032.t.sol +++ b/testdata/default/repros/Issue6032.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/6032 contract Issue6032Test is DSTest { diff --git a/testdata/repros/Issue6070.t.sol b/testdata/default/repros/Issue6070.t.sol similarity index 95% rename from testdata/repros/Issue6070.t.sol rename to testdata/default/repros/Issue6070.t.sol index db330f4b1..e699f5ca9 100644 --- a/testdata/repros/Issue6070.t.sol +++ b/testdata/default/repros/Issue6070.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/6070 contract Issue6066Test is DSTest { diff --git a/testdata/repros/Issue6115.t.sol b/testdata/default/repros/Issue6115.t.sol similarity index 100% rename from testdata/repros/Issue6115.t.sol rename to testdata/default/repros/Issue6115.t.sol diff --git a/testdata/repros/Issue6170.t.sol b/testdata/default/repros/Issue6170.t.sol similarity index 95% rename from testdata/repros/Issue6170.t.sol rename to testdata/default/repros/Issue6170.t.sol index 43f2067d6..543ca3142 100644 --- a/testdata/repros/Issue6170.t.sol +++ b/testdata/default/repros/Issue6170.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; contract Emitter { event Values(uint256 indexed a, uint256 indexed b); diff --git a/testdata/repros/Issue6180.t.sol b/testdata/default/repros/Issue6180.t.sol similarity index 95% rename from testdata/repros/Issue6180.t.sol rename to testdata/default/repros/Issue6180.t.sol index 7ff068434..591c60bdf 100644 --- a/testdata/repros/Issue6180.t.sol +++ b/testdata/default/repros/Issue6180.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/6180 contract Issue6180Test is DSTest { diff --git a/testdata/repros/Issue6293.t.sol b/testdata/default/repros/Issue6293.t.sol similarity index 93% rename from testdata/repros/Issue6293.t.sol rename to testdata/default/repros/Issue6293.t.sol index c90f56806..303e8fbbe 100644 --- a/testdata/repros/Issue6293.t.sol +++ b/testdata/default/repros/Issue6293.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/6293 contract Issue6293Test is DSTest { diff --git a/testdata/repros/Issue6355.t.sol b/testdata/default/repros/Issue6355.t.sol similarity index 96% rename from testdata/repros/Issue6355.t.sol rename to testdata/default/repros/Issue6355.t.sol index 7271011c6..d7830152a 100644 --- a/testdata/repros/Issue6355.t.sol +++ b/testdata/default/repros/Issue6355.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/6355 contract Issue6355Test is DSTest { diff --git a/testdata/repros/Issue6437.t.sol b/testdata/default/repros/Issue6437.t.sol similarity index 97% rename from testdata/repros/Issue6437.t.sol rename to testdata/default/repros/Issue6437.t.sol index 529c96d2e..c18af2dfd 100644 --- a/testdata/repros/Issue6437.t.sol +++ b/testdata/default/repros/Issue6437.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/6437 contract Issue6437Test is DSTest { diff --git a/testdata/repros/Issue6501.t.sol b/testdata/default/repros/Issue6501.t.sol similarity index 100% rename from testdata/repros/Issue6501.t.sol rename to testdata/default/repros/Issue6501.t.sol diff --git a/testdata/repros/Issue6538.t.sol b/testdata/default/repros/Issue6538.t.sol similarity index 95% rename from testdata/repros/Issue6538.t.sol rename to testdata/default/repros/Issue6538.t.sol index d174449c7..d83bbc850 100644 --- a/testdata/repros/Issue6538.t.sol +++ b/testdata/default/repros/Issue6538.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/6538 contract Issue6538Test is DSTest { diff --git a/testdata/repros/Issue6554.t.sol b/testdata/default/repros/Issue6554.t.sol similarity index 61% rename from testdata/repros/Issue6554.t.sol rename to testdata/default/repros/Issue6554.t.sol index be7af3d9d..c13ebc4a7 100644 --- a/testdata/repros/Issue6554.t.sol +++ b/testdata/default/repros/Issue6554.t.sol @@ -2,15 +2,15 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/6554 contract Issue6554Test is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); function testPermissions() public { - vm.writeFile("./out/Issue6554.t.sol/cachedFile.txt", "cached data"); - string memory content = vm.readFile("./out/Issue6554.t.sol/cachedFile.txt"); + vm.writeFile("./out/default/Issue6554.t.sol/cachedFile.txt", "cached data"); + string memory content = vm.readFile("./out/default/Issue6554.t.sol/cachedFile.txt"); assertEq(content, "cached data"); } } diff --git a/testdata/repros/Issue6616.t.sol b/testdata/default/repros/Issue6616.t.sol similarity index 96% rename from testdata/repros/Issue6616.t.sol rename to testdata/default/repros/Issue6616.t.sol index 6c9991f0e..24fa00e21 100644 --- a/testdata/repros/Issue6616.t.sol +++ b/testdata/default/repros/Issue6616.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/6616 contract Issue6616Test is DSTest { diff --git a/testdata/repros/Issue6634.t.sol b/testdata/default/repros/Issue6634.t.sol similarity index 99% rename from testdata/repros/Issue6634.t.sol rename to testdata/default/repros/Issue6634.t.sol index 3b1acb9c1..22294f6df 100644 --- a/testdata/repros/Issue6634.t.sol +++ b/testdata/default/repros/Issue6634.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; import "../logs/console.sol"; contract Box { diff --git a/testdata/repros/Issue6759.t.sol b/testdata/default/repros/Issue6759.t.sol similarity index 95% rename from testdata/repros/Issue6759.t.sol rename to testdata/default/repros/Issue6759.t.sol index 45a2f42b0..a8039035e 100644 --- a/testdata/repros/Issue6759.t.sol +++ b/testdata/default/repros/Issue6759.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; // https://github.com/foundry-rs/foundry/issues/6759 contract Issue6759Test is DSTest { diff --git a/testdata/repros/Issue6966.t.sol b/testdata/default/repros/Issue6966.t.sol similarity index 100% rename from testdata/repros/Issue6966.t.sol rename to testdata/default/repros/Issue6966.t.sol diff --git a/testdata/script/broadcast/deploy.sol/31337/run-latest.json b/testdata/default/script/broadcast/deploy.sol/31337/run-latest.json similarity index 100% rename from testdata/script/broadcast/deploy.sol/31337/run-latest.json rename to testdata/default/script/broadcast/deploy.sol/31337/run-latest.json diff --git a/testdata/script/deploy.sol b/testdata/default/script/deploy.sol similarity index 89% rename from testdata/script/deploy.sol rename to testdata/default/script/deploy.sol index f05afe487..013e009d3 100644 --- a/testdata/script/deploy.sol +++ b/testdata/default/script/deploy.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity 0.8.18; -import {DSTest} from "../lib/ds-test/src/test.sol"; -import {Vm} from "../cheats/Vm.sol"; +import {DSTest} from "lib/ds-test/src/test.sol"; +import {Vm} from "cheats/Vm.sol"; contract Greeter { string name; diff --git a/testdata/spec/ShanghaiCompat.t.sol b/testdata/default/spec/ShanghaiCompat.t.sol similarity index 96% rename from testdata/spec/ShanghaiCompat.t.sol rename to testdata/default/spec/ShanghaiCompat.t.sol index f490441ef..02856a88f 100644 --- a/testdata/spec/ShanghaiCompat.t.sol +++ b/testdata/default/spec/ShanghaiCompat.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.20; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; contract ShanghaiCompat is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); diff --git a/testdata/trace/ConflictingSignatures.t.sol b/testdata/default/trace/ConflictingSignatures.t.sol similarity index 97% rename from testdata/trace/ConflictingSignatures.t.sol rename to testdata/default/trace/ConflictingSignatures.t.sol index 896390212..67dfd5d3a 100644 --- a/testdata/trace/ConflictingSignatures.t.sol +++ b/testdata/default/trace/ConflictingSignatures.t.sol @@ -1,7 +1,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; contract ReturnsNothing { function func() public pure {} diff --git a/testdata/trace/Trace.t.sol b/testdata/default/trace/Trace.t.sol similarity index 98% rename from testdata/trace/Trace.t.sol rename to testdata/default/trace/Trace.t.sol index 2eefba7ba..d513e8637 100644 --- a/testdata/trace/Trace.t.sol +++ b/testdata/default/trace/Trace.t.sol @@ -1,7 +1,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -import "../cheats/Vm.sol"; +import "cheats/Vm.sol"; contract RecursiveCall { TraceTest factory; From e5318c3054e5f883d1467da9fae5d29567a03d43 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 20 Mar 2024 18:34:30 +0100 Subject: [PATCH 097/430] chore: bump svm-rs (#7458) --- Cargo.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 29154610a..ba462d889 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3003,7 +3003,7 @@ dependencies = [ "similar", "solang-parser", "strum 0.26.2", - "svm-rs 0.4.0", + "svm-rs 0.4.1", "tempfile", "thiserror", "tokio", @@ -3312,7 +3312,7 @@ dependencies = [ "serde_json", "sha2 0.10.8", "solang-parser", - "svm-rs 0.4.0", + "svm-rs 0.4.1", "svm-rs-builds", "tempfile", "thiserror", @@ -7418,9 +7418,9 @@ dependencies = [ [[package]] name = "svm-rs" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde88464d5718a437e9f6be54cf3771837a111bed305c4f4f9d3497471e96249" +checksum = "9bd5e919f01c9280dce59ab66296449d0e9144b8472b8892fbacf9612998b653" dependencies = [ "const-hex", "dirs 5.0.1", @@ -7438,15 +7438,15 @@ dependencies = [ [[package]] name = "svm-rs-builds" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ff43323737122457c266fe28a454635edd9cd15f8b6277251eb4703ef54f829" +checksum = "5bcf7abc816dd67daf88fccfb835118b0e71cf8cc3e1d0e120893e139799df6c" dependencies = [ "build_const", "const-hex", "semver 1.0.22", "serde_json", - "svm-rs 0.4.0", + "svm-rs 0.4.1", ] [[package]] From db76f71f5c2ba5c1440944a3d01bdcd45ae106b7 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 21 Mar 2024 08:21:37 +0100 Subject: [PATCH 098/430] feat: write instruction result when displaying call traces --- crates/evm/traces/src/lib.rs | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/crates/evm/traces/src/lib.rs b/crates/evm/traces/src/lib.rs index d7d55696f..26e1aeb2d 100644 --- a/crates/evm/traces/src/lib.rs +++ b/crates/evm/traces/src/lib.rs @@ -122,23 +122,20 @@ pub async fn render_trace_arena( // Display trace return data let color = trace_color(&node.trace); - write!(s, "{child}{EDGE}{}", color.paint(RETURN))?; - if node.trace.kind.is_any_create() { - match &return_data { - None => { - writeln!(s, "{} bytes of code", node.trace.output.len())?; - } - Some(val) => { - writeln!(s, "{val}")?; - } + write!( + s, + "{child}{EDGE}{}{}", + color.paint(RETURN), + color.paint(format!("[{:?}] ", node.trace.status)) + )?; + match return_data { + Some(val) => writeln!(s, "{val}"), + None if node.trace.kind.is_any_create() => { + writeln!(s, "{} bytes of code", node.trace.output.len()) } - } else { - match &return_data { - None if node.trace.output.is_empty() => writeln!(s, "()")?, - None => writeln!(s, "{}", node.trace.output)?, - Some(val) => writeln!(s, "{val}")?, - } - } + None if node.trace.output.is_empty() => Ok(()), + None => writeln!(s, "{}", node.trace.output), + }?; Ok(()) } From 3e565e88b618c8a78d26537ce9a1adcd6460123d Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 21 Mar 2024 08:22:41 +0100 Subject: [PATCH 099/430] Revert "feat: write instruction result when displaying call traces" This reverts commit db76f71f5c2ba5c1440944a3d01bdcd45ae106b7. --- crates/evm/traces/src/lib.rs | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/crates/evm/traces/src/lib.rs b/crates/evm/traces/src/lib.rs index 26e1aeb2d..d7d55696f 100644 --- a/crates/evm/traces/src/lib.rs +++ b/crates/evm/traces/src/lib.rs @@ -122,20 +122,23 @@ pub async fn render_trace_arena( // Display trace return data let color = trace_color(&node.trace); - write!( - s, - "{child}{EDGE}{}{}", - color.paint(RETURN), - color.paint(format!("[{:?}] ", node.trace.status)) - )?; - match return_data { - Some(val) => writeln!(s, "{val}"), - None if node.trace.kind.is_any_create() => { - writeln!(s, "{} bytes of code", node.trace.output.len()) + write!(s, "{child}{EDGE}{}", color.paint(RETURN))?; + if node.trace.kind.is_any_create() { + match &return_data { + None => { + writeln!(s, "{} bytes of code", node.trace.output.len())?; + } + Some(val) => { + writeln!(s, "{val}")?; + } } - None if node.trace.output.is_empty() => Ok(()), - None => writeln!(s, "{}", node.trace.output), - }?; + } else { + match &return_data { + None if node.trace.output.is_empty() => writeln!(s, "()")?, + None => writeln!(s, "{}", node.trace.output)?, + Some(val) => writeln!(s, "{val}")?, + } + } Ok(()) } From b342ff2c72e2872c2bb3f8f2d9fcec15d679fb3c Mon Sep 17 00:00:00 2001 From: Yotam Bar-On Date: Thu, 21 Mar 2024 15:11:26 +0200 Subject: [PATCH 100/430] feat(cheatcodes) vm.prompt: Prompt user for interactive input (#7012) * Implement vm.prompt cheatcode * chore: speedup prompt test locally * move prompt.sol --------- Co-authored-by: Matthias Seitz --- Cargo.lock | 3 ++ crates/cheatcodes/Cargo.toml | 1 + crates/cheatcodes/assets/cheatcodes.json | 40 +++++++++++++++++++ crates/cheatcodes/spec/src/vm.rs | 10 +++++ crates/cheatcodes/src/config.rs | 5 +++ crates/cheatcodes/src/fs.rs | 50 ++++++++++++++++++++++++ crates/config/README.md | 1 + crates/config/src/lib.rs | 3 ++ crates/evm/traces/src/decoder/mod.rs | 1 + crates/forge/tests/cli/config.rs | 1 + crates/forge/tests/it/test_helpers.rs | 3 ++ testdata/cheats/Vm.sol | 2 + testdata/default/cheats/Prompt.t.sol | 18 +++++++++ 13 files changed, 138 insertions(+) create mode 100644 testdata/default/cheats/Prompt.t.sol diff --git a/Cargo.lock b/Cargo.lock index ba462d889..87a5f0bea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2091,7 +2091,9 @@ checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" dependencies = [ "console", "shell-words", + "tempfile", "thiserror", + "zeroize", ] [[package]] @@ -3167,6 +3169,7 @@ dependencies = [ "alloy-sol-types", "base64 0.22.0", "const-hex", + "dialoguer", "eyre", "foundry-cheatcodes-spec", "foundry-common", diff --git a/crates/cheatcodes/Cargo.toml b/crates/cheatcodes/Cargo.toml index 57b39966e..47315f5ce 100644 --- a/crates/cheatcodes/Cargo.toml +++ b/crates/cheatcodes/Cargo.toml @@ -43,3 +43,4 @@ walkdir = "2" p256 = "0.13.2" thiserror = "1" rustc-hash.workspace = true +dialoguer = "0.11.0" diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index b08607226..9c69772b6 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -6038,6 +6038,46 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "prompt", + "description": "Prompts the user for a string value in the terminal.", + "declaration": "function prompt(string calldata promptText) external returns (string memory input);", + "visibility": "external", + "mutability": "", + "signature": "prompt(string)", + "selector": "0x47eaf474", + "selectorBytes": [ + 71, + 234, + 244, + 116 + ] + }, + "group": "filesystem", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "promptSecret", + "description": "Prompts the user for a hidden string value in the terminal.", + "declaration": "function promptSecret(string calldata promptText) external returns (string memory input);", + "visibility": "external", + "mutability": "", + "signature": "promptSecret(string)", + "selector": "0x1e279d41", + "selectorBytes": [ + 30, + 39, + 157, + 65 + ] + }, + "group": "filesystem", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "readCallers", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index bd6500fb7..cdcca3331 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1399,6 +1399,16 @@ interface Vm { #[cheatcode(group = Filesystem)] function tryFfi(string[] calldata commandInput) external returns (FfiResult memory result); + // -------- User Interaction -------- + + /// Prompts the user for a string value in the terminal. + #[cheatcode(group = Filesystem)] + function prompt(string calldata promptText) external returns (string memory input); + + /// Prompts the user for a hidden string value in the terminal. + #[cheatcode(group = Filesystem)] + function promptSecret(string calldata promptText) external returns (string memory input); + // ======== Environment Variables ======== /// Sets environment variables. diff --git a/crates/cheatcodes/src/config.rs b/crates/cheatcodes/src/config.rs index eb8f56ab7..1048f6b69 100644 --- a/crates/cheatcodes/src/config.rs +++ b/crates/cheatcodes/src/config.rs @@ -11,6 +11,7 @@ use foundry_evm_core::opts::EvmOpts; use std::{ collections::HashMap, path::{Path, PathBuf}, + time::Duration, }; /// Additional, configurable context the `Cheatcodes` inspector has access to @@ -22,6 +23,8 @@ pub struct CheatsConfig { pub ffi: bool, /// Use the create 2 factory in all cases including tests and non-broadcasting scripts. pub always_use_create_2_factory: bool, + /// Sets a timeout for vm.prompt cheatcodes + pub prompt_timeout: Duration, /// RPC storage caching settings determines what chains and endpoints to cache pub rpc_storage_caching: StorageCachingConfig, /// All known endpoints and their aliases @@ -55,6 +58,7 @@ impl CheatsConfig { Self { ffi: evm_opts.ffi, always_use_create_2_factory: evm_opts.always_use_create_2_factory, + prompt_timeout: Duration::from_secs(config.prompt_timeout), rpc_storage_caching: config.rpc_storage_caching.clone(), rpc_endpoints, paths: config.project_paths(), @@ -171,6 +175,7 @@ impl Default for CheatsConfig { Self { ffi: false, always_use_create_2_factory: false, + prompt_timeout: Duration::from_secs(120), rpc_storage_caching: Default::default(), rpc_endpoints: Default::default(), paths: ProjectPathsConfig::builder().build_with_root("./"), diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index 3e345db94..7789915b5 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -4,6 +4,7 @@ use crate::{Cheatcode, Cheatcodes, Result, Vm::*}; use alloy_json_abi::ContractObject; use alloy_primitives::U256; use alloy_sol_types::SolValue; +use dialoguer::{Input, Password}; use foundry_common::{fs, get_artifact_path}; use foundry_config::fs_permissions::FsAccessKind; use std::{ @@ -11,6 +12,8 @@ use std::{ io::{BufRead, BufReader, Write}, path::Path, process::Command, + sync::mpsc, + thread, time::{SystemTime, UNIX_EPOCH}, }; use walkdir::WalkDir; @@ -296,6 +299,20 @@ impl Cheatcode for tryFfiCall { } } +impl Cheatcode for promptCall { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let Self { promptText: text } = self; + prompt(state, text, prompt_input).map(|res| res.abi_encode()) + } +} + +impl Cheatcode for promptSecretCall { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let Self { promptText: text } = self; + prompt(state, text, prompt_password).map(|res| res.abi_encode()) + } +} + pub(super) fn write_file(state: &Cheatcodes, path: &Path, contents: &[u8]) -> Result { let path = state.config.ensure_path_allowed(path, FsAccessKind::Write)?; // write access to foundry.toml is not allowed @@ -370,6 +387,39 @@ fn ffi(state: &Cheatcodes, input: &[String]) -> Result { }) } +fn prompt_input(prompt_text: &str) -> Result { + Input::new().allow_empty(true).with_prompt(prompt_text).interact_text() +} + +fn prompt_password(prompt_text: &str) -> Result { + Password::new().with_prompt(prompt_text).interact() +} + +fn prompt( + state: &Cheatcodes, + prompt_text: &str, + input: fn(&str) -> Result, +) -> Result { + let text_clone = prompt_text.to_string(); + let timeout = state.config.prompt_timeout; + let (send, recv) = mpsc::channel(); + + thread::spawn(move || { + send.send(input(&text_clone)).unwrap(); + }); + + match recv.recv_timeout(timeout) { + Ok(res) => res.map_err(|err| { + println!(); + err.to_string().into() + }), + Err(_) => { + println!(); + Err("Prompt timed out".into()) + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/config/README.md b/crates/config/README.md index 46fa83e5a..ffa4cdc75 100644 --- a/crates/config/README.md +++ b/crates/config/README.md @@ -116,6 +116,7 @@ match_path = "*/Foo*" no_match_path = "*/Bar*" ffi = false always_use_create_2_factory = false +prompt_timeout = 120 # These are the default callers, generated using `address(uint160(uint256(keccak256("foundry default caller"))))` sender = '0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38' tx_origin = '0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38' diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 4f27b58b8..0c53a6c14 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -244,6 +244,8 @@ pub struct Config { pub ffi: bool, /// Use the create 2 factory in all cases including tests and non-broadcasting scripts. pub always_use_create_2_factory: bool, + /// Sets a timeout in seconds for vm.prompt cheatcodes + pub prompt_timeout: u64, /// The address which will be executing all tests pub sender: Address, /// The tx.origin value during EVM execution @@ -1873,6 +1875,7 @@ impl Default for Config { invariant: Default::default(), always_use_create_2_factory: false, ffi: false, + prompt_timeout: 120, sender: Config::DEFAULT_SENDER, tx_origin: Config::DEFAULT_SENDER, initial_balance: U256::from(0xffffffffffffffffffffffffu128), diff --git a/crates/evm/traces/src/decoder/mod.rs b/crates/evm/traces/src/decoder/mod.rs index 2eac34d00..f53d8b516 100644 --- a/crates/evm/traces/src/decoder/mod.rs +++ b/crates/evm/traces/src/decoder/mod.rs @@ -520,6 +520,7 @@ impl CallTraceDecoder { match func.name.as_str() { s if s.starts_with("env") => Some(""), "createWallet" | "deriveKey" => Some(""), + "promptSecret" => Some(""), "parseJson" if self.verbosity < 5 => Some(""), "readFile" if self.verbosity < 5 => Some(""), _ => None, diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 6714df59d..81884f3bc 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -72,6 +72,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { invariant: InvariantConfig { runs: 256, ..Default::default() }, ffi: true, always_use_create_2_factory: false, + prompt_timeout: 0, sender: "00a329c0648769A73afAc7F9381D08FB43dBEA72".parse().unwrap(), tx_origin: "00a329c0648769A73afAc7F9F81E08FB43dBEA72".parse().unwrap(), initial_balance: U256::from(0xffffffffffffffffffffffffu128), diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index 3d1648756..bf31842b8 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -197,6 +197,9 @@ impl ForgeTestData { 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 opts = self.evm_opts.clone(); let env = opts.local_evm_env(); diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 623ef254b..b5a604e14 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -299,6 +299,8 @@ interface Vm { function prank(address msgSender, address txOrigin) external; function prevrandao(bytes32 newPrevrandao) external; function projectRoot() external view returns (string memory path); + function prompt(string calldata promptText) external returns (string memory input); + function promptSecret(string calldata promptText) external returns (string memory input); function readCallers() external returns (CallerMode callerMode, address msgSender, address txOrigin); function readDir(string calldata path) external view returns (DirEntry[] memory entries); function readDir(string calldata path, uint64 maxDepth) external view returns (DirEntry[] memory entries); diff --git a/testdata/default/cheats/Prompt.t.sol b/testdata/default/cheats/Prompt.t.sol new file mode 100644 index 000000000..dadfd30a9 --- /dev/null +++ b/testdata/default/cheats/Prompt.t.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract PromptTest is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + function testPrompt_revertNotATerminal() public { + // should revert in CI and testing environments either with timout or because no terminal is available + vm._expectCheatcodeRevert(); + vm.prompt("test"); + + vm._expectCheatcodeRevert(); + vm.promptSecret("test"); + } +} From c2233ec9fe61e0920c61c6d779bc707252852037 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 21 Mar 2024 16:04:46 +0100 Subject: [PATCH 101/430] feat: write instruction result when displaying call traces (#7465) * feat: write instruction result when displaying call traces * fix: new line, update tests * space --- crates/evm/traces/src/lib.rs | 30 +++++++++---------- .../include_custom_types_in_traces.stdout | 4 +-- crates/forge/tests/fixtures/repro_6531.stdout | 6 ++-- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/crates/evm/traces/src/lib.rs b/crates/evm/traces/src/lib.rs index d7d55696f..2538c03ca 100644 --- a/crates/evm/traces/src/lib.rs +++ b/crates/evm/traces/src/lib.rs @@ -122,23 +122,21 @@ pub async fn render_trace_arena( // Display trace return data let color = trace_color(&node.trace); - write!(s, "{child}{EDGE}{}", color.paint(RETURN))?; - if node.trace.kind.is_any_create() { - match &return_data { - None => { - writeln!(s, "{} bytes of code", node.trace.output.len())?; - } - Some(val) => { - writeln!(s, "{val}")?; - } + write!( + s, + "{child}{EDGE}{}{}", + color.paint(RETURN), + color.paint(format!("[{:?}] ", node.trace.status)) + )?; + match return_data { + Some(val) => write!(s, "{val}"), + None if node.trace.kind.is_any_create() => { + write!(s, "{} bytes of code", node.trace.output.len()) } - } else { - match &return_data { - None if node.trace.output.is_empty() => writeln!(s, "()")?, - None => writeln!(s, "{}", node.trace.output)?, - Some(val) => writeln!(s, "{val}")?, - } - } + None if node.trace.output.is_empty() => Ok(()), + None => write!(s, "{}", node.trace.output), + }?; + writeln!(s)?; Ok(()) } diff --git a/crates/forge/tests/fixtures/include_custom_types_in_traces.stdout b/crates/forge/tests/fixtures/include_custom_types_in_traces.stdout index 786679a67..571cc6927 100644 --- a/crates/forge/tests/fixtures/include_custom_types_in_traces.stdout +++ b/crates/forge/tests/fixtures/include_custom_types_in_traces.stdout @@ -6,13 +6,13 @@ Ran 2 tests for test/Contract.t.sol:CustomTypesTest [FAIL. Reason: PoolNotInitialized()] testErr() (gas: 231) Traces: [231] CustomTypesTest::testErr() - └─ ← PoolNotInitialized() + └─ ← [Revert] PoolNotInitialized() [PASS] testEvent() (gas: 1312) Traces: [1312] CustomTypesTest::testEvent() ├─ emit MyEvent(a: 100) - └─ ← () + └─ ← [Stop] Suite result: FAILED. 1 passed; 1 failed; 0 skipped; finished in 3.88ms diff --git a/crates/forge/tests/fixtures/repro_6531.stdout b/crates/forge/tests/fixtures/repro_6531.stdout index 01f282bf7..35c27c948 100644 --- a/crates/forge/tests/fixtures/repro_6531.stdout +++ b/crates/forge/tests/fixtures/repro_6531.stdout @@ -7,10 +7,10 @@ Ran 1 test for test/Contract.t.sol:USDTCallingTest Traces: [9559] USDTCallingTest::test() ├─ [0] VM::createSelectFork("") - │ └─ ← 0 + │ └─ ← [Return] 0 ├─ [3110] 0xdAC17F958D2ee523a2206206994597C13D831ec7::name() [staticcall] - │ └─ ← "Tether USD" - └─ ← () + │ └─ ← [Return] "Tether USD" + └─ ← [Stop] Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.43s From 5ecc1bf6ceae678791ff23f4c233c0bba6757285 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 22 Mar 2024 03:51:49 +0100 Subject: [PATCH 102/430] chore: remove Instruction enum in debug steps (#7464) --- crates/debugger/src/tui/draw.rs | 46 ++++++++--------- crates/evm/core/src/debug.rs | 61 +++-------------------- crates/evm/evm/src/inspectors/debugger.rs | 21 ++------ 3 files changed, 29 insertions(+), 99 deletions(-) diff --git a/crates/debugger/src/tui/draw.rs b/crates/debugger/src/tui/draw.rs index 41eca5e76..76e596513 100644 --- a/crates/debugger/src/tui/draw.rs +++ b/crates/debugger/src/tui/draw.rs @@ -4,7 +4,6 @@ use super::context::{BufferKind, DebuggerContext}; use crate::op::OpcodeParam; use alloy_primitives::U256; use foundry_compilers::sourcemap::SourceElement; -use foundry_evm_core::debug::Instruction; use ratatui::{ layout::{Alignment, Constraint, Direction, Layout, Rect}, style::{Color, Modifier, Style}, @@ -456,8 +455,7 @@ impl DebuggerContext<'_> { let min_len = decimal_digits(stack.len()).max(2); - let params = - if let Instruction::OpCode(op) = step.instruction { OpcodeParam::of(op) } else { &[] }; + let params = OpcodeParam::of(step.instruction); let text: Vec = stack .iter() @@ -516,20 +514,18 @@ impl DebuggerContext<'_> { let mut write_offset = None; let mut write_size = None; let mut color = None; - if let Instruction::OpCode(op) = step.instruction { - let stack_len = step.stack.len(); - if stack_len > 0 { - if let Some(accesses) = get_buffer_accesses(op, &step.stack) { - if let Some(read_access) = accesses.read { - offset = Some(read_access.1.offset); - size = Some(read_access.1.size); - color = Some(Color::Cyan); - } - if let Some(write_access) = accesses.write { - if self.active_buffer == BufferKind::Memory { - write_offset = Some(write_access.offset); - write_size = Some(write_access.size); - } + let stack_len = step.stack.len(); + if stack_len > 0 { + if let Some(accesses) = get_buffer_accesses(step.instruction, &step.stack) { + if let Some(read_access) = accesses.read { + offset = Some(read_access.1.offset); + size = Some(read_access.1.size); + color = Some(Color::Cyan); + } + if let Some(write_access) = accesses.write { + if self.active_buffer == BufferKind::Memory { + write_offset = Some(write_access.offset); + write_size = Some(write_access.size); } } } @@ -542,15 +538,13 @@ impl DebuggerContext<'_> { if self.current_step > 0 { let prev_step = self.current_step - 1; let prev_step = &self.debug_steps()[prev_step]; - if let Instruction::OpCode(op) = prev_step.instruction { - if let Some(write_access) = - get_buffer_accesses(op, &prev_step.stack).and_then(|a| a.write) - { - if self.active_buffer == BufferKind::Memory { - offset = Some(write_access.offset); - size = Some(write_access.size); - color = Some(Color::Green); - } + if let Some(write_access) = + get_buffer_accesses(prev_step.instruction, &prev_step.stack).and_then(|a| a.write) + { + if self.active_buffer == BufferKind::Memory { + offset = Some(write_access.offset); + size = Some(write_access.size); + color = Some(Color::Green); } } } diff --git a/crates/evm/core/src/debug.rs b/crates/evm/core/src/debug.rs index 70d94ff97..056bde54c 100644 --- a/crates/evm/core/src/debug.rs +++ b/crates/evm/core/src/debug.rs @@ -4,7 +4,6 @@ use arrayvec::ArrayVec; use revm::interpreter::OpCode; use revm_inspectors::tracing::types::CallKind; use serde::{Deserialize, Serialize}; -use std::fmt::Display; /// An arena of [DebugNode]s #[derive(Clone, Debug, Default, Serialize, Deserialize)] @@ -176,7 +175,7 @@ pub struct DebugStep { /// Returndata *prior* to running the associated opcode pub returndata: Bytes, /// Opcode to be executed - pub instruction: Instruction, + pub instruction: u8, /// Optional bytes that are being pushed onto the stack. /// Empty if the opcode is not a push or PUSH0. #[serde(serialize_with = "hex::serialize", deserialize_with = "deserialize_arrayvec_hex")] @@ -197,7 +196,7 @@ impl Default for DebugStep { memory: Default::default(), calldata: Default::default(), returndata: Default::default(), - instruction: Instruction::OpCode(revm::interpreter::opcode::INVALID), + instruction: revm::interpreter::opcode::INVALID, push_bytes: Default::default(), pc: 0, total_gas_used: 0, @@ -208,65 +207,17 @@ impl Default for DebugStep { impl DebugStep { /// Pretty print the step's opcode pub fn pretty_opcode(&self) -> String { + let instruction = OpCode::new(self.instruction).map_or("INVALID", |op| op.as_str()); if !self.push_bytes.is_empty() { - format!("{}(0x{})", self.instruction, hex::encode(&self.push_bytes)) + format!("{instruction}(0x{})", hex::encode(&self.push_bytes)) } else { - self.instruction.to_string() + instruction.to_string() } } /// Returns `true` if the opcode modifies memory. pub fn opcode_modifies_memory(&self) -> bool { - self.instruction.opcode().and_then(OpCode::new).map_or(false, opcodes::modifies_memory) - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum Instruction { - OpCode(u8), - Cheatcode([u8; 4]), -} - -impl From for Instruction { - fn from(op: u8) -> Instruction { - Instruction::OpCode(op) - } -} - -impl Display for Instruction { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Instruction::OpCode(op) => write!( - f, - "{}", - OpCode::new(*op).map_or_else( - || format!("UNDEFINED(0x{op:02x})"), - |opcode| opcode.as_str().to_string(), - ) - ), - Instruction::Cheatcode(cheat) => write!( - f, - "VM_{}", - crate::abi::Vm::CHEATCODES - .iter() - .map(|c| &c.func) - .find(|c| c.selector_bytes == *cheat) - .expect("unknown cheatcode found in debugger") - .id - .to_uppercase() - ), - } - } -} - -impl Instruction { - /// Returns the opcode of the instruction, if it is an opcode. - #[inline] - pub fn opcode(&self) -> Option { - match self { - Instruction::OpCode(op) => Some(*op), - _ => None, - } + OpCode::new(self.instruction).map_or(false, opcodes::modifies_memory) } } diff --git a/crates/evm/evm/src/inspectors/debugger.rs b/crates/evm/evm/src/inspectors/debugger.rs index f6232bd47..d68a14bf6 100644 --- a/crates/evm/evm/src/inspectors/debugger.rs +++ b/crates/evm/evm/src/inspectors/debugger.rs @@ -1,10 +1,9 @@ use alloy_primitives::Address; use arrayvec::ArrayVec; -use foundry_common::{ErrorExt, SELECTOR_LEN}; +use foundry_common::ErrorExt; use foundry_evm_core::{ backend::DatabaseExt, - constants::CHEATCODE_ADDRESS, - debug::{DebugArena, DebugNode, DebugStep, Instruction}, + debug::{DebugArena, DebugNode, DebugStep}, utils::gas_used, }; use revm::{ @@ -47,7 +46,6 @@ impl Debugger { } impl Inspector for Debugger { - #[inline] fn step(&mut self, interp: &mut Interpreter, ecx: &mut EvmContext) { let pc = interp.program_counter(); let op = interp.current_opcode(); @@ -88,13 +86,12 @@ impl Inspector for Debugger { memory, calldata: interp.contract().input.clone(), returndata: interp.return_data_buffer.clone(), - instruction: Instruction::OpCode(op), + instruction: op, push_bytes: push_bytes.unwrap_or_default(), total_gas_used, }); } - #[inline] fn call(&mut self, ecx: &mut EvmContext, inputs: &mut CallInputs) -> Option { self.enter( ecx.journaled_state.depth() as usize, @@ -102,19 +99,9 @@ impl Inspector for Debugger { inputs.context.scheme.into(), ); - if inputs.contract == CHEATCODE_ADDRESS { - if let Some(selector) = inputs.input.get(..SELECTOR_LEN) { - self.arena.arena[self.head].steps.push(DebugStep { - instruction: Instruction::Cheatcode(selector.try_into().unwrap()), - ..Default::default() - }); - } - } - None } - #[inline] fn call_end( &mut self, _context: &mut EvmContext, @@ -126,7 +113,6 @@ impl Inspector for Debugger { outcome } - #[inline] fn create( &mut self, ecx: &mut EvmContext, @@ -154,7 +140,6 @@ impl Inspector for Debugger { None } - #[inline] fn create_end( &mut self, _context: &mut EvmContext, From 9d2125b013cbcb61dce2546379a79a4d99ba2f78 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Fri, 22 Mar 2024 15:29:29 +0200 Subject: [PATCH 103/430] fix(invariant): call override strategy panic (#7469) * fix(invariant): override call strat panic * Add test --- crates/evm/fuzz/src/strategies/invariants.rs | 12 +++++++++++- crates/forge/tests/it/invariant.rs | 1 + .../fuzz/invariant/common/InvariantReentrancy.t.sol | 12 +++++++++++- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/crates/evm/fuzz/src/strategies/invariants.rs b/crates/evm/fuzz/src/strategies/invariants.rs index 5fd766bb4..1d226b003 100644 --- a/crates/evm/fuzz/src/strategies/invariants.rs +++ b/crates/evm/fuzz/src/strategies/invariants.rs @@ -25,7 +25,17 @@ pub fn override_call_strat( .prop_flat_map(move |target_address| { let fuzz_state = fuzz_state.clone(); let calldata_fuzz_config = calldata_fuzz_config.clone(); - let (_, abi, functions) = &contracts.lock()[&target_address]; + + let contracts = &contracts.lock(); + let (_, abi, functions) = contracts.get(&target_address).unwrap_or({ + // Choose a random contract if target selected by lazy strategy is not in fuzz run + // identified contracts. This can happen when contract is created in `setUp` call + // but is not included in targetContracts. + let rand_index = rand::thread_rng().gen_range(0..contracts.iter().len()); + let (_, contract_specs) = contracts.iter().nth(rand_index).unwrap(); + contract_specs + }); + let func = select_random_function(abi, functions); func.prop_flat_map(move |func| { fuzz_contract_with_calldata(&fuzz_state, &calldata_fuzz_config, target_address, func) diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index e902b6aa9..82cf4909e 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -153,6 +153,7 @@ async fn test_invariant() { 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 results = runner.test_collect(&filter); diff --git a/testdata/default/fuzz/invariant/common/InvariantReentrancy.t.sol b/testdata/default/fuzz/invariant/common/InvariantReentrancy.t.sol index 086d5f99a..06b4b21d7 100644 --- a/testdata/default/fuzz/invariant/common/InvariantReentrancy.t.sol +++ b/testdata/default/fuzz/invariant/common/InvariantReentrancy.t.sol @@ -5,7 +5,9 @@ import "ds-test/test.sol"; contract Malicious { function world() public { - // Does not matter, since it will get overridden. + // add code so contract is accounted as valid sender + // see https://github.com/foundry-rs/foundry/issues/4245 + payable(msg.sender).transfer(1); } } @@ -39,6 +41,14 @@ contract InvariantReentrancy is DSTest { vuln = new Vulnerable(address(mal)); } + // do not include `mal` in identified contracts + // see https://github.com/foundry-rs/foundry/issues/4245 + function targetContracts() public view returns (address[] memory) { + address[] memory targets = new address[](1); + targets[0] = address(vuln); + return targets; + } + function invariantNotStolen() public { require(vuln.stolen() == false, "stolen"); } From f73d855ff32e94190c8c8b637ae47a73461efdab Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 22 Mar 2024 16:28:50 +0100 Subject: [PATCH 104/430] perf(debugger): don't clone debug info twice (#7468) --- crates/debugger/src/tui/context.rs | 8 +++----- crates/forge/bin/cmd/test/mod.rs | 6 +++--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/crates/debugger/src/tui/context.rs b/crates/debugger/src/tui/context.rs index 6f8c30939..b0da8a0c5 100644 --- a/crates/debugger/src/tui/context.rs +++ b/crates/debugger/src/tui/context.rs @@ -115,11 +115,9 @@ impl<'a> DebuggerContext<'a> { } fn gen_opcode_list(&mut self) { - self.opcode_list = self.opcode_list(); - } - - fn opcode_list(&self) -> Vec { - self.debug_steps().iter().map(DebugStep::pretty_opcode).collect() + self.opcode_list.clear(); + let debug_steps = &self.debugger.debug_arena[self.draw_memory.inner_call_index].steps; + self.opcode_list.extend(debug_steps.iter().map(DebugStep::pretty_opcode)); } fn active_buffer(&self) -> &[u8] { diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index dfb1d8cf5..13130d464 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -225,7 +225,7 @@ impl TestArgs { if should_debug { // There is only one test. - let Some(test) = outcome.into_tests_cloned().next() else { + let Some((_, test_result)) = outcome.tests().next() else { return Err(eyre::eyre!("no tests were executed")); }; @@ -236,9 +236,9 @@ impl TestArgs { // Run the debugger. let mut builder = Debugger::builder() - .debug_arenas(test.result.debug.as_slice()) + .debug_arenas(test_result.debug.as_slice()) .sources(sources) - .breakpoints(test.result.breakpoints); + .breakpoints(test_result.breakpoints.clone()); if let Some(decoder) = &outcome.decoder { builder = builder.decoder(decoder); } From f9da73dff7d089a4a79ba4977419aec06cc10330 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 22 Mar 2024 23:30:34 +0100 Subject: [PATCH 105/430] chore(fuzz): improve `override_call_strat` (#7477) --- crates/evm/fuzz/src/strategies/invariants.rs | 22 +++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/crates/evm/fuzz/src/strategies/invariants.rs b/crates/evm/fuzz/src/strategies/invariants.rs index 1d226b003..c98e84598 100644 --- a/crates/evm/fuzz/src/strategies/invariants.rs +++ b/crates/evm/fuzz/src/strategies/invariants.rs @@ -26,17 +26,19 @@ pub fn override_call_strat( let fuzz_state = fuzz_state.clone(); let calldata_fuzz_config = calldata_fuzz_config.clone(); - let contracts = &contracts.lock(); - let (_, abi, functions) = contracts.get(&target_address).unwrap_or({ - // Choose a random contract if target selected by lazy strategy is not in fuzz run - // identified contracts. This can happen when contract is created in `setUp` call - // but is not included in targetContracts. - let rand_index = rand::thread_rng().gen_range(0..contracts.iter().len()); - let (_, contract_specs) = contracts.iter().nth(rand_index).unwrap(); - contract_specs - }); + let func = { + let contracts = contracts.lock(); + let (_, abi, functions) = contracts.get(&target_address).unwrap_or_else(|| { + // Choose a random contract if target selected by lazy strategy is not in fuzz run + // identified contracts. This can happen when contract is created in `setUp` call + // but is not included in targetContracts. + let rand_index = rand::thread_rng().gen_range(0..contracts.len()); + let (_, contract_specs) = contracts.iter().nth(rand_index).unwrap(); + contract_specs + }); + select_random_function(abi, functions) + }; - let func = select_random_function(abi, functions); func.prop_flat_map(move |func| { fuzz_contract_with_calldata(&fuzz_state, &calldata_fuzz_config, target_address, func) }) From 0cd972f3f813fcb54d6ba51d8c215d49ebc98a8c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 24 Mar 2024 13:59:14 +0100 Subject: [PATCH 106/430] chore(deps): weekly `cargo update` (#7479) Updating git repository `https://github.com/alloy-rs/alloy` Updating git repository `https://github.com/paradigmxyz/evm-inspectors` Updating aho-corasick v1.1.2 -> v1.1.3 Updating async-recursion v1.0.5 -> v1.1.0 Updating backtrace v0.3.69 -> v0.3.71 Updating bitflags v2.4.2 -> v2.5.0 Updating bs58 v0.5.0 -> v0.5.1 Updating bytes v1.5.0 -> v1.6.0 Updating cargo-platform v0.1.7 -> v0.1.8 Downgrading enr v0.10.1 -> v0.10.0 (latest: v0.11.0) Adding fs2 v0.4.3 Updating futures-lite v2.2.0 -> v2.3.0 Updating indexmap v2.2.5 -> v2.2.6 Updating indoc v2.0.4 -> v2.0.5 Updating pear v0.2.8 -> v0.2.9 Updating pear_codegen v0.2.8 -> v0.2.9 Updating regex v1.10.3 -> v1.10.4 Updating reqwest v0.11.26 -> v0.11.27 (latest: v0.12.1) Updating rustix v0.38.31 -> v0.38.32 Updating smallvec v1.13.1 -> v1.13.2 Downgrading svm-rs v0.3.6 -> v0.3.5 (latest: v0.4.1) Updating toml v0.8.11 -> v0.8.12 Updating toml_edit v0.22.7 -> v0.22.9 note: pass `--verbose` to see 190 unchanged dependencies behind latest Co-authored-by: mattsse --- Cargo.lock | 140 ++++++++++++++++++++++++++++------------------------- 1 file changed, 75 insertions(+), 65 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 87a5f0bea..ca3a63fa4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -63,9 +63,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -840,9 +840,9 @@ dependencies = [ [[package]] name = "async-recursion" -version = "1.0.5" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" +checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5" dependencies = [ "proc-macro2", "quote", @@ -1001,9 +1001,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -1079,9 +1079,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" dependencies = [ "arbitrary", "serde", @@ -1148,9 +1148,9 @@ dependencies = [ [[package]] name = "bs58" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" dependencies = [ "sha2 0.10.8", "tinyvec", @@ -1208,9 +1208,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" dependencies = [ "serde", ] @@ -1261,9 +1261,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "694c8807f2ae16faecc43dc17d74b3eb042482789fd0eb64b39a2e04e087053f" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" dependencies = [ "serde", ] @@ -1867,7 +1867,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "crossterm_winapi", "libc", "mio", @@ -2287,17 +2287,17 @@ checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" [[package]] name = "enr" -version = "0.10.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dc3eabaca59dc39ea5ed15062e4abc5bba9723b1cff7a4fea3faae0647f04c0" +checksum = "2a3d8dc56e02f954cac8eb489772c552c473346fc34f67412bb6244fd647f7e4" dependencies = [ - "alloy-rlp", "base64 0.21.7", "bytes", "hex", "k256", "log", "rand 0.8.5", + "rlp", "serde", "sha3", "zeroize", @@ -2524,7 +2524,7 @@ dependencies = [ "serde", "serde_json", "syn 2.0.53", - "toml 0.8.11", + "toml 0.8.12", "walkdir", ] @@ -2706,7 +2706,7 @@ dependencies = [ "serde", "serde_json", "solang-parser", - "svm-rs 0.3.6", + "svm-rs 0.3.5", "thiserror", "tiny-keccak", "tokio", @@ -2875,7 +2875,7 @@ dependencies = [ "pear", "serde", "tempfile", - "toml 0.8.11", + "toml 0.8.12", "uncased", "version_check", ] @@ -3038,7 +3038,7 @@ dependencies = [ "serde_json", "solang-parser", "thiserror", - "toml 0.8.11", + "toml 0.8.12", "tracing", ] @@ -3053,7 +3053,7 @@ dependencies = [ "pretty_assertions", "solang-parser", "thiserror", - "toml 0.8.11", + "toml 0.8.12", "tracing", "tracing-subscriber", ] @@ -3186,7 +3186,7 @@ dependencies = [ "rustc-hash", "serde_json", "thiserror", - "toml 0.8.11", + "toml 0.8.12", "tracing", "walkdir", ] @@ -3353,7 +3353,7 @@ dependencies = [ "solang-parser", "tempfile", "thiserror", - "toml 0.8.11", + "toml 0.8.12", "toml_edit 0.21.1", "tracing", "walkdir", @@ -3576,6 +3576,16 @@ dependencies = [ "tracing", ] +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "fs4" version = "0.7.0" @@ -3667,9 +3677,9 @@ checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-lite" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445ba825b27408685aaecefd65178908c36c6e96aaf6d8599419d46e624192ba" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" dependencies = [ "futures-core", "pin-project-lite", @@ -3828,7 +3838,7 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e874f41437441c02991dcea76990b9058fadfc54b02ab4dd06ab2218af43897" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "bstr", "gix-path", "libc", @@ -3874,7 +3884,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c07c98204529ac3f24b34754540a852593d2a4c7349008df389240266627a72a" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "bstr", "gix-features", "gix-path", @@ -3959,7 +3969,7 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9615cbd6b456898aeb942cd75e5810c382fbfc48dbbff2fa23ebd2d33dcbe9c7" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "gix-path", "libc", "windows", @@ -4451,9 +4461,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "2.2.5" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -4474,9 +4484,9 @@ dependencies = [ [[package]] name = "indoc" -version = "2.0.4" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" [[package]] name = "inlinable_string" @@ -4763,7 +4773,7 @@ version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "libc", "redox_syscall", ] @@ -5063,7 +5073,7 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "cfg-if", "cfg_aliases", "libc", @@ -5319,7 +5329,7 @@ version = "0.10.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "cfg-if", "foreign-types", "libc", @@ -5528,9 +5538,9 @@ dependencies = [ [[package]] name = "pear" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ccca0f6c17acc81df8e242ed473ec144cbf5c98037e69aa6d144780aad103c8" +checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" dependencies = [ "inlinable_string", "pear_codegen", @@ -5539,9 +5549,9 @@ dependencies = [ [[package]] name = "pear_codegen" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e22670e8eb757cff11d6c199ca7b987f352f0346e0be4dd23869ec72cb53c77" +checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" dependencies = [ "proc-macro2", "proc-macro2-diagnostics", @@ -5975,7 +5985,7 @@ checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.4.2", + "bitflags 2.5.0", "lazy_static", "num-traits", "rand 0.8.5", @@ -6024,7 +6034,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dce76ce678ffc8e5675b22aa1405de0b7037e2fdf8913fea40d1926c6fe1e6e7" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "memchr", "pulldown-cmark-escape", "unicase", @@ -6153,7 +6163,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ebc917cfb527a566c37ecb94c7e3fd098353516fb4eb6bea17015ade0182425" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "cassowary", "crossterm", "indoc", @@ -6221,9 +6231,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.3" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", @@ -6265,9 +6275,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.26" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bf93c4af7a8bb7d879d51cebe797356ff10ae8516ace542b5182d9dcac10b2" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ "base64 0.21.7", "bytes", @@ -6375,7 +6385,7 @@ checksum = "2a4d7d3e793e907dc0797a9d3b43abfdf5226d133855214db9bd27d4cee33ebd" dependencies = [ "alloy-primitives", "auto_impl", - "bitflags 2.4.2", + "bitflags 2.5.0", "bitvec", "c-kzg", "cfg-if", @@ -6640,11 +6650,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.31" +version = "0.38.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys", @@ -6730,7 +6740,7 @@ version = "12.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "994eca4bca05c87e86e15d90fc7a91d1be64b4482b38cb2d27474568fe7c9db9" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "cfg-if", "clipboard-win", "fd-lock 3.0.13", @@ -7227,9 +7237,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" @@ -7401,13 +7411,13 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "svm-rs" -version = "0.3.6" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9b34cc1af809be8d20575428638da2c6a1eb12b1da06dae79c1b82a4ddf83ac" +checksum = "11297baafe5fa0c99d5722458eac6a5e25c01eb1b8e5cd137f54079093daa7a4" dependencies = [ - "const-hex", "dirs 5.0.1", - "fs4", + "fs2", + "hex", "once_cell", "reqwest", "semver 1.0.22", @@ -7816,15 +7826,15 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af06656561d28735e9c1cd63dfd57132c8155426aa6af24f36a00a351f88c48e" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.7", + "toml_edit 0.22.9", ] [[package]] @@ -7871,9 +7881,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.7" +version = "0.22.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18769cd1cec395d70860ceb4d932812a0b4d06b1a4bb336745a4d21b9496e992" +checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" dependencies = [ "indexmap", "serde", @@ -7910,7 +7920,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "bytes", "futures-core", "futures-util", From 88e09f6f24a771b8b37def2d437660b13146bef6 Mon Sep 17 00:00:00 2001 From: Panagiotis Ganelis <50522617+PanGan21@users.noreply.github.com> Date: Sun, 24 Mar 2024 14:59:45 +0200 Subject: [PATCH 107/430] feat(anvil): remove all txs from tx pool by sender origin (#7480) * feat(anvil): remove all txs from pool by sender origin * refactor(anvil): combine pending and ready transactions iterators in one --- crates/anvil/core/src/eth/mod.rs | 14 ++++++++++ crates/anvil/src/eth/api.rs | 9 ++++++ crates/anvil/src/eth/pool/mod.rs | 44 +++++++++++++++++++++++++++++- crates/anvil/tests/it/anvil_api.rs | 25 +++++++++++++++++ 4 files changed, 91 insertions(+), 1 deletion(-) diff --git a/crates/anvil/core/src/eth/mod.rs b/crates/anvil/core/src/eth/mod.rs index 203e8532a..e5a394a8f 100644 --- a/crates/anvil/core/src/eth/mod.rs +++ b/crates/anvil/core/src/eth/mod.rs @@ -717,6 +717,13 @@ pub enum EthRequest { /// contract. #[cfg_attr(feature = "serde", serde(rename = "ots_getContractCreator", with = "sequence"))] OtsGetContractCreator(Address), + + /// Removes transactions from the pool by sender origin. + #[cfg_attr( + feature = "serde", + serde(rename = "anvil_removePoolTransactions", with = "sequence") + )] + RemovePoolTransactions(Address), } /// Represents ethereum JSON-RPC API @@ -1506,4 +1513,11 @@ true}]}"#; let value: serde_json::Value = serde_json::from_str(s).unwrap(); let _req = serde_json::from_value::(value).unwrap(); } + + #[test] + fn test_remove_pool_transactions() { + let s = r#"{"method": "anvil_removePoolTransactions", "params":["0x364d6D0333432C3Ac016Ca832fb8594A8cE43Ca6"]}"#; + let value: serde_json::Value = serde_json::from_str(s).unwrap(); + let _req = serde_json::from_value::(value).unwrap(); + } } diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 83754480b..402bf4b75 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -413,6 +413,9 @@ impl EthApi { EthRequest::OtsGetContractCreator(address) => { self.ots_get_contract_creator(address).await.to_rpc_result() } + EthRequest::RemovePoolTransactions(address) => { + self.anvil_remove_pool_transactions(address).await.to_rpc_result() + } } } @@ -1781,6 +1784,12 @@ impl EthApi { }) } + pub async fn anvil_remove_pool_transactions(&self, address: Address) -> Result<()> { + node_info!("anvil_removePoolTransactions"); + self.pool.remove_transactions_by_address(address); + Ok(()) + } + /// Snapshot the state of the blockchain at the current block. /// /// Handler for RPC call: `evm_snapshot` diff --git a/crates/anvil/src/eth/pool/mod.rs b/crates/anvil/src/eth/pool/mod.rs index 8848a954b..34a75c205 100644 --- a/crates/anvil/src/eth/pool/mod.rs +++ b/crates/anvil/src/eth/pool/mod.rs @@ -36,7 +36,7 @@ use crate::{ }, mem::storage::MinedBlockOutcome, }; -use alloy_primitives::{TxHash, U64}; +use alloy_primitives::{Address, TxHash, U64}; use alloy_rpc_types::txpool::TxpoolStatus; use anvil_core::eth::transaction::PendingTransaction; use futures::channel::mpsc::{channel, Receiver, Sender}; @@ -141,6 +141,11 @@ impl Pool { self.inner.write().remove_invalid(tx_hashes) } + /// Remove transactions by sender + pub fn remove_transactions_by_address(&self, sender: Address) -> Vec> { + self.inner.write().remove_transactions_by_address(sender) + } + /// Removes a single transaction from the pool /// /// This is similar to `[Pool::remove_invalid()]` but for a single transaction. @@ -218,6 +223,24 @@ impl PoolInner { ) } + /// Returns an iterator over all transactions in the pool filtered by the sender + pub fn transactions_by_sender( + &self, + sender: Address, + ) -> impl Iterator> + '_ { + let pending_txs = self + .pending_transactions + .transactions() + .filter(move |tx| tx.pending_transaction.sender().eq(&sender)); + + let ready_txs = self + .ready_transactions + .get_transactions() + .filter(move |tx| tx.pending_transaction.sender().eq(&sender)); + + pending_txs.chain(ready_txs) + } + /// Returns true if this pool already contains the transaction fn contains(&self, tx_hash: &TxHash) -> bool { self.pending_transactions.contains(tx_hash) || self.ready_transactions.contains(tx_hash) @@ -342,6 +365,25 @@ impl PoolInner { removed } + + /// Remove transactions by sender address + pub fn remove_transactions_by_address(&mut self, sender: Address) -> Vec> { + let tx_hashes = + self.transactions_by_sender(sender).map(move |tx| tx.hash()).collect::>(); + + if tx_hashes.is_empty() { + return vec![] + } + + trace!(target: "txpool", "Removing transactions: {:?}", tx_hashes); + + let mut removed = self.ready_transactions.remove_with_markers(tx_hashes.clone(), None); + removed.extend(self.pending_transactions.remove(tx_hashes)); + + trace!(target: "txpool", "Removed transactions: {:?}", removed); + + removed + } } /// Represents the outcome of a prune diff --git a/crates/anvil/tests/it/anvil_api.rs b/crates/anvil/tests/it/anvil_api.rs index 91ba92d77..2f37f9b56 100644 --- a/crates/anvil/tests/it/anvil_api.rs +++ b/crates/anvil/tests/it/anvil_api.rs @@ -9,6 +9,7 @@ use anvil_core::{ use ethers::{ abi::{ethereum_types::BigEndianHash, AbiDecode}, prelude::{Middleware, SignerMiddleware}, + signers::Signer, types::{ transaction::eip2718::TypedTransaction, Address, BlockNumber, Eip1559TransactionRequest, TransactionRequest, H256, U256, U64, @@ -631,3 +632,27 @@ async fn test_fork_revert_call_latest_block_timestamp() { latest_block.header.miner.to_ethers() ); } + +#[tokio::test(flavor = "multi_thread")] +async fn can_remove_pool_transactions() { + let (api, handle) = spawn(NodeConfig::test()).await; + let provider = ethers_http_provider(&handle.http_endpoint()); + let wallet = handle.dev_wallets().next().unwrap().to_ethers(); + let provider = Arc::new(SignerMiddleware::new(provider, wallet.clone())); + + let sender = Address::random(); + let to = Address::random(); + let val = 1337u64; + + let tx = TransactionRequest::new().from(sender).to(to).value(val); + + provider.send_transaction(tx.from(wallet.address()), None).await.unwrap(); + + let initial_txs = provider.txpool_inspect().await.unwrap(); + assert_eq!(initial_txs.pending.len(), 1); + + api.anvil_remove_pool_transactions(wallet.address().to_alloy()).await.unwrap(); + + let final_txs = provider.txpool_inspect().await.unwrap(); + assert_eq!(final_txs.pending.len(), 0); +} From e5acbcfe71d5d6687fcc649e91776454f5c3eb73 Mon Sep 17 00:00:00 2001 From: Lu Zhang <8418040+longbowlu@users.noreply.github.com> Date: Mon, 25 Mar 2024 04:25:35 -0700 Subject: [PATCH 108/430] [anvil] correct log index for getTransactionReceipt (#7483) correctly aggregate log index for getTransactionReceipt --- crates/anvil/core/src/eth/transaction/mod.rs | 10 ++++++++ crates/anvil/src/eth/backend/mem/mod.rs | 5 ++-- crates/anvil/tests/it/logs.rs | 26 ++++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 72a88c547..10de5f80e 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -1011,6 +1011,16 @@ impl TypedReceipt { TypedReceipt::Deposit(r) => &r.bloom, } } + + pub fn logs(&self) -> &Vec { + match self { + TypedReceipt::Legacy(r) | + TypedReceipt::EIP1559(r) | + TypedReceipt::EIP2930(r) | + TypedReceipt::EIP4844(r) | + TypedReceipt::Deposit(r) => &r.receipt.logs, + } + } } impl From for ReceiptWithBloom { diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 013c303e3..a60ba5640 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -2009,8 +2009,9 @@ impl Backend { let mut pre_receipts_log_index = None; if !cumulative_receipts.is_empty() { cumulative_receipts.truncate(cumulative_receipts.len() - 1); - pre_receipts_log_index = - Some(cumulative_receipts.iter().map(|_r| logs.len() as u32).sum::()); + pre_receipts_log_index = Some( + cumulative_receipts.iter().map(|r| r.logs().len() as u32).sum::(), + ); } logs.iter() .enumerate() diff --git a/crates/anvil/tests/it/logs.rs b/crates/anvil/tests/it/logs.rs index 5fc9cf9f2..2f9290f3e 100644 --- a/crates/anvil/tests/it/logs.rs +++ b/crates/anvil/tests/it/logs.rs @@ -86,6 +86,32 @@ async fn get_all_events() { let num_logs = num_tx + pre_logs.len(); assert_eq!(logs.len(), num_logs); + + // test that logs returned from get_logs and get_transaction_receipt have + // the same log_index, block_number, and transaction_hash + let mut tasks = vec![]; + let mut seen_tx_hashes = std::collections::HashSet::new(); + for log in &logs { + if seen_tx_hashes.contains(&log.transaction_hash.unwrap()) { + continue; + } + tasks.push(client.get_transaction_receipt(log.transaction_hash.unwrap())); + seen_tx_hashes.insert(log.transaction_hash.unwrap()); + } + let receipt_logs = futures::future::join_all(tasks) + .await + .into_iter() + .collect::, _>>() + .unwrap() + .into_iter() + .flat_map(|receipt| receipt.unwrap().logs) + .collect::>(); + assert_eq!(receipt_logs.len(), logs.len()); + for (receipt_log, log) in receipt_logs.iter().zip(logs.iter()) { + assert_eq!(receipt_log.transaction_hash, log.transaction_hash); + assert_eq!(receipt_log.block_number, log.block_number); + assert_eq!(receipt_log.log_index, log.log_index); + } } #[tokio::test(flavor = "multi_thread")] From bf5bfe0a3af293152f84f965e21856bc740aac0d Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Mon, 25 Mar 2024 13:11:44 -0700 Subject: [PATCH 109/430] feat(anvil): Add memery limit to anvil node (#7482) * add memery-limit to anvil node * Update config.rs * use unwrap_or_default * add variable alias * custom value to 100 for memory-limit * Update config.rs * Update config.rs * remove old comment * nightly fmt * fmt * docs --------- Co-authored-by: Matthias Seitz --- crates/anvil/src/cmd.rs | 4 ++++ crates/anvil/src/config.rs | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index 508c3dc8e..2c5e08ba1 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -171,6 +171,9 @@ pub struct NodeArgs { #[command(flatten)] pub server_config: ServerConfig, + /// The memory limit per EVM execution in bytes. + #[arg(long)] + pub memory_limit: Option, } #[cfg(windows)] @@ -235,6 +238,7 @@ impl NodeArgs { .with_optimism(self.evm_opts.optimism) .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.memory_limit) } fn account_generator(&self) -> AccountGenerator { diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 354bf2c58..039d7779b 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -172,6 +172,8 @@ pub struct NodeConfig { pub enable_optimism: bool, /// Slots in an epoch pub slots_in_an_epoch: u64, + /// Memory configuration layer + pub memory_limit: Option, } impl NodeConfig { @@ -407,11 +409,18 @@ impl Default for NodeConfig { disable_default_create2_deployer: false, enable_optimism: false, slots_in_an_epoch: 32, + memory_limit: None, } } } impl NodeConfig { + /// Returns the memory limit of the node + #[must_use] + pub fn with_memory_limit(mut self, mems_value: Option) -> Self { + self.memory_limit = mems_value; + self + } /// Returns the base fee to use pub fn get_base_fee(&self) -> U256 { self.base_fee @@ -831,6 +840,10 @@ impl NodeConfig { cfg.disable_block_gas_limit = self.disable_block_gas_limit; cfg.handler_cfg.is_optimism = self.enable_optimism; + if let Some(value) = self.memory_limit { + cfg.memory_limit = value; + } + let env = revm::primitives::Env { cfg: cfg.cfg_env, block: BlockEnv { From 563e0624ba5a4a317202b4c9bc1d0120ed7c49f0 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 25 Mar 2024 22:27:01 +0100 Subject: [PATCH 110/430] chore: move var to evm args (#7492) --- crates/anvil/src/cmd.rs | 9 +++++---- crates/anvil/src/config.rs | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index 2c5e08ba1..9cde54553 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -171,9 +171,6 @@ pub struct NodeArgs { #[command(flatten)] pub server_config: ServerConfig, - /// The memory limit per EVM execution in bytes. - #[arg(long)] - pub memory_limit: Option, } #[cfg(windows)] @@ -238,7 +235,7 @@ impl NodeArgs { .with_optimism(self.evm_opts.optimism) .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.memory_limit) + .with_memory_limit(self.evm_opts.memory_limit) } fn account_generator(&self) -> AccountGenerator { @@ -507,6 +504,10 @@ pub struct AnvilEvmArgs { /// Disable the default create2 deployer #[arg(long, visible_alias = "no-create2")] pub disable_default_create2_deployer: bool, + + /// The memory limit per EVM execution in bytes. + #[arg(long)] + pub memory_limit: Option, } /// Resolves an alias passed as fork-url to the matching url defined in the rpc_endpoints section diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 039d7779b..96e7896eb 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -172,7 +172,7 @@ pub struct NodeConfig { pub enable_optimism: bool, /// Slots in an epoch pub slots_in_an_epoch: u64, - /// Memory configuration layer + /// The memory limit per EVM execution in bytes. pub memory_limit: Option, } From b0698bbe0ed8a3d0799845a12e8f1ec3fbc69144 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 26 Mar 2024 16:37:30 +0200 Subject: [PATCH 111/430] fix(config): inline config intermingled (#7496) fix(config): inline config intermingled when multiple contracts in same file --- crates/config/src/inline/natspec.rs | 51 +++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/crates/config/src/inline/natspec.rs b/crates/config/src/inline/natspec.rs index 3b62d40cc..27742eb56 100644 --- a/crates/config/src/inline/natspec.rs +++ b/crates/config/src/inline/natspec.rs @@ -189,13 +189,13 @@ impl SolangParser { } let Ok((pt, comments)) = solang_parser::parse(src, 0) else { return }; - let mut prev_end = 0; for item in &pt.0 { let pt::SourceUnitPart::ContractDefinition(c) = item else { continue }; let Some(id) = c.name.as_ref() else { continue }; if id.name != contract_name { continue }; + let mut prev_end = c.loc.start(); for part in &c.parts { let pt::ContractPart::FunctionDefinition(f) = part else { continue }; let start = f.loc.start(); @@ -215,7 +215,6 @@ impl SolangParser { } prev_end = f.loc.end(); } - prev_end = c.loc.end(); } } } @@ -401,4 +400,52 @@ contract FuzzInlineConf is DSTest { docs: conf.to_string(), } } + + #[test] + fn parse_solang_multiple_contracts_from_same_file() { + let src = r#" +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity >=0.8.0; + +import "ds-test/test.sol"; + +contract FuzzInlineConf is DSTest { + /// forge-config: default.fuzz.runs = 1 + function testInlineConfFuzz1() {} +} + +contract FuzzInlineConf2 is DSTest { + /// forge-config: default.fuzz.runs = 2 + function testInlineConfFuzz2() {} +} + "#; + let mut natspecs = vec![]; + let solang = SolangParser::new(); + let id = || "inline/FuzzInlineConf.t.sol:FuzzInlineConf".to_string(); + let default_line = || "0:0:0".to_string(); + solang.parse(&mut natspecs, src, &id(), "FuzzInlineConf"); + assert_eq!( + natspecs, + [NatSpec { + contract: id(), + function: "testInlineConfFuzz1".to_string(), + line: default_line(), + docs: "forge-config: default.fuzz.runs = 1".to_string(), + },] + ); + + let mut natspecs = vec![]; + let id = || "inline/FuzzInlineConf2.t.sol:FuzzInlineConf2".to_string(); + solang.parse(&mut natspecs, src, &id(), "FuzzInlineConf2"); + assert_eq!( + natspecs, + [NatSpec { + contract: id(), + function: "testInlineConfFuzz2".to_string(), + line: default_line(), + // should not get config from previous contract + docs: "forge-config: default.fuzz.runs = 2".to_string(), + },] + ); + } } From 157a253f486a20da2df71ef50a1eb76acc038f09 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Tue, 26 Mar 2024 19:26:18 +0400 Subject: [PATCH 112/430] fix: avoid creating extra journal entries (#7493) * fix: avoid creating extra journal entries * add test * fmt * graceful error handling --- crates/evm/core/src/backend/mod.rs | 33 +++++++++++++---------- crates/evm/evm/src/inspectors/stack.rs | 35 +++++++++++++++---------- crates/forge/tests/it/repros.rs | 2 ++ testdata/default/repros/Issue7481.t.sol | 22 ++++++++++++++++ 4 files changed, 64 insertions(+), 28 deletions(-) create mode 100644 testdata/default/repros/Issue7481.t.sol diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index f55259f4d..ca6cbb1ef 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -17,7 +17,7 @@ use revm::{ precompile::{PrecompileSpecId, Precompiles}, primitives::{ Account, AccountInfo, Bytecode, CreateScheme, Env, EnvWithHandlerCfg, HashMap as Map, Log, - ResultAndState, SpecId, StorageSlot, TransactTo, KECCAK_EMPTY, + ResultAndState, SpecId, State, StorageSlot, TransactTo, KECCAK_EMPTY, }, Database, DatabaseCommit, Inspector, JournaledState, }; @@ -1888,7 +1888,19 @@ fn commit_transaction>( }; trace!(elapsed = ?now.elapsed(), "transacted transaction"); - apply_state_changeset(res.state, journaled_state, fork); + apply_state_changeset(res.state, journaled_state, fork)?; + Ok(()) +} + +/// Helper method which updates data in the state with the data from the database. +pub fn update_state(state: &mut State, db: &mut DB) -> Result<(), DB::Error> { + for (addr, acc) in state.iter_mut() { + acc.info = db.basic(*addr)?.unwrap_or_default(); + for (key, val) in acc.storage.iter_mut() { + val.present_value = db.storage(*addr, *key)?; + } + } + Ok(()) } @@ -1898,19 +1910,12 @@ fn apply_state_changeset( state: Map, journaled_state: &mut JournaledState, fork: &mut Fork, -) { - let changed_accounts = state.keys().copied().collect::>(); +) -> Result<(), DatabaseError> { // commit the state and update the loaded accounts fork.db.commit(state); - for addr in changed_accounts { - // reload all changed accounts by removing them from the journaled state and reloading them - // from the now updated database - if journaled_state.state.remove(&addr).is_some() { - let _ = journaled_state.load_account(addr, &mut fork.db); - } - if fork.journaled_state.state.remove(&addr).is_some() { - let _ = fork.journaled_state.load_account(addr, &mut fork.db); - } - } + update_state(&mut journaled_state.state, &mut fork.db)?; + update_state(&mut fork.journaled_state.state, &mut fork.db)?; + + Ok(()) } diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index ea0af6bbc..6f83bf43f 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -3,7 +3,10 @@ use super::{ StackSnapshotType, TracingInspector, TracingInspectorConfig, }; use alloy_primitives::{Address, Bytes, Log, U256}; -use foundry_evm_core::{backend::DatabaseExt, debug::DebugArena}; +use foundry_evm_core::{ + backend::{update_state, DatabaseExt}, + debug::DebugArena, +}; use foundry_evm_coverage::HitMaps; use foundry_evm_traces::CallTraceArena; use revm::{ @@ -11,7 +14,7 @@ use revm::{ CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, Gas, InstructionResult, Interpreter, InterpreterResult, }, - primitives::{BlockEnv, Env, EnvWithHandlerCfg, ExecutionResult, Output, State, TransactTo}, + primitives::{BlockEnv, Env, EnvWithHandlerCfg, ExecutionResult, Output, TransactTo}, DatabaseCommit, EvmContext, Inspector, }; use std::{collections::HashMap, sync::Arc}; @@ -228,16 +231,6 @@ macro_rules! call_inspectors_adjust_depth { }; } -/// Helper method which updates data in the state with the data from the database. -fn update_state(state: &mut State, db: &mut DB) { - for (addr, acc) in state.iter_mut() { - acc.info = db.basic(*addr).unwrap().unwrap_or_default(); - for (key, val) in acc.storage.iter_mut() { - val.present_value = db.storage(*addr, *key).unwrap(); - } - } -} - /// The collected results of [`InspectorStack`]. pub struct InspectorData { pub logs: Vec, @@ -500,8 +493,22 @@ impl InspectorStack { ecx.db.commit(res.state.clone()); // Update both states with new DB data after commit. - update_state(&mut ecx.journaled_state.state, &mut ecx.db); - update_state(&mut res.state, &mut ecx.db); + if let Err(e) = update_state(&mut ecx.journaled_state.state, &mut ecx.db) { + let res = InterpreterResult { + result: InstructionResult::Revert, + output: Bytes::from(e.to_string()), + gas, + }; + return (res, None) + } + if let Err(e) = update_state(&mut res.state, &mut ecx.db) { + let res = InterpreterResult { + result: InstructionResult::Revert, + output: Bytes::from(e.to_string()), + gas, + }; + return (res, None) + } // Merge transaction journal into the active journal. for (addr, acc) in res.state { diff --git a/crates/forge/tests/it/repros.rs b/crates/forge/tests/it/repros.rs index 38cf76331..02f1aa15e 100644 --- a/crates/forge/tests/it/repros.rs +++ b/crates/forge/tests/it/repros.rs @@ -328,3 +328,5 @@ test_repro!(6634; |config| { cheats_config.always_use_create_2_factory = true; config.runner.cheats_config = std::sync::Arc::new(cheats_config); }); + +test_repro!(7481); diff --git a/testdata/default/repros/Issue7481.t.sol b/testdata/default/repros/Issue7481.t.sol new file mode 100644 index 000000000..eb568dc94 --- /dev/null +++ b/testdata/default/repros/Issue7481.t.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +// https://github.com/foundry-rs/foundry/issues/7481 +// This test ensures that we don't panic +contract Issue7481Test is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + function testFailTransact() public { + vm.createSelectFork("rpcAlias", 19514903); + + // Transfer some funds to sender of tx being transacted to ensure that it appears in journaled state + payable(address(0x5C60cD7a3D50877Bfebd484750FBeb245D936dAD)).call{value: 1}(""); + vm.transact(0xccfd66fc409a633a99b5b75b0e9a2040fcf562d03d9bee3fefc1a5c0eb49c999); + + // Revert the current call to ensure that revm can revert state journal + revert("HERE"); + } +} From 9148dbc2fe0c72e249669b11d04caae593fa6113 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Tue, 26 Mar 2024 20:52:12 +0400 Subject: [PATCH 113/430] chore: fix clippy and remove goerli usage from tests (#7501) * clippy * rm goerli tests * fix nonce * fix test * fix test --- crates/anvil/src/eth/backend/executor.rs | 14 ++++++++------ crates/cast/bin/cmd/wallet/mod.rs | 2 +- crates/forge/tests/it/test_helpers.rs | 6 ++++++ crates/script/src/artifacts.rs | 1 - crates/script/src/execute.rs | 4 +--- crates/script/src/simulate.rs | 9 +-------- testdata/default/cheats/RpcUrls.t.sol | 7 +++++-- testdata/default/repros/Issue2956.t.sol | 4 ++-- testdata/default/repros/Issue3221.t.sol | 4 ++-- testdata/default/repros/Issue3223.t.sol | 6 ++---- testdata/default/repros/Issue3674.t.sol | 2 +- 11 files changed, 29 insertions(+), 30 deletions(-) diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index 320d46884..5e68924c9 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -174,12 +174,14 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' let ExecutedTransaction { transaction, logs, out, traces, exit_reason: exit, .. } = tx; logs_bloom(logs.clone(), &mut bloom); - let contract_address = if let Some(Output::Create(_, contract_address)) = out { - trace!(target: "backend", "New contract deployed: at {:?}", contract_address); - contract_address - } else { - None - }; + let contract_address = out.as_ref().and_then(|out| { + if let Output::Create(_, contract_address) = out { + trace!(target: "backend", "New contract deployed: at {:?}", contract_address); + *contract_address + } else { + None + } + }); let transaction_index = transaction_infos.len() as u32; let info = TransactionInfo { diff --git a/crates/cast/bin/cmd/wallet/mod.rs b/crates/cast/bin/cmd/wallet/mod.rs index 931ecefa4..50467421c 100644 --- a/crates/cast/bin/cmd/wallet/mod.rs +++ b/crates/cast/bin/cmd/wallet/mod.rs @@ -356,7 +356,7 @@ flag to set your key via: let phrase = Mnemonic::::new_from_phrase(mnemonic.as_str())?.to_phrase(); let builder = MnemonicBuilder::::default().phrase(phrase.as_str()); let derivation_path = "m/44'/60'/0'/0/"; - let index = if let Some(i) = mnemonic_index { i } else { 0 }; + let index = mnemonic_index.unwrap_or_default(); let wallet = builder .clone() .derivation_path(format!("{derivation_path}{index}"))? diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index bf31842b8..0112d4bb9 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -292,6 +292,12 @@ pub fn rpc_endpoints() -> RpcEndpoints { "https://eth-mainnet.alchemyapi.io/v2/Lc7oIGYeL_QvInzI0Wiu_pOZZDEKBrdf".to_string(), ), ), + ( + "rpcAliasSepolia", + RpcEndpoint::Url( + "https://eth-sepolia.g.alchemy.com/v2/Lc7oIGYeL_QvInzI0Wiu_pOZZDEKBrdf".to_string(), + ), + ), ("rpcEnvAlias", RpcEndpoint::Env("${RPC_ENV_ALIAS}".to_string())), ]) } diff --git a/crates/script/src/artifacts.rs b/crates/script/src/artifacts.rs index 0a2bd77dd..ebd149f49 100644 --- a/crates/script/src/artifacts.rs +++ b/crates/script/src/artifacts.rs @@ -3,7 +3,6 @@ use alloy_json_abi::JsonAbi; /// Bundles info of an artifact pub struct ArtifactInfo<'a> { pub contract_name: String, - pub contract_id: String, pub abi: &'a JsonAbi, pub code: &'a Vec, } diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index cdf4353d3..4a3a34852 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -274,8 +274,6 @@ For more information, please see https://eips.ethereum.org/EIPS/eip-3855", /// Container for data being collected after execution. pub struct ExecutionArtifacts { - /// Mapping from contract to its runtime code. - pub known_contracts: ContractsByArtifact, /// Trace decoder used to decode traces. pub decoder: CallTraceDecoder, /// Return values from the execution result. @@ -327,7 +325,7 @@ impl ExecutedState { build_data: self.build_data, execution_data: self.execution_data, execution_result: self.execution_result, - execution_artifacts: ExecutionArtifacts { known_contracts, decoder, returns, rpc_data }, + execution_artifacts: ExecutionArtifacts { decoder, returns, rpc_data }, }) } diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index 07e962ddf..e63c5efce 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -66,7 +66,6 @@ impl PreSimulationState { script_config: self.script_config, script_wallets: self.script_wallets, build_data: self.build_data, - execution_data: self.execution_data, execution_artifacts: self.execution_artifacts, transactions, }) @@ -199,12 +198,7 @@ impl PreSimulationState { if let Ok(Some((_, (abi, code)))) = contracts.find_by_name_or_identifier(contract_name) { - let info = ArtifactInfo { - contract_name: contract_name.to_string(), - contract_id: contract_id.to_string(), - abi, - code, - }; + let info = ArtifactInfo { contract_name: contract_name.to_string(), abi, code }; return Some((*addr, info)); } None @@ -259,7 +253,6 @@ pub struct FilledTransactionsState { pub script_config: ScriptConfig, pub script_wallets: ScriptWallets, pub build_data: LinkedBuildData, - pub execution_data: ExecutionData, pub execution_artifacts: ExecutionArtifacts, pub transactions: VecDeque, } diff --git a/testdata/default/cheats/RpcUrls.t.sol b/testdata/default/cheats/RpcUrls.t.sol index 3d7b298f4..4e3ceba58 100644 --- a/testdata/default/cheats/RpcUrls.t.sol +++ b/testdata/default/cheats/RpcUrls.t.sol @@ -33,12 +33,15 @@ contract RpcUrlTest is DSTest { assertEq(url, envUrl); string[2][] memory allUrls = vm.rpcUrls(); - assertEq(allUrls.length, 2); + assertEq(allUrls.length, 3); string[2] memory val = allUrls[0]; assertEq(val[0], "rpcAlias"); string[2] memory env = allUrls[1]; - assertEq(env[0], "rpcEnvAlias"); + assertEq(env[0], "rpcAliasSepolia"); + + string[2] memory env2 = allUrls[2]; + assertEq(env2[0], "rpcEnvAlias"); } } diff --git a/testdata/default/repros/Issue2956.t.sol b/testdata/default/repros/Issue2956.t.sol index f77340da4..9d9e5f9ac 100644 --- a/testdata/default/repros/Issue2956.t.sol +++ b/testdata/default/repros/Issue2956.t.sol @@ -11,7 +11,7 @@ contract Issue2956Test is DSTest { uint256 fork2; function setUp() public { - fork1 = vm.createFork("https://goerli.infura.io/v3/b9794ad1ddf84dfb8c34d6bb5dca2001", 7475589); + fork1 = vm.createFork("rpcAliasSepolia", 5565573); fork2 = vm.createFork("https://api.avax-test.network/ext/bc/C/rpc", 12880747); } @@ -28,7 +28,7 @@ contract Issue2956Test is DSTest { new Counter(); vm.selectFork(fork1); - assertEq(vm.getNonce(user), 3); + assertEq(vm.getNonce(user), 1); vm.prank(user); new Counter(); } diff --git a/testdata/default/repros/Issue3221.t.sol b/testdata/default/repros/Issue3221.t.sol index 9fbc51f60..4a9dd7be4 100644 --- a/testdata/default/repros/Issue3221.t.sol +++ b/testdata/default/repros/Issue3221.t.sol @@ -11,7 +11,7 @@ contract Issue3221Test is DSTest { uint256 fork2; function setUp() public { - fork1 = vm.createFork("https://goerli.infura.io/v3/b1d3925804e74152b316ca7da97060d3", 7475589); + fork1 = vm.createFork("rpcAliasSepolia", 5565573); fork2 = vm.createFork("https://api.avax-test.network/ext/bc/C/rpc", 12880747); } @@ -27,7 +27,7 @@ contract Issue3221Test is DSTest { new Counter(); vm.selectFork(fork1); - assertEq(vm.getNonce(user), 3); + assertEq(vm.getNonce(user), 1); vm.prank(user); new Counter(); } diff --git a/testdata/default/repros/Issue3223.t.sol b/testdata/default/repros/Issue3223.t.sol index 4408d24ee..d4c5da751 100644 --- a/testdata/default/repros/Issue3223.t.sol +++ b/testdata/default/repros/Issue3223.t.sol @@ -11,7 +11,7 @@ contract Issue3223Test is DSTest { uint256 fork2; function setUp() public { - fork1 = vm.createFork("https://goerli.infura.io/v3/b9794ad1ddf84dfb8c34d6bb5dca2001", 7475589); + fork1 = vm.createFork("rpcAliasSepolia", 2362365); fork2 = vm.createFork("https://api.avax-test.network/ext/bc/C/rpc", 12880747); } @@ -25,9 +25,7 @@ contract Issue3223Test is DSTest { new Counter(); vm.selectFork(fork1); - assertEq(vm.getNonce(user), 3); - vm.prank(user); - new Counter(); + assertEq(vm.getNonce(user), 1); } } diff --git a/testdata/default/repros/Issue3674.t.sol b/testdata/default/repros/Issue3674.t.sol index f13f7f162..813f73b5d 100644 --- a/testdata/default/repros/Issue3674.t.sol +++ b/testdata/default/repros/Issue3674.t.sol @@ -9,7 +9,7 @@ contract Issue3674Test is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); function testNonceCreateSelect() public { - vm.createSelectFork("https://goerli.infura.io/v3/b9794ad1ddf84dfb8c34d6bb5dca2001"); + vm.createSelectFork("rpcAliasSepolia"); vm.createSelectFork("https://api.avax-test.network/ext/bc/C/rpc"); assert(vm.getNonce(msg.sender) > 0x17); From 35a8cce6cefdbf9691c65a9d5497557bc85d8db3 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Wed, 27 Mar 2024 11:15:03 +0200 Subject: [PATCH 114/430] perf(fuzz): use default std hasher for fuzz dict states (#7505) --- Cargo.lock | 1 - crates/evm/fuzz/Cargo.toml | 1 - crates/evm/fuzz/src/strategies/state.rs | 3 +-- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ca3a63fa4..665b53187 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3477,7 +3477,6 @@ dependencies = [ "proptest", "rand 0.8.5", "revm", - "rustc-hash", "serde", "thiserror", "tracing", diff --git a/crates/evm/fuzz/Cargo.toml b/crates/evm/fuzz/Cargo.toml index 3c20b029a..8a21d9652 100644 --- a/crates/evm/fuzz/Cargo.toml +++ b/crates/evm/fuzz/Cargo.toml @@ -39,5 +39,4 @@ rand.workspace = true serde = "1" thiserror = "1" tracing = "0.1" -rustc-hash.workspace = true indexmap.workspace = true diff --git a/crates/evm/fuzz/src/strategies/state.rs b/crates/evm/fuzz/src/strategies/state.rs index 1cf1d3788..a01c55668 100644 --- a/crates/evm/fuzz/src/strategies/state.rs +++ b/crates/evm/fuzz/src/strategies/state.rs @@ -17,8 +17,7 @@ use std::{fmt, sync::Arc}; // We're using `IndexSet` to have a stable element order when restoring persisted state, as well as // for performance when iterating over the sets. -type FxIndexSet = - indexmap::set::IndexSet>; +type FxIndexSet = indexmap::set::IndexSet; /// A set of arbitrary 32 byte data from the VM used to generate values for the strategy. /// From 9881e7de5d112c8d602b466a901b83fc99667906 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 27 Mar 2024 22:33:09 +0100 Subject: [PATCH 115/430] chore(script): use `try_join_all` in `build_runners` (#7508) --- crates/script/src/simulate.rs | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index e63c5efce..96b1eaefe 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -21,7 +21,7 @@ use foundry_common::{ get_contract_name, provider::ethers::RpcUrl, shell, types::ToAlloy, ContractsByArtifact, }; use foundry_evm::traces::render_trace_arena; -use futures::future::join_all; +use futures::future::{join_all, try_join_all}; use parking_lot::RwLock; use std::{ collections::{BTreeMap, HashMap, VecDeque}, @@ -207,7 +207,7 @@ impl PreSimulationState { } /// Build [ScriptRunner] forking given RPC for each RPC used in the script. - async fn build_runners(&self) -> Result> { + async fn build_runners(&self) -> Result> { let rpcs = self.execution_artifacts.rpc_data.total_rpcs.clone(); if !shell::verbosity().is_silent() { let n = rpcs.len(); @@ -215,17 +215,13 @@ impl PreSimulationState { println!("\n## Setting up {n} EVM{s}."); } - let futs = rpcs - .into_iter() - .map(|rpc| async move { - let mut script_config = self.script_config.clone(); - script_config.evm_opts.fork_url = Some(rpc.clone()); - let runner = script_config.get_runner().await?; - Ok((rpc.clone(), runner)) - }) - .collect::>(); - - join_all(futs).await.into_iter().collect() + let futs = rpcs.into_iter().map(|rpc| async move { + let mut script_config = self.script_config.clone(); + script_config.evm_opts.fork_url = Some(rpc.clone()); + let runner = script_config.get_runner().await?; + Ok((rpc.clone(), runner)) + }); + try_join_all(futs).await } /// If simulation is disabled, converts transactions into [TransactionWithMetadata] type From 369597f2b8a83c775fdb37f7c60fe5f0beb573e1 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 28 Mar 2024 03:41:39 +0100 Subject: [PATCH 116/430] perf: use `jemalloc` as the global allocator on unix (try 2) (#7448) --- .github/workflows/release.yml | 6 ++++-- Cargo.lock | 24 ++++++++++++++++++++++++ Cargo.toml | 2 ++ crates/anvil/Cargo.toml | 18 ++++++++---------- crates/anvil/src/anvil.rs | 6 ++++++ crates/cast/Cargo.toml | 4 ++++ crates/cast/bin/main.rs | 4 ++++ crates/chisel/Cargo.toml | 4 ++++ crates/chisel/bin/main.rs | 4 ++++ crates/forge/Cargo.toml | 4 ++++ crates/forge/bin/main.rs | 8 ++++++-- 11 files changed, 70 insertions(+), 14 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 071b0f628..c7422bfb6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -135,8 +135,10 @@ jobs: target="${{ matrix.target }}" flags=() - # `keccak-asm` does not support MSVC or aarch64 Linux. - [[ "$target" != *msvc* && "$target" != "aarch64-unknown-linux-gnu" ]] && flags+=(--features=asm-keccak) + # `jemalloc` and `keccak-asm` are not supported on MSVC or aarch64 Linux. + if [[ "$target" != *msvc* && "$target" != "aarch64-unknown-linux-gnu" ]]; then + flags+=(--features asm-keccak,jemalloc) + fi [[ "$target" == *windows* ]] && exe=".exe" diff --git a/Cargo.lock b/Cargo.lock index 665b53187..05a4e79a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -576,6 +576,7 @@ dependencies = [ "serde_repr", "tempfile", "thiserror", + "tikv-jemallocator", "tokio", "tower", "tracing", @@ -1338,6 +1339,7 @@ dependencies = [ "serde", "serde_json", "tempfile", + "tikv-jemallocator", "tokio", "tracing", "vergen", @@ -1402,6 +1404,7 @@ dependencies = [ "serial_test", "solang-parser", "strum 0.26.2", + "tikv-jemallocator", "time", "tokio", "tracing", @@ -3008,6 +3011,7 @@ dependencies = [ "svm-rs 0.4.1", "tempfile", "thiserror", + "tikv-jemallocator", "tokio", "tower-http", "tracing", @@ -7636,6 +7640,26 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "tikv-jemalloc-sys" +version = "0.5.4+5.3.0-patched" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9402443cb8fd499b6f327e40565234ff34dbda27460c5b47db0db77443dd85d1" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "tikv-jemallocator" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965fe0c26be5c56c94e38ba547249074803efd52adfb66de62107d95aab3eaca" +dependencies = [ + "libc", + "tikv-jemalloc-sys", +] + [[package]] name = "time" version = "0.3.34" diff --git a/Cargo.toml b/Cargo.toml index e8dac00db..640c652ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,6 +65,7 @@ ruint.opt-level = 3 sha2.opt-level = 3 sha3.opt-level = 3 tiny-keccak.opt-level = 3 +bitvec.opt-level = 3 # fuzzing proptest.opt-level = 3 @@ -208,6 +209,7 @@ tracing = "0.1" tracing-subscriber = "0.3" vergen = { version = "8", default-features = false } indexmap = "2.2" +tikv-jemallocator = "0.5.4" axum = "0.6" hyper = "0.14" diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index 18682e5d2..7a554a022 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -16,11 +16,7 @@ path = "src/anvil.rs" required-features = ["cli"] [build-dependencies] -vergen = { workspace = true, default-features = false, features = [ - "build", - "git", - "gitcl", -] } +vergen = { workspace = true, default-features = false, features = ["build", "git", "gitcl"] } [dependencies] # foundry internal @@ -81,11 +77,7 @@ rand = "0.8" eyre.workspace = true # cli -clap = { version = "4", features = [ - "derive", - "env", - "wrap_help", -], optional = true } +clap = { version = "4", features = ["derive", "env", "wrap_help"], optional = true } clap_complete = { version = "4", optional = true } chrono.workspace = true auto_impl = "1" @@ -94,6 +86,9 @@ fdlimit = { version = "0.3", optional = true } clap_complete_fig = "4" ethereum-forkid = "0.12" +[target.'cfg(unix)'.dependencies] +tikv-jemallocator = { workspace = true, optional = true } + [dev-dependencies] alloy-json-abi.workspace = true ethers = { workspace = true, features = ["abigen"] } @@ -109,3 +104,6 @@ default = ["cli"] cmd = ["clap", "clap_complete", "ctrlc", "anvil-server/clap"] cli = ["tokio/full", "cmd", "fdlimit"] asm-keccak = ["alloy-primitives/asm-keccak"] +# TODO: parity dependencies are not compatible with a different global allocator. +# jemalloc = ["dep:tikv-jemallocator"] +jemalloc = [] diff --git a/crates/anvil/src/anvil.rs b/crates/anvil/src/anvil.rs index 8ce75751c..54a5e41cf 100644 --- a/crates/anvil/src/anvil.rs +++ b/crates/anvil/src/anvil.rs @@ -4,6 +4,12 @@ use anvil::cmd::NodeArgs; use clap::{CommandFactory, Parser, Subcommand}; use foundry_cli::utils; +// TODO: parity dependencies are not compatible with a different global allocator. +#[cfg(any())] +#[cfg(all(feature = "jemalloc", unix))] +#[global_allocator] +static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; + /// A fast local Ethereum development node. #[derive(Parser)] #[command(name = "anvil", version = anvil::VERSION_MESSAGE, next_display_order = None)] diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index e97ae59c5..5361b3dea 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -75,6 +75,9 @@ tracing.workspace = true yansi = "0.5" evmole = "0.3.1" +[target.'cfg(unix)'.dependencies] +tikv-jemallocator = { workspace = true, optional = true } + [dev-dependencies] foundry-test-utils.workspace = true async-trait = "0.1" @@ -85,6 +88,7 @@ default = ["rustls"] rustls = ["foundry-cli/rustls", "foundry-wallets/rustls"] openssl = ["foundry-cli/openssl", "foundry-wallets/openssl"] asm-keccak = ["alloy-primitives/asm-keccak"] +jemalloc = ["dep:tikv-jemallocator"] [[bench]] name = "vanity" diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index 9ea81369e..a81c1acd8 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -30,6 +30,10 @@ pub mod tx; use opts::{Cast as Opts, CastSubcommand, ToBaseArgs}; +#[cfg(all(feature = "jemalloc", unix))] +#[global_allocator] +static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; + #[tokio::main] async fn main() -> Result<()> { handler::install(); diff --git a/crates/chisel/Cargo.toml b/crates/chisel/Cargo.toml index 589beaa3e..9829abb7c 100644 --- a/crates/chisel/Cargo.toml +++ b/crates/chisel/Cargo.toml @@ -50,6 +50,9 @@ tokio = { version = "1", features = ["full"] } yansi = "0.5" tracing.workspace = true +[target.'cfg(unix)'.dependencies] +tikv-jemallocator = { workspace = true, optional = true } + [dev-dependencies] criterion = { version = "0.5", features = ["async_tokio"] } once_cell = "1" @@ -61,6 +64,7 @@ default = ["rustls"] rustls = ["reqwest/rustls-tls", "reqwest/rustls-tls-native-roots"] openssl = ["foundry-compilers/openssl", "reqwest/default-tls"] asm-keccak = ["alloy-primitives/asm-keccak"] +jemalloc = ["dep:tikv-jemallocator"] [[bench]] name = "session_source" diff --git a/crates/chisel/bin/main.rs b/crates/chisel/bin/main.rs index 43b6a1b21..0cca09bf9 100644 --- a/crates/chisel/bin/main.rs +++ b/crates/chisel/bin/main.rs @@ -27,6 +27,10 @@ use std::path::PathBuf; use tracing::debug; use yansi::Paint; +#[cfg(all(feature = "jemalloc", unix))] +#[global_allocator] +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); diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 84abd4637..f5a6b27c7 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -87,6 +87,9 @@ hyper.workspace = true tower-http = { workspace = true, features = ["fs"] } opener = "0.6" +[target.'cfg(unix)'.dependencies] +tikv-jemallocator = { workspace = true, optional = true } + [dev-dependencies] anvil.workspace = true foundry-test-utils.workspace = true @@ -110,6 +113,7 @@ rustls = [ ] openssl = ["foundry-cli/openssl", "reqwest/default-tls", "foundry-wallets/openssl"] asm-keccak = ["alloy-primitives/asm-keccak"] +jemalloc = ["dep:tikv-jemallocator"] [[bench]] name = "test" diff --git a/crates/forge/bin/main.rs b/crates/forge/bin/main.rs index 0743d69e6..b5ba3228f 100644 --- a/crates/forge/bin/main.rs +++ b/crates/forge/bin/main.rs @@ -7,11 +7,15 @@ use eyre::Result; use foundry_cli::{handler, utils}; mod cmd; -mod opts; - use cmd::{cache::CacheSubcommands, generate::GenerateSubcommands, watch}; + +mod opts; use opts::{Forge, ForgeSubcommand}; +#[cfg(all(feature = "jemalloc", unix))] +#[global_allocator] +static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; + fn main() -> Result<()> { handler::install(); utils::load_dotenv(); From 39ac1a1b16cb34a64a67df6ba4446f5539eebf2f Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Thu, 28 Mar 2024 11:48:35 +0200 Subject: [PATCH 117/430] chore: remove misleading/unneeded FxIndexSet type (#7511) --- crates/evm/fuzz/src/strategies/state.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/crates/evm/fuzz/src/strategies/state.rs b/crates/evm/fuzz/src/strategies/state.rs index a01c55668..df70ba885 100644 --- a/crates/evm/fuzz/src/strategies/state.rs +++ b/crates/evm/fuzz/src/strategies/state.rs @@ -6,6 +6,7 @@ use alloy_primitives::{Address, Bytes, Log, B256, U256}; use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; use foundry_config::FuzzDictionaryConfig; use foundry_evm_core::utils::StateChangeset; +use indexmap::IndexSet; use parking_lot::RwLock; use proptest::prelude::{BoxedStrategy, Strategy}; use revm::{ @@ -15,21 +16,19 @@ use revm::{ }; use std::{fmt, sync::Arc}; -// We're using `IndexSet` to have a stable element order when restoring persisted state, as well as -// for performance when iterating over the sets. -type FxIndexSet = indexmap::set::IndexSet; - /// A set of arbitrary 32 byte data from the VM used to generate values for the strategy. /// /// Wrapped in a shareable container. pub type EvmFuzzState = Arc>; +// We're using `IndexSet` to have a stable element order when restoring persisted state, as well as +// for performance when iterating over the sets. #[derive(Default)] pub struct FuzzDictionary { /// Collected state values. - state_values: FxIndexSet<[u8; 32]>, + state_values: IndexSet<[u8; 32]>, /// Addresses that already had their PUSH bytes collected. - addresses: FxIndexSet
, + addresses: IndexSet
, } impl fmt::Debug for FuzzDictionary { @@ -43,22 +42,22 @@ impl fmt::Debug for FuzzDictionary { impl FuzzDictionary { #[inline] - pub fn values(&self) -> &FxIndexSet<[u8; 32]> { + pub fn values(&self) -> &IndexSet<[u8; 32]> { &self.state_values } #[inline] - pub fn values_mut(&mut self) -> &mut FxIndexSet<[u8; 32]> { + pub fn values_mut(&mut self) -> &mut IndexSet<[u8; 32]> { &mut self.state_values } #[inline] - pub fn addresses(&self) -> &FxIndexSet
{ + pub fn addresses(&self) -> &IndexSet
{ &self.addresses } #[inline] - pub fn addresses_mut(&mut self) -> &mut FxIndexSet
{ + pub fn addresses_mut(&mut self) -> &mut IndexSet
{ &mut self.addresses } } From 345d000e22e596adfb1171332e5d45cc33d368f1 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 28 Mar 2024 17:18:45 +0400 Subject: [PATCH 118/430] feat: `vm.sign` for scripts (#7454) * feat: vm.sign for script wallets * more tests * clippy * if let some else * review fixes --- crates/cheatcodes/assets/cheatcodes.json | 44 ++++++++++++++++++- crates/cheatcodes/spec/src/vm.rs | 32 ++++++++++++-- crates/cheatcodes/src/evm.rs | 14 ++++++ crates/cheatcodes/src/utils.rs | 54 +++++++++++++++++++++--- crates/forge/tests/cli/script.rs | 22 ++++++++++ crates/test-utils/src/script.rs | 5 ++- testdata/cheats/Vm.sol | 2 + testdata/default/cheats/Broadcast.t.sol | 42 ++++++++++++++++++ 8 files changed, 201 insertions(+), 14 deletions(-) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 9c69772b6..dea514322 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -2941,7 +2941,7 @@ { "func": { "id": "broadcast_0", - "description": "Using the address that calls the test contract, has the next call (at this call depth only)\ncreate a transaction that can later be signed and sent onchain.", + "description": "Has the next call (at this call depth only) create transactions that can later be signed and sent onchain.\nBroadcasting address is determined by checking the following in order:\n1. If `--sender` argument was provided, that address is used.\n2. If exactly one signer (e.g. private key, hw wallet, keystore) is set when `forge broadcast` is invoked, that signer is used.\n3. Otherwise, default foundry sender (1804c8AB1F12E6bbf3894d4083f33e07309d1f38) is used.", "declaration": "function broadcast() external;", "visibility": "external", "mutability": "", @@ -7081,6 +7081,46 @@ { "func": { "id": "sign_1", + "description": "Signs `digest` with signer provided to script using the secp256k1 curve.\nIf `--sender` is provided, the signer with provided address is used, otherwise,\nif exactly one signer is provided to the script, that signer is used.\nRaises error if signer passed through `--sender` does not match any unlocked signers or\nif `--sender` is not provided and not exactly one signer is passed to the script.", + "declaration": "function sign(bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s);", + "visibility": "external", + "mutability": "pure", + "signature": "sign(bytes32)", + "selector": "0x799cd333", + "selectorBytes": [ + 121, + 156, + 211, + 51 + ] + }, + "group": "evm", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "sign_2", + "description": "Signs `digest` with signer provided to script using the secp256k1 curve.\nRaises error if none of the signers passed into the script have provided address.", + "declaration": "function sign(address signer, bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s);", + "visibility": "external", + "mutability": "pure", + "signature": "sign(address,bytes32)", + "selector": "0x8c1aa205", + "selectorBytes": [ + 140, + 26, + 162, + 5 + ] + }, + "group": "evm", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "sign_3", "description": "Signs data with a `Wallet`.", "declaration": "function sign(Wallet calldata wallet, bytes32 digest) external returns (uint8 v, bytes32 r, bytes32 s);", "visibility": "external", @@ -7181,7 +7221,7 @@ { "func": { "id": "startBroadcast_0", - "description": "Using the address that calls the test contract, has all subsequent calls\n(at this call depth only) create transactions that can later be signed and sent onchain.", + "description": "Has all subsequent calls (at this call depth only) create transactions that can later be signed and sent onchain.\nBroadcasting address is determined by checking the following in order:\n1. If `--sender` argument was provided, that address is used.\n2. If exactly one signer (e.g. private key, hw wallet, keystore) is set when `forge broadcast` is invoked, that signer is used.\n3. Otherwise, default foundry sender (1804c8AB1F12E6bbf3894d4083f33e07309d1f38) is used.", "declaration": "function startBroadcast() external;", "visibility": "external", "mutability": "", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index cdcca3331..8f0de363a 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -249,6 +249,22 @@ interface Vm { #[cheatcode(group = Evm, safety = Safe)] function sign(uint256 privateKey, bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s); + /// Signs `digest` with signer provided to script using the secp256k1 curve. + /// + /// If `--sender` is provided, the signer with provided address is used, otherwise, + /// if exactly one signer is provided to the script, that signer is used. + /// + /// Raises error if signer passed through `--sender` does not match any unlocked signers or + /// if `--sender` is not provided and not exactly one signer is passed to the script. + #[cheatcode(group = Evm, safety = Safe)] + function sign(bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s); + + /// Signs `digest` with signer provided to script using the secp256k1 curve. + /// + /// Raises error if none of the signers passed into the script have provided address. + #[cheatcode(group = Evm, safety = Safe)] + function sign(address signer, bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s); + /// Signs `digest` with `privateKey` using the secp256r1 curve. #[cheatcode(group = Evm, safety = Safe)] function signP256(uint256 privateKey, bytes32 digest) external pure returns (bytes32 r, bytes32 s); @@ -1563,8 +1579,12 @@ interface Vm { // -------- Broadcasting Transactions -------- - /// Using the address that calls the test contract, has the next call (at this call depth only) - /// create a transaction that can later be signed and sent onchain. + /// Has the next call (at this call depth only) create transactions that can later be signed and sent onchain. + /// + /// Broadcasting address is determined by checking the following in order: + /// 1. If `--sender` argument was provided, that address is used. + /// 2. If exactly one signer (e.g. private key, hw wallet, keystore) is set when `forge broadcast` is invoked, that signer is used. + /// 3. Otherwise, default foundry sender (1804c8AB1F12E6bbf3894d4083f33e07309d1f38) is used. #[cheatcode(group = Scripting)] function broadcast() external; @@ -1578,8 +1598,12 @@ interface Vm { #[cheatcode(group = Scripting)] function broadcast(uint256 privateKey) external; - /// Using the address that calls the test contract, has all subsequent calls - /// (at this call depth only) create transactions that can later be signed and sent onchain. + /// Has all subsequent calls (at this call depth only) create transactions that can later be signed and sent onchain. + /// + /// Broadcasting address is determined by checking the following in order: + /// 1. If `--sender` argument was provided, that address is used. + /// 2. If exactly one signer (e.g. private key, hw wallet, keystore) is set when `forge broadcast` is invoked, that signer is used. + /// 3. Otherwise, default foundry sender (1804c8AB1F12E6bbf3894d4083f33e07309d1f38) is used. #[cheatcode(group = Scripting)] function startBroadcast() external; diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index 601c97ad0..2f3dafede 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -145,6 +145,20 @@ impl Cheatcode for sign_0Call { } } +impl Cheatcode for sign_1Call { + fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { digest } = self; + super::utils::sign_with_wallet(ccx, None, digest) + } +} + +impl Cheatcode for sign_2Call { + fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { signer, digest } = self; + super::utils::sign_with_wallet(ccx, Some(*signer), digest) + } +} + impl Cheatcode for signP256Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { privateKey, digest } = self; diff --git a/crates/cheatcodes/src/utils.rs b/crates/cheatcodes/src/utils.rs index 525120d7d..f9edc8e40 100644 --- a/crates/cheatcodes/src/utils.rs +++ b/crates/cheatcodes/src/utils.rs @@ -1,7 +1,7 @@ //! Implementations of [`Utils`](crate::Group::Utils) cheatcodes. use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Result, Vm::*}; -use alloy_primitives::{keccak256, B256, U256}; +use alloy_primitives::{keccak256, Address, B256, U256}; use alloy_signer::{ coins_bip39::{ ChineseSimplified, ChineseTraditional, Czech, English, French, Italian, Japanese, Korean, @@ -10,7 +10,8 @@ use alloy_signer::{ LocalWallet, MnemonicBuilder, Signer, SignerSync, }; use alloy_sol_types::SolValue; -use foundry_evm_core::constants::DEFAULT_CREATE2_DEPLOYER; +use foundry_common::types::{ToAlloy, ToEthers}; +use foundry_evm_core::{constants::DEFAULT_CREATE2_DEPLOYER, utils::RuntimeOrHandle}; use k256::{ ecdsa::SigningKey, elliptic_curve::{sec1::ToEncodedPoint, Curve}, @@ -49,7 +50,7 @@ impl Cheatcode for getNonce_1Call { } } -impl Cheatcode for sign_1Call { +impl Cheatcode for sign_3Call { fn apply_full(&self, _: &mut CheatsCtxt) -> Result { let Self { wallet, digest } = self; sign(&wallet.privateKey, digest) @@ -156,6 +157,10 @@ fn create_wallet(private_key: &U256, label: Option<&str>, state: &mut Cheatcodes .abi_encode()) } +fn encode_vrs(v: u8, r: U256, s: U256) -> Vec { + (U256::from(v), B256::from(r), B256::from(s)).abi_encode() +} + pub(super) 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)?; @@ -165,11 +170,46 @@ pub(super) fn sign(private_key: &U256, digest: &B256) -> Result { assert_eq!(recovered, wallet.address()); - let v = U256::from(sig.v().y_parity_byte_non_eip155().unwrap_or(sig.v().y_parity_byte())); - let r = B256::from(sig.r()); - let s = B256::from(sig.s()); + let v = sig.v().y_parity_byte_non_eip155().unwrap_or(sig.v().y_parity_byte()); + + Ok(encode_vrs(v, sig.r(), sig.s())) +} - Ok((v, r, s).abi_encode()) +pub(super) fn sign_with_wallet( + ccx: &mut CheatsCtxt, + signer: Option
, + digest: &B256, +) -> Result { + let Some(script_wallets) = &ccx.state.script_wallets else { + return Err("no wallets are available".into()); + }; + + let mut script_wallets = script_wallets.inner.lock(); + let maybe_provided_sender = script_wallets.provided_sender; + let signers = script_wallets.multi_wallet.signers()?; + + let signer = if let Some(signer) = signer { + signer + } else if let Some(provided_sender) = maybe_provided_sender { + provided_sender + } else if signers.len() == 1 { + *signers.keys().next().unwrap() + } else { + return Err("could not determine signer".into()); + }; + + let wallet = signers + .get(&signer) + .ok_or_else(|| fmt_err!("signer with address {signer} is not available"))?; + + let sig = RuntimeOrHandle::new() + .block_on(wallet.sign_hash(digest)) + .map_err(|err| fmt_err!("{err}"))?; + + let recovered = sig.recover(digest.to_ethers()).map_err(|err| fmt_err!("{err}"))?; + assert_eq!(recovered.to_alloy(), signer); + + Ok(encode_vrs(sig.v as u8, sig.r.to_alloy(), sig.s.to_alloy())) } pub(super) fn sign_p256(private_key: &U256, digest: &B256, _state: &mut Cheatcodes) -> Result { diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index e212683c1..ff03a6b57 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -1157,3 +1157,25 @@ contract ScriptC {} tester.cmd.forge_fuse().args(["script", "script/B.sol"]); tester.simulate(ScriptOutcome::OkNoEndpoint); }); + +forgetest_async!(can_sign_with_script_wallet_single, |prj, cmd| { + foundry_test_utils::util::initialize(prj.root()); + + let mut tester = ScriptTester::new_broadcast_without_endpoint(cmd, prj.root()); + tester + .add_sig("ScriptSign", "run()") + .load_private_keys(&[0]) + .await + .simulate(ScriptOutcome::OkNoEndpoint); +}); + +forgetest_async!(can_sign_with_script_wallet_multiple, |prj, cmd| { + let mut tester = ScriptTester::new_broadcast_without_endpoint(cmd, prj.root()); + let acc = tester.accounts_pub[0].to_checksum(None); + tester + .add_sig("ScriptSign", "run(address)") + .arg(&acc) + .load_private_keys(&[0, 1, 2]) + .await + .simulate(ScriptOutcome::OkRun); +}); diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index 80b06ae76..b60723d9d 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -264,6 +264,7 @@ pub enum ScriptOutcome { ScriptFailed, UnsupportedLibraries, ErrorSelectForkOnBroadcast, + OkRun, } impl ScriptOutcome { @@ -279,6 +280,7 @@ impl ScriptOutcome { Self::ScriptFailed => "script failed: ", Self::UnsupportedLibraries => "Multi chain deployment does not support library linking at the moment.", Self::ErrorSelectForkOnBroadcast => "cannot select forks during a broadcast", + Self::OkRun => "Script ran successfully", } } @@ -287,7 +289,8 @@ impl ScriptOutcome { ScriptOutcome::OkNoEndpoint | ScriptOutcome::OkSimulation | ScriptOutcome::OkBroadcast | - ScriptOutcome::WarnSpecifyDeployer => false, + ScriptOutcome::WarnSpecifyDeployer | + ScriptOutcome::OkRun => false, ScriptOutcome::MissingSender | ScriptOutcome::MissingWallet | ScriptOutcome::StaticCallNotAllowed | diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index b5a604e14..2116acf2b 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -351,6 +351,8 @@ interface Vm { function setNonceUnsafe(address account, uint64 newNonce) external; function signP256(uint256 privateKey, bytes32 digest) external pure returns (bytes32 r, bytes32 s); function sign(uint256 privateKey, bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s); + function sign(bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s); + function sign(address signer, bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s); function sign(Wallet calldata wallet, bytes32 digest) external returns (uint8 v, bytes32 r, bytes32 s); function skip(bool skipTest) external; function sleep(uint256 duration) external; diff --git a/testdata/default/cheats/Broadcast.t.sol b/testdata/default/cheats/Broadcast.t.sol index d44bc9540..6a099dc6e 100644 --- a/testdata/default/cheats/Broadcast.t.sol +++ b/testdata/default/cheats/Broadcast.t.sol @@ -528,3 +528,45 @@ contract ScriptAdditionalContracts is DSTest { new Parent(); } } + +contract SignatureTester { + address public immutable owner; + + constructor() { + owner = msg.sender; + } + + function verifySignature(bytes32 digest, uint8 v, bytes32 r, bytes32 s) public view returns (bool) { + require(ecrecover(digest, v, r, s) == owner, "Invalid signature"); + } +} + +contract ScriptSign is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + bytes32 digest = keccak256("something"); + + function run() external { + vm.startBroadcast(); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(digest); + + vm._expectCheatcodeRevert( + bytes(string.concat("signer with address ", vm.toString(address(this)), " is not available")) + ); + vm.sign(address(this), digest); + + SignatureTester tester = new SignatureTester(); + (, address caller,) = vm.readCallers(); + assertEq(tester.owner(), caller); + tester.verifySignature(digest, v, r, s); + } + + function run(address sender) external { + vm._expectCheatcodeRevert(bytes("could not determine signer")); + vm.sign(digest); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(sender, digest); + address actual = ecrecover(digest, v, r, s); + + assertEq(actual, sender); + } +} From 617dfc28cb8206a0003edcf73a6f1058adaef740 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Fri, 29 Mar 2024 03:03:24 +0400 Subject: [PATCH 119/430] fix(anvil): clean up `eth_estimateGas` (#7515) * fix(anvil): clean up eth_estimateGas * fix doc * fix doc * fix doc * review fixes --- crates/anvil/src/eth/api.rs | 229 ++++++++++++++---------------------- 1 file changed, 88 insertions(+), 141 deletions(-) diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 402bf4b75..aee967084 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -1,5 +1,5 @@ use super::{ - backend::mem::{state, BlockRequest}, + backend::mem::{state, BlockRequest, State}, sign::build_typed_transaction, }; use crate::{ @@ -2214,10 +2214,10 @@ impl EthApi { // configured gas limit let mut highest_gas_limit = request.gas.unwrap_or(block_env.gas_limit); - // check with the funds of the sender - if let Some(from) = request.from { - let gas_price = fees.gas_price.unwrap_or_default(); - if gas_price > U256::ZERO { + let gas_price = fees.gas_price.unwrap_or_default(); + // If we have non-zero gas price, cap gas limit by sender balance + if !gas_price.is_zero() { + if let Some(from) = request.from { let mut available_funds = self.backend.get_balance_with_state(&state, from)?; if let Some(value) = request.value { if value > available_funds { @@ -2228,86 +2228,42 @@ impl EthApi { } // amount of gas the sender can afford with the `gas_price` let allowance = available_funds.checked_div(gas_price).unwrap_or_default(); - if highest_gas_limit > allowance { - trace!(target: "node", "eth_estimateGas capped by limited user funds"); - highest_gas_limit = allowance; - } + highest_gas_limit = std::cmp::min(highest_gas_limit, allowance); } } - // if the provided gas limit is less than computed cap, use that - let gas_limit = std::cmp::min(request.gas.unwrap_or(highest_gas_limit), highest_gas_limit); let mut call_to_estimate = request.clone(); - call_to_estimate.gas = Some(gas_limit); + call_to_estimate.gas = Some(highest_gas_limit); // execute the call without writing to db let ethres = self.backend.call_with_state(&state, call_to_estimate, fees.clone(), block_env.clone()); - // Exceptional case: init used too much gas, we need to increase the gas limit and try - // again - if let Err(BlockchainError::InvalidTransaction(InvalidTransactionError::GasTooHigh(_))) = - ethres - { - // if price or limit was included in the request then we can execute the request - // again with the block's gas limit to check if revert is gas related or not - if request.gas.is_some() || request.gas_price.is_some() { - return Err(map_out_of_gas_err( - request, - state, - self.backend.clone(), - block_env, - fees, - gas_limit, - )); + let gas_used = match ethres.try_into()? { + GasEstimationCallResult::Success(gas) => Ok(U256::from(gas)), + GasEstimationCallResult::OutOfGas => { + Err(InvalidTransactionError::BasicOutOfGas(highest_gas_limit).into()) } - } - - let (exit, out, gas, _) = ethres?; - match exit { - return_ok!() => { - // succeeded - } - InstructionResult::OutOfGas | InstructionResult::OutOfFunds => { - return Err(InvalidTransactionError::BasicOutOfGas(gas_limit).into()) - } - // need to check if the revert was due to lack of gas or unrelated reason - // we're also checking for InvalidFEOpcode here because this can be used to trigger an error common usage in openzeppelin - return_revert!() | InstructionResult::InvalidFEOpcode => { - // if price or limit was included in the request then we can execute the request - // again with the max gas limit to check if revert is gas related or not - return if request.gas.is_some() || request.gas_price.is_some() { - Err(map_out_of_gas_err( - request, - state, - self.backend.clone(), - block_env, - fees, - gas_limit, - )) - } else { - // the transaction did fail due to lack of gas from the user - Err(InvalidTransactionError::Revert(Some(convert_transact_out(&out).0.into())) - .into()) - }; + GasEstimationCallResult::Revert(output) => { + Err(InvalidTransactionError::Revert(output).into()) } - reason => { - warn!(target: "node", "estimation failed due to {:?}", reason); - return Err(BlockchainError::EvmError(reason)); + GasEstimationCallResult::EvmError(err) => { + warn!(target: "node", "estimation failed due to {:?}", err); + Err(BlockchainError::EvmError(err)) } - } + }?; // at this point we know the call succeeded but want to find the _best_ (lowest) gas the // transaction succeeds with. we find this by doing a binary search over the // possible range NOTE: this is the gas the transaction used, which is less than the // transaction requires to succeed - let gas: U256 = U256::from(gas); + // Get the starting lowest gas needed depending on the transaction kind. let mut lowest_gas_limit = determine_base_gas_by_kind(&request); // pick a point that's close to the estimated gas let mut mid_gas_limit = std::cmp::min( - gas * U256::from(3), + gas_used * U256::from(3), (highest_gas_limit + lowest_gas_limit) / U256::from(2), ); @@ -2321,52 +2277,25 @@ impl EthApi { block_env.clone(), ); - // Exceptional case: init used too much gas, we need to increase the gas limit and try - // again - if let Err(BlockchainError::InvalidTransaction(InvalidTransactionError::GasTooHigh( - _, - ))) = ethres - { - // increase the lowest gas limit - lowest_gas_limit = mid_gas_limit; - - // new midpoint - mid_gas_limit = (highest_gas_limit + lowest_gas_limit) / U256::from(2); - continue; - } - - match ethres { - Ok((exit, _, _gas, _)) => match exit { + match ethres.try_into()? { + GasEstimationCallResult::Success(_) => { // If the transaction succeeded, we can set a ceiling for the highest gas limit // at the current midpoint, as spending any more gas would // make no sense (as the TX would still succeed). - return_ok!() => { - highest_gas_limit = mid_gas_limit; - } - // If the transaction failed due to lack of gas, we can set a floor for the - // lowest gas limit at the current midpoint, as spending any - // less gas would make no sense (as the TX would still revert due to lack of - // gas). - InstructionResult::Revert | - InstructionResult::OutOfGas | - InstructionResult::OutOfFunds | - // we're also checking for InvalidFEOpcode here because this can be used to trigger an error common usage in openzeppelin - InstructionResult::InvalidFEOpcode => { - lowest_gas_limit = mid_gas_limit; - } - // The tx failed for some other reason. - reason => { - warn!(target: "node", "estimation failed due to {:?}", reason); - return Err(BlockchainError::EvmError(reason)) - } - }, - // We've already checked for the exceptional GasTooHigh case above, so this is a - // real error. - Err(reason) => { - warn!(target: "node", "estimation failed due to {:?}", reason); - return Err(reason); + highest_gas_limit = mid_gas_limit; } - } + GasEstimationCallResult::OutOfGas | + GasEstimationCallResult::Revert(_) | + GasEstimationCallResult::EvmError(_) => { + // If the transaction failed, we can set a floor for the lowest gas limit at the + // current midpoint, as spending any less gas would make no + // sense (as the TX would still revert due to lack of gas). + // + // We don't care about the reason here, as we known that trasaction is correct + // as it succeeded earlier + lowest_gas_limit = mid_gas_limit; + } + }; // new midpoint mid_gas_limit = (highest_gas_limit + lowest_gas_limit) / U256::from(2); } @@ -2631,42 +2560,6 @@ fn ensure_return_ok(exit: InstructionResult, out: &Option) -> Result( - mut request: TransactionRequest, - state: D, - backend: Arc, - block_env: BlockEnv, - fees: FeeDetails, - gas_limit: U256, -) -> BlockchainError -where - D: DatabaseRef, -{ - request.gas = Some(backend.gas_limit()); - let (exit, out, _, _) = match backend.call_with_state(&state, request, fees, block_env) { - Ok(res) => res, - Err(err) => return err, - }; - match exit { - return_ok!() => { - // transaction succeeded by manually increasing the gas limit to - // highest, which means the caller lacks funds to pay for the tx - InvalidTransactionError::BasicOutOfGas(gas_limit).into() - } - return_revert!() => { - // reverted again after bumping the limit - InvalidTransactionError::Revert(Some(convert_transact_out(&out).0.into())).into() - } - reason => { - warn!(target: "node", "estimation failed due to {:?}", reason); - BlockchainError::EvmError(reason) - } - } -} - /// Determines the minimum gas needed for a transaction depending on the transaction kind. #[inline] fn determine_base_gas_by_kind(request: &TransactionRequest) -> U256 { @@ -2698,3 +2591,57 @@ fn determine_base_gas_by_kind(request: &TransactionRequest) -> U256 { _ => MIN_CREATE_GAS, } } + +/// Keeps result of a call to revm EVM used for gas estimation +enum GasEstimationCallResult { + Success(u64), + OutOfGas, + Revert(Option), + EvmError(InstructionResult), +} + +/// Converts the result of a call to revm EVM into a [GasEstimationCallRes]. +impl TryFrom, u64, State)>> for GasEstimationCallResult { + type Error = BlockchainError; + + fn try_from(res: Result<(InstructionResult, Option, u64, State)>) -> Result { + match res { + // Exceptional case: init used too much gas, treated as out of gas error + Err(BlockchainError::InvalidTransaction(InvalidTransactionError::GasTooHigh(_))) => { + Ok(Self::OutOfGas) + } + Err(err) => Err(err), + Ok((exit, output, gas, _)) => match exit { + return_ok!() | InstructionResult::CallOrCreate => Ok(Self::Success(gas)), + + InstructionResult::Revert => Ok(Self::Revert(output.map(|o| o.into_data()))), + + InstructionResult::OutOfGas | + InstructionResult::MemoryOOG | + InstructionResult::MemoryLimitOOG | + InstructionResult::PrecompileOOG | + InstructionResult::InvalidOperandOOG => Ok(Self::OutOfGas), + + InstructionResult::OpcodeNotFound | + InstructionResult::CallNotAllowedInsideStatic | + InstructionResult::StateChangeDuringStaticCall | + InstructionResult::InvalidFEOpcode | + InstructionResult::InvalidJump | + InstructionResult::NotActivated | + InstructionResult::StackUnderflow | + InstructionResult::StackOverflow | + InstructionResult::OutOfOffset | + InstructionResult::CreateCollision | + InstructionResult::OverflowPayment | + InstructionResult::PrecompileError | + InstructionResult::NonceOverflow | + InstructionResult::CreateContractSizeLimit | + InstructionResult::CreateContractStartingWithEF | + InstructionResult::CreateInitCodeSizeLimit | + InstructionResult::FatalExternalError | + InstructionResult::OutOfFunds | + InstructionResult::CallTooDeep => Ok(Self::EvmError(exit)), + }, + } + } +} From 452956fe491c5ba10264cf78801ab24481face34 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Fri, 29 Mar 2024 17:34:24 +0400 Subject: [PATCH 120/430] fix: coverage for libraries (#7510) * fix: coverage for internal libraries * optimize * optimize * doc * rm tests * clippy * clippy + fmt * clean up * for loop * review fixes --- crates/evm/coverage/src/analysis.rs | 205 ++++------------------------ crates/evm/coverage/src/anchors.rs | 17 ++- crates/forge/bin/cmd/coverage.rs | 18 ++- 3 files changed, 56 insertions(+), 184 deletions(-) diff --git a/crates/evm/coverage/src/analysis.rs b/crates/evm/coverage/src/analysis.rs index 1926d2b4e..a136f2d79 100644 --- a/crates/evm/coverage/src/analysis.rs +++ b/crates/evm/coverage/src/analysis.rs @@ -3,7 +3,7 @@ use foundry_common::TestFunctionExt; use foundry_compilers::artifacts::ast::{self, Ast, Node, NodeType}; use rustc_hash::FxHashMap; use semver::Version; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; /// A visitor that walks the AST of a single contract and finds coverage items. #[derive(Clone, Debug)] @@ -23,36 +23,14 @@ pub struct ContractVisitor<'a> { /// Coverage items pub items: Vec, - - /// Node IDs of this contract's base contracts, as well as IDs for referenced contracts such as - /// libraries - pub base_contract_node_ids: HashSet, } impl<'a> ContractVisitor<'a> { pub fn new(source_id: usize, source: &'a str, contract_name: String) -> Self { - Self { - source_id, - source, - contract_name, - branch_id: 0, - last_line: 0, - items: Vec::new(), - base_contract_node_ids: HashSet::new(), - } + Self { source_id, source, contract_name, branch_id: 0, last_line: 0, items: Vec::new() } } pub fn visit(mut self, contract_ast: Node) -> eyre::Result { - let linearized_base_contracts: Vec = - contract_ast.attribute("linearizedBaseContracts").ok_or_else(|| { - eyre::eyre!( - "The contract's AST node is missing a list of linearized base contracts" - ) - })?; - - // We skip the first ID because that's the ID of the contract itself - self.base_contract_node_ids.extend(&linearized_base_contracts[1..]); - // Find all functions and walk their AST for node in contract_ast.nodes { if node.node_type == NodeType::FunctionDefinition { @@ -318,25 +296,13 @@ impl<'a> ContractVisitor<'a> { }); let expr: Option = node.attribute("expression"); - match expr.as_ref().map(|expr| &expr.node_type) { + if let Some(NodeType::Identifier) = expr.as_ref().map(|expr| &expr.node_type) { // Might be a require/assert call - Some(NodeType::Identifier) => { - let name: Option = expr.and_then(|expr| expr.attribute("name")); - if let Some("assert" | "require") = name.as_deref() { - self.push_branches(&node.src, self.branch_id); - self.branch_id += 1; - } - } - // Might be a call to a library - Some(NodeType::MemberAccess) => { - let referenced_declaration_id = expr - .and_then(|expr| expr.attribute("expression")) - .and_then(|subexpr: Node| subexpr.attribute("referencedDeclaration")); - if let Some(id) = referenced_declaration_id { - self.base_contract_node_ids.insert(id); - } + let name: Option = expr.and_then(|expr| expr.attribute("name")); + if let Some("assert" | "require") = name.as_deref() { + self.push_branches(&node.src, self.branch_id); + self.branch_id += 1; } - _ => (), } Ok(()) @@ -435,9 +401,6 @@ impl<'a> ContractVisitor<'a> { pub struct SourceAnalysis { /// A collection of coverage items. pub items: Vec, - /// A mapping of contract IDs to item IDs relevant to the contract (including items in base - /// contracts). - pub contract_items: HashMap>, } /// Analyzes a set of sources to find coverage items. @@ -445,16 +408,10 @@ pub struct SourceAnalysis { pub struct SourceAnalyzer { /// A map of source IDs to their source code sources: FxHashMap, - /// A map of AST node IDs of contracts to their contract IDs. - contract_ids: FxHashMap, /// A map of contract IDs to their AST nodes. contracts: HashMap, /// A collection of coverage items. items: Vec, - /// A map of contract IDs to item IDs. - contract_items: HashMap>, - /// A map of contracts to their base contracts - contract_bases: HashMap>, } impl SourceAnalyzer { @@ -476,8 +433,6 @@ impl SourceAnalyzer { continue } - let node_id = - child.id.ok_or_else(|| eyre::eyre!("The contract's AST node has no ID"))?; let contract_id = ContractId { version: version.clone(), source_id, @@ -485,7 +440,6 @@ impl SourceAnalyzer { .attribute("name") .ok_or_else(|| eyre::eyre!("Contract has no name"))?, }; - analyzer.contract_ids.insert(node_id, contract_id.clone()); analyzer.contracts.insert(contract_id, child); } } @@ -497,11 +451,7 @@ impl SourceAnalyzer { /// /// Coverage items are found by: /// - Walking the AST of each contract (except interfaces) - /// - Recording the items and base contracts of each contract - /// - /// Finally, the item IDs of each contract and its base contracts are flattened, and the return - /// value represents all coverage items in the project, along with a mapping of contract IDs to - /// item IDs. + /// - Recording the items of each contract /// /// Each coverage item contains relevant information to find opcodes corresponding to them: the /// source ID the item is in, the source code range of the item, and the contract name the item @@ -511,127 +461,32 @@ impl SourceAnalyzer { /// two different solc versions will produce overlapping source IDs if the compiler version is /// not taken into account. pub fn analyze(mut self) -> eyre::Result { - // Analyze the contracts - self.analyze_contracts()?; - - // Flatten the data - let mut flattened: HashMap> = HashMap::new(); - for contract_id in self.contract_items.keys() { - let mut item_ids: Vec = Vec::new(); - - // - // for a specific contract (id == contract_id): - // - // self.contract_bases.get(contract_id) includes the following contracts: - // 1. all the ancestors of this contract (including parent, grandparent, ... - // contracts) - // 2. the libraries **directly** used by this contract - // - // The missing contracts are: - // 1. libraries used in ancestors of this contracts - // 2. libraries used in libraries (i.e libs indirectly used by this contract) - // - // We want to find out all the above contracts and libraries related to this contract. - - for contract_or_lib in { - // A set of contracts and libraries related to this contract (will include "this" - // contract itself) - let mut contracts_libraries: HashSet<&ContractId> = HashSet::new(); - - // we use a stack for depth-first search. - let mut stack: Vec<&ContractId> = Vec::new(); - - // push "this" contract onto the stack - stack.push(contract_id); - - while let Some(contract_or_lib) = stack.pop() { - // whenever a contract_or_lib is removed from the stack, it is added to the set - contracts_libraries.insert(contract_or_lib); - - // push all ancestors of contract_or_lib and libraries used by contract_or_lib - // onto the stack - if let Some(bases) = self.contract_bases.get(contract_or_lib) { - stack.extend( - bases.iter().filter(|base| !contracts_libraries.contains(base)), - ); - } - } - - contracts_libraries - } { - // get items of each contract or library - if let Some(items) = self.contract_items.get(contract_or_lib) { - item_ids.extend(items.iter()); - } - } - - // If there are no items for this contract, then it was most likely filtered - if !item_ids.is_empty() { - flattened.insert(contract_id.clone(), item_ids); - } - } - - Ok(SourceAnalysis { items: self.items.clone(), contract_items: flattened }) - } - - fn analyze_contracts(&mut self) -> eyre::Result<()> { - for contract_id in self.contracts.keys() { - // Find this contract's coverage items if we haven't already - if !self.contract_items.contains_key(contract_id) { - let ContractVisitor { items, base_contract_node_ids, .. } = ContractVisitor::new( - contract_id.source_id, - self.sources.get(&contract_id.source_id).unwrap_or_else(|| { - panic!( - "We should have the source code for source ID {}", - contract_id.source_id - ) - }), - contract_id.contract_name.clone(), - ) - .visit( - self.contracts - .get(contract_id) - .unwrap_or_else(|| { - panic!("We should have the AST of contract: {contract_id:?}") - }) - .clone(), - )?; - - let is_test = items.iter().any(|item| { - if let CoverageItemKind::Function { name } = &item.kind { - name.is_test() - } else { - false - } - }); - - // Record this contract's base contracts - // We don't do this for test contracts because we don't care about them - if !is_test { - self.contract_bases.insert( - contract_id.clone(), - base_contract_node_ids - .iter() - .filter_map(|base_contract_node_id| { - self.contract_ids.get(base_contract_node_id).cloned() - }) - .collect(), - ); - } - - // For tests and contracts with no items we still record an empty Vec so we don't - // end up here again - if items.is_empty() || is_test { - self.contract_items.insert(contract_id.clone(), Vec::new()); + for (contract_id, ast) in self.contracts { + let ContractVisitor { items, .. } = ContractVisitor::new( + contract_id.source_id, + self.sources.get(&contract_id.source_id).ok_or_else(|| { + eyre::eyre!( + "We should have the source code for source ID {}", + contract_id.source_id + ) + })?, + contract_id.contract_name.clone(), + ) + .visit(ast)?; + + let is_test = items.iter().any(|item| { + if let CoverageItemKind::Function { name } = &item.kind { + name.is_test() } else { - let item_ids: Vec = - (self.items.len()..self.items.len() + items.len()).collect(); - self.items.extend(items); - self.contract_items.insert(contract_id.clone(), item_ids.clone()); + false } + }); + + if !is_test { + self.items.extend(items); } } - Ok(()) + Ok(SourceAnalysis { items: self.items }) } } diff --git a/crates/evm/coverage/src/anchors.rs b/crates/evm/coverage/src/anchors.rs index 237d941d1..d45fdae57 100644 --- a/crates/evm/coverage/src/anchors.rs +++ b/crates/evm/coverage/src/anchors.rs @@ -1,10 +1,12 @@ +use std::collections::HashMap; + use super::{CoverageItem, CoverageItemKind, ItemAnchor, SourceLocation}; use alloy_primitives::Bytes; use foundry_compilers::sourcemap::{SourceElement, SourceMap}; use foundry_evm_core::utils::IcPcMap; use revm::{ interpreter::opcode::{self, spec_opcode_gas}, - primitives::SpecId, + primitives::{HashSet, SpecId}, }; /// Attempts to find anchors for the given items using the given source map and bytecode. @@ -12,13 +14,20 @@ pub fn find_anchors( bytecode: &Bytes, source_map: &SourceMap, ic_pc_map: &IcPcMap, - item_ids: &[usize], items: &[CoverageItem], + items_by_source_id: &HashMap>, ) -> Vec { - item_ids + // Prepare coverage items from all sources referenced in the source map + let potential_item_ids = source_map .iter() + .filter_map(|element| items_by_source_id.get(&(element.index? as usize))) + .flatten() + .collect::>(); + + potential_item_ids + .into_iter() .filter_map(|item_id| { - let item = items.get(*item_id)?; + let item = &items[*item_id]; match item.kind { CoverageItemKind::Branch { path_id, .. } => { diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index e140b8436..c59a54ade 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -257,19 +257,27 @@ impl CoverageArgs { })?, )? .analyze()?; - let anchors: HashMap> = source_analysis - .contract_items + + // Build helper mapping used by `find_anchors` + let mut items_by_source_id: HashMap<_, Vec<_>> = + HashMap::with_capacity(source_analysis.items.len()); + + for (item_id, item) in source_analysis.items.iter().enumerate() { + items_by_source_id.entry(item.loc.source_id).or_default().push(item_id); + } + + let anchors: HashMap> = source_maps .iter() - .filter_map(|(contract_id, item_ids)| { + .filter_map(|(contract_id, (_, deployed_source_map))| { // TODO: Creation source map/bytecode as well Some(( contract_id.clone(), find_anchors( &bytecodes.get(contract_id)?.1, - &source_maps.get(contract_id)?.1, + deployed_source_map, &ic_pc_maps.get(contract_id)?.1, - item_ids, &source_analysis.items, + &items_by_source_id, ), )) }) From d1ab09d080db9341eded80231e001ad191b0b706 Mon Sep 17 00:00:00 2001 From: Roman Krasiuk Date: Fri, 29 Mar 2024 16:48:02 +0100 Subject: [PATCH 121/430] chore: upgrade nix deps & migrate to stable (#7517) --- flake.lock | 24 ++++++++++++------------ flake.nix | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/flake.lock b/flake.lock index a03305813..9ad80af8b 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1694529238, - "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", "owner": "numtide", "repo": "flake-utils", - "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", "type": "github" }, "original": { @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1700538105, - "narHash": "sha256-uZhOCmwv8VupEmPZm3erbr9XXmyg7K67Ul3+Rx2XMe0=", + "lastModified": 1711655175, + "narHash": "sha256-1xiaYhC3ul4y+i3eicYxeERk8ZkrNjLkrFSb/UW36Zw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "51a01a7e5515b469886c120e38db325c96694c2f", + "rev": "64c81edb4b97a51c5bbc54c191763ac71a6517ee", "type": "github" }, "original": { @@ -52,11 +52,11 @@ ] }, "locked": { - "lastModified": 1700705722, - "narHash": "sha256-cFfTFToYTeRQtdNqo53+E+G5RxPiTbWusGq+MpZSpbA=", + "lastModified": 1711678273, + "narHash": "sha256-7lIB0hMRnfzx/9oSIwTnwXmVnbvVGRoadOCW+1HI5zY=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "67998ae1cabcf683cb115c5ab01ae4ff067e3d60", + "rev": "42a168449605950935f15ea546f6f770e5f7f629", "type": "github" }, "original": { @@ -75,11 +75,11 @@ ] }, "locked": { - "lastModified": 1700417764, - "narHash": "sha256-ssdwqKWkYUd/Nr6P9veR4D/PrtlwGJkPoUQoEgVJVpo=", + "lastModified": 1711538161, + "narHash": "sha256-rETVdEIQ2PyEcNgzXXFSiYAYl0koCeGDIWp9XYBTxoQ=", "owner": "hellwolf", "repo": "solc.nix", - "rev": "80d2e38e98e589872b0dc3770f838c4be847305e", + "rev": "a995838545a7383a0b37776e969743b1346d5479", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 5abae8e64..46ddb920c 100644 --- a/flake.nix +++ b/flake.nix @@ -27,9 +27,9 @@ overlays = [ rust-overlay.overlays.default solc.overlay ]; }; lib = pkgs.lib; - toolchain = pkgs.rust-bin.selectLatestNightlyWith (toolchain: toolchain.default.override { + toolchain = pkgs.rust-bin.stable.latest.default.override { extensions = [ "rustfmt" "clippy" "rust-src" ]; - }); + }; in { devShells.default = pkgs.mkShell { From a16714ed40f733013d7a80f4f969564175c3318e Mon Sep 17 00:00:00 2001 From: Enrique Date: Sat, 30 Mar 2024 06:49:54 -0400 Subject: [PATCH 122/430] chore: re-add evalir to codeowners (#7521) --- .github/CODEOWNERS | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b452279e7..4eea49b38 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,7 +1,7 @@ -* @danipopes @mattsse +* @danipopes @evalir @mattsse -crates/anvil/ @danipopes @mattsse -crates/cheatcodes/ @danipopes @mattsse @klkvr +crates/anvil/ @danipopes @mattsse @evalir +crates/cheatcodes/ @danipopes @mattsse @klkvr @evalir crates/evm/coverage/ @onbjerg crates/fmt/ @rkrasiuk crates/linking/ @klkvr From d94e3c631e2da7756af46c70f8f58b75563b7013 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sat, 30 Mar 2024 23:33:00 +0400 Subject: [PATCH 123/430] feat: allow supplying function name via `forge script --sig` (#7518) * feat: allow supplying fn name via forge script --sig * fmt * clippy * add test --- crates/forge/tests/cli/script.rs | 18 +++++++++++ crates/script/src/lib.rs | 52 ++++++++++++++++++-------------- 2 files changed, 48 insertions(+), 22 deletions(-) diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index ff03a6b57..a2d4dc4fb 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -1179,3 +1179,21 @@ forgetest_async!(can_sign_with_script_wallet_multiple, |prj, cmd| { .await .simulate(ScriptOutcome::OkRun); }); + +forgetest_async!(fails_with_function_name_and_overloads, |prj, cmd| { + let script = prj + .add_script( + "Sctipt.s.sol", + r#" +contract Script { + function run() external {} + + function run(address,uint256) external {} +} + "#, + ) + .unwrap(); + + cmd.arg("script").args([&script.to_string_lossy(), "--sig", "run"]); + assert!(cmd.stderr_lossy().contains("Multiple functions with the same name")); +}); diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 010b5bbe9..fd4ba66f6 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -12,7 +12,7 @@ use build::PreprocessedState; use clap::{Parser, ValueHint}; use dialoguer::Confirm; use ethers_signers::Signer; -use eyre::{ContextCompat, Result, WrapErr}; +use eyre::{ContextCompat, Result}; use forge_verify::RetryArgs; use foundry_cli::{opts::CoreBuildArgs, utils::LoadConfig}; use foundry_common::{ @@ -311,30 +311,38 @@ impl ScriptArgs { /// /// Note: We assume that the `sig` is already stripped of its prefix, See [`ScriptArgs`] fn get_method_and_calldata(&self, abi: &JsonAbi) -> Result<(Function, Bytes)> { - let (func, data) = if let Ok(func) = get_func(&self.sig) { - ( - abi.functions().find(|&abi_func| abi_func.selector() == func.selector()).wrap_err( - format!("Function `{}` is not implemented in your script.", self.sig), - )?, - encode_function_args(&func, &self.args)?.into(), - ) - } else { - let decoded = hex::decode(&self.sig).wrap_err("Invalid hex calldata")?; + if let Ok(decoded) = hex::decode(&self.sig) { let selector = &decoded[..SELECTOR_LEN]; - ( - abi.functions().find(|&func| selector == &func.selector()[..]).ok_or_else( - || { - eyre::eyre!( - "Function selector `{}` not found in the ABI", - hex::encode(selector) - ) - }, - )?, - decoded.into(), - ) + let func = + abi.functions().find(|func| selector == &func.selector()[..]).ok_or_else(|| { + eyre::eyre!( + "Function selector `{}` not found in the ABI", + hex::encode(selector) + ) + })?; + return Ok((func.clone(), decoded.into())); + } + + let func = if self.sig.contains('(') { + let func = get_func(&self.sig)?; + abi.functions() + .find(|&abi_func| abi_func.selector() == func.selector()) + .wrap_err(format!("Function `{}` is not implemented in your script.", self.sig))? + } else { + let matching_functions = + abi.functions().filter(|func| func.name == self.sig).collect::>(); + match matching_functions.len() { + 0 => eyre::bail!("Function `{}` not found in the ABI", self.sig), + 1 => matching_functions[0], + 2.. => eyre::bail!( + "Multiple functions with the same name `{}` found in the ABI", + self.sig + ), + } }; + let data = encode_function_args(func, &self.args)?; - Ok((func.clone(), data)) + Ok((func.clone(), data.into())) } /// Checks if the transaction is a deployment with either a size above the `CONTRACT_MAX_SIZE` From bd56eef59fff9d9597ab0aff4bf4fd6f0a9e399e Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Mon, 1 Apr 2024 16:01:21 +0300 Subject: [PATCH 124/430] fix(bench): avoid panic if test benchmark execution not success (#7535) fix(bench): avoid panic if execution not success --- crates/forge/benches/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/forge/benches/test.rs b/crates/forge/benches/test.rs index 7650077b1..7646a3c21 100644 --- a/crates/forge/benches/test.rs +++ b/crates/forge/benches/test.rs @@ -15,7 +15,7 @@ fn forge_test_benchmark(c: &mut Criterion) { let mut cmd = prj.forge_command(); cmd.arg("test"); b.iter(|| { - cmd.ensure_execute_success().unwrap(); + cmd.print_output(); }); }); } From 0578aaecc478411113a6434fc432c765306d7e00 Mon Sep 17 00:00:00 2001 From: Krishang <93703995+kamuik16@users.noreply.github.com> Date: Mon, 1 Apr 2024 22:12:36 +0530 Subject: [PATCH 125/430] feat: print IPC path (#7526) * feat: print ipc path * moved if check * moved println --- crates/anvil/src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/anvil/src/lib.rs b/crates/anvil/src/lib.rs index 23a328d7a..afc600a3b 100644 --- a/crates/anvil/src/lib.rs +++ b/crates/anvil/src/lib.rs @@ -237,6 +237,9 @@ impl NodeHandle { pub(crate) fn print(&self, fork: Option<&ClientFork>) { self.config.print(fork); if !self.config.silent { + if let Some(ipc_path) = self.ipc_path() { + println!("IPC path: {}", ipc_path); + } println!( "Listening on {}", self.addresses @@ -244,7 +247,7 @@ impl NodeHandle { .map(|addr| { addr.to_string() }) .collect::>() .join(", ") - ) + ); } } From f625d0fa7c51e65b4bf1e8f7931cd1c6e2e285e9 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Tue, 2 Apr 2024 01:51:24 +0400 Subject: [PATCH 126/430] fix: coverage bug (#7532) fix --- crates/forge/bin/cmd/coverage.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index c59a54ade..9b6d19e74 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -268,6 +268,7 @@ impl CoverageArgs { let anchors: HashMap> = source_maps .iter() + .filter(|(contract_id, _)| contract_id.version == version) .filter_map(|(contract_id, (_, deployed_source_map))| { // TODO: Creation source map/bytecode as well Some(( From 85cb9fbcd0cf1b7a4d0a831048ba0dc4800da30e Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Tue, 2 Apr 2024 21:11:20 +0400 Subject: [PATCH 127/430] fix: debugger doesn't work with external libraries (#7504) * add TestContract * use TestContract * wip * fix * clippy + fmt * smaller diff --- Cargo.lock | 1 + crates/common/Cargo.toml | 1 + crates/common/src/compile.rs | 14 +++---- crates/forge/bin/cmd/test/mod.rs | 10 ++++- crates/forge/src/multi_runner.rs | 49 ++++++++++++++-------- crates/forge/src/result.rs | 6 ++- crates/forge/src/runner.rs | 41 ++++++++---------- crates/linking/src/lib.rs | 72 ++++++++++++++++++-------------- crates/script/src/build.rs | 46 +++++++++++++------- crates/script/src/execute.rs | 2 +- 10 files changed, 144 insertions(+), 98 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 05a4e79a1..54f207db0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3271,6 +3271,7 @@ dependencies = [ "foundry-block-explorers", "foundry-compilers", "foundry-config", + "foundry-linking", "foundry-macros", "glob", "globset", diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index cf6e53040..fb161da61 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -13,6 +13,7 @@ repository.workspace = true foundry-block-explorers = { workspace = true, features = ["foundry-compilers"] } foundry-compilers.workspace = true foundry-config.workspace = true +foundry-linking.workspace = true ethers-core.workspace = true ethers-middleware.workspace = true diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index 8272dece3..af91a0176 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -5,12 +5,13 @@ use comfy_table::{presets::ASCII_MARKDOWN, Attribute, Cell, Color, Table}; use eyre::{Context, Result}; use foundry_block_explorers::contract::Metadata; use foundry_compilers::{ - artifacts::{BytecodeObject, CompactContractBytecode, ContractBytecodeSome}, + artifacts::{BytecodeObject, ContractBytecodeSome, Libraries}, remappings::Remapping, report::{BasicStdoutReporter, NoReporter, Report}, Artifact, ArtifactId, FileFilter, Graph, Project, ProjectCompileOutput, ProjectPathsConfig, Solc, SolcConfig, }; +use foundry_linking::Linker; use rustc_hash::FxHashMap; use std::{ collections::{BTreeMap, HashMap}, @@ -296,7 +297,10 @@ impl ContractSources { pub fn from_project_output( output: &ProjectCompileOutput, root: &Path, + libraries: &Libraries, ) -> Result { + let linker = Linker::new(root, output.artifact_ids().collect()); + let mut sources = ContractSources::default(); for (id, artifact) in output.artifact_ids() { if let Some(file_id) = artifact.id { @@ -304,12 +308,8 @@ impl ContractSources { let source_code = std::fs::read_to_string(abs_path).wrap_err_with(|| { format!("failed to read artifact source file for `{}`", id.identifier()) })?; - let compact = CompactContractBytecode { - abi: artifact.abi.clone(), - bytecode: artifact.bytecode.clone(), - deployed_bytecode: artifact.deployed_bytecode.clone(), - }; - let contract = compact_to_contract(compact)?; + let linked = linker.link(&id, libraries)?; + let contract = compact_to_contract(linked)?; sources.insert(&id, file_id, source_code, contract); } else { warn!(id = id.identifier(), "source not found"); diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 13130d464..78000de61 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -224,14 +224,20 @@ impl TestArgs { let outcome = self.run_tests(runner, config, verbosity, &filter).await?; if should_debug { - // There is only one test. - let Some((_, test_result)) = outcome.tests().next() else { + // Get first non-empty suite result. We will have only one such entry + let Some((suite_result, test_result)) = outcome + .results + .iter() + .find(|(_, r)| !r.test_results.is_empty()) + .map(|(_, r)| (r, r.test_results.values().next().unwrap())) + else { return Err(eyre::eyre!("no tests were executed")); }; let sources = ContractSources::from_project_output( output_clone.as_ref().unwrap(), project.root(), + &suite_result.libraries, )?; // Run the debugger. diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 7712a7ee6..d545d773d 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -5,7 +5,9 @@ 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_compilers::{contracts::ArtifactContracts, Artifact, ArtifactId, ProjectCompileOutput}; +use foundry_compilers::{ + artifacts::Libraries, contracts::ArtifactContracts, Artifact, ArtifactId, ProjectCompileOutput, +}; use foundry_evm::{ backend::Backend, decode::RevertDecoder, @@ -26,7 +28,15 @@ use std::{ time::Instant, }; -pub type DeployableContracts = BTreeMap)>; +#[derive(Debug, Clone)] +pub struct TestContract { + pub abi: JsonAbi, + pub bytecode: Bytes, + pub libs_to_deploy: Vec, + pub libraries: Libraries, +} + +pub type DeployableContracts = BTreeMap; /// A multi contract runner receives a set of contracts deployed in an EVM instance and proceeds /// to run all test functions in these contracts. @@ -67,8 +77,10 @@ impl MultiContractRunner { pub fn matching_contracts<'a>( &'a self, filter: &'a dyn TestFilter, - ) -> impl Iterator))> { - self.contracts.iter().filter(|&(id, (abi, _, _))| matches_contract(id, abi, filter)) + ) -> impl Iterator { + self.contracts + .iter() + .filter(|&(id, TestContract { abi, .. })| matches_contract(id, abi, filter)) } /// Returns an iterator over all test functions that match the filter. @@ -77,7 +89,7 @@ impl MultiContractRunner { filter: &'a dyn TestFilter, ) -> impl Iterator { self.matching_contracts(filter) - .flat_map(|(_, (abi, _, _))| abi.functions()) + .flat_map(|(_, TestContract { abi, .. })| abi.functions()) .filter(|func| is_matching_test(func, filter)) } @@ -89,14 +101,14 @@ impl MultiContractRunner { self.contracts .iter() .filter(|(id, _)| filter.matches_path(&id.source) && filter.matches_contract(&id.name)) - .flat_map(|(_, (abi, _, _))| abi.functions()) + .flat_map(|(_, TestContract { abi, .. })| abi.functions()) .filter(|func| func.is_test() || func.is_invariant_test()) } /// Returns all matching tests grouped by contract grouped by file (file -> (contract -> tests)) pub fn list(&self, filter: &dyn TestFilter) -> BTreeMap>> { self.matching_contracts(filter) - .map(|(id, (abi, _, _))| { + .map(|(id, TestContract { abi, .. })| { let source = id.source.as_path().display().to_string(); let name = id.name.clone(); let tests = abi @@ -169,22 +181,19 @@ impl MultiContractRunner { find_time, ); - contracts.par_iter().for_each_with(tx, |tx, &(id, (abi, deploy_code, libs))| { + contracts.par_iter().for_each_with(tx, |tx, &(id, contract)| { let identifier = id.identifier(); let executor = executor.clone(); - let result = self.run_tests(&identifier, abi, executor, deploy_code, libs, filter); + let result = self.run_tests(&identifier, contract, executor, filter); let _ = tx.send((identifier, result)); }) } - #[allow(clippy::too_many_arguments)] fn run_tests( &self, name: &str, - contract: &JsonAbi, + contract: &TestContract, executor: Executor, - deploy_code: &Bytes, - libs: &[Bytes], filter: &dyn TestFilter, ) -> SuiteResult { let mut span_name = name; @@ -199,11 +208,9 @@ impl MultiContractRunner { name, executor, contract, - deploy_code, self.evm_opts.initial_balance, self.sender, &self.revert_decoder, - libs, self.debug, ); let r = runner.run_tests(filter, &self.test_options, Some(&self.known_contracts)); @@ -307,14 +314,17 @@ impl MultiContractRunnerBuilder { .map(|(i, _)| (i.identifier(), root.join(&i.source).to_string_lossy().into())) .collect::>(); - let linker = Linker::new(root, contracts); + let linker = Linker::new( + root, + contracts.iter().map(|(id, artifact)| (id.clone(), artifact)).collect(), + ); // Create a mapping of name => (abi, deployment code, Vec) let mut deployable_contracts = DeployableContracts::default(); let mut known_contracts = ContractsByArtifact::default(); - for (id, contract) in &linker.contracts.0 { + for (id, contract) in contracts.iter() { let Some(abi) = contract.abi.as_ref() else { continue; }; @@ -341,7 +351,10 @@ impl MultiContractRunnerBuilder { if abi.constructor.as_ref().map(|c| c.inputs.is_empty()).unwrap_or(true) && abi.functions().any(|func| func.name.is_test() || func.name.is_invariant_test()) { - deployable_contracts.insert(id.clone(), (abi.clone(), bytecode, libs_to_deploy)); + deployable_contracts.insert( + id.clone(), + TestContract { abi: abi.clone(), bytecode, libs_to_deploy, libraries }, + ); } if let Some(bytes) = linked_contract.get_deployed_bytecode_bytes() { diff --git a/crates/forge/src/result.rs b/crates/forge/src/result.rs index ad1173381..a50e59b95 100644 --- a/crates/forge/src/result.rs +++ b/crates/forge/src/result.rs @@ -2,6 +2,7 @@ use alloy_primitives::{Address, Log}; use foundry_common::{evm::Breakpoints, get_contract_name, get_file_name, shell}; +use foundry_compilers::artifacts::Libraries; use foundry_evm::{ coverage::HitMaps, debug::DebugArena, @@ -193,6 +194,8 @@ pub struct SuiteResult { pub test_results: BTreeMap, /// Generated warnings. pub warnings: Vec, + /// Libraries used to link test contract. + pub libraries: Libraries, } impl SuiteResult { @@ -200,8 +203,9 @@ impl SuiteResult { duration: Duration, test_results: BTreeMap, warnings: Vec, + libraries: Libraries, ) -> Self { - Self { duration, test_results, warnings } + Self { duration, test_results, warnings, libraries } } /// Returns an iterator over all individual succeeding tests and their names. diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 3c9eee1dd..7f3258e35 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -1,12 +1,12 @@ //! The Forge test runner. use crate::{ - multi_runner::is_matching_test, + multi_runner::{is_matching_test, TestContract}, result::{SuiteResult, TestKind, TestResult, TestSetup, TestStatus}, TestFilter, TestOptions, }; -use alloy_json_abi::{Function, JsonAbi}; -use alloy_primitives::{Address, Bytes, U256}; +use alloy_json_abi::Function; +use alloy_primitives::{Address, U256}; use eyre::Result; use foundry_common::{ contracts::{ContractsByAddress, ContractsByArtifact}, @@ -37,14 +37,10 @@ use std::{ #[derive(Clone, Debug)] pub struct ContractRunner<'a> { pub name: &'a str, + /// The data of the contract being ran. + pub contract: &'a TestContract, /// The executor used by the runner. pub executor: Executor, - /// Library contracts to be deployed before the test contract - pub predeploy_libs: &'a [Bytes], - /// The deployed contract's code - pub code: &'a Bytes, - /// The test contract's ABI - pub contract: &'a JsonAbi, /// Revert decoder. Contains all known errors. pub revert_decoder: &'a RevertDecoder, /// The initial balance of the test contract @@ -56,27 +52,22 @@ pub struct ContractRunner<'a> { } impl<'a> ContractRunner<'a> { - #[allow(clippy::too_many_arguments)] pub fn new( name: &'a str, executor: Executor, - contract: &'a JsonAbi, - code: &'a Bytes, + contract: &'a TestContract, initial_balance: U256, sender: Option
, revert_decoder: &'a RevertDecoder, - predeploy_libs: &'a [Bytes], debug: bool, ) -> Self { Self { name, executor, contract, - code, initial_balance, sender: sender.unwrap_or_default(), revert_decoder, - predeploy_libs, debug, } } @@ -104,8 +95,8 @@ impl<'a> ContractRunner<'a> { // Deploy libraries let mut logs = Vec::new(); - let mut traces = Vec::with_capacity(self.predeploy_libs.len()); - for code in self.predeploy_libs.iter() { + let mut traces = Vec::with_capacity(self.contract.libs_to_deploy.len()); + for code in self.contract.libs_to_deploy.iter() { match self.executor.deploy( self.sender, code.clone(), @@ -131,7 +122,7 @@ impl<'a> ContractRunner<'a> { // Deploy the test contract match self.executor.deploy( self.sender, - self.code.clone(), + self.contract.bytecode.clone(), U256::ZERO, Some(self.revert_decoder), ) { @@ -194,7 +185,7 @@ impl<'a> ContractRunner<'a> { let mut warnings = Vec::new(); let setup_fns: Vec<_> = - self.contract.functions().filter(|func| func.name.is_setup()).collect(); + self.contract.abi.functions().filter(|func| func.name.is_setup()).collect(); let needs_setup = setup_fns.len() == 1 && setup_fns[0].name == "setUp"; @@ -215,10 +206,11 @@ impl<'a> ContractRunner<'a> { [("setUp()".to_string(), TestResult::fail("multiple setUp functions".to_string()))] .into(), warnings, + self.contract.libraries.clone(), ) } - let has_invariants = self.contract.functions().any(|func| func.is_invariant_test()); + let has_invariants = self.contract.abi.functions().any(|func| func.is_invariant_test()); // Invariant testing requires tracing to figure out what contracts were created. let tmp_tracing = self.executor.inspector.tracer.is_none() && has_invariants && needs_setup; @@ -251,6 +243,7 @@ impl<'a> ContractRunner<'a> { )] .into(), warnings, + self.contract.libraries.clone(), ) } @@ -259,6 +252,7 @@ impl<'a> ContractRunner<'a> { let find_timer = Instant::now(); let functions = self .contract + .abi .functions() .filter(|func| is_matching_test(func, filter)) .collect::>(); @@ -266,7 +260,7 @@ impl<'a> ContractRunner<'a> { debug!( "Found {} test functions out of {} in {:?}", functions.len(), - self.contract.functions().count(), + self.contract.abi.functions().count(), find_time, ); @@ -305,7 +299,8 @@ impl<'a> ContractRunner<'a> { .collect::>(); let duration = start.elapsed(); - let suite_result = SuiteResult::new(duration, test_results, warnings); + let suite_result = + SuiteResult::new(duration, test_results, warnings, self.contract.libraries.clone()); info!( duration=?suite_result.duration, "done. {}/{} successful", @@ -494,7 +489,7 @@ impl<'a> ContractRunner<'a> { ); let invariant_contract = - InvariantContract { address, invariant_function: func, abi: self.contract }; + InvariantContract { address, invariant_function: func, abi: &self.contract.abi }; let InvariantFuzzTestResult { error, cases, reverts, last_run_inputs, gas_report_traces } = match evm.invariant_fuzz(invariant_contract.clone()) { diff --git a/crates/linking/src/lib.rs b/crates/linking/src/lib.rs index 110a6d8f2..869a18e64 100644 --- a/crates/linking/src/lib.rs +++ b/crates/linking/src/lib.rs @@ -2,13 +2,13 @@ use alloy_primitives::{Address, Bytes}; use foundry_compilers::{ - artifacts::{CompactContractBytecode, Libraries}, + artifacts::{CompactContractBytecode, CompactContractBytecodeCow, Libraries}, contracts::ArtifactContracts, Artifact, ArtifactId, }; use semver::Version; use std::{ - collections::BTreeSet, + collections::{BTreeMap, BTreeSet}, path::{Path, PathBuf}, str::FromStr, }; @@ -24,11 +24,11 @@ pub enum LinkerError { InvalidAddress(
::Err), } -pub struct Linker { +pub struct Linker<'a> { /// Root of the project, used to determine whether artifact/library path can be stripped. pub root: PathBuf, /// Compilation artifacts. - pub contracts: ArtifactContracts, + pub contracts: ArtifactContracts>, } /// Output of the `link_with_nonce_or_address` @@ -41,8 +41,11 @@ pub struct LinkOutput { pub libs_to_deploy: Vec, } -impl Linker { - pub fn new(root: impl Into, contracts: ArtifactContracts) -> Self { +impl<'a> Linker<'a> { + pub fn new( + root: impl Into, + contracts: ArtifactContracts>, + ) -> Linker<'a> { Linker { root: root.into(), contracts } } @@ -62,7 +65,7 @@ impl Linker { /// library path in the form of "./path/to/Lib.sol:Lib" /// /// Optionally accepts solc version, and if present, only compares artifacts with given version. - fn find_artifact_id_by_library_path<'a>( + fn find_artifact_id_by_library_path( &'a self, file: &str, name: &str, @@ -85,16 +88,23 @@ impl Linker { } /// Performs DFS on the graph of link references, and populates `deps` with all found libraries. - fn collect_dependencies<'a>( + fn collect_dependencies( &'a self, target: &'a ArtifactId, deps: &mut BTreeSet<&'a ArtifactId>, ) -> Result<(), LinkerError> { - let references = self - .contracts - .get(target) - .ok_or(LinkerError::MissingTargetArtifact)? - .all_link_references(); + let contract = self.contracts.get(target).ok_or(LinkerError::MissingTargetArtifact)?; + + let mut references = BTreeMap::new(); + if let Some(bytecode) = &contract.bytecode { + references.extend(bytecode.link_references.clone()); + } + if let Some(deployed_bytecode) = &contract.deployed_bytecode { + if let Some(bytecode) = &deployed_bytecode.bytecode { + references.extend(bytecode.link_references.clone()); + } + } + for (file, libs) in &references { for contract in libs.keys() { let id = self @@ -121,7 +131,7 @@ impl Linker { /// When calling for `target` being an external library itself, you should check that `target` /// does not appear in `libs_to_deploy` to avoid deploying it twice. It may happen in cases /// when there is a dependency cycle including `target`. - pub fn link_with_nonce_or_address<'a>( + pub fn link_with_nonce_or_address( &'a self, libraries: Libraries, sender: Address, @@ -174,16 +184,21 @@ impl Linker { for (name, address) in libs { let address = Address::from_str(address).map_err(LinkerError::InvalidAddress)?; if let Some(bytecode) = contract.bytecode.as_mut() { - bytecode.link(file.to_string_lossy(), name, address); + bytecode.to_mut().link(file.to_string_lossy(), name, address); } if let Some(deployed_bytecode) = - contract.deployed_bytecode.as_mut().and_then(|b| b.bytecode.as_mut()) + contract.deployed_bytecode.as_mut().and_then(|b| b.to_mut().bytecode.as_mut()) { deployed_bytecode.link(file.to_string_lossy(), name, address); } } } - Ok(contract) + + Ok(CompactContractBytecode { + abi: contract.abi.map(|a| a.into_owned()), + bytecode: contract.bytecode.map(|b| b.into_owned()), + deployed_bytecode: contract.deployed_bytecode.map(|b| b.into_owned()), + }) } pub fn get_linked_artifacts( @@ -197,12 +212,12 @@ impl Linker { #[cfg(test)] mod tests { use super::*; - use foundry_compilers::{Project, ProjectPathsConfig}; + use foundry_compilers::{Project, ProjectCompileOutput, ProjectPathsConfig}; use std::collections::HashMap; struct LinkerTest { project: Project, - linker: Linker, + output: ProjectCompileOutput, dependency_assertions: HashMap>, } @@ -220,20 +235,13 @@ mod tests { let project = Project::builder().paths(paths).ephemeral().no_artifacts().build().unwrap(); - let mut contracts = project.compile().unwrap(); + let mut output = project.compile().unwrap(); if strip_prefixes { - contracts = contracts.with_stripped_file_prefixes(project.root()); + output = output.with_stripped_file_prefixes(project.root()); } - let contracts = contracts - .into_artifacts() - .map(|(id, c)| (id, c.into_contract_bytecode())) - .collect::(); - - let linker = Linker::new(project.root(), contracts); - - Self { project, linker, dependency_assertions: HashMap::new() } + Self { project, output, dependency_assertions: HashMap::new() } } fn assert_dependencies( @@ -246,7 +254,8 @@ mod tests { } fn test_with_sender_and_nonce(self, sender: Address, initial_nonce: u64) { - for id in self.linker.contracts.keys() { + let linker = Linker::new(self.project.root(), self.output.artifact_ids().collect()); + for id in linker.contracts.keys() { // If we didn't strip paths, artifacts will have absolute paths. // That's expected and we want to ensure that only `libraries` object has relative // paths, artifacts should be kept as is. @@ -263,8 +272,7 @@ mod tests { continue; } - let LinkOutput { libs_to_deploy, libraries, .. } = self - .linker + let LinkOutput { libs_to_deploy, libraries, .. } = linker .link_with_nonce_or_address(Default::default(), sender, initial_nonce, id) .expect("Linking failed"); diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index 41e898d96..b0aba755e 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -22,22 +22,26 @@ use foundry_compilers::{ cache::SolFilesCache, contracts::ArtifactContracts, info::ContractInfo, - ArtifactId, + ArtifactId, ProjectCompileOutput, }; use foundry_linking::{LinkOutput, Linker}; -use std::{str::FromStr, sync::Arc}; +use std::{path::PathBuf, str::FromStr, sync::Arc}; /// Container for the compiled contracts. pub struct BuildData { + /// Root of the project + pub project_root: PathBuf, /// Linker which can be used to link contracts, owns [ArtifactContracts] map. - pub linker: Linker, + pub output: ProjectCompileOutput, /// Id of target contract artifact. pub target: ArtifactId, - /// Source files of the contracts. Used by debugger. - pub sources: ContractSources, } impl BuildData { + pub fn get_linker(&self) -> Linker { + Linker::new(self.project_root.clone(), self.output.artifact_ids().collect()) + } + /// Links the build data with given libraries, using sender and nonce to compute addresses of /// missing libraries. pub fn link( @@ -46,8 +50,12 @@ impl BuildData { sender: Address, nonce: u64, ) -> Result { - let link_output = - self.linker.link_with_nonce_or_address(known_libraries, sender, nonce, &self.target)?; + let link_output = self.get_linker().link_with_nonce_or_address( + known_libraries, + sender, + nonce, + &self.target, + )?; LinkedBuildData::new(link_output, self) } @@ -55,8 +63,12 @@ impl BuildData { /// Links the build data with the given libraries. Expects supplied libraries set being enough /// to fully link target contract. pub fn link_with_libraries(self, libraries: Libraries) -> Result { - let link_output = - self.linker.link_with_nonce_or_address(libraries, Address::ZERO, 0, &self.target)?; + let link_output = self.get_linker().link_with_nonce_or_address( + libraries, + Address::ZERO, + 0, + &self.target, + )?; if !link_output.libs_to_deploy.is_empty() { eyre::bail!("incomplete libraries set"); @@ -76,12 +88,20 @@ pub struct LinkedBuildData { pub libraries: Libraries, /// Libraries that need to be deployed by sender before script execution. pub predeploy_libraries: Vec, + /// Source files of the contracts. Used by debugger. + pub sources: ContractSources, } impl LinkedBuildData { pub fn new(link_output: LinkOutput, build_data: BuildData) -> Result { + let sources = ContractSources::from_project_output( + &build_data.output, + &build_data.project_root, + &link_output.libraries, + )?; + let highlevel_known_contracts = build_data - .linker + .get_linker() .get_linked_artifacts(&link_output.libraries)? .iter() .filter_map(|(id, contract)| { @@ -97,6 +117,7 @@ impl LinkedBuildData { highlevel_known_contracts, libraries: link_output.libraries, predeploy_libraries: link_output.libs_to_deploy, + sources, }) } @@ -220,16 +241,13 @@ impl PreprocessedState { target_id = Some(id); } - let sources = ContractSources::from_project_output(&output, project.root())?; - let contracts = output.into_artifacts().collect(); let target = target_id.ok_or_eyre("Could not find target contract")?; - let linker = Linker::new(project.root(), contracts); Ok(CompiledState { args, script_config, script_wallets, - build_data: BuildData { linker, target, sources }, + build_data: BuildData { output, target, project_root: project.root().clone() }, }) } } diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index 4a3a34852..b2fab7154 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -511,7 +511,7 @@ impl PreSimulationState { let mut debugger = Debugger::builder() .debug_arenas(self.execution_result.debug.as_deref().unwrap_or_default()) .decoder(&self.execution_artifacts.decoder) - .sources(self.build_data.build_data.sources.clone()) + .sources(self.build_data.sources.clone()) .breakpoints(self.execution_result.breakpoints.clone()) .build(); debugger.try_run()?; From dbc48ead1044066a3e12c796fca9dc077f5913fe Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Tue, 2 Apr 2024 13:23:14 -0400 Subject: [PATCH 128/430] feat(verify): multichain verification for etherscan (#7537) * feat(verify): multichain verification for etherscan * fix: bump reqwest * fix: ci cargo deny --- Cargo.lock | 531 ++++++++++++++++++++--------- Cargo.toml | 13 +- crates/config/Cargo.toml | 6 +- crates/verify/src/etherscan/mod.rs | 5 +- 4 files changed, 381 insertions(+), 174 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 54f207db0..d929efd32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -222,7 +222,7 @@ dependencies = [ "auto_impl", "futures", "lru", - "reqwest", + "reqwest 0.11.27", "serde", "thiserror", "tokio", @@ -266,7 +266,7 @@ checksum = "1a047897373be4bbb0224c1afdabca92648dc57a9c9ef6e7b0be3aff7a859c83" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -279,7 +279,7 @@ dependencies = [ "alloy-transport-http", "futures", "pin-project", - "reqwest", + "reqwest 0.11.27", "serde", "serde_json", "tokio", @@ -347,7 +347,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.53", + "syn 2.0.57", "syn-solidity", "tiny-keccak", ] @@ -398,7 +398,7 @@ source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61 dependencies = [ "alloy-json-rpc", "alloy-transport", - "reqwest", + "reqwest 0.11.27", "serde_json", "tower", "url", @@ -430,7 +430,7 @@ dependencies = [ "alloy-pubsub", "alloy-transport", "futures", - "http", + "http 0.2.12", "serde_json", "tokio", "tokio-tungstenite", @@ -564,7 +564,7 @@ dependencies = [ "foundry-evm", "futures", "hash-db", - "hyper", + "hyper 0.14.28", "itertools 0.12.1", "k256", "memory-db", @@ -633,7 +633,7 @@ dependencies = [ "bytes", "clap", "futures", - "hyper", + "hyper 0.14.28", "parity-tokio-ipc", "parking_lot", "pin-project", @@ -814,7 +814,7 @@ checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" dependencies = [ "concurrent-queue", "event-listener 5.2.0", - "event-listener-strategy 0.5.0", + "event-listener-strategy 0.5.1", "futures-core", "pin-project-lite", ] @@ -847,7 +847,7 @@ checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -869,7 +869,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -880,13 +880,13 @@ checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" [[package]] name = "async-trait" -version = "0.1.78" +version = "0.1.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "461abc97219de0eaaf81fe3ef974a540158f3d079c2ab200f891f1a2ef201e85" +checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -939,14 +939,14 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] name = "autocfg" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "axum" @@ -960,9 +960,9 @@ dependencies = [ "bitflags 1.3.2", "bytes", "futures-util", - "http", - "http-body", - "hyper", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.28", "itoa", "matchit", "memchr", @@ -992,8 +992,8 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http", - "http-body", + "http 0.2.12", + "http-body 0.4.6", "mime", "rustversion", "tower-layer", @@ -1395,7 +1395,7 @@ dependencies = [ "foundry-evm", "once_cell", "regex", - "reqwest", + "reqwest 0.11.27", "revm", "rustyline", "semver 1.0.22", @@ -1415,9 +1415,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.35" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" +checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" dependencies = [ "android-tzdata", "iana-time-zone", @@ -1465,9 +1465,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.3" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "949626d00e063efc93b6dca932419ceb5432f99769911c0b995f7e884c778813" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ "clap_builder", "clap_derive", @@ -1509,14 +1509,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.3" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90239a040c80f5e14809ca132ddc4176ab33d5e17e49691793296e3fcb34d72f" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -1919,9 +1919,9 @@ dependencies = [ [[package]] name = "crypto-mac" -version = "0.11.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +checksum = "25fab6889090c8133f3deb8f73ba3c65a7f456f66436fc012a1b1e272b1e103e" dependencies = [ "generic-array", "subtle", @@ -1967,7 +1967,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -1978,7 +1978,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core", "quote", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -2002,9 +2002,9 @@ checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] name = "der" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", "pem-rfc7468", @@ -2039,7 +2039,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -2060,7 +2060,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -2070,7 +2070,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" dependencies = [ "derive_builder_core", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -2314,7 +2314,7 @@ checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -2523,10 +2523,10 @@ dependencies = [ "proc-macro2", "quote", "regex", - "reqwest", + "reqwest 0.11.27", "serde", "serde_json", - "syn 2.0.53", + "syn 2.0.57", "toml 0.8.12", "walkdir", ] @@ -2544,7 +2544,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -2570,7 +2570,7 @@ dependencies = [ "serde", "serde_json", "strum 0.26.2", - "syn 2.0.53", + "syn 2.0.57", "tempfile", "thiserror", "tiny-keccak", @@ -2585,7 +2585,7 @@ checksum = "e79e5973c26d4baf0ce55520bd732314328cabe53193286671b47144145b9649" dependencies = [ "chrono", "ethers-core", - "reqwest", + "reqwest 0.11.27", "semver 1.0.22", "serde", "serde_json", @@ -2609,7 +2609,7 @@ dependencies = [ "futures-locks", "futures-util", "instant", - "reqwest", + "reqwest 0.11.27", "serde", "serde_json", "thiserror", @@ -2637,12 +2637,12 @@ dependencies = [ "futures-timer", "futures-util", "hashers", - "http", + "http 0.2.12", "instant", "jsonwebtoken", "once_cell", "pin-project", - "reqwest", + "reqwest 0.11.27", "serde", "serde_json", "thiserror", @@ -2758,9 +2758,9 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feedafcaa9b749175d5ac357452a9d41ea2911da598fde46ce1fe02c37751291" +checksum = "332f51cb23d20b0de8458b86580878211da09bcd4503cb579c225b3d124cabb3" dependencies = [ "event-listener 5.2.0", "pin-project-lite", @@ -2797,9 +2797,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" [[package]] name = "fastrlp" @@ -2822,7 +2822,7 @@ dependencies = [ "bytes", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -2987,7 +2987,7 @@ dependencies = [ "foundry-wallets", "futures", "globset", - "hyper", + "hyper 0.14.28", "indicatif", "itertools 0.12.1", "once_cell", @@ -2999,7 +2999,7 @@ dependencies = [ "proptest", "rayon", "regex", - "reqwest", + "reqwest 0.11.27", "revm-inspectors", "rustc-hash", "semver 1.0.22", @@ -3123,7 +3123,7 @@ dependencies = [ "futures", "once_cell", "regex", - "reqwest", + "reqwest 0.11.27", "semver 1.0.22", "serde", "serde_json", @@ -3143,15 +3143,15 @@ dependencies = [ [[package]] name = "foundry-block-explorers" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a056d4aa33a639c0aa1e9e473c25b9b191be30cbea94b31445fac5c272418ae" +checksum = "3e88929768c22f9912694c634db053cfd480c3f7bd7eb39223e29f4fb40b64b7" dependencies = [ "alloy-chains", "alloy-json-abi", "alloy-primitives", "foundry-compilers", - "reqwest", + "reqwest 0.12.2", "semver 1.0.22", "serde", "serde_json", @@ -3278,7 +3278,7 @@ dependencies = [ "once_cell", "pretty_assertions", "rand 0.8.5", - "reqwest", + "reqwest 0.11.27", "rustc-hash", "semver 1.0.22", "serde", @@ -3349,7 +3349,7 @@ dependencies = [ "path-slash", "pretty_assertions", "regex", - "reqwest", + "reqwest 0.12.2", "revm-primitives", "semver 1.0.22", "serde", @@ -3530,7 +3530,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -3707,7 +3707,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -4034,7 +4034,7 @@ dependencies = [ "bstr", "log", "regex-automata 0.4.6", - "regex-syntax 0.8.2", + "regex-syntax 0.8.3", ] [[package]] @@ -4071,7 +4071,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", + "http 0.2.12", "indexmap", "slab", "tokio", @@ -4091,9 +4091,9 @@ dependencies = [ [[package]] name = "handlebars" -version = "5.1.0" +version = "5.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab283476b99e66691dee3f1640fea91487a8d81f50fb5ecc75538f8f8879a1e4" +checksum = "d08485b96a0e6393e9e4d1b8d48cf74ad6c063cd905eb33f42c1ce3f0377539b" dependencies = [ "log", "pest", @@ -4245,6 +4245,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http-body" version = "0.4.6" @@ -4252,7 +4263,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "http", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +dependencies = [ + "bytes", + "futures-core", + "http 1.1.0", + "http-body 1.0.0", "pin-project-lite", ] @@ -4291,8 +4325,8 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http", - "http-body", + "http 0.2.12", + "http-body 0.4.6", "httparse", "httpdate", "itoa", @@ -4304,17 +4338,36 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186548d73ac615b32a73aafe38fb4f56c0d340e110e5a200bcadbaf2e199263a" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + [[package]] name = "hyper-rustls" version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" dependencies = [ - "http", - "hyper", + "http 0.2.12", + "hyper 0.14.28", "log", "rustls 0.20.9", - "rustls-native-certs", + "rustls-native-certs 0.6.3", "tokio", "tokio-rustls 0.23.4", ] @@ -4326,13 +4379,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", - "http", - "hyper", + "http 0.2.12", + "hyper 0.14.28", "rustls 0.21.10", "tokio", "tokio-rustls 0.24.1", ] +[[package]] +name = "hyper-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" +dependencies = [ + "futures-util", + "http 1.1.0", + "hyper 1.2.0", + "hyper-util", + "rustls 0.22.3", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.25.0", + "tower-service", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -4340,12 +4410,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper", + "hyper 0.14.28", "native-tls", "tokio", "tokio-native-tls", ] +[[package]] +name = "hyper-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "hyper 1.2.0", + "pin-project-lite", + "socket2", + "tokio", + "tower", + "tower-service", + "tracing", +] + [[package]] name = "iana-time-zone" version = "0.1.60" @@ -4609,9 +4699,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" @@ -4733,7 +4823,7 @@ dependencies = [ "lalrpop-util", "petgraph", "regex", - "regex-syntax 0.8.2", + "regex-syntax 0.8.3", "string_cache", "term", "tiny-keccak", @@ -4773,13 +4863,12 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "libredox" -version = "0.0.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.5.0", "libc", - "redox_syscall", ] [[package]] @@ -4917,9 +5006,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "memmap2" @@ -4979,7 +5068,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -5246,7 +5335,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -5350,7 +5439,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -5361,9 +5450,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.101" +version = "0.9.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" dependencies = [ "cc", "libc", @@ -5560,7 +5649,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -5589,9 +5678,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.8" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f8023d0fb78c8e03784ea1c7f3fa36e68a723138990b8d5a47d916b651e7a8" +checksum = "311fb059dee1a7b802f036316d790138c613a4e8b180c822e3925a662e9f0c95" dependencies = [ "memchr", "thiserror", @@ -5600,9 +5689,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.8" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0d24f72393fd16ab6ac5738bc33cdb6a9aa73f8b902e8fe29cf4e67d7dd1026" +checksum = "f73541b156d32197eecda1a4014d7f868fd2bcb3c550d5386087cfba442bf69c" dependencies = [ "pest", "pest_generator", @@ -5610,22 +5699,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.8" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc17e2a6c7d0a492f0158d7a4bd66cc17280308bbaff78d5bef566dca35ab80" +checksum = "c35eeed0a3fab112f75165fdc026b3913f4183133f19b49be773ac9ea966e8bd" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] name = "pest_meta" -version = "2.7.8" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "934cd7631c050f4674352a6e835d5f6711ffbfb9345c2fc0107155ac495ae293" +checksum = "2adbf29bb9776f28caece835398781ab24435585fe0d4dc1374a61db5accedca" dependencies = [ "once_cell", "pest", @@ -5721,7 +5810,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -5759,14 +5848,14 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -5865,12 +5954,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" dependencies = [ "proc-macro2", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -5965,7 +6054,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", "version_check", "yansi 1.0.1", ] @@ -5995,7 +6084,7 @@ dependencies = [ "rand 0.8.5", "rand_chacha 0.3.1", "rand_xorshift", - "regex-syntax 0.8.2", + "regex-syntax 0.8.3", "rusty-fork", "tempfile", "unarray", @@ -6181,9 +6270,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -6210,9 +6299,9 @@ dependencies = [ [[package]] name = "redox_users" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ "getrandom 0.2.12", "libredox", @@ -6242,7 +6331,7 @@ dependencies = [ "aho-corasick", "memchr", "regex-automata 0.4.6", - "regex-syntax 0.8.2", + "regex-syntax 0.8.3", ] [[package]] @@ -6262,7 +6351,7 @@ checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.2", + "regex-syntax 0.8.3", ] [[package]] @@ -6273,9 +6362,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "reqwest" @@ -6289,9 +6378,9 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http", - "http-body", - "hyper", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.28", "hyper-rustls 0.24.2", "hyper-tls", "ipnet", @@ -6303,8 +6392,8 @@ dependencies = [ "percent-encoding", "pin-project-lite", "rustls 0.21.10", - "rustls-native-certs", - "rustls-pemfile", + "rustls-native-certs 0.6.3", + "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", @@ -6322,6 +6411,47 @@ dependencies = [ "winreg", ] +[[package]] +name = "reqwest" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d66674f2b6fb864665eea7a3c1ac4e3dfacd2fda83cf6f935a612e01b0e3338" +dependencies = [ + "base64 0.21.7", + "bytes", + "futures-core", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.2.0", + "hyper-rustls 0.26.0", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls 0.22.3", + "rustls-native-certs 0.7.0", + "rustls-pemfile 1.0.4", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls 0.25.0", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "revm" version = "7.1.0" @@ -6544,8 +6674,8 @@ dependencies = [ "bytes", "crc32fast", "futures", - "http", - "hyper", + "http 0.2.12", + "hyper 0.14.28", "hyper-rustls 0.23.2", "lazy_static", "log", @@ -6568,7 +6698,7 @@ dependencies = [ "chrono", "dirs-next", "futures", - "hyper", + "hyper 0.14.28", "serde", "serde_json", "shlex", @@ -6603,8 +6733,8 @@ dependencies = [ "futures", "hex", "hmac 0.11.0", - "http", - "hyper", + "http 0.2.12", + "hyper 0.14.28", "log", "md-5 0.9.1", "percent-encoding", @@ -6685,10 +6815,24 @@ checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" dependencies = [ "log", "ring 0.17.8", - "rustls-webpki", + "rustls-webpki 0.101.7", "sct", ] +[[package]] +name = "rustls" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99008d7ad0bbbea527ec27bddbc0e432c5b87d8175178cee68d2eec9c4a1813c" +dependencies = [ + "log", + "ring 0.17.8", + "rustls-pki-types", + "rustls-webpki 0.102.2", + "subtle", + "zeroize", +] + [[package]] name = "rustls-native-certs" version = "0.6.3" @@ -6696,7 +6840,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", - "rustls-pemfile", + "rustls-pemfile 1.0.4", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-native-certs" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" +dependencies = [ + "openssl-probe", + "rustls-pemfile 2.1.1", + "rustls-pki-types", "schannel", "security-framework", ] @@ -6710,6 +6867,22 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pemfile" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f48172685e6ff52a556baa527774f61fcaa884f59daf3375c62a3f1cd2549dab" +dependencies = [ + "base64 0.21.7", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd36cc4259e3e4514335c4a138c6b43171a8d61d8f5c9348f9fc7529416f247" + [[package]] name = "rustls-webpki" version = "0.101.7" @@ -6720,6 +6893,17 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "rustls-webpki" +version = "0.102.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" +dependencies = [ + "ring 0.17.8", + "rustls-pki-types", + "untrusted 0.9.0", +] + [[package]] name = "rustversion" version = "1.0.14" @@ -6787,9 +6971,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ef2175c2907e7c8bc0a9c3f86aeb5ec1f3b275300ad58a44d0c3ae379a5e52e" +checksum = "788745a868b0e751750388f4e6546eb921ef714a4317fa6954f7cde114eb2eb7" dependencies = [ "cfg-if", "derive_more", @@ -6799,9 +6983,9 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b8eb8fd61c5cdd3390d9b2132300a7e7618955b98b8416f118c1b4e144f" +checksum = "7dc2f4e8bc344b9fc3d5f74f72c2e55bfc38d28dc2ebc69c194a3df424e4d9ac" dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", @@ -6904,9 +7088,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.9.2" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -6917,9 +7101,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" dependencies = [ "core-foundation-sys", "libc", @@ -6981,7 +7165,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -6997,9 +7181,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.114" +version = "1.0.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" dependencies = [ "indexmap", "itoa", @@ -7035,7 +7219,7 @@ checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -7081,7 +7265,7 @@ checksum = "b93fb4adc70021ac1b47f7d45e8cc4169baaa7ea58483bc5b721d19a26202212" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -7208,9 +7392,9 @@ dependencies = [ [[package]] name = "similar" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32fea41aca09ee824cc9724996433064c89f7777e60762749a4170a14abbfa21" +checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" [[package]] name = "simple_asn1" @@ -7378,7 +7562,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -7391,7 +7575,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -7409,9 +7593,9 @@ dependencies = [ [[package]] name = "subtle" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "svm-rs" @@ -7423,7 +7607,7 @@ dependencies = [ "fs2", "hex", "once_cell", - "reqwest", + "reqwest 0.11.27", "semver 1.0.22", "serde", "serde_json", @@ -7443,7 +7627,7 @@ dependencies = [ "dirs 5.0.1", "fs4", "once_cell", - "reqwest", + "reqwest 0.11.27", "semver 1.0.22", "serde", "serde_json", @@ -7479,9 +7663,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.53" +version = "2.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" +checksum = "11a6ae1e52eb25aab8f3fb9fca13be982a373b8f1157ca14b897a825ba4a2d35" dependencies = [ "proc-macro2", "quote", @@ -7497,7 +7681,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -7619,7 +7803,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -7736,9 +7920,9 @@ checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" [[package]] name = "tokio" -version = "1.36.0" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", "bytes", @@ -7761,7 +7945,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -7795,6 +7979,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +dependencies = [ + "rustls 0.22.3", + "rustls-pki-types", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.15" @@ -7948,8 +8143,8 @@ dependencies = [ "bytes", "futures-core", "futures-util", - "http", - "http-body", + "http 0.2.12", + "http-body 0.4.6", "http-range-header", "httpdate", "mime", @@ -7995,7 +8190,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -8118,7 +8313,7 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http", + "http 0.2.12", "httparse", "log", "native-tls", @@ -8360,7 +8555,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", "wasm-bindgen-shared", ] @@ -8394,7 +8589,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -8796,9 +8991,9 @@ dependencies = [ [[package]] name = "xml-rs" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" +checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193" [[package]] name = "yansi" @@ -8829,7 +9024,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -8849,7 +9044,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.57", ] [[package]] @@ -8893,9 +9088,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.9+zstd.1.5.5" +version = "2.0.10+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index 640c652ae..a7804cc45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ resolver = "2" [workspace.package] version = "0.2.0" edition = "2021" -rust-version = "1.76" # Remember to update clippy.toml as well +rust-version = "1.76" # Remember to update clippy.toml as well authors = ["Foundry Contributors"] license = "MIT OR Apache-2.0" homepage = "https://github.com/foundry-rs/foundry" @@ -139,13 +139,15 @@ foundry-wallets = { path = "crates/wallets" } foundry-linking = { path = "crates/linking" } # solc & compilation utilities -foundry-block-explorers = { version = "0.2.3", default-features = false } +foundry-block-explorers = { version = "0.2.4", default-features = false } foundry-compilers = { version = "0.3.13", default-features = false } ## revm # no default features to avoid c-kzg revm = { version = "7.1", default-features = false, features = ["std"] } -revm-primitives = { version = "3", default-features = false, features = ["std"] } +revm-primitives = { version = "3", default-features = false, features = [ + "std", +] } revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "ba0b6ab", features = [ "serde", ] } @@ -189,7 +191,10 @@ solang-parser = "=0.3.3" ## misc arrayvec = "0.7" base64 = "0.22" -chrono = { version = "0.4", default-features = false, features = ["clock", "std"] } +chrono = { version = "0.4", default-features = false, features = [ + "clock", + "std", +] } color-eyre = "0.6" derive_more = "0.99" evm-disassembler = "0.5" diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml index 0504bb369..7e43c2276 100644 --- a/crates/config/Cargo.toml +++ b/crates/config/Cargo.toml @@ -29,7 +29,7 @@ Inflector = "0.11" number_prefix = "0.4" once_cell = "1" regex = "1" -reqwest = { version = "0.11", default-features = false } +reqwest = { version = "0.12", default-features = false } semver = { version = "1", features = ["serde"] } serde_json.workspace = true serde_regex = "1" @@ -47,3 +47,7 @@ path-slash = "0.2.1" pretty_assertions.workspace = true figment = { version = "0.10", features = ["test"] } tempfile = "3" + +[features] +default = ["rustls"] +rustls = ["reqwest/rustls-tls-native-roots"] diff --git a/crates/verify/src/etherscan/mod.rs b/crates/verify/src/etherscan/mod.rs index 7e79321ef..5922f64ad 100644 --- a/crates/verify/src/etherscan/mod.rs +++ b/crates/verify/src/etherscan/mod.rs @@ -304,7 +304,10 @@ impl EtherscanVerificationProvider { builder = if let Some(api_url) = api_url { // we don't want any trailing slashes because this can cause cloudflare issues: let api_url = api_url.trim_end_matches('/'); - builder.with_api_url(api_url)?.with_url(base_url.unwrap_or(api_url))? + builder + .with_chain_id(chain) + .with_api_url(api_url)? + .with_url(base_url.unwrap_or(api_url))? } else { builder.chain(chain)? }; From f6208d8db68f9acbe4ff8cd76958309efb61ea0b Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 3 Apr 2024 15:22:55 +0400 Subject: [PATCH 129/430] fix: debugger breaks when source file has multiple contract definitions (#7550) * fix contracts sources Co-authored-by: lazymio * fix doc * fmt --------- Co-authored-by: lazymio --- crates/common/src/compile.rs | 51 ++++++++++++++++++++++----------- crates/debugger/src/tui/draw.rs | 4 +-- crates/debugger/src/tui/mod.rs | 4 +-- 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index af91a0176..654ddd693 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -288,8 +288,10 @@ impl ProjectCompiler { pub struct ContractSources { /// Map over artifacts' contract names -> vector of file IDs pub ids_by_name: HashMap>, - /// Map over file_id -> (source code, contract) - pub sources_by_id: FxHashMap, + /// Map over file_id -> source code + pub sources_by_id: FxHashMap, + /// Map over file_id -> contract name -> bytecode + pub artifacts_by_id: FxHashMap>, } impl ContractSources { @@ -327,30 +329,45 @@ impl ContractSources { bytecode: ContractBytecodeSome, ) { self.ids_by_name.entry(artifact_id.name.clone()).or_default().push(file_id); - self.sources_by_id.insert(file_id, (source, bytecode)); + self.sources_by_id.insert(file_id, source); + self.artifacts_by_id.entry(file_id).or_default().insert(artifact_id.name.clone(), bytecode); } /// Returns the source for a contract by file ID. - pub fn get(&self, id: u32) -> Option<&(String, ContractBytecodeSome)> { + pub fn get(&self, id: u32) -> Option<&String> { self.sources_by_id.get(&id) } /// Returns all sources for a contract by name. - pub fn get_sources( - &self, - name: &str, - ) -> Option> { - self.ids_by_name - .get(name) - .map(|ids| ids.iter().filter_map(|id| Some((*id, self.sources_by_id.get(id)?)))) - } - - /// Returns all (name, source) pairs. - pub fn entries(&self) -> impl Iterator { - self.ids_by_name.iter().flat_map(|(name, ids)| { - ids.iter().filter_map(|id| self.sources_by_id.get(id).map(|s| (name.clone(), s))) + pub fn get_sources<'a>( + &'a self, + name: &'a str, + ) -> Option> { + self.ids_by_name.get(name).map(|ids| { + ids.iter().filter_map(|id| { + Some(( + *id, + self.sources_by_id.get(id)?.as_ref(), + self.artifacts_by_id.get(id)?.get(name)?, + )) + }) }) } + + /// Returns all (name, source, bytecode) sets. + pub fn entries(&self) -> impl Iterator { + self.artifacts_by_id + .iter() + .filter_map(|(id, artifacts)| { + let source = self.sources_by_id.get(id)?; + Some( + artifacts + .iter() + .map(move |(name, bytecode)| (name.as_ref(), source.as_ref(), bytecode)), + ) + }) + .flatten() + } } // https://eips.ethereum.org/EIPS/eip-170 diff --git a/crates/debugger/src/tui/draw.rs b/crates/debugger/src/tui/draw.rs index 76e596513..8d91f2803 100644 --- a/crates/debugger/src/tui/draw.rs +++ b/crates/debugger/src/tui/draw.rs @@ -346,7 +346,7 @@ impl DebuggerContext<'_> { let is_create = matches!(self.call_kind(), CallKind::Create | CallKind::Create2); let pc = self.current_step().pc; let Some((source_element, source_code)) = - files_source_code.find_map(|(file_id, (source_code, contract_source))| { + files_source_code.find_map(|(file_id, source_code, contract_source)| { let bytecode = if is_create { &contract_source.bytecode } else { @@ -369,7 +369,7 @@ impl DebuggerContext<'_> { .contracts_sources .sources_by_id .get(&(source_element.index?)) - .map(|(source_code, _)| (source_element.clone(), source_code)) + .map(|source_code| (source_element.clone(), source_code.as_ref())) }) }) else { diff --git a/crates/debugger/src/tui/mod.rs b/crates/debugger/src/tui/mod.rs index d9543fc58..16964e768 100644 --- a/crates/debugger/src/tui/mod.rs +++ b/crates/debugger/src/tui/mod.rs @@ -67,9 +67,9 @@ impl Debugger { ) -> Self { let pc_ic_maps = contracts_sources .entries() - .filter_map(|(contract_name, (_, contract))| { + .filter_map(|(contract_name, _, contract)| { Some(( - contract_name.clone(), + contract_name.to_owned(), ( PcIcMap::new(SpecId::LATEST, contract.bytecode.bytes()?), PcIcMap::new(SpecId::LATEST, contract.deployed_bytecode.bytes()?), From 0875a834de77fbafa933bebbd51839de3841e10e Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Thu, 4 Apr 2024 13:33:28 +0300 Subject: [PATCH 130/430] fix(fmt): fix indent closing parenthesis enclosed in { } (#7557) * fix(fmt): fix indent closing parenthesis enclosed in { } * Fix testdata bad formatting --- crates/fmt/src/formatter.rs | 2 +- crates/fmt/testdata/Repros/fmt.sol | 12 ++++++++++++ crates/fmt/testdata/Repros/original.sol | 12 ++++++++++++ testdata/default/cheats/RecordAccountAccesses.t.sol | 2 +- 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/crates/fmt/src/formatter.rs b/crates/fmt/src/formatter.rs index bf6965100..d10cf4b38 100644 --- a/crates/fmt/src/formatter.rs +++ b/crates/fmt/src/formatter.rs @@ -895,7 +895,7 @@ impl<'a, W: Write> Formatter<'a, W> { write_chunk!(fmt, "{}", stringified.trim_start()) })?; if !last.content.trim_start().is_empty() { - self.write_whitespace_separator(true)?; + self.indented(1, |fmt| fmt.write_whitespace_separator(true))?; } let last_chunk = self.chunk_at(last.loc_before(), last.loc_next(), last.spaced, &last.content); diff --git a/crates/fmt/testdata/Repros/fmt.sol b/crates/fmt/testdata/Repros/fmt.sol index 8439563ab..dc1ac24eb 100644 --- a/crates/fmt/testdata/Repros/fmt.sol +++ b/crates/fmt/testdata/Repros/fmt.sol @@ -5,3 +5,15 @@ function errorIdentifier() { bytes memory error = bytes(""); if (error.length > 0) {} } + +// https://github.com/foundry-rs/foundry/issues/7549 +function one() external { + this.other({ + data: abi.encodeCall( + this.other, + ( + "bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla" + ) + ) + }); +} diff --git a/crates/fmt/testdata/Repros/original.sol b/crates/fmt/testdata/Repros/original.sol index 8439563ab..cee4fc97a 100644 --- a/crates/fmt/testdata/Repros/original.sol +++ b/crates/fmt/testdata/Repros/original.sol @@ -5,3 +5,15 @@ function errorIdentifier() { bytes memory error = bytes(""); if (error.length > 0) {} } + +// https://github.com/foundry-rs/foundry/issues/7549 +function one() external { + this.other({ + data: abi.encodeCall( + this.other, + ( + "bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla" + ) + ) + }); +} diff --git a/testdata/default/cheats/RecordAccountAccesses.t.sol b/testdata/default/cheats/RecordAccountAccesses.t.sol index bea20570a..a0aa2cb53 100644 --- a/testdata/default/cheats/RecordAccountAccesses.t.sol +++ b/testdata/default/cheats/RecordAccountAccesses.t.sol @@ -943,7 +943,7 @@ contract RecordAccountAccessesTest is DSTest { data: abi.encodeCall( Create2or.create2, (bytes32(0), abi.encodePacked(type(ConstructorStorer).creationCode, abi.encode(true))) - ), + ), reverted: false, storageAccesses: new Vm.StorageAccess[](0), depth: 1 From c10f32a34d5f1d552432a25015c23d6b6ee9b8d4 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 4 Apr 2024 22:35:45 +0400 Subject: [PATCH 131/430] feat(test): only compile files needed for tests (#7334) * feat(forge test): only compile files needed for tests * remove comment * clippy * update fixtures * getCode + getDeployedCode updates * fixes * fix path matching * clippy * add config flag * fix * docs * fmt * patch compilers * fix Cargo.toml * update patch * update patch * doc * rm space * cargo cheats * new output selection fn * log compiler errors on failure * fixes --- Cargo.lock | 5 +- crates/cheatcodes/Cargo.toml | 1 + crates/cheatcodes/assets/cheatcodes.json | 4 +- crates/cheatcodes/spec/src/vm.rs | 6 +- crates/cheatcodes/src/config.rs | 20 ++- crates/cheatcodes/src/fs.rs | 69 ++++++++++- crates/chisel/src/executor.rs | 1 + crates/common/src/contracts.rs | 16 +-- crates/config/src/lib.rs | 8 +- crates/forge/bin/cmd/coverage.rs | 9 +- crates/forge/bin/cmd/test/mod.rs | 115 +++++++++++++----- crates/forge/src/lib.rs | 2 +- crates/forge/src/multi_runner.rs | 2 +- crates/forge/tests/cli/config.rs | 1 + .../suggest_when_no_tests_match.stdout | 4 - .../forge/tests/fixtures/warn_no_tests.stdout | 4 - .../tests/fixtures/warn_no_tests_match.stdout | 4 - crates/forge/tests/it/test_helpers.rs | 3 +- crates/script/src/build.rs | 12 +- crates/script/src/execute.rs | 6 +- crates/script/src/lib.rs | 8 +- testdata/default/cheats/GetCode.t.sol | 10 ++ testdata/default/cheats/GetDeployedCode.t.sol | 12 ++ 23 files changed, 244 insertions(+), 78 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d929efd32..87931fb8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3188,6 +3188,7 @@ dependencies = [ "parking_lot", "revm", "rustc-hash", + "semver 1.0.22", "serde_json", "thiserror", "toml 0.8.12", @@ -4062,9 +4063,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fbd2820c5e49886948654ab546d0688ff24530286bdcf8fca3cefb16d4618eb" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ "bytes", "fnv", diff --git a/crates/cheatcodes/Cargo.toml b/crates/cheatcodes/Cargo.toml index 47315f5ce..8006867f4 100644 --- a/crates/cheatcodes/Cargo.toml +++ b/crates/cheatcodes/Cargo.toml @@ -42,5 +42,6 @@ k256.workspace = true walkdir = "2" p256 = "0.13.2" thiserror = "1" +semver = "1" rustc-hash.workspace = true dialoguer = "0.11.0" diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index dea514322..0245890f9 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -4621,7 +4621,7 @@ { "func": { "id": "getCode", - "description": "Gets the creation bytecode from an artifact file. Takes in the relative path to the json file.", + "description": "Gets the creation bytecode from an artifact file. Takes in the relative path to the json file or the path to the\nartifact in the form of :: where and parts are optional.", "declaration": "function getCode(string calldata artifactPath) external view returns (bytes memory creationBytecode);", "visibility": "external", "mutability": "view", @@ -4641,7 +4641,7 @@ { "func": { "id": "getDeployedCode", - "description": "Gets the deployed bytecode from an artifact file. Takes in the relative path to the json file.", + "description": "Gets the deployed bytecode from an artifact file. Takes in the relative path to the json file or the path to the\nartifact in the form of :: where and parts are optional.", "declaration": "function getDeployedCode(string calldata artifactPath) external view returns (bytes memory runtimeBytecode);", "visibility": "external", "mutability": "view", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 8f0de363a..42bc15678 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1397,11 +1397,13 @@ interface Vm { #[cheatcode(group = Filesystem)] function writeLine(string calldata path, string calldata data) external; - /// Gets the creation bytecode from an artifact file. Takes in the relative path to the json file. + /// Gets the creation bytecode from an artifact file. Takes in the relative path to the json file or the path to the + /// artifact in the form of :: where and parts are optional. #[cheatcode(group = Filesystem)] function getCode(string calldata artifactPath) external view returns (bytes memory creationBytecode); - /// Gets the deployed bytecode from an artifact file. Takes in the relative path to the json file. + /// Gets the deployed bytecode from an artifact file. Takes in the relative path to the json file or the path to the + /// artifact in the form of :: where and parts are optional. #[cheatcode(group = Filesystem)] function getDeployedCode(string calldata artifactPath) external view returns (bytes memory runtimeBytecode); diff --git a/crates/cheatcodes/src/config.rs b/crates/cheatcodes/src/config.rs index 1048f6b69..9d0ca2ea4 100644 --- a/crates/cheatcodes/src/config.rs +++ b/crates/cheatcodes/src/config.rs @@ -2,7 +2,7 @@ use super::Result; use crate::{script::ScriptWallets, Vm::Rpc}; use alloy_primitives::Address; use foundry_common::fs::normalize_path; -use foundry_compilers::{utils::canonicalize, ProjectPathsConfig}; +use foundry_compilers::{utils::canonicalize, ArtifactId, ProjectPathsConfig}; use foundry_config::{ cache::StorageCachingConfig, fs_permissions::FsAccessKind, Config, FsPermissions, ResolvedRpcEndpoints, @@ -43,11 +43,20 @@ pub struct CheatsConfig { pub labels: HashMap, /// Script wallets pub script_wallets: Option, + /// Artifacts which are guaranteed to be fresh (either recompiled or cached). + /// If Some, `vm.getDeployedCode` invocations are validated to be in scope of this list. + /// If None, no validation is performed. + pub available_artifacts: Option>, } impl CheatsConfig { /// Extracts the necessary settings from the Config - pub fn new(config: &Config, evm_opts: EvmOpts, script_wallets: Option) -> Self { + pub fn new( + config: &Config, + evm_opts: EvmOpts, + available_artifacts: Option>, + script_wallets: Option, + ) -> Self { let mut allowed_paths = vec![config.__root.0.clone()]; allowed_paths.extend(config.libs.clone()); allowed_paths.extend(config.allow_paths.clone()); @@ -55,6 +64,10 @@ impl CheatsConfig { let rpc_endpoints = config.rpc_endpoints.clone().resolved(); trace!(?rpc_endpoints, "using resolved rpc endpoints"); + // If user explicitly disabled safety checks, do not set available_artifacts + let available_artifacts = + if config.unchecked_cheatcode_artifacts { None } else { available_artifacts }; + Self { ffi: evm_opts.ffi, always_use_create_2_factory: evm_opts.always_use_create_2_factory, @@ -68,6 +81,7 @@ impl CheatsConfig { evm_opts, labels: config.labels.clone(), script_wallets, + available_artifacts, } } @@ -185,6 +199,7 @@ impl Default for CheatsConfig { evm_opts: Default::default(), labels: Default::default(), script_wallets: None, + available_artifacts: Default::default(), } } } @@ -199,6 +214,7 @@ mod tests { &Config { __root: PathBuf::from(root).into(), fs_permissions, ..Default::default() }, Default::default(), None, + None, ) } diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index 7789915b5..9760b4f16 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -5,12 +5,13 @@ use alloy_json_abi::ContractObject; use alloy_primitives::U256; use alloy_sol_types::SolValue; use dialoguer::{Input, Password}; -use foundry_common::{fs, get_artifact_path}; +use foundry_common::fs; use foundry_config::fs_permissions::FsAccessKind; +use semver::Version; use std::{ collections::hash_map::Entry, io::{BufRead, BufReader, Write}, - path::Path, + path::{Path, PathBuf}, process::Command, sync::mpsc, thread, @@ -269,9 +270,69 @@ impl Cheatcode for getDeployedCodeCall { } } +/// Returns the path to the json artifact depending on the input +fn get_artifact_path(state: &Cheatcodes, path: &str) -> Result { + if path.ends_with(".json") { + Ok(PathBuf::from(path)) + } else { + let mut parts = path.split(':'); + let file = PathBuf::from(parts.next().unwrap()); + let contract_name = parts.next(); + let version = parts.next(); + + let version = if let Some(version) = version { + Some(Version::parse(version).map_err(|_| fmt_err!("Error parsing version"))?) + } else { + None + }; + + // Use available artifacts list if available + if let Some(available_ids) = &state.config.available_artifacts { + let mut artifact = None; + + for id in available_ids.iter() { + // name might be in the form of "Counter.0.8.23" + let id_name = id.name.split('.').next().unwrap(); + + if !id.source.ends_with(&file) { + continue; + } + if let Some(name) = contract_name { + if id_name != name { + continue; + } + } + if let Some(ref version) = version { + if id.version.minor != version.minor || + id.version.major != version.major || + id.version.patch != version.patch + { + continue; + } + } + if artifact.is_some() { + return Err(fmt_err!("Multiple matching artifacts found")); + } + artifact = Some(id); + } + + let artifact = artifact.ok_or_else(|| fmt_err!("No matching artifact found"))?; + Ok(artifact.path.clone()) + } else { + let file = file.to_string_lossy(); + let contract_name = if let Some(contract_name) = contract_name { + contract_name.to_owned() + } else { + file.replace(".sol", "") + }; + Ok(state.config.paths.artifacts.join(format!("{file}/{contract_name}.json"))) + } + } +} + /// Reads the bytecode object(s) from the matching artifact fn read_bytecode(state: &Cheatcodes, path: &str) -> Result { - let path = get_artifact_path(&state.config.paths, path); + let path = get_artifact_path(state, path)?; let path = state.config.ensure_path_allowed(path, FsAccessKind::Read)?; let data = fs::read_to_string(path)?; serde_json::from_str::(&data).map_err(Into::into) @@ -424,7 +485,7 @@ fn prompt( mod tests { use super::*; use crate::CheatsConfig; - use std::{path::PathBuf, sync::Arc}; + use std::sync::Arc; fn cheats() -> Cheatcodes { let config = CheatsConfig { diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index 69e35f947..b5b440959 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -307,6 +307,7 @@ impl SessionSource { &self.config.foundry_config, self.config.evm_opts.clone(), None, + None, ) .into(), ) diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index 2687b93e3..c406d0e45 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -5,13 +5,12 @@ use alloy_primitives::{hex, Address, Selector, B256}; use eyre::Result; use foundry_compilers::{ artifacts::{CompactContractBytecode, ContractBytecodeSome}, - ArtifactId, ProjectPathsConfig, + ArtifactId, }; use std::{ collections::BTreeMap, fmt, ops::{Deref, DerefMut}, - path::PathBuf, }; type ArtifactWithContractRef<'a> = (&'a ArtifactId, &'a (JsonAbi, Vec)); @@ -170,19 +169,6 @@ pub fn get_file_name(id: &str) -> &str { id.split(':').next().unwrap_or(id) } -/// Returns the path to the json artifact depending on the input -pub fn get_artifact_path(paths: &ProjectPathsConfig, path: &str) -> PathBuf { - if path.ends_with(".json") { - PathBuf::from(path) - } else { - let parts: Vec<&str> = path.split(':').collect(); - let file = parts[0]; - let contract_name = - if parts.len() == 1 { parts[0].replace(".sol", "") } else { parts[1].to_string() }; - paths.artifacts.join(format!("{file}/{contract_name}.json")) - } -} - /// Helper function to convert CompactContractBytecode ~> ContractBytecodeSome pub fn compact_to_contract(contract: CompactContractBytecode) -> Result { Ok(ContractBytecodeSome { diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 0c53a6c14..88671dce1 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -387,6 +387,10 @@ pub struct Config { /// Address labels pub labels: HashMap, + /// Whether to enable safety checks for `vm.getCode` and `vm.getDeployedCode` invocations. + /// If disabled, it is possible to access artifacts which were not recompiled or cached. + pub unchecked_cheatcode_artifacts: bool, + /// The root path where the config detection started from, `Config::with_root` #[doc(hidden)] // We're skipping serialization here, so it won't be included in the [`Config::to_string()`] @@ -662,7 +666,8 @@ impl Config { self.create_project(false, true) } - fn create_project(&self, cached: bool, no_artifacts: bool) -> Result { + /// Creates a [Project] with the given `cached` and `no_artifacts` flags + pub fn create_project(&self, cached: bool, no_artifacts: bool) -> Result { let mut project = Project::builder() .artifacts(self.configured_artifacts_handler()) .paths(self.project_paths()) @@ -1925,6 +1930,7 @@ impl Default for Config { fmt: Default::default(), doc: Default::default(), labels: Default::default(), + unchecked_cheatcode_artifacts: false, __non_exhaustive: (), __warnings: vec![], } diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index 9b6d19e74..8dc4629b9 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -303,6 +303,8 @@ impl CoverageArgs { ) -> Result<()> { let root = project.paths.root; + let artifact_ids = output.artifact_ids().map(|(id, _)| id).collect(); + // Build the contract runner let env = evm_opts.evm_env().await?; let mut runner = MultiContractRunnerBuilder::default() @@ -310,7 +312,12 @@ impl CoverageArgs { .evm_spec(config.evm_spec_id()) .sender(evm_opts.sender) .with_fork(evm_opts.get_fork(&config, env.clone())) - .with_cheats_config(CheatsConfig::new(&config, evm_opts.clone(), None)) + .with_cheats_config(CheatsConfig::new( + &config, + evm_opts.clone(), + Some(artifact_ids), + None, + )) .with_test_options(TestOptions { fuzz: config.fuzz, invariant: config.invariant, diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 78000de61..b4c26e7ee 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -6,9 +6,10 @@ use forge::{ decode::decode_console_logs, gas_report::GasReport, inspectors::CheatsConfig, + multi_runner::matches_contract, result::{SuiteResult, TestOutcome, TestStatus}, traces::{identifier::SignaturesIdentifier, CallTraceDecoderBuilder, TraceKind}, - MultiContractRunner, MultiContractRunnerBuilder, TestOptions, TestOptionsBuilder, + MultiContractRunner, MultiContractRunnerBuilder, TestFilter, TestOptions, TestOptionsBuilder, }; use foundry_cli::{ opts::CoreBuildArgs, @@ -19,6 +20,7 @@ use foundry_common::{ evm::EvmArgs, shell, }; +use foundry_compilers::artifacts::output_selection::OutputSelection; use foundry_config::{ figment, figment::{ @@ -30,7 +32,12 @@ use foundry_config::{ use foundry_debugger::Debugger; use foundry_evm::traces::identifier::TraceIdentifiers; use regex::Regex; -use std::{sync::mpsc::channel, time::Instant}; +use std::{ + collections::{BTreeMap, BTreeSet}, + path::PathBuf, + sync::mpsc::channel, + time::Instant, +}; use watchexec::config::{InitConfig, RuntimeConfig}; use yansi::Paint; @@ -134,6 +141,69 @@ impl TestArgs { self.execute_tests().await } + /// Returns sources which include any tests to be executed. + /// If no filters are provided, sources are filtered by existence of test/invariant methods in + /// them, If filters are provided, sources are additionaly filtered by them. + pub fn get_sources_to_compile( + &self, + config: &Config, + filter: &ProjectPathsAwareFilter, + ) -> Result> { + let mut project = config.create_project(true, true)?; + project.solc_config.settings.output_selection = + OutputSelection::common_output_selection(["abi".to_string()]); + let output = project.compile()?; + + if output.has_compiler_errors() { + println!("{}", output); + eyre::bail!("Compilation failed"); + } + + // ABIs of all sources + let abis = output + .into_artifacts() + .filter_map(|(id, artifact)| artifact.abi.map(|abi| (id, abi))) + .collect::>(); + + // Filter sources by their abis and contract names. + let sources = abis + .iter() + .filter(|(id, abi)| matches_contract(id, abi, filter)) + .map(|(id, _)| id.source.clone()) + .collect::>(); + + if sources.is_empty() { + if filter.is_empty() { + println!( + "No tests found in project! \ + Forge looks for functions that starts with `test`." + ); + } else { + println!("No tests match the provided pattern:"); + print!("{filter}"); + + // Try to suggest a test when there's no match + if let Some(test_pattern) = &filter.args().test_pattern { + let test_name = test_pattern.as_str(); + let candidates = abis + .into_iter() + .filter(|(id, _)| { + filter.matches_path(&id.source) && filter.matches_contract(&id.name) + }) + .flat_map(|(_, abi)| abi.functions.into_keys()) + .collect::>(); + if let Some(suggestion) = utils::did_you_mean(test_name, candidates).pop() { + println!("\nDid you mean `{suggestion}`?"); + } + } + } + + eyre::bail!("No tests to run"); + } + + Ok(sources) + } + /// Executes all the tests in the project. /// /// This will trigger the build process first. On success all test contracts that match the @@ -168,10 +238,12 @@ impl TestArgs { let mut filter = self.filter(&config); trace!(target: "forge::test", ?filter, "using filter"); - let mut compiler = ProjectCompiler::new().quiet_if(self.json || self.opts.silent); - if config.sparse_mode { - compiler = compiler.filter(Box::new(filter.clone())); - } + let sources_to_compile = self.get_sources_to_compile(&config, &filter)?; + + let compiler = ProjectCompiler::new() + .quiet_if(self.json || self.opts.silent) + .files(sources_to_compile); + let output = compiler.compile(&project)?; // Create test options from general project settings and compiler output. @@ -199,13 +271,20 @@ impl TestArgs { // Clone the output only if we actually need it later for the debugger. let output_clone = should_debug.then(|| output.clone()); + let artifact_ids = output.artifact_ids().map(|(id, _)| id).collect(); + let runner = MultiContractRunnerBuilder::default() .set_debug(should_debug) .initial_balance(evm_opts.initial_balance) .evm_spec(config.evm_spec_id()) .sender(evm_opts.sender) .with_fork(evm_opts.get_fork(&config, env.clone())) - .with_cheats_config(CheatsConfig::new(&config, evm_opts.clone(), None)) + .with_cheats_config(CheatsConfig::new( + &config, + evm_opts.clone(), + Some(artifact_ids), + None, + )) .with_test_options(test_options) .enable_isolation(evm_opts.isolate) .build(project_root, output, env, evm_opts)?; @@ -270,28 +349,6 @@ impl TestArgs { trace!(target: "forge::test", "running all tests"); let num_filtered = runner.matching_test_functions(filter).count(); - if num_filtered == 0 { - println!(); - if filter.is_empty() { - println!( - "No tests found in project! \ - Forge looks for functions that starts with `test`." - ); - } else { - println!("No tests match the provided pattern:"); - print!("{filter}"); - - // Try to suggest a test when there's no match - if let Some(test_pattern) = &filter.args().test_pattern { - let test_name = test_pattern.as_str(); - // Filter contracts but not test functions. - let candidates = runner.all_test_functions(filter).map(|f| &f.name); - if let Some(suggestion) = utils::did_you_mean(test_name, candidates).pop() { - println!("\nDid you mean `{suggestion}`?"); - } - } - } - } if self.debug.is_some() && num_filtered != 1 { eyre::bail!( "{num_filtered} tests matched your criteria, but exactly 1 test must match in order to run the debugger.\n\n\ diff --git a/crates/forge/src/lib.rs b/crates/forge/src/lib.rs index d163f819c..98dc0aa47 100644 --- a/crates/forge/src/lib.rs +++ b/crates/forge/src/lib.rs @@ -15,7 +15,7 @@ pub mod coverage; pub mod gas_report; -mod multi_runner; +pub mod multi_runner; pub use multi_runner::{MultiContractRunner, MultiContractRunnerBuilder}; mod runner; diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index d545d773d..94d0b0155 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -383,7 +383,7 @@ impl MultiContractRunnerBuilder { } } -fn matches_contract(id: &ArtifactId, abi: &JsonAbi, filter: &dyn TestFilter) -> bool { +pub fn matches_contract(id: &ArtifactId, abi: &JsonAbi, filter: &dyn TestFilter) -> bool { (filter.matches_path(&id.source) && filter.matches_contract(&id.name)) && abi.functions().any(|func| is_matching_test(func, filter)) } diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 81884f3bc..22d885af4 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -126,6 +126,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { labels: Default::default(), cancun: true, isolate: true, + unchecked_cheatcode_artifacts: false, __non_exhaustive: (), __warnings: vec![], }; diff --git a/crates/forge/tests/fixtures/suggest_when_no_tests_match.stdout b/crates/forge/tests/fixtures/suggest_when_no_tests_match.stdout index 1c27d8005..1cf6ad73f 100644 --- a/crates/forge/tests/fixtures/suggest_when_no_tests_match.stdout +++ b/crates/forge/tests/fixtures/suggest_when_no_tests_match.stdout @@ -1,7 +1,3 @@ -Compiling 1 files with 0.8.23 -Solc 0.8.23 finished in 185.25ms -Compiler run successful! - No tests match the provided pattern: match-test: `testA.*` no-match-test: `testB.*` diff --git a/crates/forge/tests/fixtures/warn_no_tests.stdout b/crates/forge/tests/fixtures/warn_no_tests.stdout index 9b2b8bff4..a9a7e7fc6 100644 --- a/crates/forge/tests/fixtures/warn_no_tests.stdout +++ b/crates/forge/tests/fixtures/warn_no_tests.stdout @@ -1,5 +1 @@ -Compiling 1 files with 0.8.23 -Solc 0.8.23 -Compiler run successful! - No tests found in project! Forge looks for functions that starts with `test`. diff --git a/crates/forge/tests/fixtures/warn_no_tests_match.stdout b/crates/forge/tests/fixtures/warn_no_tests_match.stdout index 56f068238..4b4080f15 100644 --- a/crates/forge/tests/fixtures/warn_no_tests_match.stdout +++ b/crates/forge/tests/fixtures/warn_no_tests_match.stdout @@ -1,7 +1,3 @@ -Compiling 1 files with 0.8.23 -Solc 0.8.23 -Compiler run successful! - No tests match the provided pattern: match-test: `testA.*` no-match-test: `testB.*` diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index 0112d4bb9..8d54976cc 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -204,8 +204,9 @@ impl ForgeTestData { let opts = self.evm_opts.clone(); let env = opts.local_evm_env(); let output = self.output.clone(); + let artifact_ids = output.artifact_ids().map(|(id, _)| id).collect(); self.base_runner() - .with_cheats_config(CheatsConfig::new(&config, opts.clone(), None)) + .with_cheats_config(CheatsConfig::new(&config, opts.clone(), Some(artifact_ids), None)) .sender(config.sender) .with_test_options(self.test_opts.clone()) .build(root, output, env, opts.clone()) diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index b0aba755e..ab17a95a9 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -35,6 +35,9 @@ pub struct BuildData { pub output: ProjectCompileOutput, /// Id of target contract artifact. pub target: ArtifactId, + /// Artifact ids of the contracts. Passed to cheatcodes to enable usage of + /// `vm.getDeployedCode`. + pub artifact_ids: Vec, } impl BuildData { @@ -211,6 +214,8 @@ impl PreprocessedState { let mut target_id: Option = None; + let artifact_ids = output.artifact_ids().map(|(id, _)| id).collect(); + // Find target artfifact id by name and path in compilation artifacts. for (id, contract) in output.artifact_ids().filter(|(id, _)| id.source == target_path) { if let Some(name) = &target_name { @@ -247,7 +252,12 @@ impl PreprocessedState { args, script_config, script_wallets, - build_data: BuildData { output, target, project_root: project.root().clone() }, + build_data: BuildData { + output, + target, + project_root: project.root().clone(), + artifact_ids, + }, }) } } diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index b2fab7154..d89461c9e 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -98,7 +98,11 @@ impl PreExecutionState { pub async fn execute(mut self) -> Result { let mut runner = self .script_config - .get_runner_with_cheatcodes(self.script_wallets.clone(), self.args.debug) + .get_runner_with_cheatcodes( + self.build_data.build_data.artifact_ids.clone(), + self.script_wallets.clone(), + self.args.debug, + ) .await?; let mut result = self.execute_with_runner(&mut runner).await?; diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index fd4ba66f6..fc8678b41 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -550,15 +550,16 @@ impl ScriptConfig { async fn get_runner_with_cheatcodes( &mut self, + artifact_ids: Vec, script_wallets: ScriptWallets, debug: bool, ) -> Result { - self._get_runner(Some(script_wallets), debug).await + self._get_runner(Some((artifact_ids, script_wallets)), debug).await } async fn _get_runner( &mut self, - script_wallets: Option, + cheats_data: Option<(Vec, ScriptWallets)>, debug: bool, ) -> Result { trace!("preparing script runner"); @@ -587,7 +588,7 @@ impl ScriptConfig { .spec(self.config.evm_spec_id()) .gas_limit(self.evm_opts.gas_limit()); - if let Some(script_wallets) = script_wallets { + if let Some((artifact_ids, script_wallets)) = cheats_data { builder = builder.inspectors(|stack| { stack .debug(debug) @@ -595,6 +596,7 @@ impl ScriptConfig { CheatsConfig::new( &self.config, self.evm_opts.clone(), + Some(artifact_ids), Some(script_wallets), ) .into(), diff --git a/testdata/default/cheats/GetCode.t.sol b/testdata/default/cheats/GetCode.t.sol index 8f47188d5..d308712e9 100644 --- a/testdata/default/cheats/GetCode.t.sol +++ b/testdata/default/cheats/GetCode.t.sol @@ -4,6 +4,8 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; +contract TestContract {} + contract GetCodeTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); @@ -70,4 +72,12 @@ contract GetCodeTest is DSTest { function testFailGetUnlinked() public { vm.getCode("UnlinkedContract.sol"); } + + function testWithVersion() public { + bytes memory code = vm.getCode("cheats/GetCode.t.sol:TestContract:0.8.18"); + assertEq(type(TestContract).creationCode, code); + + vm._expectCheatcodeRevert("No matching artifact found"); + vm.getCode("cheats/GetCode.t.sol:TestContract:0.8.19"); + } } diff --git a/testdata/default/cheats/GetDeployedCode.t.sol b/testdata/default/cheats/GetDeployedCode.t.sol index fc8ed609e..8d95b243c 100644 --- a/testdata/default/cheats/GetDeployedCode.t.sol +++ b/testdata/default/cheats/GetDeployedCode.t.sol @@ -4,6 +4,8 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; +contract TestContract {} + contract GetDeployedCodeTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); @@ -36,6 +38,16 @@ contract GetDeployedCodeTest is DSTest { emit Payload(address(this), address(0), "hello"); over.emitPayload(address(0), "hello"); } + + function testWithVersion() public { + TestContract test = new TestContract(); + bytes memory code = vm.getDeployedCode("cheats/GetDeployedCode.t.sol:TestContract:0.8.18"); + + assertEq(address(test).code, code); + + vm._expectCheatcodeRevert("No matching artifact found"); + vm.getDeployedCode("cheats/GetDeployedCode.t.sol:TestContract:0.8.19"); + } } interface Override { From 1631c5ca8a17d546fc4b5f6ee7de7b230c2dddcb Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 4 Apr 2024 22:43:20 +0400 Subject: [PATCH 132/430] fix: do not flood dictionary with data dependent on fuzz inputs (#7552) * fix dictionary * clippy + fmt * fix --- crates/evm/evm/src/executors/fuzz/mod.rs | 15 +- crates/evm/evm/src/executors/invariant/mod.rs | 24 +- crates/evm/fuzz/src/inspector.rs | 7 +- crates/evm/fuzz/src/strategies/calldata.rs | 2 +- crates/evm/fuzz/src/strategies/mod.rs | 3 +- crates/evm/fuzz/src/strategies/param.rs | 4 +- crates/evm/fuzz/src/strategies/state.rs | 230 +++++++++++------- crates/forge/src/runner.rs | 14 +- 8 files changed, 170 insertions(+), 129 deletions(-) diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index 41beb7e43..ae63d2386 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -10,10 +10,7 @@ use foundry_evm_core::{ }; use foundry_evm_coverage::HitMaps; use foundry_evm_fuzz::{ - strategies::{ - build_initial_state, collect_state_from_call, fuzz_calldata, fuzz_calldata_from_state, - EvmFuzzState, - }, + strategies::{build_initial_state, fuzz_calldata, fuzz_calldata_from_state, EvmFuzzState}, BaseCounterExample, CounterExample, FuzzCase, FuzzError, FuzzTestResult, }; use foundry_evm_traces::CallTraceArena; @@ -89,7 +86,7 @@ impl FuzzedExecutor { ]; debug!(func=?func.name, should_fail, "fuzzing"); let run_result = self.runner.clone().run(&strat, |calldata| { - let fuzz_res = self.single_fuzz(&state, address, should_fail, calldata)?; + let fuzz_res = self.single_fuzz(address, should_fail, calldata)?; match fuzz_res { FuzzOutcome::Case(case) => { @@ -195,7 +192,6 @@ impl FuzzedExecutor { /// or a `CounterExampleOutcome` pub fn single_fuzz( &self, - state: &EvmFuzzState, address: Address, should_fail: bool, calldata: alloy_primitives::Bytes, @@ -206,9 +202,6 @@ impl FuzzedExecutor { .map_err(|_| TestCaseError::fail(FuzzError::FailedContractCall))?; let state_changeset = call.state_changeset.take().unwrap(); - // Build fuzzer state - collect_state_from_call(&call.logs, &state_changeset, state, &self.config.dictionary); - // When the `assume` cheatcode is called it returns a special string if call.result.as_ref() == MAGIC_ASSUME { return Err(TestCaseError::reject(FuzzError::AssumeReject)) @@ -247,9 +240,9 @@ impl FuzzedExecutor { /// Stores fuzz state for use with [fuzz_calldata_from_state] pub fn build_fuzz_state(&self) -> EvmFuzzState { if let Some(fork_db) = self.executor.backend.active_fork_db() { - build_initial_state(fork_db, &self.config.dictionary) + build_initial_state(fork_db, self.config.dictionary) } else { - build_initial_state(self.executor.backend.mem_db(), &self.config.dictionary) + build_initial_state(self.executor.backend.mem_db(), self.config.dictionary) } } } diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 5ed7998ac..835a2bc87 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -6,7 +6,7 @@ use alloy_primitives::{Address, FixedBytes, U256}; use alloy_sol_types::{sol, SolCall}; use eyre::{eyre, ContextCompat, Result}; use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; -use foundry_config::{FuzzDictionaryConfig, InvariantConfig}; +use foundry_config::InvariantConfig; use foundry_evm_core::{ constants::{CALLER, CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME}, utils::{get_function, StateChangeset}, @@ -17,8 +17,8 @@ use foundry_evm_fuzz::{ RandomCallGenerator, SenderFilters, TargetedContracts, }, strategies::{ - build_initial_state, collect_created_contracts, collect_state_from_call, invariant_strat, - override_call_strat, CalldataFuzzDictionary, EvmFuzzState, + build_initial_state, collect_created_contracts, invariant_strat, override_call_strat, + CalldataFuzzDictionary, EvmFuzzState, }, FuzzCase, FuzzedCases, }; @@ -246,13 +246,7 @@ impl<'a> InvariantExecutor<'a> { let mut state_changeset = call_result.state_changeset.to_owned().expect("no changesets"); - collect_data( - &mut state_changeset, - sender, - &call_result, - &fuzz_state, - &self.config.dictionary, - ); + collect_data(&mut state_changeset, sender, &call_result, &fuzz_state); if let Err(error) = collect_created_contracts( &state_changeset, @@ -325,11 +319,14 @@ impl<'a> InvariantExecutor<'a> { } fuzz_cases.borrow_mut().push(FuzzedCases::new(fuzz_runs)); + // Revert state to not persist values between runs. + fuzz_state.revert(); + Ok(()) }); trace!(target: "forge::test::invariant::calldata_address_fuzz_dictionary", "{:?}", calldata_fuzz_dictionary.inner.addresses); - trace!(target: "forge::test::invariant::dictionary", "{:?}", fuzz_state.read().values().iter().map(hex::encode).collect::>()); + trace!(target: "forge::test::invariant::dictionary", "{:?}", fuzz_state.dictionary_read().values().iter().map(hex::encode).collect::>()); let (reverts, error) = failures.into_inner().into_inner(); @@ -361,7 +358,7 @@ impl<'a> InvariantExecutor<'a> { // Stores fuzz state for use with [fuzz_calldata_from_state]. let fuzz_state: EvmFuzzState = - build_initial_state(self.executor.backend.mem_db(), &self.config.dictionary); + build_initial_state(self.executor.backend.mem_db(), self.config.dictionary); // During execution, any newly created contract is added here and used through the rest of // the fuzz run. @@ -668,7 +665,6 @@ fn collect_data( sender: &Address, call_result: &RawCallResult, fuzz_state: &EvmFuzzState, - config: &FuzzDictionaryConfig, ) { // Verify it has no code. let mut has_code = false; @@ -683,7 +679,7 @@ fn collect_data( sender_changeset = state_changeset.remove(sender); } - collect_state_from_call(&call_result.logs, &*state_changeset, fuzz_state, config); + fuzz_state.collect_state_from_call(&call_result.logs, &*state_changeset); // Re-add changes if let Some(changed) = sender_changeset { diff --git a/crates/evm/fuzz/src/inspector.rs b/crates/evm/fuzz/src/inspector.rs index e58afb214..5e7e644b1 100644 --- a/crates/evm/fuzz/src/inspector.rs +++ b/crates/evm/fuzz/src/inspector.rs @@ -1,4 +1,5 @@ use crate::{invariant::RandomCallGenerator, strategies::EvmFuzzState}; +use alloy_primitives::U256; use revm::{ interpreter::{CallInputs, CallOutcome, CallScheme, Interpreter}, Database, EvmContext, Inspector, @@ -61,11 +62,7 @@ impl Inspector for Fuzzer { impl Fuzzer { /// Collects `stack` and `memory` values into the fuzz dictionary. fn collect_data(&mut self, interpreter: &Interpreter) { - let mut state = self.fuzz_state.write(); - - for slot in interpreter.stack().data() { - state.values_mut().insert(slot.to_be_bytes()); - } + self.fuzz_state.collect_values(interpreter.stack().data().iter().map(U256::to_be_bytes)); // TODO: disabled for now since it's flooding the dictionary // for index in 0..interpreter.shared_memory.len() / 32 { diff --git a/crates/evm/fuzz/src/strategies/calldata.rs b/crates/evm/fuzz/src/strategies/calldata.rs index b3d41bfad..ff3bb5713 100644 --- a/crates/evm/fuzz/src/strategies/calldata.rs +++ b/crates/evm/fuzz/src/strategies/calldata.rs @@ -41,7 +41,7 @@ impl CalldataFuzzDictionaryConfig { if dict_size > 0 { addresses.extend(std::iter::repeat_with(Address::random).take(dict_size)); // Add all addresses that already had their PUSH bytes collected. - addresses.extend(state.read().addresses()); + addresses.extend(state.dictionary_read().addresses()); } Self { addresses: addresses.into_iter().collect() } diff --git a/crates/evm/fuzz/src/strategies/mod.rs b/crates/evm/fuzz/src/strategies/mod.rs index 63e008ec0..0e82a4d4b 100644 --- a/crates/evm/fuzz/src/strategies/mod.rs +++ b/crates/evm/fuzz/src/strategies/mod.rs @@ -14,8 +14,7 @@ pub use calldata::{ mod state; pub use state::{ - build_initial_state, collect_created_contracts, collect_state_from_call, - fuzz_calldata_from_state, EvmFuzzState, + build_initial_state, collect_created_contracts, fuzz_calldata_from_state, EvmFuzzState, }; mod invariants; diff --git a/crates/evm/fuzz/src/strategies/param.rs b/crates/evm/fuzz/src/strategies/param.rs index f287c75df..20e69a27e 100644 --- a/crates/evm/fuzz/src/strategies/param.rs +++ b/crates/evm/fuzz/src/strategies/param.rs @@ -89,7 +89,7 @@ pub fn fuzz_param_from_state( let state = state.clone(); // Use `Index` instead of `Selector` to not iterate over the entire dictionary. any::().prop_map(move |index| { - let state = state.read(); + let state = state.dictionary_read(); let values = state.values(); let index = index.index(values.len()); *values.iter().nth(index).unwrap() @@ -184,7 +184,7 @@ mod tests { let f = "testArray(uint64[2] calldata values)"; let func = get_func(f).unwrap(); let db = CacheDB::new(EmptyDB::default()); - let state = build_initial_state(&db, &FuzzDictionaryConfig::default()); + let state = build_initial_state(&db, FuzzDictionaryConfig::default()); let strat = proptest::prop_oneof![ 60 => fuzz_calldata(func.clone()), 40 => fuzz_calldata_from_state(func, &state), diff --git a/crates/evm/fuzz/src/strategies/state.rs b/crates/evm/fuzz/src/strategies/state.rs index df70ba885..3c8f490f3 100644 --- a/crates/evm/fuzz/src/strategies/state.rs +++ b/crates/evm/fuzz/src/strategies/state.rs @@ -7,7 +7,7 @@ use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; use foundry_config::FuzzDictionaryConfig; use foundry_evm_core::utils::StateChangeset; use indexmap::IndexSet; -use parking_lot::RwLock; +use parking_lot::{lock_api::RwLockReadGuard, RawRwLock, RwLock}; use proptest::prelude::{BoxedStrategy, Strategy}; use revm::{ db::{CacheDB, DatabaseRef}, @@ -19,16 +19,103 @@ use std::{fmt, sync::Arc}; /// A set of arbitrary 32 byte data from the VM used to generate values for the strategy. /// /// Wrapped in a shareable container. -pub type EvmFuzzState = Arc>; +#[derive(Clone, Debug)] +pub struct EvmFuzzState { + inner: Arc>, +} + +impl EvmFuzzState { + pub fn new(dictionary: FuzzDictionary) -> Self { + Self { inner: Arc::new(RwLock::new(dictionary)) } + } + + pub fn collect_values(&self, values: impl IntoIterator) { + let mut dict = self.inner.write(); + for value in values { + dict.insert_value(value); + } + } + + /// Collects state changes from a [StateChangeset] and logs into an [EvmFuzzState] according to + /// the given [FuzzDictionaryConfig]. + pub fn collect_state_from_call(&self, logs: &[Log], state_changeset: &StateChangeset) { + let mut dict = self.inner.write(); + + // Insert log topics and data. + for log in logs { + for topic in log.topics() { + dict.insert_value(topic.0); + } + let chunks = log.data.data.chunks_exact(32); + let rem = chunks.remainder(); + for chunk in chunks { + dict.insert_value(chunk.try_into().unwrap()); + } + if !rem.is_empty() { + dict.insert_value(B256::right_padding_from(rem).0); + } + } + + for (address, account) in state_changeset { + // Insert basic account information + dict.insert_value(address.into_word().into()); + + if dict.config.include_push_bytes { + // Insert push bytes + if let Some(code) = &account.info.code { + dict.insert_address(*address); + for push_byte in collect_push_bytes(code.bytes()) { + dict.insert_value(push_byte); + } + } + } + + if dict.config.include_storage { + // Insert storage + for (slot, value) in &account.storage { + let value = value.present_value; + dict.insert_value(B256::from(*slot).0); + dict.insert_value(B256::from(value).0); + // also add the value below and above the storage value to the dictionary. + if value != U256::ZERO { + let below_value = value - U256::from(1); + dict.insert_value(B256::from(below_value).0); + } + if value != U256::MAX { + let above_value = value + U256::from(1); + dict.insert_value(B256::from(above_value).0); + } + } + } + } + } + + /// Removes all newly added entries from the dictionary. + /// + /// Should be called between fuzz/invariant runs to avoid accumumlating data derived from fuzz + /// inputs. + pub fn revert(&self) { + self.inner.write().revert(); + } + + pub fn dictionary_read(&self) -> RwLockReadGuard<'_, RawRwLock, FuzzDictionary> { + self.inner.read() + } +} // We're using `IndexSet` to have a stable element order when restoring persisted state, as well as // for performance when iterating over the sets. -#[derive(Default)] pub struct FuzzDictionary { /// Collected state values. state_values: IndexSet<[u8; 32]>, /// Addresses that already had their PUSH bytes collected. addresses: IndexSet
, + /// Configuration for the dictionary. + config: FuzzDictionaryConfig, + /// New keys added to the dictionary since container initialization. + new_values: IndexSet<[u8; 32]>, + /// New addresses added to the dictionary since container initialization. + new_addreses: IndexSet
, } impl fmt::Debug for FuzzDictionary { @@ -41,14 +128,39 @@ impl fmt::Debug for FuzzDictionary { } impl FuzzDictionary { - #[inline] - pub fn values(&self) -> &IndexSet<[u8; 32]> { - &self.state_values + pub fn new( + initial_values: IndexSet<[u8; 32]>, + initial_addresses: IndexSet
, + config: FuzzDictionaryConfig, + ) -> Self { + Self { + state_values: initial_values, + addresses: initial_addresses, + config, + new_values: IndexSet::new(), + new_addreses: IndexSet::new(), + } + } + + pub fn insert_value(&mut self, value: [u8; 32]) { + if self.state_values.len() < self.config.max_fuzz_dictionary_values && + self.state_values.insert(value) + { + self.new_values.insert(value); + } + } + + pub fn insert_address(&mut self, address: Address) { + if self.addresses.len() < self.config.max_fuzz_dictionary_addresses && + self.addresses.insert(address) + { + self.new_addreses.insert(address); + } } #[inline] - pub fn values_mut(&mut self) -> &mut IndexSet<[u8; 32]> { - &mut self.state_values + pub fn values(&self) -> &IndexSet<[u8; 32]> { + &self.state_values } #[inline] @@ -56,9 +168,16 @@ impl FuzzDictionary { &self.addresses } - #[inline] - pub fn addresses_mut(&mut self) -> &mut IndexSet
{ - &mut self.addresses + pub fn revert(&mut self) { + for key in self.new_values.iter() { + self.state_values.swap_remove(key); + } + for address in self.new_addreses.iter() { + self.addresses.swap_remove(address); + } + + self.new_values.clear(); + self.new_addreses.clear(); } } @@ -88,22 +207,22 @@ pub fn fuzz_calldata_from_state(func: Function, state: &EvmFuzzState) -> BoxedSt /// Builds the initial [EvmFuzzState] from a database. pub fn build_initial_state( db: &CacheDB, - config: &FuzzDictionaryConfig, + config: FuzzDictionaryConfig, ) -> EvmFuzzState { - let mut state = FuzzDictionary::default(); + let mut values = IndexSet::new(); + let mut addresses = IndexSet::new(); for (address, account) in db.accounts.iter() { let address: Address = *address; // Insert basic account information - state.values_mut().insert(address.into_word().into()); + values.insert(address.into_word().into()); // Insert push bytes if config.include_push_bytes { if let Some(code) = &account.info.code { - if state.addresses_mut().insert(address) { - for push_byte in collect_push_bytes(code.bytes()) { - state.values_mut().insert(push_byte); - } + addresses.insert(address); + for push_byte in collect_push_bytes(code.bytes()) { + values.insert(push_byte); } } } @@ -111,16 +230,16 @@ pub fn build_initial_state( if config.include_storage { // Insert storage for (slot, value) in &account.storage { - state.values_mut().insert(B256::from(*slot).0); - state.values_mut().insert(B256::from(*value).0); + values.insert(B256::from(*slot).0); + values.insert(B256::from(*value).0); // also add the value below and above the storage value to the dictionary. if *value != U256::ZERO { let below_value = value - U256::from(1); - state.values_mut().insert(B256::from(below_value).0); + values.insert(B256::from(below_value).0); } if *value != U256::MAX { let above_value = value + U256::from(1); - state.values_mut().insert(B256::from(above_value).0); + values.insert(B256::from(above_value).0); } } } @@ -128,73 +247,12 @@ pub fn build_initial_state( // need at least some state data if db is empty otherwise we can't select random data for state // fuzzing - if state.values().is_empty() { + if values.is_empty() { // prefill with a random addresses - state.values_mut().insert(Address::random().into_word().into()); + values.insert(Address::random().into_word().into()); } - Arc::new(RwLock::new(state)) -} - -/// Collects state changes from a [StateChangeset] and logs into an [EvmFuzzState] according to the -/// given [FuzzDictionaryConfig]. -pub fn collect_state_from_call( - logs: &[Log], - state_changeset: &StateChangeset, - state: &EvmFuzzState, - config: &FuzzDictionaryConfig, -) { - let mut state = state.write(); - - // Insert log topics and data. - for log in logs { - for topic in log.topics() { - state.values_mut().insert(topic.0); - } - let chunks = log.data.data.chunks_exact(32); - let rem = chunks.remainder(); - for chunk in chunks { - state.values_mut().insert(chunk.try_into().unwrap()); - } - if !rem.is_empty() { - state.values_mut().insert(B256::right_padding_from(rem).0); - } - } - - for (address, account) in state_changeset { - // Insert basic account information - state.values_mut().insert(address.into_word().into()); - - if config.include_push_bytes && state.addresses.len() < config.max_fuzz_dictionary_addresses - { - // Insert push bytes - if let Some(code) = &account.info.code { - if state.addresses_mut().insert(*address) { - for push_byte in collect_push_bytes(code.bytes()) { - state.values_mut().insert(push_byte); - } - } - } - } - - if config.include_storage && state.state_values.len() < config.max_fuzz_dictionary_values { - // Insert storage - for (slot, value) in &account.storage { - let value = value.present_value; - state.values_mut().insert(B256::from(*slot).0); - state.values_mut().insert(B256::from(value).0); - // also add the value below and above the storage value to the dictionary. - if value != U256::ZERO { - let below_value = value - U256::from(1); - state.values_mut().insert(B256::from(below_value).0); - } - if value != U256::MAX { - let above_value = value + U256::from(1); - state.values_mut().insert(B256::from(above_value).0); - } - } - } - } + EvmFuzzState::new(FuzzDictionary::new(values, addresses, config)) } /// The maximum number of bytes we will look at in bytecodes to find push bytes (24 KiB). diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 7f3258e35..508ee9234 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -611,7 +611,6 @@ impl<'a> ContractRunner<'a> { self.sender, fuzz_config.clone(), ); - let state = fuzzed_executor.build_fuzz_state(); let result = fuzzed_executor.fuzz(func, address, should_fail, self.revert_decoder); let mut debug = Default::default(); @@ -650,13 +649,12 @@ impl<'a> ContractRunner<'a> { result.first_case.calldata.clone() }; // rerun the last relevant test with traces - let debug_result = FuzzedExecutor::new( - debug_executor, - runner, - self.sender, - fuzz_config, - ) - .single_fuzz(&state, address, should_fail, calldata); + let debug_result = + FuzzedExecutor::new(debug_executor, runner, self.sender, fuzz_config).single_fuzz( + address, + should_fail, + calldata, + ); (debug, breakpoints) = match debug_result { Ok(fuzz_outcome) => match fuzz_outcome { From 1281421e04144a8de0341f9cad9b623a012a74bf Mon Sep 17 00:00:00 2001 From: Krishang <93703995+kamuik16@users.noreply.github.com> Date: Fri, 5 Apr 2024 00:34:59 +0530 Subject: [PATCH 133/430] Feat: Index cheatcode for Strings (#7539) * feat: index cheatcode * some nits to make it work * nit: use as_str() * final changes * chore: reviewed changes --- crates/cheatcodes/assets/cheatcodes.json | 20 ++++++++++++++++++++ crates/cheatcodes/spec/src/vm.rs | 5 +++++ crates/cheatcodes/src/string.rs | 9 +++++++++ testdata/cheats/Vm.sol | 1 + testdata/default/cheats/StringUtils.t.sol | 12 ++++++++++++ 5 files changed, 47 insertions(+) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 0245890f9..360d5de1c 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -4798,6 +4798,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "indexOf", + "description": "Returns the index of the first occurrence of a `key` in an `input` string.\nReturns `NOT_FOUND` (i.e. `type(uint256).max`) if the `key` is not found.\nReturns 0 in case of an empty `key`.", + "declaration": "function indexOf(string memory input, string memory key) external pure returns (uint256);", + "visibility": "external", + "mutability": "pure", + "signature": "indexOf(string,string)", + "selector": "0x8a0807b7", + "selectorBytes": [ + 138, + 8, + 7, + 183 + ] + }, + "group": "string", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "isDir", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 42bc15678..93042c199 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1680,6 +1680,11 @@ interface Vm { /// Splits the given `string` into an array of strings divided by the `delimiter`. #[cheatcode(group = String)] function split(string calldata input, string calldata delimiter) external pure returns (string[] memory outputs); + /// Returns the index of the first occurrence of a `key` in an `input` string. + /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `key` is not found. + /// Returns 0 in case of an empty `key`. + #[cheatcode(group = String)] + function indexOf(string memory input, string memory key) external pure returns (uint256); // ======== JSON Parsing and Manipulation ======== diff --git a/crates/cheatcodes/src/string.rs b/crates/cheatcodes/src/string.rs index b7992b945..c98560a72 100644 --- a/crates/cheatcodes/src/string.rs +++ b/crates/cheatcodes/src/string.rs @@ -2,6 +2,7 @@ use crate::{Cheatcode, Cheatcodes, Result, Vm::*}; use alloy_dyn_abi::{DynSolType, DynSolValue}; +use alloy_primitives::U256; use alloy_sol_types::SolValue; // address @@ -135,6 +136,14 @@ impl Cheatcode for splitCall { } } +// indexOf +impl Cheatcode for indexOfCall { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { input, key } = self; + Ok(input.find(key).map(U256::from).unwrap_or(U256::MAX).abi_encode()) + } +} + pub(super) fn parse(s: &str, ty: &DynSolType) -> Result { parse_value(s, ty).map(|v| v.abi_encode()) } diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 2116acf2b..aff05c278 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -237,6 +237,7 @@ interface Vm { function getNonce(address account) external view returns (uint64 nonce); function getNonce(Wallet calldata wallet) external returns (uint64 nonce); function getRecordedLogs() external returns (Log[] memory logs); + function indexOf(string memory input, string memory key) external pure returns (uint256); function isDir(string calldata path) external returns (bool result); function isFile(string calldata path) external returns (bool result); function isPersistent(address account) external view returns (bool persistent); diff --git a/testdata/default/cheats/StringUtils.t.sol b/testdata/default/cheats/StringUtils.t.sol index 471d628be..136164a41 100644 --- a/testdata/default/cheats/StringUtils.t.sol +++ b/testdata/default/cheats/StringUtils.t.sol @@ -39,4 +39,16 @@ contract StringManipulationTest is DSTest { assertEq("World", splitResult[1]); assertEq("Reth", splitResult[2]); } + + function testIndexOf() public { + string memory input = "Hello, World!"; + string memory key1 = "Hello,"; + string memory key2 = "World!"; + string memory key3 = ""; + string memory key4 = "foundry"; + assertEq(vm.indexOf(input, key1), 0); + assertEq(vm.indexOf(input, key2), 7); + assertEq(vm.indexOf(input, key3), 0); + assertEq(vm.indexOf(input, key4), type(uint256).max); + } } From b994a65719a36a4a5a775ee8a7f4b580f888babd Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 5 Apr 2024 15:07:35 +0200 Subject: [PATCH 134/430] chore: reduce logs in tests (#7566) --- crates/chisel/src/executor.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index b5b440959..e0774a2b6 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -1782,9 +1782,6 @@ mod tests { } fn init_tracing() { - if std::env::var_os("RUST_LOG").is_none() { - std::env::set_var("RUST_LOG", "debug"); - } let _ = tracing_subscriber::FmtSubscriber::builder() .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) .try_init(); From dfab23e52e09ed6495d8c9eb861fe19a22b230a5 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Fri, 5 Apr 2024 16:30:25 +0300 Subject: [PATCH 135/430] fix(script): decode custom error in script fail message (#7563) --- crates/forge/tests/cli/script.rs | 34 ++++++++++++++++++++++++++++++++ crates/script/src/execute.rs | 6 +++--- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index a2d4dc4fb..8af676efd 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -1197,3 +1197,37 @@ contract Script { cmd.arg("script").args([&script.to_string_lossy(), "--sig", "run"]); assert!(cmd.stderr_lossy().contains("Multiple functions with the same name")); }); + +forgetest_async!(can_decode_custom_errors, |prj, cmd| { + cmd.args(["init", "--force"]).arg(prj.root()); + cmd.assert_non_empty_stdout(); + cmd.forge_fuse(); + + let script = prj + .add_script( + "CustomErrorScript.s.sol", + r#" +import { Script } from "forge-std/Script.sol"; + +contract ContractWithCustomError { + error CustomError(); + + constructor() { + revert CustomError(); + } +} + +contract CustomErrorScript is Script { + ContractWithCustomError test; + + function run() public { + test = new ContractWithCustomError(); + } +} +"#, + ) + .unwrap(); + + cmd.arg("script").arg(script).args(["--tc", "CustomErrorScript"]); + assert!(cmd.stderr_lossy().contains("script failed: CustomError()")); +}); diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index d89461c9e..7a2546ec8 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -23,7 +23,7 @@ use foundry_compilers::artifacts::ContractBytecodeSome; use foundry_config::{Config, NamedChain}; use foundry_debugger::Debugger; use foundry_evm::{ - decode::{decode_console_logs, RevertDecoder}, + decode::decode_console_logs, inspectors::cheatcodes::{BroadcastableTransaction, BroadcastableTransactions}, traces::{ identifier::{SignaturesIdentifier, TraceIdentifiers}, @@ -423,7 +423,7 @@ impl PreSimulationState { if !self.execution_result.success { return Err(eyre::eyre!( "script failed: {}", - RevertDecoder::new().decode(&self.execution_result.returned[..], None) + &self.execution_artifacts.decoder.revert_decoder.decode(&result.returned[..], None) )); } @@ -504,7 +504,7 @@ impl PreSimulationState { if !result.success { return Err(eyre::eyre!( "script failed: {}", - RevertDecoder::new().decode(&result.returned[..], None) + &self.execution_artifacts.decoder.revert_decoder.decode(&result.returned[..], None) )); } From c2162e2d69bcdc8c06522816e5a4064c49026d14 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sat, 6 Apr 2024 02:17:32 +0400 Subject: [PATCH 136/430] fix: always compile sources when running tests (#7572) --- crates/forge/bin/cmd/test/mod.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index b4c26e7ee..104fa4c48 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -20,7 +20,7 @@ use foundry_common::{ evm::EvmArgs, shell, }; -use foundry_compilers::artifacts::output_selection::OutputSelection; +use foundry_compilers::{artifacts::output_selection::OutputSelection, utils::source_files_iter}; use foundry_config::{ figment, figment::{ @@ -166,13 +166,13 @@ impl TestArgs { .collect::>(); // Filter sources by their abis and contract names. - let sources = abis + let mut test_sources = abis .iter() .filter(|(id, abi)| matches_contract(id, abi, filter)) .map(|(id, _)| id.source.clone()) .collect::>(); - if sources.is_empty() { + if test_sources.is_empty() { if filter.is_empty() { println!( "No tests found in project! \ @@ -201,7 +201,10 @@ impl TestArgs { eyre::bail!("No tests to run"); } - Ok(sources) + // Always recompile all sources to ensure that `getCode` cheatcode can use any artifact. + test_sources.extend(source_files_iter(project.paths.sources)); + + Ok(test_sources) } /// Executes all the tests in the project. From 5b0dc8c7fa99e4e4fd1fd6f1a4b6535fdc075555 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sat, 6 Apr 2024 02:21:48 +0400 Subject: [PATCH 137/430] fix: `--match-path` is broken (#7579) * fix: --match-path is broken * rm docs --- crates/forge/bin/cmd/test/filter.rs | 6 ++++-- crates/forge/tests/cli/test_cmd.rs | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/crates/forge/bin/cmd/test/filter.rs b/crates/forge/bin/cmd/test/filter.rs index 65d3d0ed5..eb8baea46 100644 --- a/crates/forge/bin/cmd/test/filter.rs +++ b/crates/forge/bin/cmd/test/filter.rs @@ -196,7 +196,8 @@ impl FileFilter for ProjectPathsAwareFilter { /// /// If no file regex is set this returns true if the file ends with `.t.sol`, see /// [FoundryPathExr::is_sol_test()] - fn is_match(&self, file: &Path) -> bool { + fn is_match(&self, mut file: &Path) -> bool { + file = file.strip_prefix(&self.paths.root).unwrap_or(file); self.args_filter.is_match(file) } } @@ -210,8 +211,9 @@ impl TestFilter for ProjectPathsAwareFilter { self.args_filter.matches_contract(contract_name) } - fn matches_path(&self, path: &Path) -> bool { + fn matches_path(&self, mut path: &Path) -> bool { // we don't want to test files that belong to a library + path = path.strip_prefix(&self.paths.root).unwrap_or(path); self.args_filter.matches_path(path) && !self.paths.has_library_ancestor(path) } } diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 5aad1848a..5872cc72e 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -526,3 +526,18 @@ contract GasLimitTest is Test { cmd.args(["test", "-vvvv", "--isolate", "--disable-block-gas-limit"]).assert_success(); }); + +forgetest!(test_match_path, |prj, cmd| { + prj.add_source( + "dummy", + r" +contract Dummy { + function testDummy() public {} +} +", + ) + .unwrap(); + + cmd.args(["test", "--match-path", "src/dummy.sol"]); + cmd.assert_success() +}); From 0c961f742a0a567edf4618b9e6f12d286bc3a51c Mon Sep 17 00:00:00 2001 From: Hoa Lee - Icetea <95201916+hoaleee@users.noreply.github.com> Date: Sat, 6 Apr 2024 22:18:33 +0700 Subject: [PATCH 138/430] Make batch size as a option (#7540) * batch size as a option * Update crates/script/src/broadcast.rs Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * Update crates/script/src/broadcast.rs Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * Update crates/script/src/lib.rs Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * Update crates/script/src/lib.rs Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * Update crates/script/src/lib.rs Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- crates/script/src/broadcast.rs | 3 ++- crates/script/src/lib.rs | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index 224bcc44c..04329891d 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -321,7 +321,8 @@ impl BundledState { // We send transactions and wait for receipts in batches of 100, since some networks // cannot handle more than that. - let batch_size = if sequential_broadcast { 1 } else { 100 }; + let valid_batch_size = self.args.batch_size.min(100); + let batch_size = if sequential_broadcast { 1 } else { valid_batch_size }; let mut index = already_broadcasted; for (batch_number, batch) in diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index fc8678b41..1f002d4bb 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -107,6 +107,10 @@ pub struct ScriptArgs { #[arg(long)] pub broadcast: bool, + /// Batch size of transactions. + #[arg(long, default_value = "100")] + pub batch_size: usize, + /// Skips on-chain simulation. #[arg(long)] pub skip_simulation: bool, From 72bc4f4e616c6e2b79ee3582301fbb1d38660857 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sun, 7 Apr 2024 10:48:16 +0200 Subject: [PATCH 139/430] chore: improve script tx batch size logic (#7583) --- crates/script/src/broadcast.rs | 22 +++++++++------------- crates/script/src/lib.rs | 2 ++ 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index 04329891d..983c02374 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -319,15 +319,11 @@ impl BundledState { let pb = init_progress!(transactions, "txes"); - // We send transactions and wait for receipts in batches of 100, since some networks - // cannot handle more than that. - let valid_batch_size = self.args.batch_size.min(100); - let batch_size = if sequential_broadcast { 1 } else { valid_batch_size }; + // We send transactions and wait for receipts in batches. + let batch_size = if sequential_broadcast { 1 } else { self.args.batch_size }; let mut index = already_broadcasted; - for (batch_number, batch) in - transactions.chunks(batch_size).map(|f| f.to_vec()).enumerate() - { + for (batch_number, batch) in transactions.chunks(batch_size).enumerate() { let mut pending_transactions = vec![]; shell::println(format!( @@ -335,17 +331,17 @@ impl BundledState { batch_number * batch_size, batch_number * batch_size + std::cmp::min(batch_size, batch.len()) - 1 ))?; - for (tx, kind, is_fixed_gas_limit) in batch.into_iter() { - let tx_hash = send_transaction( + for (tx, kind, is_fixed_gas_limit) in batch { + let fut = send_transaction( provider.clone(), - tx, - kind, + tx.clone(), + kind.clone(), sequential_broadcast, - is_fixed_gas_limit, + *is_fixed_gas_limit, estimate_via_rpc, self.args.gas_estimate_multiplier, ); - pending_transactions.push(tx_hash); + pending_transactions.push(fut); } if !pending_transactions.is_empty() { diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 1f002d4bb..5b27a5ff4 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -108,6 +108,8 @@ pub struct ScriptArgs { pub broadcast: bool, /// Batch size of transactions. + /// + /// This is ignored and set to 1 if batching is not available or `--slow` is enabled. #[arg(long, default_value = "100")] pub batch_size: usize, From 61f046d528966d5fa97157c11b84795bb611a6e4 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 8 Apr 2024 12:54:07 +0400 Subject: [PATCH 140/430] fix: use `alloy-trie` for `eth_getProof` (#7546) * use alloy-trie for eth_getProof * fmt * collect proofs via single pass * fixes and test * tests * add files * ordered_trie_root * clippy * move to workspace --- Cargo.lock | 189 +++-------- Cargo.toml | 1 + crates/anvil/Cargo.toml | 4 +- crates/anvil/core/Cargo.toml | 10 +- crates/anvil/core/src/eth/trie.rs | 48 ++- crates/anvil/src/config.rs | 10 + crates/anvil/src/eth/backend/db.rs | 49 +-- crates/anvil/src/eth/backend/genesis.rs | 8 +- crates/anvil/src/eth/backend/mem/fork_db.rs | 6 +- .../anvil/src/eth/backend/mem/in_memory_db.rs | 25 +- crates/anvil/src/eth/backend/mem/mod.rs | 150 ++++----- crates/anvil/src/eth/backend/mem/state.rs | 90 +++--- crates/anvil/src/eth/backend/mem/storage.rs | 2 +- crates/anvil/test-data/storage_sample.json | 33 ++ crates/anvil/tests/it/proof.rs | 134 ++++++++ crates/anvil/tests/it/proof/eip1186.rs | 297 ------------------ crates/anvil/tests/it/proof/mod.rs | 76 ----- 17 files changed, 367 insertions(+), 765 deletions(-) create mode 100644 crates/anvil/test-data/storage_sample.json create mode 100644 crates/anvil/tests/it/proof.rs delete mode 100644 crates/anvil/tests/it/proof/eip1186.rs delete mode 100644 crates/anvil/tests/it/proof/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 87931fb8f..6e48a7bc3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,17 +38,6 @@ dependencies = [ "cpufeatures", ] -[[package]] -name = "ahash" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" -dependencies = [ - "getrandom 0.2.12", - "once_cell", - "version_check", -] - [[package]] name = "ahash" version = "0.8.11" @@ -438,6 +427,22 @@ dependencies = [ "ws_stream_wasm", ] +[[package]] +name = "alloy-trie" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b9e1498416f7e7f09af8061970e14936846b6271e153aa5ba539a22a7eb414d" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "derive_more", + "hashbrown", + "nybbles", + "serde", + "smallvec", + "tracing", +] + [[package]] name = "ammonia" version = "3.3.0" @@ -538,6 +543,7 @@ dependencies = [ "alloy-signer", "alloy-sol-types", "alloy-transport", + "alloy-trie", "anvil-core", "anvil-rpc", "anvil-server", @@ -563,11 +569,9 @@ dependencies = [ "foundry-config", "foundry-evm", "futures", - "hash-db", "hyper 0.14.28", "itertools 0.12.1", "k256", - "memory-db", "parking_lot", "pretty_assertions", "rand 0.8.5", @@ -581,7 +585,6 @@ dependencies = [ "tower", "tracing", "tracing-subscriber", - "trie-db", "vergen", "yansi 0.5.1", ] @@ -598,20 +601,16 @@ dependencies = [ "alloy-rlp", "alloy-rpc-trace-types", "alloy-rpc-types", + "alloy-trie", "anvil-core", "bytes", "c-kzg", "foundry-common", "foundry-evm", - "hash-db", - "hash256-std-hasher", - "keccak-hasher", "rand 0.8.5", - "reference-trie", "revm", "serde", "serde_json", - "triehash", ] [[package]] @@ -1988,7 +1987,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown 0.14.3", + "hashbrown", "lock_api", "once_cell", "parking_lot_core", @@ -4104,37 +4103,13 @@ dependencies = [ "thiserror", ] -[[package]] -name = "hash-db" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" - -[[package]] -name = "hash256-std-hasher" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" -dependencies = [ - "crunchy", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.8", -] - [[package]] name = "hashbrown" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ - "ahash 0.8.11", + "ahash", "allocator-api2", "serde", ] @@ -4561,7 +4536,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown", ] [[package]] @@ -4780,17 +4755,6 @@ dependencies = [ "sha3-asm", ] -[[package]] -name = "keccak-hasher" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711adba9940a039f4374fc5724c0a5eaca84a2d558cce62256bfe26f0dbef05e" -dependencies = [ - "hash-db", - "hash256-std-hasher", - "tiny-keccak", -] - [[package]] name = "kqueue" version = "1.0.8" @@ -4912,7 +4876,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" dependencies = [ - "hashbrown 0.14.3", + "hashbrown", ] [[package]] @@ -5038,17 +5002,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "memory-db" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6566c70c1016f525ced45d7b7f97730a2bafb037c788211d0c186ef5b2189f0a" -dependencies = [ - "hash-db", - "hashbrown 0.12.3", - "parity-util-mem", -] - [[package]] name = "miette" version = "5.10.0" @@ -5354,6 +5307,19 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" +[[package]] +name = "nybbles" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95f06be0417d97f81fe4e5c86d7d01b392655a9cac9c19a848aa033e18937b23" +dependencies = [ + "alloy-rlp", + "const-hex", + "proptest", + "serde", + "smallvec", +] + [[package]] name = "object" version = "0.32.2" @@ -5531,31 +5497,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "parity-util-mem" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c32561d248d352148124f036cac253a644685a21dc9fea383eb4907d7bd35a8f" -dependencies = [ - "cfg-if", - "hashbrown 0.12.3", - "impl-trait-for-tuples", - "parity-util-mem-derive", - "parking_lot", - "winapi", -] - -[[package]] -name = "parity-util-mem-derive" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" -dependencies = [ - "proc-macro2", - "syn 1.0.109", - "synstructure", -] - [[package]] name = "parking" version = "2.2.0" @@ -6309,20 +6250,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "reference-trie" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f63dfce83d1e0e80cf2dc5222df5c2bc30992b30c44d1e66e7c9793d3a418e4" -dependencies = [ - "hash-db", - "hash256-std-hasher", - "keccak-hasher", - "parity-scale-codec", - "trie-db", - "trie-root", -] - [[package]] name = "regex" version = "1.10.4" @@ -6526,7 +6453,7 @@ dependencies = [ "cfg-if", "dyn-clone", "enumn", - "hashbrown 0.14.3", + "hashbrown", "hex", "serde", ] @@ -7691,18 +7618,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "unicode-xid", -] - [[package]] name = "system-configuration" version = "0.5.1" @@ -8267,38 +8182,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "trie-db" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32d034c0d3db64b43c31de38e945f15b40cd4ca6d2dcfc26d4798ce8de4ab83" -dependencies = [ - "hash-db", - "hashbrown 0.12.3", - "log", - "rustc-hex", - "smallvec", -] - -[[package]] -name = "trie-root" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a36c5ca3911ed3c9a5416ee6c679042064b93fc637ded67e25f92e68d783891" -dependencies = [ - "hash-db", -] - -[[package]] -name = "triehash" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1631b201eb031b563d2e85ca18ec8092508e262a3196ce9bd10a67ec87b9f5c" -dependencies = [ - "hash-db", - "rlp", -] - [[package]] name = "try-lock" version = "0.2.5" diff --git a/Cargo.toml b/Cargo.toml index a7804cc45..661bcb128 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -184,6 +184,7 @@ alloy-json-abi = "0.6.3" alloy-sol-types = "0.6.3" syn-solidity = "0.6.3" alloy-chains = "0.1" +alloy-trie = "0.3" alloy-rlp = "0.3.3" solang-parser = "=0.3.3" diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index 7a554a022..4ccbfaddd 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -32,9 +32,6 @@ foundry-evm.workspace = true bytes = "1.4.0" k256.workspace = true ethers = { workspace = true, features = ["rustls", "ws", "ipc", "optimism"] } -trie-db = "0.23" -hash-db = "0.15" -memory-db = "0.29" alloy-primitives = { workspace = true, features = ["serde"] } alloy-consensus = { workspace = true, features = ["k256", "kzg"] } alloy-network.workspace = true @@ -48,6 +45,7 @@ alloy-providers.workspace = true alloy-transport.workspace = true alloy-chains.workspace = true alloy-genesis.workspace = true +alloy-trie.workspace = true # axum related axum.workspace = true diff --git a/crates/anvil/core/Cargo.toml b/crates/anvil/core/Cargo.toml index 6b6041ce5..999c60689 100644 --- a/crates/anvil/core/Cargo.toml +++ b/crates/anvil/core/Cargo.toml @@ -20,21 +20,15 @@ alloy-rpc-trace-types.workspace = true alloy-rlp.workspace = true alloy-eips.workspace = true alloy-network = { workspace = true, features = ["k256"] } -alloy-consensus = { workspace = true, features = ["k256", "kzg"]} +alloy-consensus = { workspace = true, features = ["k256", "kzg"] } alloy-dyn-abi = { workspace = true, features = ["std", "eip712"] } +alloy-trie.workspace = true serde = { workspace = true, optional = true } serde_json.workspace = true bytes = "1.4" c-kzg = { version = "0.4.2", features = ["serde"] } -# trie -hash-db = { version = "0.15", default-features = false } -hash256-std-hasher = { version = "0.15", default-features = false } -triehash = { version = "0.8", default-features = false } -reference-trie = "0.25" -keccak-hasher = "0.15" - # misc rand = "0.8" diff --git a/crates/anvil/core/src/eth/trie.rs b/crates/anvil/core/src/eth/trie.rs index 5d144a5db..bcc86ac8b 100644 --- a/crates/anvil/core/src/eth/trie.rs +++ b/crates/anvil/core/src/eth/trie.rs @@ -1,36 +1,12 @@ //! Utility functions for Ethereum adapted from https://github.dev/rust-blockchain/ethereum/blob/755dffaa4903fbec1269f50cde9863cf86269a14/src/util.rs -use alloy_primitives::B256; +use std::collections::BTreeMap; -pub use keccak_hasher::KeccakHasher; - -// reexport some trie types -pub use reference_trie::*; +use alloy_primitives::{fixed_bytes, B256}; +use alloy_trie::{HashBuilder, Nibbles}; /// The KECCAK of the RLP encoding of empty data. -pub const KECCAK_NULL_RLP: B256 = B256::new([ - 0x56, 0xe8, 0x1f, 0x17, 0x1b, 0xcc, 0x55, 0xa6, 0xff, 0x83, 0x45, 0xe6, 0x92, 0xc0, 0xf8, 0x6e, - 0x5b, 0x48, 0xe0, 0x1b, 0x99, 0x6c, 0xad, 0xc0, 0x01, 0x62, 0x2f, 0xb5, 0xe3, 0x63, 0xb4, 0x21, -]); - -/// Generates a trie root hash for a vector of key-value tuples -pub fn trie_root(input: I) -> B256 -where - I: IntoIterator, - K: AsRef<[u8]> + Ord, - V: AsRef<[u8]>, -{ - B256::from(triehash::trie_root::(input)) -} - -/// Generates a key-hashed (secure) trie root hash for a vector of key-value tuples. -pub fn sec_trie_root(input: I) -> B256 -where - I: IntoIterator, - K: AsRef<[u8]>, - V: AsRef<[u8]>, -{ - B256::from(triehash::sec_trie_root::(input)) -} +pub const KECCAK_NULL_RLP: B256 = + fixed_bytes!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"); /// Generates a trie root hash for a vector of values pub fn ordered_trie_root(input: I) -> B256 @@ -38,5 +14,17 @@ where I: IntoIterator, V: AsRef<[u8]>, { - B256::from(triehash::ordered_trie_root::(input)) + let mut builder = HashBuilder::default(); + + let input = input + .into_iter() + .enumerate() + .map(|(i, v)| (alloy_rlp::encode(i), v)) + .collect::>(); + + for (key, value) in input { + builder.add_leaf(Nibbles::unpack(key), value.as_ref()); + } + + builder.root() } diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 96e7896eb..a1e736619 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -358,6 +358,16 @@ impl NodeConfig { pub fn test() -> Self { Self { enable_tracing: true, silent: true, port: 0, ..Default::default() } } + + /// Returns a new config which does not initialize any accounts on node startup. + pub fn empty_state() -> Self { + Self { + genesis_accounts: vec![], + signer_accounts: vec![], + disable_default_create2_deployer: true, + ..Default::default() + } + } } impl Default for NodeConfig { diff --git a/crates/anvil/src/eth/backend/db.rs b/crates/anvil/src/eth/backend/db.rs index 31e60ddbe..6149c0108 100644 --- a/crates/anvil/src/eth/backend/db.rs +++ b/crates/anvil/src/eth/backend/db.rs @@ -1,9 +1,8 @@ //! Helper types for working with [revm](foundry_evm::revm) -use crate::{mem::state::trie_hash_db, revm::primitives::AccountInfo}; +use crate::revm::primitives::AccountInfo; use alloy_primitives::{keccak256, Address, Bytes, B256, U256, U64}; use alloy_rpc_types::BlockId; -use anvil_core::eth::trie::KeccakHasher; use foundry_common::errors::FsPathError; use foundry_evm::{ backend::{DatabaseError, DatabaseResult, MemDb, RevertSnapshotAction, StateSnapshot}, @@ -14,22 +13,13 @@ use foundry_evm::{ Database, DatabaseCommit, }, }; -use hash_db::HashDB; use serde::{Deserialize, Serialize}; use std::{collections::BTreeMap, fmt, path::Path}; -/// Type alias for the `HashDB` representation of the Database -pub type AsHashDB = Box>>; - -/// Helper trait get access to the data in `HashDb` form +/// Helper trait get access to the full state data of the database #[auto_impl::auto_impl(Box)] -pub trait MaybeHashDatabase: DatabaseRef { - /// Return the DB as read-only hashdb and the root key - fn maybe_as_hash_db(&self) -> Option<(AsHashDB, B256)> { - None - } - /// Return the storage DB as read-only hashdb and the storage root of the account - fn maybe_account_db(&self, _addr: Address) -> Option<(AsHashDB, B256)> { +pub trait MaybeFullDatabase: DatabaseRef { + fn maybe_as_full_db(&self) -> Option<&HashMap> { None } @@ -43,15 +33,12 @@ pub trait MaybeHashDatabase: DatabaseRef { fn init_from_snapshot(&mut self, snapshot: StateSnapshot); } -impl<'a, T: 'a + MaybeHashDatabase + ?Sized> MaybeHashDatabase for &'a T +impl<'a, T: 'a + MaybeFullDatabase + ?Sized> MaybeFullDatabase for &'a T where &'a T: DatabaseRef, { - fn maybe_as_hash_db(&self) -> Option<(AsHashDB, B256)> { - T::maybe_as_hash_db(self) - } - fn maybe_account_db(&self, addr: Address) -> Option<(AsHashDB, B256)> { - T::maybe_account_db(self, addr) + fn maybe_as_full_db(&self) -> Option<&HashMap> { + T::maybe_as_full_db(self) } fn clear_into_snapshot(&mut self) -> StateSnapshot { @@ -79,7 +66,7 @@ pub trait Db: DatabaseRef + Database + DatabaseCommit - + MaybeHashDatabase + + MaybeFullDatabase + MaybeForkedDatabase + fmt::Debug + Send @@ -220,9 +207,9 @@ impl + Send + Sync + Clone + fmt::Debug> D } } -impl> MaybeHashDatabase for CacheDB { - fn maybe_as_hash_db(&self) -> Option<(AsHashDB, B256)> { - Some(trie_hash_db(&self.accounts)) +impl> MaybeFullDatabase for CacheDB { + fn maybe_as_full_db(&self) -> Option<&HashMap> { + Some(&self.accounts) } fn clear_into_snapshot(&mut self) -> StateSnapshot { @@ -279,12 +266,12 @@ impl> MaybeForkedDatabase for CacheDB { } /// Represents a state at certain point -pub struct StateDb(pub(crate) Box); +pub struct StateDb(pub(crate) Box); // === impl StateDB === impl StateDb { - pub fn new(db: impl MaybeHashDatabase + Send + Sync + 'static) -> Self { + pub fn new(db: impl MaybeFullDatabase + Send + Sync + 'static) -> Self { Self(Box::new(db)) } } @@ -308,13 +295,9 @@ impl DatabaseRef for StateDb { } } -impl MaybeHashDatabase for StateDb { - fn maybe_as_hash_db(&self) -> Option<(AsHashDB, B256)> { - self.0.maybe_as_hash_db() - } - - fn maybe_account_db(&self, addr: Address) -> Option<(AsHashDB, B256)> { - self.0.maybe_account_db(addr) +impl MaybeFullDatabase for StateDb { + fn maybe_as_full_db(&self) -> Option<&HashMap> { + self.0.maybe_as_full_db() } fn clear_into_snapshot(&mut self) -> StateSnapshot { diff --git a/crates/anvil/src/eth/backend/genesis.rs b/crates/anvil/src/eth/backend/genesis.rs index 14197b63d..bbfbda55e 100644 --- a/crates/anvil/src/eth/backend/genesis.rs +++ b/crates/anvil/src/eth/backend/genesis.rs @@ -1,6 +1,6 @@ //! Genesis settings -use crate::eth::backend::db::{Db, MaybeHashDatabase}; +use crate::eth::backend::db::{Db, MaybeFullDatabase}; use alloy_genesis::{Genesis, GenesisAccount}; use alloy_primitives::{Address, B256, U256}; use foundry_evm::{ @@ -84,7 +84,7 @@ impl GenesisConfig { /// [AccountInfo] pub(crate) fn state_db_at_genesis<'a>( &self, - db: Box, + db: Box, ) -> AtGenesisStateDb<'a> { AtGenesisStateDb { genesis: self.genesis_init.clone(), @@ -103,7 +103,7 @@ impl GenesisConfig { pub(crate) struct AtGenesisStateDb<'a> { genesis: Option, accounts: HashMap, - db: Box, + db: Box, } impl<'a> DatabaseRef for AtGenesisStateDb<'a> { @@ -138,7 +138,7 @@ impl<'a> DatabaseRef for AtGenesisStateDb<'a> { } } -impl<'a> MaybeHashDatabase for AtGenesisStateDb<'a> { +impl<'a> MaybeFullDatabase for AtGenesisStateDb<'a> { fn clear_into_snapshot(&mut self) -> StateSnapshot { self.db.clear_into_snapshot() } diff --git a/crates/anvil/src/eth/backend/mem/fork_db.rs b/crates/anvil/src/eth/backend/mem/fork_db.rs index 7f9262fd6..d99aeb5ed 100644 --- a/crates/anvil/src/eth/backend/mem/fork_db.rs +++ b/crates/anvil/src/eth/backend/mem/fork_db.rs @@ -1,6 +1,6 @@ use crate::{ eth::backend::db::{ - Db, MaybeForkedDatabase, MaybeHashDatabase, SerializableAccountRecord, SerializableState, + Db, MaybeForkedDatabase, MaybeFullDatabase, SerializableAccountRecord, SerializableState, StateDb, }, revm::primitives::AccountInfo, @@ -81,7 +81,7 @@ impl Db for ForkedDatabase { } } -impl MaybeHashDatabase for ForkedDatabase { +impl MaybeFullDatabase for ForkedDatabase { fn clear_into_snapshot(&mut self) -> StateSnapshot { let db = self.inner().db(); let accounts = std::mem::take(&mut *db.accounts.write()); @@ -104,7 +104,7 @@ impl MaybeHashDatabase for ForkedDatabase { } } -impl MaybeHashDatabase for ForkDbSnapshot { +impl MaybeFullDatabase for ForkDbSnapshot { fn clear_into_snapshot(&mut self) -> StateSnapshot { std::mem::take(&mut self.snapshot) } diff --git a/crates/anvil/src/eth/backend/mem/in_memory_db.rs b/crates/anvil/src/eth/backend/mem/in_memory_db.rs index c6ebc8a91..1c96a0eb5 100644 --- a/crates/anvil/src/eth/backend/mem/in_memory_db.rs +++ b/crates/anvil/src/eth/backend/mem/in_memory_db.rs @@ -2,17 +2,18 @@ use crate::{ eth::backend::db::{ - AsHashDB, Db, MaybeForkedDatabase, MaybeHashDatabase, SerializableAccountRecord, - SerializableState, StateDb, + Db, MaybeForkedDatabase, MaybeFullDatabase, SerializableAccountRecord, SerializableState, + StateDb, }, - mem::state::{state_merkle_trie_root, storage_trie_db, trie_hash_db}, - revm::primitives::AccountInfo, + mem::state::state_root, + revm::{db::DbAccount, primitives::AccountInfo}, }; use alloy_primitives::{Address, B256, U256, U64}; use alloy_rpc_types::BlockId; use foundry_evm::{ backend::{DatabaseResult, StateSnapshot}, fork::BlockchainDb, + hashbrown::HashMap, }; // reexport for convenience @@ -90,7 +91,7 @@ impl Db for MemDb { } fn maybe_state_root(&self) -> Option { - Some(state_merkle_trie_root(&self.inner.accounts)) + Some(state_root(&self.inner.accounts)) } fn current_state(&self) -> StateDb { @@ -98,17 +99,9 @@ impl Db for MemDb { } } -impl MaybeHashDatabase for MemDb { - fn maybe_as_hash_db(&self) -> Option<(AsHashDB, B256)> { - Some(trie_hash_db(&self.inner.accounts)) - } - - fn maybe_account_db(&self, addr: Address) -> Option<(AsHashDB, B256)> { - if let Some(acc) = self.inner.accounts.get(&addr) { - Some(storage_trie_db(&acc.storage)) - } else { - Some(storage_trie_db(&Default::default())) - } +impl MaybeFullDatabase for MemDb { + fn maybe_as_full_db(&self) -> Option<&HashMap> { + Some(&self.inner.accounts) } fn clear_into_snapshot(&mut self) -> StateSnapshot { diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index a60ba5640..da49d8d35 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -1,14 +1,18 @@ //! In memory blockchain backend +use self::state::trie_storage; use crate::{ config::PruneStateHistoryConfig, eth::{ backend::{ cheats::CheatsManager, - db::{AsHashDB, Db, MaybeHashDatabase, SerializableState}, + db::{Db, MaybeFullDatabase, SerializableState}, executor::{ExecutedTransactions, TransactionExecutor}, fork::ClientFork, genesis::GenesisConfig, - mem::storage::MinedTransactionReceipt, + mem::{ + state::{storage_root, trie_accounts}, + storage::MinedTransactionReceipt, + }, notifications::{NewBlockNotification, NewBlockNotifications}, time::{utc_from_secs, TimeManager}, validate::TransactionValidator, @@ -32,7 +36,6 @@ use crate::{ use alloy_consensus::{Header, Receipt, ReceiptWithBloom}; use alloy_network::Sealable; use alloy_primitives::{keccak256, Address, Bytes, TxHash, B256, B64, U128, U256, U64, U8}; -use alloy_rlp::Decodable; use alloy_rpc_trace_types::{ geth::{DefaultFrame, GethDebugTracingOptions, GethDefaultTracingOptions, GethTrace}, parity::LocalizedTransactionTrace, @@ -40,18 +43,17 @@ use alloy_rpc_trace_types::{ use alloy_rpc_types::{ request::TransactionRequest, state::StateOverride, AccessList, Block as AlloyBlock, BlockId, BlockNumberOrTag as BlockNumber, EIP1186AccountProofResponse as AccountProof, - EIP1186StorageProof as StorageProof, Filter, FilteredParams, Header as AlloyHeader, Log, - Transaction, TransactionReceipt, + EIP1186StorageProof as StorageProof, Filter, FilteredParams, Header as AlloyHeader, + JsonStorageKey, Log, Transaction, TransactionReceipt, }; +use alloy_trie::{HashBuilder, Nibbles}; use anvil_core::{ eth::{ block::{Block, BlockInfo}, - proof::BasicAccount, transaction::{ MaybeImpersonatedTransaction, PendingTransaction, TransactionInfo, TypedReceipt, TypedTransaction, }, - trie::RefTrieDB, utils::{alloy_to_revm_access_list, meets_eip155}, }, types::{Forking, Index}, @@ -76,19 +78,16 @@ use foundry_evm::{ utils::new_evm_with_inspector_ref, }; use futures::channel::mpsc::{unbounded, UnboundedSender}; -use hash_db::HashDB; use parking_lot::{Mutex, RwLock}; -use revm::primitives::ResultAndState; +use revm::primitives::{HashMap, ResultAndState}; use std::{ - collections::{BTreeMap, HashMap}, + collections::BTreeMap, io::{Read, Write}, - ops::Deref, sync::Arc, time::Duration, }; use storage::{Blockchain, MinedTransaction}; use tokio::sync::RwLock as AsyncRwLock; -use trie_db::{Recorder, Trie}; pub mod cache; pub mod fork_db; @@ -848,7 +847,7 @@ impl Backend { f: F, ) -> T where - F: FnOnce(Box, BlockInfo) -> T, + F: FnOnce(Box, BlockInfo) -> T, { let db = self.db.read().await; let env = self.next_env(); @@ -1631,7 +1630,7 @@ impl Backend { f: F, ) -> Result where - F: FnOnce(Box, BlockEnv) -> T, + F: FnOnce(Box, BlockEnv) -> T, { let block_number = match block_request { Some(BlockRequest::Pending(pool_transactions)) => { @@ -2175,74 +2174,41 @@ impl Backend { keys: Vec, block_request: Option, ) -> Result { - let account_key = B256::from(alloy_primitives::utils::keccak256(address)); let block_number = block_request.as_ref().map(|r| r.block_number()); self.with_database_at(block_request, |block_db, _| { trace!(target: "backend", "get proof for {:?} at {:?}", address, block_number); - let (db, root) = block_db.maybe_as_hash_db().ok_or(BlockchainError::DataUnavailable)?; + let db = block_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?; + let account = db.get(&address).cloned().unwrap_or_default(); - let data: &dyn HashDB<_, _> = db.deref(); - let mut recorder = Recorder::new(); - let trie = RefTrieDB::new(&data, &root.0) - .map_err(|err| BlockchainError::TrieError(err.to_string()))?; + let mut builder = HashBuilder::default() + .with_proof_retainer(vec![Nibbles::unpack(keccak256(address))]); - let maybe_account: Option = { - let acc_decoder = |mut bytes: &[u8]| { - BasicAccount::decode(&mut bytes).unwrap_or_else(|_| { - panic!("prove_account_at, could not query trie for account={:?}", &address) - }) - }; - let query = (&mut recorder, acc_decoder); - trie.get_with(account_key.as_slice(), query) - .map_err(|err| BlockchainError::TrieError(err.to_string()))? - }; - let account = maybe_account.unwrap_or_default(); - - let proof = recorder - .drain() - .into_iter() - .map(|r| r.data) - .map(|record| { - // proof is rlp encoded: - // - // - alloy_rlp::encode(record).to_vec().into() - }) - .collect::>(); - - let account_db = - block_db.maybe_account_db(address).ok_or(BlockchainError::DataUnavailable)?; + for (key, account) in trie_accounts(db) { + builder.add_leaf(key, &account); + } + + let _ = builder.root(); + + let proof = builder.take_proofs().values().cloned().collect::>(); + let storage_proofs = prove_storage(&account.storage, &keys); let account_proof = AccountProof { address, - balance: account.balance, - nonce: account.nonce.to::(), - code_hash: account.code_hash, - storage_hash: account.storage_root, + balance: account.info.balance, + nonce: U64::from(account.info.nonce), + code_hash: account.info.code_hash, + storage_hash: storage_root(&account.storage), account_proof: proof, storage_proof: keys .into_iter() - .map(|storage_key| { - // the key that should be proofed is the keccak256 of the storage key - let key = B256::from(keccak256(storage_key)); - prove_storage(&account, &account_db.0, key).map( - |(storage_proof, storage_value)| StorageProof { - key: alloy_rpc_types::JsonStorageKey(storage_key), - value: U256::from_be_bytes(storage_value.0), - proof: storage_proof - .into_iter() - .map(|proof| { - // proof is rlp encoded: - // - // - alloy_rlp::encode(proof).to_vec().into() - }) - .collect(), - }, - ) + .zip(storage_proofs) + .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 } }) - .collect::, _>>()?, + .collect(), }; Ok(account_proof) @@ -2464,25 +2430,29 @@ pub fn transaction_build( /// `storage_key` is the hash of the desired storage key, meaning /// this will only work correctly under a secure trie. /// `storage_key` == keccak(key) -pub fn prove_storage( - acc: &BasicAccount, - data: &AsHashDB, - storage_key: B256, -) -> Result<(Vec>, B256), BlockchainError> { - let data: &dyn HashDB<_, _> = data.deref(); - let mut recorder = Recorder::new(); - let trie = RefTrieDB::new(&data, &acc.storage_root.0) - .map_err(|err| BlockchainError::TrieError(err.to_string())) - .unwrap(); - - let item: U256 = { - let decode_value = - |mut bytes: &[u8]| U256::decode(&mut bytes).expect("decoding db value failed"); - let query = (&mut recorder, decode_value); - trie.get_with(storage_key.as_slice(), query) - .map_err(|err| BlockchainError::TrieError(err.to_string()))? - .unwrap_or(U256::ZERO) - }; - - Ok((recorder.drain().into_iter().map(|r| r.data).collect(), B256::from(item))) +pub fn prove_storage(storage: &HashMap, keys: &[B256]) -> Vec> { + let keys: Vec<_> = keys.iter().map(|key| Nibbles::unpack(keccak256(key))).collect(); + + let mut builder = HashBuilder::default().with_proof_retainer(keys.clone()); + + for (key, value) in trie_storage(storage) { + builder.add_leaf(key, &value); + } + + let _ = builder.root(); + + let mut proofs = Vec::new(); + let all_proof_nodes = builder.take_proofs(); + + for proof_key in keys { + // Iterate over all proof nodes and find the matching ones. + // The filtered results are guaranteed to be in order. + let matching_proof_nodes = all_proof_nodes + .iter() + .filter(|(path, _)| proof_key.starts_with(path)) + .map(|(_, node)| node.clone()); + proofs.push(matching_proof_nodes.collect()); + } + + proofs } diff --git a/crates/anvil/src/eth/backend/mem/state.rs b/crates/anvil/src/eth/backend/mem/state.rs index 29a774f18..fb8be9b99 100644 --- a/crates/anvil/src/eth/backend/mem/state.rs +++ b/crates/anvil/src/eth/backend/mem/state.rs @@ -1,10 +1,10 @@ //! Support for generating the state root for memdb storage -use crate::eth::{backend::db::AsHashDB, error::BlockchainError}; -use alloy_primitives::{Address, Bytes, B256, U256}; +use crate::eth::error::BlockchainError; +use alloy_primitives::{keccak256, Address, B256, U256}; use alloy_rlp::Encodable; use alloy_rpc_types::state::StateOverride; -use anvil_core::eth::trie::RefSecTrieDBMut; +use alloy_trie::{HashBuilder, Nibbles}; use foundry_evm::{ backend::DatabaseError, revm::{ @@ -12,74 +12,62 @@ use foundry_evm::{ primitives::{AccountInfo, Bytecode, HashMap}, }, }; -use memory_db::HashKey; -use trie_db::TrieMut; -/// Returns storage trie of an account as `HashDB` -pub fn storage_trie_db(storage: &HashMap) -> (AsHashDB, B256) { - // Populate DB with full trie from entries. - let (db, root) = { - let mut db = , _>>::default(); - let mut root = Default::default(); - { - let mut trie = RefSecTrieDBMut::new(&mut db, &mut root); - for (k, v) in storage.iter().filter(|(_k, v)| *v != &U256::from(0)) { - let key = B256::from(*k); - let mut value: Vec = Vec::new(); - U256::encode(v, &mut value); - trie.insert(key.as_slice(), value.as_ref()).unwrap(); - } - } - (db, root) - }; +pub fn build_root(values: impl IntoIterator)>) -> B256 { + let mut builder = HashBuilder::default(); + for (key, value) in values { + builder.add_leaf(key, value.as_ref()); + } + builder.root() +} - (Box::new(db), B256::from(root)) +/// Builds state root from the given accounts +pub fn state_root(accounts: &HashMap) -> B256 { + build_root(trie_accounts(accounts)) } -/// Returns the account data as `HashDB` -pub fn trie_hash_db(accounts: &HashMap) -> (AsHashDB, B256) { - let accounts = trie_accounts(accounts); +/// Builds storage root from the given storage +pub fn storage_root(storage: &HashMap) -> B256 { + build_root(trie_storage(storage)) +} - // Populate DB with full trie from entries. - let (db, root) = { - let mut db = , _>>::default(); - let mut root = Default::default(); - { - let mut trie = RefSecTrieDBMut::new(&mut db, &mut root); - for (address, value) in accounts { - trie.insert(address.as_ref(), value.as_ref()).unwrap(); - } - } - (db, root) - }; +/// Builds iterator over stored key-value pairs ready for storage trie root calculation. +pub fn trie_storage(storage: &HashMap) -> Vec<(Nibbles, Vec)> { + let mut storage = storage + .iter() + .map(|(key, value)| { + let data = alloy_rlp::encode(value); + (Nibbles::unpack(keccak256(key.to_be_bytes::<32>())), data) + }) + .collect::>(); + storage.sort_by(|(key1, _), (key2, _)| key1.cmp(key2)); - (Box::new(db), B256::from(root)) + storage } -/// Returns all RLP-encoded Accounts -pub fn trie_accounts(accounts: &HashMap) -> Vec<(Address, Bytes)> { - accounts +/// Builds iterator over stored key-value pairs ready for account trie root calculation. +pub fn trie_accounts(accounts: &HashMap) -> Vec<(Nibbles, Vec)> { + let mut accounts = accounts .iter() .map(|(address, account)| { - let storage_root = trie_account_rlp(&account.info, &account.storage); - (*address, storage_root) + let data = trie_account_rlp(&account.info, &account.storage); + (Nibbles::unpack(keccak256(*address)), data) }) - .collect() -} + .collect::>(); + accounts.sort_by(|(key1, _), (key2, _)| key1.cmp(key2)); -pub fn state_merkle_trie_root(accounts: &HashMap) -> B256 { - trie_hash_db(accounts).1 + accounts } /// Returns the RLP for this account. -pub fn trie_account_rlp(info: &AccountInfo, storage: &HashMap) -> Bytes { +pub fn trie_account_rlp(info: &AccountInfo, storage: &HashMap) -> Vec { let mut out: Vec = Vec::new(); let list: [&dyn Encodable; 4] = - [&info.nonce, &info.balance, &storage_trie_db(storage).1, &info.code_hash]; + [&info.nonce, &info.balance, &storage_root(storage), &info.code_hash]; alloy_rlp::encode_list::<_, dyn Encodable>(&list, &mut out); - out.into() + out } /// Applies the given state overrides to the state, returning a new CacheDB state diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index 0e2ac2b63..8d72b30f1 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -1,7 +1,7 @@ //! In-memory blockchain storage use crate::eth::{ backend::{ - db::{MaybeHashDatabase, StateDb}, + db::{MaybeFullDatabase, StateDb}, mem::cache::DiskStateCache, }, pool::transactions::PoolTransaction, diff --git a/crates/anvil/test-data/storage_sample.json b/crates/anvil/test-data/storage_sample.json new file mode 100644 index 000000000..7e2daf48f --- /dev/null +++ b/crates/anvil/test-data/storage_sample.json @@ -0,0 +1,33 @@ +{ + "0x0000000000000000000000000000000000000000000000000000000000000022": "0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b", + "0x0000000000000000000000000000000000000000000000000000000000000023": "0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71", + "0x0000000000000000000000000000000000000000000000000000000000000024": "0xc78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c", + "0x0000000000000000000000000000000000000000000000000000000000000025": "0x536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c", + "0x0000000000000000000000000000000000000000000000000000000000000026": "0x9efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30", + "0x0000000000000000000000000000000000000000000000000000000000000027": "0xd88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa1", + "0x0000000000000000000000000000000000000000000000000000000000000028": "0x87eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c", + "0x0000000000000000000000000000000000000000000000000000000000000029": "0x26846476fd5fc54a5d43385167c95144f2643f533cc85bb9d16b782f8d7db193", + "0x000000000000000000000000000000000000000000000000000000000000002a": "0x506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1", + "0x000000000000000000000000000000000000000000000000000000000000002b": "0xffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b", + "0x000000000000000000000000000000000000000000000000000000000000002c": "0x6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220", + "0x000000000000000000000000000000000000000000000000000000000000002d": "0xb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5f", + "0x000000000000000000000000000000000000000000000000000000000000002e": "0xdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85e", + "0x000000000000000000000000000000000000000000000000000000000000002f": "0xb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784", + "0x0000000000000000000000000000000000000000000000000000000000000030": "0xd49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb", + "0x0000000000000000000000000000000000000000000000000000000000000031": "0x8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb", + "0x0000000000000000000000000000000000000000000000000000000000000032": "0x8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab", + "0x0000000000000000000000000000000000000000000000000000000000000033": "0x95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4", + "0x0000000000000000000000000000000000000000000000000000000000000034": "0xf893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17f", + "0x0000000000000000000000000000000000000000000000000000000000000035": "0xcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa", + "0x0000000000000000000000000000000000000000000000000000000000000036": "0x8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9c", + "0x0000000000000000000000000000000000000000000000000000000000000037": "0xfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167", + "0x0000000000000000000000000000000000000000000000000000000000000038": "0xe71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d7", + "0x0000000000000000000000000000000000000000000000000000000000000039": "0x31206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc0", + "0x000000000000000000000000000000000000000000000000000000000000003a": "0x21352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544", + "0x000000000000000000000000000000000000000000000000000000000000003b": "0x619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765", + "0x000000000000000000000000000000000000000000000000000000000000003c": "0x7cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4", + "0x000000000000000000000000000000000000000000000000000000000000003d": "0x848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe1", + "0x000000000000000000000000000000000000000000000000000000000000003e": "0x8869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636", + "0x000000000000000000000000000000000000000000000000000000000000003f": "0xb5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c", + "0x0000000000000000000000000000000000000000000000000000000000000040": "0x985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7" +} \ No newline at end of file diff --git a/crates/anvil/tests/it/proof.rs b/crates/anvil/tests/it/proof.rs new file mode 100644 index 000000000..d5a09e8ad --- /dev/null +++ b/crates/anvil/tests/it/proof.rs @@ -0,0 +1,134 @@ +//! tests for `eth_getProof` + +use std::{collections::BTreeMap, str::FromStr}; + +use alloy_primitives::{address, fixed_bytes, Address, Bytes, B256, U256}; +use anvil::{eth::EthApi, spawn, NodeConfig}; + +async fn verify_account_proof( + api: &EthApi, + address: Address, + proof: impl IntoIterator, +) { + let expected_proof = + proof.into_iter().map(Bytes::from_str).collect::, _>>().unwrap(); + let proof = api.get_proof(address, Vec::new(), None).await.unwrap(); + + assert_eq!(proof.account_proof, expected_proof); +} + +async fn verify_storage_proof( + api: &EthApi, + address: Address, + slot: B256, + proof: impl IntoIterator, +) { + let expected_proof = + proof.into_iter().map(Bytes::from_str).collect::, _>>().unwrap(); + let proof = api.get_proof(address, vec![slot], None).await.unwrap(); + + assert_eq!(proof.storage_proof[0].proof, expected_proof); +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_account_proof() { + let (api, _handle) = spawn(NodeConfig::empty_state()).await; + + api.anvil_set_balance( + address!("2031f89b3ea8014eb51a78c316e42af3e0d7695f"), + U256::from(45000000000000000000_u128), + ) + .await + .unwrap(); + api.anvil_set_balance(address!("33f0fc440b8477fcfbe9d0bf8649e7dea9baedb2"), U256::from(1)) + .await + .unwrap(); + api.anvil_set_balance( + address!("62b0dd4aab2b1a0a04e279e2b828791a10755528"), + U256::from(1100000000000000000_u128), + ) + .await + .unwrap(); + api.anvil_set_balance( + address!("1ed9b1dd266b607ee278726d324b855a093394a6"), + U256::from(120000000000000000_u128), + ) + .await + .unwrap(); + + verify_account_proof(&api, address!("2031f89b3ea8014eb51a78c316e42af3e0d7695f"), [ + "0xe48200a7a040f916999be583c572cc4dd369ec53b0a99f7de95f13880cf203d98f935ed1b3", + "0xf87180a04fb9bab4bb88c062f32452b7c94c8f64d07b5851d44a39f1e32ba4b1829fdbfb8080808080a0b61eeb2eb82808b73c4ad14140a2836689f4ab8445d69dd40554eaf1fce34bc080808080808080a0dea230ff2026e65de419288183a340125b04b8405cc61627b3b4137e2260a1e880", + "0xf8719f31355ec1c8f7e26bb3ccbcb0b75d870d15846c0b98e5cc452db46c37faea40b84ff84d80890270801d946c940000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" + ]).await; + + verify_account_proof(&api, address!("33f0fc440b8477fcfbe9d0bf8649e7dea9baedb2"), [ + "0xe48200a7a040f916999be583c572cc4dd369ec53b0a99f7de95f13880cf203d98f935ed1b3", + "0xf87180a04fb9bab4bb88c062f32452b7c94c8f64d07b5851d44a39f1e32ba4b1829fdbfb8080808080a0b61eeb2eb82808b73c4ad14140a2836689f4ab8445d69dd40554eaf1fce34bc080808080808080a0dea230ff2026e65de419288183a340125b04b8405cc61627b3b4137e2260a1e880", + "0xe48200d3a0ef957210bca5b9b402d614eb8408c88cfbf4913eb6ab83ca233c8b8f0e626b54", + "0xf851808080a02743a5addaf4cf9b8c0c073e1eaa555deaaf8c41cb2b41958e88624fa45c2d908080808080a0bfbf6937911dfb88113fecdaa6bde822e4e99dae62489fcf61a91cb2f36793d680808080808080", + "0xf8679e207781e762f3577784bab7491fcc43e291ce5a356b9bc517ac52eed3a37ab846f8448001a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + ]).await; + + verify_account_proof(&api, address!("62b0dd4aab2b1a0a04e279e2b828791a10755528"), [ + "0xe48200a7a040f916999be583c572cc4dd369ec53b0a99f7de95f13880cf203d98f935ed1b3", + "0xf87180a04fb9bab4bb88c062f32452b7c94c8f64d07b5851d44a39f1e32ba4b1829fdbfb8080808080a0b61eeb2eb82808b73c4ad14140a2836689f4ab8445d69dd40554eaf1fce34bc080808080808080a0dea230ff2026e65de419288183a340125b04b8405cc61627b3b4137e2260a1e880", + "0xf8709f3936599f93b769acf90c7178fd2ddcac1b5b4bc9949ee5a04b7e0823c2446eb84ef84c80880f43fc2c04ee0000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + ]).await; + + verify_account_proof(&api, address!("1ed9b1dd266b607ee278726d324b855a093394a6"), [ + "0xe48200a7a040f916999be583c572cc4dd369ec53b0a99f7de95f13880cf203d98f935ed1b3", + "0xf87180a04fb9bab4bb88c062f32452b7c94c8f64d07b5851d44a39f1e32ba4b1829fdbfb8080808080a0b61eeb2eb82808b73c4ad14140a2836689f4ab8445d69dd40554eaf1fce34bc080808080808080a0dea230ff2026e65de419288183a340125b04b8405cc61627b3b4137e2260a1e880", + "0xe48200d3a0ef957210bca5b9b402d614eb8408c88cfbf4913eb6ab83ca233c8b8f0e626b54", + "0xf851808080a02743a5addaf4cf9b8c0c073e1eaa555deaaf8c41cb2b41958e88624fa45c2d908080808080a0bfbf6937911dfb88113fecdaa6bde822e4e99dae62489fcf61a91cb2f36793d680808080808080", + "0xf86f9e207a32b8ab5eb4b043c65b1f00c93f517bc8883c5cd31baf8e8a279475e3b84ef84c808801aa535d3d0c0000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" + ]).await; +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_storage_proof() { + let target = address!("1ed9b1dd266b607ee278726d324b855a093394a6"); + + let (api, _handle) = spawn(NodeConfig::empty_state()).await; + let storage: BTreeMap = + serde_json::from_str(include_str!("../../test-data/storage_sample.json")).unwrap(); + + for (key, value) in storage { + api.anvil_set_storage_at(target, key, value).await.unwrap(); + } + + verify_storage_proof(&api, target, fixed_bytes!("0000000000000000000000000000000000000000000000000000000000000022"), [ + "0xf9019180a0aafd5b14a6edacd149e110ba6776a654f2dbffca340902be933d011113f2750380a0a502c93b1918c4c6534d4593ae03a5a23fa10ebc30ffb7080b297bff2446e42da02eb2bf45fd443bd1df8b6f9c09726a4c6252a0f7896a131a081e39a7f644b38980a0a9cf7f673a0bce76fd40332afe8601542910b48dea44e93933a3e5e930da5d19a0ddf79db0a36d0c8134ba143bcb541cd4795a9a2bae8aca0ba24b8d8963c2a77da0b973ec0f48f710bf79f63688485755cbe87f9d4c68326bb83c26af620802a80ea0f0855349af6bf84afc8bca2eda31c8ef8c5139be1929eeb3da4ba6b68a818cb0a0c271e189aeeb1db5d59d7fe87d7d6327bbe7cfa389619016459196497de3ccdea0e7503ba5799e77aa31bbe1310c312ca17b2c5bcc8fa38f266675e8f154c2516ba09278b846696d37213ab9d20a5eb42b03db3173ce490a2ef3b2f3b3600579fc63a0e9041059114f9c910adeca12dbba1fef79b2e2c8899f2d7213cd22dfe4310561a047c59da56bb2bf348c9dd2a2e8f5538a92b904b661cfe54a4298b85868bbe4858080", + "0xf85180a0776aa456ba9c5008e03b82b841a9cf2fc1e8578cfacd5c9015804eae315f17fb80808080808080808080808080a072e3e284d47badbb0a5ca1421e1179d3ea90cc10785b26b74fb8a81f0f9e841880", + "0xf843a020035b26e3e9eee00e0d72fd1ee8ddca6894550dca6916ea2ac6baa90d11e510a1a0f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b" + ]).await; + + verify_storage_proof(&api, target, fixed_bytes!("0000000000000000000000000000000000000000000000000000000000000023"), [ + "0xf9019180a0aafd5b14a6edacd149e110ba6776a654f2dbffca340902be933d011113f2750380a0a502c93b1918c4c6534d4593ae03a5a23fa10ebc30ffb7080b297bff2446e42da02eb2bf45fd443bd1df8b6f9c09726a4c6252a0f7896a131a081e39a7f644b38980a0a9cf7f673a0bce76fd40332afe8601542910b48dea44e93933a3e5e930da5d19a0ddf79db0a36d0c8134ba143bcb541cd4795a9a2bae8aca0ba24b8d8963c2a77da0b973ec0f48f710bf79f63688485755cbe87f9d4c68326bb83c26af620802a80ea0f0855349af6bf84afc8bca2eda31c8ef8c5139be1929eeb3da4ba6b68a818cb0a0c271e189aeeb1db5d59d7fe87d7d6327bbe7cfa389619016459196497de3ccdea0e7503ba5799e77aa31bbe1310c312ca17b2c5bcc8fa38f266675e8f154c2516ba09278b846696d37213ab9d20a5eb42b03db3173ce490a2ef3b2f3b3600579fc63a0e9041059114f9c910adeca12dbba1fef79b2e2c8899f2d7213cd22dfe4310561a047c59da56bb2bf348c9dd2a2e8f5538a92b904b661cfe54a4298b85868bbe4858080", + "0xf8518080808080a0d546c4ca227a267d29796643032422374624ed109b3d94848c5dc06baceaee76808080808080a027c48e210ccc6e01686be2d4a199d35f0e1e8df624a8d3a17c163be8861acd6680808080", + "0xf843a0207b2b5166478fd4318d2acc6cc2c704584312bdd8781b32d5d06abda57f4230a1a0db56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71" + ]).await; + + verify_storage_proof(&api, target, fixed_bytes!("0000000000000000000000000000000000000000000000000000000000000024"), [ + "0xf9019180a0aafd5b14a6edacd149e110ba6776a654f2dbffca340902be933d011113f2750380a0a502c93b1918c4c6534d4593ae03a5a23fa10ebc30ffb7080b297bff2446e42da02eb2bf45fd443bd1df8b6f9c09726a4c6252a0f7896a131a081e39a7f644b38980a0a9cf7f673a0bce76fd40332afe8601542910b48dea44e93933a3e5e930da5d19a0ddf79db0a36d0c8134ba143bcb541cd4795a9a2bae8aca0ba24b8d8963c2a77da0b973ec0f48f710bf79f63688485755cbe87f9d4c68326bb83c26af620802a80ea0f0855349af6bf84afc8bca2eda31c8ef8c5139be1929eeb3da4ba6b68a818cb0a0c271e189aeeb1db5d59d7fe87d7d6327bbe7cfa389619016459196497de3ccdea0e7503ba5799e77aa31bbe1310c312ca17b2c5bcc8fa38f266675e8f154c2516ba09278b846696d37213ab9d20a5eb42b03db3173ce490a2ef3b2f3b3600579fc63a0e9041059114f9c910adeca12dbba1fef79b2e2c8899f2d7213cd22dfe4310561a047c59da56bb2bf348c9dd2a2e8f5538a92b904b661cfe54a4298b85868bbe4858080", + "0xf85180808080a030263404acfee103d0b1019053ff3240fce433c69b709831673285fa5887ce4c80808080808080a0f8f1fbb1f7b482d9860480feebb83ff54a8b6ec1ead61cc7d2f25d7c01659f9c80808080", + "0xf843a020d332d19b93bcabe3cce7ca0c18a052f57e5fd03b4758a09f30f5ddc4b22ec4a1a0c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c", + ]).await; + + verify_storage_proof(&api, target, fixed_bytes!("0000000000000000000000000000000000000000000000000000000000000100"), [ + "0xf9019180a0aafd5b14a6edacd149e110ba6776a654f2dbffca340902be933d011113f2750380a0a502c93b1918c4c6534d4593ae03a5a23fa10ebc30ffb7080b297bff2446e42da02eb2bf45fd443bd1df8b6f9c09726a4c6252a0f7896a131a081e39a7f644b38980a0a9cf7f673a0bce76fd40332afe8601542910b48dea44e93933a3e5e930da5d19a0ddf79db0a36d0c8134ba143bcb541cd4795a9a2bae8aca0ba24b8d8963c2a77da0b973ec0f48f710bf79f63688485755cbe87f9d4c68326bb83c26af620802a80ea0f0855349af6bf84afc8bca2eda31c8ef8c5139be1929eeb3da4ba6b68a818cb0a0c271e189aeeb1db5d59d7fe87d7d6327bbe7cfa389619016459196497de3ccdea0e7503ba5799e77aa31bbe1310c312ca17b2c5bcc8fa38f266675e8f154c2516ba09278b846696d37213ab9d20a5eb42b03db3173ce490a2ef3b2f3b3600579fc63a0e9041059114f9c910adeca12dbba1fef79b2e2c8899f2d7213cd22dfe4310561a047c59da56bb2bf348c9dd2a2e8f5538a92b904b661cfe54a4298b85868bbe4858080", + "0xf891a090bacef44b189ddffdc5f22edc70fe298c58e5e523e6e1dfdf7dbc6d657f7d1b80a026eed68746028bc369eb456b7d3ee475aa16f34e5eaa0c98fdedb9c59ebc53b0808080a09ce86197173e14e0633db84ce8eea32c5454eebe954779255644b45b717e8841808080a0328c7afb2c58ef3f8c4117a8ebd336f1a61d24591067ed9c5aae94796cac987d808080808080", + ]).await; +} + +#[tokio::test(flavor = "multi_thread")] +async fn can_get_random_account_proofs() { + let (api, _handle) = spawn(NodeConfig::test()).await; + + for acc in std::iter::repeat_with(Address::random).take(10) { + let _ = api + .get_proof(acc, Vec::new(), None) + .await + .unwrap_or_else(|_| panic!("Failed to get proof for {acc:?}")); + } +} diff --git a/crates/anvil/tests/it/proof/eip1186.rs b/crates/anvil/tests/it/proof/eip1186.rs deleted file mode 100644 index c83cdf4f8..000000000 --- a/crates/anvil/tests/it/proof/eip1186.rs +++ /dev/null @@ -1,297 +0,0 @@ -/// Taken from https://github.com/paritytech/trie/blob/aa3168d6de01793e71ebd906d3a82ae4b363db59/trie-eip1186/src/eip1186.rs -use hash_db::Hasher; -use trie_db::{ - node::{decode_hash, Node, NodeHandle, Value}, - CError, NibbleSlice, NodeCodec, TrieHash, TrieLayout, -}; - -/// Errors that may occur during proof verification. Most of the errors types simply indicate that -/// the proof is invalid with respect to the statement being verified, and the exact error type can -/// be used for debugging. -#[derive(Debug, PartialEq, Eq)] -pub enum VerifyError<'a, HO, CE> { - /// The proof does not contain any value for the given key - /// the error carries the nibbles left after traversing the trie - NonExistingValue(NibbleSlice<'a>), - /// The proof contains a value for the given key - /// while we were expecting to find a non-existence proof - ExistingValue(Vec), - /// The proof indicates that the trie contains a different value. - /// the error carries the value contained in the trie - ValueMismatch(Vec), - /// The proof is missing trie nodes required to verify. - IncompleteProof, - /// The node hash computed from the proof is not matching. - HashMismatch(HO), - /// One of the proof nodes could not be decoded. - DecodeError(CE), - /// Error in converting a plain hash into a HO - HashDecodeError(&'a [u8]), -} - -#[cfg(feature = "std")] -impl<'a, HO: std::fmt::Debug, CE: std::error::Error> std::fmt::Display for VerifyError<'a, HO, CE> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { - match self { - VerifyError::NonExistingValue(key) => { - write!(f, "Key does not exist in trie: reaming key={:?}", key) - } - VerifyError::ExistingValue(value) => { - write!(f, "trie contains a value for given key value={:?}", value) - } - VerifyError::ValueMismatch(key) => { - write!(f, "Expected value was not found in the trie: key={:?}", key) - } - VerifyError::IncompleteProof => write!(f, "Proof is incomplete -- expected more nodes"), - VerifyError::HashMismatch(hash) => write!(f, "hash mismatch found: hash={:?}", hash), - VerifyError::DecodeError(err) => write!(f, "Unable to decode proof node: {}", err), - VerifyError::HashDecodeError(plain_hash) => { - write!(f, "Unable to decode hash value plain_hash: {:?}", plain_hash) - } - } - } -} - -#[cfg(feature = "std")] -impl<'a, HO: std::fmt::Debug, CE: std::error::Error + 'static> std::error::Error - for VerifyError<'a, HO, CE> -{ - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - VerifyError::DecodeError(err) => Some(err), - _ => None, - } - } -} - -/// Verify a compact proof for key-value pairs in a trie given a root hash. -pub fn verify_proof<'a, L>( - root: &::Out, - proof: &'a [Vec], - raw_key: &'a [u8], - expected_value: Option<&[u8]>, -) -> Result<(), VerifyError<'a, TrieHash, CError>> -where - L: TrieLayout, -{ - if proof.is_empty() { - return Err(VerifyError::IncompleteProof) - } - let key = NibbleSlice::new(raw_key); - process_node::(Some(root), &proof[0], key, expected_value, &proof[1..]) -} - -fn process_node<'a, L>( - expected_node_hash: Option<&::Out>, - encoded_node: &'a [u8], - key: NibbleSlice<'a>, - expected_value: Option<&[u8]>, - proof: &'a [Vec], -) -> Result<(), VerifyError<'a, TrieHash, CError>> -where - L: TrieLayout, -{ - if let Some(value) = expected_value { - if encoded_node == value { - return Ok(()) - } - } - if let Some(expected) = expected_node_hash { - let calculated_node_hash = ::hash(encoded_node); - if calculated_node_hash != *expected { - return Err(VerifyError::HashMismatch(calculated_node_hash)) - } - } - let node = ::decode(encoded_node).map_err(VerifyError::DecodeError)?; - match node { - Node::Empty => process_empty::(key, expected_value, proof), - Node::Leaf(nib, data) => process_leaf::(nib, data, key, expected_value, proof), - Node::Extension(nib, handle) => { - process_extension::(&nib, handle, key, expected_value, proof) - } - Node::Branch(children, maybe_data) => { - process_branch::(children, maybe_data, key, expected_value, proof) - } - Node::NibbledBranch(nib, children, maybe_data) => { - process_nibbledbranch::(nib, children, maybe_data, key, expected_value, proof) - } - } -} - -fn process_empty<'a, L>( - key: NibbleSlice<'a>, - expected_value: Option<&[u8]>, - _: &[Vec], -) -> Result<(), VerifyError<'a, TrieHash, CError>> -where - L: TrieLayout, -{ - if expected_value.is_none() { - Ok(()) - } else { - Err(VerifyError::NonExistingValue(key)) - } -} - -fn process_leaf<'a, L>( - nib: NibbleSlice, - data: Value<'a>, - key: NibbleSlice<'a>, - expected_value: Option<&[u8]>, - proof: &'a [Vec], -) -> Result<(), VerifyError<'a, TrieHash, CError>> -where - L: TrieLayout, -{ - if key != nib && expected_value.is_none() { - return Ok(()) - } else if key != nib { - return Err(VerifyError::NonExistingValue(key)) - } - match_value::(Some(data), key, expected_value, proof) -} -fn process_extension<'a, L>( - nib: &NibbleSlice, - handle: NodeHandle<'a>, - mut key: NibbleSlice<'a>, - expected_value: Option<&[u8]>, - proof: &'a [Vec], -) -> Result<(), VerifyError<'a, TrieHash, CError>> -where - L: TrieLayout, -{ - if !key.starts_with(nib) && expected_value.is_none() { - return Ok(()) - } else if !key.starts_with(nib) { - return Err(VerifyError::NonExistingValue(key)) - } - key.advance(nib.len()); - - match handle { - NodeHandle::Inline(encoded_node) => { - process_node::(None, encoded_node, key, expected_value, proof) - } - NodeHandle::Hash(plain_hash) => { - let new_root = decode_hash::(plain_hash) - .ok_or_else(|| VerifyError::HashDecodeError(plain_hash))?; - process_node::(Some(&new_root), &proof[0], key, expected_value, &proof[1..]) - } - } -} - -fn process_nibbledbranch<'a, L>( - nib: NibbleSlice, - children: [Option>; 16], - maybe_data: Option>, - mut key: NibbleSlice<'a>, - expected_value: Option<&[u8]>, - proof: &'a [Vec], -) -> Result<(), VerifyError<'a, TrieHash, CError>> -where - L: TrieLayout, -{ - if !key.starts_with(&nib) && expected_value.is_none() { - return Ok(()) - } else if !key.starts_with(&nib) && expected_value.is_some() { - return Err(VerifyError::NonExistingValue(key)) - } - key.advance(nib.len()); - - if key.is_empty() { - match_value::(maybe_data, key, expected_value, proof) - } else { - match_children::(children, key, expected_value, proof) - } -} - -fn process_branch<'a, L>( - children: [Option>; 16], - maybe_data: Option>, - key: NibbleSlice<'a>, - expected_value: Option<&[u8]>, - proof: &'a [Vec], -) -> Result<(), VerifyError<'a, TrieHash, CError>> -where - L: TrieLayout, -{ - if key.is_empty() { - match_value::(maybe_data, key, expected_value, proof) - } else { - match_children::(children, key, expected_value, proof) - } -} -fn match_children<'a, L>( - children: [Option>; 16], - mut key: NibbleSlice<'a>, - expected_value: Option<&[u8]>, - proof: &'a [Vec], -) -> Result<(), VerifyError<'a, TrieHash, CError>> -where - L: TrieLayout, -{ - match children.get(key.at(0) as usize) { - Some(Some(NodeHandle::Hash(hash))) => { - if proof.is_empty() { - Err(VerifyError::IncompleteProof) - } else { - key.advance(1); - let new_root = decode_hash::(hash) - .ok_or_else(|| VerifyError::HashDecodeError(hash))?; - process_node::(Some(&new_root), &proof[0], key, expected_value, &proof[1..]) - } - } - Some(Some(NodeHandle::Inline(encoded_node))) => { - key.advance(1); - process_node::(None, encoded_node, key, expected_value, proof) - } - Some(None) => { - if expected_value.is_none() { - Ok(()) - } else { - Err(VerifyError::NonExistingValue(key)) - } - } - None => panic!("key index is out of range in children array"), - } -} - -fn match_value<'a, L>( - maybe_data: Option>, - key: NibbleSlice<'a>, - expected_value: Option<&[u8]>, - proof: &'a [Vec], -) -> Result<(), VerifyError<'a, TrieHash, CError>> -where - L: TrieLayout, -{ - match (maybe_data, proof.first(), expected_value) { - (None, _, None) => Ok(()), - (None, _, Some(_)) => Err(VerifyError::NonExistingValue(key)), - (Some(Value::Inline(inline_data)), _, Some(value)) => { - if inline_data == value { - Ok(()) - } else { - Err(VerifyError::ValueMismatch(inline_data.to_vec())) - } - } - (Some(Value::Inline(inline_data)), _, None) => { - Err(VerifyError::ExistingValue(inline_data.to_vec())) - } - (Some(Value::Node(plain_hash, _)), Some(next_proof_item), Some(value)) => { - let value_hash = L::Hash::hash(value); - let node_hash = decode_hash::(plain_hash) - .ok_or_else(|| VerifyError::HashDecodeError(plain_hash))?; - if node_hash != value_hash { - Err(VerifyError::HashMismatch(node_hash)) - } else if next_proof_item != value { - Err(VerifyError::ValueMismatch(next_proof_item.to_vec())) - } else { - Ok(()) - } - } - (Some(Value::Node(_, _)), None, _) => Err(VerifyError::IncompleteProof), - (Some(Value::Node(_, _)), Some(proof_item), None) => { - Err(VerifyError::ExistingValue(proof_item.to_vec())) - } - } -} diff --git a/crates/anvil/tests/it/proof/mod.rs b/crates/anvil/tests/it/proof/mod.rs deleted file mode 100644 index 85e8c630f..000000000 --- a/crates/anvil/tests/it/proof/mod.rs +++ /dev/null @@ -1,76 +0,0 @@ -//! tests for `eth_getProof` - -use crate::proof::eip1186::verify_proof; -use alloy_primitives::{keccak256, Address, B256, U256}; -use alloy_rlp::Decodable; -use alloy_rpc_types::EIP1186AccountProofResponse; -use anvil::{spawn, NodeConfig}; -use anvil_core::eth::{proof::BasicAccount, trie::ExtensionLayout}; -use foundry_evm::revm::primitives::KECCAK_EMPTY; - -mod eip1186; - -#[tokio::test(flavor = "multi_thread")] -async fn can_get_proof() { - let (api, _handle) = spawn(NodeConfig::test()).await; - - let acc: Address = "0xaaaf5374fce5edbc8e2a8697c15331677e6ebaaa".parse().unwrap(); - - let key = U256::ZERO; - let value = U256::from(1); - - api.anvil_set_storage_at(acc, key, B256::from(value)).await.unwrap(); - - let proof: EIP1186AccountProofResponse = - api.get_proof(acc, vec![B256::from(key)], None).await.unwrap(); - - let account = BasicAccount { - nonce: U256::from(0), - balance: U256::from(0), - storage_root: proof.storage_hash, - code_hash: KECCAK_EMPTY, - }; - - let rlp_account = alloy_rlp::encode(&account); - - let root: B256 = api.state_root().await.unwrap(); - let acc_proof: Vec> = proof - .account_proof - .into_iter() - .map(|node| Vec::::decode(&mut &node[..]).unwrap()) - .collect(); - - verify_proof::( - &root.0, - &acc_proof, - &keccak256(acc.as_slice())[..], - Some(rlp_account.as_ref()), - ) - .unwrap(); - - assert_eq!(proof.storage_proof.len(), 1); - let expected_value = alloy_rlp::encode(value); - let proof = proof.storage_proof[0].clone(); - let storage_proof: Vec> = - proof.proof.into_iter().map(|node| Vec::::decode(&mut &node[..]).unwrap()).collect(); - let key = B256::from(keccak256(proof.key.0 .0)); - verify_proof::( - &account.storage_root.0, - &storage_proof, - key.as_slice(), - Some(expected_value.as_ref()), - ) - .unwrap(); -} - -#[tokio::test(flavor = "multi_thread")] -async fn can_get_random_account_proofs() { - let (api, _handle) = spawn(NodeConfig::test()).await; - - for acc in std::iter::repeat_with(Address::random).take(10) { - let _ = api - .get_proof(acc, Vec::new(), None) - .await - .unwrap_or_else(|_| panic!("Failed to get proof for {acc:?}")); - } -} From 5274799a98395d2104be27d50e1852868ca118a1 Mon Sep 17 00:00:00 2001 From: Krishang <93703995+kamuik16@users.noreply.github.com> Date: Mon, 8 Apr 2024 15:40:22 +0530 Subject: [PATCH 141/430] chore(forge): kB to B in build --sizes (#7588) --- crates/common/src/compile.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index 654ddd693..bba5e15d2 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -403,8 +403,8 @@ impl Display for SizeReport { table.load_preset(ASCII_MARKDOWN); table.set_header([ Cell::new("Contract").add_attribute(Attribute::Bold).fg(Color::Blue), - Cell::new("Size (kB)").add_attribute(Attribute::Bold).fg(Color::Blue), - Cell::new("Margin (kB)").add_attribute(Attribute::Bold).fg(Color::Blue), + Cell::new("Size (B)").add_attribute(Attribute::Bold).fg(Color::Blue), + Cell::new("Margin (B)").add_attribute(Attribute::Bold).fg(Color::Blue), ]); // filters out non dev contracts (Test or Script) @@ -419,8 +419,8 @@ impl Display for SizeReport { table.add_row([ Cell::new(name).fg(color), - Cell::new(contract.size as f64 / 1000.0).fg(color), - Cell::new(margin as f64 / 1000.0).fg(color), + Cell::new(contract.size).fg(color), + Cell::new(margin).fg(color), ]); } From 04e2263ff8ffcd7bfd2b705a0f7af08209f800e4 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 8 Apr 2024 19:13:41 +0400 Subject: [PATCH 142/430] fix: smarter `getCode` validation (#7597) * fix: smarter getCode validation * clippy + doc fix * fix * multi-version profile and tests * fmt * add more parsing options * clippy + fmt --- crates/cheatcodes/src/config.rs | 7 + crates/cheatcodes/src/fs.rs | 122 +++++++++++++----- crates/chisel/src/executor.rs | 1 + crates/forge/bin/cmd/coverage.rs | 1 + crates/forge/bin/cmd/test/mod.rs | 1 + crates/forge/src/multi_runner.rs | 55 ++++---- crates/forge/tests/it/cheats.rs | 19 ++- crates/forge/tests/it/test_helpers.rs | 14 +- crates/script/src/execute.rs | 1 + crates/script/src/lib.rs | 8 +- testdata/default/cheats/GetCode.t.sol | 12 ++ testdata/multi-version/Counter.sol | 11 ++ testdata/multi-version/Importer.sol | 7 + testdata/multi-version/cheats/GetCode.t.sol | 25 ++++ testdata/multi-version/cheats/GetCode17.t.sol | 26 ++++ 15 files changed, 238 insertions(+), 72 deletions(-) create mode 100644 testdata/multi-version/Counter.sol create mode 100644 testdata/multi-version/Importer.sol create mode 100644 testdata/multi-version/cheats/GetCode.t.sol create mode 100644 testdata/multi-version/cheats/GetCode17.t.sol diff --git a/crates/cheatcodes/src/config.rs b/crates/cheatcodes/src/config.rs index 9d0ca2ea4..f93ab4f58 100644 --- a/crates/cheatcodes/src/config.rs +++ b/crates/cheatcodes/src/config.rs @@ -8,6 +8,7 @@ use foundry_config::{ ResolvedRpcEndpoints, }; use foundry_evm_core::opts::EvmOpts; +use semver::Version; use std::{ collections::HashMap, path::{Path, PathBuf}, @@ -47,6 +48,8 @@ pub struct CheatsConfig { /// If Some, `vm.getDeployedCode` invocations are validated to be in scope of this list. /// If None, no validation is performed. pub available_artifacts: Option>, + /// Version of the script/test contract which is currently running. + pub running_version: Option, } impl CheatsConfig { @@ -56,6 +59,7 @@ impl CheatsConfig { evm_opts: EvmOpts, available_artifacts: Option>, script_wallets: Option, + running_version: Option, ) -> Self { let mut allowed_paths = vec![config.__root.0.clone()]; allowed_paths.extend(config.libs.clone()); @@ -82,6 +86,7 @@ impl CheatsConfig { labels: config.labels.clone(), script_wallets, available_artifacts, + running_version, } } @@ -200,6 +205,7 @@ impl Default for CheatsConfig { labels: Default::default(), script_wallets: None, available_artifacts: Default::default(), + running_version: Default::default(), } } } @@ -215,6 +221,7 @@ mod tests { Default::default(), None, None, + None, ) } diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index 9760b4f16..54335a5e5 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -271,14 +271,40 @@ impl Cheatcode for getDeployedCodeCall { } /// Returns the path to the json artifact depending on the input +/// +/// Can parse following input formats: +/// - `path/to/artifact.json` +/// - `path/to/contract.sol` +/// - `path/to/contract.sol:ContractName` +/// - `path/to/contract.sol:ContractName:0.8.23` +/// - `path/to/contract.sol:0.8.23` +/// - `ContractName` +/// - `ContractName:0.8.23` fn get_artifact_path(state: &Cheatcodes, path: &str) -> Result { if path.ends_with(".json") { Ok(PathBuf::from(path)) } else { let mut parts = path.split(':'); - let file = PathBuf::from(parts.next().unwrap()); - let contract_name = parts.next(); - let version = parts.next(); + + let mut file = None; + let mut contract_name = None; + let mut version = None; + + let path_or_name = parts.next().unwrap(); + if path_or_name.ends_with(".sol") { + file = Some(PathBuf::from(path_or_name)); + if let Some(name_or_version) = parts.next() { + if name_or_version.contains('.') { + version = Some(name_or_version); + } else { + contract_name = Some(name_or_version); + version = parts.next(); + } + } + } else { + contract_name = Some(path_or_name); + version = parts.next(); + } let version = if let Some(version) = version { Some(Version::parse(version).map_err(|_| fmt_err!("Error parsing version"))?) @@ -288,44 +314,70 @@ fn get_artifact_path(state: &Cheatcodes, path: &str) -> Result { // Use available artifacts list if available if let Some(available_ids) = &state.config.available_artifacts { - let mut artifact = None; - - for id in available_ids.iter() { - // name might be in the form of "Counter.0.8.23" - let id_name = id.name.split('.').next().unwrap(); - - if !id.source.ends_with(&file) { - continue; - } - if let Some(name) = contract_name { - if id_name != name { - continue; + let filtered = available_ids + .iter() + .filter(|id| { + // name might be in the form of "Counter.0.8.23" + let id_name = id.name.split('.').next().unwrap(); + + if let Some(path) = &file { + if !id.source.ends_with(path) { + return false; + } } - } - if let Some(ref version) = version { - if id.version.minor != version.minor || - id.version.major != version.major || - id.version.patch != version.patch - { - continue; + if let Some(name) = contract_name { + if id_name != name { + return false; + } } + if let Some(ref version) = version { + if id.version.minor != version.minor || + id.version.major != version.major || + id.version.patch != version.patch + { + return false; + } + } + true + }) + .collect::>(); + + let artifact = match filtered.len() { + 0 => Err(fmt_err!("No matching artifact found")), + 1 => Ok(filtered[0]), + _ => { + // If we know the current script/test contract solc version, try to filter by it + state + .config + .running_version + .as_ref() + .and_then(|version| { + let filtered = filtered + .into_iter() + .filter(|id| id.version == *version) + .collect::>(); + + (filtered.len() == 1).then_some(filtered[0]) + }) + .ok_or_else(|| fmt_err!("Multiple matching artifacts found")) } - if artifact.is_some() { - return Err(fmt_err!("Multiple matching artifacts found")); - } - artifact = Some(id); - } + }?; - let artifact = artifact.ok_or_else(|| fmt_err!("No matching artifact found"))?; Ok(artifact.path.clone()) } else { - let file = file.to_string_lossy(); - let contract_name = if let Some(contract_name) = contract_name { - contract_name.to_owned() - } else { - file.replace(".sol", "") - }; - Ok(state.config.paths.artifacts.join(format!("{file}/{contract_name}.json"))) + let path_in_artifacts = + match (file.map(|f| f.to_string_lossy().to_string()), contract_name) { + (Some(file), Some(contract_name)) => Ok(format!("{file}/{contract_name}.json")), + (None, Some(contract_name)) => { + Ok(format!("{contract_name}.sol/{contract_name}.json")) + } + (Some(file), None) => { + let name = file.replace(".sol", ""); + Ok(format!("{file}/{name}.json")) + } + _ => Err(fmt_err!("Invalid artifact path")), + }?; + Ok(state.config.paths.artifacts.join(path_in_artifacts)) } } } diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index e0774a2b6..6343013b1 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -308,6 +308,7 @@ impl SessionSource { self.config.evm_opts.clone(), None, None, + self.solc.version().ok(), ) .into(), ) diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index 8dc4629b9..29538164b 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -317,6 +317,7 @@ impl CoverageArgs { evm_opts.clone(), Some(artifact_ids), None, + None, )) .with_test_options(TestOptions { fuzz: config.fuzz, diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 104fa4c48..16f5ec516 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -287,6 +287,7 @@ impl TestArgs { evm_opts.clone(), Some(artifact_ids), None, + None, // populated separately for each test contract )) .with_test_options(test_options) .enable_isolation(evm_opts.isolate) diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 94d0b0155..f19e67d22 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -9,13 +9,8 @@ use foundry_compilers::{ artifacts::Libraries, contracts::ArtifactContracts, Artifact, ArtifactId, ProjectCompileOutput, }; use foundry_evm::{ - backend::Backend, - decode::RevertDecoder, - executors::{Executor, ExecutorBuilder}, - fork::CreateFork, - inspectors::CheatsConfig, - opts::EvmOpts, - revm, + backend::Backend, decode::RevertDecoder, executors::ExecutorBuilder, fork::CreateFork, + inspectors::CheatsConfig, opts::EvmOpts, revm, }; use foundry_linking::{LinkOutput, Linker}; use rayon::prelude::*; @@ -158,18 +153,6 @@ impl MultiContractRunner { // The DB backend that serves all the data. let db = Backend::spawn(self.fork.take()); - let executor = ExecutorBuilder::new() - .inspectors(|stack| { - stack - .cheatcodes(self.cheats_config.clone()) - .trace(self.evm_opts.verbosity >= 3 || self.debug) - .debug(self.debug) - .coverage(self.coverage) - .enable_isolation(self.isolation) - }) - .spec(self.evm_spec) - .gas_limit(self.evm_opts.gas_limit()) - .build(self.env.clone(), db); let find_timer = Instant::now(); let contracts = self.matching_contracts(filter).collect::>(); @@ -182,30 +165,46 @@ impl MultiContractRunner { ); contracts.par_iter().for_each_with(tx, |tx, &(id, contract)| { - let identifier = id.identifier(); - let executor = executor.clone(); - let result = self.run_tests(&identifier, contract, executor, filter); - let _ = tx.send((identifier, result)); + let result = self.run_tests(id, contract, db.clone(), filter); + let _ = tx.send((id.identifier(), result)); }) } fn run_tests( &self, - name: &str, + artifact_id: &ArtifactId, contract: &TestContract, - executor: Executor, + db: Backend, filter: &dyn TestFilter, ) -> SuiteResult { - let mut span_name = name; + let identifier = artifact_id.identifier(); + let mut span_name = identifier.as_str(); + + let mut cheats_config = self.cheats_config.as_ref().clone(); + cheats_config.running_version = Some(artifact_id.version.clone()); + + let executor = ExecutorBuilder::new() + .inspectors(|stack| { + stack + .cheatcodes(Arc::new(cheats_config)) + .trace(self.evm_opts.verbosity >= 3 || self.debug) + .debug(self.debug) + .coverage(self.coverage) + .enable_isolation(self.isolation) + }) + .spec(self.evm_spec) + .gas_limit(self.evm_opts.gas_limit()) + .build(self.env.clone(), db); + if !enabled!(tracing::Level::TRACE) { - span_name = get_contract_name(span_name); + span_name = get_contract_name(&identifier); } let _guard = info_span!("run_tests", name = span_name).entered(); debug!("start executing all tests in contract"); let runner = ContractRunner::new( - name, + &identifier, executor, contract, self.evm_opts.initial_balance, diff --git a/crates/forge/tests/it/cheats.rs b/crates/forge/tests/it/cheats.rs index fd6e9866e..920dd8f2d 100644 --- a/crates/forge/tests/it/cheats.rs +++ b/crates/forge/tests/it/cheats.rs @@ -2,14 +2,13 @@ use crate::{ config::*, - test_helpers::{RE_PATH_SEPARATOR, TEST_DATA_DEFAULT}, + test_helpers::{ForgeTestData, RE_PATH_SEPARATOR, TEST_DATA_DEFAULT, TEST_DATA_MULTI_VERSION}, }; use foundry_config::{fs_permissions::PathPermission, FsPermissions}; use foundry_test_utils::Filter; /// Executes all cheat code tests but not fork cheat codes -#[tokio::test(flavor = "multi_thread")] -async fn test_cheats_local() { +async fn test_cheats_local(test_data: &ForgeTestData) { let mut filter = Filter::new(".*", ".*", &format!(".*cheats{RE_PATH_SEPARATOR}*")).exclude_paths("Fork"); @@ -18,9 +17,19 @@ async fn test_cheats_local() { filter = filter.exclude_tests("(Ffi|File|Line|Root)"); } - let mut config = TEST_DATA_DEFAULT.config.clone(); + let mut config = test_data.config.clone(); config.fs_permissions = FsPermissions::new(vec![PathPermission::read_write("./")]); - let runner = TEST_DATA_DEFAULT.runner_with_config(config); + let runner = test_data.runner_with_config(config); TestConfig::with_filter(runner, filter).run().await; } + +#[tokio::test(flavor = "multi_thread")] +async fn test_cheats_local_default() { + test_cheats_local(&TEST_DATA_DEFAULT).await +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_cheats_local_multi_version() { + test_cheats_local(&TEST_DATA_MULTI_VERSION).await +} diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index 8d54976cc..a8ab8229c 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -32,6 +32,7 @@ const TESTDATA: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../testdata"); pub enum ForgeTestProfile { Default, Cancun, + MultiVersion, } impl fmt::Display for ForgeTestProfile { @@ -39,6 +40,7 @@ impl fmt::Display for ForgeTestProfile { match self { ForgeTestProfile::Default => write!(f, "default"), ForgeTestProfile::Cancun => write!(f, "cancun"), + ForgeTestProfile::MultiVersion => write!(f, "multi-version"), } } } @@ -206,7 +208,13 @@ impl ForgeTestData { let output = self.output.clone(); let artifact_ids = output.artifact_ids().map(|(id, _)| id).collect(); self.base_runner() - .with_cheats_config(CheatsConfig::new(&config, opts.clone(), Some(artifact_ids), None)) + .with_cheats_config(CheatsConfig::new( + &config, + opts.clone(), + Some(artifact_ids), + None, + None, + )) .sender(config.sender) .with_test_options(self.test_opts.clone()) .build(root, output, env, opts.clone()) @@ -274,6 +282,10 @@ pub static TEST_DATA_DEFAULT: Lazy = pub static TEST_DATA_CANCUN: Lazy = Lazy::new(|| ForgeTestData::new(ForgeTestProfile::Cancun)); +/// Data for tests requiring Cancun support on Solc and EVM level. +pub static TEST_DATA_MULTI_VERSION: Lazy = + Lazy::new(|| ForgeTestData::new(ForgeTestProfile::MultiVersion)); + pub fn manifest_root() -> &'static Path { let mut root = Path::new(env!("CARGO_MANIFEST_DIR")); // need to check here where we're executing the test from, if in `forge` we need to also allow diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index 7a2546ec8..7b811a67c 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -102,6 +102,7 @@ impl PreExecutionState { self.build_data.build_data.artifact_ids.clone(), self.script_wallets.clone(), self.args.debug, + self.build_data.build_data.target.clone(), ) .await?; let mut result = self.execute_with_runner(&mut runner).await?; diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 5b27a5ff4..530c84d36 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -559,13 +559,14 @@ impl ScriptConfig { artifact_ids: Vec, script_wallets: ScriptWallets, debug: bool, + target: ArtifactId, ) -> Result { - self._get_runner(Some((artifact_ids, script_wallets)), debug).await + self._get_runner(Some((artifact_ids, script_wallets, target)), debug).await } async fn _get_runner( &mut self, - cheats_data: Option<(Vec, ScriptWallets)>, + cheats_data: Option<(Vec, ScriptWallets, ArtifactId)>, debug: bool, ) -> Result { trace!("preparing script runner"); @@ -594,7 +595,7 @@ impl ScriptConfig { .spec(self.config.evm_spec_id()) .gas_limit(self.evm_opts.gas_limit()); - if let Some((artifact_ids, script_wallets)) = cheats_data { + if let Some((artifact_ids, script_wallets, target)) = cheats_data { builder = builder.inspectors(|stack| { stack .debug(debug) @@ -604,6 +605,7 @@ impl ScriptConfig { self.evm_opts.clone(), Some(artifact_ids), Some(script_wallets), + Some(target.version), ) .into(), ) diff --git a/testdata/default/cheats/GetCode.t.sol b/testdata/default/cheats/GetCode.t.sol index d308712e9..73980d7b2 100644 --- a/testdata/default/cheats/GetCode.t.sol +++ b/testdata/default/cheats/GetCode.t.sol @@ -6,6 +6,8 @@ import "cheats/Vm.sol"; contract TestContract {} +contract TestContractGetCode {} + contract GetCodeTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); @@ -80,4 +82,14 @@ contract GetCodeTest is DSTest { vm._expectCheatcodeRevert("No matching artifact found"); vm.getCode("cheats/GetCode.t.sol:TestContract:0.8.19"); } + + function testByName() public { + bytes memory code = vm.getCode("TestContractGetCode"); + assertEq(type(TestContractGetCode).creationCode, code); + } + + function testByNameAndVersion() public { + bytes memory code = vm.getCode("TestContractGetCode:0.8.18"); + assertEq(type(TestContractGetCode).creationCode, code); + } } diff --git a/testdata/multi-version/Counter.sol b/testdata/multi-version/Counter.sol new file mode 100644 index 000000000..4f0c35033 --- /dev/null +++ b/testdata/multi-version/Counter.sol @@ -0,0 +1,11 @@ +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } +} diff --git a/testdata/multi-version/Importer.sol b/testdata/multi-version/Importer.sol new file mode 100644 index 000000000..d8e274e8f --- /dev/null +++ b/testdata/multi-version/Importer.sol @@ -0,0 +1,7 @@ +pragma solidity 0.8.17; + +import "./Counter.sol"; + +// Please do not remove or change version pragma for this file. +// If you need to ensure that some of the files are compiled with +// solc 0.8.17, you should add imports of them to this file. diff --git a/testdata/multi-version/cheats/GetCode.t.sol b/testdata/multi-version/cheats/GetCode.t.sol new file mode 100644 index 000000000..e4a7bd14a --- /dev/null +++ b/testdata/multi-version/cheats/GetCode.t.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; +import "../Counter.sol"; + +contract GetCodeTest is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + function testGetCodeMultiVersion() public { + assertEq(vm.getCode("Counter.sol"), type(Counter).creationCode); + require( + keccak256(vm.getCode("Counter.sol")) != keccak256(vm.getCode("Counter.sol:Counter:0.8.17")), + "Invalid artifact" + ); + assertEq(vm.getCode("Counter.sol"), vm.getCode("Counter.sol:Counter:0.8.18")); + } + + function testGetCodeByNameMultiVersion() public { + assertEq(vm.getCode("Counter"), type(Counter).creationCode); + require(keccak256(vm.getCode("Counter")) != keccak256(vm.getCode("Counter:0.8.17")), "Invalid artifact"); + assertEq(vm.getCode("Counter"), vm.getCode("Counter:0.8.18")); + } +} diff --git a/testdata/multi-version/cheats/GetCode17.t.sol b/testdata/multi-version/cheats/GetCode17.t.sol new file mode 100644 index 000000000..068a910cf --- /dev/null +++ b/testdata/multi-version/cheats/GetCode17.t.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.17; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; +import "../Counter.sol"; + +// Same as GetCode.t.sol but for 0.8.17 version +contract GetCodeTest17 is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + function testGetCodeMultiVersion() public { + assertEq(vm.getCode("Counter.sol"), type(Counter).creationCode); + require( + keccak256(vm.getCode("Counter.sol")) != keccak256(vm.getCode("Counter.sol:Counter:0.8.18")), + "Invalid artifact" + ); + assertEq(vm.getCode("Counter.sol"), vm.getCode("Counter.sol:Counter:0.8.17")); + } + + function testGetCodeByNameMultiVersion() public { + assertEq(vm.getCode("Counter"), type(Counter).creationCode); + require(keccak256(vm.getCode("Counter")) != keccak256(vm.getCode("Counter:0.8.18")), "Invalid artifact"); + assertEq(vm.getCode("Counter.sol"), vm.getCode("Counter:0.8.17")); + } +} From b88d167bbbd203d97fd9e06121da87bdacbca3a5 Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Mon, 8 Apr 2024 17:51:42 +0200 Subject: [PATCH 143/430] feat(forge): add `vm.lastCallGas` cheatcode (#7573) * add `gasUsed and lastGasUsed methods to Vm * reorder * basic sketching of idea, planning to use Gas struct to deliver as much gas related information rather than a uint256 * add transformation * to prevent recording gas by default, only enable after recordGas is enabled * update struct layout, implementation builds, now connecting to cheatcodes * fix cheatcodes * refactor to use simple u64 as Gas struct doesnt have a default * change from Gas struct to simple u64 as I ran into issues with cache being reset to 0 * it appears cheatcodes are resolved before the actual function calls are therefore it doesnt actually remember the correct value from the previous execution but only of the previous executed cheatcode * still not working * finally works, stupid me didnt realize i had to cross call frames * emit gas record * test convenient single field access * add gas record struct back * pass down isolate bool, only enable gas tracing if enabled * raise error if cheatcode is used outside of isolation mode * mark as view * show gas refund and memory expansion * improve example * add isolation test, currently does not run as expected * fix fmt * avoid formatting changes * avoid commiting formatting changes, editor now configured correctly * lastGasUsed -> lastCallGas * small name fix * remove separate isolation profile, just configure on the runner * fix forge fmt * note on why path should never happen * remove separate isolated param, inherit from config * add support for non-isolation mode * remove isolate standalone, create additional entry in cheats and document subset of cheats that require to be tested in isolation mode as well * improve tests, use asserts and add option to exclude contracts from test filter, not just individual tests or paths * typo, no need to define path exclusion of forks in isolated tests as it is not relevant --- crates/cheatcodes/assets/cheatcodes.json | 51 +++++++++ crates/cheatcodes/spec/src/lib.rs | 1 + crates/cheatcodes/spec/src/vm.rs | 52 ++++++--- crates/cheatcodes/src/evm.rs | 13 +++ crates/cheatcodes/src/inspector.rs | 22 +++- crates/forge/tests/it/cheats.rs | 23 +++- crates/forge/tests/it/test_helpers.rs | 8 +- crates/test-utils/src/filter.rs | 17 +++ testdata/cheats/Vm.sol | 2 + testdata/default/cheats/LastCallGas.t.sol | 125 ++++++++++++++++++++++ 10 files changed, 293 insertions(+), 21 deletions(-) create mode 100644 testdata/default/cheats/LastCallGas.t.sol diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 360d5de1c..3b4d071d7 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -415,6 +415,37 @@ "description": "If the access was reverted." } ] + }, + { + "name": "Gas", + "description": "", + "fields": [ + { + "name": "gasLimit", + "ty": "uint64", + "description": "" + }, + { + "name": "gasTotalUsed", + "ty": "uint64", + "description": "" + }, + { + "name": "gasMemoryUsed", + "ty": "uint64", + "description": "" + }, + { + "name": "gasRefunded", + "ty": "int64", + "description": "" + }, + { + "name": "gasRemaining", + "ty": "uint64", + "description": "" + } + ] } ], "cheatcodes": [ @@ -4958,6 +4989,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "lastCallGas", + "description": "Gets the gas used in the last call.", + "declaration": "function lastCallGas() external view returns (Gas memory gas);", + "visibility": "external", + "mutability": "view", + "signature": "lastCallGas()", + "selector": "0x2b589b28", + "selectorBytes": [ + 43, + 88, + 155, + 40 + ] + }, + "group": "evm", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "load", diff --git a/crates/cheatcodes/spec/src/lib.rs b/crates/cheatcodes/spec/src/lib.rs index 10a53e18d..16bb60834 100644 --- a/crates/cheatcodes/spec/src/lib.rs +++ b/crates/cheatcodes/spec/src/lib.rs @@ -83,6 +83,7 @@ impl Cheatcodes<'static> { Vm::ChainInfo::STRUCT.clone(), Vm::AccountAccess::STRUCT.clone(), Vm::StorageAccess::STRUCT.clone(), + Vm::Gas::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 93042c199..c350690fc 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -73,6 +73,19 @@ interface Vm { address emitter; } + struct Gas { + // The gas limit of the call. + uint64 gasLimit; + // The total gas used. + uint64 gasTotalUsed; + // The amount of gas used for memory expansion. + uint64 gasMemoryUsed; + // The amount of gas refunded. + int64 gasRefunded; + // The amount of gas remaining. + uint64 gasRemaining; + } + /// An RPC URL and its alias. Returned by `rpcUrlStructs`. struct Rpc { /// The alias of the RPC URL. @@ -169,6 +182,22 @@ interface Vm { uint256 chainId; } + /// The storage accessed during an `AccountAccess`. + struct StorageAccess { + /// The account whose storage was accessed. + address account; + /// The slot that was accessed. + bytes32 slot; + /// If the access was a write. + bool isWrite; + /// The previous value of the slot. + bytes32 previousValue; + /// The new value of the slot. + bytes32 newValue; + /// If the access was reverted. + bool reverted; + } + /// The result of a `stopAndReturnStateDiff` call. struct AccountAccess { /// The chain and fork the access occurred. @@ -207,22 +236,6 @@ interface Vm { uint64 depth; } - /// The storage accessed during an `AccountAccess`. - struct StorageAccess { - /// The account whose storage was accessed. - address account; - /// The slot that was accessed. - bytes32 slot; - /// If the access was a write. - bool isWrite; - /// The previous value of the slot. - bytes32 previousValue; - /// The new value of the slot. - bytes32 newValue; - /// If the access was reverted. - bool reverted; - } - // ======== EVM ======== /// Gets the address for a given private key. @@ -594,6 +607,7 @@ interface Vm { function getRecordedLogs() external returns (Log[] memory logs); // -------- Gas Metering -------- + // It's recommend to use the `noGasMetering` modifier included with forge-std, instead of // using these functions directly. @@ -605,6 +619,12 @@ interface Vm { #[cheatcode(group = Evm, safety = Safe)] function resumeGasMetering() external; + // -------- Gas Measurement -------- + + /// Gets the gas used in the last call. + #[cheatcode(group = Evm, safety = Safe)] + function lastCallGas() external view returns (Gas memory gas); + // ======== Test Assertions and Utilities ======== /// If the condition is false, discard this run's fuzz inputs and generate new ones. diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index 2f3dafede..d7328fcc0 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -224,6 +224,19 @@ impl Cheatcode for resumeGasMeteringCall { } } +impl Cheatcode for lastCallGasCall { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let Self {} = self; + ensure!(state.last_call_gas.is_some(), "`lastCallGas` is only available after a call"); + Ok(state + .last_call_gas + .as_ref() + // This should never happen, as we ensure `last_call_gas` is `Some` above. + .expect("`lastCallGas` is only available after a call") + .abi_encode()) + } +} + impl Cheatcode for chainIdCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newChainId } = self; diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 699840d93..934cae0c7 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -145,6 +145,10 @@ pub struct Cheatcodes { /// Recorded logs pub recorded_logs: Option>, + /// Cache of the amount of gas used in previous call. + /// This is used by the `lastCallGas` cheatcode. + pub last_call_gas: Option, + /// Mocked calls // **Note**: inner must a BTreeMap because of special `Ord` impl for `MockCallDataContext` pub mocked_calls: HashMap>, @@ -1033,9 +1037,25 @@ impl Inspector for Cheatcodes { // Exit early for calls to cheatcodes as other logic is not relevant for cheatcode // invocations if cheatcode_call { - return outcome; + return outcome } + // Record the gas usage of the call, this allows the `lastCallGas` cheatcode to + // retrieve the gas usage of the last call. + let gas = outcome.result.gas; + self.last_call_gas = Some(crate::Vm::Gas { + // The gas limit of the call. + gasLimit: gas.limit(), + // The total gas used. + gasTotalUsed: gas.spend(), + // The amount of gas used for memory expansion. + gasMemoryUsed: gas.memory(), + // The amount of gas refunded. + gasRefunded: gas.refunded(), + // The amount of gas remaining. + gasRemaining: gas.remaining(), + }); + // If `startStateDiffRecording` has been called, update the `reverted` status of the // previous call depth's recorded accesses, if any if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack { diff --git a/crates/forge/tests/it/cheats.rs b/crates/forge/tests/it/cheats.rs index 920dd8f2d..42113cdc7 100644 --- a/crates/forge/tests/it/cheats.rs +++ b/crates/forge/tests/it/cheats.rs @@ -7,10 +7,11 @@ use crate::{ use foundry_config::{fs_permissions::PathPermission, FsPermissions}; use foundry_test_utils::Filter; -/// Executes all cheat code tests but not fork cheat codes +/// Executes all cheat code tests but not fork cheat codes or tests that require isolation mode async fn test_cheats_local(test_data: &ForgeTestData) { - let mut filter = - Filter::new(".*", ".*", &format!(".*cheats{RE_PATH_SEPARATOR}*")).exclude_paths("Fork"); + let mut filter = Filter::new(".*", ".*", &format!(".*cheats{RE_PATH_SEPARATOR}*")) + .exclude_paths("Fork") + .exclude_contracts("Isolated"); // Exclude FFI tests on Windows because no `echo`, and file tests that expect certain file paths if cfg!(windows) { @@ -24,11 +25,27 @@ async fn test_cheats_local(test_data: &ForgeTestData) { TestConfig::with_filter(runner, filter).run().await; } +/// Executes subset of all cheat code tests in isolation mode +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); + + TestConfig::with_filter(runner, filter).run().await; +} + #[tokio::test(flavor = "multi_thread")] async fn test_cheats_local_default() { test_cheats_local(&TEST_DATA_DEFAULT).await } +#[tokio::test(flavor = "multi_thread")] +async fn test_cheats_local_default_isolated() { + test_cheats_local_isolated(&TEST_DATA_DEFAULT).await +} + #[tokio::test(flavor = "multi_thread")] async fn test_cheats_local_multi_version() { test_cheats_local(&TEST_DATA_MULTI_VERSION).await diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index a8ab8229c..6fc8a3745 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -203,7 +203,12 @@ impl ForgeTestData { config.prompt_timeout = 0; let root = self.project.root(); - let opts = self.evm_opts.clone(); + let mut opts = self.evm_opts.clone(); + + if config.isolate { + opts.isolate = true; + } + let env = opts.local_evm_env(); let output = self.output.clone(); let artifact_ids = output.artifact_ids().map(|(id, _)| id).collect(); @@ -215,6 +220,7 @@ impl ForgeTestData { None, None, )) + .enable_isolation(opts.isolate) .sender(config.sender) .with_test_options(self.test_opts.clone()) .build(root, output, env, opts.clone()) diff --git a/crates/test-utils/src/filter.rs b/crates/test-utils/src/filter.rs index fb07237f2..e24f87c17 100644 --- a/crates/test-utils/src/filter.rs +++ b/crates/test-utils/src/filter.rs @@ -7,6 +7,7 @@ pub struct Filter { contract_regex: Regex, path_regex: Regex, exclude_tests: Option, + exclude_contracts: Option, exclude_paths: Option, } @@ -21,6 +22,7 @@ impl Filter { path_regex: Regex::new(path_pattern) .unwrap_or_else(|_| panic!("Failed to parse path pattern: `{path_pattern}`")), exclude_tests: None, + exclude_contracts: None, exclude_paths: None, } } @@ -41,6 +43,14 @@ impl Filter { self } + /// All contracts to also exclude + /// + /// This is a workaround since regex does not support negative look aheads + pub fn exclude_contracts(mut self, pattern: &str) -> Self { + self.exclude_contracts = Some(Regex::new(pattern).unwrap()); + self + } + /// All paths to also exclude /// /// This is a workaround since regex does not support negative look aheads @@ -55,6 +65,7 @@ impl Filter { contract_regex: Regex::new(".*").unwrap(), path_regex: Regex::new(".*").unwrap(), exclude_tests: None, + exclude_contracts: None, exclude_paths: None, } } @@ -71,6 +82,12 @@ impl TestFilter for Filter { } fn matches_contract(&self, contract_name: &str) -> bool { + if let Some(exclude) = &self.exclude_contracts { + if exclude.is_match(contract_name) { + return false; + } + } + self.contract_regex.is_match(contract_name) } diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index aff05c278..1d7c81973 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -18,6 +18,7 @@ interface Vm { struct ChainInfo { uint256 forkId; uint256 chainId; } struct AccountAccess { ChainInfo chainInfo; AccountAccessKind kind; address account; address accessor; bool initialized; uint256 oldBalance; uint256 newBalance; bytes deployedCode; uint256 value; bytes data; bool reverted; StorageAccess[] storageAccesses; uint64 depth; } struct StorageAccess { address account; bytes32 slot; bool isWrite; bytes32 previousValue; bytes32 newValue; bool reverted; } + struct Gas { uint64 gasLimit; uint64 gasTotalUsed; uint64 gasMemoryUsed; int64 gasRefunded; uint64 gasRemaining; } function _expectCheatcodeRevert() external; function _expectCheatcodeRevert(bytes4 revertData) external; function _expectCheatcodeRevert(bytes calldata revertData) external; @@ -245,6 +246,7 @@ interface Vm { function keyExistsJson(string calldata json, string calldata key) external view returns (bool); function keyExistsToml(string calldata toml, string calldata key) external view returns (bool); function label(address account, string calldata newLabel) external; + function lastCallGas() external view returns (Gas memory gas); function load(address target, bytes32 slot) external view returns (bytes32 data); function loadAllocs(string calldata pathToAllocsJson) external; function makePersistent(address account) external; diff --git a/testdata/default/cheats/LastCallGas.t.sol b/testdata/default/cheats/LastCallGas.t.sol new file mode 100644 index 000000000..ec8c6ba0a --- /dev/null +++ b/testdata/default/cheats/LastCallGas.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"; + +contract Target { + uint256 public slot0; + + function expandMemory(uint256 n) public pure returns (uint256) { + uint256[] memory arr = new uint256[](n); + + for (uint256 i = 0; i < n; i++) { + arr[i] = i; + } + + return arr.length; + } + + function setValue(uint256 value) public { + slot0 = value; + } + + function resetValue() public { + slot0 = 0; + } + + fallback() external {} +} + +abstract contract LastCallGasFixture is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + Target public target; + + struct Gas { + uint64 gasTotalUsed; + uint64 gasMemoryUsed; + int64 gasRefunded; + } + + function testRevertNoCachedLastCallGas() public { + vm.expectRevert(); + vm.lastCallGas(); + } + + function _setup() internal { + // Cannot be set in `setUp` due to `testRevertNoCachedLastCallGas` + // relying on no calls being made before `lastCallGas` is called. + target = new Target(); + } + + function _performCall() internal returns (bool success) { + (success,) = address(target).call(""); + } + + function _performExpandMemory() internal view { + target.expandMemory(1000); + } + + function _performRefund() internal { + target.setValue(1); + target.resetValue(); + } + + function _assertGas(Vm.Gas memory lhs, Gas memory rhs) internal { + assertGt(lhs.gasLimit, 0); + assertGt(lhs.gasRemaining, 0); + assertEq(lhs.gasTotalUsed, rhs.gasTotalUsed); + assertEq(lhs.gasMemoryUsed, rhs.gasMemoryUsed); + assertEq(lhs.gasRefunded, rhs.gasRefunded); + } +} + +contract LastCallGasIsolatedTest is LastCallGasFixture { + function testRecordLastCallGas() public { + _setup(); + _performCall(); + _assertGas(vm.lastCallGas(), Gas({gasTotalUsed: 21064, gasMemoryUsed: 0, gasRefunded: 0})); + + _performCall(); + _assertGas(vm.lastCallGas(), Gas({gasTotalUsed: 21064, gasMemoryUsed: 0, gasRefunded: 0})); + + _performCall(); + _assertGas(vm.lastCallGas(), Gas({gasTotalUsed: 21064, gasMemoryUsed: 0, gasRefunded: 0})); + } + + function testRecordGasMemory() public { + _setup(); + _performExpandMemory(); + _assertGas(vm.lastCallGas(), Gas({gasTotalUsed: 186470, gasMemoryUsed: 4994, gasRefunded: 0})); + } + + function testRecordGasRefund() public { + _setup(); + _performRefund(); + _assertGas(vm.lastCallGas(), Gas({gasTotalUsed: 21380, gasMemoryUsed: 0, gasRefunded: 4800})); + } +} + +// Without isolation mode enabled the gas usage will be incorrect. +contract LastCallGasDefaultTest is LastCallGasFixture { + function testRecordLastCallGas() public { + _setup(); + _performCall(); + _assertGas(vm.lastCallGas(), Gas({gasTotalUsed: 64, gasMemoryUsed: 9, gasRefunded: 0})); + + _performCall(); + _assertGas(vm.lastCallGas(), Gas({gasTotalUsed: 64, gasMemoryUsed: 9, gasRefunded: 0})); + + _performCall(); + _assertGas(vm.lastCallGas(), Gas({gasTotalUsed: 64, gasMemoryUsed: 9, gasRefunded: 0})); + } + + function testRecordGasMemory() public { + _setup(); + _performExpandMemory(); + _assertGas(vm.lastCallGas(), Gas({gasTotalUsed: 186470, gasMemoryUsed: 4994, gasRefunded: 0})); + } + + function testRecordGasRefund() public { + _setup(); + _performRefund(); + _assertGas(vm.lastCallGas(), Gas({gasTotalUsed: 216, gasMemoryUsed: 9, gasRefunded: 19900})); + } +} From 14daacfe40565b19bdfc3b78c6cf1775f54529da Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 9 Apr 2024 01:19:16 +0300 Subject: [PATCH 144/430] fix(invariant): decode custom error with target contract abis (#7559) * fix(invariant): decode custom error with target contract abis * Changes after review: don't collect --- .../evm/evm/src/executors/invariant/error.rs | 16 +++++++-- .../evm/evm/src/executors/invariant/funcs.rs | 4 ++- crates/evm/evm/src/executors/invariant/mod.rs | 5 ++- crates/forge/tests/it/invariant.rs | 25 +++++++++++++ .../common/InvariantCustomError.t.sol | 35 +++++++++++++++++++ 5 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 testdata/default/fuzz/invariant/common/InvariantCustomError.t.sol diff --git a/crates/evm/evm/src/executors/invariant/error.rs b/crates/evm/evm/src/executors/invariant/error.rs index d46d6da2c..98977b539 100644 --- a/crates/evm/evm/src/executors/invariant/error.rs +++ b/crates/evm/evm/src/executors/invariant/error.rs @@ -5,7 +5,9 @@ use alloy_primitives::{Address, Bytes, Log}; use eyre::Result; use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; use foundry_evm_core::{constants::CALLER, decode::RevertDecoder}; -use foundry_evm_fuzz::{BaseCounterExample, CounterExample, FuzzedCases, Reason}; +use foundry_evm_fuzz::{ + invariant::FuzzRunIdentifiedContracts, BaseCounterExample, CounterExample, FuzzedCases, Reason, +}; use foundry_evm_traces::{load_contracts, CallTraceArena, TraceKind, Traces}; use itertools::Itertools; use parking_lot::RwLock; @@ -98,8 +100,10 @@ pub struct FailedInvariantCaseData { } impl FailedInvariantCaseData { + #[allow(clippy::too_many_arguments)] pub fn new( invariant_contract: &InvariantContract<'_>, + targeted_contracts: &FuzzRunIdentifiedContracts, error_func: Option<&Function>, calldata: &[BasicTxDetails], call_result: RawCallResult, @@ -112,8 +116,16 @@ impl FailedInvariantCaseData { } else { (None, "Revert") }; + + // Collect abis of fuzzed and invariant contracts to decode custom error. + let targets = targeted_contracts.lock(); + let abis = targets + .iter() + .map(|contract| &contract.1 .1) + .chain(std::iter::once(invariant_contract.abi)); + let revert_reason = RevertDecoder::new() - .with_abi(invariant_contract.abi) + .with_abis(abis) .decode(call_result.result.as_ref(), Some(call_result.exit_reason)); Self { diff --git a/crates/evm/evm/src/executors/invariant/funcs.rs b/crates/evm/evm/src/executors/invariant/funcs.rs index 218f97e92..daa326b0c 100644 --- a/crates/evm/evm/src/executors/invariant/funcs.rs +++ b/crates/evm/evm/src/executors/invariant/funcs.rs @@ -6,7 +6,7 @@ use alloy_primitives::Log; use foundry_common::{ContractsByAddress, ContractsByArtifact}; use foundry_evm_core::constants::CALLER; use foundry_evm_coverage::HitMaps; -use foundry_evm_fuzz::invariant::{BasicTxDetails, InvariantContract}; +use foundry_evm_fuzz::invariant::{BasicTxDetails, FuzzRunIdentifiedContracts, InvariantContract}; use foundry_evm_traces::{load_contracts, TraceKind, Traces}; use revm::primitives::U256; use std::borrow::Cow; @@ -16,6 +16,7 @@ use std::borrow::Cow; /// Either returns the call result if successful, or nothing if there was an error. pub fn assert_invariants( invariant_contract: &InvariantContract<'_>, + targeted_contracts: &FuzzRunIdentifiedContracts, executor: &Executor, calldata: &[BasicTxDetails], invariant_failures: &mut InvariantFailures, @@ -49,6 +50,7 @@ pub fn assert_invariants( if invariant_failures.error.is_none() { let case_data = FailedInvariantCaseData::new( invariant_contract, + targeted_contracts, Some(func), calldata, call_result, diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 835a2bc87..8f1d3ca57 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -179,6 +179,7 @@ impl<'a> InvariantExecutor<'a> { // This does not count as a fuzz run. It will just register the revert. let last_call_results = RefCell::new(assert_invariants( &invariant_contract, + &targeted_contracts, &self.executor, &[], &mut failures.borrow_mut(), @@ -711,7 +712,7 @@ fn can_continue( !executor.is_success(*contract.0, false, Cow::Borrowed(state_changeset), false) }); - // Assert invariants IFF the call did not revert and the handlers did not fail. + // Assert invariants IF the call did not revert and the handlers did not fail. if !call_result.reverted && !handlers_failed { if let Some(traces) = call_result.traces { run_traces.push(traces); @@ -719,6 +720,7 @@ fn can_continue( call_results = assert_invariants( invariant_contract, + targeted_contracts, executor, calldata, failures, @@ -735,6 +737,7 @@ fn can_continue( if fail_on_revert { let case_data = FailedInvariantCaseData::new( invariant_contract, + targeted_contracts, None, calldata, call_result, diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index 82cf4909e..4de3d3dcc 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -145,6 +145,10 @@ async fn test_invariant() { "default/fuzz/invariant/common/InvariantAssume.t.sol:InvariantAssume", vec![("invariant_dummy()", true, None, None, None)], ), + ( + "default/fuzz/invariant/common/InvariantCustomError.t.sol:InvariantCustomError", + vec![("invariant_decode_error()", true, None, None, None)], + ), ]), ); } @@ -411,3 +415,24 @@ 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 results = runner.test_collect(&filter); + assert_multiple( + &results, + BTreeMap::from([( + "default/fuzz/invariant/common/InvariantCustomError.t.sol:InvariantCustomError", + vec![( + "invariant_decode_error()", + false, + Some("InvariantCustomError(111, \"custom\")".into()), + None, + None, + )], + )]), + ); +} diff --git a/testdata/default/fuzz/invariant/common/InvariantCustomError.t.sol b/testdata/default/fuzz/invariant/common/InvariantCustomError.t.sol new file mode 100644 index 000000000..737cf5ba9 --- /dev/null +++ b/testdata/default/fuzz/invariant/common/InvariantCustomError.t.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.0; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract ContractWithCustomError { + error InvariantCustomError(uint256, string); + + function revertWithInvariantCustomError() external { + revert InvariantCustomError(111, "custom"); + } +} + +contract Handler is DSTest { + ContractWithCustomError target; + + constructor() { + target = new ContractWithCustomError(); + } + + function revertTarget() external { + target.revertWithInvariantCustomError(); + } +} + +contract InvariantCustomError is DSTest { + Handler handler; + + function setUp() external { + handler = new Handler(); + } + + function invariant_decode_error() public {} +} From bbdb034e8d700703534a892c838bf0310372f83c Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 9 Apr 2024 11:44:48 +0200 Subject: [PATCH 145/430] feat: right-align and prettify --sizes output (#7601) --- Cargo.lock | 11 +++++++++++ Cargo.toml | 13 +++++-------- crates/cheatcodes/assets/cheatcodes.json | 12 ++++++------ crates/cheatcodes/spec/src/vm.rs | 11 ++++++----- crates/common/Cargo.toml | 1 + crates/common/src/compile.rs | 12 +++++++++--- 6 files changed, 38 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6e48a7bc3..532cd24cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3275,6 +3275,7 @@ dependencies = [ "foundry-macros", "glob", "globset", + "num-format", "once_cell", "pretty_assertions", "rand 0.8.5", @@ -5219,6 +5220,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-format" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +dependencies = [ + "arrayvec", + "itoa", +] + [[package]] name = "num-integer" version = "0.1.46" diff --git a/Cargo.toml b/Cargo.toml index 661bcb128..ad300a085 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,8 @@ resolver = "2" [workspace.package] version = "0.2.0" edition = "2021" -rust-version = "1.76" # Remember to update clippy.toml as well +# Remember to update clippy.toml as well +rust-version = "1.76" authors = ["Foundry Contributors"] license = "MIT OR Apache-2.0" homepage = "https://github.com/foundry-rs/foundry" @@ -145,9 +146,7 @@ foundry-compilers = { version = "0.3.13", default-features = false } ## revm # no default features to avoid c-kzg revm = { version = "7.1", default-features = false, features = ["std"] } -revm-primitives = { version = "3", default-features = false, features = [ - "std", -] } +revm-primitives = { version = "3", default-features = false, features = ["std"] } revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "ba0b6ab", features = [ "serde", ] } @@ -192,10 +191,7 @@ solang-parser = "=0.3.3" ## misc arrayvec = "0.7" base64 = "0.22" -chrono = { version = "0.4", default-features = false, features = [ - "clock", - "std", -] } +chrono = { version = "0.4", default-features = false, features = ["clock", "std"] } color-eyre = "0.6" derive_more = "0.99" evm-disassembler = "0.5" @@ -216,6 +212,7 @@ tracing-subscriber = "0.3" vergen = { version = "8", default-features = false } indexmap = "2.2" tikv-jemallocator = "0.5.4" +num-format = "0.4.4" axum = "0.6" hyper = "0.14" diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 3b4d071d7..102c810cd 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -418,32 +418,32 @@ }, { "name": "Gas", - "description": "", + "description": "Gas used. Returned by `lastCallGas`.", "fields": [ { "name": "gasLimit", "ty": "uint64", - "description": "" + "description": "The gas limit of the call." }, { "name": "gasTotalUsed", "ty": "uint64", - "description": "" + "description": "The total gas used." }, { "name": "gasMemoryUsed", "ty": "uint64", - "description": "" + "description": "The amount of gas used for memory expansion." }, { "name": "gasRefunded", "ty": "int64", - "description": "" + "description": "The amount of gas refunded." }, { "name": "gasRemaining", "ty": "uint64", - "description": "" + "description": "The amount of gas remaining." } ] } diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index c350690fc..7fb3e2922 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -73,16 +73,17 @@ interface Vm { address emitter; } + /// Gas used. Returned by `lastCallGas`. struct Gas { - // The gas limit of the call. + /// The gas limit of the call. uint64 gasLimit; - // The total gas used. + /// The total gas used. uint64 gasTotalUsed; - // The amount of gas used for memory expansion. + /// The amount of gas used for memory expansion. uint64 gasMemoryUsed; - // The amount of gas refunded. + /// The amount of gas refunded. int64 gasRefunded; - // The amount of gas remaining. + /// The amount of gas remaining. uint64 gasRemaining; } diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index fb161da61..d458786f9 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -59,6 +59,7 @@ url = "2" walkdir = "2" yansi = "0.5" rustc-hash.workspace = true +num-format.workspace = true [dev-dependencies] foundry-macros.workspace = true diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index bba5e15d2..baf73c992 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -1,7 +1,7 @@ //! Support for compiling [foundry_compilers::Project] use crate::{compact_to_contract, glob::GlobMatcher, term::SpinnerReporter, TestFunctionExt}; -use comfy_table::{presets::ASCII_MARKDOWN, Attribute, Cell, Color, Table}; +use comfy_table::{presets::ASCII_MARKDOWN, Attribute, Cell, CellAlignment, Color, Table}; use eyre::{Context, Result}; use foundry_block_explorers::contract::Metadata; use foundry_compilers::{ @@ -12,6 +12,7 @@ use foundry_compilers::{ Solc, SolcConfig, }; use foundry_linking::Linker; +use num_format::{Locale, ToFormattedString}; use rustc_hash::FxHashMap; use std::{ collections::{BTreeMap, HashMap}, @@ -417,10 +418,15 @@ impl Display for SizeReport { _ => Color::Red, }; + let locale = &Locale::en; table.add_row([ Cell::new(name).fg(color), - Cell::new(contract.size).fg(color), - Cell::new(margin).fg(color), + Cell::new(contract.size.to_formatted_string(locale)) + .set_alignment(CellAlignment::Right) + .fg(color), + Cell::new(margin.to_formatted_string(locale)) + .set_alignment(CellAlignment::Right) + .fg(color), ]); } From a5104477dcf483da2f1f9fe7008178db8a51112a Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 9 Apr 2024 15:22:23 +0300 Subject: [PATCH 146/430] fix(invariant): honor targetContract setting, don't update targets if any (#7595) * fix(invariant): respect targetContract setup * Fix test fmt * Check identified contracts after collecting `targetInterfaces` --- .../evm/evm/src/executors/invariant/error.rs | 2 +- crates/evm/evm/src/executors/invariant/mod.rs | 50 +++++++------- crates/evm/fuzz/src/invariant/mod.rs | 18 ++++- crates/evm/fuzz/src/strategies/invariants.rs | 6 +- crates/evm/fuzz/src/strategies/state.rs | 4 +- crates/forge/tests/it/invariant.rs | 35 ++++++++++ .../target/FuzzedTargetContracts.t.sol | 66 +++++++++++++++++++ 7 files changed, 150 insertions(+), 31 deletions(-) create mode 100644 testdata/default/fuzz/invariant/target/FuzzedTargetContracts.t.sol diff --git a/crates/evm/evm/src/executors/invariant/error.rs b/crates/evm/evm/src/executors/invariant/error.rs index 98977b539..0695ad6c4 100644 --- a/crates/evm/evm/src/executors/invariant/error.rs +++ b/crates/evm/evm/src/executors/invariant/error.rs @@ -118,7 +118,7 @@ impl FailedInvariantCaseData { }; // Collect abis of fuzzed and invariant contracts to decode custom error. - let targets = targeted_contracts.lock(); + let targets = targeted_contracts.targets.lock(); let abis = targets .iter() .map(|contract| &contract.1 .1) diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 8f1d3ca57..eb4cecb38 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -23,7 +23,7 @@ use foundry_evm_fuzz::{ FuzzCase, FuzzedCases, }; use foundry_evm_traces::CallTraceArena; -use parking_lot::{Mutex, RwLock}; +use parking_lot::RwLock; use proptest::{ strategy::{BoxedStrategy, Strategy, ValueTree}, test_runner::{TestCaseError, TestRunner}, @@ -249,17 +249,20 @@ impl<'a> InvariantExecutor<'a> { collect_data(&mut state_changeset, sender, &call_result, &fuzz_state); - if let Err(error) = collect_created_contracts( - &state_changeset, - self.project_contracts, - self.setup_contracts, - &self.artifact_filters, - targeted_contracts.clone(), - &mut created_contracts, - ) { - warn!(target: "forge::test", "{error}"); + // Collect created contracts and add to fuzz targets only if targeted contracts + // are updatable. + if targeted_contracts.is_updatable { + if let Err(error) = collect_created_contracts( + &state_changeset, + self.project_contracts, + self.setup_contracts, + &self.artifact_filters, + &targeted_contracts, + &mut created_contracts, + ) { + warn!(target: "forge::test", "{error}"); + } } - // Commit changes to the database. executor.backend.commit(state_changeset.clone()); @@ -309,7 +312,7 @@ impl<'a> InvariantExecutor<'a> { // We clear all the targeted contracts created during this run. if !created_contracts.is_empty() { - let mut writable_targeted = targeted_contracts.lock(); + let mut writable_targeted = targeted_contracts.targets.lock(); for addr in created_contracts.iter() { writable_targeted.remove(addr); } @@ -353,19 +356,10 @@ impl<'a> InvariantExecutor<'a> { let (targeted_senders, targeted_contracts) = self.select_contracts_and_senders(invariant_contract.address)?; - if targeted_contracts.is_empty() { - eyre::bail!("No contracts to fuzz."); - } - // Stores fuzz state for use with [fuzz_calldata_from_state]. let fuzz_state: EvmFuzzState = build_initial_state(self.executor.backend.mem_db(), self.config.dictionary); - // During execution, any newly created contract is added here and used through the rest of - // the fuzz run. - let targeted_contracts: FuzzRunIdentifiedContracts = - Arc::new(Mutex::new(targeted_contracts)); - let calldata_fuzz_config = CalldataFuzzDictionary::new(&self.config.dictionary, &fuzz_state); @@ -500,7 +494,7 @@ impl<'a> InvariantExecutor<'a> { pub fn select_contracts_and_senders( &self, to: Address, - ) -> eyre::Result<(SenderFilters, TargetedContracts)> { + ) -> eyre::Result<(SenderFilters, FuzzRunIdentifiedContracts)> { let targeted_senders = self.call_sol_default(to, &IInvariantTest::targetSendersCall {}).targetedSenders; let excluded_senders = @@ -532,7 +526,15 @@ impl<'a> InvariantExecutor<'a> { self.select_selectors(to, &mut contracts)?; - Ok((SenderFilters::new(targeted_senders, excluded_senders), contracts)) + // There should be at least one contract identified as target for fuzz runs. + if contracts.is_empty() { + eyre::bail!("No contracts to fuzz."); + } + + Ok(( + SenderFilters::new(targeted_senders, excluded_senders), + FuzzRunIdentifiedContracts::new(contracts, selected.is_empty()), + )) } /// Extends the contracts and selectors to fuzz with the addresses and ABIs specified in @@ -708,7 +710,7 @@ fn can_continue( let mut call_results = None; // Detect handler assertion failures first. - let handlers_failed = targeted_contracts.lock().iter().any(|contract| { + let handlers_failed = targeted_contracts.targets.lock().iter().any(|contract| { !executor.is_success(*contract.0, false, Cow::Borrowed(state_changeset), false) }); diff --git a/crates/evm/fuzz/src/invariant/mod.rs b/crates/evm/fuzz/src/invariant/mod.rs index 6dfcd8248..d682041e9 100644 --- a/crates/evm/fuzz/src/invariant/mod.rs +++ b/crates/evm/fuzz/src/invariant/mod.rs @@ -10,7 +10,23 @@ mod filters; pub use filters::{ArtifactFilters, SenderFilters}; pub type TargetedContracts = BTreeMap)>; -pub type FuzzRunIdentifiedContracts = Arc>; + +/// Contracts identified as targets during a fuzz run. +/// During execution, any newly created contract is added as target and used through the rest of +/// the fuzz run if the collection is updatable (no `targetContract` specified in `setUp`). +#[derive(Clone, Debug)] +pub struct FuzzRunIdentifiedContracts { + /// Contracts identified as targets during a fuzz run. + pub targets: Arc>, + /// Whether target contracts are updatable or not. + pub is_updatable: bool, +} + +impl FuzzRunIdentifiedContracts { + pub fn new(targets: TargetedContracts, is_updatable: bool) -> Self { + Self { targets: Arc::new(Mutex::new(targets)), is_updatable } + } +} /// (Sender, (TargetContract, Calldata)) pub type BasicTxDetails = (Address, (Address, Bytes)); diff --git a/crates/evm/fuzz/src/strategies/invariants.rs b/crates/evm/fuzz/src/strategies/invariants.rs index c98e84598..137e70852 100644 --- a/crates/evm/fuzz/src/strategies/invariants.rs +++ b/crates/evm/fuzz/src/strategies/invariants.rs @@ -16,7 +16,7 @@ pub fn override_call_strat( target: Arc>, calldata_fuzz_config: CalldataFuzzDictionary, ) -> SBoxedStrategy<(Address, Bytes)> { - let contracts_ref = contracts.clone(); + let contracts_ref = contracts.targets.clone(); proptest::prop_oneof![ 80 => proptest::strategy::LazyJust::new(move || *target.read()), 20 => any::() @@ -27,7 +27,7 @@ pub fn override_call_strat( let calldata_fuzz_config = calldata_fuzz_config.clone(); let func = { - let contracts = contracts.lock(); + let contracts = contracts.targets.lock(); let (_, abi, functions) = contracts.get(&target_address).unwrap_or_else(|| { // Choose a random contract if target selected by lazy strategy is not in fuzz run // identified contracts. This can happen when contract is created in `setUp` call @@ -81,7 +81,7 @@ fn generate_call( any::() .prop_flat_map(move |selector| { let (contract, func) = { - let contracts = contracts.lock(); + let contracts = contracts.targets.lock(); let contracts = contracts.iter().filter(|(_, (_, abi, _))| !abi.functions.is_empty()); let (&contract, (_, abi, functions)) = selector.select(contracts); diff --git a/crates/evm/fuzz/src/strategies/state.rs b/crates/evm/fuzz/src/strategies/state.rs index 3c8f490f3..3a3b17ed6 100644 --- a/crates/evm/fuzz/src/strategies/state.rs +++ b/crates/evm/fuzz/src/strategies/state.rs @@ -304,10 +304,10 @@ pub fn collect_created_contracts( project_contracts: &ContractsByArtifact, setup_contracts: &ContractsByAddress, artifact_filters: &ArtifactFilters, - targeted_contracts: FuzzRunIdentifiedContracts, + targeted_contracts: &FuzzRunIdentifiedContracts, created_contracts: &mut Vec
, ) -> eyre::Result<()> { - let mut writable_targeted = targeted_contracts.lock(); + let mut writable_targeted = targeted_contracts.targets.lock(); for (address, account) in state_changeset { if !setup_contracts.contains_key(address) { if let (true, Some(code)) = (&account.is_touched(), &account.info.code) { diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index 4de3d3dcc..49cbce5db 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -149,6 +149,14 @@ async fn test_invariant() { "default/fuzz/invariant/common/InvariantCustomError.t.sol:InvariantCustomError", vec![("invariant_decode_error()", true, None, None, None)], ), + ( + "default/fuzz/invariant/target/FuzzedTargetContracts.t.sol:ExplicitTargetContract", + vec![("invariant_explicit_target()", true, None, None, None)], + ), + ( + "default/fuzz/invariant/target/FuzzedTargetContracts.t.sol:DynamicTargetContract", + vec![("invariant_dynamic_targets()", true, None, None, None)], + ), ]), ); } @@ -436,3 +444,30 @@ 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 results = runner.test_collect(&filter); + assert_multiple( + &results, + BTreeMap::from([ + ( + "default/fuzz/invariant/target/FuzzedTargetContracts.t.sol:ExplicitTargetContract", + vec![("invariant_explicit_target()", true, None, None, None)], + ), + ( + "default/fuzz/invariant/target/FuzzedTargetContracts.t.sol:DynamicTargetContract", + vec![( + "invariant_dynamic_targets()", + false, + Some("revert: wrong target selector called".into()), + None, + None, + )], + ), + ]), + ); +} diff --git a/testdata/default/fuzz/invariant/target/FuzzedTargetContracts.t.sol b/testdata/default/fuzz/invariant/target/FuzzedTargetContracts.t.sol new file mode 100644 index 000000000..7988d5c8a --- /dev/null +++ b/testdata/default/fuzz/invariant/target/FuzzedTargetContracts.t.sol @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; + +interface Vm { + function etch(address target, bytes calldata newRuntimeBytecode) external; +} + +// https://github.com/foundry-rs/foundry/issues/5625 +// https://github.com/foundry-rs/foundry/issues/6166 +// `Target.wrongSelector` is not called when handler added as `targetContract` +// `Target.wrongSelector` is called (and test fails) when no `targetContract` set +contract Target { + uint256 count; + + function wrongSelector() external { + revert("wrong target selector called"); + } + + function goodSelector() external { + count++; + } +} + +contract Handler is DSTest { + function increment() public { + Target(0x6B175474E89094C44Da98b954EedeAC495271d0F).goodSelector(); + } +} + +contract ExplicitTargetContract is DSTest { + Vm vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + Handler handler; + + function setUp() public { + Target target = new Target(); + bytes memory targetCode = address(target).code; + vm.etch(address(0x6B175474E89094C44Da98b954EedeAC495271d0F), targetCode); + + handler = new Handler(); + } + + function targetContracts() public returns (address[] memory) { + address[] memory addrs = new address[](1); + addrs[0] = address(handler); + return addrs; + } + + function invariant_explicit_target() public {} +} + +contract DynamicTargetContract is DSTest { + Vm vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + Handler handler; + + function setUp() public { + Target target = new Target(); + bytes memory targetCode = address(target).code; + vm.etch(address(0x6B175474E89094C44Da98b954EedeAC495271d0F), targetCode); + + handler = new Handler(); + } + + function invariant_dynamic_targets() public {} +} From a6d6a3a8f0442adb0162082fdd8e5aaa69287c80 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 9 Apr 2024 16:21:20 +0300 Subject: [PATCH 147/430] feat(cheatcodes): forge execution context check (#7377) * feat(cheatcodes): forge execution context check * Add tests for test and snapshot contexts * Add isTestContext, isScriptContext cheatcodes * Add script dry run and broadcast tests * Proper enum in cheatcodes schema, alphabetical order * Single isContext cheatcode in env group, taking enum as param. remove context group * Changes after review: remove discriminant calls, use OnceLock * Review changes: tests should not be async * Review changes: implement PartialEq for ForgeContext, remove is_forge_context fn * Properly add new ForgeContext enum --- crates/cheatcodes/assets/cheatcodes.json | 62 ++++++++++++++++++ crates/cheatcodes/spec/src/lib.rs | 1 + crates/cheatcodes/spec/src/vm.rs | 54 ++++++++++++++++ crates/cheatcodes/src/env.rs | 18 +++++- crates/cheatcodes/src/lib.rs | 2 + crates/forge/bin/main.rs | 25 ++++++++ crates/forge/tests/cli/context.rs | 81 ++++++++++++++++++++++++ crates/forge/tests/cli/main.rs | 1 + testdata/cheats/Vm.sol | 2 + 9 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 crates/forge/tests/cli/context.rs diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 102c810cd..26d9dd2e1 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -83,6 +83,48 @@ "description": "The account's code was copied." } ] + }, + { + "name": "ForgeContext", + "description": "Forge execution contexts.", + "variants": [ + { + "name": "TestGroup", + "description": "Test group execution context (test, coverage or snapshot)." + }, + { + "name": "Test", + "description": "`forge test` execution context." + }, + { + "name": "Coverage", + "description": "`forge coverage` execution context." + }, + { + "name": "Snapshot", + "description": "`forge snapshot` execution context." + }, + { + "name": "ScriptGroup", + "description": "Script group execution context (dry run, broadcast or resume)." + }, + { + "name": "ScriptDryRun", + "description": "`forge script` execution context." + }, + { + "name": "ScriptBroadcast", + "description": "`forge script --broadcast` execution context." + }, + { + "name": "ScriptResume", + "description": "`forge script --resume` execution context." + }, + { + "name": "Unknown", + "description": "Unknown `forge` execution context." + } + ] } ], "structs": [ @@ -4849,6 +4891,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "isContext", + "description": "Returns true if `forge` command was executed in given context.", + "declaration": "function isContext(ForgeContext context) external view returns (bool isContext);", + "visibility": "external", + "mutability": "view", + "signature": "isContext(uint8)", + "selector": "0x64af255d", + "selectorBytes": [ + 100, + 175, + 37, + 93 + ] + }, + "group": "environment", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "isDir", diff --git a/crates/cheatcodes/spec/src/lib.rs b/crates/cheatcodes/spec/src/lib.rs index 16bb60834..b2a267f8d 100644 --- a/crates/cheatcodes/spec/src/lib.rs +++ b/crates/cheatcodes/spec/src/lib.rs @@ -88,6 +88,7 @@ impl Cheatcodes<'static> { enums: Cow::Owned(vec![ Vm::CallerMode::ENUM.clone(), Vm::AccountAccessKind::ENUM.clone(), + Vm::ForgeContext::ENUM.clone(), ]), errors: Vm::VM_ERRORS.iter().map(|&x| x.clone()).collect(), events: Cow::Borrowed(&[]), diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 7fb3e2922..e7ac87da1 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -3,6 +3,7 @@ #![allow(missing_docs)] use super::*; +use crate::Vm::ForgeContext; use alloy_sol_types::sol; use foundry_macros::Cheatcode; @@ -63,6 +64,28 @@ interface Vm { Extcodecopy, } + /// Forge execution contexts. + enum ForgeContext { + /// Test group execution context (test, coverage or snapshot). + TestGroup, + /// `forge test` execution context. + Test, + /// `forge coverage` execution context. + Coverage, + /// `forge snapshot` execution context. + Snapshot, + /// Script group execution context (dry run, broadcast or resume). + ScriptGroup, + /// `forge script` execution context. + ScriptDryRun, + /// `forge script --broadcast` execution context. + ScriptBroadcast, + /// `forge script --resume` execution context. + ScriptResume, + /// Unknown `forge` execution context. + Unknown, + } + /// An Ethereum log. Returned by `getRecordedLogs`. struct Log { /// The topics of the log, including the signature, if any. @@ -1598,6 +1621,10 @@ interface Vm { external view returns (bytes[] memory value); + /// Returns true if `forge` command was executed in given context. + #[cheatcode(group = Environment)] + function isContext(ForgeContext context) external view returns (bool isContext); + // ======== Scripts ======== // -------- Broadcasting Transactions -------- @@ -2065,3 +2092,30 @@ interface Vm { function toBase64URL(string calldata data) external pure returns (string memory); } } + +impl PartialEq for ForgeContext { + // Handles test group case (any of test, coverage or snapshot) + // and script group case (any of dry run, broadcast or resume). + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (_, &ForgeContext::TestGroup) => { + self == &ForgeContext::Test || + self == &ForgeContext::Snapshot || + self == &ForgeContext::Coverage + } + (_, &ForgeContext::ScriptGroup) => { + self == &ForgeContext::ScriptDryRun || + self == &ForgeContext::ScriptBroadcast || + self == &ForgeContext::ScriptResume + } + (&ForgeContext::Test, &ForgeContext::Test) | + (&ForgeContext::Snapshot, &ForgeContext::Snapshot) | + (&ForgeContext::Coverage, &ForgeContext::Coverage) | + (&ForgeContext::ScriptDryRun, &ForgeContext::ScriptDryRun) | + (&ForgeContext::ScriptBroadcast, &ForgeContext::ScriptBroadcast) | + (&ForgeContext::ScriptResume, &ForgeContext::ScriptResume) | + (&ForgeContext::Unknown, &ForgeContext::Unknown) => true, + _ => false, + } + } +} diff --git a/crates/cheatcodes/src/env.rs b/crates/cheatcodes/src/env.rs index d6aaea149..231b47c97 100644 --- a/crates/cheatcodes/src/env.rs +++ b/crates/cheatcodes/src/env.rs @@ -4,7 +4,10 @@ use crate::{string, Cheatcode, Cheatcodes, Error, Result, Vm::*}; use alloy_dyn_abi::DynSolType; use alloy_primitives::Bytes; use alloy_sol_types::SolValue; -use std::env; +use std::{env, sync::OnceLock}; + +/// Stores the forge execution context for the duration of the program. +static FORGE_CONTEXT: OnceLock = OnceLock::new(); impl Cheatcode for setEnvCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { @@ -235,6 +238,19 @@ impl Cheatcode for envOr_13Call { } } +impl Cheatcode for isContextCall { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { context } = self; + Ok((FORGE_CONTEXT.get() == Some(context)).abi_encode()) + } +} + +/// Set `forge` command current execution context for the duration of the program. +/// Execution context is immutable, subsequent calls of this function won't change the context. +pub fn set_execution_context(context: ForgeContext) { + let _ = FORGE_CONTEXT.set(context); +} + fn env(key: &str, ty: &DynSolType) -> Result { get_env(key).and_then(|val| string::parse(&val, ty).map_err(map_env_err(key, &val))) } diff --git a/crates/cheatcodes/src/lib.rs b/crates/cheatcodes/src/lib.rs index 2fa866b1a..01695fa70 100644 --- a/crates/cheatcodes/src/lib.rs +++ b/crates/cheatcodes/src/lib.rs @@ -34,8 +34,10 @@ mod test; mod toml; mod utils; +pub use env::set_execution_context; pub use script::ScriptWallets; pub use test::expect::ExpectedCallTracker; +pub use Vm::ForgeContext; /// Cheatcode implementation. pub(crate) trait Cheatcode: CheatcodeDef + DynCheatcode { diff --git a/crates/forge/bin/main.rs b/crates/forge/bin/main.rs index b5ba3228f..acbe80d81 100644 --- a/crates/forge/bin/main.rs +++ b/crates/forge/bin/main.rs @@ -5,6 +5,7 @@ use clap::{CommandFactory, Parser}; use clap_complete::generate; use eyre::Result; use foundry_cli::{handler, utils}; +use foundry_evm::inspectors::cheatcodes::{set_execution_context, ForgeContext}; mod cmd; use cmd::{cache::CacheSubcommands, generate::GenerateSubcommands, watch}; @@ -23,6 +24,8 @@ fn main() -> Result<()> { utils::enable_paint(); let opts = Forge::parse(); + init_execution_context(&opts.cmd); + match opts.cmd { ForgeSubcommand::Test(cmd) => { if cmd.is_watch() { @@ -107,3 +110,25 @@ fn main() -> Result<()> { }, } } + +/// Set the program execution context based on `forge` subcommand used. +/// The execution context can be set only once per program, and it can be checked by using +/// cheatcodes. +fn init_execution_context(subcommand: &ForgeSubcommand) { + let context = match subcommand { + ForgeSubcommand::Test(_) => ForgeContext::Test, + ForgeSubcommand::Coverage(_) => ForgeContext::Coverage, + ForgeSubcommand::Snapshot(_) => ForgeContext::Snapshot, + ForgeSubcommand::Script(cmd) => { + if cmd.broadcast { + ForgeContext::ScriptBroadcast + } else if cmd.resume { + ForgeContext::ScriptResume + } else { + ForgeContext::ScriptDryRun + } + } + _ => ForgeContext::Unknown, + }; + set_execution_context(context); +} diff --git a/crates/forge/tests/cli/context.rs b/crates/forge/tests/cli/context.rs new file mode 100644 index 000000000..34a7598a3 --- /dev/null +++ b/crates/forge/tests/cli/context.rs @@ -0,0 +1,81 @@ +//! Contains tests for checking forge execution context cheatcodes +const FORGE_TEST_CONTEXT_CONTRACT: &str = r#" +import "./test.sol"; +interface Vm { + enum ForgeContext { TestGroup, Test, Coverage, Snapshot, ScriptGroup, ScriptDryRun, ScriptBroadcast, ScriptResume, Unknown } + function isContext(ForgeContext context) external view returns (bool isContext); +} + +contract ForgeContextTest is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + function testForgeTestContext() external view { + require(vm.isContext(Vm.ForgeContext.TestGroup) && !vm.isContext(Vm.ForgeContext.ScriptGroup), "wrong context"); + require(vm.isContext(Vm.ForgeContext.Test), "wrong context"); + require(!vm.isContext(Vm.ForgeContext.Coverage), "wrong context"); + require(!vm.isContext(Vm.ForgeContext.Snapshot), "wrong context"); + } + function testForgeSnapshotContext() external view { + require(vm.isContext(Vm.ForgeContext.TestGroup) && !vm.isContext(Vm.ForgeContext.ScriptGroup), "wrong context"); + require(vm.isContext(Vm.ForgeContext.Snapshot), "wrong context"); + require(!vm.isContext(Vm.ForgeContext.Test), "wrong context"); + require(!vm.isContext(Vm.ForgeContext.Coverage), "wrong context"); + } + function testForgeCoverageContext() external view { + require(vm.isContext(Vm.ForgeContext.TestGroup) && !vm.isContext(Vm.ForgeContext.ScriptGroup), "wrong context"); + require(vm.isContext(Vm.ForgeContext.Coverage), "wrong context"); + require(!vm.isContext(Vm.ForgeContext.Test), "wrong context"); + require(!vm.isContext(Vm.ForgeContext.Snapshot), "wrong context"); + } + + function runDryRun() external view { + require(vm.isContext(Vm.ForgeContext.ScriptGroup) && !vm.isContext(Vm.ForgeContext.TestGroup), "wrong context"); + require(vm.isContext(Vm.ForgeContext.ScriptDryRun), "wrong context"); + require(!vm.isContext(Vm.ForgeContext.ScriptBroadcast), "wrong context"); + require(!vm.isContext(Vm.ForgeContext.ScriptResume), "wrong context"); + } + function runBroadcast() external view { + require(vm.isContext(Vm.ForgeContext.ScriptGroup) && !vm.isContext(Vm.ForgeContext.TestGroup), "wrong context"); + require(vm.isContext(Vm.ForgeContext.ScriptBroadcast), "wrong context"); + require(!vm.isContext(Vm.ForgeContext.ScriptDryRun), "wrong context"); + require(!vm.isContext(Vm.ForgeContext.ScriptResume), "wrong context"); + } +} + "#; + +// tests that context properly set for `forge test` command +forgetest!(can_set_forge_test_standard_context, |prj, cmd| { + prj.insert_ds_test(); + prj.add_source("ForgeContextTest.t.sol", FORGE_TEST_CONTEXT_CONTRACT).unwrap(); + cmd.args(["test", "--match-test", "testForgeTestContext"]).assert_success(); +}); + +// tests that context properly set for `forge snapshot` command +forgetest!(can_set_forge_test_snapshot_context, |prj, cmd| { + prj.insert_ds_test(); + prj.add_source("ForgeContextTest.t.sol", FORGE_TEST_CONTEXT_CONTRACT).unwrap(); + cmd.args(["snapshot", "--match-test", "testForgeSnapshotContext"]).assert_success(); +}); + +// tests that context properly set for `forge coverage` command +forgetest!(can_set_forge_test_coverage_context, |prj, cmd| { + prj.insert_ds_test(); + prj.add_source("ForgeContextTest.t.sol", FORGE_TEST_CONTEXT_CONTRACT).unwrap(); + cmd.args(["coverage", "--match-test", "testForgeCoverageContext"]).assert_success(); +}); + +// tests that context properly set for `forge script` command +forgetest!(can_set_forge_script_dry_run_context, |prj, cmd| { + prj.insert_ds_test(); + let script = + prj.add_source("ForgeScriptContextTest.s.sol", FORGE_TEST_CONTEXT_CONTRACT).unwrap(); + cmd.arg("script").arg(script).args(["--sig", "runDryRun()"]).assert_success(); +}); + +// tests that context properly set for `forge script --broadcast` command +forgetest!(can_set_forge_script_broadcast_context, |prj, cmd| { + prj.insert_ds_test(); + let script = + prj.add_source("ForgeScriptContextTest.s.sol", FORGE_TEST_CONTEXT_CONTRACT).unwrap(); + cmd.arg("script").arg(script).args(["--broadcast", "--sig", "runBroadcast()"]).assert_success(); +}); diff --git a/crates/forge/tests/cli/main.rs b/crates/forge/tests/cli/main.rs index e9dab70f7..543b84dc7 100644 --- a/crates/forge/tests/cli/main.rs +++ b/crates/forge/tests/cli/main.rs @@ -8,6 +8,7 @@ mod build; mod cache; mod cmd; mod config; +mod context; mod coverage; mod create; mod debug; diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 1d7c81973..8f799c9f6 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -8,6 +8,7 @@ pragma experimental ABIEncoderV2; interface Vm { enum CallerMode { None, Broadcast, RecurrentBroadcast, Prank, RecurrentPrank } enum AccountAccessKind { Call, DelegateCall, CallCode, StaticCall, Create, SelfDestruct, Resume, Balance, Extcodesize, Extcodehash, Extcodecopy } + enum ForgeContext { TestGroup, Test, Coverage, Snapshot, ScriptGroup, ScriptDryRun, ScriptBroadcast, ScriptResume, Unknown } struct Log { bytes32[] topics; bytes data; address emitter; } struct Rpc { string key; string url; } struct EthGetLogs { address emitter; bytes32[] topics; bytes data; bytes32 blockHash; uint64 blockNumber; bytes32 transactionHash; uint64 transactionIndex; uint256 logIndex; bool removed; } @@ -239,6 +240,7 @@ interface Vm { function getNonce(Wallet calldata wallet) external returns (uint64 nonce); function getRecordedLogs() external returns (Log[] memory logs); function indexOf(string memory input, string memory key) external pure returns (uint256); + function isContext(ForgeContext context) external view returns (bool isContext); function isDir(string calldata path) external returns (bool result); function isFile(string calldata path) external returns (bool result); function isPersistent(address account) external view returns (bool persistent); From f840dbd930f8d7765652ba671a997a414a4bae78 Mon Sep 17 00:00:00 2001 From: evalir Date: Tue, 9 Apr 2024 22:52:53 +0200 Subject: [PATCH 148/430] [wip] feat: provider alloy migration (#7106) * chore: make cast use an alloy provider * move initial methods to alloy * feat(`foundry-common`): NameOrAddress ENS util (#7122) * feat(foundry-common): NameOrAddress ENS util * chore: rename err * chore: remove from impl for str * chore: unrelated fix from alloy upgrade * nit * feat(`cast`): Move non `tx` methods to alloy (#7129) * chore: add alloy contract * feat(cast): migrate most methods to alloy * chore: leave todo for converting a tx envelope into an rpc tx * fix: use proper type for storage * readd decodetx for now * chore: extend txbuilder to build an alloy tx request * feat: migrate most methods bar send/decode raw tx * fix: include tx data * simplify txbuilder * chore: simplify back access_list * chore: remove unnecesary conversion * fmt * doctests * fmt * do not use trait * Update crates/cast/bin/main.rs Co-authored-by: Matthias Seitz * cleanup builder * clippy * fix doc comments --------- Co-authored-by: Matthias Seitz * DocumentMut * wip * wip * wip: bump alloy * wip * wip * wip * [wip] migrate to alloy providers and signers (#7425) wip * fix wallets after alloy bump * clean up deps * use serde on consensus types * update TypedTransaction for anvil * make anvil compile * wip: make script compile * fix script * make forge compile * fix: anvil tests * bump alloy * fix tests * fix tx builder * fix cargo.toml * fix cargo.toml * fix script gas price logic * remove ethers from anvil * clippy * rm all_derives * deps * fmt * fix tests * configure clippy * clippy * add feature * fix cargo deny * fix persist * fix doctests * fmt * fix clap * review fixes * fmt * bump alloy * Update cargo.toml * fmt * fixes * ethers clean-up * fix(fmt): fix indent closing parenthesis enclosed in { } (#7557) * fix(fmt): fix indent closing parenthesis enclosed in { } * Fix testdata bad formatting * feat(test): only compile files needed for tests (#7334) * feat(forge test): only compile files needed for tests * remove comment * clippy * update fixtures * getCode + getDeployedCode updates * fixes * fix path matching * clippy * add config flag * fix * docs * fmt * patch compilers * fix Cargo.toml * update patch * update patch * doc * rm space * cargo cheats * new output selection fn * log compiler errors on failure * fixes * fix: do not flood dictionary with data dependent on fuzz inputs (#7552) * fix dictionary * clippy + fmt * fix * Feat: Index cheatcode for Strings (#7539) * feat: index cheatcode * some nits to make it work * nit: use as_str() * final changes * chore: reviewed changes * chore: reduce logs in tests (#7566) * fix(script): decode custom error in script fail message (#7563) * clippy * bump alloy * AnyNetwork * bump alloy * add comment * clippy * bump alloy * fixes * refactor cast logs to use alloy (#7594) * refactor cast logs to use alloy * fmt * make clippy happy * cleanup * doc nits --------- Co-authored-by: evalir --------- Co-authored-by: Matthias Seitz Co-authored-by: Arsenii Kulikov Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Krishang <93703995+kamuik16@users.noreply.github.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: bernard-wagner --- Cargo.lock | 1108 +++++++++++------ Cargo.toml | 65 +- clippy.toml | 3 + crates/anvil/Cargo.toml | 21 +- crates/anvil/core/Cargo.toml | 10 +- crates/anvil/core/src/eth/block.rs | 37 +- crates/anvil/core/src/eth/mod.rs | 18 +- crates/anvil/core/src/eth/transaction/mod.rs | 739 ++++++----- .../core/src/eth/transaction/optimism.rs | 131 +- crates/anvil/core/src/types.rs | 6 +- crates/anvil/src/cmd.rs | 14 +- crates/anvil/src/config.rs | 66 +- crates/anvil/src/eth/api.rs | 222 ++-- crates/anvil/src/eth/backend/executor.rs | 103 +- crates/anvil/src/eth/backend/fork.rs | 107 +- crates/anvil/src/eth/backend/mem/mod.rs | 420 +++---- crates/anvil/src/eth/backend/mem/storage.rs | 25 +- crates/anvil/src/eth/error.rs | 9 +- crates/anvil/src/eth/fees.rs | 135 +- crates/anvil/src/eth/otterscan/api.rs | 12 +- crates/anvil/src/eth/otterscan/types.rs | 34 +- crates/anvil/src/eth/pool/transactions.rs | 10 +- crates/anvil/src/eth/sign.rs | 18 +- crates/anvil/src/lib.rs | 4 +- crates/anvil/src/pubsub.rs | 50 +- crates/anvil/src/tasks/mod.rs | 67 +- crates/anvil/tests/it/anvil_api.rs | 16 +- crates/anvil/tests/it/api.rs | 12 +- crates/anvil/tests/it/fork.rs | 57 +- crates/anvil/tests/it/gas.rs | 27 +- crates/anvil/tests/it/genesis.rs | 6 +- crates/anvil/tests/it/otterscan.rs | 11 +- crates/anvil/tests/it/transaction.rs | 44 +- crates/anvil/tests/it/wsapi.rs | 2 +- crates/cast/Cargo.toml | 28 +- crates/cast/bin/cmd/access_list.rs | 91 +- crates/cast/bin/cmd/call.rs | 141 +-- crates/cast/bin/cmd/estimate.rs | 42 +- crates/cast/bin/cmd/find_block.rs | 34 +- crates/cast/bin/cmd/logs.rs | 278 ++--- crates/cast/bin/cmd/mktx.rs | 30 +- crates/cast/bin/cmd/run.rs | 21 +- crates/cast/bin/cmd/send.rs | 66 +- crates/cast/bin/cmd/storage.rs | 43 +- crates/cast/bin/cmd/wallet/list.rs | 4 +- crates/cast/bin/cmd/wallet/mod.rs | 26 +- crates/cast/bin/cmd/wallet/vanity.rs | 3 +- crates/cast/bin/main.rs | 74 +- crates/cast/bin/opts.rs | 27 +- crates/cast/bin/tx.rs | 97 +- crates/cast/src/lib.rs | 353 +++--- crates/cast/src/tx.rs | 402 ------ crates/cheatcodes/Cargo.toml | 5 +- crates/cheatcodes/src/env.rs | 3 +- crates/cheatcodes/src/error.rs | 3 +- crates/cheatcodes/src/evm.rs | 10 +- crates/cheatcodes/src/evm/fork.rs | 38 +- crates/cheatcodes/src/evm/mock.rs | 4 +- crates/cheatcodes/src/fs.rs | 9 +- crates/cheatcodes/src/inspector.rs | 51 +- crates/cheatcodes/src/script.rs | 2 +- crates/cheatcodes/src/test/expect.rs | 6 +- crates/cheatcodes/src/utils.rs | 24 +- crates/cli/Cargo.toml | 12 +- crates/cli/src/opts/transaction.rs | 4 +- crates/cli/src/utils/abi.rs | 61 + crates/cli/src/utils/mod.rs | 71 +- crates/common/Cargo.toml | 19 +- crates/common/src/abi.rs | 2 +- crates/common/src/constants.rs | 2 +- crates/common/src/ens.rs | 193 +++ crates/common/src/fmt/ui.rs | 295 +++-- crates/common/src/lib.rs | 1 + crates/common/src/provider/alloy.rs | 28 +- crates/common/src/provider/ethers.rs | 40 - crates/common/src/provider/retry.rs | 3 + crates/common/src/runtime_client.rs | 8 +- crates/common/src/transactions.rs | 84 +- crates/common/src/types.rs | 96 +- crates/config/Cargo.toml | 2 +- crates/config/src/fix.rs | 8 +- crates/config/src/lib.rs | 6 +- crates/config/src/utils.rs | 6 +- crates/evm/core/Cargo.toml | 10 +- crates/evm/core/src/backend/cow.rs | 6 +- crates/evm/core/src/backend/mod.rs | 56 +- crates/evm/core/src/fork/backend.rs | 59 +- crates/evm/core/src/fork/init.rs | 23 +- crates/evm/core/src/fork/multi.rs | 10 +- crates/evm/core/src/opts.rs | 6 +- crates/evm/core/src/utils.rs | 11 +- crates/evm/evm/src/inspectors/stack.rs | 4 +- crates/evm/fuzz/src/strategies/state.rs | 6 +- crates/forge/Cargo.toml | 15 +- crates/forge/bin/cmd/create.rs | 261 ++-- crates/forge/tests/cli/utils.rs | 5 +- crates/forge/tests/it/invariant.rs | 4 +- crates/forge/tests/it/repros.rs | 32 +- crates/script/Cargo.toml | 15 +- crates/script/src/broadcast.rs | 162 ++- crates/script/src/build.rs | 11 +- crates/script/src/execute.rs | 13 +- crates/script/src/lib.rs | 14 +- crates/script/src/providers.rs | 36 +- crates/script/src/receipts.rs | 78 +- crates/script/src/sequence.rs | 27 +- crates/script/src/simulate.rs | 34 +- crates/script/src/transaction.rs | 260 +--- crates/test-utils/Cargo.toml | 4 +- crates/test-utils/src/script.rs | 47 +- crates/verify/Cargo.toml | 2 +- crates/verify/src/etherscan/mod.rs | 15 +- crates/wallets/Cargo.toml | 22 +- crates/wallets/src/error.rs | 8 +- crates/wallets/src/multi_wallet.rs | 16 +- crates/wallets/src/utils.rs | 42 +- crates/wallets/src/wallet.rs | 11 +- crates/wallets/src/wallet_signer.rs | 133 +- 118 files changed, 3842 insertions(+), 4081 deletions(-) delete mode 100644 crates/cast/src/tx.rs create mode 100644 crates/cli/src/utils/abi.rs create mode 100644 crates/common/src/ens.rs diff --git a/Cargo.lock b/Cargo.lock index 532cd24cf..770e28567 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,22 +79,41 @@ dependencies = [ [[package]] name = "alloy-consensus" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ "alloy-eips", - "alloy-network", "alloy-primitives", "alloy-rlp", + "alloy-serde", "c-kzg", - "sha2 0.10.8", + "serde", + "sha2", + "thiserror", +] + +[[package]] +name = "alloy-contract" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +dependencies = [ + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-network", + "alloy-primitives", + "alloy-provider", + "alloy-rpc-types", + "alloy-sol-types", + "alloy-transport", + "futures", + "futures-util", "thiserror", ] [[package]] name = "alloy-dyn-abi" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2919acdad13336bc5dc26b636cdd6892c2f27fb0d4a58320a00c2713cf6a4e9a" +checksum = "872f239c15befa27cc4f0d3d82a70b3365c2d0202562bf906eb93b299fa31882" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -114,29 +133,32 @@ dependencies = [ [[package]] name = "alloy-eips" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ "alloy-primitives", "alloy-rlp", + "alloy-serde", + "c-kzg", + "derive_more", + "once_cell", "serde", - "thiserror", ] [[package]] name = "alloy-genesis" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ "alloy-primitives", - "alloy-rpc-types", + "alloy-serde", "serde", ] [[package]] name = "alloy-json-abi" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ed0f2a6c3a1c947b4508522a53a190dba8f94dcd4e3e1a5af945a498e78f2f" +checksum = "83a35ddfd27576474322a5869e4c123e5f3e7b2177297c18e4e82ea501cb125b" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -147,7 +169,7 @@ dependencies = [ [[package]] name = "alloy-json-rpc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ "alloy-primitives", "serde", @@ -158,20 +180,24 @@ dependencies = [ [[package]] name = "alloy-network" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ + "alloy-consensus", "alloy-eips", "alloy-json-rpc", "alloy-primitives", - "alloy-rlp", - "serde", + "alloy-rpc-types", + "alloy-signer", + "async-trait", + "futures-utils-wasm", + "thiserror", ] [[package]] name = "alloy-primitives" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "600d34d8de81e23b6d909c094e23b3d357e01ca36b78a8c5424c501eedbe86f0" +checksum = "99bbad0a6b588ef4aec1b5ddbbfdacd9ef04e00b979617765b03174318ee1f3a" dependencies = [ "alloy-rlp", "arbitrary", @@ -195,25 +221,27 @@ dependencies = [ ] [[package]] -name = "alloy-providers" +name = "alloy-provider" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ + "alloy-eips", + "alloy-json-rpc", "alloy-network", "alloy-primitives", + "alloy-pubsub", "alloy-rpc-client", - "alloy-rpc-trace-types", "alloy-rpc-types", + "alloy-rpc-types-trace", "alloy-transport", - "alloy-transport-http", "async-stream", "async-trait", "auto_impl", + "dashmap", "futures", + "futures-utils-wasm", "lru", - "reqwest 0.11.27", - "serde", - "thiserror", + "serde_json", "tokio", "tracing", ] @@ -221,7 +249,7 @@ dependencies = [ [[package]] name = "alloy-pubsub" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -255,63 +283,144 @@ checksum = "1a047897373be4bbb0224c1afdabca92648dc57a9c9ef6e7b0be3aff7a859c83" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] name = "alloy-rpc-client" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ "alloy-json-rpc", + "alloy-primitives", + "alloy-pubsub", "alloy-transport", "alloy-transport-http", "futures", "pin-project", - "reqwest 0.11.27", "serde", "serde_json", "tokio", "tokio-stream", "tower", "tracing", - "url", ] [[package]] -name = "alloy-rpc-trace-types" +name = "alloy-rpc-types" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-genesis", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "alloy-sol-types", + "itertools 0.12.1", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "alloy-rpc-types-trace" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ "alloy-primitives", "alloy-rpc-types", + "alloy-serde", "serde", "serde_json", ] [[package]] -name = "alloy-rpc-types" +name = "alloy-serde" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ "alloy-primitives", - "alloy-rlp", - "itertools 0.12.1", "serde", "serde_json", - "thiserror", ] [[package]] name = "alloy-signer" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ - "alloy-network", + "alloy-dyn-abi", "alloy-primitives", "alloy-sol-types", "async-trait", "auto_impl", + "elliptic-curve", + "k256", + "thiserror", +] + +[[package]] +name = "alloy-signer-aws" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +dependencies = [ + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "async-trait", + "aws-sdk-kms", + "k256", + "spki", + "thiserror", + "tracing", +] + +[[package]] +name = "alloy-signer-ledger" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +dependencies = [ + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "async-trait", + "coins-ledger", + "futures-util", + "semver 1.0.22", + "thiserror", + "tracing", +] + +[[package]] +name = "alloy-signer-trezor" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +dependencies = [ + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "async-trait", + "semver 1.0.22", + "thiserror", + "tracing", + "trezor-client", +] + +[[package]] +name = "alloy-signer-wallet" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +dependencies = [ + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "async-trait", "coins-bip32", "coins-bip39", "elliptic-curve", @@ -323,38 +432,54 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86ec0a47740b20bc5613b8712d0d321d031c4efc58e9645af96085d5cccfc27" +checksum = "452d929748ac948a10481fff4123affead32c553cf362841c5103dd508bdfc16" dependencies = [ "alloy-json-abi", + "alloy-sol-macro-input", "const-hex", - "dunce", "heck 0.4.1", "indexmap", "proc-macro-error", "proc-macro2", "quote", - "serde_json", - "syn 2.0.57", + "syn 2.0.58", "syn-solidity", "tiny-keccak", ] +[[package]] +name = "alloy-sol-macro-input" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df64e094f6d2099339f9e82b5b38440b159757b6920878f28316243f8166c8d1" +dependencies = [ + "alloy-json-abi", + "const-hex", + "dunce", + "heck 0.5.0", + "proc-macro2", + "quote", + "serde_json", + "syn 2.0.58", + "syn-solidity", +] + [[package]] name = "alloy-sol-type-parser" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0045cc89524e1451ccf33e8581355b6027ac7c6e494bb02959d4213ad0d8e91d" +checksum = "715f4d09a330cc181fc7c361b5c5c2766408fa59a0bac60349dcb7baabd404cc" dependencies = [ "winnow 0.6.5", ] [[package]] name = "alloy-sol-types" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad09ec5853fa700d12d778ad224dcdec636af424d29fad84fb9a2f16a5b0ef09" +checksum = "43bc2d6dfc2a19fd56644494479510f98b1ee929e04cf0d4aa45e98baa3e545b" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -366,11 +491,12 @@ dependencies = [ [[package]] name = "alloy-transport" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ "alloy-json-rpc", "base64 0.22.0", "futures-util", + "futures-utils-wasm", "serde", "serde_json", "thiserror", @@ -383,11 +509,11 @@ dependencies = [ [[package]] name = "alloy-transport-http" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ "alloy-json-rpc", "alloy-transport", - "reqwest 0.11.27", + "reqwest 0.12.2", "serde_json", "tower", "url", @@ -396,7 +522,7 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -414,7 +540,7 @@ dependencies = [ [[package]] name = "alloy-transport-ws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -429,9 +555,9 @@ dependencies = [ [[package]] name = "alloy-trie" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9e1498416f7e7f09af8061970e14936846b6271e153aa5ba539a22a7eb414d" +checksum = "beb28aa4ecd32fdfa1b1bdd111ff7357dd562c6b2372694cf9e613434fcba659" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -536,11 +662,12 @@ dependencies = [ "alloy-json-abi", "alloy-network", "alloy-primitives", - "alloy-providers", + "alloy-provider", "alloy-rlp", - "alloy-rpc-trace-types", "alloy-rpc-types", + "alloy-rpc-types-trace", "alloy-signer", + "alloy-signer-wallet", "alloy-sol-types", "alloy-transport", "alloy-trie", @@ -599,8 +726,8 @@ dependencies = [ "alloy-network", "alloy-primitives", "alloy-rlp", - "alloy-rpc-trace-types", "alloy-rpc-types", + "alloy-rpc-types-trace", "alloy-trie", "anvil-core", "bytes", @@ -846,7 +973,7 @@ checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -868,7 +995,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -885,7 +1012,7 @@ checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -938,7 +1065,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -947,6 +1074,324 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +[[package]] +name = "aws-config" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297b64446175a73987cedc3c438d79b2a654d0fff96f65ff530fbe039347644c" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-sdk-sso", + "aws-sdk-ssooidc", + "aws-sdk-sts", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "hex", + "http 0.2.12", + "hyper 0.14.28", + "ring 0.17.8", + "time", + "tokio", + "tracing", + "url", + "zeroize", +] + +[[package]] +name = "aws-credential-types" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa8587ae17c8e967e4b05a62d495be2fb7701bec52a97f7acfe8a29f938384c8" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "zeroize", +] + +[[package]] +name = "aws-runtime" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b13dc54b4b49f8288532334bba8f87386a40571c47c37b1304979b556dc613c8" +dependencies = [ + "aws-credential-types", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 0.2.12", + "http-body 0.4.6", + "percent-encoding", + "pin-project-lite", + "tracing", + "uuid 1.8.0", +] + +[[package]] +name = "aws-sdk-kms" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a4e610d67363f6846c903ebca4ce65439033d5ec2a5d8effc96d5eaa53355" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-sso" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "019a07902c43b03167ea5df0182f0cb63fae89f9a9682c44d18cf2e4a042cb34" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-ssooidc" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04c46ee08a48a7f4eaa4ad201dcc1dd537b49c50859d14d4510e00ad9d3f9af2" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-sts" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f752ac730125ca6017f72f9db5ec1772c9ecc664f87aa7507a7d81b023c23713" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-query", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sigv4" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d6f29688a4be9895c0ba8bef861ad0c0dac5c15e9618b9b7a6c233990fc263" +dependencies = [ + "aws-credential-types", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "form_urlencoded", + "hex", + "hmac", + "http 0.2.12", + "http 1.1.0", + "once_cell", + "percent-encoding", + "sha2", + "time", + "tracing", +] + +[[package]] +name = "aws-smithy-async" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62220bc6e97f946ddd51b5f1361f78996e704677afc518a4ff66b7a72ea1378c" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "aws-smithy-http" +version = "0.60.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f10fa66956f01540051b0aa7ad54574640f748f9839e843442d99b970d3aff9" +dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http-body 0.4.6", + "once_cell", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tracing", +] + +[[package]] +name = "aws-smithy-json" +version = "0.60.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4683df9469ef09468dad3473d129960119a0d3593617542b7d52086c8486f2d6" +dependencies = [ + "aws-smithy-types", +] + +[[package]] +name = "aws-smithy-query" +version = "0.60.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fbd61ceb3fe8a1cb7352e42689cec5335833cd9f94103a61e98f9bb61c64bb" +dependencies = [ + "aws-smithy-types", + "urlencoding", +] + +[[package]] +name = "aws-smithy-runtime" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c53572b4cd934ee5e8461ad53caa36e9d246aaef42166e3ac539e206a925d330" +dependencies = [ + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "fastrand", + "h2", + "http 0.2.12", + "http-body 0.4.6", + "http-body 1.0.0", + "hyper 0.14.28", + "hyper-rustls 0.24.2", + "once_cell", + "pin-project-lite", + "pin-utils", + "rustls 0.21.10", + "tokio", + "tracing", +] + +[[package]] +name = "aws-smithy-runtime-api" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccb2b3a7030dc9a3c9a08ce0b25decea5130e9db19619d4dffbbff34f75fe850" +dependencies = [ + "aws-smithy-async", + "aws-smithy-types", + "bytes", + "http 0.2.12", + "http 1.1.0", + "pin-project-lite", + "tokio", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-smithy-types" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe14dceea1e70101d38fbf2a99e6a34159477c0fb95e68e05c66bd7ae4c3729" +dependencies = [ + "base64-simd", + "bytes", + "bytes-utils", + "http 0.2.12", + "http 1.1.0", + "http-body 0.4.6", + "http-body 1.0.0", + "http-body-util", + "itoa", + "num-integer", + "pin-project-lite", + "pin-utils", + "ryu", + "serde", + "time", +] + +[[package]] +name = "aws-smithy-xml" +version = "0.60.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872c68cf019c0e4afc5de7753c4f7288ce4b71663212771bf5e4542eb9346ca9" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "aws-types" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dbf2f3da841a8930f159163175cf6a3d16ddde517c1b0fba7aa776822800f40" +dependencies = [ + "aws-credential-types", + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "http 0.2.12", + "rustc_version 0.4.0", + "tracing", +] + [[package]] name = "axum" version = "0.6.20" @@ -1038,6 +1483,16 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] + [[package]] name = "base64ct" version = "1.6.0" @@ -1094,19 +1549,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ "funty", - "radium", - "serde", - "tap", - "wyz", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", + "radium", + "serde", + "tap", + "wyz", ] [[package]] @@ -1152,7 +1598,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" dependencies = [ - "sha2 0.10.8", + "sha2", "tinyvec", ] @@ -1215,6 +1661,16 @@ dependencies = [ "serde", ] +[[package]] +name = "bytes-utils" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35" +dependencies = [ + "bytes", + "either", +] + [[package]] name = "bzip2" version = "0.4.4" @@ -1238,9 +1694,9 @@ dependencies = [ [[package]] name = "c-kzg" -version = "0.4.2" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94a4bc5367b6284358d2a6a6a1dc2d92ec4b86034561c3b9d3341909752fd848" +checksum = "3130f3d8717cc02e668a896af24984d5d5d4e8bf12e278e982e0f1bd88a0f9af" dependencies = [ "blst", "cc", @@ -1292,14 +1748,23 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" name = "cast" version = "0.2.0" dependencies = [ + "alloy-chains", + "alloy-consensus", + "alloy-contract", "alloy-dyn-abi", "alloy-json-abi", + "alloy-json-rpc", + "alloy-network", "alloy-primitives", - "alloy-providers", + "alloy-provider", "alloy-rlp", "alloy-rpc-types", "alloy-signer", + "alloy-signer-wallet", + "alloy-sol-types", + "alloy-transport", "async-trait", + "aws-sdk-kms", "chrono", "clap", "clap_complete", @@ -1308,12 +1773,8 @@ dependencies = [ "const-hex", "criterion", "dunce", - "eth-keystore", "ethers-contract", "ethers-core", - "ethers-middleware", - "ethers-providers", - "ethers-signers", "evm-disassembler", "evmole", "eyre", @@ -1332,8 +1793,6 @@ dependencies = [ "rayon", "regex", "rpassword", - "rusoto_core", - "rusoto_kms", "semver 1.0.22", "serde", "serde_json", @@ -1421,7 +1880,6 @@ dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", - "serde", "windows-targets 0.52.4", ] @@ -1481,7 +1939,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.11.0", + "strsim 0.11.1", "terminal_size", "unicase", "unicode-width", @@ -1515,7 +1973,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -1557,10 +2015,10 @@ dependencies = [ "bs58", "coins-core", "digest 0.10.7", - "hmac 0.12.1", + "hmac", "k256", "serde", - "sha2 0.10.8", + "sha2", "thiserror", ] @@ -1572,11 +2030,11 @@ checksum = "3db8fba409ce3dc04f7d804074039eb68b960b0829161f8e06c95fea3f122528" dependencies = [ "bitvec", "coins-bip32", - "hmac 0.12.1", + "hmac", "once_cell", "pbkdf2 0.12.2", "rand 0.8.5", - "sha2 0.10.8", + "sha2", "thiserror", ] @@ -1595,7 +2053,7 @@ dependencies = [ "ripemd", "serde", "serde_derive", - "sha2 0.10.8", + "sha2", "sha3", "thiserror", ] @@ -1916,16 +2374,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "crypto-mac" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25fab6889090c8133f3deb8f73ba3c65a7f456f66436fc012a1b1e272b1e103e" -dependencies = [ - "generic-array", - "subtle", -] - [[package]] name = "ctr" version = "0.9.2" @@ -1966,7 +2414,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -1977,7 +2425,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -2038,7 +2486,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -2059,7 +2507,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -2069,7 +2517,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" dependencies = [ "derive_builder_core", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -2119,7 +2567,7 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.4", + "block-buffer", "const-oid", "crypto-common", "subtle", @@ -2313,7 +2761,7 @@ checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -2375,16 +2823,16 @@ dependencies = [ "ctr", "digest 0.10.7", "hex", - "hmac 0.12.1", + "hmac", "pbkdf2 0.11.0", "rand 0.8.5", "scrypt", "serde", "serde_json", - "sha2 0.10.8", + "sha2", "sha3", "thiserror", - "uuid", + "uuid 0.8.2", ] [[package]] @@ -2525,7 +2973,7 @@ dependencies = [ "reqwest 0.11.27", "serde", "serde_json", - "syn 2.0.57", + "syn 2.0.58", "toml 0.8.12", "walkdir", ] @@ -2543,7 +2991,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -2569,7 +3017,7 @@ dependencies = [ "serde", "serde_json", "strum 0.26.2", - "syn 2.0.57", + "syn 2.0.58", "tempfile", "thiserror", "tiny-keccak", @@ -2666,23 +3114,14 @@ dependencies = [ "async-trait", "coins-bip32", "coins-bip39", - "coins-ledger", "const-hex", "elliptic-curve", "eth-keystore", "ethers-core", - "futures-executor", - "futures-util", - "home", "rand 0.8.5", - "rusoto_core", - "rusoto_kms", - "semver 1.0.22", - "sha2 0.10.8", - "spki", + "sha2", "thiserror", "tracing", - "trezor-client", ] [[package]] @@ -2698,7 +3137,7 @@ dependencies = [ "ethers-core", "glob", "home", - "md-5 0.10.6", + "md-5", "num_cpus", "once_cell", "path-slash", @@ -2821,7 +3260,7 @@ dependencies = [ "bytes", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -2948,10 +3387,17 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" name = "forge" version = "0.2.0" dependencies = [ + "alloy-chains", + "alloy-consensus", "alloy-dyn-abi", "alloy-json-abi", + "alloy-network", "alloy-primitives", + "alloy-provider", "alloy-rpc-types", + "alloy-signer", + "alloy-signer-wallet", + "alloy-transport", "anvil", "async-trait", "axum", @@ -2965,9 +3411,6 @@ dependencies = [ "dunce", "ethers-contract", "ethers-core", - "ethers-middleware", - "ethers-providers", - "ethers-signers", "evm-disassembler", "eyre", "forge-doc", @@ -3065,18 +3508,21 @@ dependencies = [ name = "forge-script" version = "0.2.0" dependencies = [ + "alloy-chains", "alloy-dyn-abi", + "alloy-eips", "alloy-json-abi", + "alloy-network", "alloy-primitives", + "alloy-provider", "alloy-rpc-types", + "alloy-signer", + "alloy-transport", "async-recursion", "clap", "const-hex", "dialoguer", "dunce", - "ethers-core", - "ethers-providers", - "ethers-signers", "eyre", "forge-verify", "foundry-cheatcodes", @@ -3107,10 +3553,10 @@ version = "0.2.0" dependencies = [ "alloy-json-abi", "alloy-primitives", + "alloy-provider", "async-trait", "clap", "const-hex", - "ethers-providers", "eyre", "foundry-block-explorers", "foundry-cli", @@ -3142,9 +3588,9 @@ dependencies = [ [[package]] name = "foundry-block-explorers" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e88929768c22f9912694c634db053cfd480c3f7bd7eb39223e29f4fb40b64b7" +checksum = "ee75d972291181ae98bd1b48647ca8d8832159012b240ca1b7225085d4a63f00" dependencies = [ "alloy-chains", "alloy-json-abi", @@ -3166,9 +3612,10 @@ dependencies = [ "alloy-genesis", "alloy-json-abi", "alloy-primitives", - "alloy-providers", + "alloy-provider", "alloy-rpc-types", "alloy-signer", + "alloy-signer-wallet", "alloy-sol-types", "base64 0.22.0", "const-hex", @@ -3210,14 +3657,16 @@ dependencies = [ name = "foundry-cli" version = "0.2.0" dependencies = [ + "alloy-chains", "alloy-dyn-abi", "alloy-json-abi", "alloy-primitives", + "alloy-provider", + "alloy-transport", "clap", "color-eyre", + "const-hex", "dotenvy", - "ethers-core", - "ethers-providers", "eyre", "forge-fmt", "foundry-common", @@ -3226,6 +3675,7 @@ dependencies = [ "foundry-debugger", "foundry-evm", "foundry-wallets", + "futures", "indicatif", "once_cell", "regex", @@ -3244,15 +3694,17 @@ dependencies = [ name = "foundry-common" version = "0.2.0" dependencies = [ + "alloy-consensus", + "alloy-contract", "alloy-dyn-abi", "alloy-json-abi", "alloy-json-rpc", "alloy-primitives", - "alloy-providers", + "alloy-provider", "alloy-pubsub", "alloy-rpc-client", "alloy-rpc-types", - "alloy-signer", + "alloy-signer-wallet", "alloy-sol-types", "alloy-transport", "alloy-transport-http", @@ -3280,6 +3732,7 @@ dependencies = [ "pretty_assertions", "rand 0.8.5", "reqwest 0.11.27", + "reqwest 0.12.2", "rustc-hash", "semver 1.0.22", "serde", @@ -3296,9 +3749,9 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "079ada1a2093e0fec67caa15ccf018a2d1b5747c16ba1c11a28df53530eb1a5f" +checksum = "dd3323f90e9f256a2c359dbb1cc7e1b7e4fef9e04e5a82693895e959a6efe010" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3309,7 +3762,7 @@ dependencies = [ "futures-util", "home", "itertools 0.12.1", - "md-5 0.10.6", + "md-5", "memmap2 0.9.4", "once_cell", "path-slash", @@ -3319,7 +3772,7 @@ dependencies = [ "semver 1.0.22", "serde", "serde_json", - "sha2 0.10.8", + "sha2", "solang-parser", "svm-rs 0.4.1", "svm-rs-builds", @@ -3360,7 +3813,7 @@ dependencies = [ "tempfile", "thiserror", "toml 0.8.12", - "toml_edit 0.21.1", + "toml_edit 0.22.9", "tracing", "walkdir", ] @@ -3420,9 +3873,10 @@ dependencies = [ "alloy-genesis", "alloy-json-abi", "alloy-primitives", - "alloy-providers", + "alloy-provider", "alloy-rpc-types", "alloy-sol-types", + "alloy-transport", "arrayvec", "auto_impl", "const-hex", @@ -3531,7 +3985,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -3539,8 +3993,7 @@ name = "foundry-test-utils" version = "0.2.0" dependencies = [ "alloy-primitives", - "ethers-core", - "ethers-providers", + "alloy-provider", "eyre", "fd-lock 4.0.2", "foundry-common", @@ -3560,21 +4013,27 @@ dependencies = [ name = "foundry-wallets" version = "0.2.0" dependencies = [ + "alloy-consensus", + "alloy-dyn-abi", + "alloy-network", "alloy-primitives", + "alloy-signer", + "alloy-signer-aws", + "alloy-signer-ledger", + "alloy-signer-trezor", + "alloy-signer-wallet", + "alloy-sol-types", "async-trait", + "aws-config", + "aws-sdk-kms", "clap", "const-hex", "derive_builder", - "ethers-core", - "ethers-providers", - "ethers-signers", "eyre", "foundry-common", "foundry-config", "itertools 0.12.1", "rpassword", - "rusoto_core", - "rusoto_kms", "serde", "thiserror", "tokio", @@ -3708,7 +4167,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -3751,6 +4210,12 @@ dependencies = [ "slab", ] +[[package]] +name = "futures-utils-wasm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" + [[package]] name = "fxhash" version = "0.2.1" @@ -4169,16 +4634,6 @@ dependencies = [ "rusb", ] -[[package]] -name = "hmac" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" -dependencies = [ - "crypto-mac", - "digest 0.9.0", -] - [[package]] name = "hmac" version = "0.12.1" @@ -4334,21 +4789,6 @@ dependencies = [ "want", ] -[[package]] -name = "hyper-rustls" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" -dependencies = [ - "http 0.2.12", - "hyper 0.14.28", - "log", - "rustls 0.20.9", - "rustls-native-certs 0.6.3", - "tokio", - "tokio-rustls 0.23.4", -] - [[package]] name = "hyper-rustls" version = "0.24.2" @@ -4358,7 +4798,9 @@ dependencies = [ "futures-util", "http 0.2.12", "hyper 0.14.28", + "log", "rustls 0.21.10", + "rustls-native-certs 0.6.3", "tokio", "tokio-rustls 0.24.1", ] @@ -4733,7 +5175,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "once_cell", - "sha2 0.10.8", + "sha2", "signature", ] @@ -4921,17 +5363,6 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" -[[package]] -name = "md-5" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" -dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "opaque-debug", -] - [[package]] name = "md-5" version = "0.10.6" @@ -5023,7 +5454,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -5300,7 +5731,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -5352,12 +5783,6 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - [[package]] name = "open-fastrlp" version = "0.1.4" @@ -5417,7 +5842,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -5444,6 +5869,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "outref" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" + [[package]] name = "overload" version = "0.1.1" @@ -5465,7 +5896,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "sha2 0.10.8", + "sha2", ] [[package]] @@ -5567,9 +5998,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ "digest 0.10.7", - "hmac 0.12.1", + "hmac", "password-hash", - "sha2 0.10.8", + "sha2", ] [[package]] @@ -5579,7 +6010,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ "digest 0.10.7", - "hmac 0.12.1", + "hmac", ] [[package]] @@ -5602,7 +6033,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -5660,7 +6091,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -5671,7 +6102,7 @@ checksum = "2adbf29bb9776f28caece835398781ab24435585fe0d4dc1374a61db5accedca" dependencies = [ "once_cell", "pest", - "sha2 0.10.8", + "sha2", ] [[package]] @@ -5763,7 +6194,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -5801,7 +6232,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -5912,7 +6343,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" dependencies = [ "proc-macro2", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -6007,7 +6438,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", "version_check", "yansi 1.0.1", ] @@ -6076,9 +6507,9 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.10.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce76ce678ffc8e5675b22aa1405de0b7037e2fdf8913fea40d1926c6fe1e6e7" +checksum = "5f0530d13d87d1f549b66a3e8d0c688952abe5994e204ed62615baaf25dc029c" dependencies = [ "bitflags 2.5.0", "memchr", @@ -6293,6 +6724,12 @@ dependencies = [ "regex-syntax 0.8.3", ] +[[package]] +name = "regex-lite" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b661b2f27137bdbc16f00eda72866a92bb28af1753ffbd56744fb6e2e9cd8e" + [[package]] name = "regex-syntax" version = "0.6.29" @@ -6346,7 +6783,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots", + "webpki-roots 0.25.4", "winreg", ] @@ -6388,14 +6825,15 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "webpki-roots 0.26.1", "winreg", ] [[package]] name = "revm" -version = "7.1.0" +version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "217d21144d329f21d5245b8e6a46e0d6d0a527d9917d7a087f225b161e529169" +checksum = "72a454c1c650b2b2e23f0c461af09e6c31e1d15e1cbebe905a701c46b8a50afc" dependencies = [ "auto_impl", "cfg-if", @@ -6409,11 +6847,11 @@ dependencies = [ [[package]] name = "revm-inspectors" version = "0.1.0" -source = "git+https://github.com/paradigmxyz/evm-inspectors?rev=ba0b6ab#ba0b6ab695802c752601f17f5c941b62a067ad64" +source = "git+https://github.com/paradigmxyz/evm-inspectors?rev=510d4d0#510d4d0d06130d52ee996fa8aebd32cdc8267905" dependencies = [ "alloy-primitives", - "alloy-rpc-trace-types", "alloy-rpc-types", + "alloy-rpc-types-trace", "alloy-sol-types", "anstyle", "colorchoice", @@ -6425,9 +6863,9 @@ dependencies = [ [[package]] name = "revm-interpreter" -version = "3.3.0" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "776848391ed76d5103ca1aa1632cd21b521e2870afb30b63723da862d69efd0f" +checksum = "d322f2730cd300e99d271a1704a2dfb8973d832428f5aa282aaa40e2473b5eec" dependencies = [ "revm-primitives", "serde", @@ -6435,9 +6873,9 @@ dependencies = [ [[package]] name = "revm-precompile" -version = "5.0.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3fd1856a7cb09197a02669d779e1afb5a627b0888a24814ba2b6a1ad4c3ff8d" +checksum = "931f692f3f4fc72ec39d5d270f8e9d208c4a6008de7590ee96cf948e3b6d3f8d" dependencies = [ "aurora-engine-modexp", "c-kzg", @@ -6446,15 +6884,15 @@ dependencies = [ "revm-primitives", "ripemd", "secp256k1", - "sha2 0.10.8", + "sha2", "substrate-bn", ] [[package]] name = "revm-primitives" -version = "3.0.0" +version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a4d7d3e793e907dc0797a9d3b43abfdf5226d133855214db9bd27d4cee33ebd" +checksum = "cbbc9640790cebcb731289afb7a7d96d16ad94afeb64b5d0b66443bd151e79d6" dependencies = [ "alloy-primitives", "auto_impl", @@ -6475,7 +6913,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ - "hmac 0.12.1", + "hmac", "subtle", ] @@ -6602,89 +7040,6 @@ dependencies = [ "libusb1-sys", ] -[[package]] -name = "rusoto_core" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1db30db44ea73551326269adcf7a2169428a054f14faf9e1768f2163494f2fa2" -dependencies = [ - "async-trait", - "base64 0.13.1", - "bytes", - "crc32fast", - "futures", - "http 0.2.12", - "hyper 0.14.28", - "hyper-rustls 0.23.2", - "lazy_static", - "log", - "rusoto_credential", - "rusoto_signature", - "rustc_version 0.4.0", - "serde", - "serde_json", - "tokio", - "xml-rs", -] - -[[package]] -name = "rusoto_credential" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee0a6c13db5aad6047b6a44ef023dbbc21a056b6dab5be3b79ce4283d5c02d05" -dependencies = [ - "async-trait", - "chrono", - "dirs-next", - "futures", - "hyper 0.14.28", - "serde", - "serde_json", - "shlex", - "tokio", - "zeroize", -] - -[[package]] -name = "rusoto_kms" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e1fc19cfcfd9f6b2f96e36d5b0dddda9004d2cbfc2d17543e3b9f10cc38fce8" -dependencies = [ - "async-trait", - "bytes", - "futures", - "rusoto_core", - "serde", - "serde_json", -] - -[[package]] -name = "rusoto_signature" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5ae95491c8b4847931e291b151127eccd6ff8ca13f33603eb3d0035ecb05272" -dependencies = [ - "base64 0.13.1", - "bytes", - "chrono", - "digest 0.9.0", - "futures", - "hex", - "hmac 0.11.0", - "http 0.2.12", - "hyper 0.14.28", - "log", - "md-5 0.9.1", - "percent-encoding", - "pin-project-lite", - "rusoto_credential", - "rustc_version 0.4.0", - "serde", - "sha2 0.9.9", - "tokio", -] - [[package]] name = "rustc-demangle" version = "0.1.23" @@ -6734,18 +7089,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rustls" -version = "0.20.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" -dependencies = [ - "log", - "ring 0.16.20", - "sct", - "webpki", -] - [[package]] name = "rustls" version = "0.21.10" @@ -6977,10 +7320,10 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f9e24d2b632954ded8ab2ef9fea0a0c769ea56ea98bddbafbad22caeeadf45d" dependencies = [ - "hmac 0.12.1", + "hmac", "pbkdf2 0.11.0", "salsa20", - "sha2 0.10.8", + "sha2", ] [[package]] @@ -7013,6 +7356,7 @@ version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10" dependencies = [ + "rand 0.8.5", "secp256k1-sys", ] @@ -7104,7 +7448,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -7158,7 +7502,7 @@ checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -7204,7 +7548,7 @@ checksum = "b93fb4adc70021ac1b47f7d45e8cc4169baaa7ea58483bc5b721d19a26202212" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -7224,19 +7568,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - [[package]] name = "sha2" version = "0.10.8" @@ -7469,9 +7800,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "strsim" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" @@ -7501,7 +7832,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -7514,7 +7845,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -7550,7 +7881,7 @@ dependencies = [ "semver 1.0.22", "serde", "serde_json", - "sha2 0.10.8", + "sha2", "thiserror", "url", "zip", @@ -7570,7 +7901,7 @@ dependencies = [ "semver 1.0.22", "serde", "serde_json", - "sha2 0.10.8", + "sha2", "thiserror", "url", "zip", @@ -7602,9 +7933,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.57" +version = "2.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11a6ae1e52eb25aab8f3fb9fca13be982a373b8f1157ca14b897a825ba4a2d35" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" dependencies = [ "proc-macro2", "quote", @@ -7613,14 +7944,14 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3d0961cd53c23ea94eeec56ba940f636f6394788976e9f16ca5ee0aca7464a" +checksum = "4497156948bd342b52038035a6fa514a89626e37af9d2c52a5e8d8ebcc7ee479" dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -7730,7 +8061,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -7872,7 +8203,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -7885,17 +8216,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-rustls" -version = "0.23.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" -dependencies = [ - "rustls 0.20.9", - "tokio", - "webpki", -] - [[package]] name = "tokio-rustls" version = "0.24.1" @@ -7937,13 +8257,11 @@ checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" dependencies = [ "futures-util", "log", - "native-tls", "rustls 0.21.10", "tokio", - "tokio-native-tls", "tokio-rustls 0.24.1", "tungstenite", - "webpki-roots", + "webpki-roots 0.25.4", ] [[package]] @@ -8117,7 +8435,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -8211,7 +8529,6 @@ dependencies = [ "http 0.2.12", "httparse", "log", - "native-tls", "rand 0.8.5", "rustls 0.21.10", "sha1", @@ -8337,6 +8654,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "utf-8" version = "0.7.6" @@ -8359,6 +8682,12 @@ dependencies = [ "serde", ] +[[package]] +name = "uuid" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" + [[package]] name = "valuable" version = "0.1.0" @@ -8389,6 +8718,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + [[package]] name = "wait-timeout" version = "0.2.0" @@ -8450,7 +8785,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", "wasm-bindgen-shared", ] @@ -8484,7 +8819,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -8554,20 +8889,19 @@ dependencies = [ ] [[package]] -name = "webpki" -version = "0.22.4" +name = "webpki-roots" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" -dependencies = [ - "ring 0.17.8", - "untrusted 0.9.0", -] +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "webpki-roots" -version = "0.25.4" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" +dependencies = [ + "rustls-pki-types", +] [[package]] name = "which" @@ -8885,10 +9219,10 @@ dependencies = [ ] [[package]] -name = "xml-rs" -version = "0.8.20" +name = "xmlparser" +version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" [[package]] name = "yansi" @@ -8919,7 +9253,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -8939,7 +9273,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -8955,7 +9289,7 @@ dependencies = [ "crc32fast", "crossbeam-utils", "flate2", - "hmac 0.12.1", + "hmac", "pbkdf2 0.11.0", "sha1", "time", diff --git a/Cargo.toml b/Cargo.toml index ad300a085..1bc2329a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -107,9 +107,6 @@ codegen-units = 1 [profile.release.package] mdbook.opt-level = 1 protobuf.opt-level = 1 -rusoto_core.opt-level = 1 -rusoto_credential.opt-level = 1 -rusoto_kms.opt-level = 1 toml_edit.opt-level = 1 trezor-client.opt-level = 1 @@ -140,16 +137,16 @@ foundry-wallets = { path = "crates/wallets" } foundry-linking = { path = "crates/linking" } # solc & compilation utilities -foundry-block-explorers = { version = "0.2.4", default-features = false } -foundry-compilers = { version = "0.3.13", default-features = false } +foundry-block-explorers = { version = "0.2.5", default-features = false } +foundry-compilers = { version = "0.3.14", default-features = false } ## revm # no default features to avoid c-kzg -revm = { version = "7.1", default-features = false, features = ["std"] } -revm-primitives = { version = "3", default-features = false, features = ["std"] } -revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "ba0b6ab", features = [ +revm = { version = "8", default-features = false } +revm-primitives = { version = "3", default-features = false } +revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "510d4d0", features = [ "serde", -] } +] } ## ethers ethers = { version = "2.0.14", default-features = false } @@ -161,30 +158,34 @@ ethers-signers = { version = "2.0.14", default-features = false } ethers-middleware = { version = "2.0.14", default-features = false } ## alloy -alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-providers = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-rpc-trace-types = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-primitives = { version = "0.6.3", features = ["getrandom"] } -alloy-dyn-abi = "0.6.3" -alloy-json-abi = "0.6.3" -alloy-sol-types = "0.6.3" -syn-solidity = "0.6.3" +alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-signer-wallet = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-primitives = { version = "0.7.0", features = ["getrandom"] } +alloy-dyn-abi = "0.7.0" +alloy-json-abi = "0.7.0" +alloy-sol-types = "0.7.0" +syn-solidity = "0.7.0" alloy-chains = "0.1" -alloy-trie = "0.3" - +alloy-trie = "0.3.1" alloy-rlp = "0.3.3" solang-parser = "=0.3.3" diff --git a/clippy.toml b/clippy.toml index 472818efe..09acb653d 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1 +1,4 @@ msrv = "1.76" +# bytes::Bytes is included by default and alloy_primitives::Bytes is a wrapper around it, +# so it is safe to ignore it as well +ignore-interior-mutability = ["bytes::Bytes", "alloy_primitives::Bytes"] diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index 4ccbfaddd..99bf00081 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -16,7 +16,11 @@ path = "src/anvil.rs" required-features = ["cli"] [build-dependencies] -vergen = { workspace = true, default-features = false, features = ["build", "git", "gitcl"] } +vergen = { workspace = true, default-features = false, features = [ + "build", + "git", + "gitcl", +] } [dependencies] # foundry internal @@ -36,12 +40,13 @@ alloy-primitives = { workspace = true, features = ["serde"] } alloy-consensus = { workspace = true, features = ["k256", "kzg"] } alloy-network.workspace = true alloy-rlp.workspace = true -alloy-signer = { workspace = true, features = ["eip712", "mnemonic"] } +alloy-signer = { workspace = true, features = ["eip712"] } +alloy-signer-wallet = { workspace = true, features = ["mnemonic"] } alloy-sol-types = { workspace = true, features = ["std"] } alloy-dyn-abi = { workspace = true, features = ["std", "eip712"] } alloy-rpc-types.workspace = true -alloy-rpc-trace-types.workspace = true -alloy-providers.workspace = true +alloy-rpc-types-trace.workspace = true +alloy-provider = { workspace = true, features = ["pubsub"] } alloy-transport.workspace = true alloy-chains.workspace = true alloy-genesis.workspace = true @@ -75,7 +80,11 @@ rand = "0.8" eyre.workspace = true # cli -clap = { version = "4", features = ["derive", "env", "wrap_help"], optional = true } +clap = { version = "4", features = [ + "derive", + "env", + "wrap_help", +], optional = true } clap_complete = { version = "4", optional = true } chrono.workspace = true auto_impl = "1" @@ -89,7 +98,7 @@ tikv-jemallocator = { workspace = true, optional = true } [dev-dependencies] alloy-json-abi.workspace = true -ethers = { workspace = true, features = ["abigen"] } +ethers = { workspace = true, features = ["abigen", "optimism"] } ethers-core = { workspace = true, features = ["optimism"] } foundry-compilers = { workspace = true, features = ["project-util", "full"] } diff --git a/crates/anvil/core/Cargo.toml b/crates/anvil/core/Cargo.toml index 999c60689..feddead95 100644 --- a/crates/anvil/core/Cargo.toml +++ b/crates/anvil/core/Cargo.toml @@ -12,11 +12,15 @@ repository.workspace = true [dependencies] foundry-common.workspace = true foundry-evm.workspace = true -revm = { workspace = true, default-features = false, features = ["std", "serde", "memory_limit"] } +revm = { workspace = true, default-features = false, features = [ + "std", + "serde", + "memory_limit", +] } alloy-primitives = { workspace = true, features = ["serde"] } alloy-rpc-types = { workspace = true } -alloy-rpc-trace-types.workspace = true +alloy-rpc-types-trace.workspace = true alloy-rlp.workspace = true alloy-eips.workspace = true alloy-network = { workspace = true, features = ["k256"] } @@ -27,7 +31,7 @@ alloy-trie.workspace = true serde = { workspace = true, optional = true } serde_json.workspace = true bytes = "1.4" -c-kzg = { version = "0.4.2", features = ["serde"] } +c-kzg = { version = "1", features = ["serde"] } # misc rand = "0.8" diff --git a/crates/anvil/core/src/eth/block.rs b/crates/anvil/core/src/eth/block.rs index c304eefa4..462d16c82 100644 --- a/crates/anvil/core/src/eth/block.rs +++ b/crates/anvil/core/src/eth/block.rs @@ -3,7 +3,7 @@ use super::{ trie, }; use alloy_consensus::Header; -use alloy_primitives::{Address, Bloom, Bytes, B256, U256}; +use alloy_primitives::{Address, Bloom, Bytes, B256, B64, U256}; use alloy_rlp::{RlpDecodable, RlpEncodable}; // Type alias to optionally support impersonated transactions @@ -88,13 +88,13 @@ pub struct PartialHeader { pub logs_bloom: Bloom, pub difficulty: U256, pub number: u64, - pub gas_limit: u64, - pub gas_used: u64, + pub gas_limit: u128, + pub gas_used: u128, pub timestamp: u64, pub extra_data: Bytes, pub mix_hash: B256, - pub nonce: u64, - pub base_fee: Option, + pub nonce: B64, + pub base_fee: Option, } impl From
for PartialHeader { @@ -120,7 +120,6 @@ impl From
for PartialHeader { #[cfg(test)] mod tests { - use alloy_network::Sealable; use alloy_primitives::{ b256, hex::{self, FromHex}, @@ -143,11 +142,11 @@ mod tests { difficulty: Default::default(), number: 124u64, gas_limit: Default::default(), - gas_used: 1337u64, + gas_used: 1337u128, timestamp: 0, extra_data: Default::default(), mix_hash: Default::default(), - nonce: 99u64, + nonce: B64::with_last_byte(99), withdrawals_root: Default::default(), blob_gas_used: Default::default(), excess_blob_gas: Default::default(), @@ -159,7 +158,7 @@ mod tests { let decoded: Header = Header::decode(&mut encoded.as_ref()).unwrap(); assert_eq!(header, decoded); - header.base_fee_per_gas = Some(12345u64); + header.base_fee_per_gas = Some(12345u128); let encoded = alloy_rlp::encode(&header); let decoded: Header = Header::decode(&mut encoded.as_ref()).unwrap(); @@ -182,8 +181,8 @@ mod tests { logs_bloom: Bloom::from_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(), difficulty: U256::from(2222), number: 0xd05u64, - gas_limit: 0x115cu64, - gas_used: 0x15b3u64, + gas_limit: 0x115cu128, + gas_used: 0x15b3u128, timestamp: 0x1a0au64, extra_data: hex::decode("7788").unwrap().into(), mix_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), @@ -191,7 +190,7 @@ mod tests { blob_gas_used: None, excess_blob_gas: None, parent_beacon_block_root: None, - nonce: 0, + nonce: B64::ZERO, base_fee_per_gas: None, }; @@ -214,12 +213,12 @@ mod tests { logs_bloom: <[u8; 256]>::from_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap().into(), difficulty: U256::from(2222), number: 0xd05u64, - gas_limit: 0x115cu64, - gas_used: 0x15b3u64, + gas_limit: 0x115cu128, + gas_used: 0x15b3u128, timestamp: 0x1a0au64, extra_data: hex::decode("7788").unwrap().into(), mix_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - nonce: 0, + nonce: B64::ZERO, withdrawals_root: None, blob_gas_used: None, excess_blob_gas: None, @@ -245,19 +244,19 @@ mod tests { logs_bloom: Bloom::from_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(), difficulty: U256::from(0x020000), number: 1u64, - gas_limit: U256::from(0x016345785d8a0000u128).to::(), - gas_used: U256::from(0x015534).to::(), + gas_limit: U256::from(0x016345785d8a0000u128).to::(), + gas_used: U256::from(0x015534).to::(), timestamp: 0x079e, extra_data: hex::decode("42").unwrap().into(), mix_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - nonce: 0, + nonce: B64::ZERO, base_fee_per_gas: Some(875), withdrawals_root: None, blob_gas_used: None, excess_blob_gas: None, parent_beacon_block_root: None, }; - assert_eq!(header.hash(), expected_hash); + assert_eq!(header.hash_slow(), expected_hash); } #[test] diff --git a/crates/anvil/core/src/eth/mod.rs b/crates/anvil/core/src/eth/mod.rs index e5a394a8f..7a2a153e8 100644 --- a/crates/anvil/core/src/eth/mod.rs +++ b/crates/anvil/core/src/eth/mod.rs @@ -3,13 +3,13 @@ use crate::{ types::{EvmMineOptions, Forking, Index}, }; use alloy_primitives::{Address, Bytes, TxHash, B256, B64, U256}; -use alloy_rpc_trace_types::geth::{GethDebugTracingOptions, GethDefaultTracingOptions}; use alloy_rpc_types::{ pubsub::{Params as SubscriptionParams, SubscriptionKind}, request::TransactionRequest, state::StateOverride, - BlockId, BlockNumberOrTag as BlockNumber, Filter, + BlockId, BlockNumberOrTag as BlockNumber, Filter, WithOtherFields, }; +use alloy_rpc_types_trace::geth::{GethDebugTracingOptions, GethDefaultTracingOptions}; pub mod block; pub mod proof; @@ -143,7 +143,7 @@ pub enum EthRequest { EthSign(Address, Bytes), #[cfg_attr(feature = "serde", serde(rename = "eth_signTransaction"))] - EthSignTransaction(Box), + EthSignTransaction(Box>), /// Signs data via [EIP-712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md). #[cfg_attr(feature = "serde", serde(rename = "eth_signTypedData"))] @@ -158,27 +158,27 @@ pub enum EthRequest { EthSignTypedDataV4(Address, alloy_dyn_abi::TypedData), #[cfg_attr(feature = "serde", serde(rename = "eth_sendTransaction", with = "sequence"))] - EthSendTransaction(Box), + EthSendTransaction(Box>), #[cfg_attr(feature = "serde", serde(rename = "eth_sendRawTransaction", with = "sequence"))] EthSendRawTransaction(Bytes), #[cfg_attr(feature = "serde", serde(rename = "eth_call"))] EthCall( - TransactionRequest, + WithOtherFields, #[cfg_attr(feature = "serde", serde(default))] Option, #[cfg_attr(feature = "serde", serde(default))] Option, ), #[cfg_attr(feature = "serde", serde(rename = "eth_createAccessList"))] EthCreateAccessList( - TransactionRequest, + WithOtherFields, #[cfg_attr(feature = "serde", serde(default))] Option, ), #[cfg_attr(feature = "serde", serde(rename = "eth_estimateGas"))] EthEstimateGas( - TransactionRequest, + WithOtherFields, #[cfg_attr(feature = "serde", serde(default))] Option, #[cfg_attr(feature = "serde", serde(default))] Option, ), @@ -272,7 +272,7 @@ pub enum EthRequest { /// geth's `debug_traceCall` endpoint #[cfg_attr(feature = "serde", serde(rename = "debug_traceCall"))] DebugTraceCall( - TransactionRequest, + WithOtherFields, #[cfg_attr(feature = "serde", serde(default))] Option, #[cfg_attr(feature = "serde", serde(default))] GethDefaultTracingOptions, ), @@ -597,7 +597,7 @@ pub enum EthRequest { feature = "serde", serde(rename = "eth_sendUnsignedTransaction", with = "sequence") )] - EthSendUnsignedTransaction(Box), + EthSendUnsignedTransaction(Box>), /// Turn on call traces for transactions that are returned to the user when they execute a /// transaction (instead of just txhash/receipt) diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 10de5f80e..84e236f92 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -5,21 +5,24 @@ use crate::eth::{ utils::eip_to_revm_access_list, }; use alloy_consensus::{ - BlobTransactionSidecar, ReceiptWithBloom, TxEip1559, TxEip2930, TxEip4844, TxEip4844Variant, - TxEip4844WithSidecar, TxLegacy, + AnyReceiptEnvelope, BlobTransactionSidecar, Receipt, ReceiptEnvelope, ReceiptWithBloom, Signed, + TxEip1559, TxEip2930, TxEip4844, TxEip4844Variant, TxEip4844WithSidecar, TxEnvelope, TxLegacy, + TxReceipt, }; -use alloy_network::{Signed, Transaction, TxKind}; -use alloy_primitives::{Address, Bloom, Bytes, Log, Signature, TxHash, B256, U128, U256, U64}; -use alloy_rlp::{Decodable, Encodable}; +use alloy_eips::eip2718::Decodable2718; +use alloy_primitives::{Address, Bloom, Bytes, Log, Signature, TxHash, TxKind, B256, U256}; +use alloy_rlp::{length_of_length, Decodable, Encodable, Header}; use alloy_rpc_types::{ - request::TransactionRequest, AccessList, Signature as RpcSignature, - Transaction as RpcTransaction, + request::TransactionRequest, AccessList, AnyTransactionReceipt, Signature as RpcSignature, + Transaction as RpcTransaction, TransactionReceipt, WithOtherFields, }; +use bytes::BufMut; use foundry_evm::traces::CallTraceNode; use revm::{ interpreter::InstructionResult, primitives::{CreateScheme, OptimismFields, TransactTo, TxEnv}, }; +use serde::{Deserialize, Serialize}; use std::ops::Deref; use super::utils::from_eip_to_alloy_access_list; @@ -35,26 +38,30 @@ pub fn impersonated_signature() -> Signature { /// Converts a [TransactionRequest] into a [TypedTransactionRequest]. /// Should be removed once the call builder abstraction for providers is in place. -pub fn transaction_request_to_typed(tx: TransactionRequest) -> Option { - let TransactionRequest { - from, - to, - gas_price, - max_fee_per_gas, - max_priority_fee_per_gas, - max_fee_per_blob_gas, - mut blob_versioned_hashes, - gas, - value, - input, - nonce, - mut access_list, - sidecar, - transaction_type, +pub fn transaction_request_to_typed( + tx: WithOtherFields, +) -> Option { + let WithOtherFields:: { + inner: + TransactionRequest { + from, + to, + gas_price, + max_fee_per_gas, + max_priority_fee_per_gas, + max_fee_per_blob_gas, + mut blob_versioned_hashes, + gas, + value, + input, + nonce, + mut access_list, + sidecar, + transaction_type, + .. + }, other, - .. } = tx; - let transaction_type = transaction_type.map(|id| id.to::()); // Special case: OP-stack deposit tx if transaction_type == Some(126) { @@ -79,14 +86,15 @@ pub fn transaction_request_to_typed(tx: TransactionRequest) -> Option { + (Some(0), _, None, None, None, None, None, None, _) | + (None, Some(_), None, None, None, None, None, None, _) => { Some(TypedTransactionRequest::Legacy(TxLegacy { - nonce: nonce.unwrap_or_default().to::(), - gas_price: gas_price.unwrap_or_default().to::(), - gas_limit: gas.unwrap_or_default().to::(), + nonce: nonce.unwrap_or_default(), + gas_price: gas_price.unwrap_or_default(), + gas_limit: gas.unwrap_or_default(), value: value.unwrap_or(U256::ZERO), input: input.into_input().unwrap_or_default(), to: match to { @@ -97,12 +105,12 @@ pub fn transaction_request_to_typed(tx: TransactionRequest) -> Option { + (Some(1), _, None, None, _, None, None, None, _) | + (None, _, None, None, Some(_), None, None, None, _) => { Some(TypedTransactionRequest::EIP2930(TxEip2930 { - nonce: nonce.unwrap_or_default().to::(), - gas_price: gas_price.unwrap_or_default().to(), - gas_limit: gas.unwrap_or_default().to::(), + nonce: nonce.unwrap_or_default(), + gas_price: gas_price.unwrap_or_default(), + gas_limit: gas.unwrap_or_default(), value: value.unwrap_or(U256::ZERO), input: input.into_input().unwrap_or_default(), to: match to { @@ -110,20 +118,20 @@ pub fn transaction_request_to_typed(tx: TransactionRequest) -> Option TxKind::Create, }, chain_id: 0, - access_list: to_eip_access_list(access_list.unwrap_or_default()), + access_list: access_list.unwrap_or_default(), })) } // EIP1559 - (Some(2), None, _, _, _, _, None, None) | - (None, None, Some(_), _, _, _, None, None) | - (None, None, _, Some(_), _, _, None, None) | - (None, None, None, None, None, _, None, None) => { + (Some(2), None, _, _, _, _, None, None, _) | + (None, None, Some(_), _, _, _, None, None, _) | + (None, None, _, Some(_), _, _, None, None, _) | + (None, None, None, None, None, _, None, None, _) => { // Empty fields fall back to the canonical transaction schema. Some(TypedTransactionRequest::EIP1559(TxEip1559 { - nonce: nonce.unwrap_or_default().to::(), - max_fee_per_gas: max_fee_per_gas.unwrap_or_default().to::(), - max_priority_fee_per_gas: max_priority_fee_per_gas.unwrap_or_default().to::(), - gas_limit: gas.unwrap_or_default().to::(), + nonce: nonce.unwrap_or_default(), + max_fee_per_gas: max_fee_per_gas.unwrap_or_default(), + max_priority_fee_per_gas: max_priority_fee_per_gas.unwrap_or_default(), + gas_limit: gas.unwrap_or_default(), value: value.unwrap_or(U256::ZERO), input: input.into_input().unwrap_or_default(), to: match to { @@ -131,25 +139,22 @@ pub fn transaction_request_to_typed(tx: TransactionRequest) -> Option TxKind::Create, }, chain_id: 0, - access_list: to_eip_access_list(access_list.unwrap_or_default()), + access_list: access_list.unwrap_or_default(), })) } // EIP4844 - (Some(3), None, _, _, _, Some(_), Some(_), Some(sidecar)) => { + (Some(3), None, _, _, _, Some(_), Some(_), Some(sidecar), Some(to)) => { let tx = TxEip4844 { - nonce: nonce.unwrap_or_default().to::(), - max_fee_per_gas: max_fee_per_gas.unwrap_or_default().to::(), - max_priority_fee_per_gas: max_priority_fee_per_gas.unwrap_or_default().to::(), - max_fee_per_blob_gas: max_fee_per_blob_gas.unwrap_or_default().to::(), - gas_limit: gas.unwrap_or_default().to::(), + nonce: nonce.unwrap_or_default(), + max_fee_per_gas: max_fee_per_gas.unwrap_or_default(), + max_priority_fee_per_gas: max_priority_fee_per_gas.unwrap_or_default(), + max_fee_per_blob_gas: max_fee_per_blob_gas.unwrap_or_default(), + gas_limit: gas.unwrap_or_default(), value: value.unwrap_or(U256::ZERO), input: input.into_input().unwrap_or_default(), - to: match to { - Some(to) => TxKind::Call(to), - None => TxKind::Create, - }, + to, chain_id: 0, - access_list: to_eip_access_list(access_list.unwrap_or_default()), + access_list: access_list.unwrap_or_default(), blob_versioned_hashes: blob_versioned_hashes.unwrap_or_default(), }; let blob_sidecar = BlobTransactionSidecar { @@ -288,19 +293,19 @@ pub fn to_alloy_transaction_with_hash_and_sender( match transaction { TypedTransaction::Legacy(t) => RpcTransaction { hash, - nonce: U64::from(t.nonce), + nonce: t.tx().nonce, block_hash: None, block_number: None, transaction_index: None, from, to: None, - value: t.value, - gas_price: Some(U128::from(t.gas_price)), - max_fee_per_gas: Some(U128::from(t.gas_price)), - max_priority_fee_per_gas: Some(U128::from(t.gas_price)), - gas: U256::from(t.gas_limit), - input: t.input.clone(), - chain_id: t.chain_id.map(U64::from), + 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(), @@ -310,93 +315,93 @@ pub fn to_alloy_transaction_with_hash_and_sender( access_list: None, transaction_type: None, max_fee_per_blob_gas: None, - blob_versioned_hashes: vec![], + blob_versioned_hashes: None, other: Default::default(), }, TypedTransaction::EIP2930(t) => RpcTransaction { hash, - nonce: U64::from(t.nonce), + nonce: t.tx().nonce, block_hash: None, block_number: None, transaction_index: None, from, to: None, - value: t.value, - gas_price: Some(U128::from(t.gas_price)), - max_fee_per_gas: Some(U128::from(t.gas_price)), - max_priority_fee_per_gas: Some(U128::from(t.gas_price)), - gas: U256::from(t.gas_limit), - input: t.input.clone(), - chain_id: Some(U64::from(t.chain_id)), + 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().y_parity_byte()), y_parity: Some(alloy_rpc_types::Parity::from(t.signature().v().y_parity())), }), - access_list: Some(from_eip_to_alloy_access_list(t.access_list.clone()).0), - transaction_type: Some(U64::from(1)), + access_list: Some(t.tx().access_list.clone()), + transaction_type: Some(1), max_fee_per_blob_gas: None, - blob_versioned_hashes: vec![], + blob_versioned_hashes: None, other: Default::default(), }, TypedTransaction::EIP1559(t) => RpcTransaction { hash, - nonce: U64::from(t.nonce), + nonce: t.tx().nonce, block_hash: None, block_number: None, transaction_index: None, from, to: None, - value: t.value, + value: t.tx().value, gas_price: None, - max_fee_per_gas: Some(U128::from(t.max_fee_per_gas)), - max_priority_fee_per_gas: Some(U128::from(t.max_priority_fee_per_gas)), - gas: U256::from(t.gas_limit), - input: t.input.clone(), - chain_id: Some(U64::from(t.chain_id)), + 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().y_parity_byte()), y_parity: Some(alloy_rpc_types::Parity::from(t.signature().v().y_parity())), }), - access_list: Some(from_eip_to_alloy_access_list(t.access_list.clone()).0), - transaction_type: Some(U64::from(2)), + access_list: Some(t.tx().access_list.clone()), + transaction_type: Some(2), max_fee_per_blob_gas: None, - blob_versioned_hashes: vec![], + blob_versioned_hashes: None, other: Default::default(), }, TypedTransaction::EIP4844(t) => RpcTransaction { hash, - nonce: U64::from(t.tx().tx().nonce), + nonce: t.tx().tx().nonce, block_hash: None, block_number: None, transaction_index: None, from, to: None, value: t.tx().tx().value, - gas_price: Some(U128::from(t.tx().tx().max_fee_per_gas)), - max_fee_per_gas: Some(U128::from(t.tx().tx().max_fee_per_gas)), - max_priority_fee_per_gas: Some(U128::from(t.tx().tx().max_priority_fee_per_gas)), - gas: U256::from(t.tx().tx().gas_limit), + 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(U64::from(t.tx().tx().chain_id)), + 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().y_parity_byte()), y_parity: Some(alloy_rpc_types::Parity::from(t.signature().v().y_parity())), }), - access_list: Some(from_eip_to_alloy_access_list(t.tx().tx().access_list.clone()).0), - transaction_type: Some(U64::from(3)), - max_fee_per_blob_gas: Some(U128::from(t.tx().tx().max_fee_per_blob_gas)), - blob_versioned_hashes: t.tx().tx().blob_versioned_hashes.clone(), + access_list: Some(from_eip_to_alloy_access_list(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()), other: Default::default(), }, TypedTransaction::Deposit(t) => RpcTransaction { hash, - nonce: U64::from(t.nonce), + nonce: t.nonce, block_hash: None, block_number: None, transaction_index: None, @@ -406,14 +411,14 @@ pub fn to_alloy_transaction_with_hash_and_sender( gas_price: None, max_fee_per_gas: None, max_priority_fee_per_gas: None, - gas: U256::from(t.gas_limit), + gas: t.gas_limit, input: t.input.clone().0.into(), - chain_id: t.chain_id().map(U64::from), + 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: vec![], + blob_versioned_hashes: None, other: Default::default(), }, } @@ -447,7 +452,7 @@ impl PendingTransaction { } } - pub fn nonce(&self) -> U256 { + pub fn nonce(&self) -> u64 { self.transaction.nonce() } @@ -472,7 +477,7 @@ impl PendingTransaction { let caller = *self.sender(); match &self.transaction.transaction { TypedTransaction::Legacy(tx) => { - let chain_id = tx.chain_id(); + let chain_id = tx.tx().chain_id; let TxLegacy { nonce, gas_price, gas_limit, value, to, input, .. } = tx.tx(); TxEnv { caller, @@ -483,7 +488,7 @@ impl PendingTransaction { value: (*value), gas_price: U256::from(*gas_price), gas_priority_fee: None, - gas_limit: *gas_limit, + gas_limit: *gas_limit as u64, access_list: vec![], ..Default::default() } @@ -509,7 +514,7 @@ impl PendingTransaction { value: *value, gas_price: U256::from(*gas_price), gas_priority_fee: None, - gas_limit: *gas_limit, + gas_limit: *gas_limit as u64, access_list: eip_to_revm_access_list(access_list.0.clone()), ..Default::default() } @@ -536,7 +541,7 @@ impl PendingTransaction { value: *value, gas_price: U256::from(*max_fee_per_gas), gas_priority_fee: Some(U256::from(*max_priority_fee_per_gas)), - gas_limit: *gas_limit, + gas_limit: *gas_limit as u64, access_list: eip_to_revm_access_list(access_list.0.clone()), ..Default::default() } @@ -558,7 +563,7 @@ impl PendingTransaction { } = tx.tx().tx(); TxEnv { caller, - transact_to: transact_to(to), + transact_to: TransactTo::call(*to), data: alloy_primitives::Bytes(input.0.clone()), chain_id: Some(*chain_id), nonce: Some(*nonce), @@ -567,7 +572,7 @@ impl PendingTransaction { gas_priority_fee: Some(U256::from(*max_priority_fee_per_gas)), max_fee_per_blob_gas: Some(U256::from(*max_fee_per_blob_gas)), blob_hashes: blob_versioned_hashes.clone(), - gas_limit: *gas_limit, + gas_limit: *gas_limit as u64, access_list: eip_to_revm_access_list(access_list.0.clone()), ..Default::default() } @@ -590,11 +595,11 @@ impl PendingTransaction { transact_to: transact_to(kind), data: alloy_primitives::Bytes(input.0.clone()), chain_id, - nonce: Some(nonce.to::()), + nonce: Some(*nonce), value: *value, gas_price: U256::ZERO, gas_priority_fee: None, - gas_limit: gas_limit.to::(), + gas_limit: *gas_limit as u64, access_list: vec![], optimism: OptimismFields { source_hash: Some(*source_hash), @@ -630,31 +635,31 @@ impl TypedTransaction { matches!(self, TypedTransaction::EIP1559(_)) } - pub fn gas_price(&self) -> U256 { - U256::from(match self { - TypedTransaction::Legacy(tx) => tx.gas_price, - TypedTransaction::EIP2930(tx) => tx.gas_price, - TypedTransaction::EIP1559(tx) => tx.max_fee_per_gas, + pub fn gas_price(&self) -> u128 { + match self { + TypedTransaction::Legacy(tx) => tx.tx().gas_price, + TypedTransaction::EIP2930(tx) => tx.tx().gas_price, + TypedTransaction::EIP1559(tx) => tx.tx().max_fee_per_gas, TypedTransaction::EIP4844(tx) => tx.tx().tx().max_fee_per_blob_gas, TypedTransaction::Deposit(_) => 0, - }) + } } - pub fn gas_limit(&self) -> U256 { - U256::from(match self { - TypedTransaction::Legacy(tx) => tx.gas_limit, - TypedTransaction::EIP2930(tx) => tx.gas_limit, - TypedTransaction::EIP1559(tx) => tx.gas_limit, + pub fn gas_limit(&self) -> u128 { + match self { + TypedTransaction::Legacy(tx) => tx.tx().gas_limit, + TypedTransaction::EIP2930(tx) => tx.tx().gas_limit, + TypedTransaction::EIP1559(tx) => tx.tx().gas_limit, TypedTransaction::EIP4844(tx) => tx.tx().tx().gas_limit, - TypedTransaction::Deposit(tx) => tx.gas_limit.to::(), - }) + TypedTransaction::Deposit(tx) => tx.gas_limit, + } } pub fn value(&self) -> U256 { U256::from(match self { - TypedTransaction::Legacy(tx) => tx.value, - TypedTransaction::EIP2930(tx) => tx.value, - TypedTransaction::EIP1559(tx) => tx.value, + TypedTransaction::Legacy(tx) => tx.tx().value, + TypedTransaction::EIP2930(tx) => tx.tx().value, + TypedTransaction::EIP1559(tx) => tx.tx().value, TypedTransaction::EIP4844(tx) => tx.tx().tx().value, TypedTransaction::Deposit(tx) => tx.value, }) @@ -662,9 +667,9 @@ impl TypedTransaction { pub fn data(&self) -> &Bytes { match self { - TypedTransaction::Legacy(tx) => &tx.input, - TypedTransaction::EIP2930(tx) => &tx.input, - TypedTransaction::EIP1559(tx) => &tx.input, + TypedTransaction::Legacy(tx) => &tx.tx().input, + TypedTransaction::EIP2930(tx) => &tx.tx().input, + TypedTransaction::EIP1559(tx) => &tx.tx().input, TypedTransaction::EIP4844(tx) => &tx.tx().tx().input, TypedTransaction::Deposit(tx) => &tx.input, } @@ -682,7 +687,7 @@ impl TypedTransaction { } /// Max cost of the transaction - pub fn max_cost(&self) -> U256 { + pub fn max_cost(&self) -> u128 { self.gas_limit().saturating_mul(self.gas_price()) } @@ -691,51 +696,51 @@ impl TypedTransaction { match self { TypedTransaction::Legacy(t) => TransactionEssentials { kind: t.tx().to, - input: t.input.clone(), - nonce: U256::from(t.tx().nonce), - gas_limit: U256::from(t.tx().gas_limit), + input: t.tx().input.clone(), + nonce: t.tx().nonce, + gas_limit: t.tx().gas_limit, gas_price: Some(U256::from(t.tx().gas_price)), max_fee_per_gas: None, max_priority_fee_per_gas: None, max_fee_per_blob_gas: None, blob_versioned_hashes: None, - value: t.value, + value: t.tx().value, chain_id: t.tx().chain_id, access_list: Default::default(), }, TypedTransaction::EIP2930(t) => TransactionEssentials { kind: t.tx().to, - input: t.input.clone(), - nonce: U256::from(t.tx().nonce), - gas_limit: U256::from(t.tx().gas_limit), + input: t.tx().input.clone(), + nonce: t.tx().nonce, + gas_limit: t.tx().gas_limit, gas_price: Some(U256::from(t.tx().gas_price)), max_fee_per_gas: None, max_priority_fee_per_gas: None, max_fee_per_blob_gas: None, blob_versioned_hashes: None, - value: t.value, - chain_id: Some(t.chain_id), - access_list: to_alloy_access_list(t.access_list.clone()), + value: t.tx().value, + chain_id: Some(t.tx().chain_id), + access_list: t.tx().access_list.clone(), }, TypedTransaction::EIP1559(t) => TransactionEssentials { - kind: t.to, - input: t.input.clone(), - nonce: U256::from(t.nonce), - gas_limit: U256::from(t.gas_limit), + kind: t.tx().to, + input: t.tx().input.clone(), + nonce: t.tx().nonce, + gas_limit: t.tx().gas_limit, gas_price: None, - max_fee_per_gas: Some(U256::from(t.max_fee_per_gas)), - max_priority_fee_per_gas: Some(U256::from(t.max_priority_fee_per_gas)), + max_fee_per_gas: Some(U256::from(t.tx().max_fee_per_gas)), + max_priority_fee_per_gas: Some(U256::from(t.tx().max_priority_fee_per_gas)), max_fee_per_blob_gas: None, blob_versioned_hashes: None, - value: t.value, - chain_id: Some(t.chain_id), - access_list: to_alloy_access_list(t.access_list.clone()), + value: t.tx().value, + chain_id: Some(t.tx().chain_id), + access_list: t.tx().access_list.clone(), }, TypedTransaction::EIP4844(t) => TransactionEssentials { - kind: t.tx().tx().to, + kind: TxKind::Call(t.tx().tx().to), input: t.tx().tx().input.clone(), - nonce: U256::from(t.tx().tx().nonce), - gas_limit: U256::from(t.tx().tx().gas_limit), + nonce: t.tx().tx().nonce, + gas_limit: t.tx().tx().gas_limit, gas_price: Some(U256::from(t.tx().tx().max_fee_per_blob_gas)), max_fee_per_gas: Some(U256::from(t.tx().tx().max_fee_per_gas)), max_priority_fee_per_gas: Some(U256::from(t.tx().tx().max_priority_fee_per_gas)), @@ -743,7 +748,7 @@ impl TypedTransaction { blob_versioned_hashes: Some(t.tx().tx().blob_versioned_hashes.clone()), value: t.tx().tx().value, chain_id: Some(t.tx().tx().chain_id), - access_list: to_alloy_access_list(t.tx().tx().access_list.clone()), + access_list: t.tx().tx().access_list.clone(), }, TypedTransaction::Deposit(t) => TransactionEssentials { kind: t.kind, @@ -762,21 +767,21 @@ impl TypedTransaction { } } - pub fn nonce(&self) -> U256 { + pub fn nonce(&self) -> u64 { match self { - TypedTransaction::Legacy(t) => U256::from(t.nonce), - TypedTransaction::EIP2930(t) => U256::from(t.nonce), - TypedTransaction::EIP1559(t) => U256::from(t.nonce), - TypedTransaction::EIP4844(t) => U256::from(t.tx().tx().nonce), - TypedTransaction::Deposit(t) => U256::from(t.nonce), + TypedTransaction::Legacy(t) => t.tx().nonce, + TypedTransaction::EIP2930(t) => t.tx().nonce, + TypedTransaction::EIP1559(t) => t.tx().nonce, + TypedTransaction::EIP4844(t) => t.tx().tx().nonce, + TypedTransaction::Deposit(t) => t.nonce, } } pub fn chain_id(&self) -> Option { match self { - TypedTransaction::Legacy(t) => t.chain_id, - TypedTransaction::EIP2930(t) => Some(t.chain_id), - TypedTransaction::EIP1559(t) => Some(t.chain_id), + TypedTransaction::Legacy(t) => t.tx().chain_id, + TypedTransaction::EIP2930(t) => Some(t.tx().chain_id), + TypedTransaction::EIP1559(t) => Some(t.tx().chain_id), TypedTransaction::EIP4844(t) => Some(t.tx().tx().chain_id), TypedTransaction::Deposit(t) => t.chain_id(), } @@ -852,19 +857,19 @@ impl TypedTransaction { } /// Returns what kind of transaction this is - pub fn kind(&self) -> &TxKind { + pub fn kind(&self) -> TxKind { match self { - TypedTransaction::Legacy(tx) => &tx.to, - TypedTransaction::EIP2930(tx) => &tx.to, - TypedTransaction::EIP1559(tx) => &tx.to, - TypedTransaction::EIP4844(tx) => &tx.tx().tx().to, - TypedTransaction::Deposit(tx) => &tx.kind, + TypedTransaction::Legacy(tx) => tx.tx().to, + TypedTransaction::EIP2930(tx) => tx.tx().to, + TypedTransaction::EIP1559(tx) => tx.tx().to, + TypedTransaction::EIP4844(tx) => TxKind::Call(tx.tx().tx().to), + TypedTransaction::Deposit(tx) => tx.kind, } } /// Returns the callee if this transaction is a call pub fn to(&self) -> Option
{ - self.kind().to() + self.kind().to().copied() } /// Returns the Signature of the transaction @@ -887,65 +892,72 @@ impl TypedTransaction { impl Encodable for TypedTransaction { fn encode(&self, out: &mut dyn bytes::BufMut) { match self { - TypedTransaction::Legacy(tx) => tx.encode(out), - TypedTransaction::EIP2930(tx) => tx.encode(out), - TypedTransaction::EIP1559(tx) => tx.encode(out), - TypedTransaction::EIP4844(tx) => tx.encode(out), - TypedTransaction::Deposit(tx) => tx.encode(out), + TypedTransaction::Legacy(tx) => TxEnvelope::from(tx.clone()).encode(out), + TypedTransaction::EIP2930(tx) => TxEnvelope::from(tx.clone()).encode(out), + TypedTransaction::EIP1559(tx) => TxEnvelope::from(tx.clone()).encode(out), + TypedTransaction::EIP4844(tx) => TxEnvelope::from(tx.clone()).encode(out), + TypedTransaction::Deposit(tx) => { + let tx_payload_len = tx.fields_len(); + let tx_header_len = Header { list: false, payload_length: tx_payload_len }.length(); + Header { list: false, payload_length: 1 + tx_payload_len + tx_header_len } + .encode(out); + out.put_u8(0x7E); + tx.encode(out); + } } } } impl Decodable for TypedTransaction { fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { - use bytes::Buf; - use std::cmp::Ordering; + let mut h_decode_copy = *buf; + let header = alloy_rlp::Header::decode(&mut h_decode_copy)?; - let first = *buf.first().ok_or(alloy_rlp::Error::Custom("empty slice"))?; + // Legacy TX + if header.list { + return Ok(TxEnvelope::decode(buf)?.into()) + } - // a signed transaction is either encoded as a string (non legacy) or a list (legacy). - // We should not consume the buffer if we are decoding a legacy transaction, so let's - // check if the first byte is between 0x80 and 0xbf. - match first.cmp(&alloy_rlp::EMPTY_LIST_CODE) { - Ordering::Less => { - // strip out the string header - // NOTE: typed transaction encodings either contain a "rlp header" which contains - // the type of the payload and its length, or they do not contain a header and - // start with the tx type byte. - // - // This line works for both types of encodings because byte slices starting with - // 0x01 and 0x02 return a Header { list: false, payload_length: 1 } when input to - // Header::decode. - // If the encoding includes a header, the header will be properly decoded and - // consumed. - // Otherwise, header decoding will succeed but nothing is consumed. - let _header = alloy_rlp::Header::decode(buf)?; - let tx_type = *buf.first().ok_or(alloy_rlp::Error::Custom( - "typed tx cannot be decoded from an empty slice", - ))?; - if tx_type == 0x01 { - buf.advance(1); - as Decodable>::decode(buf).map(TypedTransaction::EIP2930) - } else if tx_type == 0x02 { - buf.advance(1); - as Decodable>::decode(buf).map(TypedTransaction::EIP1559) - } else if tx_type == 0x03 { - buf.advance(1); - as Decodable>::decode(buf) - .map(TypedTransaction::EIP4844) - } else if tx_type == 0x7E { - buf.advance(1); - ::decode(buf).map(TypedTransaction::Deposit) - } else { - Err(alloy_rlp::Error::Custom("invalid tx type")) - } - } - Ordering::Equal => { - Err(alloy_rlp::Error::Custom("an empty list is not a valid transaction encoding")) - } - Ordering::Greater => { - as Decodable>::decode(buf).map(TypedTransaction::Legacy) - } + // Check byte after header + let ty = *h_decode_copy.first().ok_or(alloy_rlp::Error::Custom("empty slice"))?; + + if ty != 0x7E { + Ok(TxEnvelope::decode(buf)?.into()) + } else { + Ok(Self::Deposit(DepositTransaction::decode(&mut h_decode_copy)?)) + } + } +} + +impl Decodable2718 for TypedTransaction { + fn typed_decode(ty: u8, buf: &mut &[u8]) -> alloy_rlp::Result { + 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)), + _ => unreachable!(), + } + } + + fn fallback_decode(buf: &mut &[u8]) -> alloy_rlp::Result { + match TxEnvelope::fallback_decode(buf)? { + TxEnvelope::Legacy(tx) => Ok(Self::Legacy(tx)), + _ => unreachable!(), + } + } +} + +impl From for TypedTransaction { + fn from(value: TxEnvelope) -> Self { + match value { + TxEnvelope::Legacy(tx) => TypedTransaction::Legacy(tx), + TxEnvelope::Eip2930(tx) => TypedTransaction::EIP2930(tx), + TxEnvelope::Eip1559(tx) => TypedTransaction::EIP1559(tx), + TxEnvelope::Eip4844(tx) => TypedTransaction::EIP4844(tx), + _ => unreachable!(), } } } @@ -954,8 +966,8 @@ impl Decodable for TypedTransaction { pub struct TransactionEssentials { pub kind: TxKind, pub input: Bytes, - pub nonce: U256, - pub gas_limit: U256, + pub nonce: u64, + pub gas_limit: u128, pub gas_price: Option, pub max_fee_per_gas: Option, pub max_priority_fee_per_gas: Option, @@ -970,75 +982,168 @@ pub struct TransactionEssentials { #[derive(Clone, Debug, PartialEq, Eq)] pub struct TransactionInfo { pub transaction_hash: B256, - pub transaction_index: u32, + pub transaction_index: u64, pub from: Address, pub to: Option
, pub contract_address: Option
, - pub logs: Vec, - pub logs_bloom: Bloom, pub traces: Vec, pub exit: InstructionResult, pub out: Option, pub nonce: u64, + pub gas_used: u128, } -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum TypedReceipt { - Legacy(ReceiptWithBloom), - EIP2930(ReceiptWithBloom), - EIP1559(ReceiptWithBloom), - EIP4844(ReceiptWithBloom), - Deposit(ReceiptWithBloom), +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct DepositReceipt { + #[serde(flatten)] + pub inner: ReceiptWithBloom, + pub deposit_nonce: Option, + pub deposit_nonce_version: Option, } -impl TypedReceipt { - pub fn gas_used(&self) -> U256 { - match self { - TypedReceipt::Legacy(r) | - TypedReceipt::EIP1559(r) | - TypedReceipt::EIP2930(r) | - TypedReceipt::EIP4844(r) | - TypedReceipt::Deposit(r) => U256::from(r.receipt.cumulative_gas_used), +impl DepositReceipt { + fn payload_len(&self) -> usize { + self.inner.receipt.status.length() + + self.inner.receipt.cumulative_gas_used.length() + + self.inner.logs_bloom.length() + + self.inner.receipt.logs.length() + + self.deposit_nonce.map_or(0, |n| n.length()) + + self.deposit_nonce_version.map_or(0, |n| n.length()) + } + + /// Returns the rlp header for the receipt payload. + fn receipt_rlp_header(&self) -> alloy_rlp::Header { + alloy_rlp::Header { list: true, payload_length: self.payload_len() } + } + + /// Encodes the receipt data. + fn encode_fields(&self, out: &mut dyn BufMut) { + self.receipt_rlp_header().encode(out); + self.inner.receipt.status.encode(out); + self.inner.receipt.cumulative_gas_used.encode(out); + self.inner.logs_bloom.encode(out); + self.inner.receipt.logs.encode(out); + if let Some(n) = self.deposit_nonce { + n.encode(out); + } + if let Some(n) = self.deposit_nonce_version { + n.encode(out); } } - pub fn logs_bloom(&self) -> &Bloom { - match self { - TypedReceipt::Legacy(r) | - TypedReceipt::EIP1559(r) | - TypedReceipt::EIP2930(r) | - TypedReceipt::EIP4844(r) | - TypedReceipt::Deposit(r) => &r.bloom, + /// Decodes the receipt payload + fn decode_receipt(buf: &mut &[u8]) -> alloy_rlp::Result { + let b: &mut &[u8] = &mut &**buf; + let rlp_head = alloy_rlp::Header::decode(b)?; + if !rlp_head.list { + return Err(alloy_rlp::Error::UnexpectedString); + } + let started_len = b.len(); + let remaining = |b: &[u8]| rlp_head.payload_length - (started_len - b.len()) > 0; + + let status = Decodable::decode(b)?; + let cumulative_gas_used = Decodable::decode(b)?; + let logs_bloom = Decodable::decode(b)?; + let logs = Decodable::decode(b)?; + let deposit_nonce = remaining(b).then(|| alloy_rlp::Decodable::decode(b)).transpose()?; + let deposit_nonce_version = + remaining(b).then(|| alloy_rlp::Decodable::decode(b)).transpose()?; + + let this = Self { + inner: ReceiptWithBloom { + receipt: Receipt { status, cumulative_gas_used, logs }, + logs_bloom, + }, + deposit_nonce, + deposit_nonce_version, + }; + + let consumed = started_len - b.len(); + if consumed != rlp_head.payload_length { + return Err(alloy_rlp::Error::ListLengthMismatch { + expected: rlp_head.payload_length, + got: consumed, + }); } + + *buf = *b; + Ok(this) + } +} + +impl alloy_rlp::Encodable for DepositReceipt { + fn encode(&self, out: &mut dyn BufMut) { + self.encode_fields(out); + } + + fn length(&self) -> usize { + let payload_length = self.payload_len(); + payload_length + length_of_length(payload_length) } +} - pub fn logs(&self) -> &Vec { +impl alloy_rlp::Decodable for DepositReceipt { + fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { + Self::decode_receipt(buf) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(tag = "type")] +pub enum TypedReceipt { + #[serde(rename = "0x0", alias = "0x00")] + Legacy(ReceiptWithBloom), + #[serde(rename = "0x1", alias = "0x01")] + EIP2930(ReceiptWithBloom), + #[serde(rename = "0x2", alias = "0x02")] + EIP1559(ReceiptWithBloom), + #[serde(rename = "0x3", alias = "0x03")] + EIP4844(ReceiptWithBloom), + #[serde(rename = "0x7E", alias = "0x7e")] + Deposit(DepositReceipt), +} + +impl TypedReceipt { + pub fn as_receipt_with_bloom(&self) -> &ReceiptWithBloom { match self { TypedReceipt::Legacy(r) | TypedReceipt::EIP1559(r) | TypedReceipt::EIP2930(r) | - TypedReceipt::EIP4844(r) | - TypedReceipt::Deposit(r) => &r.receipt.logs, + TypedReceipt::EIP4844(r) => r, + TypedReceipt::Deposit(r) => &r.inner, } } } -impl From for ReceiptWithBloom { - fn from(val: TypedReceipt) -> Self { - match val { - TypedReceipt::Legacy(r) | - TypedReceipt::EIP1559(r) | - TypedReceipt::EIP2930(r) | - TypedReceipt::EIP4844(r) | - TypedReceipt::Deposit(r) => r, +impl TypedReceipt { + pub fn cumulative_gas_used(&self) -> u128 { + self.as_receipt_with_bloom().cumulative_gas_used() + } + + pub fn logs_bloom(&self) -> &Bloom { + &self.as_receipt_with_bloom().logs_bloom + } + + pub fn logs(&self) -> &[Log] { + self.as_receipt_with_bloom().logs() + } +} + +impl From> for TypedReceipt { + fn from(value: ReceiptEnvelope) -> Self { + match value { + ReceiptEnvelope::Legacy(r) => TypedReceipt::Legacy(r), + ReceiptEnvelope::Eip2930(r) => TypedReceipt::EIP2930(r), + ReceiptEnvelope::Eip1559(r) => TypedReceipt::EIP1559(r), + ReceiptEnvelope::Eip4844(r) => TypedReceipt::EIP4844(r), + _ => unreachable!(), } } } impl Encodable for TypedReceipt { fn encode(&self, out: &mut dyn bytes::BufMut) { - use alloy_rlp::Header; - match self { TypedReceipt::Legacy(r) => r.encode(out), receipt => { @@ -1080,7 +1185,6 @@ impl Encodable for TypedReceipt { impl Decodable for TypedReceipt { fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { - use alloy_rlp::Header; use bytes::Buf; use std::cmp::Ordering; @@ -1109,7 +1213,7 @@ impl Decodable for TypedReceipt { ::decode(buf).map(TypedReceipt::EIP4844) } else if receipt_type == 0x7E { buf.advance(1); - ::decode(buf).map(TypedReceipt::Deposit) + ::decode(buf).map(TypedReceipt::Deposit) } else { Err(alloy_rlp::Error::Custom("invalid receipt type")) } @@ -1124,41 +1228,59 @@ impl Decodable for TypedReceipt { } } -/// Translates an EIP-2930 access list to an alloy-rpc-types access list. -pub fn to_alloy_access_list( - access_list: alloy_eips::eip2930::AccessList, -) -> alloy_rpc_types::AccessList { - alloy_rpc_types::AccessList( - access_list - .0 - .into_iter() - .map(|item| alloy_rpc_types::AccessListItem { - address: item.address, - storage_keys: item.storage_keys, - }) - .collect(), - ) -} - -/// Translates an alloy-rpc-types access list to an EIP-2930 access list. -pub fn to_eip_access_list( - access_list: alloy_rpc_types::AccessList, -) -> alloy_eips::eip2930::AccessList { - alloy_eips::eip2930::AccessList( - access_list - .0 - .into_iter() - .map(|item| alloy_eips::eip2930::AccessListItem { - address: item.address, - storage_keys: item.storage_keys, - }) - .collect(), - ) +pub type ReceiptResponse = TransactionReceipt>; + +pub fn convert_to_anvil_receipt(receipt: AnyTransactionReceipt) -> Option { + let WithOtherFields { + inner: + TransactionReceipt { + transaction_hash, + transaction_index, + block_hash, + block_number, + gas_used, + contract_address, + effective_gas_price, + from, + to, + blob_gas_price, + blob_gas_used, + state_root, + inner: AnyReceiptEnvelope { inner: receipt_with_bloom, r#type }, + }, + other, + } = receipt; + + Some(TransactionReceipt { + transaction_hash, + transaction_index, + block_hash, + block_number, + gas_used, + contract_address, + effective_gas_price, + from, + to, + blob_gas_price, + blob_gas_used, + state_root, + inner: match r#type { + 0x00 => TypedReceipt::Legacy(receipt_with_bloom), + 0x01 => TypedReceipt::EIP2930(receipt_with_bloom), + 0x02 => TypedReceipt::EIP1559(receipt_with_bloom), + 0x03 => TypedReceipt::EIP4844(receipt_with_bloom), + 0x7E => TypedReceipt::Deposit(DepositReceipt { + inner: receipt_with_bloom, + deposit_nonce: other.get("depositNonce").and_then(|v| v.as_u64()), + deposit_nonce_version: other.get("depositNonceVersion").and_then(|v| v.as_u64()), + }), + _ => return None, + }, + }) } #[cfg(test)] mod tests { - use alloy_consensus::Receipt; use alloy_primitives::{b256, hex, LogData}; use std::str::FromStr; @@ -1171,8 +1293,8 @@ mod tests { let tx = TxLegacy { nonce: 2u64, - gas_price: 1000000000u64.into(), - gas_limit: 100000u64, + gas_price: 1000000000u128, + gas_limit: 100000u128, to: TxKind::Call(Address::from_slice( &hex::decode("d3e8763675e4c425df46cc3b5c0f6cbdac396046").unwrap()[..], )), @@ -1238,10 +1360,7 @@ mod tests { _ => unreachable!(), }; - assert_eq!( - tx.tx().tx().to, - TxKind::Call(address!("11E9CA82A3a762b4B5bd264d4173a242e7a77064")) - ); + assert_eq!(tx.tx().tx().to, address!("11E9CA82A3a762b4B5bd264d4173a242e7a77064")); assert_eq!( tx.tx().tx().blob_versioned_hashes, @@ -1266,11 +1385,11 @@ mod tests { panic!("decoding TypedTransaction failed"); }; - assert_eq!(tx.input, Bytes::from(b"")); - assert_eq!(tx.gas_price, 1); - assert_eq!(tx.gas_limit, 21000); - assert_eq!(tx.nonce, 0); - if let TxKind::Call(to) = tx.to { + assert_eq!(tx.tx().input, Bytes::from(b"")); + assert_eq!(tx.tx().gas_price, 1); + assert_eq!(tx.tx().gas_limit, 21000); + assert_eq!(tx.tx().nonce, 0); + if let TxKind::Call(to) = tx.tx().to { assert_eq!( to, "0x095e7baea6a6c7c4c2dfeb977efac326af552d87".parse::
().unwrap() @@ -1278,7 +1397,7 @@ mod tests { } else { panic!("expected a call transaction"); } - assert_eq!(tx.value, U256::from(0x0au64)); + assert_eq!(tx.tx().value, U256::from(0x0au64)); assert_eq!( tx.recover_signer().unwrap(), "0f65fe9276bc9a24ae7083ae28e2660ef72df99e".parse::
().unwrap() @@ -1292,8 +1411,8 @@ mod tests { let mut data = vec![]; let receipt = TypedReceipt::Legacy(ReceiptWithBloom { receipt: Receipt { - success: false, - cumulative_gas_used: 0x1u64, + status: false, + cumulative_gas_used: 0x1u128, logs: vec![Log { address: Address::from_str("0000000000000000000000000000000000000011").unwrap(), data: LogData::new_unchecked( @@ -1311,7 +1430,7 @@ mod tests { ), }], }, - bloom: [0; 256].into(), + logs_bloom: [0; 256].into(), }); receipt.encode(&mut data); @@ -1327,8 +1446,8 @@ mod tests { let expected = TypedReceipt::Legacy(ReceiptWithBloom { receipt: Receipt { - success: false, - cumulative_gas_used: 0x1u64, + status: false, + cumulative_gas_used: 0x1u128, logs: vec![Log { address: Address::from_str("0000000000000000000000000000000000000011").unwrap(), data: LogData::new_unchecked( @@ -1346,7 +1465,7 @@ mod tests { ), }], }, - bloom: [0; 256].into(), + logs_bloom: [0; 256].into(), }); let receipt = TypedReceipt::decode(&mut &data[..]).unwrap(); diff --git a/crates/anvil/core/src/eth/transaction/optimism.rs b/crates/anvil/core/src/eth/transaction/optimism.rs index 86edc810a..dedaffaf3 100644 --- a/crates/anvil/core/src/eth/transaction/optimism.rs +++ b/crates/anvil/core/src/eth/transaction/optimism.rs @@ -1,6 +1,5 @@ -use alloy_consensus::TxType; -use alloy_network::{Transaction, TxKind}; -use alloy_primitives::{Address, Bytes, ChainId, Signature, B256, U256}; +use alloy_consensus::{SignableTransaction, Signed, Transaction, TxType}; +use alloy_primitives::{keccak256, Address, Bytes, ChainId, Signature, TxKind, B256, U256}; use alloy_rlp::{ length_of_length, Decodable, Encodable, Error as DecodeError, Header as RlpHeader, }; @@ -13,7 +12,7 @@ pub struct DepositTransactionRequest { pub kind: TxKind, pub mint: U256, pub value: U256, - pub gas_limit: U256, + pub gas_limit: u128, pub is_system_tx: bool, pub input: Bytes, } @@ -134,108 +133,74 @@ impl DepositTransactionRequest { 1 + length_of_length(payload_length) + payload_length } - /// Outputs the signature hash of the transaction by first encoding without a signature, then - /// hashing. - pub(crate) fn signature_hash(&self) -> B256 { - let mut buf = Vec::with_capacity(self.payload_len_for_signature()); - self.encode_for_signing(&mut buf); - alloy_primitives::utils::keccak256(&buf) + fn encoded_len_with_signature(&self, signature: &Signature) -> usize { + // this counts the tx fields and signature fields + let payload_length = self.fields_len() + signature.rlp_vrs_len(); + + // this counts: + // * tx type byte + // * inner header length + // * inner payload length + 1 + alloy_rlp::Header { list: true, payload_length }.length() + payload_length } } impl Transaction for DepositTransactionRequest { - type Signature = Signature; - - fn chain_id(&self) -> Option { - None - } - - fn gas_limit(&self) -> u64 { - self.gas_limit.to::() - } - - fn nonce(&self) -> u64 { - u64::MAX + fn input(&self) -> &[u8] { + &self.input } - fn decode_signed(buf: &mut &[u8]) -> alloy_rlp::Result> - where - Self: Sized, - { - let header = alloy_rlp::Header::decode(buf)?; - if !header.list { - return Err(alloy_rlp::Error::UnexpectedString); - } - - let tx = Self::decode_inner(buf)?; - let signature = Signature::decode_rlp_vrs(buf)?; - - Ok(tx.into_signed(signature)) + /// Get `to`. + fn to(&self) -> TxKind { + self.kind } - fn encode_signed(&self, signature: &Signature, out: &mut dyn bytes::BufMut) { - self.encode_with_signature(signature, out) + /// Get `value`. + fn value(&self) -> U256 { + self.value } - fn gas_price(&self) -> Option { + /// Get `chain_id`. + fn chain_id(&self) -> Option { None } - fn input(&self) -> &[u8] { - &self.input + /// Get `nonce`. + fn nonce(&self) -> u64 { + u64::MAX } - fn input_mut(&mut self) -> &mut Bytes { - &mut self.input + /// Get `gas_limit`. + fn gas_limit(&self) -> u128 { + self.gas_limit } - fn into_signed(self, signature: Signature) -> alloy_network::Signed - where - Self: Sized, - { - alloy_network::Signed::new_unchecked(self.clone(), signature, self.signature_hash()) + /// Get `gas_price`. + fn gas_price(&self) -> Option { + None } +} +impl SignableTransaction for DepositTransactionRequest { fn set_chain_id(&mut self, _chain_id: ChainId) {} - fn set_gas_limit(&mut self, limit: u64) { - self.gas_limit = U256::from(limit); - } - - fn set_gas_price(&mut self, _price: U256) {} - - fn set_input(&mut self, data: Bytes) { - self.input = data; - } - - fn set_nonce(&mut self, _nonce: u64) {} - - fn set_to(&mut self, to: TxKind) { - self.kind = to; - } - - fn set_value(&mut self, value: U256) { - self.value = value; - } - - fn signature_hash(&self) -> B256 { - self.signature_hash() + fn payload_len_for_signature(&self) -> usize { + self.payload_len_for_signature() } - fn to(&self) -> TxKind { - self.kind - } + fn into_signed(self, signature: Signature) -> Signed { + let mut buf = Vec::with_capacity(self.encoded_len_with_signature(&signature)); + self.encode_with_signature(&signature, &mut buf); + let hash = keccak256(&buf); - fn value(&self) -> U256 { - self.value + // Drop any v chain id value to ensure the signature format is correct at the time of + // combination for an EIP-4844 transaction. V should indicate the y-parity of the + // signature. + Signed::new_unchecked(self, signature.with_parity_bool(), hash) } fn encode_for_signing(&self, out: &mut dyn alloy_rlp::BufMut) { - self.encode_for_signing(out) - } - - fn payload_len_for_signature(&self) -> usize { - self.payload_len_for_signature() + self.encode_for_signing(out); } } @@ -265,19 +230,19 @@ impl Encodable for DepositTransactionRequest { /// See #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct DepositTransaction { - pub nonce: U256, + pub nonce: u64, pub source_hash: B256, pub from: Address, pub kind: TxKind, pub mint: U256, pub value: U256, - pub gas_limit: U256, + pub gas_limit: u128, pub is_system_tx: bool, pub input: Bytes, } impl DepositTransaction { - pub fn nonce(&self) -> &U256 { + pub fn nonce(&self) -> &u64 { &self.nonce } @@ -336,7 +301,7 @@ impl DepositTransaction { /// - `input` pub fn decode_inner(buf: &mut &[u8]) -> Result { Ok(Self { - nonce: U256::ZERO, + nonce: 0, source_hash: Decodable::decode(buf)?, from: Decodable::decode(buf)?, kind: Decodable::decode(buf)?, diff --git a/crates/anvil/core/src/types.rs b/crates/anvil/core/src/types.rs index db966d956..2e0c63b13 100644 --- a/crates/anvil/core/src/types.rs +++ b/crates/anvil/core/src/types.rs @@ -190,10 +190,10 @@ pub struct NodeInfo { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] pub struct NodeEnvironment { - pub base_fee: U256, + pub base_fee: u128, pub chain_id: u64, - pub gas_limit: U256, - pub gas_price: U256, + pub gas_limit: u128, + pub gas_price: u128, } #[derive(Clone, Debug, Default, PartialEq, Eq)] diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index 9cde54553..8e2915241 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -5,7 +5,7 @@ use crate::{ }; use alloy_genesis::Genesis; use alloy_primitives::{utils::Unit, U256}; -use alloy_signer::coins_bip39::{English, Mnemonic}; +use alloy_signer_wallet::coins_bip39::{English, Mnemonic}; use anvil_server::ServerConfig; use clap::Parser; use core::fmt; @@ -194,9 +194,9 @@ impl NodeArgs { }; NodeConfig::default() - .with_gas_limit(self.evm_opts.gas_limit.map(U256::from)) + .with_gas_limit(self.evm_opts.gas_limit) .disable_block_gas_limit(self.evm_opts.disable_block_gas_limit) - .with_gas_price(self.evm_opts.gas_price.map(U256::from)) + .with_gas_price(self.evm_opts.gas_price) .with_hardfork(self.hardfork) .with_blocktime(self.block_time) .with_no_mining(self.no_mining) @@ -216,7 +216,7 @@ impl NodeArgs { .fork_retry_backoff(self.evm_opts.fork_retry_backoff.map(Duration::from_millis)) .fork_compute_units_per_second(compute_units_per_second) .with_eth_rpc_url(self.evm_opts.fork_url.map(|fork| fork.url)) - .with_base_fee(self.evm_opts.block_base_fee_per_gas.map(U256::from)) + .with_base_fee(self.evm_opts.block_base_fee_per_gas) .with_storage_caching(self.evm_opts.no_storage_caching) .with_server_config(self.server_config) .with_host(self.host) @@ -455,7 +455,7 @@ pub struct AnvilEvmArgs { /// The block gas limit. #[arg(long, alias = "block-gas-limit", help_heading = "Environment config")] - pub gas_limit: Option, + pub gas_limit: Option, /// Disable the `call.gas_limit <= block.gas_limit` constraint. #[arg( @@ -474,7 +474,7 @@ pub struct AnvilEvmArgs { /// The gas price. #[arg(long, help_heading = "Environment config")] - pub gas_price: Option, + pub gas_price: Option, /// The base fee in a block. #[arg( @@ -483,7 +483,7 @@ pub struct AnvilEvmArgs { value_name = "FEE", help_heading = "Environment config" )] - pub block_base_fee_per_gas: Option, + pub block_base_fee_per_gas: Option, /// The chain ID. #[arg(long, alias = "chain", help_heading = "Environment config")] diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index a1e736619..58de505ed 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -16,14 +16,16 @@ use crate::{ FeeManager, Hardfork, }; use alloy_genesis::Genesis; +use alloy_network::AnyNetwork; use alloy_primitives::{hex, utils::Unit, U256}; -use alloy_providers::tmp::TempProvider; +use alloy_provider::Provider; use alloy_rpc_types::BlockNumberOrTag; -use alloy_signer::{ +use alloy_signer::Signer; +use alloy_signer_wallet::{ coins_bip39::{English, Mnemonic}, - LocalWallet, MnemonicBuilder, Signer as AlloySigner, + LocalWallet, MnemonicBuilder, }; -use alloy_transport::TransportError; +use alloy_transport::{Transport, TransportError}; use anvil_server::ServerConfig; use foundry_common::{ provider::alloy::ProviderBuilder, ALCHEMY_FREE_TIER_CUPS, NON_ARCHIVE_NODE_WARNING, @@ -91,13 +93,13 @@ pub struct NodeConfig { /// Chain ID of the EVM chain pub chain_id: Option, /// Default gas limit for all txs - pub gas_limit: U256, + pub gas_limit: u128, /// If set to `true`, disables the block gas limit pub disable_block_gas_limit: bool, /// Default gas price for all txs - pub gas_price: Option, + pub gas_price: Option, /// Default base fee - pub base_fee: Option, + pub base_fee: Option, /// The hardfork to use pub hardfork: Option, /// Signer accounts that will be initialised with `genesis_balance` in the genesis block @@ -376,7 +378,7 @@ impl Default for NodeConfig { let genesis_accounts = AccountGenerator::new(10).phrase(DEFAULT_MNEMONIC).gen(); Self { chain_id: None, - gas_limit: U256::from(30_000_000), + gas_limit: 30_000_000, disable_block_gas_limit: false, gas_price: None, hardfork: None, @@ -432,15 +434,15 @@ impl NodeConfig { self } /// Returns the base fee to use - pub fn get_base_fee(&self) -> U256 { + pub fn get_base_fee(&self) -> u128 { self.base_fee - .or_else(|| self.genesis.as_ref().and_then(|g| g.base_fee_per_gas.map(U256::from))) - .unwrap_or_else(|| U256::from(INITIAL_BASE_FEE)) + .or_else(|| self.genesis.as_ref().and_then(|g| g.base_fee_per_gas)) + .unwrap_or(INITIAL_BASE_FEE) } /// Returns the base fee to use - pub fn get_gas_price(&self) -> U256 { - self.gas_price.unwrap_or_else(|| U256::from(INITIAL_GAS_PRICE)) + pub fn get_gas_price(&self) -> u128 { + self.gas_price.unwrap_or(INITIAL_GAS_PRICE) } /// Returns the base fee to use @@ -497,7 +499,7 @@ impl NodeConfig { /// Sets the gas limit #[must_use] - pub fn with_gas_limit(mut self, gas_limit: Option) -> Self { + pub fn with_gas_limit(mut self, gas_limit: Option) -> Self { if let Some(gas_limit) = gas_limit { self.gas_limit = gas_limit; } @@ -515,8 +517,8 @@ impl NodeConfig { /// Sets the gas price #[must_use] - pub fn with_gas_price(mut self, gas_price: Option) -> Self { - self.gas_price = gas_price.map(Into::into); + pub fn with_gas_price(mut self, gas_price: Option) -> Self { + self.gas_price = gas_price; self } @@ -539,8 +541,8 @@ impl NodeConfig { /// Sets the base fee #[must_use] - pub fn with_base_fee(mut self, base_fee: Option) -> Self { - self.base_fee = base_fee.map(Into::into); + pub fn with_base_fee(mut self, base_fee: Option) -> Self { + self.base_fee = base_fee; self } @@ -857,8 +859,8 @@ impl NodeConfig { let env = revm::primitives::Env { cfg: cfg.cfg_env, block: BlockEnv { - gas_limit: self.gas_limit, - basefee: self.get_base_fee(), + gas_limit: U256::from(self.gas_limit), + basefee: U256::from(self.get_base_fee()), ..Default::default() }, tx: TxEnv { chain_id: self.get_chain_id().into(), ..Default::default() }, @@ -985,7 +987,7 @@ impl NodeConfig { // but only if we're forking mainnet let chain_id = provider.get_chain_id().await.expect("Failed to fetch network chain ID"); - if alloy_chains::NamedChain::Mainnet == chain_id.to::() { + if alloy_chains::NamedChain::Mainnet == chain_id { let hardfork: Hardfork = fork_block_number.into(); env.handler_cfg.spec_id = hardfork.into(); self.hardfork = Some(hardfork); @@ -1030,19 +1032,19 @@ 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 - let gas_limit = if self.disable_block_gas_limit || block.header.gas_limit.is_zero() { - U256::from(u64::MAX) + let gas_limit = if self.disable_block_gas_limit || block.header.gas_limit == 0 { + u128::MAX } else { block.header.gas_limit }; env.block = BlockEnv { number: U256::from(fork_block_number), - timestamp: block.header.timestamp, + timestamp: U256::from(block.header.timestamp), difficulty: block.header.difficulty, // ensures prevrandao is set prevrandao: Some(block.header.mix_hash.unwrap_or_default()), - gas_limit, + gas_limit: U256::from(gas_limit), // Keep previous `coinbase` and `basefee` value coinbase: env.block.coinbase, basefee: env.block.basefee, @@ -1053,7 +1055,7 @@ latest block number: {latest_block}" if self.base_fee.is_none() { if let Some(base_fee) = block.header.base_fee_per_gas { self.base_fee = Some(base_fee); - env.block.basefee = base_fee; + env.block.basefee = U256::from(base_fee); // this is the base fee of the current block, but we need the base fee of // the next block let next_block_base_fee = fees.get_next_block_base_fee_per_gas( @@ -1062,7 +1064,7 @@ latest block number: {latest_block}" block.header.base_fee_per_gas.unwrap_or_default(), ); // update next base fee - fees.set_base_fee(U256::from(next_block_base_fee)); + fees.set_base_fee(next_block_base_fee); } } @@ -1080,9 +1082,9 @@ latest block number: {latest_block}" chain_id } else { let chain_id = if let Some(fork_chain_id) = fork_chain_id { - fork_chain_id.to::() + fork_chain_id.to() } else { - provider.get_chain_id().await.unwrap().to::() + provider.get_chain_id().await.unwrap() }; // need to update the dev signers and env with the chain id @@ -1117,7 +1119,7 @@ latest block number: {latest_block}" provider, chain_id, override_chain_id, - timestamp: block.header.timestamp.to::(), + timestamp: block.header.timestamp, base_fee: block.header.base_fee_per_gas, timeout: self.fork_request_timeout, retries: self.fork_request_retries, @@ -1241,7 +1243,9 @@ pub fn anvil_tmp_dir() -> Option { /// /// This fetches the "latest" block and checks whether the `Block` is fully populated (`hash` field /// is present). This prevents edge cases where anvil forks the "latest" block but `eth_getBlockByNumber` still returns a pending block, -async fn find_latest_fork_block(provider: P) -> Result { +async fn find_latest_fork_block, T: Transport + Clone>( + provider: P, +) -> Result { let mut num = provider.get_block_number().await?; // walk back from the head of the chain, but at most 2 blocks, which should be more than enough diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index aee967084..d21ed852f 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -31,29 +31,27 @@ use crate::{ revm::primitives::Output, ClientFork, LoggingManager, Miner, MiningMode, StorageInfo, }; -use alloy_consensus::TxLegacy; use alloy_dyn_abi::TypedData; -use alloy_network::{Signed, TxKind}; -use alloy_primitives::{Address, Bytes, TxHash, B256, B64, U256, U64}; -use alloy_rlp::Decodable; -use alloy_rpc_trace_types::{ - geth::{DefaultFrame, GethDebugTracingOptions, GethDefaultTracingOptions, GethTrace}, - parity::LocalizedTransactionTrace, -}; +use alloy_network::eip2718::Decodable2718; +use alloy_primitives::{Address, Bytes, TxHash, TxKind, B256, B64, U256, U64}; use alloy_rpc_types::{ request::TransactionRequest, state::StateOverride, txpool::{TxpoolContent, TxpoolInspect, TxpoolInspectSummary, TxpoolStatus}, AccessList, AccessListWithGasUsed, Block, BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, EIP1186AccountProofResponse, FeeHistory, Filter, FilteredParams, Log, - Transaction, TransactionReceipt, + Transaction, WithOtherFields, +}; +use alloy_rpc_types_trace::{ + geth::{DefaultFrame, GethDebugTracingOptions, GethDefaultTracingOptions, GethTrace}, + parity::LocalizedTransactionTrace, }; use alloy_transport::TransportErrorKind; use anvil_core::{ eth::{ block::BlockInfo, transaction::{ - transaction_request_to_typed, PendingTransaction, TypedTransaction, + transaction_request_to_typed, PendingTransaction, ReceiptResponse, TypedTransaction, TypedTransactionRequest, }, EthRequest, @@ -447,11 +445,6 @@ impl EthApi { Err(BlockchainError::NoSignerAvailable) } - /// Queries the current gas limit - fn current_gas_limit(&self) -> Result { - Ok(self.backend.gas_limit()) - } - async fn block_request(&self, block_number: Option) -> Result { let block_request = match block_number { Some(BlockId::Number(BlockNumber::Pending)) => { @@ -550,7 +543,7 @@ impl EthApi { /// Returns the current gas price pub fn gas_price(&self) -> Result { - Ok(self.backend.gas_price()) + Ok(U256::from(self.backend.gas_price())) } /// Returns a fee per gas that is an estimate of how much you can pay as a priority fee, or @@ -558,12 +551,12 @@ impl EthApi { /// /// Handler for ETH RPC call: `eth_maxPriorityFeePerGas` pub fn gas_max_priority_fee_per_gas(&self) -> Result { - Ok(self.backend.max_priority_fee_per_gas()) + Ok(U256::from(self.backend.max_priority_fee_per_gas())) } /// Returns the block gas limit pub fn gas_limit(&self) -> U256 { - self.backend.gas_limit() + U256::from(self.backend.gas_limit()) } /// Returns the accounts list @@ -690,7 +683,7 @@ impl EthApi { block_number: Option, ) -> Result { node_info!("eth_getTransactionCount"); - self.get_transaction_count(address, block_number).await + self.get_transaction_count(address, block_number).await.map(U256::from) } /// Returns the number of transactions in a block with given hash. @@ -845,7 +838,10 @@ impl EthApi { /// Signs a transaction /// /// Handler for ETH RPC call: `eth_signTransaction` - pub async fn sign_transaction(&self, mut request: TransactionRequest) -> Result { + pub async fn sign_transaction( + &self, + mut request: WithOtherFields, + ) -> Result { node_info!("eth_signTransaction"); let from = request.from.map(Ok).unwrap_or_else(|| { @@ -857,7 +853,7 @@ impl EthApi { if request.gas.is_none() { // estimate if not provided if let Ok(gas) = self.estimate_gas(request.clone(), None, None).await { - request.gas = Some(gas); + request.gas = Some(gas.to()); } } @@ -872,7 +868,10 @@ impl EthApi { /// Sends a transaction /// /// Handler for ETH RPC call: `eth_sendTransaction` - pub async fn send_transaction(&self, mut request: TransactionRequest) -> Result { + pub async fn send_transaction( + &self, + mut request: WithOtherFields, + ) -> Result { node_info!("eth_sendTransaction"); let from = request.from.map(Ok).unwrap_or_else(|| { @@ -883,7 +882,7 @@ impl EthApi { if request.gas.is_none() { // estimate if not provided if let Ok(gas) = self.estimate_gas(request.clone(), None, None).await { - request.gas = Some(gas); + request.gas = Some(gas.to()); } } @@ -904,7 +903,7 @@ impl EthApi { self.backend.validate_pool_transaction(&pending_transaction).await?; let requires = required_marker(nonce, on_chain_nonce, from); - let provides = vec![to_marker(nonce.to::(), from)]; + let provides = vec![to_marker(nonce, from)]; debug_assert!(requires != provides); self.add_pending_transaction(pending_transaction, requires, provides) @@ -919,26 +918,11 @@ impl EthApi { if data.is_empty() { return Err(BlockchainError::EmptyRawTransactionData); } - let transaction = if data[0] > 0x7f { - // legacy transaction - match Signed::::decode(&mut data) { - Ok(transaction) => TypedTransaction::Legacy(transaction), - Err(_) => return Err(BlockchainError::FailedToDecodeSignedTransaction), - } - } else { - // the [TypedTransaction] requires a valid rlp input, - // but EIP-1559 prepends a version byte, so we need to encode the data first to get a - // valid rlp and then rlp decode impl of `TypedTransaction` will remove and check the - // version byte - let extend = alloy_rlp::encode(data); - let tx = match TypedTransaction::decode(&mut &extend[..]) { - Ok(transaction) => transaction, - Err(_) => return Err(BlockchainError::FailedToDecodeSignedTransaction), - }; + let transaction = TypedTransaction::decode_2718(&mut data) + .map_err(|_| BlockchainError::FailedToDecodeSignedTransaction)?; + + self.ensure_typed_transaction_supported(&transaction)?; - self.ensure_typed_transaction_supported(&tx)?; - tx - }; let pending_transaction = PendingTransaction::new(transaction)?; // pre-validate @@ -952,7 +936,7 @@ impl EthApi { let priority = self.transaction_priority(&pending_transaction.transaction); let pool_transaction = PoolTransaction { requires, - provides: vec![to_marker(nonce.to::(), *pending_transaction.sender())], + provides: vec![to_marker(nonce, *pending_transaction.sender())], pending_transaction, priority, }; @@ -967,7 +951,7 @@ impl EthApi { /// Handler for ETH RPC call: `eth_call` pub async fn call( &self, - request: TransactionRequest, + request: WithOtherFields, block_number: Option, overrides: Option, ) -> Result { @@ -1020,7 +1004,7 @@ impl EthApi { /// Handler for ETH RPC call: `eth_createAccessList` pub async fn create_access_list( &self, - mut request: TransactionRequest, + mut request: WithOtherFields, block_number: Option, ) -> Result { node_info!("eth_createAccessList"); @@ -1069,7 +1053,7 @@ impl EthApi { /// Handler for ETH RPC call: `eth_estimateGas` pub async fn estimate_gas( &self, - request: TransactionRequest, + request: WithOtherFields, block_number: Option, overrides: Option, ) -> Result { @@ -1080,6 +1064,7 @@ impl EthApi { overrides, ) .await + .map(U256::from) } /// Get transaction by its hash. @@ -1088,7 +1073,10 @@ 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(); @@ -1118,7 +1106,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 } @@ -1130,7 +1118,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 } @@ -1138,7 +1126,7 @@ impl EthApi { /// Returns transaction receipt by transaction hash. /// /// Handler for ETH RPC call: `eth_getTransactionReceipt` - pub async fn transaction_receipt(&self, hash: B256) -> Result> { + pub async fn transaction_receipt(&self, hash: B256) -> Result> { node_info!("eth_getTransactionReceipt"); let tx = self.pool.get_transaction(hash); if tx.is_some() { @@ -1153,7 +1141,7 @@ impl EthApi { pub async fn block_receipts( &self, number: BlockNumber, - ) -> Result>> { + ) -> Result>> { node_info!("eth_getBlockReceipts"); self.backend.block_receipts(number).await } @@ -1266,7 +1254,7 @@ impl EthApi { // efficiently, instead we fetch it from the fork if fork.predates_fork_inclusive(number) { return fork - .fee_history(block_count, BlockNumber::Number(number), &reward_percentiles) + .fee_history(block_count.to(), BlockNumber::Number(number), &reward_percentiles) .await .map_err(BlockchainError::AlloyForkProvider); } @@ -1285,7 +1273,7 @@ impl EthApi { } let mut response = FeeHistory { - oldest_block: U256::from(lowest), + oldest_block: lowest, base_fee_per_gas: Vec::new(), gas_used_ratio: Vec::new(), reward: Some(Default::default()), @@ -1301,7 +1289,7 @@ impl EthApi { for n in lowest..=highest { // if let Some(block) = fee_history.get(&n) { - response.base_fee_per_gas.push(U256::from(block.base_fee)); + response.base_fee_per_gas.push(block.base_fee); response.gas_used_ratio.push(block.gas_used_ratio); // requested percentiles @@ -1311,11 +1299,7 @@ impl EthApi { for p in &reward_percentiles { let p = p.clamp(0.0, 100.0); let index = ((p.round() / 2f64) * 2f64) * resolution_per_percentile; - let reward = if let Some(r) = block.rewards.get(index as usize) { - U256::from(*r) - } else { - U256::ZERO - }; + let reward = block.rewards.get(index as usize).map_or(0, |r| *r); block_rewards.push(reward); } rewards.push(block_rewards); @@ -1334,20 +1318,20 @@ impl EthApi { (response.gas_used_ratio.last(), response.base_fee_per_gas.last()) { let elasticity = self.backend.elasticity(); - let last_fee_per_gas = last_fee_per_gas.to::() as f64; + let last_fee_per_gas = *last_fee_per_gas as f64; if last_gas_used > &0.5 { // increase base gas let increase = ((last_gas_used - 0.5) * 2f64) * elasticity; - let new_base_fee = (last_fee_per_gas + (last_fee_per_gas * increase)) as u64; - response.base_fee_per_gas.push(U256::from(new_base_fee)); + let new_base_fee = (last_fee_per_gas + (last_fee_per_gas * increase)) as u128; + response.base_fee_per_gas.push(new_base_fee); } else if last_gas_used < &0.5 { // decrease gas let increase = ((0.5 - last_gas_used) * 2f64) * elasticity; - let new_base_fee = (last_fee_per_gas - (last_fee_per_gas * increase)) as u64; - response.base_fee_per_gas.push(U256::from(new_base_fee)); + let new_base_fee = (last_fee_per_gas - (last_fee_per_gas * increase)) as u128; + response.base_fee_per_gas.push(new_base_fee); } else { // same base gas - response.base_fee_per_gas.push(U256::from(last_fee_per_gas as u64)); + response.base_fee_per_gas.push(last_fee_per_gas as u128); } } @@ -1362,7 +1346,7 @@ impl EthApi { /// Handler for ETH RPC call: `eth_maxPriorityFeePerGas` pub fn max_priority_fee_per_gas(&self) -> Result { node_info!("eth_maxPriorityFeePerGas"); - Ok(self.backend.max_priority_fee_per_gas()) + Ok(U256::from(self.backend.max_priority_fee_per_gas())) } /// Creates a filter object, based on filter options, to notify when the state changes (logs). @@ -1447,7 +1431,7 @@ impl EthApi { /// Handler for RPC call: `debug_traceCall` pub async fn debug_trace_call( &self, - request: TransactionRequest, + request: WithOtherFields, block_number: Option, opts: GethDefaultTracingOptions, ) -> Result { @@ -1671,7 +1655,7 @@ impl EthApi { ) .into()); } - self.backend.set_gas_price(gas); + self.backend.set_gas_price(gas.to()); Ok(()) } @@ -1686,7 +1670,7 @@ impl EthApi { ) .into()); } - self.backend.set_base_fee(basefee); + self.backend.set_base_fee(basefee.to()); Ok(()) } @@ -1842,7 +1826,7 @@ impl EthApi { /// Handler for RPC call: `evm_setBlockGasLimit` pub fn evm_set_block_gas_limit(&self, gas_limit: U256) -> Result { node_info!("evm_setBlockGasLimit"); - self.backend.set_gas_limit(gas_limit); + self.backend.set_gas_limit(gas_limit.to()); Ok(true) } @@ -1907,7 +1891,7 @@ impl EthApi { if let Some(receipt) = self.backend.mined_transaction_receipt(tx.hash) { if let Some(output) = receipt.out { // insert revert reason if failure - if receipt.inner.status_code.unwrap_or_default().to::() == 0 { + if !receipt.inner.inner.as_receipt_with_bloom().receipt.status { if let Some(reason) = RevertDecoder::new().maybe_decode(&output, None) { @@ -1980,7 +1964,7 @@ impl EthApi { /// Handler for ETH RPC call: `eth_sendUnsignedTransaction` pub async fn eth_send_unsigned_transaction( &self, - request: TransactionRequest, + request: WithOtherFields, ) -> Result { node_info!("eth_sendUnsignedTransaction"); // either use the impersonated account of the request's `from` field @@ -2001,7 +1985,7 @@ impl EthApi { self.backend.validate_pool_transaction(&pending_transaction).await?; let requires = required_marker(nonce, on_chain_nonce, from); - let provides = vec![to_marker(nonce.to::(), from)]; + let provides = vec![to_marker(nonce, from)]; self.add_pending_transaction(pending_transaction, requires, provides) } @@ -2029,9 +2013,9 @@ impl EthApi { fn convert(tx: Arc) -> TxpoolInspectSummary { let tx = &tx.pending_transaction.transaction; let to = tx.to(); - let gas_price = tx.gas_price(); + let gas_price = U256::from(tx.gas_price()); let value = tx.value(); - let gas = tx.gas_limit(); + let gas = U256::from(tx.gas_limit()); TxpoolInspectSummary { to, value, gas, gas_price } } @@ -2076,7 +2060,7 @@ 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 + tx.inner } for pending in self.pool.ready_transactions() { @@ -2146,10 +2130,10 @@ impl EthApi { async fn do_estimate_gas( &self, - request: TransactionRequest, + request: WithOtherFields, block_number: Option, overrides: Option, - ) -> Result { + ) -> Result { let block_request = self.block_request(block_number).await?; // check if the number predates the fork, if in fork mode if let BlockRequest::Number(number) = block_request { @@ -2183,10 +2167,10 @@ impl EthApi { /// This will execute the [CallRequest] and find the best gas limit via binary search fn do_estimate_gas_with_state( &self, - mut request: TransactionRequest, + mut request: WithOtherFields, state: D, block_env: BlockEnv, - ) -> Result + ) -> Result where D: DatabaseRef, { @@ -2212,11 +2196,11 @@ impl EthApi { // get the highest possible gas limit, either the request's set value or the currently // configured gas limit - let mut highest_gas_limit = request.gas.unwrap_or(block_env.gas_limit); + let mut highest_gas_limit = request.gas.unwrap_or(block_env.gas_limit.to()); let gas_price = fees.gas_price.unwrap_or_default(); // If we have non-zero gas price, cap gas limit by sender balance - if !gas_price.is_zero() { + if gas_price > 0 { if let Some(from) = request.from { let mut available_funds = self.backend.get_balance_with_state(&state, from)?; if let Some(value) = request.value { @@ -2227,7 +2211,8 @@ impl EthApi { available_funds -= value; } // amount of gas the sender can afford with the `gas_price` - let allowance = available_funds.checked_div(gas_price).unwrap_or_default(); + let allowance = + available_funds.to::().checked_div(gas_price).unwrap_or_default(); highest_gas_limit = std::cmp::min(highest_gas_limit, allowance); } } @@ -2240,7 +2225,7 @@ impl EthApi { self.backend.call_with_state(&state, call_to_estimate, fees.clone(), block_env.clone()); let gas_used = match ethres.try_into()? { - GasEstimationCallResult::Success(gas) => Ok(U256::from(gas)), + GasEstimationCallResult::Success(gas) => Ok(gas), GasEstimationCallResult::OutOfGas => { Err(InvalidTransactionError::BasicOutOfGas(highest_gas_limit).into()) } @@ -2262,13 +2247,11 @@ impl EthApi { let mut lowest_gas_limit = determine_base_gas_by_kind(&request); // pick a point that's close to the estimated gas - let mut mid_gas_limit = std::cmp::min( - gas_used * U256::from(3), - (highest_gas_limit + lowest_gas_limit) / U256::from(2), - ); + let mut mid_gas_limit = + std::cmp::min(gas_used * 3, (highest_gas_limit + lowest_gas_limit) / 2); // Binary search for the ideal gas limit - while (highest_gas_limit - lowest_gas_limit) > U256::from(1) { + while (highest_gas_limit - lowest_gas_limit) > 1 { request.gas = Some(mid_gas_limit); let ethres = self.backend.call_with_state( &state, @@ -2297,7 +2280,7 @@ impl EthApi { } }; // new midpoint - mid_gas_limit = (highest_gas_limit + lowest_gas_limit) / U256::from(2); + mid_gas_limit = (highest_gas_limit + lowest_gas_limit) / 2; } trace!(target : "node", "Estimated Gas for call {:?}", highest_gas_limit); @@ -2398,7 +2381,7 @@ impl EthApi { Some(info), Some(base_fee), ); - block_transactions.push(tx); + block_transactions.push(tx.inner); } Some(partial_block.into_full_block(block_transactions)) @@ -2406,40 +2389,40 @@ impl EthApi { fn build_typed_tx_request( &self, - request: TransactionRequest, - nonce: U256, + request: WithOtherFields, + nonce: u64, ) -> Result { - let chain_id = request.chain_id.map(|c| c.to::()).unwrap_or_else(|| self.chain_id()); + let chain_id = request.chain_id.unwrap_or_else(|| self.chain_id()); let max_fee_per_gas = request.max_fee_per_gas; let gas_price = request.gas_price; - let gas_limit = request.gas.map(Ok).unwrap_or_else(|| self.current_gas_limit())?; + let gas_limit = request.gas.unwrap_or(self.backend.gas_limit()); let request = match transaction_request_to_typed(request) { Some(TypedTransactionRequest::Legacy(mut m)) => { - m.nonce = nonce.to::(); + m.nonce = nonce; m.chain_id = Some(chain_id); - m.gas_limit = gas_limit.to::(); + m.gas_limit = gas_limit; if gas_price.is_none() { - m.gas_price = self.gas_price().unwrap_or_default().to::(); + m.gas_price = self.backend.gas_price() } TypedTransactionRequest::Legacy(m) } Some(TypedTransactionRequest::EIP2930(mut m)) => { - m.nonce = nonce.to::(); + m.nonce = nonce; m.chain_id = chain_id; - m.gas_limit = gas_limit.to::(); + m.gas_limit = gas_limit; if gas_price.is_none() { - m.gas_price = self.gas_price().unwrap_or_default().to::(); + m.gas_price = self.backend.gas_price(); } TypedTransactionRequest::EIP2930(m) } Some(TypedTransactionRequest::EIP1559(mut m)) => { - m.nonce = nonce.to::(); + m.nonce = nonce; m.chain_id = chain_id; - m.gas_limit = gas_limit.to::(); + m.gas_limit = gas_limit; if max_fee_per_gas.is_none() { - m.max_fee_per_gas = self.gas_price().unwrap_or_default().to::(); + m.max_fee_per_gas = self.backend.gas_price(); } TypedTransactionRequest::EIP1559(m) } @@ -2462,7 +2445,7 @@ impl EthApi { &self, address: Address, block_number: Option, - ) -> Result { + ) -> Result { let block_request = self.block_request(block_number).await?; if let BlockRequest::Number(number) = block_request { @@ -2473,9 +2456,7 @@ impl EthApi { } } - let nonce = self.backend.get_nonce(address, block_request).await?; - - Ok(nonce) + self.backend.get_nonce(address, block_request).await } /// Returns the nonce for this request @@ -2489,10 +2470,10 @@ impl EthApi { &self, request: &TransactionRequest, from: Address, - ) -> Result<(U256, U256)> { + ) -> Result<(u64, u64)> { let highest_nonce = self.get_transaction_count(from, Some(BlockId::Number(BlockNumber::Pending))).await?; - let nonce = request.nonce.map(|n| n.to::()).unwrap_or(highest_nonce); + let nonce = request.nonce.unwrap_or(highest_nonce); Ok((nonce, highest_nonce)) } @@ -2530,13 +2511,13 @@ impl EthApi { } } -fn required_marker(provided_nonce: U256, on_chain_nonce: U256, from: Address) -> Vec { +fn required_marker(provided_nonce: u64, on_chain_nonce: u64, from: Address) -> Vec { if provided_nonce == on_chain_nonce { return Vec::new(); } - let prev_nonce = provided_nonce.saturating_sub(U256::from(1)); + let prev_nonce = provided_nonce.saturating_sub(1); if on_chain_nonce <= prev_nonce { - vec![to_marker(prev_nonce.to::(), from)] + vec![to_marker(prev_nonce, from)] } else { Vec::new() } @@ -2562,7 +2543,7 @@ fn ensure_return_ok(exit: InstructionResult, out: &Option) -> Result U256 { +fn determine_base_gas_by_kind(request: &WithOtherFields) -> u128 { match transaction_request_to_typed(request.clone()) { Some(request) => match request { TypedTransactionRequest::Legacy(req) => match req.to { @@ -2577,10 +2558,7 @@ fn determine_base_gas_by_kind(request: &TransactionRequest) -> U256 { TxKind::Call(_) => MIN_TRANSACTION_GAS, TxKind::Create => MIN_CREATE_GAS, }, - TypedTransactionRequest::EIP4844(req) => match req.tx().to { - TxKind::Call(_) => MIN_TRANSACTION_GAS, - TxKind::Create => MIN_CREATE_GAS, - }, + TypedTransactionRequest::EIP4844(_) => MIN_TRANSACTION_GAS, TypedTransactionRequest::Deposit(req) => match req.kind { TxKind::Call(_) => MIN_TRANSACTION_GAS, TxKind::Create => MIN_CREATE_GAS, @@ -2594,17 +2572,17 @@ fn determine_base_gas_by_kind(request: &TransactionRequest) -> U256 { /// Keeps result of a call to revm EVM used for gas estimation enum GasEstimationCallResult { - Success(u64), + Success(u128), OutOfGas, Revert(Option), EvmError(InstructionResult), } /// Converts the result of a call to revm EVM into a [GasEstimationCallRes]. -impl TryFrom, u64, State)>> for GasEstimationCallResult { +impl TryFrom, u128, State)>> for GasEstimationCallResult { type Error = BlockchainError; - fn try_from(res: Result<(InstructionResult, Option, u64, State)>) -> Result { + fn try_from(res: Result<(InstructionResult, Option, u128, State)>) -> Result { match res { // Exceptional case: init used too much gas, treated as out of gas error Err(BlockchainError::InvalidTransaction(InvalidTransactionError::GasTooHigh(_))) => { diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index 5e68924c9..80a72beed 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -7,10 +7,12 @@ use crate::{ mem::inspector::Inspector, }; use alloy_consensus::{Header, Receipt, ReceiptWithBloom}; -use alloy_primitives::{Bloom, BloomInput, Log, B256, U256}; +use alloy_primitives::{Bloom, BloomInput, Log, B256}; use anvil_core::eth::{ block::{Block, BlockInfo, PartialHeader}, - transaction::{PendingTransaction, TransactionInfo, TypedReceipt, TypedTransaction}, + transaction::{ + DepositReceipt, PendingTransaction, TransactionInfo, TypedReceipt, TypedTransaction, + }, trie, }; use foundry_evm::{ @@ -33,7 +35,7 @@ pub struct ExecutedTransaction { transaction: Arc, exit_reason: InstructionResult, out: Option, - gas_used: u64, + gas_used: u128, logs: Vec, traces: Vec, nonce: u64, @@ -43,54 +45,25 @@ pub struct ExecutedTransaction { impl ExecutedTransaction { /// Creates the receipt for the transaction - fn create_receipt(&self) -> TypedReceipt { - let used_gas = U256::from(self.gas_used); - let mut bloom = Bloom::default(); - logs_bloom(self.logs.clone(), &mut bloom); + fn create_receipt(&self, cumulative_gas_used: &mut u128) -> TypedReceipt { let logs = self.logs.clone(); + *cumulative_gas_used = cumulative_gas_used.saturating_add(self.gas_used); // successful return see [Return] let status_code = u8::from(self.exit_reason as u8 <= InstructionResult::SelfDestruct as u8); + let receipt_with_bloom: ReceiptWithBloom = + Receipt { status: status_code == 1, cumulative_gas_used: *cumulative_gas_used, logs } + .into(); + match &self.transaction.pending_transaction.transaction.transaction { - TypedTransaction::Legacy(_) => TypedReceipt::Legacy(ReceiptWithBloom { - receipt: Receipt { - success: status_code == 1, - cumulative_gas_used: used_gas.to::(), - logs, - }, - bloom, - }), - TypedTransaction::EIP2930(_) => TypedReceipt::EIP2930(ReceiptWithBloom { - receipt: Receipt { - success: status_code == 1, - cumulative_gas_used: used_gas.to::(), - logs, - }, - bloom, - }), - TypedTransaction::EIP1559(_) => TypedReceipt::EIP1559(ReceiptWithBloom { - receipt: Receipt { - success: status_code == 1, - cumulative_gas_used: used_gas.to::(), - logs, - }, - bloom, - }), - TypedTransaction::EIP4844(_) => TypedReceipt::EIP4844(ReceiptWithBloom { - receipt: Receipt { - success: status_code == 1, - cumulative_gas_used: used_gas.to::(), - logs, - }, - bloom, - }), - TypedTransaction::Deposit(_) => TypedReceipt::Deposit(ReceiptWithBloom { - receipt: Receipt { - success: status_code == 1, - cumulative_gas_used: used_gas.to::(), - logs, - }, - bloom, + TypedTransaction::Legacy(_) => TypedReceipt::Legacy(receipt_with_bloom), + TypedTransaction::EIP2930(_) => TypedReceipt::EIP2930(receipt_with_bloom), + TypedTransaction::EIP1559(_) => TypedReceipt::EIP1559(receipt_with_bloom), + TypedTransaction::EIP4844(_) => TypedReceipt::EIP4844(receipt_with_bloom), + TypedTransaction::Deposit(tx) => TypedReceipt::Deposit(DepositReceipt { + inner: receipt_with_bloom, + deposit_nonce: Some(tx.nonce), + deposit_nonce_version: Some(1), }), } } @@ -121,7 +94,7 @@ pub struct TransactionExecutor<'a, Db: ?Sized, Validator: TransactionValidator> pub cfg_env: CfgEnvWithHandlerCfg, pub parent_hash: B256, /// Cumulative gas used by all executed transactions - pub gas_used: U256, + pub gas_used: u128, pub enable_steps_tracing: bool, } @@ -132,17 +105,17 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' let mut transaction_infos = Vec::new(); let mut receipts = Vec::new(); let mut bloom = Bloom::default(); - let mut cumulative_gas_used = U256::ZERO; + let mut cumulative_gas_used: u128 = 0; let mut invalid = Vec::new(); let mut included = Vec::new(); - let gas_limit = self.block_env.gas_limit; + let gas_limit = self.block_env.gas_limit.to::(); let parent_hash = self.parent_hash; - let block_number = self.block_env.number; + let block_number = self.block_env.number.to::(); let difficulty = self.block_env.difficulty; let beneficiary = self.block_env.coinbase; let timestamp = self.block_env.timestamp.to::(); let base_fee = if (self.cfg_env.handler_cfg.spec_id as u8) >= (SpecId::LONDON as u8) { - Some(self.block_env.basefee) + Some(self.block_env.basefee.to::()) } else { None }; @@ -169,10 +142,9 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' continue } }; - let receipt = tx.create_receipt(); - cumulative_gas_used = cumulative_gas_used.saturating_add(receipt.gas_used()); + let receipt = tx.create_receipt(&mut cumulative_gas_used); let ExecutedTransaction { transaction, logs, out, traces, exit_reason: exit, .. } = tx; - logs_bloom(logs.clone(), &mut bloom); + build_logs_bloom(logs.clone(), &mut bloom); let contract_address = out.as_ref().and_then(|out| { if let Output::Create(_, contract_address) = out { @@ -183,15 +155,13 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' } }); - let transaction_index = transaction_infos.len() as u32; + let transaction_index = transaction_infos.len() as u64; let info = TransactionInfo { transaction_hash: transaction.hash(), transaction_index, from: *transaction.pending_transaction.sender(), to: transaction.pending_transaction.transaction.to(), contract_address, - logs, - logs_bloom: *receipt.logs_bloom(), traces, exit, out: match out { @@ -200,6 +170,7 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' _ => None, }, nonce: tx.nonce, + gas_used: tx.gas_used, }; transaction_infos.push(info); @@ -217,14 +188,14 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' receipts_root, logs_bloom: bloom, difficulty, - number: block_number.to::(), - gas_limit: gas_limit.to::(), - gas_used: cumulative_gas_used.to::(), + number: block_number, + gas_limit, + gas_used: cumulative_gas_used, timestamp, extra_data: Default::default(), mix_hash: Default::default(), nonce: Default::default(), - base_fee: base_fee.map(|b| b.to::()), + base_fee, }; let block = Block::new(partial_header, transactions.clone(), ommers); @@ -268,8 +239,8 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator }; let env = self.env_for(&transaction.pending_transaction); // check that we comply with the block's gas limit - let max_gas = self.gas_used.saturating_add(U256::from(env.tx.gas_limit)); - if max_gas > env.block.gas_limit { + let max_gas = self.gas_used.saturating_add(env.tx.gas_limit as u128); + if max_gas > env.block.gas_limit.to::() { return Some(TransactionExecutionOutcome::Exhausted(transaction)) } @@ -342,7 +313,7 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator trace!(target: "backend", ?exit_reason, ?gas_used, "[{:?}] executed with out={:?}", transaction.hash(), out); - self.gas_used = self.gas_used.saturating_add(U256::from(gas_used)); + self.gas_used = self.gas_used.saturating_add(gas_used as u128); trace!(target: "backend::executor", "transacted [{:?}], result: {:?} gas {}", transaction.hash(), exit_reason, gas_used); @@ -350,7 +321,7 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator transaction, exit_reason, out, - gas_used, + gas_used: gas_used as u128, logs: logs.unwrap_or_default(), traces: inspector .tracer @@ -366,7 +337,7 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator } /// Inserts all logs into the bloom -fn logs_bloom(logs: Vec, bloom: &mut Bloom) { +fn build_logs_bloom(logs: Vec, bloom: &mut Bloom) { for log in logs { bloom.accrue(BloomInput::Raw(&log.address[..])); for topic in log.topics() { diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index 04ea0c70f..3727740cd 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -2,17 +2,18 @@ use crate::eth::{backend::db::Db, error::BlockchainError}; use alloy_primitives::{Address, Bytes, StorageValue, B256, U256, U64}; -use alloy_providers::tmp::TempProvider; -use alloy_rpc_trace_types::{ - geth::{GethDebugTracingOptions, GethTrace}, - parity::LocalizedTransactionTrace as Trace, -}; +use alloy_provider::Provider; use alloy_rpc_types::{ request::TransactionRequest, AccessListWithGasUsed, Block, BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, EIP1186AccountProofResponse, FeeHistory, - Filter, Log, Transaction, TransactionReceipt, + Filter, Log, Transaction, WithOtherFields, +}; +use alloy_rpc_types_trace::{ + geth::{GethDebugTracingOptions, GethTrace}, + parity::LocalizedTransactionTrace as Trace, }; use alloy_transport::TransportError; +use anvil_core::eth::transaction::{convert_to_anvil_receipt, ReceiptResponse}; use foundry_common::provider::alloy::{ProviderBuilder, RetryProvider}; use parking_lot::{ lock_api::{RwLockReadGuard, RwLockWriteGuard}, @@ -66,7 +67,7 @@ impl ClientFork { let chain_id = if let Some(chain_id) = override_chain_id { chain_id } else { - self.provider().get_chain_id().await?.to::() + self.provider().get_chain_id().await? }; self.config.write().chain_id = chain_id; } @@ -75,11 +76,11 @@ impl ClientFork { let block = provider.get_block(block_number, false).await?.ok_or(BlockchainError::BlockNotFound)?; let block_hash = block.header.hash.ok_or(BlockchainError::BlockNotFound)?; - let timestamp = block.header.timestamp.to::(); + let timestamp = block.header.timestamp; let base_fee = block.header.base_fee_per_gas; let total_difficulty = block.header.total_difficulty.unwrap_or_default(); - let number = block.header.number.ok_or(BlockchainError::BlockNotFound)?.to::(); + let number = block.header.number.ok_or(BlockchainError::BlockNotFound)?; self.config.write().update_block(number, block_hash, timestamp, base_fee, total_difficulty); self.clear_cached_storage(); @@ -116,7 +117,7 @@ impl ClientFork { self.config.read().total_difficulty } - pub fn base_fee(&self) -> Option { + pub fn base_fee(&self) -> Option { self.config.read().base_fee } @@ -147,7 +148,7 @@ impl ClientFork { /// Returns the fee history `eth_feeHistory` pub async fn fee_history( &self, - block_count: U256, + block_count: u64, newest_block: BlockNumber, reward_percentiles: &[f64], ) -> Result { @@ -167,11 +168,11 @@ impl ClientFork { /// Sends `eth_call` pub async fn call( &self, - request: &TransactionRequest, + request: &WithOtherFields, block: Option, ) -> Result { let block = block.unwrap_or(BlockNumber::Latest); - let res = self.provider().call((*request).clone(), Some(block.into())).await?; + let res = self.provider().call(request, Some(block.into())).await?; Ok(res) } @@ -179,11 +180,11 @@ impl ClientFork { /// Sends `eth_call` pub async fn estimate_gas( &self, - request: &TransactionRequest, + request: &WithOtherFields, block: Option, - ) -> Result { + ) -> Result { let block = block.unwrap_or(BlockNumber::Latest); - let res = self.provider().estimate_gas(request.clone(), Some(block.into())).await?; + let res = self.provider().estimate_gas(request, Some(block.into())).await?; Ok(res) } @@ -191,10 +192,10 @@ impl ClientFork { /// Sends `eth_createAccessList` pub async fn create_access_list( &self, - request: &TransactionRequest, + request: &WithOtherFields, block: Option, ) -> Result { - self.provider().create_access_list(request.clone(), block.map(|b| b.into())).await + self.provider().create_access_list(request, block.map(|b| b.into())).await } pub async fn storage_at( @@ -211,7 +212,7 @@ impl ClientFork { return Ok(logs); } - let logs = self.provider().get_logs(filter.clone()).await?; + let logs = self.provider().get_logs(filter).await?; let mut storage = self.storage_write(); storage.logs.insert(filter.clone(), logs.clone()); @@ -230,7 +231,7 @@ impl ClientFork { let block_id = BlockId::Number(blocknumber.into()); - let code = self.provider().get_code_at(address, Some(block_id)).await?; + let code = self.provider().get_code_at(address, block_id).await?; let mut storage = self.storage_write(); storage.code_at.insert((address, blocknumber), code.clone().0.into()); @@ -247,25 +248,21 @@ impl ClientFork { self.provider().get_balance(address, Some(blocknumber.into())).await } - pub async fn get_nonce( - &self, - address: Address, - blocknumber: u64, - ) -> Result { + pub async fn get_nonce(&self, address: Address, block: u64) -> Result { trace!(target: "backend::fork", "get_nonce={:?}", address); - self.provider().get_transaction_count(address, Some(blocknumber.into())).await + self.provider().get_transaction_count(address, Some(block.into())).await } pub async fn transaction_by_block_number_and_index( &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) => { if let Some(tx) = txs.get(index) { - return Ok(Some(tx.clone())); + return Ok(Some(WithOtherFields::new(tx.clone()))); } } BlockTransactions::Hashes(hashes) => { @@ -284,12 +281,12 @@ 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) => { if let Some(tx) = txs.get(index) { - return Ok(Some(tx.clone())); + return Ok(Some(WithOtherFields::new(tx.clone()))); } } BlockTransactions::Hashes(hashes) => { @@ -307,7 +304,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); @@ -367,12 +364,14 @@ impl ClientFork { pub async fn transaction_receipt( &self, hash: B256, - ) -> Result, TransportError> { + ) -> Result, BlockchainError> { if let Some(receipt) = self.storage_read().transaction_receipts.get(&hash).cloned() { return Ok(Some(receipt)); } if let Some(receipt) = self.provider().get_transaction_receipt(hash).await? { + let receipt = + convert_to_anvil_receipt(receipt).ok_or(BlockchainError::FailedToDecodeReceipt)?; let mut storage = self.storage_write(); storage.transaction_receipts.insert(hash, receipt.clone()); return Ok(Some(receipt)); @@ -384,7 +383,7 @@ impl ClientFork { pub async fn block_receipts( &self, number: u64, - ) -> Result>, TransportError> { + ) -> Result>, BlockchainError> { if let receipts @ Some(_) = self.storage_read().block_receipts.get(&number).cloned() { return Ok(receipts); } @@ -394,6 +393,16 @@ impl ClientFork { // this is being temporarily implemented in anvil. if self.predates_fork_inclusive(number) { let receipts = self.provider().get_block_receipts(BlockNumber::Number(number)).await?; + let receipts = receipts + .map(|r| { + r.into_iter() + .map(|r| { + convert_to_anvil_receipt(r) + .ok_or(BlockchainError::FailedToDecodeReceipt) + }) + .collect::, _>>() + }) + .transpose()?; if let Some(receipts) = receipts.clone() { let mut storage = self.storage_write(); @@ -469,14 +478,16 @@ impl ClientFork { ) -> Result, TransportError> { if let Some(block) = self.provider().get_block(block_id.into(), true).await? { let hash = block.header.hash.unwrap(); - let block_number = block.header.number.unwrap().to::(); + let block_number = block.header.number.unwrap(); let mut storage = self.storage_write(); // also insert all transactions let block_txs = match block.clone().transactions { BlockTransactions::Full(txs) => txs, _ => vec![], }; - storage.transactions.extend(block_txs.iter().map(|tx| (tx.hash, tx.clone()))); + storage + .transactions + .extend(block_txs.iter().map(|tx| (tx.hash, WithOtherFields::new(tx.clone())))); storage.hashes.insert(block_number, hash); storage.blocks.insert(hash, block.clone()); return Ok(Some(block)); @@ -520,14 +531,11 @@ impl ClientFork { let mut uncles = Vec::with_capacity(block.uncles.len()); for (uncle_idx, _) in block.uncles.iter().enumerate() { - let uncle = match self - .provider() - .get_uncle(block_number.to::().into(), U64::from(uncle_idx)) - .await? - { - Some(u) => u, - None => return Ok(None), - }; + let uncle = + match self.provider().get_uncle(block_number.into(), U64::from(uncle_idx)).await? { + Some(u) => u, + None => return Ok(None), + }; uncles.push(uncle); } self.storage_write().uncles.insert(block_hash, uncles.clone()); @@ -546,9 +554,10 @@ impl ClientFork { let mut transactions = Vec::with_capacity(block_txs_len); for tx in block.transactions.hashes() { if let Some(tx) = storage.transactions.get(tx).cloned() { - transactions.push(tx); + transactions.push(tx.inner); } } + // TODO: fix once blocks have generic transactions block.into_full_block(transactions) } } @@ -568,7 +577,7 @@ pub struct ClientForkConfig { /// The timestamp for the forked block pub timestamp: u64, /// The basefee of the forked block - pub base_fee: Option, + pub base_fee: Option, /// request timeout pub timeout: Duration, /// request retries for spurious networks @@ -611,7 +620,7 @@ impl ClientForkConfig { block_number: u64, block_hash: B256, timestamp: u64, - base_fee: Option, + base_fee: Option, total_difficulty: U256, ) { self.block_number = block_number; @@ -631,13 +640,13 @@ pub struct ForkedStorage { pub uncles: HashMap>, pub blocks: HashMap, pub hashes: HashMap, - pub transactions: HashMap, - pub transaction_receipts: HashMap, + pub transactions: HashMap>, + pub transaction_receipts: HashMap, pub transaction_traces: HashMap>, pub logs: HashMap>, pub geth_transaction_traces: HashMap, pub block_traces: HashMap>, - pub block_receipts: HashMap>, + pub block_receipts: HashMap>, pub code_at: HashMap<(Address, u64), Bytes>, } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index da49d8d35..939913b84 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -34,25 +34,24 @@ use crate::{ NodeConfig, }; use alloy_consensus::{Header, Receipt, ReceiptWithBloom}; -use alloy_network::Sealable; -use alloy_primitives::{keccak256, Address, Bytes, TxHash, B256, B64, U128, U256, U64, U8}; -use alloy_rpc_trace_types::{ +use alloy_primitives::{keccak256, Address, Bytes, TxHash, B256, U256, U64}; +use alloy_rpc_types::{ + request::TransactionRequest, serde_helpers::JsonStorageKey, state::StateOverride, AccessList, + Block as AlloyBlock, BlockId, BlockNumberOrTag as BlockNumber, + EIP1186AccountProofResponse as AccountProof, EIP1186StorageProof as StorageProof, Filter, + FilteredParams, Header as AlloyHeader, Log, Transaction, TransactionReceipt, WithOtherFields, +}; +use alloy_rpc_types_trace::{ geth::{DefaultFrame, GethDebugTracingOptions, GethDefaultTracingOptions, GethTrace}, parity::LocalizedTransactionTrace, }; -use alloy_rpc_types::{ - request::TransactionRequest, state::StateOverride, AccessList, Block as AlloyBlock, BlockId, - BlockNumberOrTag as BlockNumber, EIP1186AccountProofResponse as AccountProof, - EIP1186StorageProof as StorageProof, Filter, FilteredParams, Header as AlloyHeader, - JsonStorageKey, Log, Transaction, TransactionReceipt, -}; use alloy_trie::{HashBuilder, Nibbles}; use anvil_core::{ eth::{ block::{Block, BlockInfo}, transaction::{ - MaybeImpersonatedTransaction, PendingTransaction, TransactionInfo, TypedReceipt, - TypedTransaction, + DepositReceipt, MaybeImpersonatedTransaction, PendingTransaction, ReceiptResponse, + TransactionInfo, TypedReceipt, TypedTransaction, }, utils::{alloy_to_revm_access_list, meets_eip155}, }, @@ -97,9 +96,9 @@ pub mod state; pub mod storage; // Gas per transaction not creating a contract. -pub const MIN_TRANSACTION_GAS: U256 = U256::from_limbs([21_000, 0, 0, 0]); +pub const MIN_TRANSACTION_GAS: u128 = 21000; // Gas per transaction creating a contract. -pub const MIN_CREATE_GAS: U256 = U256::from_limbs([53_000, 0, 0, 0]); +pub const MIN_CREATE_GAS: u128 = 53000; pub type State = foundry_evm::utils::StateChangeset; @@ -410,8 +409,8 @@ impl Backend { env.block = BlockEnv { number: rU256::from(fork_block_number), - timestamp: fork_block.header.timestamp, - gas_limit: fork_block.header.gas_limit, + timestamp: U256::from(fork_block.header.timestamp), + gas_limit: U256::from(fork_block.header.gas_limit), difficulty: fork_block.header.difficulty, prevrandao: Some(fork_block.header.mix_hash.unwrap_or_default()), // Keep previous `coinbase` and `basefee` value @@ -430,7 +429,7 @@ impl Backend { fork_block.header.base_fee_per_gas.unwrap_or_default(), ); - self.fees.set_base_fee(U256::from(next_block_base_fee)); + self.fees.set_base_fee(next_block_base_fee); // also reset the total difficulty self.blockchain.storage.write().total_difficulty = fork.total_difficulty(); @@ -523,8 +522,8 @@ impl Backend { } /// Returns balance of the given account. - pub async fn current_nonce(&self, address: Address) -> DatabaseResult { - Ok(U256::from(self.get_account(address).await?.nonce)) + pub async fn current_nonce(&self, address: Address) -> DatabaseResult { + Ok(self.get_account(address).await?.nonce) } /// Sets the coinbase address @@ -619,37 +618,37 @@ impl Backend { } /// Returns the block gas limit - pub fn gas_limit(&self) -> U256 { - self.env.read().block.gas_limit + pub fn gas_limit(&self) -> u128 { + self.env.read().block.gas_limit.to() } /// Sets the block gas limit - pub fn set_gas_limit(&self, gas_limit: U256) { - self.env.write().block.gas_limit = gas_limit; + pub fn set_gas_limit(&self, gas_limit: u128) { + self.env.write().block.gas_limit = U256::from(gas_limit); } /// Returns the current base fee - pub fn base_fee(&self) -> U256 { + pub fn base_fee(&self) -> u128 { self.fees.base_fee() } /// Sets the current basefee - pub fn set_base_fee(&self, basefee: U256) { + pub fn set_base_fee(&self, basefee: u128) { self.fees.set_base_fee(basefee) } /// Returns the current gas price - pub fn gas_price(&self) -> U256 { + pub fn gas_price(&self) -> u128 { self.fees.gas_price() } /// Returns the suggested fee cap - pub fn max_priority_fee_per_gas(&self) -> U256 { + pub fn max_priority_fee_per_gas(&self) -> u128 { self.fees.max_priority_fee_per_gas() } /// Sets the gas price - pub fn set_gas_price(&self, price: U256) { + pub fn set_gas_price(&self, price: u128) { self.fees.set_gas_price(price) } @@ -705,17 +704,17 @@ impl Backend { let block = self.block_by_hash(best_block_hash).await?.ok_or(BlockchainError::BlockNotFound)?; - let reset_time = block.header.timestamp.to::(); + let reset_time = block.header.timestamp; self.time.reset(reset_time); let mut env = self.env.write(); env.block = BlockEnv { number: rU256::from(num), - timestamp: block.header.timestamp, + timestamp: U256::from(block.header.timestamp), difficulty: block.header.difficulty, // ensures prevrandao is set prevrandao: Some(block.header.mix_hash.unwrap_or_default()), - gas_limit: block.header.gas_limit, + gas_limit: U256::from(block.header.gas_limit), // Keep previous `coinbase` and `basefee` value coinbase: env.block.coinbase, basefee: env.block.basefee, @@ -795,9 +794,9 @@ impl Backend { fn next_env(&self) -> EnvWithHandlerCfg { let mut env = self.env.read().clone(); // increase block number for this block - env.block.number = env.block.number.saturating_add(rU256::from(1)); - env.block.basefee = self.base_fee(); - env.block.timestamp = rU256::from(self.time.current_call_timestamp()); + env.block.number = env.block.number.saturating_add(U256::from(1)); + env.block.basefee = U256::from(self.base_fee()); + env.block.timestamp = U256::from(self.time.current_call_timestamp()); env } @@ -864,7 +863,7 @@ impl Backend { block_env: env.block.clone(), cfg_env, parent_hash: storage.best_hash, - gas_used: U256::ZERO, + gas_used: 0, enable_steps_tracing: self.enable_steps_tracing, }; @@ -903,7 +902,7 @@ impl Backend { // increase block number for this block env.block.number = env.block.number.saturating_add(rU256::from(1)); - env.block.basefee = current_base_fee; + env.block.basefee = U256::from(current_base_fee); env.block.timestamp = rU256::from(self.time.next_timestamp()); let best_hash = self.blockchain.storage.read().best_hash; @@ -923,13 +922,13 @@ impl Backend { block_env: env.block.clone(), cfg_env: CfgEnvWithHandlerCfg::new(env.cfg.clone(), env.handler_cfg), parent_hash: best_hash, - gas_used: U256::ZERO, + gas_used: 0, enable_steps_tracing: self.enable_steps_tracing, }; let executed_tx = executor.execute(); // we also need to update the new blockhash in the db itself - let block_hash = executed_tx.block.block.header.hash(); + let block_hash = executed_tx.block.block.header.hash_slow(); db.insert_block_hash(U256::from(executed_tx.block.block.header.number), block_hash); (executed_tx, block_hash) @@ -972,7 +971,7 @@ impl Backend { if let Some(contract) = &info.contract_address { node_info!(" Contract created: {contract:?}"); } - node_info!(" Gas used: {}", receipt.gas_used()); + node_info!(" Gas used: {}", receipt.cumulative_gas_used()); if !info.exit.is_ok() { let r = RevertDecoder::new().decode( info.out.as_ref().map(|b| &b[..]).unwrap_or_default(), @@ -1018,16 +1017,16 @@ impl Backend { (outcome, header, block_hash) }; let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas( - U256::from(header.gas_used), - U256::from(header.gas_limit), - U256::from(header.base_fee_per_gas.unwrap_or_default()), + header.gas_used, + header.gas_limit, + header.base_fee_per_gas.unwrap_or_default(), ); // notify all listeners self.notify_on_new_block(header, block_hash); // update next base fee - self.fees.set_base_fee(U256::from(next_block_base_fee)); + self.fees.set_base_fee(next_block_base_fee); outcome } @@ -1039,11 +1038,11 @@ impl Backend { /// Returns an error if the `block_number` is greater than the current height pub async fn call( &self, - request: TransactionRequest, + request: WithOtherFields, fee_details: FeeDetails, block_request: Option, overrides: Option, - ) -> Result<(InstructionResult, Option, u64, State), BlockchainError> { + ) -> Result<(InstructionResult, Option, u128, State), BlockchainError> { self.with_database_at(block_request, |state, block| { let block_number = block.number.to::(); let (exit, out, gas, state) = match overrides { @@ -1060,15 +1059,18 @@ impl Backend { fn build_call_env( &self, - request: TransactionRequest, + request: WithOtherFields, fee_details: FeeDetails, block_env: BlockEnv, ) -> EnvWithHandlerCfg { - let TransactionRequest { from, to, gas, value, input, nonce, access_list, .. } = request; + let WithOtherFields:: { + inner: TransactionRequest { from, to, gas, value, input, nonce, access_list, .. }, + .. + } = request; let FeeDetails { gas_price, max_fee_per_gas, max_priority_fee_per_gas } = fee_details; - let gas_limit = gas.unwrap_or(block_env.gas_limit); + let gas_limit = gas.unwrap_or(block_env.gas_limit.to()); let mut env = self.env.read().clone(); env.block = block_env; // we want to disable this in eth_call, since this is common practice used by other node @@ -1076,7 +1078,7 @@ impl Backend { env.cfg.disable_block_gas_limit = true; if let Some(base) = max_fee_per_gas { - env.block.basefee = base; + env.block.basefee = U256::from(base); } let gas_price = gas_price.or(max_fee_per_gas).unwrap_or_else(|| self.gas_price()); @@ -1084,9 +1086,9 @@ impl Backend { env.tx = TxEnv { caller, - gas_limit: gas_limit.to::(), - gas_price, - gas_priority_fee: max_priority_fee_per_gas, + gas_limit: gas_limit as u64, + gas_price: U256::from(gas_price), + gas_priority_fee: max_priority_fee_per_gas.map(U256::from), transact_to: match to { Some(addr) => TransactTo::Call(addr), None => TransactTo::Create(CreateScheme::Create), @@ -1094,7 +1096,7 @@ impl Backend { value: value.unwrap_or_default(), data: input.into_input().unwrap_or_default(), chain_id: None, - nonce: nonce.map(|n| n.to::()), + nonce, access_list: alloy_to_revm_access_list(access_list.unwrap_or_default().0), ..Default::default() }; @@ -1111,10 +1113,10 @@ impl Backend { pub fn call_with_state( &self, state: D, - request: TransactionRequest, + request: WithOtherFields, fee_details: FeeDetails, block_env: BlockEnv, - ) -> Result<(InstructionResult, Option, u64, State), BlockchainError> + ) -> Result<(InstructionResult, Option, u128, State), BlockchainError> where D: DatabaseRef, { @@ -1133,12 +1135,12 @@ impl Backend { ExecutionResult::Halt { reason, gas_used } => (reason.into(), gas_used, None), }; inspector.print_logs(); - Ok((exit_reason, out, gas_used, state)) + Ok((exit_reason, out, gas_used as u128, state)) } pub async fn call_with_tracing( &self, - request: TransactionRequest, + request: WithOtherFields, fee_details: FeeDetails, block_request: Option, opts: GethDefaultTracingOptions, @@ -1171,7 +1173,7 @@ impl Backend { pub fn build_access_list_with_state( &self, state: D, - request: TransactionRequest, + request: WithOtherFields, fee_details: FeeDetails, block_env: BlockEnv, ) -> Result<(InstructionResult, Option, u64, AccessList), BlockchainError> @@ -1244,53 +1246,43 @@ impl Backend { fn mined_logs_for_block(&self, filter: Filter, block: Block) -> Vec { let params = FilteredParams::new(Some(filter.clone())); let mut all_logs = Vec::new(); - let block_hash = block.header.hash(); + let block_hash = block.header.hash_slow(); let mut block_log_index = 0u32; - let transactions: Vec<_> = { - let storage = self.blockchain.storage.read(); - block - .transactions - .iter() - .filter_map(|tx| storage.transactions.get(&tx.hash()).map(|tx| tx.info.clone())) - .collect() - }; + let storage = self.blockchain.storage.read(); - for transaction in transactions { - let logs = transaction.logs.clone(); - let transaction_hash = transaction.transaction_hash; - - for log in logs.into_iter() { - let mut log = Log { - address: log.address, - topics: log.topics().to_vec(), - data: log.data.data, - block_hash: None, - block_number: None, - transaction_hash: None, - transaction_index: None, - log_index: None, - removed: false, - }; + for tx in block.transactions { + let Some(tx) = storage.transactions.get(&tx.hash()) else { + continue; + }; + let logs = tx.receipt.logs(); + let transaction_hash = tx.info.transaction_hash; + + for log in logs { let mut is_match: bool = true; if !filter.address.is_empty() && filter.has_topics() { - if !params.filter_address(&log.address) || !params.filter_topics(&log.topics) { + if !params.filter_address(&log.address) || !params.filter_topics(log.topics()) { is_match = false; } } else if !filter.address.is_empty() { if !params.filter_address(&log.address) { is_match = false; } - } else if filter.has_topics() && !params.filter_topics(&log.topics) { + } else if filter.has_topics() && !params.filter_topics(log.topics()) { is_match = false; } if is_match { - log.block_hash = Some(block_hash); - log.block_number = Some(block.header.number.to_alloy()); - log.transaction_hash = Some(transaction_hash); - log.transaction_index = Some(U256::from(transaction.transaction_index)); - log.log_index = Some(U256::from(block_log_index)); + let log = Log { + inner: log.clone(), + block_hash: Some(block_hash), + block_number: Some(block.header.number), + block_timestamp: Some(block.header.timestamp), + transaction_hash: Some(transaction_hash), + transaction_index: Some(tx.info.transaction_index), + log_index: Some(block_log_index as u64), + removed: false, + }; all_logs.push(log); } block_log_index += 1; @@ -1394,7 +1386,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); } @@ -1402,7 +1394,10 @@ impl Backend { } /// Returns all transactions given a block - pub(crate) fn mined_transactions_in_block(&self, block: &Block) -> Option> { + pub(crate) fn mined_transactions_in_block( + &self, + block: &Block, + ) -> 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(); @@ -1410,13 +1405,7 @@ impl Backend { let info = storage.transactions.get(&hash)?.info.clone(); let tx = block.transactions.get(info.transaction_index as usize)?.clone(); - let tx = transaction_build( - Some(hash), - tx, - Some(block), - Some(info), - base_fee.map(|f| f.to_alloy()), - ); + let tx = transaction_build(Some(hash), tx, Some(block), Some(info), base_fee); transactions.push(tx); } Some(transactions) @@ -1508,7 +1497,7 @@ impl Backend { let block = self.get_block(id)?; let transactions = self.mined_transactions_in_block(&block)?; let block = self.convert_block(block); - Some(block.into_full_block(transactions)) + Some(block.into_full_block(transactions.into_iter().map(|t| t.inner).collect())) } /// Takes a block as it's stored internally and returns the eth api conform block format @@ -1517,7 +1506,7 @@ impl Backend { let Block { header, transactions, .. } = block; - let hash = header.hash(); + let hash = header.hash_slow(); let Header { parent_hash, ommers_hash, @@ -1550,17 +1539,17 @@ impl Backend { state_root, transactions_root, receipts_root, - number: Some(number.to_alloy()), - gas_used: gas_used.to_alloy(), - gas_limit: gas_limit.to_alloy(), + number: Some(number), + gas_used, + gas_limit, extra_data: extra_data.0.into(), logs_bloom, - timestamp: U256::from(timestamp), + timestamp, total_difficulty: Some(self.total_difficulty()), difficulty, mix_hash: Some(mix_hash), - nonce: Some(B64::from(nonce)), - base_fee_per_gas: base_fee_per_gas.map(|f| f.to_alloy()), + nonce: Some(nonce), + base_fee_per_gas, withdrawals_root: None, blob_gas_used: None, excess_blob_gas: None, @@ -1594,8 +1583,7 @@ impl Backend { .ok_or(BlockchainError::BlockNotFound)? .header .number - .ok_or(BlockchainError::BlockNotFound)? - .to::(), + .ok_or(BlockchainError::BlockNotFound)?, BlockId::Number(num) => match num { BlockNumber::Latest | BlockNumber::Pending => self.best_number(), BlockNumber::Earliest => U64::ZERO.to::(), @@ -1640,11 +1628,11 @@ impl Backend { let block = BlockEnv { number: block.header.number.to_alloy(), coinbase: block.header.beneficiary, - timestamp: rU256::from(block.header.timestamp), + timestamp: U256::from(block.header.timestamp), difficulty: block.header.difficulty, prevrandao: Some(block.header.mix_hash), - basefee: block.header.base_fee_per_gas.unwrap_or_default().to_alloy(), - gas_limit: block.header.gas_limit.to_alloy(), + basefee: U256::from(block.header.base_fee_per_gas.unwrap_or_default()), + gas_limit: U256::from(block.header.gas_limit), ..Default::default() }; f(state, block) @@ -1663,7 +1651,7 @@ impl Backend { if let Some((state, block)) = self .get_block(block_number.to::()) - .and_then(|block| Some((states.get(&block.header.hash())?, block))) + .and_then(|block| Some((states.get(&block.header.hash_slow())?, block))) { let block = BlockEnv { number: block.header.number.to_alloy(), @@ -1671,8 +1659,8 @@ impl Backend { timestamp: rU256::from(block.header.timestamp), difficulty: block.header.difficulty, prevrandao: Some(block.header.mix_hash), - basefee: block.header.base_fee_per_gas.unwrap_or_default().to_alloy(), - gas_limit: block.header.gas_limit.to_alloy(), + basefee: U256::from(block.header.base_fee_per_gas.unwrap_or_default()), + gas_limit: U256::from(block.header.gas_limit), ..Default::default() }; return Ok(f(Box::new(state), block)); @@ -1692,7 +1680,7 @@ impl Backend { block.number = block_number; block.timestamp = rU256::from(fork.timestamp()); - block.basefee = fork.base_fee().unwrap_or_default(); + block.basefee = rU256::from(fork.base_fee().unwrap_or_default()); return Ok(f(Box::new(&gen_db), block)); } @@ -1789,7 +1777,7 @@ impl Backend { &self, address: Address, block_request: BlockRequest, - ) -> Result { + ) -> Result { if let BlockRequest::Pending(pool_transactions) = &block_request { if let Some(value) = get_pool_transactions_nonce(pool_transactions, address) { return Ok(value); @@ -1802,7 +1790,7 @@ impl Backend { self.with_database_at(Some(final_block_request), |db, _| { trace!(target: "backend", "get nonce for {:?}", address); - Ok(U256::from(db.basic_ref(address)?.unwrap_or_default().nonce)) + Ok(db.basic_ref(address)?.unwrap_or_default().nonce) }) .await? } @@ -1897,7 +1885,7 @@ impl Backend { pub async fn transaction_receipt( &self, hash: B256, - ) -> Result, BlockchainError> { + ) -> Result, BlockchainError> { if let Some(receipt) = self.mined_transaction_receipt(hash) { return Ok(Some(receipt.inner)); } @@ -1905,10 +1893,7 @@ impl Backend { if let Some(fork) = self.get_fork() { let receipt = fork.transaction_receipt(hash).await?; let number = self.convert_block_number( - receipt - .clone() - .and_then(|r| r.block_number) - .map(|n| BlockNumber::from(n.to::())), + receipt.clone().and_then(|r| r.block_number).map(BlockNumber::from), ); if fork.predates_fork_inclusive(number) { @@ -1932,7 +1917,7 @@ impl Backend { } /// Returns all transaction receipts of the block - pub fn mined_block_receipts(&self, id: impl Into) -> Option> { + pub fn mined_block_receipts(&self, id: impl Into) -> Option> { let mut receipts = Vec::new(); let block = self.get_block(id)?; @@ -1946,104 +1931,83 @@ impl Backend { /// Returns the transaction receipt for the given hash pub(crate) fn mined_transaction_receipt(&self, hash: B256) -> Option { - let MinedTransaction { info, receipt, block_hash, .. } = + let MinedTransaction { info, receipt: tx_receipt, block_hash, .. } = self.blockchain.get_transaction_by_hash(&hash)?; - let ReceiptWithBloom { receipt, bloom } = receipt.into(); - let Receipt { success, cumulative_gas_used: _, logs } = receipt; - let logs_bloom = bloom; - let index = info.transaction_index as usize; - let block = self.blockchain.get_block_by_hash(&block_hash)?; - - // TODO store cumulative gas used in receipt instead - let receipts = self.get_receipts(block.transactions.iter().map(|tx| tx.hash())); - - let mut cumulative_gas_used = U256::ZERO; - for receipt in receipts.iter().take(index + 1) { - cumulative_gas_used = cumulative_gas_used.saturating_add(receipt.gas_used()); - } - - // cumulative_gas_used = cumulative_gas_used.saturating_sub(gas_used); - - let mut cumulative_receipts = receipts; - cumulative_receipts.truncate(index + 1); - let transaction = block.transactions[index].clone(); - let transaction_type = transaction.transaction.r#type(); - let effective_gas_price = match transaction.transaction { - TypedTransaction::Legacy(t) => t.gas_price, - TypedTransaction::EIP2930(t) => t.gas_price, + TypedTransaction::Legacy(t) => t.tx().gas_price, + TypedTransaction::EIP2930(t) => t.tx().gas_price, TypedTransaction::EIP1559(t) => block .header .base_fee_per_gas - .map_or(self.base_fee().to::(), |b| b as u128) - .checked_add(t.max_priority_fee_per_gas) - .unwrap_or(u128::MAX), + .unwrap_or_else(|| self.base_fee()) + .saturating_add(t.tx().max_priority_fee_per_gas), TypedTransaction::EIP4844(t) => block .header .base_fee_per_gas - .map_or(self.base_fee().to::(), |b| b as u128) - .checked_add(t.tx().tx().max_priority_fee_per_gas) - .unwrap_or(u128::MAX), + .unwrap_or_else(|| self.base_fee()) + .saturating_add(t.tx().tx().max_priority_fee_per_gas), TypedTransaction::Deposit(_) => 0_u128, }; - let deposit_nonce = transaction_type.and_then(|x| (x == 0x7E).then_some(info.nonce)); + let receipts = self.get_receipts(block.transactions.iter().map(|tx| tx.hash())); + let next_log_index = receipts[..index].iter().map(|r| r.logs().len()).sum::(); + + let receipt = tx_receipt.as_receipt_with_bloom().receipt.clone(); + let receipt = Receipt { + status: receipt.status, + cumulative_gas_used: receipt.cumulative_gas_used, + logs: receipt + .logs + .into_iter() + .enumerate() + .map(|(index, log)| alloy_rpc_types::Log { + inner: log, + block_hash: Some(block_hash), + block_number: Some(block.header.number), + block_timestamp: Some(block.header.timestamp), + transaction_hash: Some(info.transaction_hash), + transaction_index: Some(info.transaction_index), + log_index: Some((next_log_index + index) as u64), + removed: false, + }) + .collect(), + }; + let receipt_with_bloom = + ReceiptWithBloom { receipt, logs_bloom: tx_receipt.as_receipt_with_bloom().logs_bloom }; + + let inner = match tx_receipt { + TypedReceipt::EIP1559(_) => TypedReceipt::EIP1559(receipt_with_bloom), + TypedReceipt::Legacy(_) => TypedReceipt::Legacy(receipt_with_bloom), + TypedReceipt::EIP2930(_) => TypedReceipt::EIP2930(receipt_with_bloom), + TypedReceipt::EIP4844(_) => TypedReceipt::EIP4844(receipt_with_bloom), + TypedReceipt::Deposit(r) => TypedReceipt::Deposit(DepositReceipt { + inner: receipt_with_bloom, + deposit_nonce: r.deposit_nonce, + deposit_nonce_version: r.deposit_nonce_version, + }), + }; - let mut inner = TransactionReceipt { - transaction_hash: Some(info.transaction_hash), - transaction_index: U64::from(info.transaction_index), + let inner = TransactionReceipt { + inner, + transaction_hash: info.transaction_hash, + transaction_index: info.transaction_index, + block_number: Some(block.header.number), + gas_used: info.gas_used, + contract_address: info.contract_address, + effective_gas_price, block_hash: Some(block_hash), - block_number: Some(U256::from(block.header.number)), from: info.from, to: info.to, - cumulative_gas_used, - gas_used: Some(cumulative_gas_used), - contract_address: info.contract_address, - logs: { - let mut pre_receipts_log_index = None; - if !cumulative_receipts.is_empty() { - cumulative_receipts.truncate(cumulative_receipts.len() - 1); - pre_receipts_log_index = Some( - cumulative_receipts.iter().map(|r| r.logs().len() as u32).sum::(), - ); - } - logs.iter() - .enumerate() - .map(|(i, log)| Log { - address: log.address, - topics: log.topics().to_vec(), - data: log.data.data.clone(), - block_hash: Some(block_hash), - block_number: Some(U256::from(block.header.number)), - transaction_hash: Some(info.transaction_hash), - transaction_index: Some(U256::from(info.transaction_index)), - log_index: Some(U256::from( - (pre_receipts_log_index.unwrap_or(0)) + i as u32, - )), - removed: false, - }) - .collect() - }, - status_code: Some(U64::from(success)), - state_root: None, - logs_bloom, - transaction_type: transaction_type.map(U8::from).unwrap_or_default(), - effective_gas_price: U128::from(effective_gas_price), + state_root: Some(block.header.state_root), blob_gas_price: None, blob_gas_used: None, - other: Default::default(), }; - inner.other.insert( - "depositNonce".to_string(), - serde_json::to_value(deposit_nonce).expect("Infallible"), - ); - Some(MinedTransactionReceipt { inner, out: info.out.map(|o| o.0.into()) }) } @@ -2051,7 +2015,7 @@ impl Backend { pub async fn block_receipts( &self, number: BlockNumber, - ) -> Result>, BlockchainError> { + ) -> Result>, BlockchainError> { if let Some(receipts) = self.mined_block_receipts(number) { return Ok(Some(receipts)); } @@ -2060,10 +2024,7 @@ impl Backend { let number = self.convert_block_number(Some(number)); if fork.predates_fork_inclusive(number) { - let receipts = fork - .block_receipts(number) - .await - .map_err(BlockchainError::AlloyForkProvider)?; + let receipts = fork.block_receipts(number).await?; return Ok(receipts); } @@ -2076,7 +2037,7 @@ impl Backend { &self, number: BlockNumber, index: Index, - ) -> Result, BlockchainError> { + ) -> Result>, BlockchainError> { if let Some(hash) = self.mined_block_by_number(number).and_then(|b| b.header.hash) { return Ok(self.mined_transaction_by_block_hash_and_index(hash, index)); } @@ -2095,7 +2056,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); } @@ -2111,7 +2072,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()?; @@ -2126,14 +2087,14 @@ impl Backend { tx, Some(&block), Some(info), - block.header.base_fee_per_gas.map(|g| g.to_alloy()), + block.header.base_fee_per_gas, )) } 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); @@ -2146,7 +2107,7 @@ impl Backend { Ok(None) } - fn mined_transaction_by_hash(&self, hash: B256) -> Option { + fn mined_transaction_by_hash(&self, hash: B256) -> Option> { let (info, block) = { let storage = self.blockchain.storage.read(); let MinedTransaction { info, block_hash, .. } = @@ -2161,7 +2122,7 @@ impl Backend { tx, Some(&block), Some(info), - block.header.base_fee_per_gas.map(|g| g.to_alloy()), + block.header.base_fee_per_gas, )) } @@ -2242,14 +2203,14 @@ impl Backend { fn get_pool_transactions_nonce( pool_transactions: &[Arc], address: Address, -) -> Option { +) -> Option { if let Some(highest_nonce) = pool_transactions .iter() .filter(|tx| *tx.pending_transaction.sender() == address) .map(|tx| tx.pending_transaction.nonce()) .max() { - let tx_count = highest_nonce.saturating_add(U256::from(1)); + let tx_count = highest_nonce.saturating_add(1); return Some(tx_count) } None @@ -2299,7 +2260,7 @@ impl TransactionValidator for Backend { } // Check gas limit, iff block gas limit is set. - if !env.cfg.disable_block_gas_limit && tx.gas_limit() > env.block.gas_limit { + if !env.cfg.disable_block_gas_limit && tx.gas_limit() > env.block.gas_limit.to() { warn!(target: "backend", "[{:?}] gas too high", tx.hash()); return Err(InvalidTransactionError::GasTooHigh(ErrDetail { detail: String::from("tx.gas_limit > env.block.gas_limit"), @@ -2309,15 +2270,14 @@ impl TransactionValidator for Backend { // check nonce let is_deposit_tx = matches!(&pending.transaction.transaction, TypedTransaction::Deposit(_)); - let nonce: u64 = - tx.nonce().try_into().map_err(|_| InvalidTransactionError::NonceMaxValue)?; + let nonce = tx.nonce(); if nonce < account.nonce && !is_deposit_tx { warn!(target: "backend", "[{:?}] nonce too low", tx.hash()); return Err(InvalidTransactionError::NonceTooLow); } if (env.handler_cfg.spec_id as u8) >= (SpecId::LONDON as u8) { - if tx.gas_price() < env.block.basefee && !is_deposit_tx { + if tx.gas_price() < env.block.basefee.to() && !is_deposit_tx { warn!(target: "backend", "max fee per gas={}, too low, block basefee={}",tx.gas_price(), env.block.basefee); return Err(InvalidTransactionError::FeeCapTooLow); } @@ -2335,12 +2295,12 @@ impl TransactionValidator for Backend { let max_cost = tx.max_cost(); let value = tx.value(); // check sufficient funds: `gas * price + value` - let req_funds = max_cost.checked_add(value).ok_or_else(|| { + let req_funds = max_cost.checked_add(value.to()).ok_or_else(|| { warn!(target: "backend", "[{:?}] cost too high", tx.hash()); InvalidTransactionError::InsufficientFunds })?; - if account.balance < req_funds { + if account.balance < U256::from(req_funds) { warn!(target: "backend", "[{:?}] insufficient allowance={}, required={} account={:?}", tx.hash(), account.balance, req_funds, *pending.sender()); return Err(InvalidTransactionError::InsufficientFunds); } @@ -2354,7 +2314,7 @@ impl TransactionValidator for Backend { env: &EnvWithHandlerCfg, ) -> Result<(), InvalidTransactionError> { self.validate_pool_transaction_for(tx, account, env)?; - if tx.nonce().to::() > account.nonce { + if tx.nonce() > account.nonce { return Err(InvalidTransactionError::NonceTooHigh); } Ok(()) @@ -2368,11 +2328,11 @@ pub fn transaction_build( eth_transaction: MaybeImpersonatedTransaction, block: Option<&Block>, info: Option, - base_fee: Option, -) -> Transaction { + base_fee: Option, +) -> WithOtherFields { let mut transaction: Transaction = eth_transaction.clone().into(); - if info.is_some() && transaction.transaction_type.unwrap_or(U64::ZERO).to::() == 0x7E { - transaction.nonce = U64::from(info.as_ref().unwrap().nonce); + if info.is_some() && transaction.transaction_type == Some(0x7E) { + transaction.nonce = info.as_ref().unwrap().nonce; } if eth_transaction.is_dynamic_fee() { @@ -2382,12 +2342,9 @@ pub fn transaction_build( } else { // if transaction is already mined, gas price is considered base fee + priority fee: the // effective gas price. - let base_fee = base_fee.unwrap_or(U256::ZERO); - let max_priority_fee_per_gas = - transaction.max_priority_fee_per_gas.map(|g| g.to::()).unwrap_or(U256::ZERO); - transaction.gas_price = Some( - base_fee.checked_add(max_priority_fee_per_gas).unwrap_or(U256::MAX).to::(), - ); + let base_fee = base_fee.unwrap_or(0); + 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)); } } else { transaction.max_fee_per_gas = None; @@ -2397,10 +2354,9 @@ pub fn transaction_build( transaction.block_hash = block.as_ref().map(|block| B256::from(keccak256(alloy_rlp::encode(&block.header)))); - transaction.block_number = block.as_ref().map(|block| U256::from(block.header.number)); + transaction.block_number = block.as_ref().map(|block| block.header.number); - transaction.transaction_index = - info.as_ref().map(|status| U256::from(status.transaction_index)); + transaction.transaction_index = info.as_ref().map(|info| info.transaction_index); // 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 @@ -2422,7 +2378,7 @@ pub fn transaction_build( } transaction.to = info.as_ref().map_or(eth_transaction.to(), |status| status.to); - transaction + WithOtherFields::new(transaction) } /// Prove a storage key's existence or nonexistence in the account's storage diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index 8d72b30f1..434490de5 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -6,18 +6,15 @@ use crate::eth::{ }, pool::transactions::PoolTransaction, }; -use alloy_network::Sealable; use alloy_primitives::{Bytes, TxHash, B256, U256, U64}; -use alloy_rpc_trace_types::{ +use alloy_rpc_types::{BlockId, BlockNumberOrTag, TransactionInfo as RethTransactionInfo}; +use alloy_rpc_types_trace::{ geth::{DefaultFrame, GethDefaultTracingOptions}, parity::LocalizedTransactionTrace, }; -use alloy_rpc_types::{ - BlockId, BlockNumberOrTag, TransactionInfo as RethTransactionInfo, TransactionReceipt, -}; use anvil_core::eth::{ block::{Block, PartialHeader}, - transaction::{MaybeImpersonatedTransaction, TransactionInfo, TypedReceipt}, + transaction::{MaybeImpersonatedTransaction, ReceiptResponse, TransactionInfo, TypedReceipt}, }; use foundry_evm::{ revm::primitives::Env, @@ -228,18 +225,18 @@ pub struct BlockchainStorage { impl BlockchainStorage { /// Creates a new storage with a genesis block - pub fn new(env: &Env, base_fee: Option, timestamp: u64) -> Self { + pub fn new(env: &Env, base_fee: Option, timestamp: u64) -> Self { // create a dummy genesis block let partial_header = PartialHeader { timestamp, - base_fee: base_fee.map(|b| b.to::()), - gas_limit: env.block.gas_limit.to::(), + base_fee, + gas_limit: env.block.gas_limit.to::(), beneficiary: env.block.coinbase, difficulty: env.block.difficulty, ..Default::default() }; let block = Block::new::(partial_header, vec![], vec![]); - let genesis_hash = block.header.hash(); + let genesis_hash = block.header.hash_slow(); let best_hash = genesis_hash; let best_number: U64 = U64::from(0u64); @@ -339,7 +336,7 @@ pub struct Blockchain { impl Blockchain { /// Creates a new storage with a genesis block - pub fn new(env: &Env, base_fee: Option, timestamp: u64) -> Self { + pub fn new(env: &Env, base_fee: Option, timestamp: u64) -> Self { Self { storage: Arc::new(RwLock::new(BlockchainStorage::new(env, base_fee, timestamp))) } } @@ -408,7 +405,7 @@ impl MinedTransaction { ) .into_localized_transaction_traces(RethTransactionInfo { hash: Some(self.info.transaction_hash), - index: Some(self.info.transaction_index as u64), + index: Some(self.info.transaction_index), block_hash: Some(self.block_hash), block_number: Some(self.block_number), base_fee: None, @@ -418,7 +415,7 @@ impl MinedTransaction { pub fn geth_trace(&self, opts: GethDefaultTracingOptions) -> DefaultFrame { GethTraceBuilder::new(self.info.traces.clone(), TracingInspectorConfig::default_geth()) .geth_traces( - self.receipt.gas_used().to::(), + self.receipt.cumulative_gas_used() as u64, self.info.out.clone().unwrap_or_default().0.into(), opts, ) @@ -429,7 +426,7 @@ impl MinedTransaction { #[derive(Clone, Debug)] pub struct MinedTransactionReceipt { /// The actual json rpc receipt object - pub inner: TransactionReceipt, + pub inner: ReceiptResponse, /// Output data fo the transaction pub out: Option, } diff --git a/crates/anvil/src/eth/error.rs b/crates/anvil/src/eth/error.rs index 574365039..9bd3d64a4 100644 --- a/crates/anvil/src/eth/error.rs +++ b/crates/anvil/src/eth/error.rs @@ -1,7 +1,7 @@ //! Aggregated error type for this module use crate::eth::pool::transactions::PoolTransaction; -use alloy_primitives::{Bytes, SignatureError as AlloySignatureError, U256}; +use alloy_primitives::{Bytes, SignatureError as AlloySignatureError}; use alloy_signer::Error as AlloySignerError; use alloy_transport::TransportError; use anvil_rpc::{ @@ -37,6 +37,8 @@ pub enum BlockchainError { FailedToDecodeSignedTransaction, #[error("Failed to decode transaction")] FailedToDecodeTransaction, + #[error("Failed to decode receipt")] + FailedToDecodeReceipt, #[error("Failed to decode state")] FailedToDecodeStateDump, #[error("Prevrandao not in th EVM's environment after merge")] @@ -180,7 +182,7 @@ pub enum InvalidTransactionError { FeeCapTooLow, /// Thrown during estimate if caller has insufficient funds to cover the tx. #[error("Out of gas: gas required exceeds allowance: {0:?}")] - BasicOutOfGas(U256), + BasicOutOfGas(u128), /// Thrown if executing a transaction failed during estimate/call #[error("execution reverted: {0:?}")] Revert(Option), @@ -348,6 +350,9 @@ impl ToRpcResponseResult for Result { BlockchainError::FailedToDecodeTransaction => { RpcError::invalid_params("Failed to decode transaction") } + BlockchainError::FailedToDecodeReceipt => { + RpcError::invalid_params("Failed to decode receipt") + } BlockchainError::FailedToDecodeStateDump => { RpcError::invalid_params("Failed to decode state dump") } diff --git a/crates/anvil/src/eth/fees.rs b/crates/anvil/src/eth/fees.rs index ef2d38eca..2b070b281 100644 --- a/crates/anvil/src/eth/fees.rs +++ b/crates/anvil/src/eth/fees.rs @@ -2,7 +2,7 @@ use crate::eth::{ backend::{info::StorageInfo, notifications::NewBlockNotifications}, error::BlockchainError, }; -use alloy_primitives::{B256, U256}; +use alloy_primitives::B256; use anvil_core::eth::transaction::TypedTransaction; use foundry_evm::revm::primitives::SpecId; use futures::StreamExt; @@ -20,16 +20,16 @@ use std::{ pub const MAX_FEE_HISTORY_CACHE_SIZE: u64 = 2048u64; /// Initial base fee for EIP-1559 blocks. -pub const INITIAL_BASE_FEE: u64 = 1_000_000_000; +pub const INITIAL_BASE_FEE: u128 = 1_000_000_000; /// Initial default gas price for the first block -pub const INITIAL_GAS_PRICE: u64 = 1_875_000_000; +pub const INITIAL_GAS_PRICE: u128 = 1_875_000_000; /// Bounds the amount the base fee can change between blocks. -pub const BASE_FEE_CHANGE_DENOMINATOR: u64 = 8; +pub const BASE_FEE_CHANGE_DENOMINATOR: u128 = 8; /// Elasticity multiplier as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) -pub const EIP1559_ELASTICITY_MULTIPLIER: u64 = 2; +pub const EIP1559_ELASTICITY_MULTIPLIER: u128 = 2; pub fn default_elasticity() -> f64 { 1f64 / BASE_FEE_CHANGE_DENOMINATOR as f64 @@ -43,18 +43,18 @@ pub struct FeeManager { /// Tracks the base fee for the next block post London /// /// This value will be updated after a new block was mined - base_fee: Arc>, + base_fee: Arc>, /// The base price to use Pre London /// /// This will be constant value unless changed manually - gas_price: Arc>, + gas_price: Arc>, elasticity: Arc>, } // === impl FeeManager === impl FeeManager { - pub fn new(spec_id: SpecId, base_fee: U256, gas_price: U256) -> Self { + pub fn new(spec_id: SpecId, base_fee: u128, gas_price: u128) -> Self { Self { spec_id, base_fee: Arc::new(RwLock::new(base_fee)), @@ -73,7 +73,7 @@ impl FeeManager { } /// Calculates the current gas price - pub fn gas_price(&self) -> U256 { + pub fn gas_price(&self) -> u128 { if self.is_eip1559() { self.base_fee().saturating_add(self.suggested_priority_fee()) } else { @@ -82,33 +82,33 @@ impl FeeManager { } /// Suggested priority fee to add to the base fee - pub fn suggested_priority_fee(&self) -> U256 { - U256::from(1e9 as u64) + pub fn suggested_priority_fee(&self) -> u128 { + 1e9 as u128 } - pub fn base_fee(&self) -> U256 { + pub fn base_fee(&self) -> u128 { if self.is_eip1559() { *self.base_fee.read() } else { - U256::ZERO + 0 } } /// Returns the suggested fee cap /// /// Note: This currently returns a constant value: [Self::suggested_priority_fee] - pub fn max_priority_fee_per_gas(&self) -> U256 { + pub fn max_priority_fee_per_gas(&self) -> u128 { self.suggested_priority_fee() } /// Returns the current gas price - pub fn set_gas_price(&self, price: U256) { + pub fn set_gas_price(&self, price: u128) { let mut gas = self.gas_price.write(); *gas = price; } /// Returns the current base fee - pub fn set_base_fee(&self, fee: U256) { + pub fn set_base_fee(&self, fee: u128) { trace!(target: "backend::fees", "updated base fee {:?}", fee); let mut base = self.base_fee.write(); *base = fee; @@ -117,26 +117,22 @@ impl FeeManager { /// Calculates the base fee for the next block pub fn get_next_block_base_fee_per_gas( &self, - gas_used: U256, - gas_limit: U256, - last_fee_per_gas: U256, - ) -> u64 { + gas_used: u128, + gas_limit: u128, + last_fee_per_gas: u128, + ) -> u128 { // It's naturally impossible for base fee to be 0; // It means it was set by the user deliberately and therefore we treat it as a constant. // Therefore, we skip the base fee calculation altogether and we return 0. - if self.base_fee().is_zero() { + if self.base_fee() == 0 { return 0 } - calculate_next_block_base_fee( - gas_used.to::(), - gas_limit.to::(), - last_fee_per_gas.to::(), - ) + calculate_next_block_base_fee(gas_used, gas_limit, last_fee_per_gas) } } /// Calculate base fee for next block. [EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) spec -pub fn calculate_next_block_base_fee(gas_used: u64, gas_limit: u64, base_fee: u64) -> u64 { +pub fn calculate_next_block_base_fee(gas_used: u128, gas_limit: u128, base_fee: u128) -> u128 { let gas_target = gas_limit / EIP1559_ELASTICITY_MULTIPLIER; if gas_used == gas_target { @@ -144,20 +140,15 @@ pub fn calculate_next_block_base_fee(gas_used: u64, gas_limit: u64, base_fee: u6 } if gas_used > gas_target { let gas_used_delta = gas_used - gas_target; - let base_fee_delta = std::cmp::max( - 1, - base_fee as u128 * gas_used_delta as u128 / - gas_target as u128 / - BASE_FEE_CHANGE_DENOMINATOR as u128, - ); - base_fee + (base_fee_delta as u64) + let base_fee_delta = + std::cmp::max(1, base_fee * gas_used_delta / gas_target / BASE_FEE_CHANGE_DENOMINATOR); + base_fee + base_fee_delta } else { let gas_used_delta = gas_target - gas_used; - let base_fee_per_gas_delta = base_fee as u128 * gas_used_delta as u128 / - gas_target as u128 / - BASE_FEE_CHANGE_DENOMINATOR as u128; + let base_fee_per_gas_delta = + base_fee * gas_used_delta / gas_target / BASE_FEE_CHANGE_DENOMINATOR; - base_fee.saturating_sub(base_fee_per_gas_delta as u64) + base_fee.saturating_sub(base_fee_per_gas_delta) } } @@ -222,11 +213,7 @@ impl FeeHistoryService { let mut block_number: Option = None; let base_fee = self.fees.base_fee(); - let mut item = FeeHistoryCacheItem { - base_fee: base_fee.to::(), - gas_used_ratio: 0f64, - rewards: Vec::new(), - }; + let mut item = FeeHistoryCacheItem { base_fee, gas_used_ratio: 0f64, rewards: Vec::new() }; let current_block = self.storage_info.block(hash); let current_receipts = self.storage_info.receipts(hash); @@ -241,38 +228,34 @@ impl FeeHistoryService { item.gas_used_ratio = gas_used / (gas_target * elasticity); // extract useful tx info (gas_used, effective_reward) - let mut transactions: Vec<(u64, u64)> = receipts + let mut transactions: Vec<(u128, u128)> = receipts .iter() .enumerate() .map(|(i, receipt)| { - let gas_used = receipt.gas_used(); + let gas_used = receipt.cumulative_gas_used(); let effective_reward = match block.transactions.get(i).map(|tx| &tx.transaction) { Some(TypedTransaction::Legacy(t)) => { - U256::from(t.gas_price).saturating_sub(base_fee).to::() + t.tx().gas_price.saturating_sub(base_fee) } Some(TypedTransaction::EIP2930(t)) => { - U256::from(t.gas_price).saturating_sub(base_fee).to::() - } - Some(TypedTransaction::EIP1559(t)) => { - U256::from(t.max_priority_fee_per_gas) - .min(U256::from(t.max_fee_per_gas).saturating_sub(base_fee)) - .to::() + t.tx().gas_price.saturating_sub(base_fee) } + Some(TypedTransaction::EIP1559(t)) => t + .tx() + .max_priority_fee_per_gas + .min(t.tx().max_fee_per_gas.saturating_sub(base_fee)), // TODO: This probably needs to be extended to extract 4844 info. - Some(TypedTransaction::EIP4844(t)) => { - U256::from(t.tx().tx().max_priority_fee_per_gas) - .min( - U256::from(t.tx().tx().max_fee_per_gas) - .saturating_sub(base_fee), - ) - .to::() - } + Some(TypedTransaction::EIP4844(t)) => t + .tx() + .tx() + .max_priority_fee_per_gas + .min(t.tx().tx().max_fee_per_gas.saturating_sub(base_fee)), Some(TypedTransaction::Deposit(_)) => 0, None => 0, }; - (gas_used.to::(), effective_reward) + (gas_used, effective_reward) }) .collect(); @@ -283,7 +266,7 @@ impl FeeHistoryService { item.rewards = reward_percentiles .into_iter() .filter_map(|p| { - let target_gas = (p * gas_used / 100f64) as u64; + let target_gas = (p * gas_used / 100f64) as u128; let mut sum_gas = 0; for (gas_used, effective_reward) in transactions.iter().cloned() { sum_gas += gas_used; @@ -341,26 +324,22 @@ pub type FeeHistoryCache = Arc>>; /// A single item in the whole fee history cache #[derive(Clone, Debug)] pub struct FeeHistoryCacheItem { - pub base_fee: u64, + pub base_fee: u128, pub gas_used_ratio: f64, - pub rewards: Vec, + pub rewards: Vec, } #[derive(Clone, Default)] pub struct FeeDetails { - pub gas_price: Option, - pub max_fee_per_gas: Option, - pub max_priority_fee_per_gas: Option, + pub gas_price: Option, + pub max_fee_per_gas: Option, + pub max_priority_fee_per_gas: Option, } impl FeeDetails { /// All values zero pub fn zero() -> Self { - Self { - gas_price: Some(U256::ZERO), - max_fee_per_gas: Some(U256::ZERO), - max_priority_fee_per_gas: Some(U256::ZERO), - } + Self { gas_price: Some(0), max_fee_per_gas: Some(0), max_priority_fee_per_gas: Some(0) } } /// If neither `gas_price` nor `max_fee_per_gas` is `Some`, this will set both to `0` @@ -368,23 +347,23 @@ impl FeeDetails { let FeeDetails { gas_price, max_fee_per_gas, max_priority_fee_per_gas } = self; let no_fees = gas_price.is_none() && max_fee_per_gas.is_none(); - let gas_price = if no_fees { Some(U256::ZERO) } else { gas_price }; - let max_fee_per_gas = if no_fees { Some(U256::ZERO) } else { max_fee_per_gas }; + let gas_price = if no_fees { Some(0) } else { gas_price }; + let max_fee_per_gas = if no_fees { Some(0) } else { max_fee_per_gas }; Self { gas_price, max_fee_per_gas, max_priority_fee_per_gas } } /// Turns this type into a tuple - pub fn split(self) -> (Option, Option, Option) { + pub fn split(self) -> (Option, Option, Option) { let Self { gas_price, max_fee_per_gas, max_priority_fee_per_gas } = self; (gas_price, max_fee_per_gas, max_priority_fee_per_gas) } /// Creates a new instance from the request's gas related values pub fn new( - request_gas_price: Option, - request_max_fee: Option, - request_priority: Option, + request_gas_price: Option, + request_max_fee: Option, + request_priority: Option, ) -> Result { match (request_gas_price, request_max_fee, request_priority) { (gas_price, None, None) => { diff --git a/crates/anvil/src/eth/otterscan/api.rs b/crates/anvil/src/eth/otterscan/api.rs index ac092f36c..636140f13 100644 --- a/crates/anvil/src/eth/otterscan/api.rs +++ b/crates/anvil/src/eth/otterscan/api.rs @@ -7,11 +7,13 @@ use crate::eth::{ macros::node_info, EthApi, }; -use alloy_primitives::{Address, Bytes, B256, U256, U64}; -use alloy_rpc_trace_types::parity::{ +use alloy_primitives::{Address, Bytes, B256, U256}; +use alloy_rpc_types::{ + Block, BlockId, BlockNumberOrTag as BlockNumber, Transaction, WithOtherFields, +}; +use alloy_rpc_types_trace::parity::{ Action, CallAction, CreateAction, CreateOutput, RewardAction, TraceOutput, }; -use alloy_rpc_types::{Block, BlockId, BlockNumberOrTag as BlockNumber, Transaction}; use itertools::Itertools; impl EthApi { @@ -68,7 +70,7 @@ impl EthApi { node_info!("ots_getTransactionError"); if let Some(receipt) = self.backend.mined_transaction_receipt(hash) { - if receipt.inner.status_code == Some(U64::ZERO) { + if !receipt.inner.inner.as_receipt_with_bloom().receipt.status { return Ok(receipt.out.map(|b| b.0.into())) } } @@ -238,7 +240,7 @@ impl EthApi { &self, address: Address, nonce: U256, - ) -> Result> { + ) -> Result>> { node_info!("ots_getTransactionBySenderAndNonce"); let from = self.get_fork().map(|f| f.block_number() + 1).unwrap_or_default(); diff --git a/crates/anvil/src/eth/otterscan/types.rs b/crates/anvil/src/eth/otterscan/types.rs index ff1f99ce5..d7e75c02c 100644 --- a/crates/anvil/src/eth/otterscan/types.rs +++ b/crates/anvil/src/eth/otterscan/types.rs @@ -3,8 +3,9 @@ use crate::eth::{ error::{BlockchainError, Result}, }; use alloy_primitives::{Address, Bytes, B256, U256 as rU256, U256}; -use alloy_rpc_trace_types::parity::{Action, CallType, LocalizedTransactionTrace}; -use alloy_rpc_types::{Block, BlockTransactions, Transaction, TransactionReceipt}; +use alloy_rpc_types::{Block, BlockTransactions, Transaction, WithOtherFields}; +use alloy_rpc_types_trace::parity::{Action, CallType, LocalizedTransactionTrace}; +use anvil_core::eth::transaction::ReceiptResponse; use foundry_evm::{revm::interpreter::InstructionResult, traces::CallKind}; use futures::future::join_all; use serde::Serialize; @@ -40,7 +41,7 @@ pub struct Issuance { #[derive(Clone, Serialize, Debug)] pub struct OtsBlockTransactions { pub fullblock: OtsBlock, - pub receipts: Vec, + pub receipts: Vec, } /// Patched Receipt struct, to include the additional `timestamp` field expected by Otterscan @@ -48,7 +49,7 @@ pub struct OtsBlockTransactions { #[serde(rename_all = "camelCase")] pub struct OtsTransactionReceipt { #[serde(flatten)] - receipt: TransactionReceipt, + receipt: ReceiptResponse, timestamp: u64, } @@ -63,7 +64,7 @@ pub struct OtsContractCreator { #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct OtsSearchTransactions { - pub txs: Vec, + pub txs: Vec>, pub receipts: Vec, pub first_page: bool, pub last_page: bool, @@ -132,22 +133,22 @@ impl OtsBlockDetails { block_txs.iter().map(|tx| async { backend.transaction_receipt(*tx).await }); // fetch all receipts - let receipts: Vec = join_all(receipts_futs) + let receipts = join_all(receipts_futs) .await .into_iter() .map(|r| match r { Ok(Some(r)) => Ok(r), _ => Err(BlockchainError::DataUnavailable), }) - .collect::>()?; + .collect::>>()?; - let total_fees = receipts.iter().fold(U256::ZERO, |acc, receipt| { - acc + receipt.gas_used.unwrap() * (U256::from(receipt.effective_gas_price)) - }); + let total_fees = receipts + .iter() + .fold(0, |acc, receipt| acc + receipt.gas_used * receipt.effective_gas_price); Ok(Self { block: block.into(), - total_fees, + total_fees: U256::from(total_fees), // issuance has no meaningful value in anvil's backend. just default to 0 issuance: Default::default(), }) @@ -198,7 +199,7 @@ impl OtsBlockTransactions { let receipt_futs = block_txs.iter().map(|tx| async { backend.transaction_receipt(*tx).await }); - let receipts: Vec = join_all(receipt_futs) + let receipts = join_all(receipt_futs) .await .into_iter() .map(|r| match r { @@ -225,7 +226,7 @@ impl OtsSearchTransactions { ) -> Result { let txs_futs = hashes.iter().map(|hash| async { backend.transaction_by_hash(*hash).await }); - let txs: Vec = join_all(txs_futs) + let txs: Vec<_> = join_all(txs_futs) .await .into_iter() .map(|t| match t { @@ -237,11 +238,8 @@ impl OtsSearchTransactions { join_all(hashes.iter().map(|hash| async { match backend.transaction_receipt(*hash).await { Ok(Some(receipt)) => { - let timestamp = backend - .get_block(receipt.block_number.unwrap().to::()) - .unwrap() - .header - .timestamp; + let timestamp = + backend.get_block(receipt.block_number.unwrap()).unwrap().header.timestamp; Ok(OtsTransactionReceipt { receipt, timestamp }) } Ok(None) => Err(BlockchainError::DataUnavailable), diff --git a/crates/anvil/src/eth/pool/transactions.rs b/crates/anvil/src/eth/pool/transactions.rs index 312bde481..a88bc369c 100644 --- a/crates/anvil/src/eth/pool/transactions.rs +++ b/crates/anvil/src/eth/pool/transactions.rs @@ -1,5 +1,5 @@ use crate::eth::{error::PoolError, util::hex_fmt_many}; -use alloy_primitives::{Address, TxHash, U256}; +use alloy_primitives::{Address, TxHash}; use anvil_core::eth::transaction::{PendingTransaction, TypedTransaction}; use parking_lot::RwLock; use std::{ @@ -65,10 +65,10 @@ impl FromStr for TransactionOrder { /// Metric value for the priority of a transaction. /// -/// The `TransactionPriority` determines the ordering of two transactions that have all their +/// The `TransactionPriority` determines the ordering of two transactions that have all their /// markers satisfied. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] -pub struct TransactionPriority(pub U256); +pub struct TransactionPriority(pub u128); /// Internal Transaction type #[derive(Clone, PartialEq, Eq)] @@ -92,7 +92,7 @@ impl PoolTransaction { } /// Returns the gas pric of this transaction - pub fn gas_price(&self) -> U256 { + pub fn gas_price(&self) -> u128 { self.pending_transaction.transaction.gas_price() } } @@ -679,7 +679,7 @@ impl ReadyTransaction { &self.transaction.transaction.provides } - pub fn gas_price(&self) -> U256 { + pub fn gas_price(&self) -> u128 { self.transaction.transaction.gas_price() } } diff --git a/crates/anvil/src/eth/sign.rs b/crates/anvil/src/eth/sign.rs index 4886d96f0..949a517fa 100644 --- a/crates/anvil/src/eth/sign.rs +++ b/crates/anvil/src/eth/sign.rs @@ -1,8 +1,10 @@ use crate::eth::error::BlockchainError; +use alloy_consensus::{SignableTransaction, Signed}; use alloy_dyn_abi::TypedData; -use alloy_network::{Signed, Transaction}; -use alloy_primitives::{Address, Signature, B256, U256}; -use alloy_signer::{LocalWallet, Signer as AlloySigner, SignerSync as AlloySignerSync}; +use alloy_network::TxSignerSync; +use alloy_primitives::{Address, Signature, B256}; +use alloy_signer::Signer as AlloySigner; +use alloy_signer_wallet::LocalWallet; use anvil_core::eth::transaction::{ optimism::{DepositTransaction, DepositTransactionRequest}, TypedTransaction, TypedTransactionRequest, @@ -84,17 +86,13 @@ impl Signer for DevSigner { // typed data. signer.set_chain_id(None); - Ok(signer - .sign_hash( - payload.eip712_signing_hash().map_err(|_| BlockchainError::NoSignerAvailable)?, - ) - .await?) + Ok(signer.sign_dynamic_typed_data(payload).await?) } async fn sign_hash(&self, address: Address, hash: B256) -> Result { let signer = self.accounts.get(&address).ok_or(BlockchainError::NoSignerAvailable)?; - Ok(signer.sign_hash(hash).await?) + Ok(signer.sign_hash(&hash).await?) } fn sign_transaction( @@ -160,7 +158,7 @@ pub fn build_typed_transaction( source_hash, mint, is_system_tx, - nonce: U256::ZERO, + nonce: 0, }) } }; diff --git a/crates/anvil/src/lib.rs b/crates/anvil/src/lib.rs index afc600a3b..11a860bc3 100644 --- a/crates/anvil/src/lib.rs +++ b/crates/anvil/src/lib.rs @@ -17,7 +17,7 @@ use crate::{ tasks::TaskManager, }; use alloy_primitives::{Address, U256}; -use alloy_signer::{LocalWallet, Signer as AlloySigner}; +use alloy_signer_wallet::LocalWallet; use eth::backend::fork::ClientFork; use foundry_common::provider::alloy::{ProviderBuilder, RetryProvider}; use foundry_evm::revm; @@ -311,7 +311,7 @@ impl NodeHandle { } /// Default gas price for all txs - pub fn gas_price(&self) -> U256 { + pub fn gas_price(&self) -> u128 { self.config.get_gas_price() } diff --git a/crates/anvil/src/pubsub.rs b/crates/anvil/src/pubsub.rs index b0b8b33bb..a063ee669 100644 --- a/crates/anvil/src/pubsub.rs +++ b/crates/anvil/src/pubsub.rs @@ -2,10 +2,8 @@ use crate::{ eth::{backend::notifications::NewBlockNotifications, error::to_rpc_result}, StorageInfo, }; -use alloy_consensus::ReceiptWithBloom; -use alloy_network::Sealable; -use alloy_primitives::{Log, TxHash, B256, U256}; -use alloy_rpc_types::{pubsub::SubscriptionResult, FilteredParams, Log as AlloyLog}; +use alloy_primitives::{TxHash, B256}; +use alloy_rpc_types::{pubsub::SubscriptionResult, FilteredParams, Log}; use anvil_core::eth::{block::Block, subscription::SubscriptionId, transaction::TypedReceipt}; use anvil_rpc::{request::Version, response::ResponseResult}; use futures::{channel::mpsc::Receiver, ready, Stream, StreamExt}; @@ -22,7 +20,7 @@ pub struct LogsSubscription { pub blocks: NewBlockNotifications, pub storage: StorageInfo, pub filter: FilteredParams, - pub queued: VecDeque, + pub queued: VecDeque, pub id: SubscriptionId, } @@ -145,13 +143,14 @@ impl Stream for EthSubscription { } /// Returns all the logs that match the given filter -pub fn filter_logs( - block: Block, - receipts: Vec, - filter: &FilteredParams, -) -> Vec { +pub fn filter_logs(block: Block, receipts: Vec, filter: &FilteredParams) -> Vec { /// Determines whether to add this log - fn add_log(block_hash: B256, l: &Log, block: &Block, params: &FilteredParams) -> bool { + fn add_log( + block_hash: B256, + l: &alloy_primitives::Log, + block: &Block, + params: &FilteredParams, + ) -> bool { if params.filter.is_some() { let block_number = block.header.number; if !params.filter_block_range(block_number) || @@ -165,29 +164,22 @@ pub fn filter_logs( true } - let block_hash = block.header.hash(); + let block_hash = block.header.hash_slow(); let mut logs = vec![]; let mut log_index: u32 = 0; for (receipt_index, receipt) in receipts.into_iter().enumerate() { - let receipt: ReceiptWithBloom = receipt.into(); - let receipt_logs = receipt.receipt.logs; - let transaction_hash: Option = if !receipt_logs.is_empty() { - Some(block.transactions[receipt_index].hash()) - } else { - None - }; - for log in receipt_logs.into_iter() { - if add_log(block_hash, &log, &block, filter) { - logs.push(AlloyLog { - address: log.address, - topics: log.topics().to_vec(), - data: log.data.data, + let transaction_hash = block.transactions[receipt_index].hash(); + for log in receipt.logs() { + if add_log(block_hash, log, &block, filter) { + logs.push(Log { + inner: log.clone(), block_hash: Some(block_hash), - block_number: Some(U256::from(block.header.number)), - transaction_hash, - transaction_index: Some(U256::from(receipt_index)), - log_index: Some(U256::from(log_index)), + block_number: Some(block.header.number), + transaction_hash: Some(transaction_hash), + transaction_index: Some(receipt_index as u64), + log_index: Some(log_index as u64), removed: false, + block_timestamp: Some(block.header.timestamp), }); } log_index += 1; diff --git a/crates/anvil/src/tasks/mod.rs b/crates/anvil/src/tasks/mod.rs index 429f8d5d3..e42b0437c 100644 --- a/crates/anvil/src/tasks/mod.rs +++ b/crates/anvil/src/tasks/mod.rs @@ -1,12 +1,13 @@ //! Task management support use crate::{shutdown::Shutdown, tasks::block_listener::BlockListener, EthApi}; +use alloy_network::AnyNetwork; +use alloy_primitives::B256; +use alloy_provider::Provider; +use alloy_rpc_types::Block; +use alloy_transport::Transport; use anvil_core::types::Forking; -use ethers::{ - prelude::Middleware, - providers::{JsonRpcClient, PubsubClient}, - types::{Block, H256}, -}; +use futures::StreamExt; use std::{fmt, future::Future}; use tokio::{runtime::Handle, task::JoinHandle}; @@ -51,32 +52,33 @@ impl TaskManager { /// block /// /// ``` + /// use alloy_network::Ethereum; + /// use alloy_provider::RootProvider; /// use anvil::{spawn, NodeConfig}; - /// use ethers::providers::Provider; - /// use std::sync::Arc; + /// /// # async fn t() { /// let endpoint = "http://...."; /// let (api, handle) = spawn(NodeConfig::default().with_eth_rpc_url(Some(endpoint))).await; /// - /// let provider = Arc::new(Provider::try_from(endpoint).unwrap()); + /// let provider = RootProvider::connect_builtin(endpoint).await.unwrap(); /// /// handle.task_manager().spawn_reset_on_new_polled_blocks(provider, api); /// # } /// ``` - pub fn spawn_reset_on_new_polled_blocks

(&self, provider: P, api: EthApi) + pub fn spawn_reset_on_new_polled_blocks(&self, provider: P, api: EthApi) where - P: Middleware + Clone + Unpin + 'static + Send + Sync, -

::Provider: JsonRpcClient, + P: Provider + Clone + Unpin + 'static, + T: Transport + Clone, { self.spawn_block_poll_listener(provider.clone(), move |hash| { let provider = provider.clone(); let api = api.clone(); async move { - if let Ok(Some(block)) = provider.get_block(hash).await { + if let Ok(Some(block)) = provider.get_block(hash.into(), false).await { let _ = api .anvil_reset(Some(Forking { json_rpc_url: None, - block_number: block.number.map(|b| b.as_u64()), + block_number: block.header.number, })) .await; } @@ -87,16 +89,21 @@ impl TaskManager { /// Spawns a new [`BlockListener`] task that listens for new blocks (poll-based) See also /// [`Provider::watch_blocks`] and executes the future the `task_factory` returns for the new /// block hash - pub fn spawn_block_poll_listener(&self, provider: P, task_factory: F) + pub fn spawn_block_poll_listener(&self, provider: P, task_factory: F) where - P: Middleware + Unpin + 'static, -

::Provider: JsonRpcClient, - F: Fn(H256) -> Fut + Unpin + Send + Sync + 'static, + P: Provider + 'static, + T: Transport + Clone, + F: Fn(B256) -> Fut + Unpin + Send + Sync + 'static, Fut: Future + Send, { let shutdown = self.on_shutdown.clone(); self.spawn(async move { - let blocks = provider.watch_blocks().await.unwrap(); + let blocks = provider + .watch_blocks() + .await + .unwrap() + .into_stream() + .flat_map(futures::stream::iter); BlockListener::new(shutdown, blocks, task_factory).await; }); } @@ -105,21 +112,23 @@ impl TaskManager { /// block /// /// ``` + /// use alloy_network::Ethereum; + /// use alloy_provider::RootProvider; /// use anvil::{spawn, NodeConfig}; - /// use ethers::providers::Provider; + /// /// # async fn t() { /// let (api, handle) = spawn(NodeConfig::default().with_eth_rpc_url(Some("http://...."))).await; /// - /// let provider = Provider::connect("ws://...").await.unwrap(); + /// let provider = RootProvider::connect_builtin("ws://...").await.unwrap(); /// /// handle.task_manager().spawn_reset_on_subscribed_blocks(provider, api); /// /// # } /// ``` - pub fn spawn_reset_on_subscribed_blocks

(&self, provider: P, api: EthApi) + pub fn spawn_reset_on_subscribed_blocks(&self, provider: P, api: EthApi) where - P: Middleware + Unpin + 'static + Send + Sync, -

::Provider: PubsubClient, + P: Provider + 'static, + T: Transport + Clone, { self.spawn_block_subscription(provider, move |block| { let api = api.clone(); @@ -127,7 +136,7 @@ impl TaskManager { let _ = api .anvil_reset(Some(Forking { json_rpc_url: None, - block_number: block.number.map(|b| b.as_u64()), + block_number: block.header.number, })) .await; } @@ -137,16 +146,16 @@ impl TaskManager { /// Spawns a new [`BlockListener`] task that listens for new blocks (via subscription) See also /// [`Provider::subscribe_blocks()`] and executes the future the `task_factory` returns for the /// new block hash - pub fn spawn_block_subscription(&self, provider: P, task_factory: F) + pub fn spawn_block_subscription(&self, provider: P, task_factory: F) where - P: Middleware + Unpin + 'static, -

::Provider: PubsubClient, - F: Fn(Block) -> Fut + Unpin + Send + Sync + 'static, + P: Provider + 'static, + T: Transport + Clone, + F: Fn(Block) -> Fut + Unpin + Send + Sync + 'static, Fut: Future + Send, { let shutdown = self.on_shutdown.clone(); self.spawn(async move { - let blocks = provider.subscribe_blocks().await.unwrap(); + let blocks = provider.subscribe_blocks().await.unwrap().into_stream(); BlockListener::new(shutdown, blocks, task_factory).await; }); } diff --git a/crates/anvil/tests/it/anvil_api.rs b/crates/anvil/tests/it/anvil_api.rs index 2f37f9b56..d2826b9ed 100644 --- a/crates/anvil/tests/it/anvil_api.rs +++ b/crates/anvil/tests/it/anvil_api.rs @@ -44,7 +44,7 @@ async fn can_set_block_gas_limit() { api.mine_one().await; let latest_block = api.block_by_number(alloy_rpc_types::BlockNumberOrTag::Latest).await.unwrap().unwrap(); - assert_eq!(block_gas_limit.to_alloy(), latest_block.header.gas_limit); + assert_eq!(block_gas_limit.as_u128(), latest_block.header.gas_limit); } // Ref @@ -445,10 +445,10 @@ async fn can_get_node_info() { hard_fork: SpecId::CANCUN, transaction_order: "fees".to_owned(), environment: NodeEnvironment { - base_fee: U256::from_str("0x3b9aca00").unwrap().to_alloy(), + base_fee: alloy_primitives::U256::from_str("0x3b9aca00").unwrap().to(), chain_id: 0x7a69, - gas_limit: U256::from_str("0x1c9c380").unwrap().to_alloy(), - gas_price: U256::from_str("0x77359400").unwrap().to_alloy(), + gas_limit: alloy_primitives::U256::from_str("0x1c9c380").unwrap().to(), + gas_price: alloy_primitives::U256::from_str("0x77359400").unwrap().to(), }, fork_config: NodeForkConfig { fork_url: None, @@ -616,16 +616,16 @@ async fn test_fork_revert_call_latest_block_timestamp() { ); assert_eq!( - multicall.get_current_block_timestamp().await.unwrap(), - latest_block.header.timestamp.to_ethers() + multicall.get_current_block_timestamp().await.unwrap().as_u64(), + latest_block.header.timestamp ); assert_eq!( multicall.get_current_block_difficulty().await.unwrap(), latest_block.header.difficulty.to_ethers() ); assert_eq!( - multicall.get_current_block_gas_limit().await.unwrap(), - latest_block.header.gas_limit.to_ethers() + multicall.get_current_block_gas_limit().await.unwrap().as_u128(), + latest_block.header.gas_limit ); assert_eq!( multicall.get_current_block_coinbase().await.unwrap(), diff --git a/crates/anvil/tests/it/api.rs b/crates/anvil/tests/it/api.rs index 216e95813..acf9a0f98 100644 --- a/crates/anvil/tests/it/api.rs +++ b/crates/anvil/tests/it/api.rs @@ -5,12 +5,12 @@ use crate::{ utils::ethers_http_provider, }; use alloy_primitives::{Address as rAddress, B256, U256 as rU256}; -use alloy_providers::tmp::TempProvider; +use alloy_provider::Provider; use alloy_rpc_types::{ request::{TransactionInput as CallInput, TransactionRequest as CallRequest}, state::{AccountOverride, StateOverride}, + WithOtherFields, }; -use alloy_signer::Signer as AlloySigner; use anvil::{ eth::{api::CLIENT_VERSION, EthApi}, spawn, NodeConfig, CHAIN_ID, @@ -213,7 +213,7 @@ async fn can_call_on_pending_block() { .call() .await .unwrap(); - assert_eq!(block.header.timestamp, block_timestamp.to_alloy()); + assert_eq!(block.header.timestamp, block_timestamp.as_u64()); let block_gas_limit = pending_contract .get_current_block_gas_limit() @@ -221,7 +221,7 @@ async fn can_call_on_pending_block() { .call() .await .unwrap(); - assert_eq!(block.header.gas_limit, block_gas_limit.to_alloy()); + assert_eq!(block.header.gas_limit, block_gas_limit.as_u128()); let block_coinbase = pending_contract .get_current_block_coinbase() @@ -244,11 +244,11 @@ where { let result = api .call( - CallRequest { + WithOtherFields::new(CallRequest { input: CallInput::maybe_input(call.tx.data().cloned().map(|b| b.0.into())), to: Some(to.to_alloy()), ..Default::default() - }, + }), None, Some(overrides), ) diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index 39946383e..5f94a95ae 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -5,12 +5,11 @@ use crate::{ utils::{self, ethers_http_provider}, }; use alloy_primitives::{address, U256 as rU256}; -use alloy_providers::tmp::TempProvider; +use alloy_provider::Provider as AlloyProvider; use alloy_rpc_types::{ request::{TransactionInput, TransactionRequest as CallRequest}, - BlockNumberOrTag, + BlockNumberOrTag, WithOtherFields, }; -use alloy_signer::Signer as AlloySigner; use anvil::{eth::EthApi, spawn, NodeConfig, NodeHandle}; use anvil_core::types::Forking; use ethers::{ @@ -23,7 +22,7 @@ use ethers::{ }, }; use foundry_common::{ - provider::ethers::get_http_provider, + provider::alloy::get_http_provider, rpc, rpc::next_http_rpc_endpoint, types::{ToAlloy, ToEthers}, @@ -290,7 +289,7 @@ async fn test_fork_snapshotting() { let provider = handle.http_provider(); let nonce = provider.get_transaction_count(from, None).await.unwrap(); - assert_eq!(nonce, initial_nonce + rU256::from(1)); + assert_eq!(nonce, initial_nonce + 1); let to_balance = provider.get_balance(to, None).await.unwrap(); assert_eq!(balance_before.saturating_add(amount), to_balance); @@ -329,7 +328,7 @@ async fn test_fork_snapshotting_repeated() { let _ = tx_provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); let nonce = provider.get_transaction_count(from, None).await.unwrap(); - assert_eq!(nonce, initial_nonce + rU256::from(1)); + assert_eq!(nonce, initial_nonce + 1); let to_balance = provider.get_balance(to, None).await.unwrap(); assert_eq!(balance_before.saturating_add(amount), to_balance); @@ -383,7 +382,7 @@ async fn test_fork_snapshotting_blocks() { assert_eq!(block_number_after, block_number + 1); let nonce = provider.get_transaction_count(from, None).await.unwrap(); - assert_eq!(nonce, initial_nonce + rU256::from(1)); + assert_eq!(nonce, initial_nonce + 1); let to_balance = provider.get_balance(to, None).await.unwrap(); assert_eq!(balance_before.saturating_add(amount), to_balance); @@ -397,12 +396,12 @@ async fn test_fork_snapshotting_blocks() { // repeat transaction let _ = tx_provider.send_transaction(tx.clone(), None).await.unwrap().await.unwrap().unwrap(); let nonce = provider.get_transaction_count(from, None).await.unwrap(); - assert_eq!(nonce, initial_nonce + rU256::from(1)); + assert_eq!(nonce, initial_nonce + 1); // revert again: nothing to revert since snapshot gone assert!(!api.evm_revert(snapshot).await.unwrap()); let nonce = provider.get_transaction_count(from, None).await.unwrap(); - assert_eq!(nonce, initial_nonce + rU256::from(1)); + assert_eq!(nonce, initial_nonce + 1); let block_number_after = provider.get_block_number().await.unwrap(); assert_eq!(block_number_after, block_number + 1); } @@ -468,8 +467,8 @@ async fn can_reset_properly() { let (origin_api, origin_handle) = spawn(NodeConfig::test()).await; let account = origin_handle.dev_accounts().next().unwrap(); let origin_provider = origin_handle.http_provider(); - let origin_nonce = rU256::from(1u64); - origin_api.anvil_set_nonce(account, origin_nonce).await.unwrap(); + let origin_nonce = 1u64; + origin_api.anvil_set_nonce(account, rU256::from(origin_nonce)).await.unwrap(); assert_eq!(origin_nonce, origin_provider.get_transaction_count(account, None).await.unwrap()); @@ -486,10 +485,7 @@ async fn can_reset_properly() { let tx = fork_tx_provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); // nonce incremented by 1 - assert_eq!( - origin_nonce + rU256::from(1), - fork_provider.get_transaction_count(account, None).await.unwrap() - ); + assert_eq!(origin_nonce + 1, fork_provider.get_transaction_count(account, None).await.unwrap()); // resetting to origin state fork_api.anvil_reset(Some(Forking::default())).await.unwrap(); @@ -754,13 +750,19 @@ async fn test_reset_fork_on_new_blocks() { let anvil_provider = ethers_http_provider(&handle.http_endpoint()); let endpoint = next_http_rpc_endpoint(); - let provider = Arc::new(get_http_provider(&endpoint).interval(Duration::from_secs(2))); + let provider = Arc::new(get_http_provider(&endpoint)); let current_block = anvil_provider.get_block_number().await.unwrap(); handle.task_manager().spawn_reset_on_new_polled_blocks(provider.clone(), api); - let mut stream = provider.watch_blocks().await.unwrap(); + let mut stream = provider + .watch_blocks() + .await + .unwrap() + .with_poll_interval(Duration::from_secs(2)) + .into_stream() + .flat_map(futures::stream::iter); // the http watcher may fetch multiple blocks at once, so we set a timeout here to offset edge // cases where the stream immediately returns a block tokio::time::sleep(Chain::Mainnet.average_blocktime_hint().unwrap()).await; @@ -788,11 +790,11 @@ async fn test_fork_call() { let res1 = api .call( - CallRequest { + WithOtherFields::new(CallRequest { to: Some(to.to_alloy()), input: input.to_alloy().into(), ..Default::default() - }, + }), None, None, ) @@ -810,7 +812,7 @@ async fn test_fork_block_timestamp() { api.anvil_mine(Some(rU256::from(1)), None).await.unwrap(); let latest_block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); - assert!(initial_block.header.timestamp.to::() < latest_block.header.timestamp.to::()); + assert!(initial_block.header.timestamp < latest_block.header.timestamp); } #[tokio::test(flavor = "multi_thread")] @@ -821,14 +823,11 @@ async fn test_fork_snapshot_block_timestamp() { api.anvil_mine(Some(rU256::from(1)), None).await.unwrap(); let initial_block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); api.evm_revert(snapshot_id).await.unwrap(); - api.evm_set_next_block_timestamp(initial_block.header.timestamp.to::()).unwrap(); + api.evm_set_next_block_timestamp(initial_block.header.timestamp).unwrap(); api.anvil_mine(Some(rU256::from(1)), None).await.unwrap(); let latest_block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); - assert_eq!( - initial_block.header.timestamp.to::(), - latest_block.header.timestamp.to::() - ); + assert_eq!(initial_block.header.timestamp, latest_block.header.timestamp); } #[tokio::test(flavor = "multi_thread")] @@ -1092,7 +1091,7 @@ async fn test_fork_reset_basefee() { let latest = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); // basefee of +1 block: - assert_eq!(latest.header.base_fee_per_gas.unwrap(), rU256::from(59455969592u64)); + assert_eq!(latest.header.base_fee_per_gas.unwrap(), 59455969592u128); // now reset to block 18835000 -1 api.anvil_reset(Some(Forking { json_rpc_url: None, block_number: Some(18835000u64 - 1) })) @@ -1103,7 +1102,7 @@ async fn test_fork_reset_basefee() { let latest = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); // basefee of the forked block: - assert_eq!(latest.header.base_fee_per_gas.unwrap(), rU256::from(59017001138u64)); + assert_eq!(latest.header.base_fee_per_gas.unwrap(), 59017001138u128); } // @@ -1180,11 +1179,11 @@ async fn test_fork_execution_reverted() { let resp = api .call( - CallRequest { + WithOtherFields::new(CallRequest { to: Some(address!("Fd6CC4F251eaE6d02f9F7B41D1e80464D3d2F377")), input: TransactionInput::new("0x8f283b3c".as_bytes().into()), ..Default::default() - }, + }), Some(target.into()), None, ) diff --git a/crates/anvil/tests/it/gas.rs b/crates/anvil/tests/it/gas.rs index 4f6098bfb..e2d194340 100644 --- a/crates/anvil/tests/it/gas.rs +++ b/crates/anvil/tests/it/gas.rs @@ -1,7 +1,6 @@ //! Gas related tests use crate::utils::ethers_http_provider; -use alloy_primitives::U256; use anvil::{eth::fees::INITIAL_BASE_FEE, spawn, NodeConfig}; use ethers::{ prelude::Middleware, @@ -10,16 +9,13 @@ use ethers::{ TransactionRequest, }, }; -use foundry_common::types::ToAlloy; -const GAS_TRANSFER: u64 = 21_000u64; +const GAS_TRANSFER: u128 = 21_000; #[tokio::test(flavor = "multi_thread")] async fn test_basefee_full_block() { let (_api, handle) = spawn( - NodeConfig::test() - .with_base_fee(Some(INITIAL_BASE_FEE.to_alloy())) - .with_gas_limit(Some(GAS_TRANSFER.to_alloy())), + NodeConfig::test().with_base_fee(Some(INITIAL_BASE_FEE)).with_gas_limit(Some(GAS_TRANSFER)), ) .await; let provider = ethers_http_provider(&handle.http_endpoint()); @@ -34,15 +30,15 @@ async fn test_basefee_full_block() { assert!(next_base_fee > base_fee); // max increase, full block - assert_eq!(next_base_fee.as_u64(), INITIAL_BASE_FEE + 125_000_000); + assert_eq!(next_base_fee.as_u128(), INITIAL_BASE_FEE + 125_000_000); } #[tokio::test(flavor = "multi_thread")] async fn test_basefee_half_block() { let (_api, handle) = spawn( NodeConfig::test() - .with_base_fee(Some(INITIAL_BASE_FEE.to_alloy())) - .with_gas_limit(Some(GAS_TRANSFER.to_alloy() * U256::from(2))), + .with_base_fee(Some(INITIAL_BASE_FEE)) + .with_gas_limit(Some(GAS_TRANSFER * 2)), ) .await; let provider = ethers_http_provider(&handle.http_endpoint()); @@ -54,12 +50,11 @@ async fn test_basefee_half_block() { provider.get_block(BlockNumber::Latest).await.unwrap().unwrap().base_fee_per_gas.unwrap(); // unchanged, half block - assert_eq!(next_base_fee.as_u64(), INITIAL_BASE_FEE); + assert_eq!(next_base_fee.as_u128(), INITIAL_BASE_FEE); } #[tokio::test(flavor = "multi_thread")] async fn test_basefee_empty_block() { - let (api, handle) = - spawn(NodeConfig::test().with_base_fee(Some(INITIAL_BASE_FEE.to_alloy()))).await; + let (api, handle) = spawn(NodeConfig::test().with_base_fee(Some(INITIAL_BASE_FEE))).await; let provider = ethers_http_provider(&handle.http_endpoint()); let tx = TransactionRequest::new().to(Address::random()).value(1337u64); @@ -79,8 +74,8 @@ async fn test_basefee_empty_block() { #[tokio::test(flavor = "multi_thread")] async fn test_respect_base_fee() { - let base_fee = 50u64; - let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee.to_alloy()))).await; + let base_fee = 50u128; + let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee))).await; let provider = ethers_http_provider(&handle.http_endpoint()); let mut tx = TypedTransaction::default(); tx.set_value(100u64); @@ -99,8 +94,8 @@ async fn test_respect_base_fee() { #[tokio::test(flavor = "multi_thread")] async fn test_tip_above_fee_cap() { - let base_fee = 50u64; - let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee.to_alloy()))).await; + let base_fee = 50u128; + let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee))).await; let provider = ethers_http_provider(&handle.http_endpoint()); let tx = TypedTransaction::Eip1559( Eip1559TransactionRequest::new() diff --git a/crates/anvil/tests/it/genesis.rs b/crates/anvil/tests/it/genesis.rs index f5f5fec9c..0b243db3e 100644 --- a/crates/anvil/tests/it/genesis.rs +++ b/crates/anvil/tests/it/genesis.rs @@ -3,8 +3,8 @@ use std::str::FromStr; use alloy_genesis::Genesis; -use alloy_primitives::{Address, U256, U64}; -use alloy_providers::tmp::TempProvider; +use alloy_primitives::{Address, U256}; +use alloy_provider::Provider; use anvil::{spawn, NodeConfig}; #[tokio::test(flavor = "multi_thread")] @@ -41,7 +41,7 @@ async fn can_apply_genesis() { let provider = handle.http_provider(); - assert_eq!(provider.get_chain_id().await.unwrap(), U64::from(19763u64)); + assert_eq!(provider.get_chain_id().await.unwrap(), 19763u64); let addr: Address = Address::from_str("71562b71999873db5b286df957af199ec94617f7").unwrap(); let balance = provider.get_balance(addr, None).await.unwrap(); diff --git a/crates/anvil/tests/it/otterscan.rs b/crates/anvil/tests/it/otterscan.rs index 6bbd987a8..4c3442640 100644 --- a/crates/anvil/tests/it/otterscan.rs +++ b/crates/anvil/tests/it/otterscan.rs @@ -7,7 +7,6 @@ use crate::{ }; use alloy_primitives::U256 as rU256; use alloy_rpc_types::{BlockNumberOrTag, BlockTransactions}; -use alloy_signer::Signer as AlloySigner; use anvil::{ eth::otterscan::types::{ OtsInternalOperation, OtsInternalOperationType, OtsTrace, OtsTraceType, @@ -33,8 +32,8 @@ async fn can_call_erigon_get_header_by_number() { let res0 = api.erigon_get_header_by_number(0.into()).await.unwrap().unwrap(); let res1 = api.erigon_get_header_by_number(1.into()).await.unwrap().unwrap(); - assert_eq!(res0.header.number, Some(rU256::from(0))); - assert_eq!(res1.header.number, Some(rU256::from(1))); + assert_eq!(res0.header.number, Some(0)); + assert_eq!(res1.header.number, Some(1)); } #[tokio::test(flavor = "multi_thread")] @@ -487,7 +486,7 @@ async fn can_call_ots_get_block_transactions() { result.receipts.iter().enumerate().for_each(|(i, receipt)| { let expected = hashes.pop_front(); - assert_eq!(expected, receipt.transaction_hash.map(|h| h.to_ethers())); + assert_eq!(expected, Some(receipt.transaction_hash.to_ethers())); assert_eq!( expected.map(|h| h.to_alloy()), result.fullblock.block.transactions.hashes().nth(i).copied(), @@ -528,7 +527,7 @@ async fn can_call_ots_search_transactions_before() { assert_eq!(hashes.pop(), Some(tx.hash.to_ethers())); }); - block = result.txs.last().unwrap().block_number.unwrap().to::() - 1; + block = result.txs.last().unwrap().block_number.unwrap() - 1; } assert!(hashes.is_empty()); @@ -564,7 +563,7 @@ async fn can_call_ots_search_transactions_after() { assert_eq!(hashes.pop_back(), Some(tx.hash.to_ethers())); }); - block = result.txs.last().unwrap().block_number.unwrap().to::() + 1; + block = result.txs.last().unwrap().block_number.unwrap() + 1; } assert!(hashes.is_empty()); diff --git a/crates/anvil/tests/it/transaction.rs b/crates/anvil/tests/it/transaction.rs index 18a74e1a9..46c059374 100644 --- a/crates/anvil/tests/it/transaction.rs +++ b/crates/anvil/tests/it/transaction.rs @@ -2,13 +2,12 @@ use crate::{ abi::*, utils::{ethers_http_provider, ethers_ws_provider}, }; -use alloy_primitives::U256 as rU256; +use alloy_primitives::{Bytes, U256 as rU256}; use alloy_rpc_types::{ request::TransactionRequest as AlloyTransactionRequest, state::{AccountOverride, StateOverride}, - BlockNumberOrTag, + BlockNumberOrTag, WithOtherFields, }; -use alloy_signer::Signer as AlloySigner; use anvil::{spawn, Hardfork, NodeConfig}; use ethers::{ abi::ethereum_types::BigEndianHash, @@ -21,7 +20,7 @@ use ethers::{ Address, BlockNumber, Transaction, TransactionReceipt, H256, U256, }, }; -use foundry_common::types::{to_call_request_from_tx_request, ToAlloy, ToEthers}; +use foundry_common::types::{ToAlloy, ToEthers}; use futures::{future::join_all, FutureExt, StreamExt}; use std::{collections::HashSet, sync::Arc, time::Duration}; use tokio::time::timeout; @@ -434,11 +433,11 @@ async fn get_blocktimestamp_works() { api.block_by_number(alloy_rpc_types::BlockNumberOrTag::Latest).await.unwrap().unwrap(); let timestamp = contract.get_current_block_timestamp().call().await.unwrap(); - assert_eq!(timestamp, latest_block.header.timestamp.to_ethers()); + assert_eq!(timestamp.as_u64(), latest_block.header.timestamp); // repeat call same result let timestamp = contract.get_current_block_timestamp().call().await.unwrap(); - assert_eq!(timestamp, latest_block.header.timestamp.to_ethers()); + assert_eq!(timestamp.as_u64(), latest_block.header.timestamp); // mock timestamp let next_timestamp = timestamp.as_u64() + 1337; @@ -962,9 +961,12 @@ async fn estimates_gas_on_pending_by_default() { let tx = TransactionRequest::new().from(sender).to(recipient).value(1e18 as u64); client.send_transaction(tx, None).await.unwrap(); - let tx = - TransactionRequest::new().from(recipient).to(sender).value(1e10 as u64).data(vec![0x42]); - api.estimate_gas(to_call_request_from_tx_request(tx), None, None).await.unwrap(); + let tx = AlloyTransactionRequest::default() + .from(recipient.to_alloy()) + .to(Some(sender.to_alloy())) + .value(rU256::from(1e10)) + .input(Bytes::from(vec![0x42]).into()); + api.estimate_gas(WithOtherFields::new(tx), None, None).await.unwrap(); } #[tokio::test(flavor = "multi_thread")] @@ -975,11 +977,13 @@ async fn test_estimate_gas() { let sender = wallet.address(); let recipient = Address::random(); - let tx = - TransactionRequest::new().from(recipient).to(sender).value(1e10 as u64).data(vec![0x42]); + let tx = AlloyTransactionRequest::default() + .from(recipient.to_alloy()) + .to(Some(sender.to_alloy())) + .value(rU256::from(1e10)) + .input(Bytes::from(vec![0x42]).into()); // Expect the gas estimation to fail due to insufficient funds. - let error_result = - api.estimate_gas(to_call_request_from_tx_request(tx.clone()), None, None).await; + let error_result = api.estimate_gas(WithOtherFields::new(tx.clone()), None, None).await; assert!(error_result.is_err(), "Expected an error due to insufficient funds"); let error_message = error_result.unwrap_err().to_string(); @@ -998,15 +1002,12 @@ async fn test_estimate_gas() { // Estimate gas with state override implying sufficient funds. let gas_estimate = api - .estimate_gas(to_call_request_from_tx_request(tx), None, Some(state_override)) + .estimate_gas(WithOtherFields::new(tx), None, Some(state_override)) .await .expect("Failed to estimate gas with state override"); // Assert the gas estimate meets the expected minimum. - assert!( - gas_estimate >= alloy_primitives::U256::from(21000), - "Gas estimate is lower than expected minimum" - ); + assert!(gas_estimate >= rU256::from(21000), "Gas estimate is lower than expected minimum"); } #[tokio::test(flavor = "multi_thread")] @@ -1032,8 +1033,7 @@ async fn test_reject_gas_too_low() { // #[tokio::test(flavor = "multi_thread")] async fn can_call_with_high_gas_limit() { - let (_api, handle) = - spawn(NodeConfig::test().with_gas_limit(Some(U256::from(100_000_000).to_alloy()))).await; + let (_api, handle) = spawn(NodeConfig::test().with_gas_limit(Some(100_000_000))).await; let provider = ethers_http_provider(&handle.http_endpoint()); let wallet = handle.dev_wallets().next().unwrap().to_ethers(); @@ -1094,8 +1094,8 @@ async fn can_mine_multiple_in_block() { }; // broadcast it via the eth_sendTransaction API - let first = api.send_transaction(tx.clone()).await.unwrap(); - let second = api.send_transaction(tx.clone()).await.unwrap(); + let first = api.send_transaction(WithOtherFields::new(tx.clone())).await.unwrap(); + let second = api.send_transaction(WithOtherFields::new(tx.clone())).await.unwrap(); api.anvil_mine(Some(rU256::from(1)), Some(rU256::ZERO)).await.unwrap(); diff --git a/crates/anvil/tests/it/wsapi.rs b/crates/anvil/tests/it/wsapi.rs index c2073f561..ce0c4d6a4 100644 --- a/crates/anvil/tests/it/wsapi.rs +++ b/crates/anvil/tests/it/wsapi.rs @@ -1,6 +1,6 @@ //! general eth api tests with websocket provider -use alloy_providers::tmp::TempProvider; +use alloy_provider::Provider; use anvil::{spawn, NodeConfig}; use ethers::types::U256; use foundry_common::types::ToAlloy; diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index 5361b3dea..6b6dbae51 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -15,7 +15,11 @@ name = "cast" path = "bin/main.rs" [build-dependencies] -vergen = { workspace = true, default-features = false, features = ["build", "git", "gitcl"] } +vergen = { workspace = true, default-features = false, features = [ + "build", + "git", + "gitcl", +] } [dependencies] # lib @@ -30,12 +34,20 @@ alloy-dyn-abi.workspace = true alloy-json-abi.workspace = true alloy-primitives.workspace = true alloy-rlp.workspace = true -alloy-providers.workspace = true +alloy-provider = { workspace = true, features = ["pubsub"] } +alloy-transport.workspace = true alloy-rpc-types.workspace = true +alloy-json-rpc.workspace = true alloy-signer.workspace = true +alloy-signer-wallet = { workspace = true, features = ["mnemonic", "keystore"] } +alloy-contract.workspace = true +alloy-consensus = { workspace = true, features = ["serde"] } +alloy-network.workspace = true +alloy-sol-types.workspace = true +alloy-chains.workspace = true ethers-core.workspace = true -ethers-providers.workspace = true +ethers-contract.workspace = true chrono.workspace = true evm-disassembler.workspace = true @@ -48,17 +60,11 @@ serde_json.workspace = true serde.workspace = true # aws -rusoto_core = { version = "0.48", default-features = false } -rusoto_kms = { version = "0.48", default-features = false } +aws-sdk-kms = { version = "1", default-features = false } # bin foundry-cli.workspace = true -ethers-contract.workspace = true -ethers-middleware.workspace = true -ethers-signers.workspace = true -eth-keystore = "0.5" - clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } clap_complete = "4" clap_complete_fig = "4" @@ -86,7 +92,7 @@ criterion = "0.5" [features] default = ["rustls"] rustls = ["foundry-cli/rustls", "foundry-wallets/rustls"] -openssl = ["foundry-cli/openssl", "foundry-wallets/openssl"] +openssl = ["foundry-cli/openssl"] asm-keccak = ["alloy-primitives/asm-keccak"] jemalloc = ["dep:tikv-jemallocator"] diff --git a/crates/cast/bin/cmd/access_list.rs b/crates/cast/bin/cmd/access_list.rs index 2ac5a0488..ed572628d 100644 --- a/crates/cast/bin/cmd/access_list.rs +++ b/crates/cast/bin/cmd/access_list.rs @@ -1,13 +1,16 @@ -use cast::{Cast, TxBuilder}; +use alloy_network::{AnyNetwork, TransactionBuilder}; +use alloy_primitives::Address; +use alloy_provider::Provider; +use alloy_rpc_types::{BlockId, TransactionRequest, WithOtherFields}; +use alloy_transport::Transport; +use cast::Cast; use clap::Parser; -use ethers_core::types::{BlockId, NameOrAddress}; -use ethers_providers::Middleware; -use eyre::{Result, WrapErr}; +use eyre::Result; use foundry_cli::{ opts::{EthereumOpts, TransactionOpts}, - utils, + utils::{self, parse_function_args}, }; -use foundry_common::types::ToEthers; +use foundry_common::ens::NameOrAddress; use foundry_config::{Chain, Config}; use std::str::FromStr; @@ -62,18 +65,37 @@ impl AccessListArgs { let provider = utils::get_provider(&config)?; let chain = utils::get_chain(config.chain, &provider).await?; let sender = eth.wallet.sender().await; - - access_list(&provider, sender.to_ethers(), to, sig, args, data, tx, chain, block, to_json) - .await?; + let etherscan_api_key = config.get_etherscan_api_key(Some(chain)); + + let to = match to { + Some(to) => Some(to.resolve(&provider).await?), + None => None, + }; + + access_list( + &provider, + etherscan_api_key.as_deref(), + sender, + to, + sig, + args, + data, + tx, + chain, + block, + to_json, + ) + .await?; Ok(()) } } #[allow(clippy::too_many_arguments)] -async fn access_list, T: Into>( - provider: M, - from: F, - to: Option, +async fn access_list, T: Transport + Clone>( + provider: P, + etherscan_api_key: Option<&str>, + from: Address, + to: Option

, sig: Option, args: Vec, data: Option, @@ -81,32 +103,35 @@ async fn access_list, T: Into, to_json: bool, -) -> Result<()> -where - M::Error: 'static, -{ - let mut builder = TxBuilder::new(&provider, from, to, chain, tx.legacy).await?; - builder - .gas(tx.gas_limit) - .gas_price(tx.gas_price) - .priority_gas_price(tx.priority_gas_price) - .nonce(tx.nonce); - - builder.value(tx.value); - - if let Some(sig) = sig { - builder.set_args(sig.as_str(), args).await?; +) -> Result<()> { + let mut req = WithOtherFields::::default() + .with_to(to.into()) + .with_from(from) + .with_value(tx.value.unwrap_or_default()) + .with_chain_id(chain.id()); + + if let Some(gas_limit) = tx.gas_limit { + req.set_gas_limit(gas_limit.to()); } - if let Some(data) = data { - // Note: `sig+args` and `data` are mutually exclusive - builder.set_data(hex::decode(data).wrap_err("Expected hex encoded function data")?); + + if let Some(nonce) = tx.nonce { + req.set_nonce(nonce.to()); } - let builder_output = builder.peek(); + let data = if let Some(sig) = sig { + parse_function_args(&sig, args, to, chain, &provider, etherscan_api_key).await?.0 + } else if let Some(data) = data { + // Note: `sig+args` and `data` are mutually exclusive + hex::decode(data)? + } else { + Vec::new() + }; + + req.set_input(data.into()); let cast = Cast::new(&provider); - let access_list: String = cast.access_list(builder_output, block, to_json).await?; + let access_list: String = cast.access_list(&req, block, to_json).await?; println!("{}", access_list); diff --git a/crates/cast/bin/cmd/call.rs b/crates/cast/bin/cmd/call.rs index ece090bf7..eeb09d5c0 100644 --- a/crates/cast/bin/cmd/call.rs +++ b/crates/cast/bin/cmd/call.rs @@ -1,23 +1,19 @@ +use alloy_network::TransactionBuilder; use alloy_primitives::U256; -use cast::{Cast, TxBuilder}; +use alloy_rpc_types::{BlockId, TransactionRequest, WithOtherFields}; +use cast::Cast; use clap::Parser; -use ethers_core::types::{BlockId, NameOrAddress}; -use eyre::{Result, WrapErr}; +use eyre::Result; use foundry_cli::{ opts::{EthereumOpts, TransactionOpts}, - utils::{self, handle_traces, parse_ether_value, TraceResult}, -}; -use foundry_common::{ - runtime_client::RuntimeClient, - types::{ToAlloy, ToEthers}, + utils::{self, handle_traces, parse_ether_value, parse_function_args, TraceResult}, }; +use foundry_common::ens::NameOrAddress; use foundry_compilers::EvmVersion; use foundry_config::{find_project_root_path, Config}; use foundry_evm::{executors::TracingExecutor, opts::EvmOpts}; use std::str::FromStr; -type Provider = ethers_providers::Provider; - /// CLI arguments for `cast call`. #[derive(Debug, Parser)] pub struct CallArgs { @@ -118,19 +114,43 @@ impl CallArgs { let provider = utils::get_provider(&config)?; let chain = utils::get_chain(config.chain, &provider).await?; let sender = eth.wallet.sender().await; + let etherscan_api_key = config.get_etherscan_api_key(Some(chain)); + + let to = match to { + Some(to) => Some(to.resolve(&provider).await?), + None => None, + }; - let mut builder: TxBuilder<'_, Provider> = - TxBuilder::new(&provider, sender.to_ethers(), to, chain, tx.legacy).await?; + let mut req = WithOtherFields::::default() + .with_to(to.into()) + .with_from(sender) + .with_value(tx.value.unwrap_or_default()); - builder - .gas(tx.gas_limit) - .etherscan_api_key(config.get_etherscan_api_key(Some(chain))) - .gas_price(tx.gas_price) - .priority_gas_price(tx.priority_gas_price) - .nonce(tx.nonce); + if let Some(nonce) = tx.nonce { + req.set_nonce(nonce.to()); + } - match command { + let (data, func) = match command { Some(CallSubcommands::Create { code, sig, args, value }) => { + if let Some(value) = value { + req.set_value(value); + } + + let mut data = hex::decode(code)?; + + if let Some(s) = sig { + let (mut constructor_args, _) = parse_function_args( + &s, + args, + None, + chain, + &provider, + etherscan_api_key.as_deref(), + ) + .await?; + data.append(&mut constructor_args); + } + if trace { let figment = Config::figment_with_root(find_project_root_path(None).unwrap()) .merge(eth.rpc); @@ -144,8 +164,8 @@ impl CallArgs { let trace = match executor.deploy( sender, - code.into_bytes().into(), - value.unwrap_or(U256::ZERO), + data.into(), + req.value.unwrap_or_default(), None, ) { Ok(deploy_result) => TraceResult::from(deploy_result), @@ -157,12 +177,26 @@ impl CallArgs { return Ok(()); } - // fill the builder after the conditional so we dont move values - fill_create(&mut builder, value, code, sig, args).await?; + (data, None) } _ => { // fill first here because we need to use the builder in the conditional - fill_tx(&mut builder, tx.value, sig, args, data).await?; + let (data, func) = if let Some(sig) = sig { + parse_function_args( + &sig, + args, + to, + chain, + &provider, + etherscan_api_key.as_deref(), + ) + .await? + } else if let Some(data) = data { + // Note: `sig+args` and `data` are mutually exclusive + (hex::decode(data)?, None) + } else { + (Vec::new(), None) + }; if trace { let figment = Config::figment_with_root(find_project_root_path(None).unwrap()) @@ -175,71 +209,28 @@ impl CallArgs { let mut executor = TracingExecutor::new(env, fork, evm_version, debug); - let (tx, _) = builder.build(); - let trace = TraceResult::from(executor.call_raw_committing( sender, - tx.to_addr().copied().expect("an address to be here").to_alloy(), - tx.data().cloned().unwrap_or_default().to_vec().into(), - tx.value().copied().unwrap_or_default().to_alloy(), + req.to.expect("an address to be here"), + data.into(), + req.value.unwrap_or_default(), )?); handle_traces(trace, &config, chain, labels, debug).await?; return Ok(()); } + + (data, func) } }; - let builder_output = builder.build(); - println!("{}", Cast::new(provider).call(builder_output, block).await?); - - Ok(()) - } -} - -/// fills the builder from create arg -async fn fill_create( - builder: &mut TxBuilder<'_, Provider>, - value: Option, - code: String, - sig: Option, - args: Vec, -) -> Result<()> { - builder.value(value); - - let mut data = hex::decode(code)?; - - if let Some(s) = sig { - let (mut sigdata, _func) = builder.create_args(&s, args).await?; - data.append(&mut sigdata); - } - - builder.set_data(data); - - Ok(()) -} - -/// fills the builder from args -async fn fill_tx( - builder: &mut TxBuilder<'_, Provider>, - value: Option, - sig: Option, - args: Vec, - data: Option, -) -> Result<()> { - builder.value(value); + req.set_input(data.into()); - if let Some(sig) = sig { - builder.set_args(sig.as_str(), args).await?; - } + println!("{}", Cast::new(provider).call(&req, func.as_ref(), block).await?); - if let Some(data) = data { - // Note: `sig+args` and `data` are mutually exclusive - builder.set_data(hex::decode(data).wrap_err("Expected hex encoded function data")?); + Ok(()) } - - Ok(()) } #[cfg(test)] diff --git a/crates/cast/bin/cmd/estimate.rs b/crates/cast/bin/cmd/estimate.rs index 56fdc40d3..55149937b 100644 --- a/crates/cast/bin/cmd/estimate.rs +++ b/crates/cast/bin/cmd/estimate.rs @@ -1,12 +1,14 @@ +use alloy_network::TransactionBuilder; use alloy_primitives::U256; -use cast::{Cast, TxBuilder}; +use alloy_provider::Provider; +use alloy_rpc_types::{TransactionRequest, WithOtherFields}; use clap::Parser; -use ethers_core::types::NameOrAddress; use eyre::Result; use foundry_cli::{ opts::{EtherscanOpts, RpcOpts}, - utils::{self, parse_ether_value}, + utils::{self, parse_ether_value, parse_function_args}, }; +use foundry_common::ens::NameOrAddress; use foundry_config::{figment::Figment, Config}; use std::str::FromStr; @@ -81,35 +83,47 @@ impl EstimateArgs { let figment = Figment::from(Config::figment()).merge(etherscan).merge(rpc); let config = Config::try_from(figment)?; - let provider = utils::get_provider(&config)?; let chain = utils::get_chain(config.chain, &provider).await?; let api_key = config.get_etherscan_api_key(Some(chain)); - let mut builder = TxBuilder::new(&provider, from, to, chain, false).await?; - builder.etherscan_api_key(api_key); + let from = from.resolve(&provider).await?; + let to = match to { + Some(to) => Some(to.resolve(&provider).await?), + None => None, + }; + + let mut req = WithOtherFields::::default() + .with_to(to.into()) + .with_from(from) + .with_value(value.unwrap_or_default()); - match command { + let data = match command { Some(EstimateSubcommands::Create { code, sig, args, value }) => { - builder.value(value); + if let Some(value) = value { + req.set_value(value); + } let mut data = hex::decode(code)?; if let Some(s) = sig { - let (mut sigdata, _func) = builder.create_args(&s, args).await?; - data.append(&mut sigdata); + let (mut constructor_args, _) = + parse_function_args(&s, args, to, chain, &provider, api_key.as_deref()) + .await?; + data.append(&mut constructor_args); } - builder.set_data(data); + data } _ => { let sig = sig.ok_or_else(|| eyre::eyre!("Function signature must be provided."))?; - builder.value(value).set_args(sig.as_str(), args).await?; + parse_function_args(&sig, args, to, chain, &provider, api_key.as_deref()).await?.0 } }; - let builder_output = builder.peek(); - let gas = Cast::new(&provider).estimate(builder_output).await?; + req.set_input(data.into()); + + let gas = provider.estimate_gas(&req, None).await?; println!("{gas}"); Ok(()) } diff --git a/crates/cast/bin/cmd/find_block.rs b/crates/cast/bin/cmd/find_block.rs index 5038ded7b..f75f2c82f 100644 --- a/crates/cast/bin/cmd/find_block.rs +++ b/crates/cast/bin/cmd/find_block.rs @@ -1,10 +1,8 @@ -use alloy_primitives::{U256, U64}; +use alloy_provider::Provider; use cast::Cast; use clap::Parser; -use ethers_providers::Middleware; use eyre::Result; use foundry_cli::{opts::RpcOpts, utils}; -use foundry_common::types::{ToAlloy, ToEthers}; use foundry_config::Config; use futures::join; @@ -22,7 +20,7 @@ impl FindBlockArgs { pub async fn run(self) -> Result<()> { let FindBlockArgs { timestamp, rpc } = self; - let ts_target = U256::from(timestamp); + let ts_target = timestamp; let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; @@ -30,43 +28,43 @@ impl FindBlockArgs { let cast_provider = Cast::new(provider); let res = join!(cast_provider.timestamp(last_block_num), cast_provider.timestamp(1)); - let ts_block_latest = res.0?; - let ts_block_1 = res.1?; + let ts_block_latest: u64 = res.0?.to(); + let ts_block_1: u64 = res.1?.to(); let block_num = if ts_block_latest < ts_target { // If the most recent block's timestamp is below the target, return it - last_block_num.to_alloy() + last_block_num } else if ts_block_1 > ts_target { // If the target timestamp is below block 1's timestamp, return that - U64::from(1_u64) + 1 } else { // Otherwise, find the block that is closest to the timestamp - let mut low_block = U64::from(1_u64); // block 0 has a timestamp of 0: https://github.com/ethereum/go-ethereum/issues/17042#issuecomment-559414137 - let mut high_block = last_block_num.to_alloy(); - let mut matching_block: Option = None; + let mut low_block = 1_u64; // block 0 has a timestamp of 0: https://github.com/ethereum/go-ethereum/issues/17042#issuecomment-559414137 + let mut high_block = last_block_num; + let mut matching_block = None; while high_block > low_block && matching_block.is_none() { // Get timestamp of middle block (this approach approach to avoids overflow) let high_minus_low_over_2 = high_block .checked_sub(low_block) .ok_or_else(|| eyre::eyre!("unexpected underflow")) .unwrap() - .checked_div(U64::from(2_u64)) + .checked_div(2_u64) .unwrap(); let mid_block = high_block.checked_sub(high_minus_low_over_2).unwrap(); - let ts_mid_block = cast_provider.timestamp(mid_block.to_ethers()).await?; + let ts_mid_block = cast_provider.timestamp(mid_block).await?.to::(); // Check if we've found a match or should keep searching if ts_mid_block == ts_target { matching_block = Some(mid_block) - } else if high_block.checked_sub(low_block).unwrap() == U64::from(1_u64) { + } else if high_block.checked_sub(low_block).unwrap() == 1_u64 { // The target timestamp is in between these blocks. This rounds to the // highest block if timestamp is equidistant between blocks let res = join!( - cast_provider.timestamp(high_block.to_ethers()), - cast_provider.timestamp(low_block.to_ethers()) + cast_provider.timestamp(high_block), + cast_provider.timestamp(low_block) ); - let ts_high = res.0.unwrap(); - let ts_low = res.1.unwrap(); + let ts_high: u64 = res.0.unwrap().to(); + let ts_low: u64 = res.1.unwrap().to(); let high_diff = ts_high.checked_sub(ts_target).unwrap(); let low_diff = ts_target.checked_sub(ts_low).unwrap(); let is_low = low_diff < high_diff; diff --git a/crates/cast/bin/cmd/logs.rs b/crates/cast/bin/cmd/logs.rs index e7816afa3..e97521fe9 100644 --- a/crates/cast/bin/cmd/logs.rs +++ b/crates/cast/bin/cmd/logs.rs @@ -1,18 +1,14 @@ +use alloy_dyn_abi::{DynSolType, DynSolValue, Specifier}; +use alloy_json_abi::Event; +use alloy_primitives::{Address, B256}; +use alloy_rpc_types::{BlockId, BlockNumberOrTag, Filter, FilterBlockOption, FilterSet, Topic}; use cast::Cast; use clap::Parser; -use ethers_core::{ - abi::{ - token::{LenientTokenizer, StrictTokenizer, Tokenizer}, - Address, Event, HumanReadableParser, ParamType, RawTopicFilter, Token, Topic, TopicFilter, - }, - types::{ - BlockId, BlockNumber, Filter, FilterBlockOption, NameOrAddress, ValueOrArray, H256, U256, - }, -}; -use ethers_providers::Middleware; -use eyre::{Result, WrapErr}; +use eyre::Result; use foundry_cli::{opts::EthereumOpts, utils}; +use foundry_common::ens::NameOrAddress; use foundry_config::Config; +use hex::FromHex; use itertools::Itertools; use std::{io, str::FromStr}; @@ -80,13 +76,7 @@ impl LogsArgs { let cast = Cast::new(&provider); let address = match address { - Some(address) => { - let address = match address { - NameOrAddress::Name(name) => provider.resolve_name(&name).await?, - NameOrAddress::Address(address) => address, - }; - Some(address) - } + Some(address) => Some(address.resolve(&provider).await?), None => None, }; @@ -114,47 +104,36 @@ impl LogsArgs { /// successful, `topics_or_args` is parsed as indexed inputs and converted to topics. Otherwise, /// `sig_or_topic` is prepended to `topics_or_args` and used as raw topics. fn build_filter( - from_block: Option, - to_block: Option, + from_block: Option, + to_block: Option, address: Option
, sig_or_topic: Option, topics_or_args: Vec, ) -> Result { let block_option = FilterBlockOption::Range { from_block, to_block }; - let topic_filter = match sig_or_topic { + let filter = match sig_or_topic { // Try and parse the signature as an event signature - Some(sig_or_topic) => match HumanReadableParser::parse_event(sig_or_topic.as_str()) { + Some(sig_or_topic) => match foundry_common::abi::get_event(sig_or_topic.as_str()) { Ok(event) => build_filter_event_sig(event, topics_or_args)?, Err(_) => { let topics = [vec![sig_or_topic], topics_or_args].concat(); build_filter_topics(topics)? } }, - None => TopicFilter::default(), + None => Filter::default(), }; - // Convert from TopicFilter to Filter - let topics = - vec![topic_filter.topic0, topic_filter.topic1, topic_filter.topic2, topic_filter.topic3] - .into_iter() - .map(|topic| match topic { - Topic::Any => None, - Topic::This(topic) => Some(ValueOrArray::Value(Some(topic))), - _ => unreachable!(), - }) - .collect::>(); - - let filter = Filter { - block_option, - address: address.map(ValueOrArray::Value), - topics: [topics[0].clone(), topics[1].clone(), topics[2].clone(), topics[3].clone()], - }; + let mut filter = filter.select(block_option); + + if let Some(address) = address { + filter = filter.address(address) + } Ok(filter) } -/// Creates a TopicFilter from the given event signature and arguments. -fn build_filter_event_sig(event: Event, args: Vec) -> Result { +/// Creates a [Filter] from the given event signature and arguments. +fn build_filter_event_sig(event: Event, args: Vec) -> Result { let args = args.iter().map(|arg| arg.as_str()).collect::>(); // Match the args to indexed inputs. Enumerate so that the ordering can be restored @@ -164,128 +143,75 @@ fn build_filter_event_sig(event: Event, args: Vec) -> Result>>()? + .into_iter() .enumerate() .partition(|(_, (_, arg))| !arg.is_empty()); // Only parse the inputs with arguments - let indexed_tokens = parse_params(with_args.iter().map(|(_, p)| *p), true)?; + let indexed_tokens = with_args + .iter() + .map(|(_, (kind, arg))| kind.coerce_str(arg)) + .collect::, _>>()?; // Merge the inputs restoring the original ordering - let mut tokens = with_args + let mut topics = with_args .into_iter() .zip(indexed_tokens) .map(|((i, _), t)| (i, Some(t))) .chain(without_args.into_iter().map(|(i, _)| (i, None))) .sorted_by(|(i1, _), (i2, _)| i1.cmp(i2)) - .map(|(_, token)| token) - .collect::>(); + .map(|(_, token)| { + token + .map(|token| Topic::from(B256::from_slice(token.abi_encode().as_slice()))) + .unwrap_or(Topic::default()) + }) + .collect::>(); - tokens.resize(3, None); + topics.resize(3, Topic::default()); - let raw = RawTopicFilter { - topic0: tokens[0].clone().map_or(Topic::Any, Topic::This), - topic1: tokens[1].clone().map_or(Topic::Any, Topic::This), - topic2: tokens[2].clone().map_or(Topic::Any, Topic::This), - }; + let filter = Filter::new() + .event_signature(event.selector()) + .topic1(topics[0].clone()) + .topic2(topics[1].clone()) + .topic3(topics[2].clone()); - // Let filter do the hardwork of converting arguments to topics - Ok(event.filter(raw)?) + Ok(filter) } -/// Creates a TopicFilter from raw topic hashes. -fn build_filter_topics(topics: Vec) -> Result { +/// Creates a [Filter] from raw topic hashes. +fn build_filter_topics(topics: Vec) -> Result { let mut topics = topics .into_iter() - .map(|topic| if topic.is_empty() { Ok(None) } else { H256::from_str(&topic).map(Some) }) - .collect::, _>>()?; - - topics.resize(4, None); - - Ok(TopicFilter { - topic0: topics[0].map_or(Topic::Any, Topic::This), - topic1: topics[1].map_or(Topic::Any, Topic::This), - topic2: topics[2].map_or(Topic::Any, Topic::This), - topic3: topics[3].map_or(Topic::Any, Topic::This), - }) -} - -fn parse_params<'a, I: IntoIterator>( - params: I, - lenient: bool, -) -> eyre::Result> { - let mut tokens = Vec::new(); - - for (param, value) in params { - let mut token = if lenient { - LenientTokenizer::tokenize(param, value) - } else { - StrictTokenizer::tokenize(param, value) - }; - if token.is_err() && value.starts_with("0x") { - match param { - ParamType::FixedBytes(32) => { - if value.len() < 66 { - let padded_value = [value, &"0".repeat(66 - value.len())].concat(); - token = if lenient { - LenientTokenizer::tokenize(param, &padded_value) - } else { - StrictTokenizer::tokenize(param, &padded_value) - }; - } - } - ParamType::Uint(_) => { - // try again if value is hex - if let Ok(value) = U256::from_str(value).map(|v| v.to_string()) { - token = if lenient { - LenientTokenizer::tokenize(param, &value) - } else { - StrictTokenizer::tokenize(param, &value) - }; - } - } - // TODO: Not sure what to do here. Put the no effect in for now, but that is not - // ideal. We could attempt massage for every value type? - _ => {} + .map(|topic| { + if topic.is_empty() { + Ok(Topic::default()) + } else { + Ok(Topic::from(B256::from_hex(topic.as_str())?)) } - } + }) + .collect::>>>()?; - let token = token.map(sanitize_token).wrap_err_with(|| { - format!("Failed to parse `{value}`, expected value of type: {param}") - })?; - tokens.push(token); - } - Ok(tokens) -} + topics.resize(4, Topic::default()); -pub fn sanitize_token(token: Token) -> Token { - match token { - Token::Array(tokens) => { - let mut sanitized = Vec::with_capacity(tokens.len()); - for token in tokens { - let token = match token { - Token::String(val) => { - let val = match val.as_str() { - // this is supposed to be an empty string - "\"\"" | "''" => String::new(), - _ => val, - }; - Token::String(val) - } - _ => sanitize_token(token), - }; - sanitized.push(token) - } - Token::Array(sanitized) - } - _ => token, - } + let filter = Filter::new() + .event_signature(topics[0].clone()) + .topic1(topics[1].clone()) + .topic2(topics[2].clone()) + .topic3(topics[3].clone()); + + Ok(filter) } #[cfg(test)] mod tests { use super::*; - use ethers_core::types::H160; + use alloy_primitives::{U160, U256 as rU256}; + use alloy_rpc_types::ValueOrArray; const ADDRESS: &str = "0x4D1A2e2bB4F88F0250f26Ffff098B0b30B26BF38"; const TRANSFER_SIG: &str = "Transfer(address indexed,address indexed,uint256)"; @@ -294,13 +220,13 @@ mod tests { #[test] fn test_build_filter_basic() { - let from_block = Some(BlockNumber::from(1337)); - let to_block = Some(BlockNumber::Latest); + let from_block = Some(BlockNumberOrTag::from(1337)); + let to_block = Some(BlockNumberOrTag::Latest); let address = Address::from_str(ADDRESS).ok(); let expected = Filter { block_option: FilterBlockOption::Range { from_block, to_block }, - address: Some(ValueOrArray::Value(address.unwrap())), - topics: [None, None, None, None], + address: ValueOrArray::Value(address.unwrap()).into(), + topics: [vec![].into(), vec![].into(), vec![].into(), vec![].into()], }; let filter = build_filter(from_block, to_block, address, None, vec![]).unwrap(); assert_eq!(filter, expected) @@ -310,8 +236,13 @@ mod tests { fn test_build_filter_sig() { let expected = Filter { block_option: FilterBlockOption::Range { from_block: None, to_block: None }, - address: None, - topics: [Some(H256::from_str(TRANSFER_TOPIC).unwrap().into()), None, None, None], + address: vec![].into(), + topics: [ + B256::from_str(TRANSFER_TOPIC).unwrap().into(), + vec![].into(), + vec![].into(), + vec![].into(), + ], }; let filter = build_filter(None, None, None, Some(TRANSFER_SIG.to_string()), vec![]).unwrap(); @@ -322,8 +253,13 @@ mod tests { fn test_build_filter_mismatch() { let expected = Filter { block_option: FilterBlockOption::Range { from_block: None, to_block: None }, - address: None, - topics: [Some(H256::from_str(TRANSFER_TOPIC).unwrap().into()), None, None, None], + address: vec![].into(), + topics: [ + B256::from_str(TRANSFER_TOPIC).unwrap().into(), + vec![].into(), + vec![].into(), + vec![].into(), + ], }; let filter = build_filter( None, @@ -338,14 +274,16 @@ mod tests { #[test] fn test_build_filter_sig_with_arguments() { + let addr = Address::from_str(ADDRESS).unwrap(); + let addr = rU256::from(U160::from_be_bytes(addr.0 .0)); let expected = Filter { block_option: FilterBlockOption::Range { from_block: None, to_block: None }, - address: None, + address: vec![].into(), topics: [ - Some(H256::from_str(TRANSFER_TOPIC).unwrap().into()), - Some(H160::from_str(ADDRESS).unwrap().into()), - None, - None, + B256::from_str(TRANSFER_TOPIC).unwrap().into(), + addr.into(), + vec![].into(), + vec![].into(), ], }; let filter = build_filter( @@ -361,14 +299,16 @@ mod tests { #[test] fn test_build_filter_sig_with_skipped_arguments() { + let addr = Address::from_str(ADDRESS).unwrap(); + let addr = rU256::from(U160::from_be_bytes(addr.0 .0)); let expected = Filter { block_option: FilterBlockOption::Range { from_block: None, to_block: None }, - address: None, + address: vec![].into(), topics: [ - Some(H256::from_str(TRANSFER_TOPIC).unwrap().into()), - None, - Some(H160::from_str(ADDRESS).unwrap().into()), - None, + vec![B256::from_str(TRANSFER_TOPIC).unwrap()].into(), + vec![].into(), + addr.into(), + vec![].into(), ], }; let filter = build_filter( @@ -386,12 +326,12 @@ mod tests { fn test_build_filter_with_topics() { let expected = Filter { block_option: FilterBlockOption::Range { from_block: None, to_block: None }, - address: None, + address: vec![].into(), topics: [ - Some(H256::from_str(TRANSFER_TOPIC).unwrap().into()), - Some(H256::from_str(TRANSFER_TOPIC).unwrap().into()), - None, - None, + vec![B256::from_str(TRANSFER_TOPIC).unwrap()].into(), + vec![B256::from_str(TRANSFER_TOPIC).unwrap()].into(), + vec![].into(), + vec![].into(), ], }; let filter = build_filter( @@ -410,12 +350,12 @@ mod tests { fn test_build_filter_with_skipped_topic() { let expected = Filter { block_option: FilterBlockOption::Range { from_block: None, to_block: None }, - address: None, + address: vec![].into(), topics: [ - Some(H256::from_str(TRANSFER_TOPIC).unwrap().into()), - None, - Some(H256::from_str(TRANSFER_TOPIC).unwrap().into()), - None, + vec![B256::from_str(TRANSFER_TOPIC).unwrap()].into(), + vec![].into(), + vec![B256::from_str(TRANSFER_TOPIC).unwrap()].into(), + vec![].into(), ], }; let filter = build_filter( @@ -443,7 +383,7 @@ mod tests { .unwrap() .to_string(); - assert_eq!(err, "Failed to parse `1234`, expected value of type: address"); + assert_eq!(err, "parser error:\n1234\n^\nInvalid string length"); } #[test] @@ -453,7 +393,7 @@ mod tests { .unwrap() .to_string(); - assert_eq!(err, "Invalid character 's' at position 1"); + assert_eq!(err, "Odd number of digits"); } #[test] @@ -463,7 +403,7 @@ mod tests { .unwrap() .to_string(); - assert_eq!(err, "Invalid input length"); + assert_eq!(err, "Invalid string length"); } #[test] @@ -479,6 +419,6 @@ mod tests { .unwrap() .to_string(); - assert_eq!(err, "Invalid input length"); + assert_eq!(err, "Invalid string length"); } } diff --git a/crates/cast/bin/cmd/mktx.rs b/crates/cast/bin/cmd/mktx.rs index dc7f78461..def6d46f4 100644 --- a/crates/cast/bin/cmd/mktx.rs +++ b/crates/cast/bin/cmd/mktx.rs @@ -1,15 +1,15 @@ use crate::tx; +use alloy_network::{eip2718::Encodable2718, EthereumSigner, TransactionBuilder}; +use alloy_primitives::U64; +use alloy_provider::Provider; +use alloy_signer::Signer; use clap::Parser; -use ethers_core::types::NameOrAddress; -use ethers_middleware::MiddlewareBuilder; -use ethers_providers::Middleware; -use ethers_signers::Signer; use eyre::Result; use foundry_cli::{ opts::{EthereumOpts, TransactionOpts}, - utils, + utils::{self, get_provider}, }; -use foundry_common::types::ToAlloy; +use foundry_common::ens::NameOrAddress; use foundry_config::Config; use std::str::FromStr; @@ -45,7 +45,7 @@ pub struct MakeTxArgs { #[derive(Debug, Parser)] pub enum MakeTxSubcommands { /// Use to deploy raw contract bytecode. - #[clap(name = "--create")] + #[command(name = "--create")] Create { /// The initialization bytecode of the contract to deploy. code: String, @@ -86,23 +86,21 @@ impl MakeTxArgs { let signer = eth.wallet.signer().await?; let from = signer.address(); - tx::validate_from_address(eth.wallet.from, from.to_alloy())?; + tx::validate_from_address(eth.wallet.from, from)?; if resend { - tx.nonce = Some(provider.get_transaction_count(from, None).await?.to_alloy()); + tx.nonce = Some(U64::from(provider.get_transaction_count(from, None).await?)); } - let provider = provider.with_signer(signer); + let provider = get_provider(&config)?; - let (mut tx, _) = + let (tx, _) = tx::build_tx(&provider, from, to, code, sig, args, tx, chain, api_key).await?; - // Fill nonce, gas limit, gas price, and max priority fee per gas if needed - provider.fill_transaction(&mut tx, None).await?; + let tx = tx.build(&EthereumSigner::new(signer)).await?; - let signature = provider.sign_transaction(&tx, from).await?; - let signed_tx = tx.rlp_signed(&signature); - println!("{signed_tx}"); + let signed_tx = hex::encode(tx.encoded_2718()); + println!("0x{signed_tx}"); Ok(()) } diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index 3fc09565c..33c26e2a9 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -1,5 +1,5 @@ use alloy_primitives::U256; -use alloy_providers::tmp::TempProvider; +use alloy_provider::Provider; use alloy_rpc_types::BlockTransactions; use cast::revm::primitives::EnvWithHandlerCfg; use clap::Parser; @@ -109,19 +109,15 @@ impl RunArgs { .wrap_err_with(|| format!("tx not found: {:?}", tx_hash))?; // check if the tx is a system transaction - if is_known_system_sender(tx.from) || - tx.transaction_type.map(|ty| ty.to::()) == 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 )); } - let tx_block_number = tx - .block_number - .ok_or_else(|| eyre::eyre!("tx may still be pending: {:?}", tx_hash))? - .to::(); + let tx_block_number = + tx.block_number.ok_or_else(|| eyre::eyre!("tx may still be pending: {:?}", tx_hash))?; // fetch the block the transaction was mined in let block = provider.get_block(tx_block_number.into(), true).await?; @@ -136,12 +132,12 @@ impl RunArgs { env.block.number = U256::from(tx_block_number); if let Some(block) = &block { - env.block.timestamp = block.header.timestamp; + env.block.timestamp = U256::from(block.header.timestamp); env.block.coinbase = block.header.miner; env.block.difficulty = block.header.difficulty; env.block.prevrandao = Some(block.header.mix_hash.unwrap_or_default()); - env.block.basefee = block.header.base_fee_per_gas.unwrap_or_default(); - env.block.gas_limit = block.header.gas_limit; + env.block.basefee = U256::from(block.header.base_fee_per_gas.unwrap_or_default()); + env.block.gas_limit = U256::from(block.header.gas_limit); // TODO: we need a smarter way to map the block to the corresponding evm_version for // commonly used chains @@ -174,8 +170,7 @@ impl RunArgs { // we skip them otherwise this would cause // reverts if is_known_system_sender(tx.from) || - tx.transaction_type.map(|ty| ty.to::()) == - Some(SYSTEM_TRANSACTION_TYPE) + tx.transaction_type == Some(SYSTEM_TRANSACTION_TYPE) { update_progress!(pb, index); continue; diff --git a/crates/cast/bin/cmd/send.rs b/crates/cast/bin/cmd/send.rs index 092d759eb..b24de5fba 100644 --- a/crates/cast/bin/cmd/send.rs +++ b/crates/cast/bin/cmd/send.rs @@ -1,19 +1,17 @@ use crate::tx; +use alloy_network::{AnyNetwork, EthereumSigner}; +use alloy_primitives::{Address, U64}; +use alloy_provider::{Provider, ProviderBuilder}; +use alloy_signer::Signer; +use alloy_transport::Transport; use cast::Cast; use clap::Parser; -use ethers_core::types::NameOrAddress; -use ethers_middleware::SignerMiddleware; -use ethers_providers::Middleware; -use ethers_signers::Signer; use eyre::Result; use foundry_cli::{ opts::{EthereumOpts, TransactionOpts}, utils, }; -use foundry_common::{ - cli_warn, - types::{ToAlloy, ToEthers}, -}; +use foundry_common::{cli_warn, ens::NameOrAddress}; use foundry_config::{Chain, Config}; use std::str::FromStr; @@ -38,7 +36,7 @@ pub struct SendTxArgs { /// The number of confirmations until the receipt is fetched. #[arg(long, default_value = "1")] - confirmations: usize, + confirmations: u64, /// Print the transaction receipt as JSON. #[arg(long, short, help_heading = "Display options")] @@ -114,6 +112,11 @@ impl SendTxArgs { let chain = utils::get_chain(config.chain, &provider).await?; let api_key = config.get_etherscan_api_key(Some(chain)); + let to = match to { + Some(to) => Some(to.resolve(&provider).await?), + None => None, + }; + // Case 1: // Default to sending via eth_sendTransaction if the --unlocked flag is passed. // This should be the only way this RPC method is used as it requires a local node @@ -121,15 +124,15 @@ impl SendTxArgs { if unlocked { // only check current chain id if it was specified in the config if let Some(config_chain) = config.chain { - let current_chain_id = provider.get_chainid().await?.as_u64(); + let current_chain_id = provider.get_chain_id().await?; let config_chain_id = config_chain.id(); // switch chain if current chain id is not the same as the one specified in the // config if config_chain_id != current_chain_id { cli_warn!("Switching to chain {}", config_chain); provider - .request( - "wallet_switchEthereumChain", + .raw_request( + "wallet_switchEthereumChain".into(), [serde_json::json!({ "chainId": format!("0x{:x}", config_chain_id), })], @@ -139,17 +142,13 @@ impl SendTxArgs { } if resend { - tx.nonce = Some( - provider - .get_transaction_count(config.sender.to_ethers(), None) - .await? - .to_alloy(), - ); + tx.nonce = + Some(U64::from(provider.get_transaction_count(config.sender, None).await?)); } cast_send( provider, - config.sender.to_ethers(), + config.sender, to, code, sig, @@ -171,13 +170,15 @@ impl SendTxArgs { let signer = eth.wallet.signer().await?; let from = signer.address(); - tx::validate_from_address(eth.wallet.from, from.to_alloy())?; + tx::validate_from_address(eth.wallet.from, from)?; if resend { - tx.nonce = Some(provider.get_transaction_count(from, None).await?.to_alloy()); + tx.nonce = Some(U64::from(provider.get_transaction_count(from, None).await?)); } - let provider = SignerMiddleware::new_with_provider_chain(provider, signer).await?; + let signer = EthereumSigner::from(signer); + let provider = + ProviderBuilder::<_, _, AnyNetwork>::default().signer(signer).on_provider(provider); cast_send( provider, @@ -199,10 +200,10 @@ impl SendTxArgs { } #[allow(clippy::too_many_arguments)] -async fn cast_send, T: Into>( - provider: M, - from: F, - to: Option, +async fn cast_send, T: Transport + Clone>( + provider: P, + from: Address, + to: Option
, code: Option, sig: Option, args: Vec, @@ -210,19 +211,16 @@ async fn cast_send, T: Into chain: Chain, etherscan_api_key: Option, cast_async: bool, - confs: usize, + confs: u64, to_json: bool, -) -> Result<()> -where - M::Error: 'static, -{ - let builder_output = +) -> Result<()> { + let (tx, _) = tx::build_tx(&provider, from, to, code, sig, args, tx, chain, etherscan_api_key).await?; let cast = Cast::new(provider); - let pending_tx = cast.send(builder_output).await?; - let tx_hash = *pending_tx; + let pending_tx = cast.send(tx).await?; + let tx_hash = pending_tx.inner().tx_hash(); if cast_async { println!("{tx_hash:#x}"); diff --git a/crates/cast/bin/cmd/storage.rs b/crates/cast/bin/cmd/storage.rs index 397bd07e2..0bf041b8a 100644 --- a/crates/cast/bin/cmd/storage.rs +++ b/crates/cast/bin/cmd/storage.rs @@ -1,10 +1,12 @@ use crate::opts::parse_slot; -use alloy_primitives::{B256, U256}; +use alloy_network::AnyNetwork; +use alloy_primitives::{Address, B256, U256}; +use alloy_provider::Provider; +use alloy_rpc_types::BlockId; +use alloy_transport::Transport; use cast::Cast; use clap::Parser; use comfy_table::{presets::ASCII_MARKDOWN, Table}; -use ethers_core::types::{BlockId, NameOrAddress}; -use ethers_providers::Middleware; use eyre::Result; use foundry_block_explorers::Client; use foundry_cli::{ @@ -14,8 +16,7 @@ use foundry_cli::{ use foundry_common::{ abi::find_source, compile::{etherscan_project, ProjectCompiler}, - provider::ethers::RetryProvider, - types::{ToAlloy, ToEthers}, + ens::NameOrAddress, }; use foundry_compilers::{ artifacts::StorageLayout, Artifact, ConfigurableContractArtifact, Project, Solc, @@ -80,19 +81,19 @@ impl StorageArgs { let config = Config::from(&self); let Self { address, slot, block, build, .. } = self; - let provider = utils::get_provider(&config)?; + let address = address.resolve(&provider).await?; // Slot was provided, perform a simple RPC call if let Some(slot) = slot { let cast = Cast::new(provider); - println!("{}", cast.storage(address, slot.to_ethers(), block).await?); + println!("{}", cast.storage(address, slot, block).await?); return Ok(()); } // No slot was provided // Get deployed bytecode at given address - let address_code = provider.get_code(address.clone(), block).await?.to_alloy(); + let address_code = provider.get_code_at(address, block.unwrap_or_default()).await?; if address_code.is_empty() { eyre::bail!("Provided address has no deployed code and thus no storage"); } @@ -107,8 +108,7 @@ impl StorageArgs { artifact.get_deployed_bytecode_bytes().is_some_and(|b| *b == address_code) }); if let Some((_, artifact)) = artifact { - return fetch_and_print_storage(provider, address.clone(), block, artifact, true) - .await; + return fetch_and_print_storage(provider, address, block, artifact, true).await; } } @@ -123,11 +123,7 @@ impl StorageArgs { let chain = utils::get_chain(config.chain, &provider).await?; let api_key = config.get_etherscan_api_key(Some(chain)).unwrap_or_default(); let client = Client::new(chain, api_key)?; - let addr = address - .as_address() - .ok_or_else(|| eyre::eyre!("Could not resolve address"))? - .to_alloy(); - let source = find_source(client, addr).await?; + let source = find_source(client, address).await?; let metadata = source.items.first().unwrap(); if metadata.is_vyper() { eyre::bail!("Contract at provided address is not a valid Solidity contract") @@ -209,9 +205,9 @@ impl StorageValue { } } -async fn fetch_and_print_storage( - provider: RetryProvider, - address: NameOrAddress, +async fn fetch_and_print_storage, T: Transport + Clone>( + provider: P, + address: Address, block: Option, artifact: &ConfigurableContractArtifact, pretty: bool, @@ -226,18 +222,17 @@ async fn fetch_and_print_storage( } } -async fn fetch_storage_slots( - provider: RetryProvider, - address: NameOrAddress, +async fn fetch_storage_slots, T: Transport + Clone>( + provider: P, + address: Address, block: Option, layout: &StorageLayout, ) -> Result> { let requests = layout.storage.iter().map(|storage_slot| async { let slot = B256::from(U256::from_str(&storage_slot.slot)?); - let raw_slot_value = - provider.get_storage_at(address.clone(), slot.to_ethers(), block).await?.to_alloy(); + let raw_slot_value = provider.get_storage_at(address, slot.into(), block).await?; - let value = StorageValue { slot, raw_slot_value }; + let value = StorageValue { slot, raw_slot_value: raw_slot_value.into() }; Ok(value) }); diff --git a/crates/cast/bin/cmd/wallet/list.rs b/crates/cast/bin/cmd/wallet/list.rs index 2790366a3..d4ca3f013 100644 --- a/crates/cast/bin/cmd/wallet/list.rs +++ b/crates/cast/bin/cmd/wallet/list.rs @@ -1,7 +1,7 @@ use clap::Parser; use eyre::Result; -use foundry_common::{fs, types::ToAlloy}; +use foundry_common::fs; use foundry_config::Config; use foundry_wallets::multi_wallet::MultiWalletOptsBuilder; @@ -61,7 +61,7 @@ impl ListArgs { .available_senders(self.max_senders.unwrap()) .await? .iter() - .for_each(|sender| println!("{} ({})", sender.to_alloy(), $label)); + .for_each(|sender| println!("{} ({})", sender, $label)); } } Err(e) => { diff --git a/crates/cast/bin/cmd/wallet/mod.rs b/crates/cast/bin/cmd/wallet/mod.rs index 50467421c..60e21a2fb 100644 --- a/crates/cast/bin/cmd/wallet/mod.rs +++ b/crates/cast/bin/cmd/wallet/mod.rs @@ -1,13 +1,13 @@ +use alloy_dyn_abi::TypedData; use alloy_primitives::{Address, Signature}; -use alloy_signer::{ +use alloy_signer::Signer; +use alloy_signer_wallet::{ coins_bip39::{English, Mnemonic}, - LocalWallet, MnemonicBuilder, Signer as AlloySigner, + LocalWallet, MnemonicBuilder, }; use clap::Parser; -use ethers_core::types::transaction::eip712::TypedData; -use ethers_signers::Signer; use eyre::{Context, Result}; -use foundry_common::{fs, types::ToAlloy}; +use foundry_common::fs; use foundry_config::Config; use foundry_wallets::{RawWalletOpts, WalletOpts, WalletSigner}; use rand::thread_rng; @@ -258,7 +258,7 @@ impl WalletSubcommands { .signer() .await?; let addr = wallet.address(); - println!("{}", addr.to_alloy().to_checksum(None)); + println!("{}", addr.to_checksum(None)); } WalletSubcommands::Sign { message, data, from_file, no_hash, wallet } => { let wallet = wallet.signer().await?; @@ -270,13 +270,13 @@ impl WalletSubcommands { // data is a json string serde_json::from_str(&message)? }; - wallet.sign_typed_data(&typed_data).await? + wallet.sign_dynamic_typed_data(&typed_data).await? } else if no_hash { - wallet.sign_hash(&message.parse()?).await? + wallet.sign_hash(&hex::decode(&message)?[..].try_into()?).await? } else { - wallet.sign_message(Self::hex_str_to_bytes(&message)?).await? + wallet.sign_message(&Self::hex_str_to_bytes(&message)?).await? }; - println!("0x{sig}"); + println!("0x{}", hex::encode(sig.as_bytes())); } WalletSubcommands::Verify { message, signature, address } => { let recovered_address = Self::recover_address_from_message(&message, &signature)?; @@ -335,11 +335,11 @@ flag to set your key via: }; let mut rng = thread_rng(); - eth_keystore::encrypt_key( - &dir, + let (wallet, _) = LocalWallet::encrypt_keystore( + dir, &mut rng, private_key, - &password, + password, Some(&account_name), )?; let address = wallet.address(); diff --git a/crates/cast/bin/cmd/wallet/vanity.rs b/crates/cast/bin/cmd/wallet/vanity.rs index c3485ddfa..28ada95b1 100644 --- a/crates/cast/bin/cmd/wallet/vanity.rs +++ b/crates/cast/bin/cmd/wallet/vanity.rs @@ -1,5 +1,6 @@ use alloy_primitives::Address; -use alloy_signer::{k256::ecdsa::SigningKey, utils::secret_key_to_address, LocalWallet, Signer}; +use alloy_signer::{k256::ecdsa::SigningKey, utils::secret_key_to_address}; +use alloy_signer_wallet::LocalWallet; use clap::{builder::TypedValueParser, Parser}; use eyre::Result; use rayon::iter::{self, ParallelIterator}; diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index a81c1acd8..8c0181367 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -2,24 +2,23 @@ extern crate tracing; use alloy_primitives::{keccak256, Address, B256}; -use cast::{Cast, SimpleCast, TxBuilder}; +use alloy_provider::Provider; +use alloy_rpc_types::{BlockId, BlockNumberOrTag::Latest}; +use cast::{Cast, SimpleCast}; use clap::{CommandFactory, Parser}; use clap_complete::generate; -use ethers_core::types::{BlockId, BlockNumber::Latest, NameOrAddress}; -use ethers_providers::{Middleware, Provider}; use eyre::Result; use foundry_cli::{handler, prompt, stdin, utils}; use foundry_common::{ abi::get_event, - fmt::format_tokens, + ens::ProviderEnsExt, + fmt::{format_tokens, format_uint_exp}, fs, - runtime_client::RuntimeClient, selectors::{ decode_calldata, decode_event_topic, decode_function_selector, decode_selectors, import_selectors, parse_signatures, pretty_calldata, ParsedSignatures, SelectorImportData, SelectorType, }, - types::{ToAlloy, ToEthers}, }; use foundry_config::Config; use std::time::Instant; @@ -214,35 +213,16 @@ async fn main() -> Result<()> { CastSubcommand::Balance { block, who, ether, rpc, erc20 } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; + let account_addr = who.resolve(&provider).await?; match erc20 { Some(token) => { - let chain = utils::get_chain(config.chain, &provider).await?; - let mut builder: TxBuilder<'_, Provider> = TxBuilder::new( - &provider, - NameOrAddress::Address(Address::ZERO.to_ethers()), - Some(NameOrAddress::Address(token.to_ethers())), - chain, - true, - ) - .await?; - - let account_addr = match who { - NameOrAddress::Name(ens_name) => provider.resolve_name(&ens_name).await?, - NameOrAddress::Address(addr) => addr, - }; - - builder - .set_args( - "balanceOf(address) returns (uint256)", - vec![format!("{account_addr:#x}")], - ) - .await?; - let builder_output = builder.build(); - println!("{}", Cast::new(provider).call(builder_output, block).await?); + let balance = + Cast::new(&provider).erc20_balance(token, account_addr, block).await?; + println!("{}", format_uint_exp(balance)); } None => { - let value = Cast::new(provider).balance(who, block).await?; + let value = Cast::new(&provider).balance(account_addr, block).await?; if ether { println!("{}", SimpleCast::from_wei(&value.to_string(), "eth")?); } else { @@ -287,16 +267,18 @@ async fn main() -> Result<()> { CastSubcommand::Client { rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; - println!("{}", provider.client_version().await?); + println!("{}", provider.get_client_version().await?); } CastSubcommand::Code { block, who, disassemble, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; + let who = who.resolve(&provider).await?; println!("{}", Cast::new(provider).code(who, block, disassemble).await?); } CastSubcommand::Codesize { block, who, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; + let who = who.resolve(&provider).await?; println!("{}", Cast::new(provider).codesize(who, block).await?); } CastSubcommand::ComputeAddress { address, nonce, rpc } => { @@ -304,7 +286,7 @@ async fn main() -> Result<()> { let provider = utils::get_provider(&config)?; let address: Address = stdin::unwrap_line(address)?.parse()?; - let computed = Cast::new(&provider).compute_address(address, nonce).await?; + let computed = Cast::new(provider).compute_address(address, nonce).await?; println!("Computed Address: {}", computed.to_checksum(None)); } CastSubcommand::Disassemble { bytecode } => { @@ -345,24 +327,26 @@ async fn main() -> Result<()> { CastSubcommand::Implementation { block, who, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; + let who = who.resolve(&provider).await?; println!("{}", Cast::new(provider).implementation(who, block).await?); } CastSubcommand::Admin { block, who, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; + let who = who.resolve(&provider).await?; println!("{}", Cast::new(provider).admin(who, block).await?); } CastSubcommand::Nonce { block, who, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; + let who = who.resolve(&provider).await?; println!("{}", Cast::new(provider).nonce(who, block).await?); } CastSubcommand::Proof { address, slots, rpc, block } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; - let value = provider - .get_proof(address, slots.into_iter().map(|s| s.to_ethers()).collect(), block) - .await?; + let address = address.resolve(&provider).await?; + let value = provider.get_proof(address, slots.into_iter().collect(), block).await?; println!("{}", serde_json::to_string(&value)?); } CastSubcommand::Rpc(cmd) => cmd.run().await?, @@ -377,13 +361,12 @@ async fn main() -> Result<()> { let provider = utils::get_provider(&config)?; let cast = Cast::new(&provider); let pending_tx = cast.publish(raw_tx).await?; - let tx_hash = *pending_tx; + let tx_hash = pending_tx.inner().tx_hash(); if cast_async { println!("{tx_hash:#x}"); } else { - let receipt = - pending_tx.await?.ok_or_else(|| eyre::eyre!("tx {tx_hash} not found"))?; + let receipt = pending_tx.get_receipt().await?; println!("{}", serde_json::json!(receipt)); } } @@ -470,9 +453,9 @@ async fn main() -> Result<()> { let provider = utils::get_provider(&config)?; let who = stdin::unwrap_line(who)?; - let name = provider.lookup_address(who.to_ethers()).await?; + let name = provider.lookup_address(who).await?; if verify { - let address = provider.resolve_name(&name).await?.to_alloy(); + let address = provider.resolve_name(&name).await?; eyre::ensure!( address == who, "Forward lookup verification failed: got `{name:?}`, expected `{who:?}`" @@ -493,7 +476,7 @@ async fn main() -> Result<()> { "forward lookup verification failed. got {name}, expected {who}" ); } - println!("{}", address.to_alloy().to_checksum(None)); + println!("{}", address.to_checksum(None)); } // Misc @@ -555,14 +538,7 @@ async fn main() -> Result<()> { CastSubcommand::Logs(cmd) => cmd.run().await?, CastSubcommand::DecodeTransaction { tx } => { let tx = stdin::unwrap_line(tx)?; - let (tx, sig) = SimpleCast::decode_raw_transaction(&tx)?; - - // Serialize tx, sig and constructed a merged json string - let mut tx = serde_json::to_value(&tx)?; - let tx_map = tx.as_object_mut().unwrap(); - serde_json::to_value(sig)?.as_object().unwrap().iter().for_each(|(k, v)| { - tx_map.entry(k).or_insert(v.clone()); - }); + let tx = SimpleCast::decode_raw_transaction(&tx)?; println!("{}", serde_json::to_string_pretty(&tx)?); } diff --git a/crates/cast/bin/opts.rs b/crates/cast/bin/opts.rs index 5a1af1fdc..51f32b145 100644 --- a/crates/cast/bin/opts.rs +++ b/crates/cast/bin/opts.rs @@ -5,10 +5,11 @@ use crate::cmd::{ wallet::WalletSubcommands, }; use alloy_primitives::{Address, B256, U256}; +use alloy_rpc_types::BlockId; use clap::{Parser, Subcommand, ValueHint}; -use ethers_core::types::{BlockId, NameOrAddress}; use eyre::Result; use foundry_cli::opts::{EtherscanOpts, RpcOpts}; +use foundry_common::ens::NameOrAddress; use std::{path::PathBuf, str::FromStr}; const VERSION_MESSAGE: &str = concat!( @@ -423,7 +424,7 @@ pub enum CastSubcommand { /// The number of confirmations until the receipt is fetched #[arg(long, default_value = "1")] - confirmations: usize, + confirmations: u64, /// Exit immediately if the transaction was not found. #[arg(id = "async", long = "async", env = "CAST_ASYNC", alias = "cast-async")] @@ -906,9 +907,9 @@ pub fn parse_slot(s: &str) -> Result { #[cfg(test)] mod tests { use super::*; + use alloy_rpc_types::{BlockNumberOrTag, RpcBlockHash}; use cast::SimpleCast; use clap::CommandFactory; - use ethers_core::types::BlockNumber; #[test] fn verify_cli() { @@ -997,30 +998,34 @@ mod tests { let test_cases = [ TestCase { input: "0".to_string(), - expect: BlockId::Number(BlockNumber::Number(0u64.into())), + expect: BlockId::Number(BlockNumberOrTag::Number(0u64)), }, TestCase { input: "0x56462c47c03df160f66819f0a79ea07def1569f8aac0fe91bb3a081159b61b4a" .to_string(), - expect: BlockId::Hash( + expect: BlockId::Hash(RpcBlockHash::from_hash( "0x56462c47c03df160f66819f0a79ea07def1569f8aac0fe91bb3a081159b61b4a" .parse() .unwrap(), - ), + None, + )), + }, + TestCase { + input: "latest".to_string(), + expect: BlockId::Number(BlockNumberOrTag::Latest), }, - TestCase { input: "latest".to_string(), expect: BlockId::Number(BlockNumber::Latest) }, TestCase { input: "earliest".to_string(), - expect: BlockId::Number(BlockNumber::Earliest), + expect: BlockId::Number(BlockNumberOrTag::Earliest), }, TestCase { input: "pending".to_string(), - expect: BlockId::Number(BlockNumber::Pending), + expect: BlockId::Number(BlockNumberOrTag::Pending), }, - TestCase { input: "safe".to_string(), expect: BlockId::Number(BlockNumber::Safe) }, + TestCase { input: "safe".to_string(), expect: BlockId::Number(BlockNumberOrTag::Safe) }, TestCase { input: "finalized".to_string(), - expect: BlockId::Number(BlockNumber::Finalized), + expect: BlockId::Number(BlockNumberOrTag::Finalized), }, ]; diff --git a/crates/cast/bin/tx.rs b/crates/cast/bin/tx.rs index cd155de01..98f7bf67e 100644 --- a/crates/cast/bin/tx.rs +++ b/crates/cast/bin/tx.rs @@ -1,9 +1,12 @@ -use alloy_primitives::Address; -use cast::{TxBuilder, TxBuilderOutput}; -use ethers_core::types::NameOrAddress; -use ethers_providers::Middleware; +use alloy_json_abi::Function; +use alloy_network::{AnyNetwork, TransactionBuilder}; +use alloy_primitives::{Address, U256}; +use alloy_provider::Provider; +use alloy_rpc_types::{TransactionRequest, WithOtherFields}; +use alloy_transport::Transport; use eyre::Result; -use foundry_cli::opts::TransactionOpts; +use foundry_cli::{opts::TransactionOpts, utils::parse_function_args}; +use foundry_common::ens::NameOrAddress; use foundry_config::Chain; /// Prevents a misconfigured hwlib from sending a transaction that defies user-specified --from @@ -34,40 +37,86 @@ pub fn validate_to_address(code: &Option, to: &Option) -> } #[allow(clippy::too_many_arguments)] -pub async fn build_tx, T: Into>( - provider: &M, +pub async fn build_tx< + P: Provider, + T: Transport + Clone, + F: Into, + TO: Into, +>( + provider: &P, from: F, - to: Option, + to: Option, code: Option, sig: Option, args: Vec, tx: TransactionOpts, chain: impl Into, etherscan_api_key: Option, -) -> Result { - let mut builder = TxBuilder::new(provider, from, to, chain, tx.legacy).await?; - builder - .etherscan_api_key(etherscan_api_key) - .gas(tx.gas_limit) - .gas_price(tx.gas_price) - .priority_gas_price(tx.priority_gas_price) - .value(tx.value) - .nonce(tx.nonce); +) -> Result<(WithOtherFields, Option)> { + let chain = chain.into(); + + let from = from.into().resolve(provider).await?; + let to = if let Some(to) = to { Some(to.into().resolve(provider).await?) } else { None }; + + let mut req = WithOtherFields::new(TransactionRequest::default()) + .with_to(to.into()) + .with_from(from) + .with_value(tx.value.unwrap_or_default()) + .with_chain_id(chain.id()); + + req.set_nonce(if let Some(nonce) = tx.nonce { + nonce.to() + } else { + provider.get_transaction_count(from, None).await? + }); + + if tx.legacy || chain.is_legacy() { + req.set_gas_price(if let Some(gas_price) = tx.gas_price { + gas_price.to() + } else { + provider.get_gas_price().await? + }); + } else { + let (max_fee, priority_fee) = match (tx.gas_price, tx.priority_gas_price) { + (Some(gas_price), Some(priority_gas_price)) => (gas_price, priority_gas_price), + (_, _) => { + let estimate = provider.estimate_eip1559_fees(None).await?; + ( + tx.gas_price.unwrap_or(U256::from(estimate.max_fee_per_gas)), + tx.priority_gas_price.unwrap_or(U256::from(estimate.max_priority_fee_per_gas)), + ) + } + }; + + req.set_max_fee_per_gas(max_fee.to()); + req.set_max_priority_fee_per_gas(priority_fee.to()); + } let params = sig.as_deref().map(|sig| (sig, args)); - if let Some(code) = code { + let (data, func) = if let Some(code) = code { let mut data = hex::decode(code)?; if let Some((sig, args)) = params { - let (mut sigdata, _) = builder.create_args(sig, args).await?; + let (mut sigdata, _) = + parse_function_args(sig, args, None, chain, provider, etherscan_api_key.as_deref()) + .await?; data.append(&mut sigdata); } - builder.set_data(data); + (data, None) + } else if let Some((sig, args)) = params { + parse_function_args(sig, args, None, chain, provider, etherscan_api_key.as_deref()).await? } else { - builder.args(params).await?; - } + (Vec::new(), None) + }; + + req.set_input(data.into()); + + req.set_gas_limit(if let Some(gas_limit) = tx.gas_limit { + gas_limit.to() + } else { + provider.estimate_gas(&req, None).await? + }); - let builder_output = builder.build(); - Ok(builder_output) + Ok((req, func)) } diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index 38498786e..338510eb9 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -1,66 +1,70 @@ +use alloy_consensus::TxEnvelope; use alloy_dyn_abi::{DynSolType, DynSolValue, FunctionExt}; -use alloy_json_abi::ContractObject; +use alloy_json_abi::{ContractObject, Function}; +use alloy_network::AnyNetwork; use alloy_primitives::{ utils::{keccak256, ParseUnits, Unit}, - Address, Bytes, Keccak256, B256, I256, U256, + Address, Keccak256, TxHash, B256, I256, U256, +}; +use alloy_provider::{ + network::eip2718::{Decodable2718, Encodable2718}, + PendingTransactionBuilder, Provider, }; use alloy_rlp::Decodable; +use alloy_rpc_types::{BlockId, BlockNumberOrTag, Filter, TransactionRequest, WithOtherFields}; +use alloy_sol_types::sol; +use alloy_transport::Transport; use base::{Base, NumberWithBase, ToBase}; use chrono::DateTime; -use ethers_core::{ - types::{ - transaction::eip2718::TypedTransaction, BlockId, BlockNumber, Filter, NameOrAddress, - Signature, H160, H256, U64, - }, - utils::rlp, -}; -use ethers_providers::{Middleware, PendingTransaction, PubsubClient}; use evm_disassembler::{disassemble_bytes, disassemble_str, format_operations}; use eyre::{Context, ContextCompat, Result}; use foundry_block_explorers::Client; use foundry_common::{ abi::{encode_function_args, get_func}, fmt::*, - types::{ToAlloy, ToEthers}, TransactionReceiptWithRevertReason, }; use foundry_config::Chain; use futures::{future::Either, FutureExt, StreamExt}; use rayon::prelude::*; use std::{ + borrow::Cow, io, + marker::PhantomData, path::PathBuf, str::FromStr, sync::atomic::{AtomicBool, Ordering}, }; use tokio::signal::ctrl_c; -use tx::TxBuilderPeekOutput; use foundry_common::abi::encode_function_args_packed; pub use foundry_evm::*; -pub use rusoto_core::{ - credential::ChainProvider as AwsChainProvider, region::Region as AwsRegion, - request::HttpClient as AwsHttpClient, Client as AwsClient, -}; -pub use rusoto_kms::KmsClient; -pub use tx::{TxBuilder, TxBuilderOutput}; pub mod base; pub mod errors; mod rlp_converter; -mod tx; use rlp_converter::Item; // TODO: CastContract with common contract initializers? Same for CastProviders? -pub struct Cast { - provider: M, +sol! { + #[sol(rpc)] + interface IERC20 { + #[derive(Debug)] + function balanceOf(address owner) external view returns (uint256); + } } -impl Cast +pub struct Cast { + provider: P, + transport: PhantomData, +} + +impl Cast where - M::Error: 'static, + T: Transport + Clone, + P: Provider, { /// Creates a new Cast instance from the provided client /// @@ -68,16 +72,16 @@ where /// /// ``` /// use cast::Cast; - /// use ethers_providers::{Http, Provider}; + /// use foundry_common::provider::alloy::get_http_provider; /// /// # async fn foo() -> eyre::Result<()> { - /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let provider = get_http_provider("http://localhost:8545"); /// let cast = Cast::new(provider); /// # Ok(()) /// # } /// ``` - pub fn new(provider: M) -> Self { - Self { provider } + pub fn new(provider: P) -> Self { + Self { provider, transport: PhantomData } } /// Makes a read-only call to the specified address @@ -88,10 +92,12 @@ where /// use cast::{Cast, TxBuilder}; /// use ethers_core::types::Address; /// use ethers_providers::{Http, Provider}; + /// use foundry_common::provider::alloy::get_http_provider; /// use std::str::FromStr; /// /// # async fn foo() -> eyre::Result<()> { /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let alloy_provider = get_http_provider("http://localhost:8545"); /// let to = Address::from_str("0xB3C95ff08316fb2F2e3E52Ee82F8e7b605Aa1304")?; /// let sig = "function greeting(uint256 i) public returns (string)"; /// let args = vec!["5".to_owned()]; @@ -99,7 +105,7 @@ where /// TxBuilder::new(&provider, Address::zero(), Some(to), Chain::Mainnet, false).await?; /// builder.set_args(sig, args).await?; /// let builder_output = builder.build(); - /// let cast = Cast::new(provider); + /// let cast = Cast::new(provider, alloy_provider); /// let data = cast.call(builder_output, None).await?; /// println!("{}", data); /// # Ok(()) @@ -107,11 +113,11 @@ where /// ``` pub async fn call<'a>( &self, - builder_output: TxBuilderOutput, + req: &WithOtherFields, + func: Option<&Function>, block: Option, ) -> Result { - let (tx, func) = builder_output; - let res = self.provider.call(&tx, block).await?; + let res = self.provider.call(req, block).await?; let mut decoded = vec![]; @@ -123,8 +129,10 @@ where // ensure the address is a contract if res.is_empty() { // check that the recipient is a contract that can be called - if let Some(NameOrAddress::Address(addr)) = tx.to() { - if let Ok(code) = self.provider.get_code(*addr, block).await { + if let Some(addr) = req.to { + if let Ok(code) = + self.provider.get_code_at(addr, block.unwrap_or_default()).await + { if code.is_empty() { eyre::bail!("contract {addr:?} does not have any code") } @@ -174,19 +182,18 @@ where /// ``` pub async fn access_list( &self, - builder_output: TxBuilderPeekOutput<'_>, + req: &WithOtherFields, block: Option, to_json: bool, ) -> Result { - let (tx, _) = builder_output; - let access_list = self.provider.create_access_list(tx, block).await?; + let access_list = self.provider.create_access_list(req, block).await?; let res = if to_json { serde_json::to_string(&access_list)? } else { let mut s = vec![format!("gas used: {}", access_list.gas_used), "access list:".to_string()]; for al in access_list.access_list.0 { - s.push(format!("- address: {}", &al.address.to_alloy().to_checksum(None))); + s.push(format!("- address: {}", &al.address.to_checksum(None))); if !al.storage_keys.is_empty() { s.push(" keys:".to_string()); for key in al.storage_keys { @@ -200,12 +207,8 @@ where Ok(res) } - pub async fn balance + Send + Sync>( - &self, - who: T, - block: Option, - ) -> Result { - Ok(self.provider.get_balance(who, block).await?.to_alloy()) + pub async fn balance(&self, who: Address, block: Option) -> Result { + Ok(self.provider.get_balance(who, block).await?) } /// Sends a transaction to the specified address @@ -236,14 +239,13 @@ where /// # Ok(()) /// # } /// ``` - pub async fn send<'a>( + pub async fn send( &self, - builder_output: TxBuilderOutput, - ) -> Result> { - let (tx, _) = builder_output; - let res = self.provider.send_transaction(tx, None).await?; + tx: WithOtherFields, + ) -> Result> { + let res = self.provider.send_transaction(tx).await?; - Ok::<_, eyre::Error>(res) + Ok(res) } /// Publishes a raw transaction to the network @@ -262,50 +264,18 @@ where /// # Ok(()) /// # } /// ``` - pub async fn publish(&self, mut raw_tx: String) -> Result> { + pub async fn publish( + &self, + mut raw_tx: String, + ) -> Result> { raw_tx = match raw_tx.strip_prefix("0x") { Some(s) => s.to_string(), None => raw_tx, }; - let tx = Bytes::from(hex::decode(raw_tx)?); - let res = self.provider.send_raw_transaction(tx.0.into()).await?; - - Ok::<_, eyre::Error>(res) - } - - /// Estimates the gas cost of a transaction - /// - /// # Example - /// - /// ```ignore - /// use alloy_primitives::U256; - /// use cast::{Cast, TxBuilder}; - /// use ethers_core::types::Address; - /// use ethers_providers::{Http, Provider}; - /// use std::str::FromStr; - /// - /// # async fn foo() -> eyre::Result<()> { - /// let provider = Provider::::try_from("http://localhost:8545")?; - /// let from = Address::from_str("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")?; - /// let to = Address::from_str("0xB3C95ff08316fb2F2e3E52Ee82F8e7b605Aa1304")?; - /// let sig = "greet(string)()"; - /// let args = vec!["5".to_owned()]; - /// let value = U256::from_str("1").unwrap(); - /// let mut builder = TxBuilder::new(&provider, from, Some(to), Chain::Mainnet, false).await?; - /// builder.set_value(value).set_args(sig, args).await?; - /// let builder_output = builder.peek(); - /// let cast = Cast::new(&provider); - /// let data = cast.estimate(builder_output).await?; - /// println!("{}", data); - /// # Ok(()) - /// # } - /// ``` - pub async fn estimate(&self, builder_output: TxBuilderPeekOutput<'_>) -> Result { - let (tx, _) = builder_output; + let tx = hex::decode(raw_tx)?; + let res = self.provider.send_raw_transaction(&tx).await?; - let res = self.provider.estimate_gas(tx, None).await?; - - Ok::<_, eyre::Error>(res.to_alloy()) + Ok(res) } /// # Example @@ -322,53 +292,39 @@ where /// # Ok(()) /// # } /// ``` - pub async fn block>( + pub async fn block>( &self, - block: T, + block: B, full: bool, field: Option, to_json: bool, ) -> Result { let block = block.into(); - let block = if full { - let block = self - .provider - .get_block_with_txs(block) - .await? - .ok_or_else(|| eyre::eyre!("block {:?} not found", block))?; - if let Some(ref field) = field { - get_pretty_block_attr(&block, field) - .unwrap_or_else(|| format!("{field} is not a valid block field")) - } else if to_json { - serde_json::to_value(&block).unwrap().to_string() - } else { - block.pretty() + if let Some(ref field) = field { + if field == "transactions" && !full { + eyre::bail!("use --full to view transactions") } + } + + let block = self + .provider + .get_block(block, full) + .await? + .ok_or_else(|| eyre::eyre!("block {:?} not found", block))?; + + let block = if let Some(ref field) = field { + get_pretty_block_attr(&block, field) + .unwrap_or_else(|| format!("{field} is not a valid block field")) + } else if to_json { + serde_json::to_value(&block).unwrap().to_string() } else { - let block = self - .provider - .get_block(block) - .await? - .ok_or_else(|| eyre::eyre!("block {:?} not found", block))?; - - if let Some(ref field) = field { - if field == "transactions" { - "use --full to view transactions".to_string() - } else { - get_pretty_block_attr(&block, field) - .unwrap_or_else(|| format!("{field} is not a valid block field")) - } - } else if to_json { - serde_json::to_value(&block).unwrap().to_string() - } else { - block.pretty() - } + block.pretty() }; Ok(block) } - async fn block_field_as_num>(&self, block: T, field: String) -> Result { + async fn block_field_as_num>(&self, block: B, field: String) -> Result { let block = block.into(); let block_field = Cast::block( self, @@ -388,18 +344,18 @@ where Ok(ret) } - pub async fn base_fee>(&self, block: T) -> Result { + pub async fn base_fee>(&self, block: B) -> Result { Cast::block_field_as_num(self, block, String::from("baseFeePerGas")).await } - pub async fn age>(&self, block: T) -> Result { + pub async fn age>(&self, block: B) -> Result { let timestamp_str = Cast::block_field_as_num(self, block, String::from("timestamp")).await?.to_string(); let datetime = DateTime::from_timestamp(timestamp_str.parse::().unwrap(), 0).unwrap(); Ok(datetime.format("%a %b %e %H:%M:%S %Y").to_string()) } - pub async fn timestamp>(&self, block: T) -> Result { + pub async fn timestamp>(&self, block: B) -> Result { Cast::block_field_as_num(self, block, "timestamp".to_string()).await } @@ -466,16 +422,16 @@ where }) } - pub async fn chain_id(&self) -> Result { - Ok(self.provider.get_chainid().await?.to_alloy()) + pub async fn chain_id(&self) -> Result { + Ok(self.provider.get_chain_id().await?) } - pub async fn block_number(&self) -> Result { + pub async fn block_number(&self) -> Result { Ok(self.provider.get_block_number().await?) } - pub async fn gas_price(&self) -> Result { - Ok(self.provider.get_gas_price().await?.to_alloy()) + pub async fn gas_price(&self) -> Result { + Ok(self.provider.get_gas_price().await?) } /// # Example @@ -495,12 +451,8 @@ where /// # Ok(()) /// # } /// ``` - pub async fn nonce + Send + Sync>( - &self, - who: T, - block: Option, - ) -> Result { - Ok(self.provider.get_transaction_count(who, block).await?.to_alloy().to()) + pub async fn nonce(&self, who: Address, block: Option) -> Result { + Ok(self.provider.get_transaction_count(who, block).await?) } /// # Example @@ -520,15 +472,11 @@ where /// # Ok(()) /// # } /// ``` - pub async fn implementation + Send + Sync>( - &self, - who: T, - block: Option, - ) -> Result { + pub async fn implementation(&self, who: Address, block: Option) -> Result { let slot = - H256::from_str("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc")?; - let value = self.provider.get_storage_at(who, slot, block).await?; - let addr: H160 = value.into(); + B256::from_str("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc")?; + let value = self.provider.get_storage_at(who, slot.into(), block).await?; + let addr = Address::from_word(value.into()); Ok(format!("{addr:?}")) } @@ -549,15 +497,11 @@ where /// # Ok(()) /// # } /// ``` - pub async fn admin + Send + Sync>( - &self, - who: T, - block: Option, - ) -> Result { + pub async fn admin(&self, who: Address, block: Option) -> Result { let slot = - H256::from_str("0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103")?; - let value = self.provider.get_storage_at(who, slot, block).await?; - let addr: H160 = value.into(); + B256::from_str("0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103")?; + let value = self.provider.get_storage_at(who, slot.into(), block).await?; + let addr = Address::from_word(value.into()); Ok(format!("{addr:?}")) } @@ -579,8 +523,7 @@ where /// # } /// ``` pub async fn compute_address(&self, address: Address, nonce: Option) -> Result
{ - let unpacked = - if let Some(n) = nonce { n } else { self.nonce(address.to_ethers(), None).await? }; + let unpacked = if let Some(n) = nonce { n } else { self.nonce(address, None).await? }; Ok(address.create(unpacked)) } @@ -601,17 +544,17 @@ where /// # Ok(()) /// # } /// ``` - pub async fn code + Send + Sync>( + pub async fn code( &self, - who: T, + who: Address, block: Option, disassemble: bool, ) -> Result { if disassemble { - let code = self.provider.get_code(who, block).await?.to_vec(); + let code = self.provider.get_code_at(who, block.unwrap_or_default()).await?.to_vec(); Ok(format_operations(disassemble_bytes(code)?)?) } else { - Ok(format!("{}", self.provider.get_code(who, block).await?)) + Ok(format!("{}", self.provider.get_code_at(who, block.unwrap_or_default()).await?)) } } @@ -632,12 +575,8 @@ where /// # Ok(()) /// # } /// ``` - pub async fn codesize + Send + Sync>( - &self, - who: T, - block: Option, - ) -> Result { - let code = self.provider.get_code(who, block).await?.to_vec(); + pub async fn codesize(&self, who: Address, block: Option) -> Result { + let code = self.provider.get_code_at(who, block.unwrap_or_default()).await?.to_vec(); Ok(format!("{}", code.len())) } @@ -663,15 +602,11 @@ where raw: bool, to_json: bool, ) -> Result { - let tx_hash = H256::from_str(&tx_hash).wrap_err("invalid tx hash")?; - let tx = self - .provider - .get_transaction(tx_hash) - .await? - .ok_or_else(|| eyre::eyre!("tx not found: {:?}", tx_hash))?; + let tx_hash = TxHash::from_str(&tx_hash).wrap_err("invalid tx hash")?; + let tx = self.provider.get_transaction_by_hash(tx_hash).await?; Ok(if raw { - format!("0x{}", hex::encode(tx.rlp())) + format!("0x{}", hex::encode(TxEnvelope::try_from(tx.inner)?.encoded_2718())) } else if let Some(field) = field { get_pretty_tx_attr(&tx, field.as_str()) .ok_or_else(|| eyre::eyre!("invalid tx field: {}", field.to_string()))? @@ -702,11 +637,11 @@ where &self, tx_hash: String, field: Option, - confs: usize, + confs: u64, cast_async: bool, to_json: bool, ) -> Result { - let tx_hash = H256::from_str(&tx_hash).wrap_err("invalid tx hash")?; + let tx_hash = TxHash::from_str(&tx_hash).wrap_err("invalid tx hash")?; let mut receipt: TransactionReceiptWithRevertReason = match self.provider.get_transaction_receipt(tx_hash).await? { @@ -717,13 +652,10 @@ where if cast_async { eyre::bail!("tx not found: {:?}", tx_hash) } else { - let tx = PendingTransaction::new(tx_hash, self.provider.provider()); - tx.confirmations(confs).await?.ok_or_else(|| { - eyre::eyre!( - "tx not found, might have been dropped from mempool: {:?}", - tx_hash - ) - })? + PendingTransactionBuilder::new(self.provider.root(), tx_hash) + .with_required_confirmations(confs) + .get_receipt() + .await? } } } @@ -761,11 +693,14 @@ where /// # Ok(()) /// # } /// ``` - pub async fn rpc(&self, method: &str, params: T) -> Result + pub async fn rpc(&self, method: &str, params: V) -> Result where - T: std::fmt::Debug + serde::Serialize + Send + Sync, + V: alloy_json_rpc::RpcParam, { - let res = self.provider.provider().request::(method, params).await?; + let res = self + .provider + .raw_request::(Cow::Owned(method.to_string()), params) + .await?; Ok(serde_json::to_string(&res)?) } @@ -789,13 +724,16 @@ where /// # Ok(()) /// # } /// ``` - pub async fn storage + Send + Sync>( + pub async fn storage( &self, - from: T, - slot: H256, + from: Address, + slot: B256, block: Option, ) -> Result { - Ok(format!("{:?}", self.provider.get_storage_at(from, slot, block).await?)) + Ok(format!( + "{:?}", + B256::from(self.provider.get_storage_at(from, slot.into(), block).await?) + )) } pub async fn filter_logs(&self, filter: Filter, to_json: bool) -> Result { @@ -851,13 +789,13 @@ where pub async fn convert_block_number( &self, block: Option, - ) -> Result, eyre::Error> { + ) -> Result, eyre::Error> { match block { Some(block) => match block { BlockId::Number(block_number) => Ok(Some(block_number)), BlockId::Hash(hash) => { - let block = self.provider.get_block(hash).await?; - Ok(block.map(|block| block.number.unwrap()).map(BlockNumber::from)) + let block = self.provider.get_block_by_hash(hash.block_hash, false).await?; + Ok(block.map(|block| block.header.number.unwrap()).map(BlockNumberOrTag::from)) } }, None => Ok(None), @@ -890,16 +828,13 @@ where filter: Filter, output: &mut dyn io::Write, to_json: bool, - ) -> Result<()> - where - ::Provider: PubsubClient, - { + ) -> Result<()> { // Initialize the subscription stream for logs - let mut subscription = self.provider.subscribe_logs(&filter).await?; + let mut subscription = self.provider.subscribe_logs(&filter).await?.into_stream(); // Check if a to_block is specified, if so, subscribe to blocks let mut block_subscription = if filter.get_to_block().is_some() { - Some(self.provider.subscribe_blocks().await?) + Some(self.provider.subscribe_blocks().await?.into_stream()) } else { None }; @@ -922,7 +857,7 @@ where Either::Right(futures::future::pending()) } => { if let (Some(block), Some(to_block)) = (block, to_block_number) { - if block.number.map_or(false, |bn| bn > to_block) { + if block.header.number.map_or(false, |bn| bn > to_block) { break; } } @@ -958,6 +893,20 @@ where Ok(()) } + + pub async fn erc20_balance( + &self, + token: Address, + owner: Address, + block: Option, + ) -> Result { + Ok(IERC20::new(token, &self.provider) + .balanceOf(owner) + .block(block.unwrap_or_default()) + .call() + .await? + ._0) + } } pub struct InterfaceSource { @@ -2027,13 +1976,13 @@ impl SimpleCast { /// ``` /// use cast::SimpleCast as Cast; /// - /// let tx = "0x02f8f582a86a82058d8459682f008508351050808303fd84948e42f2f4101563bf679975178e880fd87d3efd4e80b884659ac74b00000000000000000000000080f0c1c49891dcfdd40b6e0f960f84e6042bcb6f000000000000000000000000b97ef9ef8734c71904d8002f8b6bc66dd9c48a6e00000000000000000000000000000000000000000000000000000000007ff4e20000000000000000000000000000000000000000000000000000000000000064c001a05d429597befe2835396206781b199122f2e8297327ed4a05483339e7a8b2022aa04c23a7f70fb29dda1b4ee342fb10a625e9b8ddc6a603fb4e170d4f6f37700cb8"; - /// let (tx, sig) = Cast::decode_raw_transaction(&tx)?; + /// let tx = "0x02f8f582a86a82058d8459682f008508351050808303fd84948e42f2f4101563bf679975178e880fd87d3efd4e80b884659ac74b00000000000000000000000080f0c1c49891dcfdd40b6e0f960f84e6042bcb6f000000000000000000000000b97ef9ef8734c71904d8002f8b6bc66dd9c48a6e00000000000000000000000000000000000000000000000000000000007ff4e20000000000000000000000000000000000000000000000000000000000000064c001a05d429597befe2835396206781b199122f2e8297327ed4a05483339e7a8b2022aa04c23a7f70fb29dda1b4ee342fb10a625e9b8ddc6a603fb4e170d4f6f37700cb8"; + /// let tx_envelope = Cast::decode_raw_transaction(&tx)?; /// # Ok::<(), eyre::Report>(()) - pub fn decode_raw_transaction(tx: &str) -> Result<(TypedTransaction, Signature)> { + pub fn decode_raw_transaction(tx: &str) -> Result { let tx_hex = hex::decode(strip_0x(tx))?; - let tx_rlp = rlp::Rlp::new(tx_hex.as_slice()); - Ok(TypedTransaction::decode_signed(&tx_rlp)?) + let tx = TxEnvelope::decode_2718(&mut tx_hex.as_slice())?; + Ok(tx) } } diff --git a/crates/cast/src/tx.rs b/crates/cast/src/tx.rs deleted file mode 100644 index 1bfd0c40f..000000000 --- a/crates/cast/src/tx.rs +++ /dev/null @@ -1,402 +0,0 @@ -use crate::errors::FunctionSignatureError; -use alloy_json_abi::Function; -use alloy_primitives::{Address, U256}; -use ethers_core::types::{ - transaction::eip2718::TypedTransaction, Eip1559TransactionRequest, NameOrAddress, - TransactionRequest, -}; -use ethers_providers::Middleware; -use eyre::{eyre, Result}; -use foundry_common::{ - abi::{encode_function_args, get_func, get_func_etherscan}, - types::{ToAlloy, ToEthers}, -}; -use foundry_config::Chain; -use futures::future::join_all; - -pub type TxBuilderOutput = (TypedTransaction, Option); -pub type TxBuilderPeekOutput<'a> = (&'a TypedTransaction, &'a Option); - -/// Transaction builder -/// -/// # Examples -/// -/// ``` -/// # async fn foo() -> eyre::Result<()> { -/// # use alloy_primitives::U256; -/// # use cast::TxBuilder; -/// # use foundry_config::NamedChain; -/// let provider = ethers_providers::test_provider::MAINNET.provider(); -/// let mut builder = -/// TxBuilder::new(&provider, "a.eth", Some("b.eth"), NamedChain::Mainnet, false).await?; -/// builder.gas(Some(U256::from(1))); -/// let (tx, _) = builder.build(); -/// # Ok(()) -/// # } -/// ``` -pub struct TxBuilder<'a, M: Middleware> { - to: Option
, - chain: Chain, - tx: TypedTransaction, - func: Option, - etherscan_api_key: Option, - provider: &'a M, -} - -impl<'a, M: Middleware> TxBuilder<'a, M> { - /// Create a new TxBuilder - /// `provider` - provider to use - /// `from` - 'from' field. Could be an ENS name - /// `to` - `to`. Could be a ENS - /// `chain` - chain to construct the tx for - /// `legacy` - use type 1 transaction - pub async fn new, T: Into>( - provider: &'a M, - from: F, - to: Option, - chain: impl Into, - legacy: bool, - ) -> Result> { - let chain = chain.into(); - let from_addr = resolve_ens(provider, from).await?; - - let mut tx: TypedTransaction = if chain.is_legacy() || legacy { - TransactionRequest::new().from(from_addr.to_ethers()).chain_id(chain.id()).into() - } else { - Eip1559TransactionRequest::new().from(from_addr.to_ethers()).chain_id(chain.id()).into() - }; - - let to_addr = if let Some(to) = to { - let addr = resolve_ens(provider, to).await?; - tx.set_to(addr.to_ethers()); - Some(addr) - } else { - None - }; - Ok(Self { to: to_addr, chain, tx, func: None, etherscan_api_key: None, provider }) - } - - /// Set gas for tx - pub fn set_gas(&mut self, v: U256) -> &mut Self { - self.tx.set_gas(v.to_ethers()); - self - } - - /// Set gas for tx, if `v` is not None - pub fn gas(&mut self, v: Option) -> &mut Self { - if let Some(value) = v { - self.set_gas(value); - } - self - } - - /// Set gas price - pub fn set_gas_price(&mut self, v: U256) -> &mut Self { - self.tx.set_gas_price(v.to_ethers()); - self - } - - /// Set gas price, if `v` is not None - pub fn gas_price(&mut self, v: Option) -> &mut Self { - if let Some(value) = v { - self.set_gas_price(value); - } - self - } - - /// Set priority gas price - pub fn set_priority_gas_price(&mut self, v: U256) -> &mut Self { - if let TypedTransaction::Eip1559(tx) = &mut self.tx { - tx.max_priority_fee_per_gas = Some(v.to_ethers()) - } - self - } - - /// Set priority gas price, if `v` is not None - pub fn priority_gas_price(&mut self, v: Option) -> &mut Self { - if let Some(value) = v { - self.set_priority_gas_price(value); - } - self - } - - /// Set value - pub fn set_value(&mut self, v: U256) -> &mut Self { - self.tx.set_value(v.to_ethers()); - self - } - - /// Set value, if `v` is not None - pub fn value(&mut self, v: Option) -> &mut Self { - if let Some(value) = v { - self.set_value(value); - } - self - } - - /// Set nonce - pub fn set_nonce(&mut self, v: U256) -> &mut Self { - self.tx.set_nonce(v.to_ethers()); - self - } - - /// Set nonce, if `v` is not None - pub fn nonce(&mut self, v: Option) -> &mut Self { - if let Some(value) = v { - self.set_nonce(value); - } - self - } - - /// Set etherscan API key. Used to look up function signature buy name - pub fn set_etherscan_api_key(&mut self, v: String) -> &mut Self { - self.etherscan_api_key = Some(v); - self - } - - /// Set etherscan API key, if `v` is not None - pub fn etherscan_api_key(&mut self, v: Option) -> &mut Self { - if let Some(value) = v { - self.set_etherscan_api_key(value); - } - self - } - - pub fn set_data(&mut self, v: Vec) -> &mut Self { - self.tx.set_data(v.into()); - self - } - - pub async fn create_args( - &mut self, - sig: &str, - args: Vec, - ) -> Result<(Vec, Function)> { - if sig.trim().is_empty() { - return Err(FunctionSignatureError::MissingSignature.into()) - } - - let args = resolve_name_args(&args, self.provider).await; - - let func = if sig.contains('(') { - // a regular function signature with parentheses - get_func(sig)? - } else if sig.starts_with("0x") { - // if only calldata is provided, returning a dummy function - get_func("x()")? - } else { - get_func_etherscan( - sig, - self.to.ok_or(FunctionSignatureError::MissingToAddress)?, - &args, - self.chain, - self.etherscan_api_key.as_ref().ok_or_else(|| { - FunctionSignatureError::MissingEtherscan { sig: sig.to_string() } - })?, - ) - .await? - }; - - if sig.starts_with("0x") { - Ok((hex::decode(sig)?, func)) - } else { - Ok((encode_function_args(&func, &args)?, func)) - } - } - - /// Set function arguments - /// `sig` can be: - /// * a fragment (`do(uint32,string)`) - /// * selector + abi-encoded calldata - /// (`0xcdba2fd40000000000000000000000000000000000000000000000000000000000007a69`) - /// * only function name (`do`) - in this case, etherscan lookup is performed on `tx.to`'s - /// contract - pub async fn set_args( - &mut self, - sig: &str, - args: Vec, - ) -> Result<&mut TxBuilder<'a, M>> { - let (data, func) = self.create_args(sig, args).await?; - self.tx.set_data(data.into()); - self.func = Some(func); - Ok(self) - } - - /// Set function arguments, if `value` is not None - pub async fn args( - &mut self, - value: Option<(&str, Vec)>, - ) -> Result<&mut TxBuilder<'a, M>> { - if let Some((sig, args)) = value { - return self.set_args(sig, args).await - } - Ok(self) - } - - /// Consuming build: returns typed transaction and optional function call - pub fn build(self) -> TxBuilderOutput { - (self.tx, self.func) - } - - /// Non-consuming build: peek into the tx content - pub fn peek(&self) -> TxBuilderPeekOutput { - (&self.tx, &self.func) - } -} - -async fn resolve_ens>( - provider: &M, - addr: T, -) -> Result
{ - let from_addr = match addr.into() { - NameOrAddress::Name(ref ens_name) => provider.resolve_name(ens_name).await, - NameOrAddress::Address(addr) => Ok(addr), - } - .map_err(|x| eyre!("Failed to resolve ENS name: {x}"))?; - Ok(from_addr.to_alloy()) -} - -async fn resolve_name_args(args: &[String], provider: &M) -> Vec { - join_all(args.iter().map(|arg| async { - if arg.contains('.') { - let addr = provider.resolve_name(arg).await; - match addr { - Ok(addr) => format!("{addr:?}"), - Err(_) => arg.to_string(), - } - } else { - arg.to_string() - } - })) - .await -} - -#[cfg(test)] -mod tests { - use crate::TxBuilder; - use alloy_primitives::{Address, U256}; - use async_trait::async_trait; - use ethers_core::types::{transaction::eip2718::TypedTransaction, NameOrAddress, H160}; - use ethers_providers::{JsonRpcClient, Middleware, ProviderError}; - use foundry_common::types::ToEthers; - use foundry_config::NamedChain; - use serde::{de::DeserializeOwned, Serialize}; - use std::str::FromStr; - - const ADDR_1: &str = "0000000000000000000000000000000000000001"; - const ADDR_2: &str = "0000000000000000000000000000000000000002"; - - #[derive(Debug)] - struct MyProvider {} - - #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] - #[cfg_attr(not(target_arch = "wasm32"), async_trait)] - impl JsonRpcClient for MyProvider { - type Error = ProviderError; - - async fn request( - &self, - _method: &str, - _params: T, - ) -> Result { - Err(ProviderError::CustomError("There is no request".to_string())) - } - } - #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] - #[cfg_attr(not(target_arch = "wasm32"), async_trait)] - impl Middleware for MyProvider { - type Error = ProviderError; - type Provider = MyProvider; - type Inner = MyProvider; - - fn inner(&self) -> &Self::Inner { - self - } - - async fn resolve_name(&self, ens_name: &str) -> Result { - match ens_name { - "a.eth" => Ok(H160::from_str(ADDR_1).unwrap()), - "b.eth" => Ok(H160::from_str(ADDR_2).unwrap()), - _ => unreachable!("don't know how to resolve {ens_name}"), - } - } - } - #[tokio::test(flavor = "multi_thread")] - async fn builder_new_non_legacy() -> eyre::Result<()> { - let provider = MyProvider {}; - let builder = - TxBuilder::new(&provider, "a.eth", Some("b.eth"), NamedChain::Mainnet, false).await?; - let (tx, args) = builder.build(); - assert_eq!(*tx.from().unwrap(), Address::from_str(ADDR_1).unwrap().to_ethers()); - assert_eq!( - *tx.to().unwrap(), - NameOrAddress::Address(Address::from_str(ADDR_2).unwrap().to_ethers()) - ); - assert_eq!(args, None); - - match tx { - TypedTransaction::Eip1559(_) => {} - _ => { - panic!("Wrong tx type"); - } - } - Ok(()) - } - - #[tokio::test(flavor = "multi_thread")] - async fn builder_new_legacy() -> eyre::Result<()> { - let provider = MyProvider {}; - let builder = - TxBuilder::new(&provider, "a.eth", Some("b.eth"), NamedChain::Mainnet, true).await?; - // don't check anything other than the tx type - the rest is covered in the non-legacy case - let (tx, _) = builder.build(); - match tx { - TypedTransaction::Legacy(_) => {} - _ => { - panic!("Wrong tx type"); - } - } - Ok(()) - } - - #[tokio::test(flavor = "multi_thread")] - async fn builder_fields() -> eyre::Result<()> { - let provider = MyProvider {}; - let mut builder = - TxBuilder::new(&provider, "a.eth", Some("b.eth"), NamedChain::Mainnet, false) - .await - .unwrap(); - builder - .gas(Some(U256::from(12u32))) - .gas_price(Some(U256::from(34u32))) - .value(Some(U256::from(56u32))) - .nonce(Some(U256::from(78u32))); - - builder.etherscan_api_key(Some(String::from("what a lovely day"))); // not testing for this :-/ - let (tx, _) = builder.build(); - - assert_eq!(tx.gas().unwrap().as_u32(), 12); - assert_eq!(tx.gas_price().unwrap().as_u32(), 34); - assert_eq!(tx.value().unwrap().as_u32(), 56); - assert_eq!(tx.nonce().unwrap().as_u32(), 78); - assert_eq!(tx.chain_id().unwrap().as_u32(), 1); - Ok(()) - } - - #[tokio::test(flavor = "multi_thread")] - async fn builder_args() -> eyre::Result<()> { - let provider = MyProvider {}; - let mut builder = - TxBuilder::new(&provider, "a.eth", Some("b.eth"), NamedChain::Mainnet, false) - .await - .unwrap(); - builder.args(Some(("what_a_day(int)", vec![String::from("31337")]))).await?; - let (_, function_maybe) = builder.build(); - - assert_ne!(function_maybe, None); - let function = function_maybe.unwrap(); - assert_eq!(function.name, String::from("what_a_day")); - // could test function.inputs() but that should be covered by utils's unit test - Ok(()) - } -} diff --git a/crates/cheatcodes/Cargo.toml b/crates/cheatcodes/Cargo.toml index 8006867f4..17cddc607 100644 --- a/crates/cheatcodes/Cargo.toml +++ b/crates/cheatcodes/Cargo.toml @@ -24,9 +24,10 @@ alloy-json-abi.workspace = true alloy-primitives.workspace = true alloy-genesis.workspace = true alloy-sol-types.workspace = true -alloy-providers.workspace = true +alloy-provider.workspace = true alloy-rpc-types.workspace = true -alloy-signer = { workspace = true, features = ["mnemonic", "keystore"] } +alloy-signer.workspace = true +alloy-signer-wallet = { workspace = true, features = ["mnemonic", "keystore"] } parking_lot = "0.12" eyre.workspace = true diff --git a/crates/cheatcodes/src/env.rs b/crates/cheatcodes/src/env.rs index 231b47c97..d9022d1b3 100644 --- a/crates/cheatcodes/src/env.rs +++ b/crates/cheatcodes/src/env.rs @@ -2,7 +2,6 @@ use crate::{string, Cheatcode, Cheatcodes, Error, Result, Vm::*}; use alloy_dyn_abi::DynSolType; -use alloy_primitives::Bytes; use alloy_sol_types::SolValue; use std::{env, sync::OnceLock}; @@ -233,7 +232,7 @@ impl Cheatcode for envOr_12Call { impl Cheatcode for envOr_13Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name, delim, defaultValue } = self; - let default = defaultValue.iter().map(|vec| vec.clone().into()).collect::>(); + let default = defaultValue.to_vec(); env_array_default(name, delim, &default, &DynSolType::Bytes) } } diff --git a/crates/cheatcodes/src/error.rs b/crates/cheatcodes/src/error.rs index 8b5f8102d..508e7173e 100644 --- a/crates/cheatcodes/src/error.rs +++ b/crates/cheatcodes/src/error.rs @@ -1,6 +1,7 @@ use crate::Vm; use alloy_primitives::{Address, Bytes}; -use alloy_signer::{Error as SignerError, WalletError}; +use alloy_signer::Error as SignerError; +use alloy_signer_wallet::WalletError; use alloy_sol_types::SolError; use foundry_common::errors::FsPathError; use foundry_config::UnresolvedEnvVarError; diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index d7328fcc0..b8f55b37c 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -3,7 +3,6 @@ use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Result, Vm::*}; use alloy_genesis::{Genesis, GenesisAccount}; use alloy_primitives::{Address, Bytes, B256, U256}; -use alloy_signer::Signer; use alloy_sol_types::SolValue; use foundry_common::fs::{read_json_file, write_json_file}; use foundry_evm_core::{ @@ -14,7 +13,10 @@ use revm::{ primitives::{Account, Bytecode, SpecId, KECCAK_EMPTY}, InnerEvmContext, }; -use std::{collections::HashMap, path::Path}; +use std::{ + collections::{BTreeMap, HashMap}, + path::Path, +}; mod fork; pub(crate) mod mapping; @@ -74,7 +76,7 @@ impl Cheatcode for loadAllocsCall { ensure!(path.exists(), "allocs file does not exist: {pathToAllocsJson}"); // Let's first assume we're reading a file with only the allocs. - let allocs: HashMap = match read_json_file(path) { + let allocs: BTreeMap = match read_json_file(path) { Ok(allocs) => allocs, Err(_) => { // Let's try and read from a genesis file, and extract allocs. @@ -112,7 +114,7 @@ impl Cheatcode for dumpStateCall { .ecx .journaled_state .state() - .into_iter() + .iter_mut() .filter(|(key, val)| !skip(key, val)) .map(|(key, val)| { ( diff --git a/crates/cheatcodes/src/evm/fork.rs b/crates/cheatcodes/src/evm/fork.rs index f5402a0d3..d2a8ff4eb 100644 --- a/crates/cheatcodes/src/evm/fork.rs +++ b/crates/cheatcodes/src/evm/fork.rs @@ -1,10 +1,10 @@ use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Result, Vm::*}; use alloy_primitives::{B256, U256}; -use alloy_providers::tmp::TempProvider; +use alloy_provider::Provider; use alloy_rpc_types::Filter; use alloy_sol_types::SolValue; use eyre::WrapErr; -use foundry_common::{provider::alloy::ProviderBuilder, types::ToEthers}; +use foundry_common::provider::alloy::ProviderBuilder; use foundry_compilers::utils::RuntimeOrHandle; use foundry_evm_core::fork::CreateFork; @@ -64,7 +64,12 @@ impl Cheatcode for createSelectFork_2Call { impl Cheatcode for rollFork_0Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { blockNumber } = self; - ccx.ecx.db.roll_fork(None, *blockNumber, &mut ccx.ecx.env, &mut ccx.ecx.journaled_state)?; + ccx.ecx.db.roll_fork( + None, + (*blockNumber).to(), + &mut ccx.ecx.env, + &mut ccx.ecx.journaled_state, + )?; Ok(Default::default()) } } @@ -87,7 +92,7 @@ impl Cheatcode for rollFork_2Call { let Self { forkId, blockNumber } = self; ccx.ecx.db.roll_fork( Some(*forkId), - *blockNumber, + (*blockNumber).to(), &mut ccx.ecx.env, &mut ccx.ecx.journaled_state, )?; @@ -225,7 +230,7 @@ impl Cheatcode for rpcCall { let method: &'static str = Box::new(method.clone()).leak(); let params_json: serde_json::Value = serde_json::from_str(params)?; let result = RuntimeOrHandle::new() - .block_on(provider.raw_request(method, params_json)) + .block_on(provider.raw_request(method.into(), params_json)) .map_err(|err| fmt_err!("{method:?}: {err}"))?; let result_as_tokens = crate::json::json_value_to_token(&result) @@ -252,36 +257,35 @@ impl Cheatcode for eth_getLogsCall { let provider = ProviderBuilder::new(&url).build()?; let mut filter = Filter::new().address(*target).from_block(from_block).to_block(to_block); for (i, topic) in topics.iter().enumerate() { - let topic = topic.to_ethers(); // todo: needed because rust wants to convert FixedBytes<32> to U256 to convert it back // to FixedBytes<32> and then to Topic for some reason removing the // From impl in alloy does not fix the situation, and it is not possible to impl // From> either because of a conflicting impl match i { - 0 => filter = filter.event_signature(U256::from_be_bytes(topic.to_fixed_bytes())), - 1 => filter = filter.topic1(U256::from_be_bytes(topic.to_fixed_bytes())), - 2 => filter = filter.topic2(U256::from_be_bytes(topic.to_fixed_bytes())), - 3 => filter = filter.topic3(U256::from_be_bytes(topic.to_fixed_bytes())), + 0 => filter = filter.event_signature(*topic), + 1 => filter = filter.topic1(*topic), + 2 => filter = filter.topic2(*topic), + 3 => filter = filter.topic3(*topic), _ => unreachable!(), }; } // todo: handle the errors somehow let logs = RuntimeOrHandle::new() - .block_on(provider.get_logs(filter)) + .block_on(provider.get_logs(&filter)) .wrap_err("failed to get logs")?; let eth_logs = logs .into_iter() .map(|log| EthGetLogs { - emitter: log.address, - topics: log.topics.into_iter().collect(), - data: log.data.0.into(), + emitter: log.address(), + topics: log.topics().to_vec(), + data: log.inner.data.data, blockHash: log.block_hash.unwrap_or_default(), - blockNumber: log.block_number.unwrap_or_default().to(), + blockNumber: log.block_number.unwrap_or_default(), transactionHash: log.transaction_hash.unwrap_or_default(), - transactionIndex: log.transaction_index.unwrap_or_default().to(), - logIndex: log.log_index.unwrap_or_default(), + transactionIndex: log.transaction_index.unwrap_or_default(), + logIndex: U256::from(log.log_index.unwrap_or_default()), removed: log.removed, }) .collect::>(); diff --git a/crates/cheatcodes/src/evm/mock.rs b/crates/cheatcodes/src/evm/mock.rs index 645c14c58..6a266a410 100644 --- a/crates/cheatcodes/src/evm/mock.rs +++ b/crates/cheatcodes/src/evm/mock.rs @@ -94,9 +94,9 @@ impl Cheatcode for mockCallRevert_1Call { fn mock_call( state: &mut Cheatcodes, callee: &Address, - cdata: &Vec, + cdata: &Bytes, value: Option<&U256>, - rdata: &Vec, + rdata: &Bytes, ret_type: InstructionResult, ) { state.mocked_calls.entry(*callee).or_default().insert( diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index 54335a5e5..3ba07b5b6 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -495,8 +495,8 @@ fn ffi(state: &Cheatcodes, input: &[String]) -> Result { }; Ok(FfiResult { exitCode: output.status.code().unwrap_or(69), - stdout: encoded_stdout, - stderr: output.stderr, + stdout: encoded_stdout.into(), + stderr: output.stderr.into(), }) } @@ -537,6 +537,7 @@ fn prompt( mod tests { use super::*; use crate::CheatsConfig; + use alloy_primitives::Bytes; use std::sync::Arc; fn cheats() -> Cheatcodes { @@ -554,7 +555,7 @@ mod tests { let cheats = cheats(); let args = ["echo".to_string(), hex::encode(msg)]; let output = ffi(&cheats, &args).unwrap(); - assert_eq!(output.stdout, msg); + assert_eq!(output.stdout, Bytes::from(msg)); } #[test] @@ -563,7 +564,7 @@ mod tests { let cheats = cheats(); let args = ["echo".to_string(), msg.to_string()]; let output = ffi(&cheats, &args).unwrap(); - assert_eq!(output.stdout, msg.as_bytes()); + assert_eq!(output.stdout, Bytes::from(msg.as_bytes())); } #[test] diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 934cae0c7..cd2d49ba6 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -15,7 +15,7 @@ use crate::{ CheatsConfig, CheatsCtxt, Error, Result, Vm, Vm::AccountAccess, }; -use alloy_primitives::{Address, Bytes, Log, B256, U256, U64}; +use alloy_primitives::{Address, Bytes, Log, B256, U256}; use alloy_rpc_types::request::{TransactionInput, TransactionRequest}; use alloy_sol_types::{SolInterface, SolValue}; use foundry_common::{evm::Breakpoints, provider::alloy::RpcUrl}; @@ -447,9 +447,9 @@ impl Inspector for Cheatcodes { oldBalance: old_balance, newBalance: old_balance + value, value, - data: vec![], + data: Bytes::new(), reverted: false, - deployedCode: vec![], + deployedCode: Bytes::new(), storageAccesses: vec![], depth: ecx.journaled_state.depth(), }; @@ -554,9 +554,9 @@ impl Inspector for Cheatcodes { oldBalance: balance, newBalance: balance, value: U256::ZERO, - data: vec![], + data: Bytes::new(), reverted: false, - deployedCode: vec![], + deployedCode: Bytes::new(), storageAccesses: vec![], depth: ecx.journaled_state.depth(), }; @@ -710,7 +710,7 @@ impl Inspector for Cheatcodes { if let Some(storage_recorded_logs) = &mut self.recorded_logs { storage_recorded_logs.push(Vm::Log { topics: log.data.topics().to_vec(), - data: log.data.data.to_vec(), + data: log.data.data.clone(), emitter: log.address, }); } @@ -870,9 +870,9 @@ impl Inspector for Cheatcodes { to: Some(call.contract), value: Some(call.transfer.value), input: TransactionInput::new(call.input.clone()), - nonce: Some(U64::from(account.info.nonce)), + nonce: Some(account.info.nonce), gas: if is_fixed_gas_limit { - Some(U256::from(call.gas_limit)) + Some(call.gas_limit as u128) } else { None }, @@ -938,9 +938,9 @@ impl Inspector for Cheatcodes { oldBalance: old_balance, newBalance: U256::ZERO, // updated on call_end value: call.transfer.value, - data: call.input.to_vec(), + data: call.input.clone(), reverted: false, - deployedCode: vec![], + deployedCode: Bytes::new(), storageAccesses: vec![], // updated on step depth: ecx.journaled_state.depth(), }]); @@ -1047,7 +1047,7 @@ impl Inspector for Cheatcodes { // The gas limit of the call. gasLimit: gas.limit(), // The total gas used. - gasTotalUsed: gas.spend(), + gasTotalUsed: gas.spent(), // The amount of gas used for memory expansion. gasMemoryUsed: gas.memory(), // The amount of gas refunded. @@ -1295,9 +1295,9 @@ impl Inspector for Cheatcodes { to, value: Some(call.value), input: TransactionInput::new(bytecode), - nonce: Some(U64::from(nonce)), + nonce: Some(nonce), gas: if is_fixed_gas_limit { - Some(U256::from(call.gas_limit)) + Some(call.gas_limit as u128) } else { None }, @@ -1365,10 +1365,10 @@ impl Inspector for Cheatcodes { oldBalance: U256::ZERO, // updated on create_end newBalance: U256::ZERO, // updated on create_end value: call.value, - data: call.init_code.to_vec(), + data: call.init_code.clone(), reverted: false, - deployedCode: vec![], // updated on create_end - storageAccesses: vec![], // updated on create_end + deployedCode: Bytes::new(), // updated on create_end + storageAccesses: vec![], // updated on create_end depth, }]); } @@ -1468,13 +1468,8 @@ impl Inspector for Cheatcodes { ecx.journaled_state.load_account(address, &mut ecx.db) { create_access.newBalance = created_acc.info.balance; - create_access.deployedCode = created_acc - .info - .code - .clone() - .unwrap_or_default() - .original_bytes() - .into(); + create_access.deployedCode = + created_acc.info.code.clone().unwrap_or_default().original_bytes(); } } } @@ -1563,10 +1558,10 @@ fn apply_create2_deployer( oldBalance: U256::ZERO, // updated on create_end newBalance: U256::ZERO, // updated on create_end value: call.value, - data: calldata, + data: calldata.into(), reverted: false, - deployedCode: vec![], // updated on create_end - storageAccesses: vec![], // updated on create_end + deployedCode: Bytes::new(), // updated on create_end + storageAccesses: vec![], // updated on create_end depth: ecx.journaled_state.depth(), }]) } @@ -1702,8 +1697,8 @@ fn append_storage_access( oldBalance: U256::ZERO, newBalance: U256::ZERO, value: U256::ZERO, - data: vec![], - deployedCode: vec![], + data: Bytes::new(), + deployedCode: Bytes::new(), depth: entry.depth, }; last.push(resume_record); diff --git a/crates/cheatcodes/src/script.rs b/crates/cheatcodes/src/script.rs index ff4e3a148..a28a8be49 100644 --- a/crates/cheatcodes/src/script.rs +++ b/crates/cheatcodes/src/script.rs @@ -2,7 +2,7 @@ use crate::{Cheatcode, CheatsCtxt, DatabaseExt, Result, Vm::*}; use alloy_primitives::{Address, U256}; -use alloy_signer::{LocalWallet, Signer}; +use alloy_signer_wallet::LocalWallet; use foundry_config::Config; use foundry_wallets::{multi_wallet::MultiWallet, WalletSigner}; use parking_lot::Mutex; diff --git a/crates/cheatcodes/src/test/expect.rs b/crates/cheatcodes/src/test/expect.rs index 02c747adb..2fdc99476 100644 --- a/crates/cheatcodes/src/test/expect.rs +++ b/crates/cheatcodes/src/test/expect.rs @@ -25,7 +25,7 @@ const DUMMY_CREATE_ADDRESS: Address = address!("00000000000000000000000000000000 /// This then allows us to customize the matching behavior for each call data on the /// `ExpectedCallData` struct and track how many times we've actually seen the call on the second /// element of the tuple. -pub type ExpectedCallTracker = HashMap, (ExpectedCallData, u64)>>; +pub type ExpectedCallTracker = HashMap>; #[derive(Clone, Debug)] pub struct ExpectedCallData { @@ -318,7 +318,7 @@ impl Cheatcode for expectSafeMemoryCallCall { fn expect_call( state: &mut Cheatcodes, target: &Address, - calldata: &Vec, + calldata: &Bytes, value: Option<&U256>, mut gas: Option, mut min_gas: Option, @@ -351,7 +351,7 @@ fn expect_call( "counted expected calls can only bet set once" ); expecteds.insert( - calldata.to_vec(), + calldata.clone(), (ExpectedCallData { value: value.copied(), gas, min_gas, count, call_type }, 0), ); } diff --git a/crates/cheatcodes/src/utils.rs b/crates/cheatcodes/src/utils.rs index f9edc8e40..7544abc0a 100644 --- a/crates/cheatcodes/src/utils.rs +++ b/crates/cheatcodes/src/utils.rs @@ -2,15 +2,15 @@ use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Result, Vm::*}; use alloy_primitives::{keccak256, Address, B256, U256}; -use alloy_signer::{ +use alloy_signer::{Signer, SignerSync}; +use alloy_signer_wallet::{ coins_bip39::{ ChineseSimplified, ChineseTraditional, Czech, English, French, Italian, Japanese, Korean, Portuguese, Spanish, Wordlist, }, - LocalWallet, MnemonicBuilder, Signer, SignerSync, + LocalWallet, MnemonicBuilder, }; use alloy_sol_types::SolValue; -use foundry_common::types::{ToAlloy, ToEthers}; use foundry_evm_core::{constants::DEFAULT_CREATE2_DEPLOYER, utils::RuntimeOrHandle}; use k256::{ ecdsa::SigningKey, @@ -157,22 +157,22 @@ fn create_wallet(private_key: &U256, label: Option<&str>, state: &mut Cheatcodes .abi_encode()) } -fn encode_vrs(v: u8, r: U256, s: U256) -> Vec { - (U256::from(v), B256::from(r), B256::from(s)).abi_encode() +fn encode_vrs(sig: alloy_primitives::Signature) -> Vec { + let v = sig.v().y_parity_byte_non_eip155().unwrap_or(sig.v().y_parity_byte()); + + (U256::from(v), B256::from(sig.r()), B256::from(sig.s())).abi_encode() } pub(super) 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)?; + let sig = wallet.sign_hash_sync(digest)?; let recovered = sig.recover_address_from_prehash(digest)?; assert_eq!(recovered, wallet.address()); - let v = sig.v().y_parity_byte_non_eip155().unwrap_or(sig.v().y_parity_byte()); - - Ok(encode_vrs(v, sig.r(), sig.s())) + Ok(encode_vrs(sig)) } pub(super) fn sign_with_wallet( @@ -206,10 +206,10 @@ pub(super) fn sign_with_wallet( .block_on(wallet.sign_hash(digest)) .map_err(|err| fmt_err!("{err}"))?; - let recovered = sig.recover(digest.to_ethers()).map_err(|err| fmt_err!("{err}"))?; - assert_eq!(recovered.to_alloy(), signer); + let recovered = sig.recover_address_from_prehash(digest).map_err(|err| fmt_err!("{err}"))?; + assert_eq!(recovered, signer); - Ok(encode_vrs(sig.v as u8, sig.r.to_alloy(), sig.s.to_alloy())) + Ok(encode_vrs(sig)) } pub(super) fn sign_p256(private_key: &U256, digest: &B256, _state: &mut Cheatcodes) -> Result { diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index aad365bb5..fcc95d55e 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -22,9 +22,9 @@ foundry-compilers = { workspace = true, features = ["full"] } alloy-dyn-abi.workspace = true alloy-json-abi.workspace = true alloy-primitives.workspace = true - -ethers-core.workspace = true -ethers-providers.workspace = true +alloy-provider.workspace = true +alloy-transport.workspace = true +alloy-chains.workspace = true clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } color-eyre.workspace = true @@ -41,11 +41,13 @@ tracing-error = "0.2" tracing-subscriber = { workspace = true, features = ["registry", "env-filter", "fmt"] } tracing.workspace = true yansi = "0.5" +hex.workspace = true +futures = "0.3" [dev-dependencies] tempfile = "3.7" [features] default = ["rustls"] -rustls = ["ethers-providers/rustls", "foundry-wallets/rustls"] -openssl = ["ethers-providers/openssl", "foundry-compilers/openssl", "foundry-wallets/openssl"] +rustls = ["foundry-wallets/rustls"] +openssl = ["foundry-compilers/openssl"] diff --git a/crates/cli/src/opts/transaction.rs b/crates/cli/src/opts/transaction.rs index 84173eaaf..81d6107bd 100644 --- a/crates/cli/src/opts/transaction.rs +++ b/crates/cli/src/opts/transaction.rs @@ -1,5 +1,5 @@ use crate::utils::parse_ether_value; -use alloy_primitives::U256; +use alloy_primitives::{U256, U64}; use clap::Parser; use serde::Serialize; @@ -38,7 +38,7 @@ pub struct TransactionOpts { /// Nonce for the transaction. #[arg(long)] - pub nonce: Option, + pub nonce: Option, /// Send a legacy transaction instead of an EIP1559 transaction. /// diff --git a/crates/cli/src/utils/abi.rs b/crates/cli/src/utils/abi.rs new file mode 100644 index 000000000..634b889ca --- /dev/null +++ b/crates/cli/src/utils/abi.rs @@ -0,0 +1,61 @@ +use alloy_chains::Chain; +use alloy_json_abi::Function; +use alloy_primitives::Address; +use alloy_provider::{network::AnyNetwork, Provider}; +use alloy_transport::Transport; +use eyre::{OptionExt, Result}; +use foundry_common::{ + abi::{encode_function_args, get_func, get_func_etherscan}, + ens::NameOrAddress, +}; +use futures::future::join_all; + +async fn resolve_name_args>( + args: &[String], + provider: &P, +) -> Vec { + join_all(args.iter().map(|arg| async { + if arg.contains('.') { + let addr = NameOrAddress::Name(arg.to_string()).resolve(provider).await; + match addr { + Ok(addr) => addr.to_string(), + Err(_) => arg.to_string(), + } + } else { + arg.to_string() + } + })) + .await +} + +pub async fn parse_function_args>( + sig: &str, + args: Vec, + to: Option
, + chain: Chain, + provider: &P, + etherscan_api_key: Option<&str>, +) -> Result<(Vec, Option)> { + if sig.trim().is_empty() { + eyre::bail!("Function signature or calldata must be provided.") + } + + let args = resolve_name_args(&args, provider).await; + + if sig.starts_with("0x") { + return Ok((hex::decode(sig)?, None)); + } + + let func = if sig.contains('(') { + // a regular function signature with parentheses + get_func(sig)? + } else { + let etherscan_api_key = etherscan_api_key.ok_or_eyre( + "If you wish to fetch function data from EtherScan, please provide an API key.", + )?; + let to = to.ok_or_eyre("A 'to' address must be provided to fetch function data.")?; + get_func_etherscan(sig, to, &args, chain, etherscan_api_key).await? + }; + + Ok((encode_function_args(&func, &args)?, Some(func))) +} diff --git a/crates/cli/src/utils/mod.rs b/crates/cli/src/utils/mod.rs index 99fcceb3e..015ce2145 100644 --- a/crates/cli/src/utils/mod.rs +++ b/crates/cli/src/utils/mod.rs @@ -1,14 +1,12 @@ use alloy_json_abi::JsonAbi; -use alloy_primitives::{utils::format_units, U256}; -use ethers_core::types::TransactionReceipt; -use ethers_providers::Middleware; +use alloy_primitives::U256; +use alloy_provider::{network::AnyNetwork, Provider}; +use alloy_transport::Transport; use eyre::{ContextCompat, Result}; -use foundry_common::types::ToAlloy; use foundry_config::{Chain, Config}; use std::{ ffi::OsStr, future::Future, - ops::Mul, path::{Path, PathBuf}, process::{Command, Output, Stdio}, time::{Duration, SystemTime, UNIX_EPOCH}, @@ -23,6 +21,9 @@ pub use cmd::*; mod suggestions; pub use suggestions::*; +mod abi; +pub use abi::*; + // reexport all `foundry_config::utils` #[doc(hidden)] pub use foundry_config::utils::*; @@ -76,28 +77,26 @@ pub fn subscriber() { } pub fn abi_to_solidity(abi: &JsonAbi, name: &str) -> Result { - let s = abi.to_sol(name); + let s = abi.to_sol(name, None); let s = forge_fmt::format(&s)?; Ok(s) } -/// Returns a [RetryProvider](foundry_common::RetryProvider) instantiated using [Config]'s RPC URL -/// and chain. -/// -/// Defaults to `http://localhost:8545` and `Mainnet`. -pub fn get_provider(config: &Config) -> Result { +/// Returns a [RetryProvider](foundry_common::alloy::RetryProvider) instantiated using [Config]'s +/// RPC +pub fn get_provider(config: &Config) -> Result { get_provider_builder(config)?.build() } -/// Returns a [ProviderBuilder](foundry_common::ProviderBuilder) instantiated using [Config]'s RPC -/// URL and chain. +/// Returns a [ProviderBuilder](foundry_common::provider::alloy::ProviderBuilder) instantiated using +/// [Config] values. /// /// Defaults to `http://localhost:8545` and `Mainnet`. pub fn get_provider_builder( config: &Config, -) -> Result { +) -> Result { let url = config.get_rpc_url_or_localhost_http()?; - let mut builder = foundry_common::provider::ethers::ProviderBuilder::new(url.as_ref()); + let mut builder = foundry_common::provider::alloy::ProviderBuilder::new(url.as_ref()); if let Ok(chain) = config.chain.unwrap_or_default().try_into() { builder = builder.chain(chain); @@ -111,14 +110,14 @@ pub fn get_provider_builder( Ok(builder) } -pub async fn get_chain(chain: Option, provider: M) -> Result +pub async fn get_chain(chain: Option, provider: P) -> Result where - M: Middleware, - M::Error: 'static, + P: Provider, + T: Transport + Clone, { match chain { Some(chain) => Ok(chain), - None => Ok(Chain::from_id(provider.get_chainid().await?.as_u64())), + None => Ok(Chain::from_id(provider.get_chain_id().await?)), } } @@ -221,40 +220,6 @@ pub fn enable_paint() { } } -/// Prints parts of the receipt to stdout -pub fn print_receipt(chain: Chain, receipt: &TransactionReceipt) { - let gas_used = receipt.gas_used.unwrap_or_default(); - let gas_price = receipt.effective_gas_price.unwrap_or_default(); - foundry_common::shell::println(format!( - "\n##### {chain}\n{status}Hash: {tx_hash:?}{caddr}\nBlock: {bn}\n{gas}\n", - status = if receipt.status.map_or(true, |s| s.is_zero()) { - "❌ [Failed]" - } else { - "✅ [Success]" - }, - tx_hash = receipt.transaction_hash, - caddr = if let Some(addr) = &receipt.contract_address { - format!("\nContract Address: {}", addr.to_alloy().to_checksum(None)) - } else { - String::new() - }, - bn = receipt.block_number.unwrap_or_default(), - gas = if gas_price.is_zero() { - format!("Gas Used: {gas_used}") - } else { - let paid = format_units(gas_used.mul(gas_price).to_alloy(), 18) - .unwrap_or_else(|_| "N/A".into()); - let gas_price = format_units(gas_price.to_alloy(), 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('.') - ) - }, - )) - .expect("could not print receipt"); -} - /// Useful extensions to [`std::process::Command`]. pub trait CommandUtils { /// Returns the command's output if execution is successful, otherwise, throws an error. diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index d458786f9..5e7eebf82 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -19,21 +19,30 @@ ethers-core.workspace = true ethers-middleware.workspace = true ethers-providers = { workspace = true, features = ["ws", "ipc"] } ethers-signers.workspace = true +# should be removed along with ethers +reqwest_ethers = { package = "reqwest", version = "0.11", default-features = false } alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } alloy-json-abi.workspace = true -alloy-primitives = { workspace = true, features = ["serde", "getrandom", "arbitrary", "rlp"] } +alloy-primitives = { workspace = true, features = [ + "serde", + "getrandom", + "arbitrary", + "rlp", +] } alloy-rpc-types.workspace = true alloy-rpc-client.workspace = true -alloy-providers.workspace = true +alloy-provider.workspace = true alloy-transport.workspace = true -alloy-signer.workspace = true -alloy-transport-http.workspace = true +alloy-signer-wallet.workspace = true +alloy-transport-http = { workspace = true, features = ["reqwest", "reqwest-rustls-tls"] } alloy-transport-ws.workspace = true alloy-transport-ipc.workspace = true alloy-json-rpc.workspace = true alloy-pubsub.workspace = true alloy-sol-types.workspace = true +alloy-contract.workspace = true +alloy-consensus.workspace = true tower.workspace = true @@ -47,7 +56,7 @@ globset = "0.4" hex.workspace = true once_cell = "1" rand.workspace = true -reqwest = { version = "0.11", default-features = false } +reqwest = { version = "0.12", default-features = false } semver = "1" serde_json.workspace = true serde.workspace = true diff --git a/crates/common/src/abi.rs b/crates/common/src/abi.rs index b08620d88..6b7615b39 100644 --- a/crates/common/src/abi.rs +++ b/crates/common/src/abi.rs @@ -159,7 +159,7 @@ pub fn find_source( } /// Helper function to coerce a value to a [DynSolValue] given a type string -fn coerce_value(ty: &str, arg: &str) -> Result { +pub fn coerce_value(ty: &str, arg: &str) -> Result { let ty = DynSolType::parse(ty)?; Ok(DynSolType::coerce_str(&ty, arg)?) } diff --git a/crates/common/src/constants.rs b/crates/common/src/constants.rs index 23ca0abab..0ba0514c2 100644 --- a/crates/common/src/constants.rs +++ b/crates/common/src/constants.rs @@ -38,7 +38,7 @@ pub const ARBITRUM_SENDER: Address = address!("000000000000000000000000000000000 pub const OPTIMISM_SYSTEM_ADDRESS: Address = address!("deaddeaddeaddeaddeaddeaddeaddeaddead0001"); /// Transaction identifier of System transaction types -pub const SYSTEM_TRANSACTION_TYPE: u64 = 126u64; +pub const SYSTEM_TRANSACTION_TYPE: u8 = 126; /// Returns whether the sender is a known L2 system sender that is the first tx in every block. /// diff --git a/crates/common/src/ens.rs b/crates/common/src/ens.rs new file mode 100644 index 000000000..cb1058384 --- /dev/null +++ b/crates/common/src/ens.rs @@ -0,0 +1,193 @@ +//! ENS Name resolving utilities. +#![allow(missing_docs)] +use alloy_primitives::{address, keccak256, Address, B256}; +use alloy_provider::{Network, Provider}; +use alloy_sol_types::sol; +use alloy_transport::Transport; +use async_trait::async_trait; +use std::str::FromStr; + +use self::EnsResolver::EnsResolverInstance; + +// ENS Registry and Resolver contracts. +sol! { + #[sol(rpc)] + // ENS Registry contract. + contract EnsRegistry { + /// Returns the resolver for the specified node. + function resolver(bytes32 node) view returns (address); + } + + #[sol(rpc)] + // ENS Resolver interface. + contract EnsResolver { + // Returns the address associated with the specified node. + function addr(bytes32 node) view returns (address); + + // Returns the name associated with an ENS node, for reverse records. + function name(bytes32 node) view returns (string); + } +} + +/// ENS registry address (`0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e`) +pub const ENS_ADDRESS: Address = address!("00000000000C2E074eC69A0dFb2997BA6C7d2e1e"); + +pub const ENS_REVERSE_REGISTRAR_DOMAIN: &str = "addr.reverse"; + +/// Error type for ENS resolution. +#[derive(Debug, thiserror::Error)] +pub enum EnsResolutionError { + /// Failed to resolve ENS registry. + #[error("Failed to get resolver from ENS registry: {0}")] + EnsRegistryResolutionFailed(String), + /// Failed to resolve ENS name to an address. + #[error("Failed to resolve ENS name to an address: {0}")] + EnsResolutionFailed(String), +} + +/// ENS name or Ethereum Address. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum NameOrAddress { + /// An ENS Name (format does not get checked) + Name(String), + /// An Ethereum Address + Address(Address), +} + +impl NameOrAddress { + /// Resolves the name to an Ethereum Address. + pub async fn resolve>( + &self, + provider: &P, + ) -> Result { + match self { + NameOrAddress::Name(name) => provider.resolve_name(name).await, + NameOrAddress::Address(addr) => Ok(*addr), + } + } +} + +impl From for NameOrAddress { + fn from(name: String) -> Self { + NameOrAddress::Name(name) + } +} + +impl From<&String> for NameOrAddress { + fn from(name: &String) -> Self { + NameOrAddress::Name(name.clone()) + } +} + +impl From
for NameOrAddress { + fn from(addr: Address) -> Self { + NameOrAddress::Address(addr) + } +} + +impl FromStr for NameOrAddress { + type Err =
::Err; + + fn from_str(s: &str) -> Result { + if let Ok(addr) = Address::from_str(s) { + Ok(NameOrAddress::Address(addr)) + } else { + Ok(NameOrAddress::Name(s.to_string())) + } + } +} + +#[async_trait] +pub trait ProviderEnsExt> { + async fn get_resolver(&self) -> Result, EnsResolutionError>; + + async fn resolve_name(&self, name: &str) -> Result { + let node = namehash(name); + let addr = self + .get_resolver() + .await? + .addr(node) + .call() + .await + .map_err(|err| EnsResolutionError::EnsResolutionFailed(err.to_string()))? + ._0; + + Ok(addr) + } + + async fn lookup_address(&self, address: Address) -> Result { + let node = namehash(&reverse_address(address)); + let name = self + .get_resolver() + .await? + .name(node) + .call() + .await + .map_err(|err| EnsResolutionError::EnsResolutionFailed(err.to_string()))? + ._0; + + Ok(name) + } +} + +#[async_trait] +impl ProviderEnsExt for P +where + P: Provider, + N: Network, + T: Transport + Clone, +{ + async fn get_resolver(&self) -> Result, EnsResolutionError> { + let registry = EnsRegistry::new(ENS_ADDRESS, self); + let address = registry + .resolver(namehash("eth")) + .call() + .await + .map_err(|err| EnsResolutionError::EnsRegistryResolutionFailed(err.to_string()))? + ._0; + + Ok(EnsResolverInstance::new(address, self)) + } +} + +/// Returns the ENS namehash as specified in [EIP-137](https://eips.ethereum.org/EIPS/eip-137) +pub fn namehash(name: &str) -> B256 { + if name.is_empty() { + return B256::ZERO + } + + // Remove the variation selector U+FE0F + let name = name.replace('\u{fe0f}', ""); + + // Generate the node starting from the right + name.rsplit('.') + .fold([0u8; 32], |node, label| *keccak256([node, *keccak256(label.as_bytes())].concat())) + .into() +} + +/// Returns the reverse-registrar name of an address. +pub fn reverse_address(addr: Address) -> String { + format!("{addr:?}.{ENS_REVERSE_REGISTRAR_DOMAIN}")[2..].to_string() +} + +#[cfg(test)] +mod test { + use super::*; + + fn assert_hex(hash: B256, val: &str) { + assert_eq!(hash.0.to_vec(), hex::decode(val).unwrap()); + } + + #[test] + fn test_namehash() { + for (name, expected) in &[ + ("", "0000000000000000000000000000000000000000000000000000000000000000"), + ("foo.eth", "de9b09fd7c5f901e23a3f19fecc54828e9c848539801e86591bd9801b019f84f"), + ("eth", "0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae"), + ("alice.eth", "0x787192fc5378cc32aa956ddfdedbf26b24e8d78e40109add0eea2c1a012c3dec"), + ("ret↩️rn.eth", "0x3de5f4c02db61b221e7de7f1c40e29b6e2f07eb48d65bf7e304715cd9ed33b24"), + ] { + assert_hex(namehash(name), expected); + } + } +} diff --git a/crates/common/src/fmt/ui.rs b/crates/common/src/fmt/ui.rs index 5549f36ab..c5bdf6c13 100644 --- a/crates/common/src/fmt/ui.rs +++ b/crates/common/src/fmt/ui.rs @@ -1,8 +1,12 @@ //! Helper trait and functions to format Ethereum types. use crate::TransactionReceiptWithRevertReason; +use alloy_consensus::{AnyReceiptEnvelope, Receipt, ReceiptWithBloom, TxType}; use alloy_primitives::*; -use ethers_core::types::{Block, Log, OtherFields, Transaction, TransactionReceipt, TxHash}; +use alloy_rpc_types::{ + other::OtherFields, AnyTransactionReceipt, Block, BlockTransactions, Log, Transaction, + TransactionReceipt, +}; use serde::Deserialize; /// length of the name column for pretty formatting `{:>20}{value}` @@ -23,6 +27,12 @@ pub trait UIfmt { fn pretty(&self) -> String; } +impl UIfmt for &T { + fn pretty(&self) -> String { + (*self).pretty() + } +} + impl UIfmt for Option { fn pretty(&self) -> String { if let Some(ref inner) = self { @@ -33,7 +43,7 @@ impl UIfmt for Option { } } -impl UIfmt for Vec { +impl UIfmt for [T] { fn pretty(&self) -> String { if !self.is_empty() { let mut s = String::with_capacity(self.len() * 64); @@ -59,13 +69,25 @@ impl UIfmt for String { } } +impl UIfmt for u64 { + fn pretty(&self) -> String { + self.to_string() + } +} + +impl UIfmt for u128 { + fn pretty(&self) -> String { + self.to_string() + } +} + impl UIfmt for bool { fn pretty(&self) -> String { self.to_string() } } -impl UIfmt for U256 { +impl UIfmt for Uint { fn pretty(&self) -> String { self.to_string() } @@ -89,6 +111,12 @@ impl UIfmt for Bloom { } } +impl UIfmt for TxType { + fn pretty(&self) -> String { + (*self as u8).to_string() + } +} + impl UIfmt for Vec { fn pretty(&self) -> String { self[..].pretty() @@ -119,32 +147,34 @@ impl UIfmt for [u8] { } } -impl UIfmt for U64 { - fn pretty(&self) -> String { - self.to_string() - } -} - -impl UIfmt for TransactionReceipt { +impl UIfmt for AnyTransactionReceipt { fn pretty(&self) -> String { let Self { - transaction_hash, - transaction_index, - block_hash, - block_number, - from, - to, - cumulative_gas_used, - gas_used, - contract_address, - logs, - status, - root, - logs_bloom, - transaction_type, - effective_gas_price, + inner: + TransactionReceipt { + transaction_hash, + transaction_index, + block_hash, + block_number, + from, + to, + gas_used, + contract_address, + state_root, + effective_gas_price, + inner: + AnyReceiptEnvelope { + r#type: transaction_type, + inner: + ReceiptWithBloom { + receipt: Receipt { status, cumulative_gas_used, logs }, + logs_bloom, + }, + }, + blob_gas_price, + blob_gas_used, + }, other, - .. } = self; let mut pretty = format!( @@ -162,7 +192,9 @@ root {} status {} transactionHash {} transactionIndex {} -type {}", +type {} +blobGasPrice {} +blobGasUsed {}", block_hash.pretty(), block_number.pretty(), contract_address.pretty(), @@ -170,13 +202,15 @@ type {}", effective_gas_price.pretty(), from.pretty(), gas_used.pretty(), - serde_json::to_string(logs).unwrap(), + serde_json::to_string(&logs).unwrap(), logs_bloom.pretty(), - root.pretty(), + state_root.pretty(), status.pretty(), transaction_hash.pretty(), transaction_index.pretty(), - transaction_type.pretty() + transaction_type, + blob_gas_price.pretty(), + blob_gas_used.pretty(), ); if let Some(to) = to { @@ -205,40 +239,38 @@ removed: {} topics: {} transactionHash: {} transactionIndex: {}", - self.address.pretty(), + self.address().pretty(), self.block_hash.pretty(), self.block_number.pretty(), - self.data.pretty(), + self.data().data.pretty(), self.log_index.pretty(), self.removed.pretty(), - self.topics.pretty(), + self.topics().pretty(), self.transaction_hash.pretty(), self.transaction_index.pretty(), ) } } -impl UIfmt for Block { +impl UIfmt for Block { fn pretty(&self) -> String { format!( " {} -transactions {}", +transactions: {}", pretty_block_basics(self), self.transactions.pretty() ) } } -impl UIfmt for Block { +impl UIfmt for BlockTransactions { fn pretty(&self) -> String { - format!( - " -{} -transactions: {}", - pretty_block_basics(self), - self.transactions.pretty() - ) + match self { + BlockTransactions::Hashes(hashes) => hashes.pretty(), + BlockTransactions::Full(transactions) => transactions.pretty(), + BlockTransactions::Uncle => String::new(), + } } } @@ -285,12 +317,12 @@ value {}{}", self.gas_price.pretty(), self.hash.pretty(), self.input.pretty(), - self.nonce.pretty(), - to_bytes(self.r).pretty(), - to_bytes(self.s).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.transaction_index.pretty(), - self.v.pretty(), + self.signature.map(|s| s.v).pretty(), self.value.pretty(), self.other.pretty() ) @@ -367,13 +399,6 @@ mod temp_ethers { with_alloy!(Address, Bloom, H64, H256, I256, U256, U64); } -/// Convert a U256 to bytes -pub fn to_bytes(uint: ethers_core::types::U256) -> [u8; 32] { - let mut buffer: [u8; 32] = [0; 32]; - uint.to_big_endian(&mut buffer); - buffer -} - /// Returns the `UiFmt::pretty()` formatted attribute of the transactions pub fn get_pretty_tx_attr(transaction: &Transaction, attr: &str) -> Option { match attr { @@ -384,12 +409,12 @@ pub fn get_pretty_tx_attr(transaction: &Transaction, attr: &str) -> Option Some(transaction.gas_price.pretty()), "hash" => Some(transaction.hash.pretty()), "input" => Some(transaction.input.pretty()), - "nonce" => Some(transaction.nonce.pretty()), - "s" => Some(to_bytes(transaction.s).pretty()), - "r" => Some(to_bytes(transaction.r).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()), "transactionIndex" | "transaction_index" => Some(transaction.transaction_index.pretty()), - "v" => Some(transaction.v.pretty()), + "v" => transaction.signature.map(|s| s.v.pretty()), "value" => Some(transaction.value.pretty()), other => { if let Some(value) = transaction.other.get(other) { @@ -410,49 +435,50 @@ pub fn get_pretty_tx_receipt_attr( "blockNumber" | "block_number" => Some(receipt.receipt.block_number.pretty()), "contractAddress" | "contract_address" => Some(receipt.receipt.contract_address.pretty()), "cumulativeGasUsed" | "cumulative_gas_used" => { - Some(receipt.receipt.cumulative_gas_used.pretty()) + Some(receipt.receipt.inner.inner.inner.receipt.cumulative_gas_used.pretty()) } "effectiveGasPrice" | "effective_gas_price" => { - Some(receipt.receipt.effective_gas_price.pretty()) + Some(receipt.receipt.effective_gas_price.to_string()) + } + "gasUsed" | "gas_used" => Some(receipt.receipt.gas_used.to_string()), + "logs" => Some(receipt.receipt.inner.inner.inner.receipt.logs.as_slice().pretty()), + "logsBloom" | "logs_bloom" => Some(receipt.receipt.inner.inner.inner.logs_bloom.pretty()), + "root" | "stateRoot" | "state_root " => Some(receipt.receipt.state_root.pretty()), + "status" | "statusCode" | "status_code" => { + Some(receipt.receipt.inner.inner.inner.receipt.status.pretty()) } - "gasUsed" | "gas_used" => Some(receipt.receipt.gas_used.pretty()), - "logs" => Some(receipt.receipt.logs.pretty()), - "logsBloom" | "logs_bloom" => Some(receipt.receipt.logs_bloom.pretty()), - "root" => Some(receipt.receipt.root.pretty()), - "status" => Some(receipt.receipt.status.pretty()), "transactionHash" | "transaction_hash" => Some(receipt.receipt.transaction_hash.pretty()), "transactionIndex" | "transaction_index" => { - Some(receipt.receipt.transaction_index.pretty()) + Some(receipt.receipt.transaction_index.to_string()) } - "type" | "transaction_type" => Some(receipt.receipt.transaction_type.pretty()), + "type" | "transaction_type" => Some(receipt.receipt.inner.inner.r#type.to_string()), "revertReason" | "revert_reason" => Some(receipt.revert_reason.pretty()), _ => None, } } /// Returns the `UiFmt::pretty()` formatted attribute of the given block -pub fn get_pretty_block_attr(block: &Block, attr: &str) -> Option { +pub fn get_pretty_block_attr(block: &Block, attr: &str) -> Option { match attr { - "baseFeePerGas" | "base_fee_per_gas" => Some(block.base_fee_per_gas.pretty()), - "difficulty" => Some(block.difficulty.pretty()), - "extraData" | "extra_data" => Some(block.extra_data.pretty()), - "gasLimit" | "gas_limit" => Some(block.gas_limit.pretty()), - "gasUsed" | "gas_used" => Some(block.gas_used.pretty()), - "hash" => Some(block.hash.pretty()), - "logsBloom" | "logs_bloom" => Some(block.logs_bloom.pretty()), - "miner" | "author" => Some(block.author.pretty()), - "mixHash" | "mix_hash" => Some(block.mix_hash.pretty()), - "nonce" => Some(block.nonce.pretty()), - "number" => Some(block.number.pretty()), - "parentHash" | "parent_hash" => Some(block.parent_hash.pretty()), - "transactionsRoot" | "transactions_root" => Some(block.transactions_root.pretty()), - "receiptsRoot" | "receipts_root" => Some(block.receipts_root.pretty()), - "sealFields" | "seal_fields" => Some(block.seal_fields.pretty()), - "sha3Uncles" | "sha_3_uncles" => Some(block.uncles_hash.pretty()), + "baseFeePerGas" | "base_fee_per_gas" => Some(block.header.base_fee_per_gas.pretty()), + "difficulty" => Some(block.header.difficulty.pretty()), + "extraData" | "extra_data" => Some(block.header.extra_data.pretty()), + "gasLimit" | "gas_limit" => Some(block.header.gas_limit.pretty()), + "gasUsed" | "gas_used" => 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()), + "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()), - "stateRoot" | "state_root" => Some(block.state_root.pretty()), - "timestamp" => Some(block.timestamp.pretty()), - "totalDifficulty" | "total_difficult" => Some(block.total_difficulty.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()), other => { if let Some(value) = block.other.get(other) { let val = EthValue::from(value.clone()); @@ -463,7 +489,7 @@ pub fn get_pretty_block_attr(block: &Block, attr: &str) -> Option(block: &Block) -> String { +fn pretty_block_basics(block: &Block) -> String { format!( " baseFeePerGas {} @@ -480,34 +506,32 @@ number {} parentHash {} transactionsRoot {} receiptsRoot {} -sealFields {} sha3Uncles {} size {} stateRoot {} timestamp {} withdrawalsRoot {} totalDifficulty {}{}", - block.base_fee_per_gas.pretty(), - block.difficulty.pretty(), - block.extra_data.pretty(), - block.gas_limit.pretty(), - block.gas_used.pretty(), - block.hash.pretty(), - block.logs_bloom.pretty(), - block.author.pretty(), - block.mix_hash.pretty(), - block.nonce.pretty(), - block.number.pretty(), - block.parent_hash.pretty(), - block.transactions_root.pretty(), - block.receipts_root.pretty(), - block.seal_fields.pretty(), - block.uncles_hash.pretty(), + block.header.base_fee_per_gas.pretty(), + block.header.difficulty.pretty(), + block.header.extra_data.pretty(), + block.header.gas_limit.pretty(), + block.header.gas_used.pretty(), + block.header.hash.pretty(), + block.header.logs_bloom.pretty(), + block.header.miner.pretty(), + block.header.mix_hash.pretty(), + block.header.nonce.pretty(), + block.header.number.pretty(), + block.header.parent_hash.pretty(), + block.header.transactions_root.pretty(), + block.header.receipts_root.pretty(), + block.header.uncles_hash.pretty(), block.size.pretty(), - block.state_root.pretty(), - block.timestamp.pretty(), - block.withdrawals_root.pretty(), - block.total_difficulty.pretty(), + block.header.state_root.pretty(), + block.header.timestamp.pretty(), + block.header.withdrawals_root.pretty(), + block.header.total_difficulty.pretty(), block.other.pretty() ) } @@ -594,7 +618,7 @@ txType 0 #[test] fn print_block_w_txs() { 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 output ="\nblockHash 0xda53da08ef6a3cbde84c33e51c04f68c3853b6a3731f10baa2324968eee63972 blockNumber 3 from 0xFdCeDC3bFca10eCb0890337fbdD1977aba84807a @@ -609,7 +633,11 @@ to 0xdca8ce283150AB773BCbeB8d38289bdB5661dE1e transactionIndex 0 v 37 value 0".to_string(); - let generated = block.transactions[0].pretty(); + let txs = match block.transactions { + BlockTransactions::Full(txs) => txs, + _ => panic!("not full transactions"), + }; + let generated = txs[0].pretty(); assert_eq!(generated.as_str(), output.as_str()); } @@ -657,45 +685,40 @@ 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(); - assert_eq!(None, get_pretty_tx_attr(&block.transactions[0], "")); - assert_eq!( - Some("3".to_string()), - get_pretty_tx_attr(&block.transactions[0], "blockNumber") - ); + 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!( Some("0xFdCeDC3bFca10eCb0890337fbdD1977aba84807a".to_string()), - get_pretty_tx_attr(&block.transactions[0], "from") - ); - assert_eq!(Some("90000".to_string()), get_pretty_tx_attr(&block.transactions[0], "gas")); - assert_eq!( - Some("20000000000".to_string()), - get_pretty_tx_attr(&block.transactions[0], "gasPrice") + get_pretty_tx_attr(&txs[0], "from") ); + assert_eq!(Some("90000".to_string()), get_pretty_tx_attr(&txs[0], "gas")); + assert_eq!(Some("20000000000".to_string()), get_pretty_tx_attr(&txs[0], "gasPrice")); assert_eq!( Some("0xc3c5f700243de37ae986082fd2af88d2a7c2752a0c0f7b9d6ac47c729d45e067".to_string()), - get_pretty_tx_attr(&block.transactions[0], "hash") + get_pretty_tx_attr(&txs[0], "hash") ); - assert_eq!(Some("0x".to_string()), get_pretty_tx_attr(&block.transactions[0], "input")); - assert_eq!(Some("2".to_string()), get_pretty_tx_attr(&block.transactions[0], "nonce")); + assert_eq!(Some("0x".to_string()), get_pretty_tx_attr(&txs[0], "input")); + assert_eq!(Some("2".to_string()), get_pretty_tx_attr(&txs[0], "nonce")); assert_eq!( Some("0x19f2694eb9113656dbea0b925e2e7ceb43df83e601c4116aee9c0dd99130be88".to_string()), - get_pretty_tx_attr(&block.transactions[0], "r") + get_pretty_tx_attr(&txs[0], "r") ); assert_eq!( Some("0x73e5764b324a4f7679d890a198ba658ba1c8cd36983ff9797e10b1b89dbb448e".to_string()), - get_pretty_tx_attr(&block.transactions[0], "s") + get_pretty_tx_attr(&txs[0], "s") ); assert_eq!( Some("0xdca8ce283150AB773BCbeB8d38289bdB5661dE1e".into()), - get_pretty_tx_attr(&block.transactions[0], "to") - ); - assert_eq!( - Some("0".to_string()), - get_pretty_tx_attr(&block.transactions[0], "transactionIndex") + get_pretty_tx_attr(&txs[0], "to") ); - assert_eq!(Some("37".to_string()), get_pretty_tx_attr(&block.transactions[0], "v")); - assert_eq!(Some("0".to_string()), get_pretty_tx_attr(&block.transactions[0], "value")); + 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("0".to_string()), get_pretty_tx_attr(&txs[0], "value")); } #[test] @@ -731,7 +754,7 @@ value 0".to_string(); } ); - let block: Block<()> = serde_json::from_value(json).unwrap(); + let block: Block = 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/lib.rs b/crates/common/src/lib.rs index c2825ac2f..36298ae8f 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -12,6 +12,7 @@ pub mod calc; pub mod compile; pub mod constants; pub mod contracts; +pub mod ens; pub mod errors; pub mod evm; pub mod fmt; diff --git a/crates/common/src/provider/alloy.rs b/crates/common/src/provider/alloy.rs index 54c9085a1..e76a898bd 100644 --- a/crates/common/src/provider/alloy.rs +++ b/crates/common/src/provider/alloy.rs @@ -3,9 +3,12 @@ use crate::{ provider::runtime_transport::RuntimeTransportBuilder, ALCHEMY_FREE_TIER_CUPS, REQUEST_TIMEOUT, }; -use alloy_primitives::U256; -use alloy_providers::tmp::{Provider, TempProvider}; +use alloy_provider::{ + network::AnyNetwork, utils::Eip1559Estimation, Provider, + ProviderBuilder as AlloyProviderBuilder, RootProvider, +}; use alloy_rpc_client::ClientBuilder; +use alloy_transport::Transport; use ethers_middleware::gas_oracle::{GasCategory, GasOracle, Polygon}; use eyre::{Result, WrapErr}; use foundry_common::types::ToAlloy; @@ -25,7 +28,7 @@ use super::{ }; /// Helper type alias for a retry provider -pub type RetryProvider = Provider>; +pub type RetryProvider = RootProvider, N>; /// Helper type alias for a rpc url pub type RpcUrl = String; @@ -239,8 +242,10 @@ impl ProviderBuilder { .build(); let client = ClientBuilder::default().layer(retry_layer).transport(transport, false); - // todo: provider polling interval - Ok(Provider::new_with_client(client)) + let provider = AlloyProviderBuilder::<_, _, AnyNetwork>::default() + .on_provider(RootProvider::new(client)); + + Ok(provider) } } @@ -250,14 +255,14 @@ impl ProviderBuilder { /// - polygon /// /// Fallback is the default [`Provider::estimate_eip1559_fees`] implementation -pub async fn estimate_eip1559_fees( +pub async fn estimate_eip1559_fees, T: Transport + Clone>( provider: &P, chain: Option, -) -> Result<(U256, U256)> { +) -> Result { let chain = if let Some(chain) = chain { chain } else { - provider.get_chain_id().await.wrap_err("Failed to get chain id")?.to() + provider.get_chain_id().await.wrap_err("Failed to get chain id")? }; if let Ok(chain) = NamedChain::try_from(chain) { @@ -272,7 +277,12 @@ pub async fn estimate_eip1559_fees( }; let estimator = Polygon::new(chain)?.category(GasCategory::Standard); let (a, b) = estimator.estimate_eip1559_fees().await?; - return Ok((a.to_alloy(), b.to_alloy())); + + let estimation = Eip1559Estimation { + max_fee_per_gas: a.to_alloy().to(), + max_priority_fee_per_gas: b.to_alloy().to(), + }; + return Ok(estimation) } _ => {} } diff --git a/crates/common/src/provider/ethers.rs b/crates/common/src/provider/ethers.rs index 0d4acd2ce..7d99763de 100644 --- a/crates/common/src/provider/ethers.rs +++ b/crates/common/src/provider/ethers.rs @@ -4,8 +4,6 @@ use crate::{ runtime_client::{RuntimeClient, RuntimeClientBuilder}, ALCHEMY_FREE_TIER_CUPS, REQUEST_TIMEOUT, }; -use ethers_core::types::U256; -use ethers_middleware::gas_oracle::{GasCategory, GasOracle, Polygon}; use ethers_providers::{is_local_endpoint, Middleware, Provider, DEFAULT_LOCAL_POLL_INTERVAL}; use eyre::{Result, WrapErr}; use foundry_config::NamedChain; @@ -251,44 +249,6 @@ impl ProviderBuilder { } } -/// Estimates EIP1559 fees depending on the chain -/// -/// Uses custom gas oracles for -/// - polygon -/// -/// Fallback is the default [`Provider::estimate_eip1559_fees`] implementation -pub async fn estimate_eip1559_fees( - provider: &M, - chain: Option, -) -> Result<(U256, U256)> -where - M::Error: 'static, -{ - let chain = if let Some(chain) = chain { - chain - } else { - provider.get_chainid().await.wrap_err("Failed to get chain id")?.as_u64() - }; - - if let Ok(chain) = NamedChain::try_from(chain) { - // handle chains that deviate from `eth_feeHistory` and have their own oracle - match chain { - NamedChain::Polygon | NamedChain::PolygonMumbai => { - // TODO: phase this out somehow - let chain = match chain { - NamedChain::Polygon => ethers_core::types::Chain::Polygon, - NamedChain::PolygonMumbai => ethers_core::types::Chain::PolygonMumbai, - _ => unreachable!(), - }; - let estimator = Polygon::new(chain)?.category(GasCategory::Standard); - return Ok(estimator.estimate_eip1559_fees().await?); - } - _ => {} - } - } - provider.estimate_eip1559_fees(None).await.wrap_err("Failed fetch EIP1559 fees") -} - #[cfg(not(windows))] fn resolve_path(path: &Path) -> Result { if path.is_absolute() { diff --git a/crates/common/src/provider/retry.rs b/crates/common/src/provider/retry.rs index e7277bd4b..b6adfb646 100644 --- a/crates/common/src/provider/retry.rs +++ b/crates/common/src/provider/retry.rs @@ -49,6 +49,9 @@ impl RetryPolicy for RateLimitRetryPolicy { false } TransportError::ErrorResp(err) => should_retry_json_rpc_error(err), + TransportError::NullResp => true, + TransportError::UnsupportedFeature(_) => false, + TransportError::LocalUsageError(_) => false, } } diff --git a/crates/common/src/runtime_client.rs b/crates/common/src/runtime_client.rs index ea0fe5938..3bb1631d5 100644 --- a/crates/common/src/runtime_client.rs +++ b/crates/common/src/runtime_client.rs @@ -7,7 +7,7 @@ use ethers_providers::{ JsonRpcError, JwtAuth, JwtKey, ProviderError, PubsubClient, RetryClient, RetryClientBuilder, RpcError, Ws, }; -use reqwest::{ +use reqwest_ethers::{ header::{HeaderName, HeaderValue}, Url, }; @@ -128,10 +128,10 @@ impl RuntimeClient { async fn connect(&self) -> Result { match self.url.scheme() { "http" | "https" => { - let mut client_builder = reqwest::Client::builder() + let mut client_builder = reqwest_ethers::Client::builder() .timeout(self.timeout) .tls_built_in_root_certs(self.url.scheme() == "https"); - let mut headers = reqwest::header::HeaderMap::new(); + let mut headers = reqwest_ethers::header::HeaderMap::new(); if let Some(jwt) = self.jwt.as_ref() { let auth = build_auth(jwt.clone()).map_err(|err| { @@ -144,7 +144,7 @@ impl RuntimeClient { .expect("Header should be valid string"); auth_value.set_sensitive(true); - headers.insert(reqwest::header::AUTHORIZATION, auth_value); + headers.insert(reqwest_ethers::header::AUTHORIZATION, auth_value); }; for header in self.headers.iter() { diff --git a/crates/common/src/transactions.rs b/crates/common/src/transactions.rs index b337147d6..cf3f5a89a 100644 --- a/crates/common/src/transactions.rs +++ b/crates/common/src/transactions.rs @@ -1,15 +1,16 @@ //! wrappers for transactions -use ethers_core::types::{BlockId, TransactionReceipt}; -use ethers_providers::Middleware; +use alloy_provider::{network::AnyNetwork, Provider}; +use alloy_rpc_types::{AnyTransactionReceipt, BlockId, WithOtherFields}; +use alloy_transport::Transport; use eyre::Result; use serde::{Deserialize, Serialize}; /// Helper type to carry a transaction along with an optional revert reason -#[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct TransactionReceiptWithRevertReason { /// The underlying transaction receipt #[serde(flatten)] - pub receipt: TransactionReceipt, + pub receipt: AnyTransactionReceipt, /// The revert reason string if the transaction status is failed #[serde(skip_serializing_if = "Option::is_none", rename = "revertReason")] @@ -18,68 +19,67 @@ pub struct TransactionReceiptWithRevertReason { impl TransactionReceiptWithRevertReason { /// Returns if the status of the transaction is 0 (failure) - pub fn is_failure(&self) -> Option { - self.receipt.status.map(|status| status.as_u64() == 0) + pub fn is_failure(&self) -> bool { + !self.receipt.inner.inner.inner.receipt.status } /// Updates the revert reason field using `eth_call` and returns an Err variant if the revert /// reason was not successfully updated - pub async fn update_revert_reason(&mut self, provider: &M) -> Result<()> { + pub async fn update_revert_reason>( + &mut self, + provider: &P, + ) -> Result<()> { self.revert_reason = self.fetch_revert_reason(provider).await?; Ok(()) } - async fn fetch_revert_reason(&self, provider: &M) -> Result> { - if let Some(false) | None = self.is_failure() { + async fn fetch_revert_reason>( + &self, + provider: &P, + ) -> Result> { + if !self.is_failure() { return Ok(None) } - if let Some(ref transaction) = provider - .get_transaction(self.receipt.transaction_hash) + let transaction = provider + .get_transaction_by_hash(self.receipt.transaction_hash) .await - .map_err(|_| eyre::eyre!("unable to fetch transaction"))? - { - if let Some(block_hash) = self.receipt.block_hash { - match provider.call(&transaction.into(), Some(BlockId::Hash(block_hash))).await { - Err(e) => return Ok(extract_revert_reason(e.to_string())), - Ok(_) => eyre::bail!("no revert reason as transaction succeeded"), - } + .map_err(|_| eyre::eyre!("unable to fetch transaction"))?; + + if let Some(block_hash) = self.receipt.block_hash { + match provider + .call( + &WithOtherFields::new(transaction.inner.into()), + Some(BlockId::Hash(block_hash.into())), + ) + .await + { + Err(e) => return Ok(extract_revert_reason(e.to_string())), + Ok(_) => eyre::bail!("no revert reason as transaction succeeded"), } - eyre::bail!("unable to fetch block_hash") } - Err(eyre::eyre!("transaction does not exist")) + eyre::bail!("unable to fetch block_hash") } } -impl From for TransactionReceiptWithRevertReason { - fn from(receipt: TransactionReceipt) -> Self { +impl From for TransactionReceiptWithRevertReason { + fn from(receipt: AnyTransactionReceipt) -> Self { Self { receipt, revert_reason: None } } } -impl From for TransactionReceipt { +impl From for AnyTransactionReceipt { fn from(receipt_with_reason: TransactionReceiptWithRevertReason) -> Self { receipt_with_reason.receipt } } fn extract_revert_reason>(error_string: S) -> Option { - let message_substr = "message: execution reverted: "; - - let mut temp = ""; - + let message_substr = "execution reverted: "; error_string .as_ref() .find(message_substr) - .and_then(|index| { - let (_, rest) = error_string.as_ref().split_at(index + message_substr.len()); - temp = rest; - rest.rfind(", ") - }) - .map(|index| { - let (reason, _) = temp.split_at(index); - reason.to_string() - }) + .map(|index| error_string.as_ref().split_at(index + message_substr.len()).1.to_string()) } #[cfg(test)] @@ -88,16 +88,10 @@ mod tests { #[test] fn test_extract_revert_reason() { - let error_string_1 = "(code: 3, message: execution reverted: Transaction too old, data: Some(String(\"0x08c379a0\")))"; - let error_string_2 = "(code: 3, message: execution reverted: missing data: amountIn, amountOut, data: Some(String(\"0x08c379a0\")))"; - let error_string_3 = - "(code: 4, message: invalid signature, data: Some(String(\"0x08c379a0\")))"; + let error_string_1 = "server returned an error response: error code 3: execution reverted: Transaction too old"; + let error_string_2 = "server returned an error response: error code 3: Invalid signature"; assert_eq!(extract_revert_reason(error_string_1), Some("Transaction too old".to_string())); - assert_eq!( - extract_revert_reason(error_string_2), - Some("missing data: amountIn, amountOut".to_string()) - ); - assert_eq!(extract_revert_reason(error_string_3), None); + assert_eq!(extract_revert_reason(error_string_2), None); } } diff --git a/crates/common/src/types.rs b/crates/common/src/types.rs index 6f4284b32..bcfed539f 100644 --- a/crates/common/src/types.rs +++ b/crates/common/src/types.rs @@ -1,18 +1,14 @@ //! Temporary utility conversion traits between ethers-rs and alloy types. -use alloy_primitives::{Address, Bloom, Bytes, B256, B64, I256, U128, U256, U64}; -use alloy_rpc_types::{ - other::OtherFields, - request::{TransactionInput, TransactionRequest as CallRequest}, - AccessList, AccessListItem, Signature, Transaction, -}; -use alloy_signer::{LocalWallet, Signer}; +use alloy_primitives::{Address, Bloom, Bytes, B256, B64, I256, U256, U64}; +use alloy_rpc_types::{AccessList, AccessListItem, BlockNumberOrTag}; +use alloy_signer_wallet::LocalWallet; use ethers_core::types::{ transaction::eip2930::{ AccessList as EthersAccessList, AccessListItem as EthersAccessListItem, }, - Bloom as EthersBloom, Bytes as EthersBytes, TransactionRequest, H160, H256, H64, - I256 as EthersI256, U256 as EthersU256, U64 as EthersU64, + BlockNumber, Bloom as EthersBloom, Bytes as EthersBytes, H160, H256, H64, I256 as EthersI256, + U256 as EthersU256, U64 as EthersU64, }; /// Conversion trait to easily convert from Ethers types to Alloy types. @@ -105,43 +101,7 @@ impl ToAlloy for u64 { } } -impl ToAlloy for ethers_core::types::Transaction { - type To = Transaction; - - fn to_alloy(self) -> Self::To { - Transaction { - hash: self.hash.to_alloy(), - nonce: U64::from(self.nonce.as_u64()), - block_hash: self.block_hash.map(ToAlloy::to_alloy), - block_number: self.block_number.map(|b| U256::from(b.as_u64())), - transaction_index: self.transaction_index.map(|b| U256::from(b.as_u64())), - from: self.from.to_alloy(), - to: self.to.map(ToAlloy::to_alloy), - value: self.value.to_alloy(), - gas_price: self.gas_price.map(|a| U128::from(a.as_u128())), - gas: self.gas.to_alloy(), - max_fee_per_gas: self.max_fee_per_gas.map(|f| U128::from(f.as_u128())), - max_priority_fee_per_gas: self - .max_priority_fee_per_gas - .map(|f| U128::from(f.as_u128())), - max_fee_per_blob_gas: None, - input: self.input.0.into(), - signature: Some(Signature { - r: self.r.to_alloy(), - s: self.s.to_alloy(), - v: U256::from(self.v.as_u64()), - y_parity: None, - }), - chain_id: self.chain_id.map(|c| U64::from(c.as_u64())), - blob_versioned_hashes: Vec::new(), - access_list: self.access_list.map(|a| a.0.into_iter().map(ToAlloy::to_alloy).collect()), - transaction_type: self.transaction_type.map(|t| t.to_alloy()), - other: Default::default(), - } - } -} - -impl ToEthers for alloy_signer::LocalWallet { +impl ToEthers for alloy_signer_wallet::LocalWallet { type To = ethers_signers::LocalWallet; fn to_ethers(self) -> Self::To { @@ -161,34 +121,6 @@ impl ToEthers for Vec { } } -/// Converts from a [TransactionRequest] to a [CallRequest]. -pub fn to_call_request_from_tx_request(tx: TransactionRequest) -> CallRequest { - CallRequest { - from: tx.from.map(|f| f.to_alloy()), - to: match tx.to { - Some(to) => match to { - ethers_core::types::NameOrAddress::Address(addr) => Some(addr.to_alloy()), - ethers_core::types::NameOrAddress::Name(_) => None, - }, - None => None, - }, - gas_price: tx.gas_price.map(|g| g.to_alloy()), - max_fee_per_gas: None, - max_priority_fee_per_gas: None, - gas: tx.gas.map(|g| g.to_alloy()), - value: tx.value.map(|v| v.to_alloy()), - input: TransactionInput::maybe_input(tx.data.map(|b| b.0.into())), - nonce: tx.nonce.map(|n| U64::from(n.as_u64())), - chain_id: tx.chain_id.map(|c| c.to_alloy()), - access_list: None, - max_fee_per_blob_gas: None, - blob_versioned_hashes: None, - transaction_type: None, - sidecar: None, - other: OtherFields::default(), - } -} - impl ToAlloy for EthersAccessList { type To = AccessList; fn to_alloy(self) -> Self::To { @@ -260,3 +192,19 @@ impl ToEthers for Bytes { EthersBytes(self.0) } } + +impl ToEthers for BlockNumberOrTag { + type To = BlockNumber; + + #[inline(always)] + fn to_ethers(self) -> Self::To { + match self { + BlockNumberOrTag::Number(n) => BlockNumber::Number(n.into()), + BlockNumberOrTag::Earliest => BlockNumber::Earliest, + BlockNumberOrTag::Latest => BlockNumber::Latest, + BlockNumberOrTag::Pending => BlockNumber::Pending, + BlockNumberOrTag::Finalized => BlockNumber::Finalized, + BlockNumberOrTag::Safe => BlockNumber::Safe, + } + } +} diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml index 7e43c2276..da1371c75 100644 --- a/crates/config/Cargo.toml +++ b/crates/config/Cargo.toml @@ -36,7 +36,7 @@ serde_regex = "1" serde.workspace = true thiserror = "1" toml = { version = "0.8", features = ["preserve_order"] } -toml_edit = "0.21" +toml_edit = "0.22.4" tracing.workspace = true walkdir = "2" diff --git a/crates/config/src/fix.rs b/crates/config/src/fix.rs index a0ce9fbc1..dc43fd255 100644 --- a/crates/config/src/fix.rs +++ b/crates/config/src/fix.rs @@ -10,7 +10,7 @@ use std::{ /// A convenience wrapper around a TOML document and the path it was read from struct TomlFile { - doc: toml_edit::Document, + doc: toml_edit::DocumentMut, path: PathBuf, } @@ -21,11 +21,11 @@ impl TomlFile { Ok(Self { doc, path }) } - fn doc(&self) -> &toml_edit::Document { + fn doc(&self) -> &toml_edit::DocumentMut { &self.doc } - fn doc_mut(&mut self) -> &mut toml_edit::Document { + fn doc_mut(&mut self) -> &mut toml_edit::DocumentMut { &mut self.doc } @@ -39,7 +39,7 @@ impl TomlFile { } impl Deref for TomlFile { - type Target = toml_edit::Document; + type Target = toml_edit::DocumentMut; fn deref(&self) -> &Self::Target { self.doc() } diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 88671dce1..51769ab31 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -1222,7 +1222,7 @@ impl Config { /// [Self::get_config_path()] and if the closure returns `true`. pub fn update_at(root: impl Into, f: F) -> eyre::Result<()> where - F: FnOnce(&Config, &mut toml_edit::Document) -> bool, + F: FnOnce(&Config, &mut toml_edit::DocumentMut) -> bool, { let config = Self::load_with_root(root).sanitized(); config.update(|doc| f(&config, doc)) @@ -1234,14 +1234,14 @@ impl Config { /// [Self::get_config_path()] and if the closure returns `true` pub fn update(&self, f: F) -> eyre::Result<()> where - F: FnOnce(&mut toml_edit::Document) -> bool, + F: FnOnce(&mut toml_edit::DocumentMut) -> bool, { let file_path = self.get_config_path(); if !file_path.exists() { return Ok(()) } let contents = fs::read_to_string(&file_path)?; - let mut doc = contents.parse::()?; + let mut doc = contents.parse::()?; if f(&mut doc) { fs::write(file_path, doc.to_string())?; } diff --git a/crates/config/src/utils.rs b/crates/config/src/utils.rs index d0d6f8e06..cb2350008 100644 --- a/crates/config/src/utils.rs +++ b/crates/config/src/utils.rs @@ -14,7 +14,7 @@ use std::{ path::{Path, PathBuf}, str::FromStr, }; -use toml_edit::{Document, Item}; +use toml_edit::{DocumentMut, Item}; /// Loads the config for the current project workspace pub fn load_config() -> Config { @@ -216,9 +216,9 @@ pub fn get_available_profiles(toml_path: impl AsRef) -> eyre::Result) -> eyre::Result { +fn read_toml(path: impl AsRef) -> eyre::Result { let path = path.as_ref().to_owned(); - let doc: Document = std::fs::read_to_string(path)?.parse()?; + let doc: DocumentMut = std::fs::read_to_string(path)?.parse()?; Ok(doc) } diff --git a/crates/evm/core/Cargo.toml b/crates/evm/core/Cargo.toml index 7e4ea3c21..cf922cbce 100644 --- a/crates/evm/core/Cargo.toml +++ b/crates/evm/core/Cargo.toml @@ -19,9 +19,15 @@ foundry-macros.workspace = true alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } alloy-json-abi.workspace = true -alloy-primitives = { workspace = true, features = ["serde", "getrandom", "arbitrary", "rlp"] } +alloy-primitives = { workspace = true, features = [ + "serde", + "getrandom", + "arbitrary", + "rlp", +] } alloy-genesis.workspace = true -alloy-providers.workspace = true +alloy-provider.workspace = true +alloy-transport.workspace = true alloy-rpc-types.workspace = true alloy-sol-types.workspace = true diff --git a/crates/evm/core/src/backend/cow.rs b/crates/evm/core/src/backend/cow.rs index c0ff88ea4..59410541a 100644 --- a/crates/evm/core/src/backend/cow.rs +++ b/crates/evm/core/src/backend/cow.rs @@ -18,7 +18,7 @@ use revm::{ }, Database, DatabaseCommit, Inspector, JournaledState, }; -use std::{borrow::Cow, collections::HashMap}; +use std::{borrow::Cow, collections::BTreeMap}; /// A wrapper around `Backend` that ensures only `revm::DatabaseRef` functions are called. /// @@ -159,7 +159,7 @@ impl<'a> DatabaseExt for CowBackend<'a> { fn roll_fork( &mut self, id: Option, - block_number: U256, + block_number: u64, env: &mut Env, journaled_state: &mut JournaledState, ) -> eyre::Result<()> { @@ -213,7 +213,7 @@ impl<'a> DatabaseExt for CowBackend<'a> { fn load_allocs( &mut self, - allocs: &HashMap, + allocs: &BTreeMap, journaled_state: &mut JournaledState, ) -> Result<(), DatabaseError> { self.backend_mut(&Env::default()).load_allocs(allocs, journaled_state) diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index ca6cbb1ef..dbd5071ee 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -7,8 +7,8 @@ use crate::{ utils::configure_tx_env, }; use alloy_genesis::GenesisAccount; -use alloy_primitives::{b256, keccak256, Address, B256, U256, U64}; -use alloy_rpc_types::{Block, BlockNumberOrTag, BlockTransactions, Transaction}; +use alloy_primitives::{b256, keccak256, Address, B256, U256}; +use alloy_rpc_types::{Block, BlockNumberOrTag, BlockTransactions, Transaction, WithOtherFields}; use eyre::Context; use foundry_common::{is_known_system_sender, SYSTEM_TRANSACTION_TYPE}; use revm::{ @@ -22,7 +22,7 @@ use revm::{ Database, DatabaseCommit, Inspector, JournaledState, }; use std::{ - collections::{HashMap, HashSet}, + collections::{BTreeMap, HashMap, HashSet}, time::Instant, }; @@ -166,7 +166,7 @@ pub trait DatabaseExt: Database { fn roll_fork( &mut self, id: Option, - block_number: U256, + block_number: u64, env: &mut Env, journaled_state: &mut JournaledState, ) -> eyre::Result<()>; @@ -262,7 +262,7 @@ pub trait DatabaseExt: Database { /// Returns [Ok] if all accounts were successfully inserted into the journal, [Err] otherwise. fn load_allocs( &mut self, - allocs: &HashMap, + allocs: &BTreeMap, journaled_state: &mut JournaledState, ) -> Result<(), DatabaseError>; @@ -857,18 +857,18 @@ impl Backend { &self, id: LocalForkId, transaction: B256, - ) -> eyre::Result<(U64, Block)> { + ) -> eyre::Result<(u64, Block)> { let fork = self.inner.get_fork_by_id(id)?; let tx = fork.db.db.get_transaction(transaction)?; // get the block number we need to fork if let Some(tx_block) = tx.block_number { - let block = fork.db.db.get_full_block(tx_block.to::())?; + let block = fork.db.db.get_full_block(tx_block)?; // we need to subtract 1 here because we want the state before the transaction // was mined - let fork_block = tx_block.to::() - 1; - Ok((U64::from(fork_block), block)) + let fork_block = tx_block - 1; + Ok((fork_block, block)) } else { let block = fork.db.db.get_full_block(BlockNumberOrTag::Latest)?; @@ -877,7 +877,7 @@ impl Backend { .number .ok_or_else(|| DatabaseError::BlockNotFound(BlockNumberOrTag::Latest.into()))?; - Ok((number.to::(), block)) + Ok((number, block)) } } @@ -904,7 +904,7 @@ 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.map(|ty| ty.to::()) == Some(SYSTEM_TRANSACTION_TYPE) + tx.transaction_type == Some(SYSTEM_TRANSACTION_TYPE) { trace!(tx=?tx.hash, "skipping system transaction"); continue; @@ -917,7 +917,7 @@ impl Backend { trace!(tx=?tx.hash, "committing transaction"); commit_transaction( - tx, + WithOtherFields::new(tx), env.clone(), journaled_state, fork, @@ -976,7 +976,7 @@ impl DatabaseExt for Backend { // another caller, so we need to ensure the caller account is present in the // journaled state and database let caller = current.tx.caller; - if !journaled_state.state.contains_key(&caller) { + journaled_state.state.entry(caller).or_insert_with(|| { let caller_account = current_state .state .get(&caller) @@ -987,8 +987,8 @@ impl DatabaseExt for Backend { // update the caller account which is required by the evm fork.db.insert_account_info(caller, caller_account.clone()); } - journaled_state.state.insert(caller, caller_account.into()); - } + caller_account.into() + }); self.inner.revert_snapshot(id, fork_id, idx, *fork); self.active_fork_ids = Some((id, idx)) } @@ -1115,7 +1115,7 @@ impl DatabaseExt for Backend { // necessarily the same caller as for the test, however we must always // ensure that fork's state contains the current sender let caller = env.tx.caller; - if !fork.journaled_state.state.contains_key(&caller) { + fork.journaled_state.state.entry(caller).or_insert_with(|| { let caller_account = active_journaled_state .state .get(&env.tx.caller) @@ -1126,8 +1126,8 @@ impl DatabaseExt for Backend { // update the caller account which is required by the evm fork.db.insert_account_info(caller, caller_account.clone()); } - fork.journaled_state.state.insert(caller, caller_account.into()); - } + caller_account.into() + }); self.update_fork_db(active_journaled_state, &mut fork); @@ -1147,14 +1147,14 @@ impl DatabaseExt for Backend { fn roll_fork( &mut self, id: Option, - block_number: U256, + block_number: u64, env: &mut Env, journaled_state: &mut JournaledState, ) -> eyre::Result<()> { trace!(?id, ?block_number, "roll fork"); let id = self.ensure_fork(id)?; let (fork_id, backend, fork_env) = - self.forks.roll_fork(self.inner.ensure_fork_id(id).cloned()?, block_number.to())?; + self.forks.roll_fork(self.inner.ensure_fork_id(id).cloned()?, block_number)?; // this will update the local mapping self.inner.roll_fork(id, fork_id, backend)?; @@ -1216,7 +1216,7 @@ impl DatabaseExt for Backend { self.get_block_number_and_block_for_transaction(id, transaction)?; // roll the fork to the transaction's block or latest if it's pending - self.roll_fork(Some(id), fork_block.to(), env, journaled_state)?; + self.roll_fork(Some(id), fork_block, env, journaled_state)?; update_env_block(env, fork_block, &block); @@ -1332,7 +1332,7 @@ impl DatabaseExt for Backend { /// Returns [Ok] if all accounts were successfully inserted into the journal, [Err] otherwise. fn load_allocs( &mut self, - allocs: &HashMap, + allocs: &BTreeMap, journaled_state: &mut JournaledState, ) -> Result<(), DatabaseError> { // Loop through all of the allocs defined in the map and commit them to the journal. @@ -1855,20 +1855,20 @@ 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, fork_block: U64, block: &Block) { - env.block.timestamp = block.header.timestamp; +fn update_env_block(env: &mut Env, fork_block: u64, block: &Block) { + env.block.timestamp = U256::from(block.header.timestamp); env.block.coinbase = block.header.miner; env.block.difficulty = block.header.difficulty; env.block.prevrandao = Some(block.header.mix_hash.unwrap_or_default()); - env.block.basefee = block.header.base_fee_per_gas.unwrap_or_default(); - env.block.gas_limit = block.header.gas_limit; - env.block.number = block.header.number.map(|n| n.to()).unwrap_or(fork_block.to()); + env.block.basefee = U256::from(block.header.base_fee_per_gas.unwrap_or_default()); + env.block.gas_limit = U256::from(block.header.gas_limit); + env.block.number = U256::from(block.header.number.unwrap_or(fork_block)); } /// Executes the given transaction and commits state changes to the database _and_ the journaled /// state, with an optional inspector fn commit_transaction>( - tx: Transaction, + tx: WithOtherFields, mut env: EnvWithHandlerCfg, journaled_state: &mut JournaledState, fork: &mut Fork, diff --git a/crates/evm/core/src/fork/backend.rs b/crates/evm/core/src/fork/backend.rs index 8e720a250..f6da76fc5 100644 --- a/crates/evm/core/src/fork/backend.rs +++ b/crates/evm/core/src/fork/backend.rs @@ -4,8 +4,9 @@ use crate::{ fork::{cache::FlushJsonBlockCacheDB, BlockchainDb}, }; use alloy_primitives::{keccak256, Address, Bytes, B256, U256}; -use alloy_providers::tmp::TempProvider; -use alloy_rpc_types::{Block, BlockId, Transaction}; +use alloy_provider::{network::AnyNetwork, Provider}; +use alloy_rpc_types::{Block, BlockId, Transaction, WithOtherFields}; +use alloy_transport::Transport; use eyre::WrapErr; use foundry_common::NON_ARCHIVE_NODE_WARNING; use futures::{ @@ -21,6 +22,7 @@ use revm::{ use rustc_hash::FxHashMap; use std::{ collections::{hash_map::Entry, HashMap, VecDeque}, + marker::PhantomData, pin::Pin, sync::{ mpsc::{channel as oneshot_channel, Sender as OneshotSender}, @@ -31,19 +33,23 @@ use std::{ // Various future/request type aliases type AccountFuture = - Pin, Address)> + Send>>; + Pin, Address)> + Send>>; type StorageFuture = Pin, Address, U256)> + Send>>; type BlockHashFuture = Pin, u64)> + Send>>; type FullBlockFuture = Pin, Err>, BlockId)> + Send>>; -type TransactionFuture = - Pin, B256)> + Send>>; +type TransactionFuture = Pin< + Box< + dyn Future, Err>, B256)> + + Send, + >, +>; type AccountInfoSender = OneshotSender>; type StorageSender = OneshotSender>; type BlockHashSender = OneshotSender>; type FullBlockSender = OneshotSender>; -type TransactionSender = OneshotSender>; +type TransactionSender = OneshotSender>>; /// Request variants that are executed by the provider enum ProviderRequest { @@ -76,8 +82,9 @@ enum BackendRequest { /// This handler will remain active as long as it is reachable (request channel still open) and /// requests are in progress. #[must_use = "futures do nothing unless polled"] -pub struct BackendHandler

{ +pub struct BackendHandler { provider: P, + transport: PhantomData, /// Stores all the data. db: BlockchainDb, /// Requests currently in progress @@ -97,9 +104,10 @@ pub struct BackendHandler

{ block_id: Option, } -impl

BackendHandler

+impl BackendHandler where - P: TempProvider + Clone + 'static, + T: Transport + Clone, + P: Provider + Clone + Unpin + 'static, { fn new( provider: P, @@ -117,6 +125,7 @@ where queued_requests: Default::default(), incoming: rx, block_id, + transport: PhantomData, } } @@ -197,7 +206,7 @@ where let fut = Box::pin(async move { let balance = provider.get_balance(address, block_id); let nonce = provider.get_transaction_count(address, block_id); - let code = provider.get_code_at(address, block_id); + let code = provider.get_code_at(address, block_id.unwrap_or_default()); let resp = tokio::try_join!(balance, nonce, code).map_err(Into::into); (resp, address) }); @@ -283,9 +292,10 @@ where } } -impl

Future for BackendHandler

+impl Future for BackendHandler where - P: TempProvider + Clone + Unpin + 'static, + T: Transport + Clone + Unpin, + P: Provider + Clone + Unpin + 'static, { type Output = (); @@ -343,7 +353,7 @@ where // update the cache let acc = AccountInfo { - nonce: nonce.to(), + nonce, balance, code: Some(Bytecode::new_raw(code).to_checked()), code_hash, @@ -512,9 +522,14 @@ impl SharedBackend { /// dropped. /// /// NOTE: this should be called with `Arc` - pub async fn spawn_backend

(provider: P, db: BlockchainDb, pin_block: Option) -> Self + pub async fn spawn_backend( + provider: P, + db: BlockchainDb, + pin_block: Option, + ) -> Self where - P: TempProvider + Unpin + 'static + Clone, + T: Transport + Clone + Unpin, + P: Provider + Unpin + 'static + Clone, { let (shared, handler) = Self::new(provider, db, pin_block); // spawn the provider handler to a task @@ -525,13 +540,14 @@ impl SharedBackend { /// Same as `Self::spawn_backend` but spawns the `BackendHandler` on a separate `std::thread` in /// its own `tokio::Runtime` - pub fn spawn_backend_thread

( + pub fn spawn_backend_thread( provider: P, db: BlockchainDb, pin_block: Option, ) -> Self where - P: TempProvider + Unpin + 'static + Clone, + T: Transport + Clone + Unpin, + P: Provider + Unpin + 'static + Clone, { let (shared, handler) = Self::new(provider, db, pin_block); @@ -554,13 +570,14 @@ impl SharedBackend { } /// Returns a new `SharedBackend` and the `BackendHandler` - pub fn new

( + pub fn new( provider: P, db: BlockchainDb, pin_block: Option, - ) -> (Self, BackendHandler

) + ) -> (Self, BackendHandler) where - P: TempProvider + Clone + 'static, + T: Transport + Clone + Unpin, + P: Provider + Unpin + 'static + Clone, { let (backend, backend_rx) = channel(1); let cache = Arc::new(FlushJsonBlockCacheDB(Arc::clone(db.cache()))); @@ -585,7 +602,7 @@ impl SharedBackend { } /// Returns the transaction for the hash - pub fn get_transaction(&self, tx: B256) -> DatabaseResult { + pub fn get_transaction(&self, tx: B256) -> DatabaseResult> { tokio::task::block_in_place(|| { let (sender, rx) = oneshot_channel(); let req = BackendRequest::Transaction(tx, sender); diff --git a/crates/evm/core/src/fork/init.rs b/crates/evm/core/src/fork/init.rs index abde7cb22..b69e02aad 100644 --- a/crates/evm/core/src/fork/init.rs +++ b/crates/evm/core/src/fork/init.rs @@ -1,7 +1,8 @@ use crate::utils::apply_chain_and_block_specific_env_changes; use alloy_primitives::{Address, U256}; -use alloy_providers::tmp::TempProvider; +use alloy_provider::{Network, Provider}; use alloy_rpc_types::{Block, BlockNumberOrTag}; +use alloy_transport::Transport; use eyre::WrapErr; use foundry_common::NON_ARCHIVE_NODE_WARNING; @@ -10,10 +11,10 @@ use revm::primitives::{BlockEnv, CfgEnv, Env, TxEnv}; /// Initializes a REVM block environment based on a forked /// ethereum provider. // todo(onbjerg): these bounds needed cus of the bounds in `Provider`, can simplify? -pub async fn environment( +pub async fn environment>( provider: &P, memory_limit: u64, - gas_price: Option, + gas_price: Option, override_chain_id: Option, pin_block: Option, origin: Address, @@ -49,7 +50,7 @@ pub async fn environment( }; let mut cfg = CfgEnv::default(); - cfg.chain_id = override_chain_id.unwrap_or(rpc_chain_id.to::()); + cfg.chain_id = override_chain_id.unwrap_or(rpc_chain_id); cfg.memory_limit = memory_limit; cfg.limit_contract_code_size = Some(usize::MAX); // EIP-3607 rejects transactions from senders with deployed code. @@ -61,20 +62,20 @@ pub async fn environment( let mut env = Env { cfg, block: BlockEnv { - number: block.header.number.expect("block number not found"), - timestamp: block.header.timestamp, + number: U256::from(block.header.number.expect("block number not found")), + timestamp: U256::from(block.header.timestamp), coinbase: block.header.miner, difficulty: block.header.difficulty, prevrandao: Some(block.header.mix_hash.unwrap_or_default()), - basefee: block.header.base_fee_per_gas.unwrap_or_default(), - gas_limit: block.header.gas_limit, + basefee: U256::from(block.header.base_fee_per_gas.unwrap_or_default()), + gas_limit: U256::from(block.header.gas_limit), ..Default::default() }, tx: TxEnv { caller: origin, - gas_price: gas_price.map(U256::from).unwrap_or(fork_gas_price), - chain_id: Some(override_chain_id.unwrap_or(rpc_chain_id.to::())), - gas_limit: block.header.gas_limit.to::(), + gas_price: U256::from(gas_price.unwrap_or(fork_gas_price)), + chain_id: Some(override_chain_id.unwrap_or(rpc_chain_id)), + gas_limit: block.header.gas_limit as u64, ..Default::default() }, }; diff --git a/crates/evm/core/src/fork/multi.rs b/crates/evm/core/src/fork/multi.rs index 4e1cb66e5..60096784c 100644 --- a/crates/evm/core/src/fork/multi.rs +++ b/crates/evm/core/src/fork/multi.rs @@ -4,7 +4,11 @@ //! concurrently active pairs at once. use crate::fork::{BackendHandler, BlockchainDb, BlockchainDbMeta, CreateFork, SharedBackend}; -use foundry_common::provider::alloy::{ProviderBuilder, RetryProvider}; +use foundry_common::provider::{ + alloy::{ProviderBuilder, RetryProvider}, + runtime_transport::RuntimeTransport, + tower::RetryBackoffService, +}; use foundry_config::Config; use futures::{ channel::mpsc::{channel, Receiver, Sender}, @@ -167,7 +171,7 @@ impl MultiFork { } } -type Handler = BackendHandler>; +type Handler = BackendHandler, Arc>; type CreateFuture = Pin> + Send>>; @@ -498,7 +502,7 @@ async fn create_fork(mut fork: CreateFork) -> eyre::Result<(ForkId, CreatedFork, // we need to use the block number from the block because the env's number can be different on // some L2s (e.g. Arbitrum). - let number = block.header.number.unwrap_or(meta.block_env.number).to::(); + let number = block.header.number.unwrap_or(meta.block_env.number.to()); // determine the cache path if caching is enabled let cache_path = if fork.enable_caching { diff --git a/crates/evm/core/src/opts.rs b/crates/evm/core/src/opts.rs index 89fdcf4e3..fcd92dd6a 100644 --- a/crates/evm/core/src/opts.rs +++ b/crates/evm/core/src/opts.rs @@ -1,7 +1,7 @@ use super::fork::environment; use crate::fork::CreateFork; use alloy_primitives::{Address, B256, U256}; -use alloy_providers::tmp::TempProvider; +use alloy_provider::Provider; use alloy_rpc_types::Block; use eyre::WrapErr; use foundry_common::{ @@ -95,7 +95,7 @@ impl EvmOpts { environment( &provider, self.memory_limit, - self.env.gas_price, + self.env.gas_price.map(|v| v as u128), self.env.chain_id, self.fork_block_number, self.sender, @@ -205,7 +205,7 @@ impl EvmOpts { .unwrap_or_else(|| panic!("Failed to establish provider to {url}")); if let Ok(id) = RuntimeOrHandle::new().block_on(provider.get_chain_id()) { - return Some(Chain::from(id.to::())); + return Some(Chain::from(id)); } } diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index 7ef8bb47f..ee8a8a4d4 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -25,7 +25,7 @@ pub fn apply_chain_and_block_specific_env_changes(env: &mut revm::primitives::En match chain { NamedChain::Mainnet => { // after merge difficulty is supplanted with prevrandao EIP-4399 - if block_number.to::() >= 15_537_351u64 { + if block_number >= 15_537_351u64 { env.block.difficulty = env.block.prevrandao.unwrap_or_default().into(); } @@ -68,14 +68,15 @@ pub fn get_function( /// Configures the env for the transaction pub fn configure_tx_env(env: &mut revm::primitives::Env, tx: &Transaction) { env.tx.caller = tx.from; - env.tx.gas_limit = tx.gas.to(); - env.tx.gas_price = tx.gas_price.unwrap_or_default().to(); - env.tx.gas_priority_fee = tx.max_priority_fee_per_gas.map(|g| g.to()); - env.tx.nonce = Some(tx.nonce.to()); + env.tx.gas_limit = tx.gas as u64; + env.tx.gas_price = U256::from(tx.gas_price.unwrap_or_default()); + env.tx.gas_priority_fee = tx.max_priority_fee_per_gas.map(U256::from); + env.tx.nonce = Some(tx.nonce); env.tx.access_list = tx .access_list .clone() .unwrap_or_default() + .0 .into_iter() .map(|item| { ( diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 6f83bf43f..a38bf0eee 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -515,9 +515,7 @@ impl InspectorStack { if let Some(acc_mut) = ecx.journaled_state.state.get_mut(&addr) { acc_mut.status |= acc.status; for (key, val) in acc.storage { - if !acc_mut.storage.contains_key(&key) { - acc_mut.storage.insert(key, val); - } + acc_mut.storage.entry(key).or_insert(val); } } else { ecx.journaled_state.state.insert(addr, acc); diff --git a/crates/evm/fuzz/src/strategies/state.rs b/crates/evm/fuzz/src/strategies/state.rs index 3a3b17ed6..f432154b9 100644 --- a/crates/evm/fuzz/src/strategies/state.rs +++ b/crates/evm/fuzz/src/strategies/state.rs @@ -212,7 +212,11 @@ pub fn build_initial_state( let mut values = IndexSet::new(); let mut addresses = IndexSet::new(); - for (address, account) in db.accounts.iter() { + // Sort accounts to ensure deterministic dictionary generation from the same setUp state. + let mut accs = db.accounts.iter().collect::>(); + accs.sort_by_key(|(address, _)| *address); + + for (address, account) in accs { let address: Address = *address; // Insert basic account information values.insert(address.into_word().into()); diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index f5a6b27c7..b7508ae4b 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -28,10 +28,6 @@ foundry-wallets.workspace = true foundry-linking.workspace = true ethers-contract.workspace = true -ethers-core.workspace = true -ethers-middleware.workspace = true -ethers-providers.workspace = true -ethers-signers.workspace = true revm-inspectors.workspace = true @@ -55,6 +51,12 @@ alloy-dyn-abi.workspace = true alloy-json-abi.workspace = true alloy-primitives = { workspace = true, features = ["serde"] } alloy-rpc-types.workspace = true +alloy-provider.workspace = true +alloy-network.workspace = true +alloy-transport.workspace = true +alloy-signer.workspace = true +alloy-consensus.workspace = true +alloy-chains.workspace = true async-trait = "0.1" clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } @@ -103,6 +105,9 @@ svm = { package = "svm-rs", version = "0.4", default-features = false, features tempfile = "3" tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] } +ethers-core.workspace = true +alloy-signer-wallet.workspace = true + [features] default = ["rustls"] rustls = [ @@ -111,7 +116,7 @@ rustls = [ "reqwest/rustls-tls", "reqwest/rustls-tls-native-roots", ] -openssl = ["foundry-cli/openssl", "reqwest/default-tls", "foundry-wallets/openssl"] +openssl = ["foundry-cli/openssl", "reqwest/default-tls"] asm-keccak = ["alloy-primitives/asm-keccak"] jemalloc = ["dep:tikv-jemallocator"] diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index 9dee30675..e42a17057 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -1,17 +1,13 @@ -use alloy_dyn_abi::{DynSolValue, JsonAbiExt, ResolveSolType}; +use alloy_chains::Chain; +use alloy_dyn_abi::{DynSolValue, JsonAbiExt, Specifier}; use alloy_json_abi::{Constructor, JsonAbi}; +use alloy_network::{AnyNetwork, EthereumSigner, TransactionBuilder}; use alloy_primitives::{Address, Bytes}; +use alloy_provider::{Provider, ProviderBuilder}; +use alloy_rpc_types::{AnyTransactionReceipt, TransactionRequest, WithOtherFields}; +use alloy_signer::Signer; +use alloy_transport::{Transport, TransportError}; use clap::{Parser, ValueHint}; -use ethers_contract::ContractError; -use ethers_core::{ - abi::InvalidOutputType, - types::{ - transaction::eip2718::TypedTransaction, BlockNumber, Chain, Eip1559TransactionRequest, - TransactionReceipt, TransactionRequest, - }, -}; -use ethers_middleware::SignerMiddleware; -use ethers_providers::Middleware; use eyre::{Context, Result}; use forge_verify::RetryArgs; use foundry_cli::{ @@ -19,10 +15,7 @@ use foundry_cli::{ utils::{self, read_constructor_args_file, remove_contract, LoadConfig}, }; use foundry_common::{ - compile::ProjectCompiler, - fmt::parse_tokens, - provider::ethers::estimate_eip1559_fees, - types::{ToAlloy, ToEthers}, + compile::ProjectCompiler, fmt::parse_tokens, provider::alloy::estimate_eip1559_fees, }; use foundry_compilers::{artifacts::BytecodeObject, info::ContractInfo, utils::canonicalized}; use serde_json::json; @@ -136,18 +129,20 @@ impl CreateArgs { let chain_id = if let Some(chain_id) = self.chain_id() { chain_id } else { - provider.get_chainid().await?.as_u64() + provider.get_chain_id().await? }; if self.unlocked { // Deploy with unlocked account let sender = self.eth.wallet.from.expect("required"); - let provider = provider.with_sender(sender.to_ethers()); - self.deploy(abi, bin, params, provider, chain_id).await + self.deploy(abi, bin, params, provider, chain_id, sender).await } else { // Deploy with signer let signer = self.eth.wallet.signer().await?; - let provider = SignerMiddleware::new_with_provider_chain(provider, signer).await?; - self.deploy(abi, bin, params, provider, chain_id).await + let deployer = signer.address(); + let provider = ProviderBuilder::<_, _, AnyNetwork>::default() + .signer(EthereumSigner::new(signer)) + .on_provider(provider); + self.deploy(abi, bin, params, provider, chain_id, deployer).await } } @@ -206,16 +201,15 @@ impl CreateArgs { } /// Deploys the contract - async fn deploy( + async fn deploy, T: Transport + Clone>( self, abi: JsonAbi, bin: BytecodeObject, args: Vec, - provider: M, + provider: P, chain: u64, + deployer_address: Address, ) -> Result<()> { - let deployer_address = - provider.default_sender().expect("no sender address set for provider"); let bin = bin.into_bytes().unwrap_or_else(|| { panic!("no bytecode found in bin object for {}", self.contract.name) }); @@ -223,7 +217,7 @@ impl CreateArgs { let factory = ContractFactory::new(abi.clone(), bin.clone(), provider.clone()); let is_args_empty = args.is_empty(); - let deployer = + let mut deployer = factory.deploy_tokens(args.clone()).context("failed to deploy contract").map_err(|e| { if is_args_empty { e.wrap_err("no arguments provided for contract constructor; consider --constructor-args or --constructor-args-path") @@ -231,57 +225,52 @@ impl CreateArgs { e } })?; - let is_legacy = self.tx.legacy || - Chain::try_from(chain).map(|x| Chain::is_legacy(&x)).unwrap_or_default(); - let mut deployer = if is_legacy { deployer.legacy() } else { deployer }; + let is_legacy = self.tx.legacy || Chain::from(chain).is_legacy(); + + deployer.tx.set_from(deployer_address); + deployer.tx.set_chain_id(chain); + + deployer.tx.set_nonce(if let Some(nonce) = self.tx.nonce { + Ok(nonce.to()) + } else { + provider.get_transaction_count(deployer_address, None).await + }?); + + deployer.tx.set_gas_limit(if let Some(gas_limit) = self.tx.gas_limit { + Ok(gas_limit.to()) + } else { + provider.estimate_gas(&deployer.tx, None).await + }?); // set tx value if specified if let Some(value) = self.tx.value { - deployer.tx.set_value(value.to_ethers()); + deployer.tx.set_value(value); } - // fill tx first because if you target a lower gas than current base, eth_estimateGas - // will fail and create will fail - provider.fill_transaction(&mut deployer.tx, None).await?; - - // the max - let mut priority_fee = self.tx.priority_gas_price; - - // set gas price if specified - if let Some(gas_price) = self.tx.gas_price { - deployer.tx.set_gas_price(gas_price.to_ethers()); - } else if !is_legacy { - // estimate EIP1559 fees - let (max_fee, max_priority_fee) = estimate_eip1559_fees(&provider, Some(chain)) + if is_legacy { + let gas_price = if let Some(gas_price) = self.tx.gas_price { + gas_price.to() + } else { + provider.get_gas_price().await? + }; + deployer.tx.set_gas_price(gas_price); + } else { + let estimate = estimate_eip1559_fees(&provider, Some(chain)) .await .wrap_err("Failed to estimate EIP1559 fees. This chain might not support EIP1559, try adding --legacy to your command.")?; - deployer.tx.set_gas_price(max_fee); - if priority_fee.is_none() { - priority_fee = Some(max_priority_fee.to_alloy()); - } - } - - // set gas limit if specified - if let Some(gas_limit) = self.tx.gas_limit { - deployer.tx.set_gas(gas_limit.to_ethers()); - } - - // set nonce if specified - if let Some(nonce) = self.tx.nonce { - deployer.tx.set_nonce(nonce.to_ethers()); - } - - // set priority fee if specified - if let Some(priority_fee) = priority_fee { - if is_legacy { - eyre::bail!("there is no priority fee for legacy txs"); - } - deployer.tx = match deployer.tx { - TypedTransaction::Eip1559(eip1559_tx_request) => TypedTransaction::Eip1559( - eip1559_tx_request.max_priority_fee_per_gas(priority_fee.to_ethers()), - ), - _ => deployer.tx, + let priority_fee = if let Some(priority_fee) = self.tx.priority_gas_price { + priority_fee.to() + } else { + estimate.max_priority_fee_per_gas }; + let max_fee = if let Some(max_fee) = self.tx.gas_price { + max_fee.to() + } else { + estimate.max_fee_per_gas + }; + + deployer.tx.set_max_fee_per_gas(max_fee); + deployer.tx.set_max_priority_fee_per_gas(priority_fee); } // Before we actually deploy the contract we try check if the verify settings are valid @@ -304,13 +293,13 @@ impl CreateArgs { let address = deployed_contract; if self.json { let output = json!({ - "deployer": deployer_address.to_alloy().to_string(), + "deployer": deployer_address.to_string(), "deployedTo": address.to_string(), "transactionHash": receipt.transaction_hash }); println!("{output}"); } else { - println!("Deployer: {}", deployer_address.to_alloy()); + println!("Deployer: {deployer_address}"); println!("Deployed to: {address}"); println!("Transaction hash: {:?}", receipt.transaction_hash); }; @@ -376,7 +365,7 @@ impl CreateArgs { /// compatibility with less-abstract Contracts. /// /// For full usage docs, see [`DeploymentTxFactory`]. -pub type ContractFactory = DeploymentTxFactory, M>; +pub type ContractFactory = DeploymentTxFactory, P, T>; /// Helper which manages the deployment transaction of a smart contract. It /// wraps a deployment transaction, and retrieves the contract address output @@ -385,16 +374,16 @@ pub type ContractFactory = DeploymentTxFactory, M>; /// Currently, we recommend using the [`ContractDeployer`] type alias. #[derive(Debug)] #[must_use = "ContractDeploymentTx does nothing unless you `send` it"] -pub struct ContractDeploymentTx { +pub struct ContractDeploymentTx { /// the actual deployer, exposed for overriding the defaults - pub deployer: Deployer, + pub deployer: Deployer, /// marker for the `Contract` type to create afterwards /// /// this type will be used to construct it via `From::from(Contract)` _contract: PhantomData, } -impl Clone for ContractDeploymentTx +impl Clone for ContractDeploymentTx where B: Clone, { @@ -403,8 +392,8 @@ where } } -impl From> for ContractDeploymentTx { - fn from(deployer: Deployer) -> Self { +impl From> for ContractDeploymentTx { + fn from(deployer: Deployer) -> Self { Self { deployer, _contract: PhantomData } } } @@ -412,17 +401,17 @@ impl From> for ContractDeploymentTx { /// Helper which manages the deployment transaction of a smart contract #[derive(Debug)] #[must_use = "Deployer does nothing unless you `send` it"] -pub struct Deployer { +pub struct Deployer { /// The deployer's transaction, exposed for overriding the defaults - pub tx: TypedTransaction, + pub tx: WithOtherFields, abi: JsonAbi, client: B, confs: usize, - block: BlockNumber, - _m: PhantomData, + _p: PhantomData

, + _t: PhantomData, } -impl Clone for Deployer +impl Clone for Deployer where B: Clone, { @@ -432,53 +421,38 @@ where abi: self.abi.clone(), client: self.client.clone(), confs: self.confs, - block: self.block, - _m: PhantomData, + _p: PhantomData, + _t: PhantomData, } } } -impl Deployer +impl Deployer where - B: Borrow + Clone, - M: Middleware, + B: Borrow

+ Clone, + P: Provider, + T: Transport + Clone, { - /// Uses a Legacy transaction instead of an EIP-1559 one to do the deployment - pub fn legacy(mut self) -> Self { - self.tx = match self.tx { - TypedTransaction::Eip1559(inner) => { - let tx: TransactionRequest = inner.into(); - TypedTransaction::Legacy(tx) - } - other => other, - }; - self - } - /// Broadcasts the contract deployment transaction and after waiting for it to /// be sufficiently confirmed (default: 1), it returns a tuple with /// the [`Contract`](crate::Contract) struct at the deployed contract's address - /// and the corresponding [`TransactionReceipt`]. + /// and the corresponding [`AnyReceipt`]. pub async fn send_with_receipt( self, - ) -> Result<(Address, TransactionReceipt), ContractError> { - let pending_tx = self + ) -> Result<(Address, AnyTransactionReceipt), ContractDeploymentError> { + let receipt = self .client .borrow() - .send_transaction(self.tx, Some(self.block.into())) - .await - .map_err(ContractError::from_middleware_error)?; - - // TODO: Should this be calculated "optimistically" by address/nonce? - let receipt = pending_tx - .confirmations(self.confs) - .await - .ok() - .flatten() - .ok_or(ContractError::ContractNotDeployed)?; - let address = receipt.contract_address.ok_or(ContractError::ContractNotDeployed)?; - - Ok((address.to_alloy(), receipt)) + .send_transaction(self.tx) + .await? + .with_required_confirmations(self.confs as u64) + .get_receipt() + .await?; + + let address = + receipt.contract_address.ok_or(ContractDeploymentError::ContractNotDeployed)?; + + Ok((address, receipt)) } } @@ -519,14 +493,15 @@ where /// # Ok(()) /// # } #[derive(Debug)] -pub struct DeploymentTxFactory { +pub struct DeploymentTxFactory { client: B, abi: JsonAbi, bytecode: Bytes, - _m: PhantomData, + _p: PhantomData

, + _t: PhantomData, } -impl Clone for DeploymentTxFactory +impl Clone for DeploymentTxFactory where B: Clone, { @@ -535,39 +510,42 @@ where client: self.client.clone(), abi: self.abi.clone(), bytecode: self.bytecode.clone(), - _m: PhantomData, + _p: PhantomData, + _t: PhantomData, } } } -impl DeploymentTxFactory +impl DeploymentTxFactory where - B: Borrow + Clone, - M: Middleware, + B: Borrow

+ Clone, + P: Provider, + T: Transport + Clone, { /// Creates a factory for deployment of the Contract with bytecode, and the /// constructor defined in the abi. The client will be used to send any deployment /// transaction. pub fn new(abi: JsonAbi, bytecode: Bytes, client: B) -> Self { - Self { client, abi, bytecode, _m: PhantomData } + Self { client, abi, bytecode, _p: PhantomData, _t: PhantomData } } /// Create a deployment tx using the provided tokens as constructor /// arguments - pub fn deploy_tokens(self, params: Vec) -> Result, ContractError> + pub fn deploy_tokens( + self, + params: Vec, + ) -> Result, ContractDeploymentError> where B: Clone, { // Encode the constructor args & concatenate with the bytecode if necessary let data: Bytes = match (self.abi.constructor(), params.is_empty()) { - (None, false) => return Err(ContractError::ConstructorError), + (None, false) => return Err(ContractDeploymentError::ConstructorError), (None, true) => self.bytecode.clone(), (Some(constructor), _) => { let input: Bytes = constructor .abi_encode_input(¶ms) - .map_err(|f| { - ContractError::DetokenizationError(InvalidOutputType(f.to_string())) - })? + .map_err(ContractDeploymentError::DetokenizationError)? .into(); // Concatenate the bytecode and abi-encoded constructor call. self.bytecode.iter().copied().chain(input).collect() @@ -575,27 +553,32 @@ where }; // create the tx object. Since we're deploying a contract, `to` is `None` - // We default to EIP1559 transactions, but the sender can convert it back - // to a legacy one. - let tx = Eip1559TransactionRequest { - to: None, - data: Some(data.to_ethers()), - ..Default::default() - }; - - let tx = tx.into(); + let tx = WithOtherFields::new(TransactionRequest::default().input(data.into()).to(None)); Ok(Deployer { client: self.client.clone(), abi: self.abi, tx, confs: 1, - block: BlockNumber::Latest, - _m: PhantomData, + _p: PhantomData, + _t: PhantomData, }) } } +#[derive(thiserror::Error, Debug)] +/// An Error which is thrown when interacting with a smart contract +pub enum ContractDeploymentError { + #[error("constructor is not defined in the ABI")] + ConstructorError, + #[error(transparent)] + DetokenizationError(#[from] alloy_dyn_abi::Error), + #[error("contract was not deployed")] + ContractNotDeployed, + #[error(transparent)] + RpcError(#[from] TransportError), +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/forge/tests/cli/utils.rs b/crates/forge/tests/cli/utils.rs index 12b0fe134..e8e79fa68 100644 --- a/crates/forge/tests/cli/utils.rs +++ b/crates/forge/tests/cli/utils.rs @@ -1,7 +1,8 @@ //! Various helper functions +use alloy_signer_wallet::LocalWallet; use ethers_core::types::{Address, Chain}; -use ethers_signers::{LocalWallet, Signer}; +use foundry_common::types::ToEthers; /// Returns the current millis since unix epoch. /// @@ -46,7 +47,7 @@ pub struct EnvExternalities { impl EnvExternalities { pub fn address(&self) -> Option

{ let pk: LocalWallet = self.pk.parse().ok()?; - Some(pk.address()) + Some(pk.address().to_ethers()) } pub fn goerli() -> Option { diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index 49cbce5db..efbd95d71 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -255,14 +255,14 @@ async fn test_invariant_shrink() { let create_fren_sequence = sequence[0].clone(); assert_eq!( create_fren_sequence.contract_name.unwrap(), - "fuzz/invariant/common/InvariantInnerContract.t.sol:Jesus" + "default/fuzz/invariant/common/InvariantInnerContract.t.sol:Jesus" ); assert_eq!(create_fren_sequence.signature.unwrap(), "create_fren()"); let betray_sequence = sequence[1].clone(); assert_eq!( betray_sequence.contract_name.unwrap(), - "fuzz/invariant/common/InvariantInnerContract.t.sol:Judas" + "default/fuzz/invariant/common/InvariantInnerContract.t.sol:Judas" ); assert_eq!(betray_sequence.signature.unwrap(), "betray()"); } diff --git a/crates/forge/tests/it/repros.rs b/crates/forge/tests/it/repros.rs index 02f1aa15e..b1f8efc49 100644 --- a/crates/forge/tests/it/repros.rs +++ b/crates/forge/tests/it/repros.rs @@ -4,10 +4,10 @@ use crate::{ config::*, test_helpers::{ForgeTestData, TEST_DATA_DEFAULT}, }; -use alloy_primitives::{address, Address}; -use ethers_core::abi::{Event, EventParam, Log, LogParam, ParamType, RawLog, Token}; +use alloy_dyn_abi::{DecodedEvent, DynSolValue, EventExt}; +use alloy_json_abi::Event; +use alloy_primitives::{address, Address, U256}; use forge::result::TestStatus; -use foundry_common::types::ToEthers; use foundry_config::{fs_permissions::PathPermission, FsPermissions}; use foundry_evm::{ constants::HARDHAT_CONSOLE_ADDRESS, @@ -125,25 +125,15 @@ test_repro!(3347, false, None, |res| { let mut res = res.remove("default/repros/Issue3347.t.sol:Issue3347Test").unwrap(); let test = res.test_results.remove("test()").unwrap(); assert_eq!(test.logs.len(), 1); - let event = Event { - name: "log2".to_string(), - inputs: vec![ - EventParam { name: "x".to_string(), kind: ParamType::Uint(256), indexed: false }, - EventParam { name: "y".to_string(), kind: ParamType::Uint(256), indexed: false }, - ], - anonymous: false, - }; - let raw_log = RawLog { - topics: test.logs[0].data.topics().iter().map(|t| t.to_ethers()).collect(), - data: test.logs[0].data.data.clone().to_vec(), - }; - let log = event.parse_log(raw_log).unwrap(); + let event = Event::parse("event log2(uint256, uint256)").unwrap(); + let decoded = event.decode_log(&test.logs[0].data, false).unwrap(); assert_eq!( - log, - Log { - params: vec![ - LogParam { name: "x".to_string(), value: Token::Uint(1u64.into()) }, - LogParam { name: "y".to_string(), value: Token::Uint(2u64.into()) } + decoded, + DecodedEvent { + indexed: vec![], + body: vec![ + DynSolValue::Uint(U256::from(1), 256), + DynSolValue::Uint(U256::from(2), 256) ] } ); diff --git a/crates/script/Cargo.toml b/crates/script/Cargo.toml index 5ae574822..e62e054bf 100644 --- a/crates/script/Cargo.toml +++ b/crates/script/Cargo.toml @@ -32,19 +32,24 @@ clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } semver = "1" futures = "0.3" async-recursion = "1.0.5" -alloy-primitives.workspace = true -alloy-dyn-abi.workspace = true + itertools.workspace = true parking_lot = "0.12" yansi = "0.5" -ethers-core.workspace = true -ethers-providers.workspace = true -ethers-signers.workspace = true revm-inspectors.workspace = true alloy-rpc-types.workspace = true alloy-json-abi.workspace = true dialoguer = { version = "0.11", default-features = false } indicatif = "0.17" +alloy-signer.workspace = true +alloy-network.workspace = true +alloy-provider.workspace = true +alloy-chains.workspace = true +alloy-dyn-abi.workspace = true +alloy-primitives.workspace = true +alloy-eips.workspace = true +alloy-transport.workspace = true + [dev-dependencies] tempfile = "3" \ No newline at end of file diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index 983c02374..0cb5b0c9c 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -1,13 +1,15 @@ +use super::receipts; use crate::{ build::LinkedBuildData, sequence::ScriptSequenceKind, verify::BroadcastedState, ScriptArgs, ScriptConfig, }; - -use super::receipts; -use alloy_primitives::{utils::format_units, Address, TxHash, U256}; -use ethers_core::types::{transaction::eip2718::TypedTransaction, BlockId}; -use ethers_providers::{JsonRpcClient, Middleware, Provider}; -use ethers_signers::Signer; +use alloy_chains::Chain; +use alloy_eips::eip2718::Encodable2718; +use alloy_network::{AnyNetwork, EthereumSigner, TransactionBuilder}; +use alloy_primitives::{utils::format_units, Address, TxHash}; +use alloy_provider::{utils::Eip1559Estimation, Provider}; +use alloy_rpc_types::{TransactionRequest, WithOtherFields}; +use alloy_transport::Transport; use eyre::{bail, Context, Result}; use forge_verify::provider::VerificationProviderType; use foundry_cheatcodes::ScriptWallets; @@ -16,14 +18,12 @@ use foundry_cli::{ utils::{has_batch_support, has_different_gas_calc}, }; use foundry_common::{ - provider::ethers::{ + provider::alloy::{ estimate_eip1559_fees, get_http_provider, try_get_http_provider, RetryProvider, }, shell, - types::{ToAlloy, ToEthers}, }; use foundry_config::Config; -use foundry_wallets::WalletSigner; use futures::{future::join_all, StreamExt}; use itertools::Itertools; use std::{ @@ -31,56 +31,49 @@ use std::{ sync::Arc, }; -pub async fn estimate_gas( - tx: &mut TypedTransaction, - provider: &Provider, +pub async fn estimate_gas( + tx: &mut WithOtherFields, + provider: &P, estimate_multiplier: u64, ) -> Result<()> where - T: JsonRpcClient, + P: Provider, + T: Transport + Clone, { // if already set, some RPC endpoints might simply return the gas value that is already // set in the request and omit the estimate altogether, so we remove it here - let _ = tx.gas_mut().take(); - - tx.set_gas( - provider - .estimate_gas(tx, None) - .await - .wrap_err_with(|| format!("Failed to estimate gas for tx: {:?}", tx.sighash()))? * - estimate_multiplier / + tx.gas = None; + + tx.set_gas_limit( + provider.estimate_gas(tx, None).await.wrap_err("Failed to estimate gas for tx")? * + estimate_multiplier as u128 / 100, ); Ok(()) } -pub async fn next_nonce( - caller: Address, - provider_url: &str, - block: Option, -) -> eyre::Result { - let provider = Provider::try_from(provider_url) +pub async fn next_nonce(caller: Address, provider_url: &str) -> eyre::Result { + let provider = try_get_http_provider(provider_url) .wrap_err_with(|| format!("bad fork_url provider: {provider_url}"))?; - let res = provider.get_transaction_count(caller.to_ethers(), block).await?.to_alloy(); - res.try_into().map_err(Into::into) + Ok(provider.get_transaction_count(caller, None).await?) } pub async fn send_transaction( provider: Arc, - mut tx: TypedTransaction, + mut tx: WithOtherFields, kind: SendTransactionKind<'_>, sequential_broadcast: bool, is_fixed_gas_limit: bool, estimate_via_rpc: bool, estimate_multiplier: u64, ) -> Result { - let from = tx.from().expect("no sender"); + let from = tx.from.expect("no sender"); if sequential_broadcast { - let nonce = provider.get_transaction_count(*from, None).await?; + let nonce = provider.get_transaction_count(from, None).await?; - let tx_nonce = tx.nonce().expect("no nonce"); - if nonce != *tx_nonce { + let tx_nonce = tx.nonce.expect("no nonce"); + if nonce != tx_nonce { bail!("EOA nonce changed unexpectedly while sending transactions. Expected {tx_nonce} got {nonce} from provider.") } } @@ -96,29 +89,26 @@ pub async fn send_transaction( debug!("sending transaction from unlocked account {:?}: {:?}", addr, tx); // Submit the transaction - provider.send_transaction(tx, None).await? + provider.send_transaction(tx).await? } SendTransactionKind::Raw(signer) => { debug!("sending transaction: {:?}", tx); - // Signing manually so we skip `fill_transaction` and its `eth_createAccessList` - // request. - let signature = - signer.sign_transaction(&tx).await.wrap_err("Failed to sign transaction")?; + let signed = tx.build(signer).await?; // Submit the raw transaction - provider.send_raw_transaction(tx.rlp_signed(&signature)).await? + provider.send_raw_transaction(signed.encoded_2718().as_ref()).await? } }; - Ok(pending.tx_hash().to_alloy()) + Ok(*pending.tx_hash()) } /// How to send a single transaction #[derive(Clone)] pub enum SendTransactionKind<'a> { Unlocked(Address), - Raw(&'a WalletSigner), + Raw(&'a EthereumSigner), } /// Represents how to send _all_ transactions @@ -126,7 +116,7 @@ pub enum SendTransactionsKind { /// Send via `eth_sendTransaction` and rely on the `from` address being unlocked. Unlocked(HashSet
), /// Send a signed transaction via `eth_sendRawTransaction` - Raw(HashMap), + Raw(HashMap), } impl SendTransactionsKind { @@ -202,8 +192,8 @@ impl BundledState { .iter() .flat_map(|sequence| { sequence - .typed_transactions() - .map(|tx| (*tx.from().expect("No sender for onchain transaction!")).to_alloy()) + .transactions() + .map(|tx| (tx.from().expect("No sender for onchain transaction!"))) }) .collect::>(); @@ -233,6 +223,11 @@ impl BundledState { ); } + let signers = signers + .into_iter() + .map(|(addr, signer)| (addr, EthereumSigner::new(signer))) + .collect(); + SendTransactionsKind::Raw(signers) }; @@ -243,23 +238,37 @@ impl BundledState { let already_broadcasted = sequence.receipts.len(); if already_broadcasted < sequence.transactions.len() { + let is_legacy = Chain::from(sequence.chain).is_legacy() || self.args.legacy; // Make a one-time gas price estimation - let (gas_price, eip1559_fees) = match self.args.with_gas_price { - None => match sequence.transactions.front().unwrap().typed_tx() { - TypedTransaction::Eip1559(_) => { - let mut fees = estimate_eip1559_fees(&provider, Some(sequence.chain)) + let (gas_price, eip1559_fees) = match ( + is_legacy, + self.args.with_gas_price, + self.args.priority_gas_price, + ) { + (true, Some(gas_price), _) => (Some(gas_price.to()), None), + (true, None, _) => (Some(provider.get_gas_price().await?), None), + (false, Some(max_fee_per_gas), Some(max_priority_fee_per_gas)) => ( + None, + Some(Eip1559Estimation { + max_fee_per_gas: max_fee_per_gas.to(), + max_priority_fee_per_gas: max_priority_fee_per_gas.to(), + }), + ), + (false, _, _) => { + let mut fees = estimate_eip1559_fees(&provider, Some(sequence.chain)) .await - .wrap_err("Failed to estimate EIP1559 fees. This chain might not support EIP1559, try adding --legacy to your command.")?; + .wrap_err("Failed to estimate EIP1559 fees. This chain might not support EIP1559, try adding --legacy to your command.")?; - if let Some(priority_gas_price) = self.args.priority_gas_price { - fees.1 = priority_gas_price.to_ethers(); - } + if let Some(gas_price) = self.args.with_gas_price { + fees.max_fee_per_gas = gas_price.to(); + } - (None, Some(fees)) + if let Some(priority_gas_price) = self.args.priority_gas_price { + fees.max_priority_fee_per_gas = priority_gas_price.to(); } - _ => (provider.get_gas_price().await.ok(), None), - }, - Some(gas_price) => (Some(gas_price.to_ethers()), None), + + (None, Some(fees)) + } }; // Iterate through transactions, matching the `from` field with the associated @@ -269,35 +278,21 @@ impl BundledState { .iter() .skip(already_broadcasted) .map(|tx_with_metadata| { - let tx = tx_with_metadata.typed_tx(); - let from = - (*tx.from().expect("No sender for onchain transaction!")).to_alloy(); + let tx = tx_with_metadata.tx(); + let from = tx.from().expect("No sender for onchain transaction!"); let kind = send_kind.for_sender(&from)?; let is_fixed_gas_limit = tx_with_metadata.is_fixed_gas_limit; let mut tx = tx.clone(); - tx.set_chain_id(sequence.chain); if let Some(gas_price) = gas_price { tx.set_gas_price(gas_price); } else { let eip1559_fees = eip1559_fees.expect("was set above"); - // fill gas price - match tx { - TypedTransaction::Eip1559(ref mut inner) => { - inner.max_priority_fee_per_gas = Some(eip1559_fees.1); - inner.max_fee_per_gas = Some(eip1559_fees.0); - } - _ => { - // If we're here, it means that first transaction of the - // sequence was EIP1559 transaction (see match statement above), - // however, we can only have transactions of the same type in - // the sequence. - unreachable!() - } - } + tx.set_max_priority_fee_per_gas(eip1559_fees.max_priority_fee_per_gas); + tx.set_max_fee_per_gas(eip1559_fees.max_fee_per_gas); } Ok((tx, kind, is_fixed_gas_limit)) @@ -375,18 +370,15 @@ impl BundledState { shell::println("\n\n==========================")?; shell::println("\nONCHAIN EXECUTION COMPLETE & SUCCESSFUL.")?; - let (total_gas, total_gas_price, total_paid) = sequence.receipts.iter().fold( - (U256::ZERO, U256::ZERO, U256::ZERO), - |acc, receipt| { - let gas_used = receipt.gas_used.unwrap_or_default().to_alloy(); - let gas_price = receipt.effective_gas_price.unwrap_or_default().to_alloy(); + let (total_gas, total_gas_price, total_paid) = + sequence.receipts.iter().fold((0, 0, 0), |acc, receipt| { + let gas_used = receipt.gas_used; + let gas_price = receipt.effective_gas_price; (acc.0 + gas_used, acc.1 + gas_price, acc.2 + gas_used * gas_price) - }, - ); + }); let paid = format_units(total_paid, 18).unwrap_or_else(|_| "N/A".to_string()); - let avg_gas_price = - format_units(total_gas_price / U256::from(sequence.receipts.len()), 9) - .unwrap_or_else(|_| "N/A".to_string()); + let avg_gas_price = format_units(total_gas_price / sequence.receipts.len() as u128, 9) + .unwrap_or_else(|_| "N/A".to_string()); shell::println(format!( "Total Paid: {} ETH ({} gas * avg {} gwei)", diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index ab17a95a9..51fde33ad 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -7,14 +7,13 @@ use crate::{ }; use alloy_primitives::{Address, Bytes}; -use ethers_providers::Middleware; +use alloy_provider::Provider; use eyre::{Context, OptionExt, Result}; use foundry_cheatcodes::ScriptWallets; use foundry_cli::utils::get_cached_entry_by_name; use foundry_common::{ compile::{self, ContractSources, ProjectCompiler}, - provider::ethers::try_get_http_provider, - types::ToAlloy, + provider::alloy::try_get_http_provider, ContractsByArtifact, }; use foundry_compilers::{ @@ -290,7 +289,7 @@ impl CompiledState { } else { let fork_url = self.script_config.evm_opts.fork_url.clone().ok_or_eyre("Missing --fork-url field, if you were trying to broadcast a multi-chain sequence, please use --multi flag")?; let provider = Arc::new(try_get_http_provider(fork_url)?); - Some(provider.get_chainid().await?.as_u64()) + Some(provider.get_chain_id().await?) }; let sequence = match self.try_load_sequence(chain, false) { @@ -318,7 +317,7 @@ impl CompiledState { s.transactions .iter() .skip(s.receipts.len()) - .map(|t| t.transaction.from().expect("from is missing in script artifact")) + .map(|t| t.transaction.from.expect("from is missing in script artifact")) }); let available_signers = self @@ -326,7 +325,7 @@ impl CompiledState { .signers() .map_err(|e| eyre::eyre!("Failed to get available signers: {}", e))?; - if !froms.all(|from| available_signers.contains(&from.to_alloy())) { + if !froms.all(|from| available_signers.contains(&from)) { // IF we are missing required signers, execute script as we might need to collect // private keys from the execution. let executed = self.link()?.prepare_execution().await?.execute().await?; diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index 7b811a67c..03ac8bc9e 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -7,16 +7,16 @@ use crate::{ use super::{runner::ScriptRunner, JsonResult, NestedValue, ScriptResult}; use alloy_dyn_abi::FunctionExt; use alloy_json_abi::{Function, InternalType, JsonAbi}; -use alloy_primitives::{Address, Bytes, U64}; +use alloy_primitives::{Address, Bytes}; +use alloy_provider::Provider; use alloy_rpc_types::request::TransactionRequest; use async_recursion::async_recursion; -use ethers_providers::Middleware; use eyre::Result; use foundry_cheatcodes::ScriptWallets; use foundry_cli::utils::{ensure_clean_constructor, needs_setup}; use foundry_common::{ fmt::{format_token, format_token_raw}, - provider::ethers::{get_http_provider, RpcUrl}, + provider::alloy::{get_http_provider, RpcUrl}, shell, ContractsByArtifact, }; use foundry_compilers::artifacts::ContractBytecodeSome; @@ -135,7 +135,7 @@ impl PreExecutionState { transaction: TransactionRequest { from: Some(self.script_config.evm_opts.sender), input: Some(bytes.clone()).into(), - nonce: Some(U64::from(self.script_config.sender_nonce + i as u64)), + nonce: Some(self.script_config.sender_nonce + i as u64), ..Default::default() }, }) @@ -253,9 +253,8 @@ impl RpcData { async fn check_shanghai_support(&self) -> Result<()> { let chain_ids = self.total_rpcs.iter().map(|rpc| async move { let provider = get_http_provider(rpc); - let id = provider.get_chainid().await.ok()?; - let id_u64: u64 = id.try_into().ok()?; - NamedChain::try_from(id_u64).ok() + let id = provider.get_chain_id().await.ok()?; + NamedChain::try_from(id).ok() }); let chains = join_all(chain_ids).await; diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 530c84d36..8973dc126 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -7,11 +7,11 @@ use self::transaction::AdditionalContract; use crate::runner::ScriptRunner; use alloy_json_abi::{Function, JsonAbi}; use alloy_primitives::{Address, Bytes, Log, U256}; +use alloy_signer::Signer; use broadcast::next_nonce; use build::PreprocessedState; use clap::{Parser, ValueHint}; use dialoguer::Confirm; -use ethers_signers::Signer; use eyre::{ContextCompat, Result}; use forge_verify::RetryArgs; use foundry_cli::{opts::CoreBuildArgs, utils::LoadConfig}; @@ -20,10 +20,8 @@ use foundry_common::{ compile::SkipBuildFilter, errors::UnlinkedByteCode, evm::{Breakpoints, EvmArgs}, - provider::ethers::RpcUrl, - shell, - types::ToAlloy, - CONTRACT_MAX_SIZE, SELECTOR_LEN, + provider::alloy::RpcUrl, + shell, CONTRACT_MAX_SIZE, SELECTOR_LEN, }; use foundry_compilers::{artifacts::ContractBytecodeSome, ArtifactId}; use foundry_config::{ @@ -305,7 +303,7 @@ impl ScriptArgs { .wallets .private_keys()? .filter(|pks| pks.len() == 1) - .map(|pks| pks.first().unwrap().address().to_alloy()); + .map(|pks| pks.first().unwrap().address()); Ok(maybe_sender) } @@ -531,7 +529,7 @@ pub struct ScriptConfig { impl ScriptConfig { pub async fn new(config: Config, evm_opts: EvmOpts) -> Result { let sender_nonce = if let Some(fork_url) = evm_opts.fork_url.as_ref() { - next_nonce(evm_opts.sender, fork_url, None).await? + next_nonce(evm_opts.sender, fork_url).await? } else { // dapptools compatibility 1 @@ -541,7 +539,7 @@ impl ScriptConfig { pub async fn update_sender(&mut self, sender: Address) -> Result<()> { self.sender_nonce = if let Some(fork_url) = self.evm_opts.fork_url.as_ref() { - next_nonce(sender, fork_url, None).await? + next_nonce(sender, fork_url).await? } else { // dapptools compatibility 1 diff --git a/crates/script/src/providers.rs b/crates/script/src/providers.rs index f29a72629..ab2ee9911 100644 --- a/crates/script/src/providers.rs +++ b/crates/script/src/providers.rs @@ -1,11 +1,6 @@ -use alloy_primitives::U256; -use ethers_providers::{Middleware, Provider}; +use alloy_provider::{utils::Eip1559Estimation, Provider}; use eyre::{Result, WrapErr}; -use foundry_common::{ - provider::ethers::{get_http_provider, RpcUrl}, - runtime_client::RuntimeClient, - types::ToAlloy, -}; +use foundry_common::provider::alloy::{get_http_provider, RetryProvider, RpcUrl}; use foundry_config::Chain; use std::{ collections::{hash_map::Entry, HashMap}, @@ -47,23 +42,22 @@ impl Deref for ProvidersManager { /// Holds related metadata to each provider RPC. #[derive(Debug)] pub struct ProviderInfo { - pub provider: Arc>, + pub provider: Arc, pub chain: u64, pub gas_price: GasPrice, - pub is_legacy: bool, } /// Represents the outcome of a gas price request #[derive(Debug)] pub enum GasPrice { - Legacy(Result), - EIP1559(Result<(U256, U256)>), + Legacy(Result), + EIP1559(Result), } impl ProviderInfo { pub async fn new(rpc: &str, mut is_legacy: bool) -> Result { let provider = Arc::new(get_http_provider(rpc)); - let chain = provider.get_chainid().await?.as_u64(); + let chain = provider.get_chain_id().await?; if let Some(chain) = Chain::from(chain).named() { is_legacy |= chain.is_legacy(); @@ -71,30 +65,22 @@ impl ProviderInfo { let gas_price = if is_legacy { GasPrice::Legacy( - provider - .get_gas_price() - .await - .wrap_err("Failed to get legacy gas price") - .map(|p| p.to_alloy()), + provider.get_gas_price().await.wrap_err("Failed to get legacy gas price"), ) } else { GasPrice::EIP1559( - provider - .estimate_eip1559_fees(None) - .await - .wrap_err("Failed to get EIP-1559 fees") - .map(|p| (p.0.to_alloy(), p.1.to_alloy())), + provider.estimate_eip1559_fees(None).await.wrap_err("Failed to get EIP-1559 fees"), ) }; - Ok(ProviderInfo { provider, chain, gas_price, is_legacy }) + Ok(ProviderInfo { provider, chain, gas_price }) } /// Returns the gas price to use - pub fn gas_price(&self) -> Result { + pub fn gas_price(&self) -> Result { let res = match &self.gas_price { GasPrice::Legacy(res) => res.as_ref(), - GasPrice::EIP1559(res) => res.as_ref().map(|res| &res.0), + GasPrice::EIP1559(res) => res.as_ref().map(|res| &res.max_fee_per_gas), }; match res { Ok(val) => Ok(*val), diff --git a/crates/script/src/receipts.rs b/crates/script/src/receipts.rs index 29848aad3..3bdc228f0 100644 --- a/crates/script/src/receipts.rs +++ b/crates/script/src/receipts.rs @@ -1,27 +1,24 @@ use super::sequence::ScriptSequence; -use alloy_primitives::TxHash; -use ethers_core::types::TransactionReceipt; -use ethers_providers::{Middleware, PendingTransaction}; +use alloy_chains::Chain; +use alloy_primitives::{utils::format_units, TxHash, U256}; +use alloy_provider::{PendingTransactionBuilder, Provider}; +use alloy_rpc_types::AnyTransactionReceipt; use eyre::Result; -use foundry_cli::{init_progress, update_progress, utils::print_receipt}; -use foundry_common::{ - provider::ethers::RetryProvider, - types::{ToAlloy, ToEthers}, -}; +use foundry_cli::{init_progress, update_progress}; +use foundry_common::provider::alloy::RetryProvider; use futures::StreamExt; use std::sync::Arc; /// Convenience enum for internal signalling of transaction status enum TxStatus { Dropped, - Success(TransactionReceipt), - Revert(TransactionReceipt), + Success(AnyTransactionReceipt), + Revert(AnyTransactionReceipt), } -impl From for TxStatus { - fn from(receipt: TransactionReceipt) -> Self { - let status = receipt.status.expect("receipt is from an ancient, pre-EIP658 block"); - if status.is_zero() { +impl From for TxStatus { + fn from(receipt: AnyTransactionReceipt) -> Self { + if !receipt.inner.inner.inner.receipt.status { TxStatus::Revert(receipt) } else { TxStatus::Success(receipt) @@ -68,7 +65,7 @@ pub async fn clear_pendings( let mut tasks = futures::stream::iter(futs).buffer_unordered(10); let mut errors: Vec = vec![]; - let mut receipts = Vec::::with_capacity(count); + let mut receipts = Vec::::with_capacity(count); // set up progress bar let mut pos = 0; @@ -87,7 +84,7 @@ pub async fn clear_pendings( } Ok(TxStatus::Success(receipt)) => { trace!(tx_hash=?tx_hash, "received tx receipt"); - deployment_sequence.remove_pending(receipt.transaction_hash.to_alloy()); + deployment_sequence.remove_pending(receipt.transaction_hash); receipts.push(receipt); } Ok(TxStatus::Revert(receipt)) => { @@ -95,7 +92,7 @@ pub async fn clear_pendings( // if this is not removed from pending, then the script becomes // un-resumable. Is this desirable on reverts? warn!(tx_hash=?tx_hash, "Transaction Failure"); - deployment_sequence.remove_pending(receipt.transaction_hash.to_alloy()); + deployment_sequence.remove_pending(receipt.transaction_hash); errors.push(format!("Transaction Failure: {:?}", receipt.transaction_hash)); } } @@ -105,7 +102,7 @@ pub async fn clear_pendings( } // sort receipts by blocks asc and index - receipts.sort_unstable(); + receipts.sort_by_key(|r| (r.block_number, r.transaction_index)); // print all receipts for receipt in receipts { @@ -136,20 +133,53 @@ async fn check_tx_status( // still neatly return the tuple let result = async move { // First check if there's a receipt - let receipt_opt = provider.get_transaction_receipt(hash.to_ethers()).await?; + let receipt_opt = provider.get_transaction_receipt(hash).await?; if let Some(receipt) = receipt_opt { return Ok(receipt.into()); } // If the tx is present in the mempool, run the pending tx future, and // assume the next drop is really really real - let pending_res = PendingTransaction::new(hash.to_ethers(), provider).await?; - match pending_res { - Some(receipt) => Ok(receipt.into()), - None => Ok(TxStatus::Dropped), - } + Ok(PendingTransactionBuilder::new(provider, hash) + .get_receipt() + .await + .map_or(TxStatus::Dropped, |r| r.into())) } .await; (hash, result) } + +/// Prints parts of the receipt to stdout +pub fn print_receipt(chain: Chain, receipt: &AnyTransactionReceipt) { + let gas_used = receipt.gas_used; + let gas_price = receipt.effective_gas_price; + foundry_common::shell::println(format!( + "\n##### {chain}\n{status}Hash: {tx_hash:?}{caddr}\nBlock: {bn}\n{gas}\n", + status = if !receipt.inner.inner.inner.receipt.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('.') + ) + }, + )) + .expect("could not print receipt"); +} diff --git a/crates/script/src/sequence.rs b/crates/script/src/sequence.rs index 9fdee7b02..2d39c1292 100644 --- a/crates/script/src/sequence.rs +++ b/crates/script/src/sequence.rs @@ -1,18 +1,14 @@ use super::{multi_sequence::MultiChainSequence, NestedValue}; use crate::{ - transaction::{wrapper, AdditionalContract, TransactionWithMetadata}, + transaction::{AdditionalContract, TransactionWithMetadata}, verify::VerifyBundle, }; use alloy_primitives::{Address, TxHash}; -use ethers_core::types::{transaction::eip2718::TypedTransaction, TransactionReceipt}; +use alloy_rpc_types::{AnyTransactionReceipt, TransactionRequest, WithOtherFields}; use eyre::{ContextCompat, Result, WrapErr}; use forge_verify::provider::VerificationProviderType; use foundry_cli::utils::{now, Git}; -use foundry_common::{ - fs, shell, - types::{ToAlloy, ToEthers}, - SELECTOR_LEN, -}; +use foundry_common::{fs, shell, SELECTOR_LEN}; use foundry_compilers::ArtifactId; use foundry_config::Config; use serde::{Deserialize, Serialize}; @@ -91,8 +87,7 @@ pub const DRY_RUN_DIR: &str = "dry-run"; #[derive(Clone, Default, Serialize, Deserialize)] pub struct ScriptSequence { pub transactions: VecDeque, - #[serde(serialize_with = "wrapper::serialize_receipts")] - pub receipts: Vec, + pub receipts: Vec, pub libraries: Vec, pub pending: Vec, #[serde(skip)] @@ -201,13 +196,13 @@ impl ScriptSequence { Ok(()) } - pub fn add_receipt(&mut self, receipt: TransactionReceipt) { + pub fn add_receipt(&mut self, receipt: AnyTransactionReceipt) { self.receipts.push(receipt); } /// Sorts all receipts with ascending transaction index pub fn sort_receipts(&mut self) { - self.receipts.sort_unstable() + self.receipts.sort_by_key(|r| (r.block_number, r.transaction_index)); } pub fn add_pending(&mut self, index: usize, tx_hash: TxHash) { @@ -284,13 +279,13 @@ impl ScriptSequence { let mut offset = 0; if tx.is_create2() { - receipt.contract_address = tx.contract_address.map(|a| a.to_ethers()); + receipt.contract_address = tx.contract_address; offset = 32; } // Verify contract created directly from the transaction if let (Some(address), Some(data)) = - (receipt.contract_address.map(|h| h.to_alloy()), tx.typed_tx().data()) + (receipt.contract_address, tx.tx().input.input()) { match verify.get_verify_args(address, offset, &data.0, &self.libraries) { Some(verify) => future_verifications.push(verify.run()), @@ -300,7 +295,7 @@ impl ScriptSequence { // Verify potential contracts created during the transaction execution for AdditionalContract { address, init_code, .. } in &tx.additional_contracts { - match verify.get_verify_args(*address, 0, init_code, &self.libraries) { + match verify.get_verify_args(*address, 0, init_code.as_ref(), &self.libraries) { Some(verify) => future_verifications.push(verify.run()), None => unverifiable_contracts.push(*address), }; @@ -357,8 +352,8 @@ impl ScriptSequence { } /// Returns the list of the transactions without the metadata. - pub fn typed_transactions(&self) -> impl Iterator { - self.transactions.iter().map(|tx| tx.typed_tx()) + pub fn transactions(&self) -> impl Iterator> { + self.transactions.iter().map(|tx| tx.tx()) } pub fn fill_sensitive(&mut self, sensitive: &SensitiveScriptSequence) { diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index 96b1eaefe..c740e59a4 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -13,13 +13,12 @@ use crate::{ sequence::get_commit_hash, ScriptArgs, ScriptConfig, ScriptResult, }; +use alloy_network::TransactionBuilder; use alloy_primitives::{utils::format_units, Address, U256}; use eyre::{Context, Result}; use foundry_cheatcodes::{BroadcastableTransactions, ScriptWallets}; use foundry_cli::utils::{has_different_gas_calc, now}; -use foundry_common::{ - get_contract_name, provider::ethers::RpcUrl, shell, types::ToAlloy, ContractsByArtifact, -}; +use foundry_common::{get_contract_name, provider::alloy::RpcUrl, shell, ContractsByArtifact}; use foundry_evm::traces::render_trace_arena; use futures::future::{join_all, try_join_all}; use parking_lot::RwLock; @@ -132,9 +131,8 @@ impl PreSimulationState { } // We inflate the gas used by the user specified percentage None => { - let gas = - U256::from(result.gas_used * self.args.gas_estimate_multiplier / 100); - tx.gas = Some(gas); + let gas = result.gas_used * self.args.gas_estimate_multiplier / 100; + tx.gas = Some(gas as u128); } } let tx = TransactionWithMetadata::new( @@ -266,7 +264,7 @@ impl FilledTransactionsState { eyre::bail!("Multi-chain deployment is not supported with libraries."); } - let mut total_gas_per_rpc: HashMap = HashMap::new(); + let mut total_gas_per_rpc: HashMap = HashMap::new(); // Batches sequence of transactions from different rpcs. let mut new_sequence = VecDeque::new(); @@ -282,15 +280,14 @@ impl FilledTransactionsState { let provider_info = manager.get_or_init_provider(&tx.rpc, self.args.legacy).await?; // Handles chain specific requirements. - tx.change_type(provider_info.is_legacy); tx.transaction.set_chain_id(provider_info.chain); if !self.args.skip_simulation { - let typed_tx = tx.typed_tx_mut(); + let tx = tx.tx_mut(); if has_different_gas_calc(provider_info.chain) { trace!("estimating with different gas calculation"); - let gas = *typed_tx.gas().expect("gas is set by simulation."); + let gas = tx.gas.expect("gas is set by simulation."); // We are trying to show the user an estimation of the total gas usage. // @@ -303,22 +300,19 @@ impl FilledTransactionsState { // for chains where `has_different_gas_calc` returns true, // we await each transaction before broadcasting the next // one. - if let Err(err) = estimate_gas( - typed_tx, - &provider_info.provider, - self.args.gas_estimate_multiplier, - ) - .await + if let Err(err) = + estimate_gas(tx, &provider_info.provider, self.args.gas_estimate_multiplier) + .await { trace!("gas estimation failed: {err}"); // Restore gas value, since `estimate_gas` will remove it. - typed_tx.set_gas(gas); + tx.set_gas_limit(gas); } } - let total_gas = total_gas_per_rpc.entry(tx_rpc.clone()).or_insert(U256::ZERO); - *total_gas += (*typed_tx.gas().expect("gas is set")).to_alloy(); + let total_gas = total_gas_per_rpc.entry(tx_rpc.clone()).or_insert(0); + *total_gas += tx.gas.expect("gas is set"); } new_sequence.push_back(tx); @@ -346,7 +340,7 @@ impl FilledTransactionsState { // We don't store it in the transactions, since we want the most updated value. // Right before broadcasting. let per_gas = if let Some(gas_price) = self.args.with_gas_price { - gas_price + gas_price.to() } else { provider_info.gas_price()? }; diff --git a/crates/script/src/transaction.rs b/crates/script/src/transaction.rs index 80a281420..e74699e74 100644 --- a/crates/script/src/transaction.rs +++ b/crates/script/src/transaction.rs @@ -1,18 +1,9 @@ use super::{artifacts::ArtifactInfo, ScriptResult}; use alloy_dyn_abi::JsonAbiExt; use alloy_primitives::{Address, Bytes, B256}; -use alloy_rpc_types::request::TransactionRequest; -use ethers_core::types::{ - transaction::eip2718::TypedTransaction, NameOrAddress, - TransactionRequest as EthersTransactionRequest, -}; +use alloy_rpc_types::{request::TransactionRequest, WithOtherFields}; use eyre::{ContextCompat, Result, WrapErr}; -use foundry_common::{ - fmt::format_token_raw, - provider::ethers::RpcUrl, - types::{ToAlloy, ToEthers}, - SELECTOR_LEN, -}; +use foundry_common::{fmt::format_token_raw, provider::alloy::RpcUrl, SELECTOR_LEN}; use foundry_evm::{constants::DEFAULT_CREATE2_DEPLOYER, traces::CallTraceDecoder}; use itertools::Itertools; use revm_inspectors::tracing::types::CallKind; @@ -24,7 +15,6 @@ use std::collections::BTreeMap; pub struct AdditionalContract { #[serde(rename = "transactionType")] pub opcode: CallKind, - #[serde(serialize_with = "wrapper::serialize_addr")] pub address: Address, pub init_code: Bytes, } @@ -37,7 +27,7 @@ pub struct TransactionWithMetadata { pub opcode: CallKind, #[serde(default = "default_string")] pub contract_name: Option, - #[serde(default = "default_address", serialize_with = "wrapper::serialize_opt_addr")] + #[serde(default = "default_address")] pub contract_address: Option
, #[serde(default = "default_string")] pub function: Option, @@ -45,7 +35,7 @@ pub struct TransactionWithMetadata { pub arguments: Option>, #[serde(skip)] pub rpc: RpcUrl, - pub transaction: TypedTransaction, + pub transaction: WithOtherFields, pub additional_contracts: Vec, pub is_fixed_gas_limit: bool, } @@ -64,18 +54,7 @@ fn default_vec_of_strings() -> Option> { impl TransactionWithMetadata { pub fn from_tx_request(transaction: TransactionRequest) -> Self { - Self { - transaction: TypedTransaction::Legacy(EthersTransactionRequest { - from: transaction.from.map(ToEthers::to_ethers), - to: transaction.to.map(ToEthers::to_ethers).map(Into::into), - value: transaction.value.map(ToEthers::to_ethers), - data: transaction.input.into_input().map(ToEthers::to_ethers), - nonce: transaction.nonce.map(|n| n.to::().into()), - gas: transaction.gas.map(ToEthers::to_ethers), - ..Default::default() - }), - ..Default::default() - } + Self { transaction: WithOtherFields::new(transaction), ..Default::default() } } pub fn new( @@ -92,8 +71,8 @@ impl TransactionWithMetadata { metadata.is_fixed_gas_limit = is_fixed_gas_limit; // Specify if any contract was directly created with this transaction - if let Some(NameOrAddress::Address(to)) = metadata.transaction.to().cloned() { - if to.to_alloy() == DEFAULT_CREATE2_DEPLOYER { + if let Some(to) = metadata.transaction.to { + if to == DEFAULT_CREATE2_DEPLOYER { metadata.set_create( true, Address::from_slice(&result.returned), @@ -101,10 +80,10 @@ impl TransactionWithMetadata { )?; } else { metadata - .set_call(to.to_alloy(), local_contracts, decoder) + .set_call(to, local_contracts, decoder) .wrap_err("Could not decode transaction type.")?; } - } else if metadata.transaction.to().is_none() { + } else { metadata.set_create( false, result.address.wrap_err("There should be a contract address from CREATE.")?, @@ -150,7 +129,7 @@ impl TransactionWithMetadata { self.contract_name = info.map(|info| info.contract_name.clone()); self.contract_address = Some(address); - let Some(data) = self.transaction.data() else { return Ok(()) }; + let Some(data) = self.transaction.input.input() else { return Ok(()) }; let Some(info) = info else { return Ok(()) }; // `create2` transactions are prefixed by a 32 byte salt. @@ -197,7 +176,7 @@ impl TransactionWithMetadata { self.opcode = CallKind::Call; self.contract_address = Some(target); - let Some(data) = self.transaction.data() else { return Ok(()) }; + let Some(data) = self.transaction.input.input() else { return Ok(()) }; if data.len() < SELECTOR_LEN { return Ok(()); } @@ -229,19 +208,11 @@ impl TransactionWithMetadata { Ok(()) } - pub fn change_type(&mut self, is_legacy: bool) { - self.transaction = if is_legacy { - TypedTransaction::Legacy(self.transaction.clone().into()) - } else { - TypedTransaction::Eip1559(self.transaction.clone().into()) - }; - } - - pub fn typed_tx(&self) -> &TypedTransaction { + pub fn tx(&self) -> &WithOtherFields { &self.transaction } - pub fn typed_tx_mut(&mut self) -> &mut TypedTransaction { + pub fn tx_mut(&mut self) -> &mut WithOtherFields { &mut self.transaction } @@ -249,208 +220,3 @@ impl TransactionWithMetadata { self.opcode == CallKind::Create2 } } - -// wrapper for modifying ethers-rs type serialization -pub mod wrapper { - pub use super::*; - use ethers_core::{ - types::{Bloom, Bytes, Log, TransactionReceipt, H256, U256, U64}, - utils::to_checksum, - }; - - pub fn serialize_addr(addr: &Address, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(&to_checksum(&addr.to_ethers(), None)) - } - - pub fn serialize_opt_addr(opt: &Option
, serializer: S) -> Result - where - S: serde::Serializer, - { - match opt { - Some(addr) => serialize_addr(addr, serializer), - None => serializer.serialize_none(), - } - } - - pub fn serialize_vec_with_wrapped( - vec: &[T], - serializer: S, - ) -> Result - where - S: serde::Serializer, - T: Clone, - WrappedType: serde::Serialize + From, - { - serializer.collect_seq(vec.iter().cloned().map(WrappedType::from)) - } - - // copied from https://github.com/gakonst/ethers-rs - #[derive(Serialize, Deserialize)] - struct WrappedLog { - /// The contract address that emitted the log. - #[serde(serialize_with = "serialize_addr")] - pub address: Address, - - /// Array of 0 to 4 32 Bytes of indexed log arguments. - /// - /// (In solidity: The first topic is the hash of the signature of the event - /// (e.g. `Deposit(address,bytes32,uint256)`), except you declared the event - /// with the anonymous specifier.) - pub topics: Vec, - - /// Data - pub data: Bytes, - - /// Block Hash - #[serde(rename = "blockHash")] - #[serde(skip_serializing_if = "Option::is_none")] - pub block_hash: Option, - - /// Block Number - #[serde(rename = "blockNumber")] - #[serde(skip_serializing_if = "Option::is_none")] - pub block_number: Option, - - /// Transaction Hash - #[serde(rename = "transactionHash")] - #[serde(skip_serializing_if = "Option::is_none")] - pub transaction_hash: Option, - - /// Transaction Index - #[serde(rename = "transactionIndex")] - #[serde(skip_serializing_if = "Option::is_none")] - pub transaction_index: Option, - - /// Integer of the log index position in the block. None if it's a pending log. - #[serde(rename = "logIndex")] - #[serde(skip_serializing_if = "Option::is_none")] - pub log_index: Option, - - /// Integer of the transactions index position log was created from. - /// None when it's a pending log. - #[serde(rename = "transactionLogIndex")] - #[serde(skip_serializing_if = "Option::is_none")] - pub transaction_log_index: Option, - - /// Log Type - #[serde(rename = "logType")] - #[serde(skip_serializing_if = "Option::is_none")] - pub log_type: Option, - - /// True when the log was removed, due to a chain reorganization. - /// false if it's a valid log. - #[serde(skip_serializing_if = "Option::is_none")] - pub removed: Option, - } - impl From for WrappedLog { - fn from(log: Log) -> Self { - Self { - address: log.address.to_alloy(), - topics: log.topics, - data: log.data, - block_hash: log.block_hash, - block_number: log.block_number, - transaction_hash: log.transaction_hash, - transaction_index: log.transaction_index, - log_index: log.log_index, - transaction_log_index: log.transaction_log_index, - log_type: log.log_type, - removed: log.removed, - } - } - } - - fn serialize_logs( - logs: &[Log], - serializer: S, - ) -> Result { - serialize_vec_with_wrapped::(logs, serializer) - } - - // "Receipt" of an executed transaction: details of its execution. - // copied from https://github.com/gakonst/ethers-rs - #[derive(Clone, Default, Serialize, Deserialize)] - pub struct WrappedTransactionReceipt { - /// Transaction hash. - #[serde(rename = "transactionHash")] - pub transaction_hash: H256, - /// Index within the block. - #[serde(rename = "transactionIndex")] - pub transaction_index: U64, - /// Hash of the block this transaction was included within. - #[serde(rename = "blockHash")] - pub block_hash: Option, - /// Number of the block this transaction was included within. - #[serde(rename = "blockNumber")] - pub block_number: Option, - /// The address of the sender. - #[serde(serialize_with = "serialize_addr")] - pub from: Address, - // The address of the receiver. `None` when its a contract creation transaction. - #[serde(serialize_with = "serialize_opt_addr")] - pub to: Option
, - /// Cumulative gas used within the block after this was executed. - #[serde(rename = "cumulativeGasUsed")] - pub cumulative_gas_used: U256, - /// Gas used by this transaction alone. - /// - /// Gas used is `None` if the client is running in light client mode. - #[serde(rename = "gasUsed")] - pub gas_used: Option, - /// Contract address created, or `None` if not a deployment. - #[serde(rename = "contractAddress", serialize_with = "serialize_opt_addr")] - pub contract_address: Option
, - /// Logs generated within this transaction. - #[serde(serialize_with = "serialize_logs")] - pub logs: Vec, - /// Status: either 1 (success) or 0 (failure). Only present after activation of [EIP-658](https://eips.ethereum.org/EIPS/eip-658) - pub status: Option, - /// State root. Only present before activation of [EIP-658](https://eips.ethereum.org/EIPS/eip-658) - #[serde(default, skip_serializing_if = "Option::is_none")] - pub root: Option, - /// Logs bloom - #[serde(rename = "logsBloom")] - pub logs_bloom: Bloom, - /// Transaction type, Some(1) for AccessList transaction, None for Legacy - #[serde(rename = "type", default, skip_serializing_if = "Option::is_none")] - pub transaction_type: Option, - /// The price paid post-execution by the transaction (i.e. base fee + priority fee). - /// Both fields in 1559-style transactions are *maximums* (max fee + max priority fee), the - /// amount that's actually paid by users can only be determined post-execution - #[serde(rename = "effectiveGasPrice", default, skip_serializing_if = "Option::is_none")] - pub effective_gas_price: Option, - } - impl From for WrappedTransactionReceipt { - fn from(receipt: TransactionReceipt) -> Self { - Self { - transaction_hash: receipt.transaction_hash, - transaction_index: receipt.transaction_index, - block_hash: receipt.block_hash, - block_number: receipt.block_number, - from: receipt.from.to_alloy(), - to: receipt.to.map(|addr| addr.to_alloy()), - cumulative_gas_used: receipt.cumulative_gas_used, - gas_used: receipt.gas_used, - contract_address: receipt.contract_address.map(|addr| addr.to_alloy()), - logs: receipt.logs, - status: receipt.status, - root: receipt.root, - logs_bloom: receipt.logs_bloom, - transaction_type: receipt.transaction_type, - effective_gas_price: receipt.effective_gas_price, - } - } - } - - pub fn serialize_receipts( - receipts: &[TransactionReceipt], - serializer: S, - ) -> Result { - serialize_vec_with_wrapped::( - receipts, serializer, - ) - } -} diff --git a/crates/test-utils/Cargo.toml b/crates/test-utils/Cargo.toml index 8e2b75931..09025e27c 100644 --- a/crates/test-utils/Cargo.toml +++ b/crates/test-utils/Cargo.toml @@ -17,9 +17,7 @@ foundry-compilers = { workspace = true, features = ["project-util"] } foundry-config.workspace = true alloy-primitives.workspace = true - -ethers-core.workspace = true -ethers-providers.workspace = true +alloy-provider.workspace = true eyre.workspace = true fd-lock = "4.0.0" diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index b60723d9d..fecbc9f5f 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -1,12 +1,8 @@ use crate::{init_tracing, TestCommand}; -use alloy_primitives::{Address, U256}; -use ethers_core::types::NameOrAddress; -use ethers_providers::Middleware; +use alloy_primitives::Address; +use alloy_provider::Provider; use eyre::Result; -use foundry_common::{ - provider::ethers::{get_http_provider, RetryProvider}, - types::{ToAlloy, ToEthers}, -}; +use foundry_common::provider::alloy::{get_http_provider, RetryProvider}; use std::{collections::BTreeMap, fs, path::Path, str::FromStr}; const BROADCAST_TEST_PATH: &str = "src/Broadcast.t.sol"; @@ -17,8 +13,8 @@ pub struct ScriptTester { pub accounts_pub: Vec
, pub accounts_priv: Vec, pub provider: Option, - pub nonces: BTreeMap, - pub address_nonces: BTreeMap, + pub nonces: BTreeMap, + pub address_nonces: BTreeMap, pub cmd: TestCommand, } @@ -121,13 +117,10 @@ impl ScriptTester { if let Some(provider) = &self.provider { let nonce = provider - .get_transaction_count( - NameOrAddress::Address(self.accounts_pub[index as usize].to_ethers()), - None, - ) + .get_transaction_count(self.accounts_pub[index as usize], None) .await .unwrap(); - self.nonces.insert(index, nonce.to_alloy()); + self.nonces.insert(index, nonce); } } self @@ -135,14 +128,9 @@ impl ScriptTester { pub async fn load_addresses(&mut self, addresses: &[Address]) -> &mut Self { for &address in addresses { - let nonce = self - .provider - .as_ref() - .unwrap() - .get_transaction_count(NameOrAddress::Address(address.to_ethers()), None) - .await - .unwrap(); - self.address_nonces.insert(address, nonce.to_alloy()); + let nonce = + self.provider.as_ref().unwrap().get_transaction_count(address, None).await.unwrap(); + self.address_nonces.insert(address, nonce); } self } @@ -181,18 +169,13 @@ impl ScriptTester { pub async fn assert_nonce_increment(&mut self, keys_indexes: &[(u32, u32)]) -> &mut Self { for &(private_key_slot, expected_increment) in keys_indexes { let addr = self.accounts_pub[private_key_slot as usize]; - let nonce = self - .provider - .as_ref() - .unwrap() - .get_transaction_count(NameOrAddress::Address(addr.to_ethers()), None) - .await - .unwrap(); + let nonce = + self.provider.as_ref().unwrap().get_transaction_count(addr, None).await.unwrap(); let prev_nonce = self.nonces.get(&private_key_slot).unwrap(); assert_eq!( nonce, - (prev_nonce + U256::from(expected_increment)).to_ethers(), + (*prev_nonce + expected_increment as u64), "nonce not incremented correctly for {addr}: \ {prev_nonce} + {expected_increment} != {nonce}" ); @@ -210,12 +193,12 @@ impl ScriptTester { .provider .as_ref() .unwrap() - .get_transaction_count(NameOrAddress::Address(address.to_ethers()), None) + .get_transaction_count(*address, None) .await .unwrap(); let prev_nonce = self.address_nonces.get(address).unwrap(); - assert_eq!(nonce, (prev_nonce + U256::from(*expected_increment)).to_ethers()); + assert_eq!(nonce, *prev_nonce + *expected_increment as u64); } self } diff --git a/crates/verify/Cargo.toml b/crates/verify/Cargo.toml index a735b39af..832cf1c6b 100644 --- a/crates/verify/Cargo.toml +++ b/crates/verify/Cargo.toml @@ -22,7 +22,7 @@ alloy-json-abi.workspace = true alloy-primitives.workspace = true serde.workspace = true eyre.workspace = true -ethers-providers.workspace = true +alloy-provider.workspace = true tracing.workspace = true foundry-compilers = { workspace = true, features = ["full"] } foundry-block-explorers = { workspace = true, features = ["foundry-compilers"] } diff --git a/crates/verify/src/etherscan/mod.rs b/crates/verify/src/etherscan/mod.rs index 5922f64ad..ec218e714 100644 --- a/crates/verify/src/etherscan/mod.rs +++ b/crates/verify/src/etherscan/mod.rs @@ -1,7 +1,7 @@ use super::{provider::VerificationProvider, VerifyArgs, VerifyCheckArgs}; use crate::retry::RETRY_CHECK_ON_VERIFY; use alloy_json_abi::Function; -use ethers_providers::Middleware; +use alloy_provider::Provider; use eyre::{eyre, Context, OptionExt, Result}; use foundry_block_explorers::{ errors::EtherscanError, @@ -10,7 +10,7 @@ use foundry_block_explorers::{ Client, }; use foundry_cli::utils::{self, get_cached_entry_by_name, read_constructor_args_file, LoadConfig}; -use foundry_common::{abi::encode_function_args, retry::Retry, types::ToEthers}; +use foundry_common::{abi::encode_function_args, retry::Retry}; use foundry_compilers::{ artifacts::{BytecodeObject, CompactContract}, cache::CacheEntry, @@ -498,20 +498,17 @@ impl EtherscanVerificationProvider { )?; let creation_data = client.contract_creation_data(args.address).await?; - let transaction = provider - .get_transaction(creation_data.transaction_hash.to_ethers()) - .await? - .ok_or_eyre("Couldn't fetch transaction data from RPC")?; + let transaction = provider.get_transaction_by_hash(creation_data.transaction_hash).await?; let receipt = provider - .get_transaction_receipt(creation_data.transaction_hash.to_ethers()) + .get_transaction_receipt(creation_data.transaction_hash) .await? .ok_or_eyre("Couldn't fetch transaction receipt from RPC")?; let maybe_creation_code: &[u8]; - if receipt.contract_address == Some(args.address.to_ethers()) { + if receipt.contract_address == Some(args.address) { maybe_creation_code = &transaction.input; - } else if transaction.to == Some(DEFAULT_CREATE2_DEPLOYER.to_ethers()) { + } else if transaction.to == Some(DEFAULT_CREATE2_DEPLOYER) { maybe_creation_code = &transaction.input[32..]; } else { eyre::bail!("Fetching of constructor arguments is not supported for contracts created by contracts") diff --git a/crates/wallets/Cargo.toml b/crates/wallets/Cargo.toml index 1ef26972d..31ea5607c 100644 --- a/crates/wallets/Cargo.toml +++ b/crates/wallets/Cargo.toml @@ -11,13 +11,18 @@ repository.workspace = true [dependencies] alloy-primitives.workspace = true - -ethers-core.workspace = true -ethers-providers.workspace = true -ethers-signers = { workspace = true, features = ["aws", "ledger", "trezor"] } - -rusoto_core = { version = "0.48", default-features = false } -rusoto_kms = { version = "0.48", default-features = false } +alloy-signer = { workspace = true, features = ["eip712"] } +alloy-signer-wallet = { workspace = true, features = ["mnemonic", "keystore"] } +alloy-signer-aws.workspace = true +alloy-signer-ledger.workspace = true +alloy-signer-trezor.workspace = true +alloy-network.workspace = true +alloy-consensus.workspace = true +alloy-sol-types.workspace = true +alloy-dyn-abi.workspace = true + +aws-sdk-kms = { version = "1", default-features = false } +aws-config = "1" foundry-config.workspace = true foundry-common.workspace = true @@ -38,5 +43,4 @@ tokio = { version = "1", features = ["macros"] } [features] default = ["rustls"] -rustls = ["ethers-providers/rustls", "rusoto_core/rustls"] -openssl = ["ethers-providers/openssl"] +rustls = ["aws-sdk-kms/rustls"] diff --git a/crates/wallets/src/error.rs b/crates/wallets/src/error.rs index 6588f5e22..b9a5e34f5 100644 --- a/crates/wallets/src/error.rs +++ b/crates/wallets/src/error.rs @@ -1,4 +1,8 @@ -use ethers_signers::{AwsSignerError, LedgerError, TrezorError, WalletError}; +use alloy_signer::k256::ecdsa; +use alloy_signer_aws::AwsSignerError; +use alloy_signer_ledger::LedgerError; +use alloy_signer_trezor::TrezorError; +use alloy_signer_wallet::WalletError; use hex::FromHexError; #[derive(Debug, thiserror::Error)] @@ -23,6 +27,8 @@ pub enum WalletSignerError { Io(#[from] std::io::Error), #[error(transparent)] InvalidHex(#[from] FromHexError), + #[error(transparent)] + Ecdsa(#[from] ecdsa::Error), #[error("{0} cannot sign raw hashes")] CannotSignRawHash(&'static str), } diff --git a/crates/wallets/src/multi_wallet.rs b/crates/wallets/src/multi_wallet.rs index d9673985a..c95bb8d0e 100644 --- a/crates/wallets/src/multi_wallet.rs +++ b/crates/wallets/src/multi_wallet.rs @@ -3,11 +3,10 @@ use crate::{ wallet_signer::{PendingSigner, WalletSigner}, }; use alloy_primitives::Address; +use alloy_signer::Signer; use clap::Parser; use derive_builder::Builder; -use ethers_signers::Signer; use eyre::Result; -use foundry_common::types::ToAlloy; use foundry_config::Config; use serde::Serialize; use std::{collections::HashMap, iter::repeat, path::PathBuf}; @@ -24,15 +23,14 @@ pub struct MultiWallet { impl MultiWallet { pub fn new(pending_signers: Vec, signers: Vec) -> Self { - let signers = - signers.into_iter().map(|signer| (signer.address().to_alloy(), signer)).collect(); + let signers = signers.into_iter().map(|signer| (signer.address(), signer)).collect(); Self { pending_signers, signers } } fn maybe_unlock_pending(&mut self) -> Result<()> { for pending in self.pending_signers.drain(..) { let signer = pending.unlock()?; - self.signers.insert(signer.address().to_alloy(), signer); + self.signers.insert(signer.address(), signer); } Ok(()) } @@ -48,7 +46,7 @@ impl MultiWallet { } pub fn add_signer(&mut self, signer: WalletSigner) { - self.signers.insert(signer.address().to_alloy(), signer); + self.signers.insert(signer.address(), signer); } } @@ -386,7 +384,7 @@ impl MultiWalletOpts { .collect::>(); for key in aws_keys { - let aws_signer = WalletSigner::from_aws(&key).await?; + let aws_signer = WalletSigner::from_aws(key).await?; wallets.push(aws_signer) } @@ -399,7 +397,7 @@ impl MultiWalletOpts { #[cfg(test)] mod tests { use super::*; - use std::path::Path; + use std::{path::Path, str::FromStr}; #[test] fn parse_keystore_args() { @@ -439,7 +437,7 @@ mod tests { assert_eq!(unlocked.len(), 1); assert_eq!( unlocked[0].address(), - "ec554aeafe75601aaab43bd4621a22284db566c2".parse().unwrap() + Address::from_str("0xec554aeafe75601aaab43bd4621a22284db566c2").unwrap() ); } diff --git a/crates/wallets/src/utils.rs b/crates/wallets/src/utils.rs index a10903313..08c95242a 100644 --- a/crates/wallets/src/utils.rs +++ b/crates/wallets/src/utils.rs @@ -1,39 +1,35 @@ use crate::{error::PrivateKeyError, PendingSigner, WalletSigner}; -use ethers_signers::{HDPath as LedgerHDPath, LocalWallet, TrezorHDPath, WalletError}; +use alloy_primitives::B256; +use alloy_signer_ledger::HDPath as LedgerHDPath; +use alloy_signer_trezor::HDPath as TrezorHDPath; +use alloy_signer_wallet::LocalWallet; use eyre::{Context, Result}; use foundry_config::Config; use std::{ fs, path::{Path, PathBuf}, - str::FromStr, }; +fn ensure_pk_not_env(pk: &str) -> Result<()> { + if !pk.starts_with("0x") && std::env::var(pk).is_ok() { + return Err(PrivateKeyError::ExistsAsEnvVar(pk.to_string()).into()); + } + Ok(()) +} + /// Validates and sanitizes user inputs, returning configured [WalletSigner]. pub fn create_private_key_signer(private_key: &str) -> Result { let privk = private_key.trim().strip_prefix("0x").unwrap_or(private_key); - match LocalWallet::from_str(privk) { + + let Ok(private_key) = hex::decode(privk) else { + ensure_pk_not_env(privk)?; + eyre::bail!("Failed to decode private key") + }; + + match LocalWallet::from_bytes(&B256::from_slice(&private_key)) { Ok(pk) => Ok(WalletSigner::Local(pk)), Err(err) => { - // helper closure to check if pk was meant to be an env var, this usually happens if - // `$` is missing - let ensure_not_env = |pk: &str| { - // check if pk was meant to be an env var - if !pk.starts_with("0x") && std::env::var(pk).is_ok() { - // SAFETY: at this point we know the user actually wanted to use an env var - // and most likely forgot the `$` anchor, so the - // `private_key` here is an unresolved env var - return Err(PrivateKeyError::ExistsAsEnvVar(pk.to_string())) - } - Ok(()) - }; - match err { - WalletError::HexError(err) => { - ensure_not_env(private_key)?; - return Err(PrivateKeyError::InvalidHex(err).into()); - } - WalletError::EcdsaError(_) => ensure_not_env(private_key)?, - _ => {} - }; + ensure_pk_not_env(privk)?; eyre::bail!("Failed to create wallet from private key: {err}") } } diff --git a/crates/wallets/src/wallet.rs b/crates/wallets/src/wallet.rs index cd7359f2e..5773e57d8 100644 --- a/crates/wallets/src/wallet.rs +++ b/crates/wallets/src/wallet.rs @@ -1,9 +1,8 @@ use crate::{raw_wallet::RawWalletOpts, utils, wallet_signer::WalletSigner}; use alloy_primitives::Address; +use alloy_signer::Signer; use clap::Parser; -use ethers_signers::Signer; use eyre::Result; -use foundry_common::types::ToAlloy; use serde::Serialize; /// The wallet options can either be: @@ -95,7 +94,7 @@ impl WalletOpts { .await? } else if self.aws { let key_id = std::env::var("AWS_KMS_KEY_ID")?; - WalletSigner::from_aws(&key_id).await? + WalletSigner::from_aws(key_id).await? } else if let Some(raw_wallet) = self.raw.signer()? { raw_wallet } else if let Some(path) = utils::maybe_get_keystore_path( @@ -139,7 +138,7 @@ of the unlocked account you want to use, or provide the --from flag with the add if let Some(from) = self.from { from } else if let Ok(signer) = self.signer().await { - signer.address().to_alloy() + signer.address() } else { Address::ZERO } @@ -176,7 +175,7 @@ mod tests { ]); let signer = wallet.signer().await.unwrap(); assert_eq!( - signer.address().to_alloy(), + signer.address(), Address::from_str("ec554aeafe75601aaab43bd4621a22284db566c2").unwrap() ); } @@ -207,7 +206,7 @@ mod tests { } Err(x) => { assert!( - x.to_string().contains("Failed to create wallet"), + x.to_string().contains("Failed to decode private key"), "Error message is not user-friendly" ); } diff --git a/crates/wallets/src/wallet_signer.rs b/crates/wallets/src/wallet_signer.rs index d71fbe1af..9cf4478e2 100644 --- a/crates/wallets/src/wallet_signer.rs +++ b/crates/wallets/src/wallet_signer.rs @@ -1,19 +1,17 @@ use crate::error::WalletSignerError; -use alloy_primitives::B256; +use alloy_consensus::SignableTransaction; +use alloy_dyn_abi::TypedData; +use alloy_network::TxSigner; +use alloy_primitives::{Address, ChainId, B256}; +use alloy_signer::{Signature, Signer}; +use alloy_signer_aws::AwsSigner; +use alloy_signer_ledger::{HDPath as LedgerHDPath, LedgerSigner}; +use alloy_signer_trezor::{HDPath as TrezorHDPath, TrezorSigner}; +use alloy_signer_wallet::{coins_bip39::English, LocalWallet, MnemonicBuilder}; +use alloy_sol_types::{Eip712Domain, SolStruct}; use async_trait::async_trait; -use ethers_core::types::{ - transaction::{eip2718::TypedTransaction, eip712::Eip712}, - Signature, -}; -use ethers_signers::{ - coins_bip39::English, AwsSigner, HDPath as LedgerHDPath, Ledger, LocalWallet, MnemonicBuilder, - Signer, Trezor, TrezorHDPath, -}; -use rusoto_core::{ - credential::ChainProvider as AwsChainProvider, region::Region as AwsRegion, - request::HttpClient as AwsHttpClient, Client as AwsClient, -}; -use rusoto_kms::KmsClient; +use aws_config::BehaviorVersion; +use aws_sdk_kms::Client as AwsClient; use std::path::PathBuf; pub type Result = std::result::Result; @@ -24,36 +22,34 @@ pub enum WalletSigner { /// Wrapper around local wallet. e.g. private key, mnemonic Local(LocalWallet), /// Wrapper around Ledger signer. - Ledger(Ledger), + Ledger(LedgerSigner), /// Wrapper around Trezor signer. - Trezor(Trezor), + Trezor(TrezorSigner), /// Wrapper around AWS KMS signer. Aws(AwsSigner), } impl WalletSigner { pub async fn from_ledger_path(path: LedgerHDPath) -> Result { - let ledger = Ledger::new(path, 1).await?; + let ledger = LedgerSigner::new(path, None).await?; Ok(Self::Ledger(ledger)) } pub async fn from_trezor_path(path: TrezorHDPath) -> Result { // cached to ~/.ethers-rs/trezor/cache/trezor.session - let trezor = Trezor::new(path, 1, None).await?; + let trezor = TrezorSigner::new(path, None).await?; Ok(Self::Trezor(trezor)) } - pub async fn from_aws(key_id: &str) -> Result { - let client = - AwsClient::new_with(AwsChainProvider::default(), AwsHttpClient::new().unwrap()); + pub async fn from_aws(key_id: String) -> Result { + let config = aws_config::load_defaults(BehaviorVersion::latest()).await; + let client = AwsClient::new(&config); - let kms = KmsClient::new_with_client(client, AwsRegion::default()); - - Ok(Self::Aws(AwsSigner::new(kms, key_id, 1).await?)) + Ok(Self::Aws(AwsSigner::new(client, key_id, None).await?)) } pub fn from_private_key(private_key: impl AsRef<[u8]>) -> Result { - let wallet = LocalWallet::from_bytes(private_key.as_ref())?; + let wallet = LocalWallet::from_bytes(&B256::from_slice(private_key.as_ref()))?; Ok(Self::Local(wallet)) } @@ -63,7 +59,7 @@ impl WalletSigner { /// - the result for Ledger signers includes addresses available for both LedgerLive and Legacy /// derivation paths /// - for Local and AWS signers the result contains a single address - pub async fn available_senders(&self, max: usize) -> Result> { + pub async fn available_senders(&self, max: usize) -> Result> { let mut senders = Vec::new(); match self { WalletSigner::Local(local) => { @@ -136,78 +132,53 @@ macro_rules! delegate { #[async_trait] impl Signer for WalletSigner { - type Error = WalletSignerError; - - async fn sign_message>(&self, message: S) -> Result { - delegate!(self, inner => inner.sign_message(message).await.map_err(Into::into)) - } - - async fn sign_transaction(&self, message: &TypedTransaction) -> Result { - delegate!(self, inner => inner.sign_transaction(message).await.map_err(Into::into)) + /// Signs the given hash. + async fn sign_hash(&self, hash: &B256) -> alloy_signer::Result { + delegate!(self, inner => inner.sign_hash(hash)).await } - async fn sign_typed_data(&self, payload: &T) -> Result { - delegate!(self, inner => inner.sign_typed_data(payload).await.map_err(Into::into)) + async fn sign_message(&self, message: &[u8]) -> alloy_signer::Result { + delegate!(self, inner => inner.sign_message(message)).await } - fn address(&self) -> ethers_core::types::Address { + fn address(&self) -> Address { delegate!(self, inner => inner.address()) } - fn chain_id(&self) -> u64 { + fn chain_id(&self) -> Option { delegate!(self, inner => inner.chain_id()) } - fn with_chain_id>(self, chain_id: T) -> Self { - match self { - Self::Local(inner) => Self::Local(inner.with_chain_id(chain_id)), - Self::Ledger(inner) => Self::Ledger(inner.with_chain_id(chain_id)), - Self::Trezor(inner) => Self::Trezor(inner.with_chain_id(chain_id)), - Self::Aws(inner) => Self::Aws(inner.with_chain_id(chain_id)), - } - } -} - -#[async_trait] -impl Signer for &WalletSigner { - type Error = WalletSignerError; - - async fn sign_message>(&self, message: S) -> Result { - (*self).sign_message(message).await + fn set_chain_id(&mut self, chain_id: Option) { + delegate!(self, inner => inner.set_chain_id(chain_id)) } - async fn sign_transaction(&self, message: &TypedTransaction) -> Result { - (*self).sign_transaction(message).await + async fn sign_typed_data( + &self, + payload: &T, + domain: &Eip712Domain, + ) -> alloy_signer::Result + where + Self: Sized, + { + delegate!(self, inner => inner.sign_typed_data(payload, domain)).await } - async fn sign_typed_data(&self, payload: &T) -> Result { - (*self).sign_typed_data(payload).await - } - - fn address(&self) -> ethers_core::types::Address { - (*self).address() - } - - fn chain_id(&self) -> u64 { - (*self).chain_id() - } - - fn with_chain_id>(self, chain_id: T) -> Self { - let _ = chain_id; - self + async fn sign_dynamic_typed_data( + &self, + payload: &TypedData, + ) -> alloy_signer::Result { + delegate!(self, inner => inner.sign_dynamic_typed_data(payload)).await } } -impl WalletSigner { - pub async fn sign_hash(&self, hash: &B256) -> Result { - match self { - // TODO: AWS can sign hashes but utilities aren't exposed in ethers-signers. - // TODO: Implement with alloy-signer. - Self::Aws(_aws) => Err(WalletSignerError::CannotSignRawHash("AWS")), - Self::Ledger(_) => Err(WalletSignerError::CannotSignRawHash("Ledger")), - Self::Local(wallet) => wallet.sign_hash(hash.0.into()).map_err(Into::into), - Self::Trezor(_) => Err(WalletSignerError::CannotSignRawHash("Trezor")), - } +#[async_trait] +impl TxSigner for WalletSigner { + async fn sign_transaction( + &self, + tx: &mut dyn SignableTransaction, + ) -> alloy_signer::Result { + delegate!(self, inner => inner.sign_transaction(tx)).await } } From 1610c138dd79491232ffda95f0b6742f1ffea520 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 9 Apr 2024 23:29:05 +0200 Subject: [PATCH 149/430] chore: use alloy calc next block base fee (#7614) --- Cargo.lock | 1 + crates/anvil/Cargo.toml | 1 + crates/anvil/src/eth/fees.rs | 29 +++-------------------------- 3 files changed, 5 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 770e28567..47bbf198c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -658,6 +658,7 @@ dependencies = [ "alloy-chains", "alloy-consensus", "alloy-dyn-abi", + "alloy-eips", "alloy-genesis", "alloy-json-abi", "alloy-network", diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index 99bf00081..76611fc80 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -39,6 +39,7 @@ ethers = { workspace = true, features = ["rustls", "ws", "ipc", "optimism"] } alloy-primitives = { workspace = true, features = ["serde"] } alloy-consensus = { workspace = true, features = ["k256", "kzg"] } alloy-network.workspace = true +alloy-eips.workspace = true alloy-rlp.workspace = true alloy-signer = { workspace = true, features = ["eip712"] } alloy-signer-wallet = { workspace = true, features = ["mnemonic"] } diff --git a/crates/anvil/src/eth/fees.rs b/crates/anvil/src/eth/fees.rs index 2b070b281..142a6a4af 100644 --- a/crates/anvil/src/eth/fees.rs +++ b/crates/anvil/src/eth/fees.rs @@ -2,6 +2,7 @@ use crate::eth::{ backend::{info::StorageInfo, notifications::NewBlockNotifications}, error::BlockchainError, }; +use alloy_eips::{calc_next_block_base_fee, eip1559::BaseFeeParams}; use alloy_primitives::B256; use anvil_core::eth::transaction::TypedTransaction; use foundry_evm::revm::primitives::SpecId; @@ -28,11 +29,8 @@ pub const INITIAL_GAS_PRICE: u128 = 1_875_000_000; /// Bounds the amount the base fee can change between blocks. pub const BASE_FEE_CHANGE_DENOMINATOR: u128 = 8; -/// Elasticity multiplier as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) -pub const EIP1559_ELASTICITY_MULTIPLIER: u128 = 2; - pub fn default_elasticity() -> f64 { - 1f64 / BASE_FEE_CHANGE_DENOMINATOR as f64 + 1f64 / BaseFeeParams::ethereum().elasticity_multiplier as f64 } /// Stores the fee related information @@ -127,28 +125,7 @@ impl FeeManager { if self.base_fee() == 0 { return 0 } - calculate_next_block_base_fee(gas_used, gas_limit, last_fee_per_gas) - } -} - -/// Calculate base fee for next block. [EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) spec -pub fn calculate_next_block_base_fee(gas_used: u128, gas_limit: u128, base_fee: u128) -> u128 { - let gas_target = gas_limit / EIP1559_ELASTICITY_MULTIPLIER; - - if gas_used == gas_target { - return base_fee - } - if gas_used > gas_target { - let gas_used_delta = gas_used - gas_target; - let base_fee_delta = - std::cmp::max(1, base_fee * gas_used_delta / gas_target / BASE_FEE_CHANGE_DENOMINATOR); - base_fee + base_fee_delta - } else { - let gas_used_delta = gas_target - gas_used; - let base_fee_per_gas_delta = - base_fee * gas_used_delta / gas_target / BASE_FEE_CHANGE_DENOMINATOR; - - base_fee.saturating_sub(base_fee_per_gas_delta) + calc_next_block_base_fee(gas_used, gas_limit, last_fee_per_gas, BaseFeeParams::ethereum()) } } From 0df7fb19e9718e5c63a07842d2a039accfb0d627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Gonz=C3=A1lez?= Date: Tue, 9 Apr 2024 23:40:53 +0200 Subject: [PATCH 150/430] feat(anvil): add support for injecting precompiles (#7589) * feat(anvil): add support for injecting precompiles * test: check precompiles get injected * feat(docs): add a few doc comments * feat(docs): document with_extra_precompiles * ref: localize changes to the anvil crate * ref: rename with_extra_precompiles -> with_precompile_factory * lint(fmt): fix formatting * ref: fix invalid comment * ref: remove unnecessary generic bound * ref: revert formatting change * ref: extract evm creation to a method * fix: inject precompiles to the executor * lint(fmt): fix formatting * chore: add doc * nit --------- Co-authored-by: Matthias Seitz --- crates/anvil/src/config.rs | 15 +++- crates/anvil/src/eth/backend/executor.rs | 7 ++ crates/anvil/src/eth/backend/mem/mod.rs | 59 +++++++++++---- crates/anvil/src/evm.rs | 92 ++++++++++++++++++++++++ crates/anvil/src/lib.rs | 3 + 5 files changed, 161 insertions(+), 15 deletions(-) create mode 100644 crates/anvil/src/evm.rs diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 58de505ed..7c35f0c9d 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -11,9 +11,8 @@ use crate::{ fees::{INITIAL_BASE_FEE, INITIAL_GAS_PRICE}, pool::transactions::TransactionOrder, }, - mem, - mem::in_memory_db::MemDb, - FeeManager, Hardfork, + mem::{self, in_memory_db::MemDb}, + FeeManager, Hardfork, PrecompileFactory, }; use alloy_genesis::Genesis; use alloy_network::AnyNetwork; @@ -176,6 +175,8 @@ pub struct NodeConfig { pub slots_in_an_epoch: u64, /// The memory limit per EVM execution in bytes. pub memory_limit: Option, + /// Factory used by `anvil` to extend the EVM's precompiles. + pub precompile_factory: Option>, } impl NodeConfig { @@ -422,6 +423,7 @@ impl Default for NodeConfig { enable_optimism: false, slots_in_an_epoch: 32, memory_limit: None, + precompile_factory: None, } } } @@ -834,6 +836,13 @@ impl NodeConfig { self } + /// Injects precompiles to `anvil`'s EVM. + #[must_use] + pub fn with_precompile_factory(mut self, factory: impl PrecompileFactory + 'static) -> Self { + self.precompile_factory = Some(Arc::new(factory)); + self + } + /// Configures everything related to env, backend and database and returns the /// [Backend](mem::Backend) /// diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index 80a72beed..d3b54be15 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -4,7 +4,9 @@ use crate::{ error::InvalidTransactionError, pool::transactions::PoolTransaction, }, + inject_precompiles, mem::inspector::Inspector, + PrecompileFactory, }; use alloy_consensus::{Header, Receipt, ReceiptWithBloom}; use alloy_primitives::{Bloom, BloomInput, Log, B256}; @@ -96,6 +98,8 @@ pub struct TransactionExecutor<'a, Db: ?Sized, Validator: TransactionValidator> /// Cumulative gas used by all executed transactions pub gas_used: u128, pub enable_steps_tracing: bool, + /// Precompiles to inject to the EVM. + pub precompile_factory: Option>, } impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<'a, DB, Validator> { @@ -265,6 +269,9 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator let exec_result = { let mut evm = foundry_evm::utils::new_evm_with_inspector(&mut *self.db, env, &mut inspector); + if let Some(ref factory) = self.precompile_factory { + inject_precompiles(&mut evm, factory.precompiles()); + } trace!(target: "backend", "[{:?}] executing", transaction.hash()); // transact and commit the transaction diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 939913b84..93b45377b 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -23,6 +23,7 @@ use crate::{ pool::transactions::PoolTransaction, util::get_precompiles_for, }, + inject_precompiles, mem::{ inspector::Inspector, storage::{BlockchainStorage, InMemoryBlockStates, MinedBlockOutcome}, @@ -31,7 +32,7 @@ use crate::{ db::DatabaseRef, primitives::{AccountInfo, U256 as rU256}, }, - NodeConfig, + NodeConfig, PrecompileFactory, }; use alloy_consensus::{Header, Receipt, ReceiptWithBloom}; use alloy_primitives::{keccak256, Address, Bytes, TxHash, B256, U256, U64}; @@ -78,7 +79,10 @@ use foundry_evm::{ }; use futures::channel::mpsc::{unbounded, UnboundedSender}; use parking_lot::{Mutex, RwLock}; -use revm::primitives::{HashMap, ResultAndState}; +use revm::{ + db::WrapDatabaseRef, + primitives::{HashMap, ResultAndState}, +}; use std::{ collections::BTreeMap, io::{Read, Write}, @@ -168,6 +172,8 @@ pub struct Backend { node_config: Arc>, /// Slots in an epoch slots_in_an_epoch: u64, + /// Precompiles to inject to the EVM. + precompile_factory: Option>, } impl Backend { @@ -214,7 +220,10 @@ impl Backend { Default::default() }; - let slots_in_an_epoch = node_config.read().await.slots_in_an_epoch; + let (slots_in_an_epoch, precompile_factory) = { + let cfg = node_config.read().await; + (cfg.slots_in_an_epoch, cfg.precompile_factory.clone()) + }; let backend = Self { db, @@ -233,6 +242,7 @@ impl Backend { transaction_block_keeper, node_config, slots_in_an_epoch, + precompile_factory, }; if let Some(interval_block_time) = automine_block_time { @@ -800,6 +810,24 @@ impl Backend { env } + /// Creates an EVM instance with optionally injected precompiles. + fn new_evm_with_inspector_ref( + &self, + db: DB, + env: EnvWithHandlerCfg, + inspector: I, + ) -> revm::Evm<'_, I, WrapDatabaseRef> + where + DB: revm::DatabaseRef, + I: revm::Inspector>, + { + let mut evm = new_evm_with_inspector_ref(db, env, inspector); + if let Some(ref factory) = self.precompile_factory { + inject_precompiles(&mut evm, factory.precompiles()); + } + evm + } + /// executes the transactions without writing to the underlying database pub async fn inspect_tx( &self, @@ -812,9 +840,8 @@ impl Backend { env.tx = tx.pending_transaction.to_revm_tx_env(); let db = self.db.read().await; let mut inspector = Inspector::default(); - - let ResultAndState { result, state } = - new_evm_with_inspector_ref(&*db, env, &mut inspector).transact()?; + let mut evm = self.new_evm_with_inspector_ref(&*db, env, &mut inspector); + let ResultAndState { result, state } = evm.transact()?; let (exit_reason, gas_used, out, logs) = match result { ExecutionResult::Success { reason, gas_used, logs, output, .. } => { (reason.into(), gas_used, Some(output), Some(logs)) @@ -825,6 +852,7 @@ impl Backend { ExecutionResult::Halt { reason, gas_used } => (reason.into(), gas_used, None, None), }; + drop(evm); inspector.print_logs(); Ok((exit_reason, out, gas_used, state, logs.unwrap_or_default())) @@ -865,6 +893,7 @@ impl Backend { parent_hash: storage.best_hash, gas_used: 0, enable_steps_tracing: self.enable_steps_tracing, + precompile_factory: self.precompile_factory.clone(), }; // create a new pending block @@ -924,6 +953,7 @@ impl Backend { parent_hash: best_hash, gas_used: 0, enable_steps_tracing: self.enable_steps_tracing, + precompile_factory: self.precompile_factory.clone(), }; let executed_tx = executor.execute(); @@ -1123,8 +1153,8 @@ impl Backend { let mut inspector = Inspector::default(); let env = self.build_call_env(request, fee_details, block_env); - let ResultAndState { result, state } = - new_evm_with_inspector_ref(state, env, &mut inspector).transact()?; + let mut evm = self.new_evm_with_inspector_ref(state, env, &mut inspector); + let ResultAndState { result, state } = evm.transact()?; let (exit_reason, gas_used, out) = match result { ExecutionResult::Success { reason, gas_used, output, .. } => { (reason.into(), gas_used, Some(output)) @@ -1134,6 +1164,7 @@ impl Backend { } ExecutionResult::Halt { reason, gas_used } => (reason.into(), gas_used, None), }; + drop(evm); inspector.print_logs(); Ok((exit_reason, out, gas_used as u128, state)) } @@ -1150,8 +1181,9 @@ impl Backend { let block_number = block.number; let env = self.build_call_env(request, fee_details, block); - let ResultAndState { result, state: _ } = - new_evm_with_inspector_ref(state, env, &mut inspector).transact()?; + let mut evm = self.new_evm_with_inspector_ref(state, env, &mut inspector); + let ResultAndState { result, state: _ } = evm.transact()?; + let (exit_reason, gas_used, out) = match result { ExecutionResult::Success { reason, gas_used, output, .. } => { (reason.into(), gas_used, Some(output)) @@ -1161,6 +1193,8 @@ impl Backend { } ExecutionResult::Halt { reason, gas_used } => (reason.into(), gas_used, None), }; + + drop(evm); let tracer = inspector.tracer.expect("tracer disappeared"); let return_value = out.as_ref().map(|o| o.data().clone()).unwrap_or_default(); let res = tracer.into_geth_builder().geth_traces(gas_used, return_value, opts); @@ -1196,8 +1230,8 @@ impl Backend { ); let env = self.build_call_env(request, fee_details, block_env); - let ResultAndState { result, state: _ } = - new_evm_with_inspector_ref(state, env, &mut inspector).transact()?; + let mut evm = self.new_evm_with_inspector_ref(state, env, &mut inspector); + let ResultAndState { result, state: _ } = evm.transact()?; let (exit_reason, gas_used, out) = match result { ExecutionResult::Success { reason, gas_used, output, .. } => { (reason.into(), gas_used, Some(output)) @@ -1207,6 +1241,7 @@ impl Backend { } ExecutionResult::Halt { reason, gas_used } => (reason.into(), gas_used, None), }; + drop(evm); let access_list = inspector.access_list(); Ok((exit_reason, out, gas_used, access_list)) } diff --git a/crates/anvil/src/evm.rs b/crates/anvil/src/evm.rs new file mode 100644 index 000000000..de1dfbd51 --- /dev/null +++ b/crates/anvil/src/evm.rs @@ -0,0 +1,92 @@ +use std::{fmt::Debug, sync::Arc}; + +use alloy_primitives::Address; +use foundry_evm::revm::{self, precompile::Precompile, ContextPrecompile, ContextPrecompiles}; + +/// Object-safe trait that enables injecting extra precompiles when using +/// `anvil` as a library. +pub trait PrecompileFactory: Send + Sync + Unpin + Debug { + /// Returns a set of precompiles to extend the EVM with. + fn precompiles(&self) -> Vec<(Address, Precompile)>; +} + +/// Appends a handler register to `evm` that injects the given `precompiles`. +/// +/// This will add an additional handler that extends the default precompiles with the given set of +/// precompiles. +pub fn inject_precompiles( + evm: &mut revm::Evm<'_, I, DB>, + precompiles: Vec<(Address, Precompile)>, +) where + DB: revm::Database, +{ + evm.handler.append_handler_register_box(Box::new(move |handler| { + let precompiles = precompiles.clone(); + let loaded_precompiles = handler.pre_execution().load_precompiles(); + handler.pre_execution.load_precompiles = Arc::new(move || { + let mut loaded_precompiles = loaded_precompiles.clone(); + loaded_precompiles.extend( + precompiles + .clone() + .into_iter() + .map(|(addr, p)| (addr, ContextPrecompile::Ordinary(p))), + ); + let mut default_precompiles = ContextPrecompiles::default(); + default_precompiles.extend(loaded_precompiles); + default_precompiles + }); + })); +} + +#[cfg(test)] +mod tests { + use crate::{evm::inject_precompiles, PrecompileFactory}; + use alloy_primitives::Address; + use foundry_evm::revm::{ + self, + primitives::{address, Bytes, Precompile, PrecompileResult, SpecId}, + }; + + #[test] + fn build_evm_with_extra_precompiles() { + const PRECOMPILE_ADDR: Address = address!("0000000000000000000000000000000000000071"); + fn my_precompile(_bytes: &Bytes, _gas_limit: u64) -> PrecompileResult { + Ok((0, Bytes::new())) + } + + #[derive(Debug)] + struct CustomPrecompileFactory; + + impl PrecompileFactory for CustomPrecompileFactory { + fn precompiles(&self) -> Vec<(Address, Precompile)> { + vec![(PRECOMPILE_ADDR, Precompile::Standard(my_precompile))] + } + } + + let db = revm::db::EmptyDB::default(); + let env = Box::::default(); + let spec = SpecId::LATEST; + let handler_cfg = revm::primitives::HandlerCfg::new(spec); + let inspector = revm::inspectors::NoOpInspector; + let context = revm::Context::new(revm::EvmContext::new_with_env(db, env), inspector); + let handler = revm::Handler::new(handler_cfg); + let mut evm = revm::Evm::new(context, handler); + assert!(!evm + .handler + .pre_execution() + .load_precompiles() + .addresses() + .any(|&addr| addr == PRECOMPILE_ADDR)); + + inject_precompiles(&mut evm, CustomPrecompileFactory.precompiles()); + assert!(evm + .handler + .pre_execution() + .load_precompiles() + .addresses() + .any(|&addr| addr == PRECOMPILE_ADDR)); + + let result = evm.transact().unwrap(); + assert!(result.result.is_success()); + } +} diff --git a/crates/anvil/src/lib.rs b/crates/anvil/src/lib.rs index 11a860bc3..31c635990 100644 --- a/crates/anvil/src/lib.rs +++ b/crates/anvil/src/lib.rs @@ -50,6 +50,9 @@ pub use hardfork::Hardfork; /// ethereum related implementations pub mod eth; +/// Evm related abstractions +mod evm; +pub use evm::{inject_precompiles, PrecompileFactory}; /// support for polling filters pub mod filter; /// commandline output From 7bb2b207f939da2bfcb0a6eea366eee414ea47a2 Mon Sep 17 00:00:00 2001 From: L Date: Tue, 9 Apr 2024 14:53:55 -0700 Subject: [PATCH 151/430] feat(cast): pretty print tx status in `cast receipt` (#7534) * feat(cast): pretty print tx status in `cast receipt` * pretty status --------- Co-authored-by: Matthias Seitz --- crates/common/src/fmt/ui.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/crates/common/src/fmt/ui.rs b/crates/common/src/fmt/ui.rs index c5bdf6c13..1607b6d10 100644 --- a/crates/common/src/fmt/ui.rs +++ b/crates/common/src/fmt/ui.rs @@ -147,6 +147,10 @@ impl UIfmt for [u8] { } } +pub fn pretty_status(status: bool) -> String { + if status { "1 (success)" } else { "0 (failed)" }.to_string() +} + impl UIfmt for AnyTransactionReceipt { fn pretty(&self) -> String { let Self { @@ -205,7 +209,7 @@ blobGasUsed {}", serde_json::to_string(&logs).unwrap(), logs_bloom.pretty(), state_root.pretty(), - status.pretty(), + pretty_status(*status), transaction_hash.pretty(), transaction_index.pretty(), transaction_type, @@ -445,7 +449,7 @@ pub fn get_pretty_tx_receipt_attr( "logsBloom" | "logs_bloom" => Some(receipt.receipt.inner.inner.inner.logs_bloom.pretty()), "root" | "stateRoot" | "state_root " => Some(receipt.receipt.state_root.pretty()), "status" | "statusCode" | "status_code" => { - Some(receipt.receipt.inner.inner.inner.receipt.status.pretty()) + Some(pretty_status(receipt.receipt.inner.inner.inner.receipt.status)) } "transactionHash" | "transaction_hash" => Some(receipt.receipt.transaction_hash.pretty()), "transactionIndex" | "transaction_index" => { From c62a3cc307ce119aa5b85c2a7afdbedcdf9bdef9 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 10 Apr 2024 00:08:32 +0200 Subject: [PATCH 152/430] chore: bump alloy --- Cargo.lock | 44 ++++++++++++++++++++++---------------------- Cargo.toml | 44 ++++++++++++++++++++++---------------------- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 47bbf198c..2ed89363d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,7 +79,7 @@ dependencies = [ [[package]] name = "alloy-consensus" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" dependencies = [ "alloy-eips", "alloy-primitives", @@ -94,7 +94,7 @@ dependencies = [ [[package]] name = "alloy-contract" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -133,7 +133,7 @@ dependencies = [ [[package]] name = "alloy-eips" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -147,7 +147,7 @@ dependencies = [ [[package]] name = "alloy-genesis" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" dependencies = [ "alloy-primitives", "alloy-serde", @@ -169,7 +169,7 @@ dependencies = [ [[package]] name = "alloy-json-rpc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" dependencies = [ "alloy-primitives", "serde", @@ -180,7 +180,7 @@ dependencies = [ [[package]] name = "alloy-network" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" dependencies = [ "alloy-consensus", "alloy-eips", @@ -223,7 +223,7 @@ dependencies = [ [[package]] name = "alloy-provider" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" dependencies = [ "alloy-eips", "alloy-json-rpc", @@ -249,7 +249,7 @@ dependencies = [ [[package]] name = "alloy-pubsub" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -289,7 +289,7 @@ dependencies = [ [[package]] name = "alloy-rpc-client" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -309,7 +309,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" dependencies = [ "alloy-consensus", "alloy-eips", @@ -327,7 +327,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -339,7 +339,7 @@ dependencies = [ [[package]] name = "alloy-serde" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" dependencies = [ "alloy-primitives", "serde", @@ -349,7 +349,7 @@ dependencies = [ [[package]] name = "alloy-signer" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -364,7 +364,7 @@ dependencies = [ [[package]] name = "alloy-signer-aws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" dependencies = [ "alloy-consensus", "alloy-network", @@ -381,7 +381,7 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" dependencies = [ "alloy-consensus", "alloy-network", @@ -398,7 +398,7 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" dependencies = [ "alloy-consensus", "alloy-network", @@ -414,7 +414,7 @@ dependencies = [ [[package]] name = "alloy-signer-wallet" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" dependencies = [ "alloy-consensus", "alloy-network", @@ -491,7 +491,7 @@ dependencies = [ [[package]] name = "alloy-transport" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" dependencies = [ "alloy-json-rpc", "base64 0.22.0", @@ -509,7 +509,7 @@ dependencies = [ [[package]] name = "alloy-transport-http" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -522,7 +522,7 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -540,7 +540,7 @@ dependencies = [ [[package]] name = "alloy-transport-ws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -6848,7 +6848,7 @@ dependencies = [ [[package]] name = "revm-inspectors" version = "0.1.0" -source = "git+https://github.com/paradigmxyz/evm-inspectors?rev=510d4d0#510d4d0d06130d52ee996fa8aebd32cdc8267905" +source = "git+https://github.com/paradigmxyz/evm-inspectors?rev=21f8f3d#21f8f3d266b05d1084e06f0c5331f2f1f4ed0905" dependencies = [ "alloy-primitives", "alloy-rpc-types", diff --git a/Cargo.toml b/Cargo.toml index 1bc2329a5..93a4f2455 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -144,7 +144,7 @@ foundry-compilers = { version = "0.3.14", default-features = false } # no default features to avoid c-kzg revm = { version = "8", default-features = false } revm-primitives = { version = "3", default-features = false } -revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "510d4d0", features = [ +revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "21f8f3d", features = [ "serde", ] } @@ -158,27 +158,27 @@ ethers-signers = { version = "2.0.14", default-features = false } ethers-middleware = { version = "2.0.14", default-features = false } ## alloy -alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } -alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } -alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } -alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } -alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } -alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } -alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } -alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } -alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } -alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } -alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } -alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } -alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } -alloy-signer-wallet = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } -alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } -alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } -alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } -alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } -alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } -alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } -alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } +alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } +alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } +alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } +alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } +alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } +alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } +alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } +alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } +alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } +alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } +alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } +alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } +alloy-signer-wallet = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } +alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } +alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } +alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } +alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } +alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } +alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } +alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } alloy-primitives = { version = "0.7.0", features = ["getrandom"] } alloy-dyn-abi = "0.7.0" alloy-json-abi = "0.7.0" From 460319558e455611be1de64be8364c65c6896d15 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 10 Apr 2024 00:38:34 +0200 Subject: [PATCH 153/430] chore: rm outdated utils (#7616) --- crates/anvil/core/src/eth/transaction/mod.rs | 25 ++++++--------- crates/anvil/core/src/eth/utils.rs | 33 +------------------- crates/anvil/src/eth/backend/mem/mod.rs | 4 +-- 3 files changed, 13 insertions(+), 49 deletions(-) diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 84e236f92..e42300d6a 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -1,9 +1,6 @@ //! Transaction related types -use crate::eth::{ - transaction::optimism::{DepositTransaction, DepositTransactionRequest}, - utils::eip_to_revm_access_list, -}; +use crate::eth::transaction::optimism::{DepositTransaction, DepositTransactionRequest}; use alloy_consensus::{ AnyReceiptEnvelope, BlobTransactionSidecar, Receipt, ReceiptEnvelope, ReceiptWithBloom, Signed, TxEip1559, TxEip2930, TxEip4844, TxEip4844Variant, TxEip4844WithSidecar, TxEnvelope, TxLegacy, @@ -25,8 +22,6 @@ use revm::{ use serde::{Deserialize, Serialize}; use std::ops::Deref; -use super::utils::from_eip_to_alloy_access_list; - pub mod optimism; /// The signature used to bypass signing via the `eth_sendUnsignedTransaction` cheat RPC @@ -393,7 +388,7 @@ pub fn to_alloy_transaction_with_hash_and_sender( v: U256::from(t.signature().v().y_parity_byte()), y_parity: Some(alloy_rpc_types::Parity::from(t.signature().v().y_parity())), }), - access_list: Some(from_eip_to_alloy_access_list(t.tx().tx().access_list.clone())), + 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()), @@ -482,7 +477,7 @@ impl PendingTransaction { TxEnv { caller, transact_to: transact_to(to), - data: alloy_primitives::Bytes(input.0.clone()), + data: input.clone(), chain_id, nonce: Some(*nonce), value: (*value), @@ -508,14 +503,14 @@ impl PendingTransaction { TxEnv { caller, transact_to: transact_to(to), - data: alloy_primitives::Bytes(input.0.clone()), + data: input.clone(), chain_id: Some(*chain_id), nonce: Some(*nonce), value: *value, gas_price: U256::from(*gas_price), gas_priority_fee: None, gas_limit: *gas_limit as u64, - access_list: eip_to_revm_access_list(access_list.0.clone()), + access_list: access_list.flattened(), ..Default::default() } } @@ -535,14 +530,14 @@ impl PendingTransaction { TxEnv { caller, transact_to: transact_to(to), - data: alloy_primitives::Bytes(input.0.clone()), + data: input.clone(), chain_id: Some(*chain_id), nonce: Some(*nonce), value: *value, gas_price: U256::from(*max_fee_per_gas), gas_priority_fee: Some(U256::from(*max_priority_fee_per_gas)), gas_limit: *gas_limit as u64, - access_list: eip_to_revm_access_list(access_list.0.clone()), + access_list: access_list.flattened(), ..Default::default() } } @@ -564,7 +559,7 @@ impl PendingTransaction { TxEnv { caller, transact_to: TransactTo::call(*to), - data: alloy_primitives::Bytes(input.0.clone()), + data: input.clone(), chain_id: Some(*chain_id), nonce: Some(*nonce), value: *value, @@ -573,7 +568,7 @@ impl PendingTransaction { max_fee_per_blob_gas: Some(U256::from(*max_fee_per_blob_gas)), blob_hashes: blob_versioned_hashes.clone(), gas_limit: *gas_limit as u64, - access_list: eip_to_revm_access_list(access_list.0.clone()), + access_list: access_list.flattened(), ..Default::default() } } @@ -593,7 +588,7 @@ impl PendingTransaction { TxEnv { caller, transact_to: transact_to(kind), - data: alloy_primitives::Bytes(input.0.clone()), + data: input.clone(), chain_id, nonce: Some(*nonce), value: *value, diff --git a/crates/anvil/core/src/eth/utils.rs b/crates/anvil/core/src/eth/utils.rs index 27bb46523..a60439280 100644 --- a/crates/anvil/core/src/eth/utils.rs +++ b/crates/anvil/core/src/eth/utils.rs @@ -1,35 +1,4 @@ -use alloy_eips::eip2930::{ - AccessList as AlloyEipAccessList, AccessListItem as AlloyEipAccessListItem, -}; -use alloy_primitives::{Address, Parity, U256}; -use alloy_rpc_types::{AccessList as AlloyAccessList, AccessListItem as AlloyAccessListItem}; - -pub fn alloy_to_revm_access_list(list: Vec) -> Vec<(Address, Vec)> { - list.into_iter() - .map(|item| (item.address, item.storage_keys.into_iter().map(|k| k.into()).collect())) - .collect() -} - -/// Translates a vec of [AlloyEipAccessListItem] to a [AlloyAccessList], translating from internal -/// type to rpc type. -pub fn from_eip_to_alloy_access_list(list: AlloyEipAccessList) -> AlloyAccessList { - AlloyAccessList( - list.0 - .into_iter() - .map(|item| AlloyAccessListItem { - address: item.address, - storage_keys: item.storage_keys.into_iter().collect(), - }) - .collect(), - ) -} - -/// Translates a vec of [AlloyEipAccessListItem] to a revm style Access List. -pub fn eip_to_revm_access_list(list: Vec) -> Vec<(Address, Vec)> { - list.into_iter() - .map(|item| (item.address, item.storage_keys.into_iter().map(|k| k.into()).collect())) - .collect() -} +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 diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 93b45377b..d1fa2007d 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -54,7 +54,7 @@ use anvil_core::{ DepositReceipt, MaybeImpersonatedTransaction, PendingTransaction, ReceiptResponse, TransactionInfo, TypedReceipt, TypedTransaction, }, - utils::{alloy_to_revm_access_list, meets_eip155}, + utils::meets_eip155, }, types::{Forking, Index}, }; @@ -1127,7 +1127,7 @@ impl Backend { data: input.into_input().unwrap_or_default(), chain_id: None, nonce, - access_list: alloy_to_revm_access_list(access_list.unwrap_or_default().0), + access_list: access_list.unwrap_or_default().flattened(), ..Default::default() }; From f0ea57a49fa1bc24185b91ab63017aa3f55871b6 Mon Sep 17 00:00:00 2001 From: Krishang <93703995+kamuik16@users.noreply.github.com> Date: Wed, 10 Apr 2024 16:40:07 +0530 Subject: [PATCH 154/430] feat(forge): blobbasefee cheatcode (#7598) * feat(forge): blobbasefee cheatcode * updated comments * nits * moved test * chore: add cancun test --------- Co-authored-by: Matthias Seitz --- crates/cheatcodes/assets/cheatcodes.json | 40 ++++++++++++++++++++++++ crates/cheatcodes/spec/src/vm.rs | 11 +++++++ crates/cheatcodes/src/evm.rs | 20 ++++++++++++ crates/forge/tests/it/cheats.rs | 10 +++++- crates/forge/tests/it/test_helpers.rs | 23 ++++++++++---- testdata/cancun/cheats/BlobBaseFee.t.sol | 14 +++++++++ testdata/cheats/Vm.sol | 2 ++ 7 files changed, 113 insertions(+), 7 deletions(-) create mode 100644 testdata/cancun/cheats/BlobBaseFee.t.sol diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 26d9dd2e1..7a02c6ad6 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -2971,6 +2971,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "blobBaseFee", + "description": "Sets `block.blobbasefee`", + "declaration": "function blobBaseFee(uint256 newBlobBaseFee) external;", + "visibility": "external", + "mutability": "", + "signature": "blobBaseFee(uint256)", + "selector": "0x6d315d7e", + "selectorBytes": [ + 109, + 49, + 93, + 126 + ] + }, + "group": "evm", + "status": "stable", + "safety": "unsafe" + }, { "func": { "id": "breakpoint_0", @@ -4651,6 +4671,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "getBlobBaseFee", + "description": "Gets the current `block.blobbasefee`.\nYou should use this instead of `block.blobbasefee` if you use `vm.blobBaseFee`, as `block.blobbasefee` is assumed to be constant across a transaction,\nand as a result will get optimized out by the compiler.\nSee https://github.com/foundry-rs/foundry/issues/6180", + "declaration": "function getBlobBaseFee() external view returns (uint256 blobBaseFee);", + "visibility": "external", + "mutability": "view", + "signature": "getBlobBaseFee()", + "selector": "0x1f6d6ef7", + "selectorBytes": [ + 31, + 109, + 110, + 247 + ] + }, + "group": "evm", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "getBlockNumber", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index e7ac87da1..ae93475b2 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -402,6 +402,17 @@ interface Vm { #[cheatcode(group = Evm, safety = Safe)] function getBlockTimestamp() external view returns (uint256 timestamp); + /// Sets `block.blobbasefee` + #[cheatcode(group = Evm, safety = Unsafe)] + function blobBaseFee(uint256 newBlobBaseFee) external; + + /// Gets the current `block.blobbasefee`. + /// You should use this instead of `block.blobbasefee` if you use `vm.blobBaseFee`, as `block.blobbasefee` is assumed to be constant across a transaction, + /// and as a result will get optimized out by the compiler. + /// See https://github.com/foundry-rs/foundry/issues/6180 + #[cheatcode(group = Evm, safety = Safe)] + function getBlobBaseFee() external view returns (uint256 blobBaseFee); + // -------- Account State -------- /// Sets an address' balance. diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index b8f55b37c..d9c0f5cff 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -328,6 +328,26 @@ impl Cheatcode for getBlockTimestampCall { } } +impl Cheatcode for blobBaseFeeCall { + fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { newBlobBaseFee } = self; + ensure!( + ccx.ecx.spec_id() >= SpecId::CANCUN, + "`blobBaseFee` is not supported before the Cancun hard fork; \ + see EIP-4844: https://eips.ethereum.org/EIPS/eip-4844" + ); + ccx.ecx.env.block.set_blob_excess_gas_and_price((*newBlobBaseFee).to()); + Ok(Default::default()) + } +} + +impl Cheatcode for getBlobBaseFeeCall { + fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + let Self {} = self; + Ok(ccx.ecx.env.block.get_blob_excess_gas().unwrap_or(0).abi_encode()) + } +} + impl Cheatcode for dealCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { account: address, newBalance: new_balance } = *self; diff --git a/crates/forge/tests/it/cheats.rs b/crates/forge/tests/it/cheats.rs index 42113cdc7..47d6ebbb9 100644 --- a/crates/forge/tests/it/cheats.rs +++ b/crates/forge/tests/it/cheats.rs @@ -2,7 +2,10 @@ use crate::{ config::*, - test_helpers::{ForgeTestData, RE_PATH_SEPARATOR, TEST_DATA_DEFAULT, TEST_DATA_MULTI_VERSION}, + test_helpers::{ + ForgeTestData, RE_PATH_SEPARATOR, TEST_DATA_CANCUN, TEST_DATA_DEFAULT, + TEST_DATA_MULTI_VERSION, + }, }; use foundry_config::{fs_permissions::PathPermission, FsPermissions}; use foundry_test_utils::Filter; @@ -50,3 +53,8 @@ async fn test_cheats_local_default_isolated() { async fn test_cheats_local_multi_version() { test_cheats_local(&TEST_DATA_MULTI_VERSION).await } + +#[tokio::test(flavor = "multi_thread")] +async fn test_cheats_local_cancun() { + test_cheats_local(&TEST_DATA_CANCUN).await +} diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index 6fc8a3745..204df223b 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -2,8 +2,8 @@ use alloy_primitives::U256; use forge::{ - inspectors::CheatsConfig, MultiContractRunner, MultiContractRunnerBuilder, TestOptions, - TestOptionsBuilder, + inspectors::CheatsConfig, revm::primitives::SpecId, MultiContractRunner, + MultiContractRunnerBuilder, TestOptions, TestOptionsBuilder, }; use foundry_compilers::{ artifacts::{Libraries, Settings}, @@ -46,6 +46,11 @@ impl fmt::Display for ForgeTestProfile { } impl ForgeTestProfile { + /// Returns true if the profile is Cancun. + pub fn is_cancun(&self) -> bool { + matches!(self, Self::Cancun) + } + pub fn root(&self) -> PathBuf { PathBuf::from(TESTDATA) } @@ -147,7 +152,7 @@ impl ForgeTestProfile { "fork/Fork.t.sol:DssExecLib:0xfD88CeE74f7D78697775aBDAE53f9Da1559728E4".to_string(), ]; - if matches!(self, Self::Cancun) { + if self.is_cancun() { config.evm_version = EvmVersion::Cancun; } @@ -162,6 +167,7 @@ pub struct ForgeTestData { pub test_opts: TestOptions, pub evm_opts: EvmOpts, pub config: Config, + pub profile: ForgeTestProfile, } impl ForgeTestData { @@ -175,15 +181,20 @@ impl ForgeTestData { let config = profile.config(); let evm_opts = profile.evm_opts(); - Self { project, output, test_opts, evm_opts, config } + Self { project, output, test_opts, evm_opts, config, profile } } /// Builds a base runner pub fn base_runner(&self) -> MultiContractRunnerBuilder { init_tracing(); - MultiContractRunnerBuilder::default() + let mut runner = MultiContractRunnerBuilder::default() .sender(self.evm_opts.sender) - .with_test_options(self.test_opts.clone()) + .with_test_options(self.test_opts.clone()); + if self.profile.is_cancun() { + runner = runner.evm_spec(SpecId::CANCUN); + } + + runner } /// Builds a non-tracing runner diff --git a/testdata/cancun/cheats/BlobBaseFee.t.sol b/testdata/cancun/cheats/BlobBaseFee.t.sol new file mode 100644 index 000000000..54fbc8f7f --- /dev/null +++ b/testdata/cancun/cheats/BlobBaseFee.t.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.25; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract BlobBaseFeeTest is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + function test_blob_base_fee() public { + vm.blobBaseFee(6969); + assertEq(vm.getBlobBaseFee(), 6969); + } +} diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 8f799c9f6..1d0b6118b 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -144,6 +144,7 @@ interface Vm { function assertTrue(bool condition) external pure; function assertTrue(bool condition, string calldata error) external pure; function assume(bool condition) external pure; + function blobBaseFee(uint256 newBlobBaseFee) external; function breakpoint(string calldata char) external; function breakpoint(string calldata char, bool value) external; function broadcast() external; @@ -228,6 +229,7 @@ interface Vm { function fee(uint256 newBasefee) external; function ffi(string[] calldata commandInput) external returns (bytes memory result); function fsMetadata(string calldata path) external view returns (FsMetadata memory metadata); + function getBlobBaseFee() external view returns (uint256 blobBaseFee); function getBlockNumber() external view returns (uint256 height); function getBlockTimestamp() external view returns (uint256 timestamp); function getCode(string calldata artifactPath) external view returns (bytes memory creationBytecode); From 9a0f0c23ac8e9a96b9f36b5eab486d0579c12ffe Mon Sep 17 00:00:00 2001 From: Krishang <93703995+kamuik16@users.noreply.github.com> Date: Wed, 10 Apr 2024 17:33:51 +0530 Subject: [PATCH 155/430] feat(forge): prompt address and uint cheatcodes (#7600) * feat: prompt address and uint cheatcode * nits * chore: change test to pass ci --- crates/cheatcodes/assets/cheatcodes.json | 40 ++++++++++++++++++++++++ crates/cheatcodes/spec/src/vm.rs | 8 +++++ crates/cheatcodes/src/fs.rs | 16 ++++++++++ testdata/cheats/Vm.sol | 2 ++ testdata/default/cheats/Prompt.t.sol | 11 +++++++ 5 files changed, 77 insertions(+) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 7a02c6ad6..c8b5d3d2a 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -6231,6 +6231,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "promptAddress", + "description": "Prompts the user for an address in the terminal.", + "declaration": "function promptAddress(string calldata promptText) external returns (address);", + "visibility": "external", + "mutability": "", + "signature": "promptAddress(string)", + "selector": "0x62ee05f4", + "selectorBytes": [ + 98, + 238, + 5, + 244 + ] + }, + "group": "filesystem", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "promptSecret", @@ -6251,6 +6271,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "promptUint", + "description": "Prompts the user for uint256 in the terminal.", + "declaration": "function promptUint(string calldata promptText) external returns (uint256);", + "visibility": "external", + "mutability": "", + "signature": "promptUint(string)", + "selector": "0x652fd489", + "selectorBytes": [ + 101, + 47, + 212, + 137 + ] + }, + "group": "filesystem", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "readCallers", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index ae93475b2..03a2cd090 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1482,6 +1482,14 @@ interface Vm { #[cheatcode(group = Filesystem)] function promptSecret(string calldata promptText) external returns (string memory input); + /// Prompts the user for an address in the terminal. + #[cheatcode(group = Filesystem)] + function promptAddress(string calldata promptText) external returns (address); + + /// Prompts the user for uint256 in the terminal. + #[cheatcode(group = Filesystem)] + function promptUint(string calldata promptText) external returns (uint256); + // ======== Environment Variables ======== /// Sets environment variables. diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index 3ba07b5b6..9b22c4d8d 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -1,6 +1,8 @@ //! Implementations of [`Filesystem`](crate::Group::Filesystem) cheatcodes. +use super::string::parse; use crate::{Cheatcode, Cheatcodes, Result, Vm::*}; +use alloy_dyn_abi::DynSolType; use alloy_json_abi::ContractObject; use alloy_primitives::U256; use alloy_sol_types::SolValue; @@ -426,6 +428,20 @@ impl Cheatcode for promptSecretCall { } } +impl Cheatcode for promptAddressCall { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let Self { promptText: text } = self; + parse(&prompt(state, text, prompt_input)?, &DynSolType::Address) + } +} + +impl Cheatcode for promptUintCall { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let Self { promptText: text } = self; + parse(&prompt(state, text, prompt_input)?, &DynSolType::Uint(256)) + } +} + pub(super) fn write_file(state: &Cheatcodes, path: &Path, contents: &[u8]) -> Result { let path = state.config.ensure_path_allowed(path, FsAccessKind::Write)?; // write access to foundry.toml is not allowed diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 1d0b6118b..44f0d52ac 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -307,7 +307,9 @@ interface Vm { function prevrandao(bytes32 newPrevrandao) external; function projectRoot() external view returns (string memory path); function prompt(string calldata promptText) external returns (string memory input); + function promptAddress(string calldata promptText) external returns (address); function promptSecret(string calldata promptText) external returns (string memory input); + function promptUint(string calldata promptText) external returns (uint256); function readCallers() external returns (CallerMode callerMode, address msgSender, address txOrigin); function readDir(string calldata path) external view returns (DirEntry[] memory entries); function readDir(string calldata path, uint64 maxDepth) external view returns (DirEntry[] memory entries); diff --git a/testdata/default/cheats/Prompt.t.sol b/testdata/default/cheats/Prompt.t.sol index dadfd30a9..9e461c2b5 100644 --- a/testdata/default/cheats/Prompt.t.sol +++ b/testdata/default/cheats/Prompt.t.sol @@ -3,6 +3,7 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; import "cheats/Vm.sol"; +import "../logs/console.sol"; contract PromptTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); @@ -15,4 +16,14 @@ contract PromptTest is DSTest { vm._expectCheatcodeRevert(); vm.promptSecret("test"); } + + function testPrompt_Address() public { + vm._expectCheatcodeRevert(); + address test = vm.promptAddress("test"); + } + + function testPrompt_Uint() public { + vm._expectCheatcodeRevert(); + uint256 test = vm.promptUint("test"); + } } From d8a162581aa2f674e581a57daf1aba24acc26206 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 10 Apr 2024 15:50:21 +0200 Subject: [PATCH 156/430] fix: dont set withdrawals root (#7626) --- crates/anvil/core/src/eth/block.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/anvil/core/src/eth/block.rs b/crates/anvil/core/src/eth/block.rs index 462d16c82..44fe1c517 100644 --- a/crates/anvil/core/src/eth/block.rs +++ b/crates/anvil/core/src/eth/block.rs @@ -65,7 +65,7 @@ impl Block { timestamp: partial_header.timestamp, extra_data: partial_header.extra_data, mix_hash: partial_header.mix_hash, - withdrawals_root: Some(partial_header.mix_hash), + withdrawals_root: None, blob_gas_used: None, excess_blob_gas: None, parent_beacon_block_root: None, From 43fb17bbaf5b4c8ff36f2b21da425b0fc81a7b94 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Wed, 10 Apr 2024 12:05:29 -0400 Subject: [PATCH 157/430] forge `verify-bytecode` (#7319) * verify bytecode boilerplate * wip(verify-bytecode): fetch creation code and constructor args * nit: todo comment * verify-bytecode: integrate build args and compile project * nits: use env eth_rpc_url * verify-bytecode: try_match * createFork * deploy contract on fork * verify-bytecode: cmp runtime code * constructor_args_range in bytecode * fix: NonceTooHigh issue and append constructor_args to local_bytecode * pretty print * verify-bytecode: pinpoint compiler settings match * verify-bytecode: cross check provided constructor args * handle revm changes * rm ethers_core as dep * nits * nit: is_runtime param * remove constructor args range check * nit * notify user on args mismatch, use args from etherscan by default. * fix: handle create2 deployments * add: checks for code at address and name mismatch * nits * add(verify-bytecode): check for bytecode hash config details and notify accordingly * use cache * nits * use verification type enum * nits * add(verify-bytecode): `--json` feature * fmt nits Co-authored-by: evalir * control flow nits * select cache version * nits and cleanup * use etherscan compiler version * smol nits * nit * fix(verify-bytecode): integrate alloy provider --------- Co-authored-by: evalir --- Cargo.lock | 3 + crates/forge/bin/main.rs | 1 + crates/forge/bin/opts.rs | 6 +- crates/verify/Cargo.toml | 6 +- crates/verify/src/bytecode.rs | 647 ++++++++++++++++++++++++++++++++++ crates/verify/src/lib.rs | 1 + 6 files changed, 661 insertions(+), 3 deletions(-) create mode 100644 crates/verify/src/bytecode.rs diff --git a/Cargo.lock b/Cargo.lock index 2ed89363d..28a029aa4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3555,6 +3555,7 @@ dependencies = [ "alloy-json-abi", "alloy-primitives", "alloy-provider", + "alloy-rpc-types", "async-trait", "clap", "const-hex", @@ -3570,12 +3571,14 @@ dependencies = [ "once_cell", "regex", "reqwest 0.11.27", + "revm-primitives", "semver 1.0.22", "serde", "serde_json", "tempfile", "tokio", "tracing", + "yansi 0.5.1", ] [[package]] diff --git a/crates/forge/bin/main.rs b/crates/forge/bin/main.rs index acbe80d81..05589f84a 100644 --- a/crates/forge/bin/main.rs +++ b/crates/forge/bin/main.rs @@ -108,6 +108,7 @@ fn main() -> Result<()> { ForgeSubcommand::Generate(cmd) => match cmd.sub { GenerateSubcommands::Test(cmd) => cmd.run(), }, + ForgeSubcommand::VerifyBytecode(cmd) => utils::block_on(cmd.run()), } } diff --git a/crates/forge/bin/opts.rs b/crates/forge/bin/opts.rs index 03ed4d551..56308e167 100644 --- a/crates/forge/bin/opts.rs +++ b/crates/forge/bin/opts.rs @@ -6,7 +6,7 @@ use crate::cmd::{ }; use clap::{Parser, Subcommand, ValueHint}; use forge_script::ScriptArgs; -use forge_verify::{VerifyArgs, VerifyCheckArgs}; +use forge_verify::{bytecode::VerifyBytecodeArgs, VerifyArgs, VerifyCheckArgs}; use std::path::PathBuf; const VERSION_MESSAGE: &str = concat!( @@ -154,6 +154,10 @@ pub enum ForgeSubcommand { /// Generate scaffold files. Generate(generate::GenerateArgs), + + /// Verify the deployed bytecode against its source. + #[clap(visible_alias = "vb")] + VerifyBytecode(VerifyBytecodeArgs), } #[cfg(test)] diff --git a/crates/verify/Cargo.toml b/crates/verify/Cargo.toml index 832cf1c6b..16efe6888 100644 --- a/crates/verify/Cargo.toml +++ b/crates/verify/Cargo.toml @@ -15,11 +15,12 @@ foundry-config.workspace = true foundry-cli.workspace = true foundry-common.workspace = true foundry-evm.workspace = true - serde_json.workspace = true hex.workspace = true alloy-json-abi.workspace = true alloy-primitives.workspace = true +alloy-rpc-types.workspace = true +revm-primitives.workspace = true serde.workspace = true eyre.workspace = true alloy-provider.workspace = true @@ -34,8 +35,9 @@ futures = "0.3" semver = "1" regex = { version = "1", default-features = false } once_cell = "1" +yansi = "0.5" [dev-dependencies] tokio = { version = "1", features = ["macros"] } foundry-test-utils.workspace = true -tempfile = "3" \ No newline at end of file +tempfile = "3" diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs new file mode 100644 index 000000000..f801ff771 --- /dev/null +++ b/crates/verify/src/bytecode.rs @@ -0,0 +1,647 @@ +use alloy_primitives::{Address, Bytes, U256}; +use alloy_provider::Provider; +use alloy_rpc_types::{BlockId, BlockNumberOrTag}; +use clap::{Parser, ValueHint}; +use eyre::{OptionExt, Result}; +use foundry_block_explorers::{contract::Metadata, Client}; +use foundry_cli::{ + opts::EtherscanOpts, + utils::{self, read_constructor_args_file, LoadConfig}, +}; +use foundry_common::{ + compile::{ProjectCompiler, SkipBuildFilter, SkipBuildFilters}, + provider::alloy::ProviderBuilder, +}; +use foundry_compilers::{ + artifacts::{BytecodeHash, BytecodeObject, CompactContractBytecode}, + info::ContractInfo, + Artifact, EvmVersion, +}; +use foundry_config::{figment, impl_figment_convert, Chain, Config}; +use foundry_evm::{ + constants::DEFAULT_CREATE2_DEPLOYER, executors::TracingExecutor, utils::configure_tx_env, +}; +use revm_primitives::{db::Database, EnvWithHandlerCfg, HandlerCfg, SpecId}; +use semver::Version; +use serde::{Deserialize, Serialize}; +use std::{fmt, path::PathBuf, str::FromStr}; +use yansi::Paint; + +impl_figment_convert!(VerifyBytecodeArgs); + +/// CLI arguments for `forge verify-bytecode`. +#[derive(Clone, Debug, Parser)] +pub struct VerifyBytecodeArgs { + /// The address of the contract to verify. + pub address: Address, + + /// The contract identifier in the form `:`. + pub contract: ContractInfo, + + /// The block at which the bytecode should be verified. + #[clap(long, value_name = "BLOCK")] + pub block: Option, + + /// The constructor args to generate the creation code. + #[clap( + long, + conflicts_with = "constructor_args_path", + value_name = "ARGS", + visible_alias = "encoded-constructor-args" + )] + pub constructor_args: Option, + + /// The path to a file containing the constructor arguments. + #[clap(long, value_hint = ValueHint::FilePath, value_name = "PATH")] + pub constructor_args_path: Option, + + /// The rpc url to use for verification. + #[clap(short = 'r', long, value_name = "RPC_URL", env = "ETH_RPC_URL")] + pub rpc_url: Option, + + /// Verfication Type: `full` or `partial`. Ref: https://docs.sourcify.dev/docs/full-vs-partial-match/ + #[clap(long, default_value = "full", value_name = "TYPE")] + pub verification_type: VerificationType, + + #[clap(flatten)] + pub etherscan_opts: EtherscanOpts, + + /// Skip building files whose names contain the given filter. + /// + /// `test` and `script` are aliases for `.t.sol` and `.s.sol`. + #[arg(long, num_args(1..))] + pub skip: Option>, + + /// The path to the project's root directory. + pub root: Option, + + /// Suppress logs and emit json results to stdout + #[clap(long, default_value = "false")] + pub json: bool, +} + +impl figment::Provider for VerifyBytecodeArgs { + fn metadata(&self) -> figment::Metadata { + figment::Metadata::named("Verify Bytecode Provider") + } + + fn data( + &self, + ) -> Result, figment::Error> { + let mut dict = figment::value::Dict::new(); + if let Some(block) = &self.block { + dict.insert("block".into(), figment::value::Value::serialize(block)?); + } + if let Some(rpc_url) = &self.rpc_url { + dict.insert("eth_rpc_url".into(), rpc_url.to_string().into()); + } + dict.insert("verification_type".into(), self.verification_type.to_string().into()); + + Ok(figment::value::Map::from([(Config::selected_profile(), dict)])) + } +} + +impl VerifyBytecodeArgs { + /// Run the `verify-bytecode` command to verify the bytecode onchain against the locally built + /// bytecode. + pub async fn run(mut self) -> Result<()> { + // Setup + let config = self.load_config_emit_warnings(); + let provider = ProviderBuilder::new(&config.get_rpc_url_or_localhost_http()?).build()?; + + let code = provider.get_code_at(self.address, BlockId::latest()).await?; + if code.is_empty() { + eyre::bail!("No bytecode found at address {}", self.address); + } + + if !self.json { + println!( + "Verifying bytecode for contract {} at address {}", + Paint::green(self.contract.name.clone()), + Paint::green(self.address.to_string()) + ); + } + + // If chain is not set, we try to get it from the RPC + // If RPC is not set, the default chain is used + let chain = if config.get_rpc_url().is_some() { + let chain_id = provider.get_chain_id().await?; + Chain::from(chain_id) + } else { + config.chain.unwrap_or_default() + }; + + // Set Etherscan options + self.etherscan_opts.chain = Some(chain); + self.etherscan_opts.key = + config.get_etherscan_config_with_chain(Some(chain))?.map(|c| c.key); + // Create etherscan client + let etherscan = Client::new(chain, self.etherscan_opts.key.clone().unwrap())?; + + // Get the constructor args using `source_code` endpoint + let source_code = etherscan.contract_source_code(self.address).await?; + + // Check if the contract name matches + let name = source_code.items.first().map(|item| item.contract_name.to_owned()); + if name.as_ref() != Some(&self.contract.name) { + eyre::bail!("Contract name mismatch"); + } + + // Get the constructor args from etherscan + let constructor_args = if let Some(args) = source_code.items.first() { + args.constructor_arguments.clone() + } else { + eyre::bail!("No constructor arguments found for contract at address {}", self.address); + }; + + // Get user provided constructor args + let provided_constructor_args = if let Some(args) = self.constructor_args.to_owned() { + args + } else if let Some(path) = self.constructor_args_path.to_owned() { + // Read from file + let res = read_constructor_args_file(path)?; + // Convert res to Bytes + res.join("") + } else { + constructor_args.to_string() + }; + + // Constructor args mismatch + if provided_constructor_args != constructor_args.to_string() && !self.json { + println!( + "{}", + Paint::red("The provider constructor args do not match the constructor args from etherscan. This will result in a mismatch - Using the args from etherscan").bold(), + ); + } + + // Get creation tx hash + let creation_data = etherscan.contract_creation_data(self.address).await?; + + let mut transaction = provider + .get_transaction_by_hash(creation_data.transaction_hash) + .await + .or_else(|e| eyre::bail!("Couldn't fetch transaction from RPC: {:?}", e))?; + let receipt = provider + .get_transaction_receipt(creation_data.transaction_hash) + .await + .or_else(|e| eyre::bail!("Couldn't fetch transacrion receipt from RPC: {:?}", e))?; + + let receipt = if let Some(receipt) = receipt { + receipt + } else { + eyre::bail!( + "Receipt not found for transaction hash {}", + creation_data.transaction_hash + ); + }; + // Extract creation code + let maybe_creation_code = if receipt.contract_address == Some(self.address) { + &transaction.input + } else if transaction.to == Some(DEFAULT_CREATE2_DEPLOYER) { + &transaction.input[32..] + } else { + eyre::bail!( + "Could not extract the creation code for contract at address {}", + self.address + ); + }; + + // If bytecode_hash is disabled then its always partial verification + let (verification_type, has_metadata) = + match (&self.verification_type, config.bytecode_hash) { + (VerificationType::Full, BytecodeHash::None) => (VerificationType::Partial, false), + (VerificationType::Partial, BytecodeHash::None) => { + (VerificationType::Partial, false) + } + (VerificationType::Full, _) => (VerificationType::Full, true), + (VerificationType::Partial, _) => (VerificationType::Partial, true), + }; + + // Etherscan compilation metadata + let etherscan_metadata = source_code.items.first().unwrap(); + + let local_bytecode = + if let Some(local_bytecode) = self.build_using_cache(etherscan_metadata, &config) { + local_bytecode + } else { + self.build_project(&config)? + }; + + // Append constructor args to the local_bytecode + let mut local_bytecode_vec = local_bytecode.to_vec(); + local_bytecode_vec.extend_from_slice(&constructor_args); + + // Cmp creation code with locally built bytecode and maybe_creation_code + let (did_match, with_status) = try_match( + local_bytecode_vec.as_slice(), + maybe_creation_code, + &constructor_args, + &verification_type, + false, + has_metadata, + )?; + + let mut json_results: Vec = vec![]; + self.print_result( + (did_match, with_status), + BytecodeType::Creation, + &mut json_results, + etherscan_metadata, + &config, + ); + + // Get contract creation block + let simulation_block = match self.block { + Some(BlockId::Number(BlockNumberOrTag::Number(block))) => block, + Some(_) => eyre::bail!("Invalid block number"), + None => { + let provider = utils::get_provider(&config)?; + provider + .get_transaction_by_hash(creation_data.transaction_hash) + .await.or_else(|e| eyre::bail!("Couldn't fetch transaction from RPC: {:?}", e))? + .block_number.ok_or_else(|| { + eyre::eyre!("Failed to get block number of the contract creation tx, specify using the --block flag") + })? + } + }; + + // Fork the chain at `simulation_block` + + let (mut fork_config, evm_opts) = config.clone().load_config_and_evm_opts()?; + fork_config.fork_block_number = Some(simulation_block - 1); + fork_config.evm_version = + etherscan_metadata.evm_version()?.unwrap_or(EvmVersion::default()); + let (mut env, fork, _chain) = + TracingExecutor::get_fork_material(&fork_config, evm_opts).await?; + + let mut executor = + TracingExecutor::new(env.clone(), fork, Some(fork_config.evm_version), false); + env.block.number = U256::from(simulation_block); + let block = provider.get_block(simulation_block.into(), true).await?; + + // Workaround for the NonceTooHigh issue as we're not simulating prior txs of the same + // block. + let prev_block_id = BlockId::Number(BlockNumberOrTag::Number(simulation_block - 1)); + let prev_block_nonce = provider + .get_transaction_count(creation_data.contract_creator, Some(prev_block_id)) + .await?; + transaction.nonce = prev_block_nonce; + + if let Some(ref block) = block { + env.block.timestamp = U256::from(block.header.timestamp); + env.block.coinbase = block.header.miner; + 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()); + env.block.gas_limit = U256::from(block.header.gas_limit); + } + + configure_tx_env(&mut env, &transaction); + + let env_with_handler = + EnvWithHandlerCfg::new(Box::new(env.clone()), HandlerCfg::new(SpecId::LATEST)); + + let contract_address = if let Some(to) = transaction.to { + 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."); + } + let result = executor.commit_tx_with_env(env_with_handler.to_owned())?; + + if result.result.len() > 20 { + eyre::bail!("Failed to deploy contract using commit_tx_with_env on fork at block {} | Err: Call result is greater than 20 bytes, cannot be converted to Address", simulation_block); + } + + Address::from_slice(&result.result) + } else { + let deploy_result = executor.deploy_with_env(env_with_handler, None)?; + deploy_result.address + }; + + // State commited using deploy_with_env, now get the runtime bytecode from the db. + let fork_runtime_code = executor + .backend + .basic(contract_address)? + .ok_or_else(|| { + eyre::eyre!( + "Failed to get runtime code for contract deployed on fork at address {}", + contract_address + ) + })? + .code + .ok_or_else(|| { + eyre::eyre!( + "Bytecode does not exist for contract deployed on fork at address {}", + contract_address + ) + })?; + + let onchain_runtime_code = provider + .get_code_at(self.address, BlockId::Number(BlockNumberOrTag::Number(simulation_block))) + .await?; + + // Compare the runtime bytecode with the locally built bytecode + let (did_match, with_status) = try_match( + &fork_runtime_code.bytecode, + &onchain_runtime_code, + &constructor_args, + &verification_type, + true, + has_metadata, + )?; + + self.print_result( + (did_match, with_status), + BytecodeType::Runtime, + &mut json_results, + etherscan_metadata, + &config, + ); + + if self.json { + println!("{}", serde_json::to_string(&json_results)?); + } + Ok(()) + } + + fn build_project(&self, config: &Config) -> Result { + let project = config.project()?; + let mut compiler = ProjectCompiler::new(); + + if let Some(skip) = &self.skip { + if !skip.is_empty() { + compiler = compiler.filter(Box::new(SkipBuildFilters::new(skip.to_owned())?)); + } + } + let output = compiler.compile(&project)?; + + let artifact = output + .find_contract(&self.contract) + .ok_or_eyre("Build Error: Contract artifact not found locally")?; + + let local_bytecode = artifact + .get_bytecode_object() + .ok_or_eyre("Contract artifact does not have bytecode")?; + + let local_bytecode = match local_bytecode.as_ref() { + BytecodeObject::Bytecode(bytes) => bytes, + BytecodeObject::Unlinked(_) => { + eyre::bail!("Unlinked bytecode is not supported for verification") + } + }; + + Ok(local_bytecode.to_owned()) + } + + fn build_using_cache(&self, etherscan_settings: &Metadata, config: &Config) -> Option { + let project = config.project().ok()?; + let cache = project.read_cache_file().ok()?; + let cached_artifacts = cache.read_artifacts::().ok()?; + + for (key, value) in cached_artifacts { + let name = self.contract.name.to_owned() + ".sol"; + let version = etherscan_settings.compiler_version.to_owned(); + if version.starts_with("vyper:") { + return None; + } + // Parse etherscan version string + let version = + version.split('+').next().unwrap_or("").trim_start_matches('v').to_string(); + if key.ends_with(name.as_str()) { + if let Some(artifact) = value.into_iter().next() { + if let Ok(version) = Version::parse(&version) { + if let Some(artifact) = artifact.1.iter().find(|a| { + a.version.major == version.major && + a.version.minor == version.minor && + a.version.patch == version.patch + }) { + return artifact + .artifact + .bytecode + .as_ref() + .and_then(|bytes| bytes.bytes().to_owned()) + .cloned(); + } + } + let artifact = artifact.1.first().unwrap(); // Get the first artifact + let local_bytecode = if let Some(local_bytecode) = &artifact.artifact.bytecode { + local_bytecode.bytes() + } else { + None + }; + + return local_bytecode.map(|bytes| bytes.to_owned()); + } + } + } + + None + } + + fn print_result( + &self, + res: (bool, Option), + bytecode_type: BytecodeType, + json_results: &mut Vec, + etherscan_config: &Metadata, + config: &Config, + ) { + if res.0 { + if !self.json { + println!( + "{} with status {}", + Paint::green(format!("{:?} code matched", bytecode_type)).bold(), + Paint::green(res.1.unwrap()).bold() + ); + } else { + let json_res = JsonResult { + bytecode_type, + matched: true, + verification_type: res.1.unwrap(), + message: None, + }; + json_results.push(json_res); + } + } else if !res.0 && !self.json { + println!( + "{}", + Paint::red(format!( + "{:?} code did not match - this may be due to varying compiler settings", + bytecode_type + )) + .bold() + ); + let mismatches = find_mismatch_in_settings(etherscan_config, config); + for mismatch in mismatches { + println!("{}", Paint::red(mismatch).bold()); + } + } else if !res.0 && self.json { + let json_res = JsonResult { + bytecode_type, + matched: false, + verification_type: self.verification_type, + message: Some(format!( + "{:?} code did not match - this may be due to varying compiler settings", + bytecode_type + )), + }; + json_results.push(json_res); + } + } +} + +/// Enum to represent the type of verification: `full` or `partial`. Ref: https://docs.sourcify.dev/docs/full-vs-partial-match/ +#[derive(Debug, Clone, clap::ValueEnum, Default, PartialEq, Eq, Serialize, Deserialize, Copy)] +pub enum VerificationType { + #[default] + #[serde(rename = "full")] + Full, + #[serde(rename = "partial")] + Partial, +} + +impl FromStr for VerificationType { + type Err = eyre::Error; + + fn from_str(s: &str) -> Result { + match s { + "full" => Ok(VerificationType::Full), + "partial" => Ok(VerificationType::Partial), + _ => eyre::bail!("Invalid verification type"), + } + } +} + +impl From for String { + fn from(v: VerificationType) -> Self { + match v { + VerificationType::Full => "full".to_string(), + VerificationType::Partial => "partial".to_string(), + } + } +} + +impl fmt::Display for VerificationType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + VerificationType::Full => write!(f, "full"), + VerificationType::Partial => write!(f, "partial"), + } + } +} + +/// Enum to represent the type of bytecode being verified +#[derive(Debug, Serialize, Deserialize, Clone, Copy)] +pub enum BytecodeType { + #[serde(rename = "creation")] + Creation, + #[serde(rename = "runtime")] + Runtime, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct JsonResult { + pub bytecode_type: BytecodeType, + pub matched: bool, + pub verification_type: VerificationType, + #[serde(skip_serializing_if = "Option::is_none")] + pub message: Option, +} + +fn try_match( + local_bytecode: &[u8], + bytecode: &[u8], + constructor_args: &[u8], + match_type: &VerificationType, + is_runtime: bool, + has_metadata: bool, +) -> Result<(bool, Option)> { + // 1. Try full match + if *match_type == VerificationType::Full && local_bytecode.starts_with(bytecode) { + Ok((true, Some(VerificationType::Full))) + } else { + try_partial_match(local_bytecode, bytecode, constructor_args, is_runtime, has_metadata) + .map(|matched| (matched, matched.then_some(VerificationType::Partial))) + } +} + +fn try_partial_match( + mut local_bytecode: &[u8], + mut bytecode: &[u8], + constructor_args: &[u8], + is_runtime: bool, + has_metadata: bool, +) -> Result { + // 1. Check length of constructor args + if constructor_args.is_empty() { + // Assume metadata is at the end of the bytecode + if has_metadata { + local_bytecode = extract_metadata_hash(local_bytecode)?; + bytecode = extract_metadata_hash(bytecode)?; + } + + // Now compare the creation code and bytecode + return Ok(local_bytecode.starts_with(bytecode)); + } + + if is_runtime { + if has_metadata { + local_bytecode = extract_metadata_hash(local_bytecode)?; + bytecode = extract_metadata_hash(bytecode)?; + } + + // Now compare the local code and bytecode + return Ok(local_bytecode.starts_with(bytecode)); + } + + // If not runtime, extract constructor args from the end of the bytecode + bytecode = &bytecode[..bytecode.len() - constructor_args.len()]; + local_bytecode = &local_bytecode[..local_bytecode.len() - constructor_args.len()]; + + if has_metadata { + local_bytecode = extract_metadata_hash(local_bytecode)?; + bytecode = extract_metadata_hash(bytecode)?; + } + + Ok(local_bytecode.starts_with(bytecode)) +} + +/// @dev This assumes that the metadata is at the end of the bytecode +fn extract_metadata_hash(bytecode: &[u8]) -> Result<&[u8]> { + // Get the last two bytes of the bytecode to find the length of CBOR metadata + let metadata_len = &bytecode[bytecode.len() - 2..]; + let metadata_len = u16::from_be_bytes([metadata_len[0], metadata_len[1]]); + + // Now discard the metadata from the bytecode + Ok(&bytecode[..bytecode.len() - 2 - metadata_len as usize]) +} + +fn find_mismatch_in_settings( + etherscan_settings: &Metadata, + local_settings: &Config, +) -> Vec { + let mut mismatches: Vec = vec![]; + if etherscan_settings.evm_version != local_settings.evm_version.to_string().to_lowercase() { + let str = format!( + "EVM version mismatch: local={}, onchain={}", + local_settings.evm_version, etherscan_settings.evm_version + ); + mismatches.push(str); + } + let local_optimizer: u64 = if local_settings.optimizer { 1 } else { 0 }; + if etherscan_settings.optimization_used != local_optimizer { + let str = format!( + "Optimizer mismatch: local={}, onchain={}", + local_settings.optimizer, etherscan_settings.optimization_used + ); + mismatches.push(str); + } + if etherscan_settings.runs != local_settings.optimizer_runs as u64 { + let str = format!( + "Optimizer runs mismatch: local={}, onchain={}", + local_settings.optimizer_runs, etherscan_settings.runs + ); + mismatches.push(str); + } + + mismatches +} diff --git a/crates/verify/src/lib.rs b/crates/verify/src/lib.rs index be451d83f..ee6dd13f6 100644 --- a/crates/verify/src/lib.rs +++ b/crates/verify/src/lib.rs @@ -23,6 +23,7 @@ use etherscan::EtherscanVerificationProvider; pub mod provider; use provider::VerificationProvider; +pub mod bytecode; pub mod retry; mod sourcify; From 440ec525deb00b4dca138794865c27d1e8ea4d01 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 10 Apr 2024 23:30:07 +0400 Subject: [PATCH 158/430] fix: `assertApproxEqRel` edge case (#7630) fix: assertApproxEqRel edge case --- crates/cheatcodes/src/test/assert.rs | 36 +++++++++++++++++----------- testdata/default/cheats/Assert.t.sol | 2 ++ 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/crates/cheatcodes/src/test/assert.rs b/crates/cheatcodes/src/test/assert.rs index d727626ca..3c1ab22f3 100644 --- a/crates/cheatcodes/src/test/assert.rs +++ b/crates/cheatcodes/src/test/assert.rs @@ -1122,13 +1122,17 @@ fn uint_assert_approx_eq_rel( right: U256, max_delta: U256, ) -> Result, EqRelAssertionError> { - if right.is_zero() && !left.is_zero() { - return Err(EqRelAssertionError::Failure(Box::new(EqRelAssertionFailure { - left, - right, - max_delta, - real_delta: EqRelDelta::Undefined, - }))) + if right.is_zero() { + if left.is_zero() { + return Ok(Default::default()) + } else { + return Err(EqRelAssertionError::Failure(Box::new(EqRelAssertionFailure { + left, + right, + max_delta, + real_delta: EqRelDelta::Undefined, + }))) + }; } let delta = get_delta_uint(left, right) @@ -1153,13 +1157,17 @@ fn int_assert_approx_eq_rel( right: I256, max_delta: U256, ) -> Result, EqRelAssertionError> { - if right.is_zero() && !left.is_zero() { - return Err(EqRelAssertionError::Failure(Box::new(EqRelAssertionFailure { - left, - right, - max_delta, - real_delta: EqRelDelta::Undefined, - }))) + if right.is_zero() { + if left.is_zero() { + return Ok(Default::default()) + } else { + return Err(EqRelAssertionError::Failure(Box::new(EqRelAssertionFailure { + left, + right, + max_delta, + real_delta: EqRelDelta::Undefined, + }))) + } } let (_, abs_right) = right.into_sign_and_abs(); diff --git a/testdata/default/cheats/Assert.t.sol b/testdata/default/cheats/Assert.t.sol index d2b0dcb35..b33af6292 100644 --- a/testdata/default/cheats/Assert.t.sol +++ b/testdata/default/cheats/Assert.t.sol @@ -823,5 +823,7 @@ contract AssertionsTest is DSTest { bytes("assertion failed: 1 !~= 0 (max delta: 0.0000000000000000%, real delta: undefined)") ); vm.assertApproxEqRel(uint256(1), uint256(0), uint256(0)); + + vm.assertApproxEqRel(uint256(0), uint256(0), uint256(0)); } } From a622e0fe4374287441ebca3470351d78d7da4479 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 11 Apr 2024 19:52:16 +0400 Subject: [PATCH 159/430] fix: use correct estimator (#7638) --- Cargo.lock | 45 +++++++++++++------------ Cargo.toml | 44 ++++++++++++------------ crates/forge/tests/cli/script.rs | 58 ++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 28a029aa4..6471888fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,7 +79,7 @@ dependencies = [ [[package]] name = "alloy-consensus" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" +source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" dependencies = [ "alloy-eips", "alloy-primitives", @@ -94,7 +94,7 @@ dependencies = [ [[package]] name = "alloy-contract" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" +source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -133,7 +133,7 @@ dependencies = [ [[package]] name = "alloy-eips" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" +source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -147,7 +147,7 @@ dependencies = [ [[package]] name = "alloy-genesis" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" +source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" dependencies = [ "alloy-primitives", "alloy-serde", @@ -169,18 +169,19 @@ dependencies = [ [[package]] name = "alloy-json-rpc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" +source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" dependencies = [ "alloy-primitives", "serde", "serde_json", "thiserror", + "tracing", ] [[package]] name = "alloy-network" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" +source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" dependencies = [ "alloy-consensus", "alloy-eips", @@ -223,7 +224,7 @@ dependencies = [ [[package]] name = "alloy-provider" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" +source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" dependencies = [ "alloy-eips", "alloy-json-rpc", @@ -249,7 +250,7 @@ dependencies = [ [[package]] name = "alloy-pubsub" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" +source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -289,7 +290,7 @@ dependencies = [ [[package]] name = "alloy-rpc-client" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" +source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -309,7 +310,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" +source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" dependencies = [ "alloy-consensus", "alloy-eips", @@ -327,7 +328,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" +source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -339,7 +340,7 @@ dependencies = [ [[package]] name = "alloy-serde" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" +source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" dependencies = [ "alloy-primitives", "serde", @@ -349,7 +350,7 @@ dependencies = [ [[package]] name = "alloy-signer" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" +source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -364,7 +365,7 @@ dependencies = [ [[package]] name = "alloy-signer-aws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" +source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" dependencies = [ "alloy-consensus", "alloy-network", @@ -381,7 +382,7 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" +source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" dependencies = [ "alloy-consensus", "alloy-network", @@ -398,7 +399,7 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" +source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" dependencies = [ "alloy-consensus", "alloy-network", @@ -414,7 +415,7 @@ dependencies = [ [[package]] name = "alloy-signer-wallet" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" +source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" dependencies = [ "alloy-consensus", "alloy-network", @@ -491,7 +492,7 @@ dependencies = [ [[package]] name = "alloy-transport" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" +source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" dependencies = [ "alloy-json-rpc", "base64 0.22.0", @@ -509,7 +510,7 @@ dependencies = [ [[package]] name = "alloy-transport-http" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" +source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -522,7 +523,7 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" +source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -540,7 +541,7 @@ dependencies = [ [[package]] name = "alloy-transport-ws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=987b393#987b3936f78c067baedcf811620975d3a4a26443" +source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -6851,7 +6852,7 @@ dependencies = [ [[package]] name = "revm-inspectors" version = "0.1.0" -source = "git+https://github.com/paradigmxyz/evm-inspectors?rev=21f8f3d#21f8f3d266b05d1084e06f0c5331f2f1f4ed0905" +source = "git+https://github.com/paradigmxyz/evm-inspectors?rev=413b892#413b892dd936d117c52d47ba07d195b09a7f1216" dependencies = [ "alloy-primitives", "alloy-rpc-types", diff --git a/Cargo.toml b/Cargo.toml index 93a4f2455..82e29e6f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -144,7 +144,7 @@ foundry-compilers = { version = "0.3.14", default-features = false } # no default features to avoid c-kzg revm = { version = "8", default-features = false } revm-primitives = { version = "3", default-features = false } -revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "21f8f3d", features = [ +revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "413b892", features = [ "serde", ] } @@ -158,27 +158,27 @@ ethers-signers = { version = "2.0.14", default-features = false } ethers-middleware = { version = "2.0.14", default-features = false } ## alloy -alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } -alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } -alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } -alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } -alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } -alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } -alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } -alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } -alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } -alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } -alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } -alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } -alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } -alloy-signer-wallet = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } -alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } -alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } -alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } -alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } -alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } -alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } -alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "987b393", default-features = false } +alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } +alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } +alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } +alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } +alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } +alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } +alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } +alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } +alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } +alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } +alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } +alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } +alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } +alloy-signer-wallet = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } +alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } +alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } +alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } +alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } +alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } +alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } +alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } alloy-primitives = { version = "0.7.0", features = ["getrandom"] } alloy-dyn-abi = "0.7.0" alloy-json-abi = "0.7.0" diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index 8af676efd..e56ae08ef 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -1231,3 +1231,61 @@ contract CustomErrorScript is Script { cmd.arg("script").arg(script).args(["--tc", "CustomErrorScript"]); assert!(cmd.stderr_lossy().contains("script failed: CustomError()")); }); + +// https://github.com/foundry-rs/foundry/issues/7620 +forgetest_async!(can_run_zero_base_fee, |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 { + vm.startBroadcast(); + address(0).call(""); + } +} + "#, + ) + .unwrap(); + + let node_config = NodeConfig::test().with_base_fee(Some(0)); + let (_api, handle) = spawn(node_config).await; + let dev = handle.dev_accounts().next().unwrap(); + + // Firstly run script with non-zero gas prices to ensure that eth_feeHistory contains non-zero + // values. + cmd.args([ + "script", + "SimpleScript", + "--fork-url", + &handle.http_endpoint(), + "--sender", + format!("{dev:?}").as_str(), + "--broadcast", + "--unlocked", + "--with-gas-price", + "2000000", + "--priority-gas-price", + "100000", + ]); + + let output = cmd.stdout_lossy(); + assert!(output.contains("ONCHAIN EXECUTION COMPLETE & SUCCESSFUL")); + + // Ensure that we can correctly estimate gas when base fee is zero but priority fee is not. + cmd.forge_fuse().args([ + "script", + "SimpleScript", + "--fork-url", + &handle.http_endpoint(), + "--sender", + format!("{dev:?}").as_str(), + "--broadcast", + "--unlocked", + ]); + + let output = cmd.stdout_lossy(); + assert!(output.contains("ONCHAIN EXECUTION COMPLETE & SUCCESSFUL")); +}); From bdc04c278f8ac716ed5fd3994bc0da841807b5cf Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 11 Apr 2024 22:45:48 +0400 Subject: [PATCH 160/430] fix: state diff for broadcasted CREATE2 deployments (#7632) fix: fix state diff for broadcasted CREATE2 deployments --- crates/cheatcodes/src/inspector.rs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index cd2d49ba6..34a3d9260 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -1342,15 +1342,6 @@ impl Inspector for Cheatcodes { let address = self.allow_cheatcodes_on_create(ecx, call); // If `recordAccountAccesses` has been called, record the create if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack { - // If the create scheme is create2, and the caller is the DEFAULT_CREATE2_DEPLOYER then - // we must add 1 to the depth to account for the call to the create2 factory. - let mut depth = ecx.journaled_state.depth(); - if let CreateScheme::Create2 { salt: _ } = call.scheme { - if call.caller == DEFAULT_CREATE2_DEPLOYER { - depth += 1; - } - } - // Record the create context as an account access and create a new vector to record all // subsequent account accesses recorded_account_diffs_stack.push(vec![AccountAccess { @@ -1369,7 +1360,7 @@ impl Inspector for Cheatcodes { reverted: false, deployedCode: Bytes::new(), // updated on create_end storageAccesses: vec![], // updated on create_end - depth, + depth: ecx.journaled_state.depth(), }]); } From 89f0fb923773cf0f8f966290e579bae92f505077 Mon Sep 17 00:00:00 2001 From: Mihir Wadekar Date: Fri, 12 Apr 2024 13:48:36 -0700 Subject: [PATCH 161/430] Adds rpc method for anvil to drop all pending transactions (#7643) * feat: Adds rpc method for anvil to drop all pending transactions * fix: drop_all_transactions will now drop both pending and ready txs * fix: moved internal drop_all_transactions to clear, and modified logic to clear transaction internals + be more performant * rustfmt --------- Co-authored-by: Matthias Seitz --- crates/anvil/core/src/eth/mod.rs | 11 +++++++++++ crates/anvil/src/eth/api.rs | 12 ++++++++++++ crates/anvil/src/eth/pool/mod.rs | 12 ++++++++++++ crates/anvil/src/eth/pool/transactions.rs | 14 ++++++++++++++ 4 files changed, 49 insertions(+) diff --git a/crates/anvil/core/src/eth/mod.rs b/crates/anvil/core/src/eth/mod.rs index 7a2a153e8..a8a07ba92 100644 --- a/crates/anvil/core/src/eth/mod.rs +++ b/crates/anvil/core/src/eth/mod.rs @@ -371,6 +371,17 @@ pub enum EthRequest { )] DropTransaction(B256), + /// Removes transactions from the pool + #[cfg_attr( + feature = "serde", + serde( + rename = "anvil_dropAllTransactions", + alias = "hardhat_dropAllTransactions", + with = "empty_params" + ) + )] + DropAllTransactions(), + /// Reset the fork to a fresh forked state, and optionally update the fork config #[cfg_attr(feature = "serde", serde(rename = "anvil_reset", alias = "hardhat_reset"))] Reset(#[cfg_attr(feature = "serde", serde(default))] Option>>), diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index d21ed852f..1ed664a34 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -296,6 +296,9 @@ impl EthApi { EthRequest::DropTransaction(tx) => { self.anvil_drop_transaction(tx).await.to_rpc_result() } + EthRequest::DropAllTransactions() => { + self.anvil_drop_all_transactions().await.to_rpc_result() + } EthRequest::Reset(fork) => { self.anvil_reset(fork.and_then(|p| p.params)).await.to_rpc_result() } @@ -1572,6 +1575,15 @@ impl EthApi { Ok(self.pool.drop_transaction(tx_hash).map(|tx| tx.hash())) } + /// Removes all transactions from the pool + /// + /// Handler for RPC call: `anvil_dropAllTransactions` + pub async fn anvil_drop_all_transactions(&self) -> Result<()> { + node_info!("anvil_dropAllTransactions"); + self.pool.clear(); + Ok(()) + } + /// Reset the fork to a fresh forked state, and optionally update the fork config. /// /// If `forking` is `None` then this will disable forking entirely. diff --git a/crates/anvil/src/eth/pool/mod.rs b/crates/anvil/src/eth/pool/mod.rs index 34a75c205..674885500 100644 --- a/crates/anvil/src/eth/pool/mod.rs +++ b/crates/anvil/src/eth/pool/mod.rs @@ -166,6 +166,12 @@ impl Pool { dropped } + /// Removes all transactions from the pool + pub fn clear(&self) { + let mut pool = self.inner.write(); + pool.clear(); + } + /// notifies all listeners about the transaction fn notify_listener(&self, hash: TxHash) { let mut listener = self.transaction_listener.lock(); @@ -211,6 +217,12 @@ impl PoolInner { self.ready_transactions.get_transactions() } + /// Clears + fn clear(&mut self) { + self.ready_transactions.clear(); + self.pending_transactions.clear(); + } + /// checks both pools for the matching transaction /// /// Returns `None` if the transaction does not exist in the pool diff --git a/crates/anvil/src/eth/pool/transactions.rs b/crates/anvil/src/eth/pool/transactions.rs index a88bc369c..9a229001d 100644 --- a/crates/anvil/src/eth/pool/transactions.rs +++ b/crates/anvil/src/eth/pool/transactions.rs @@ -134,6 +134,13 @@ impl PendingTransactions { self.waiting_queue.is_empty() } + /// Clears internal state + pub fn clear(&mut self) { + self.required_markers.clear(); + self.waiting_markers.clear(); + self.waiting_queue.clear(); + } + /// Returns an iterator over all transactions in the waiting pool pub fn transactions(&self) -> impl Iterator> + '_ { self.waiting_queue.values().map(|tx| tx.transaction.clone()) @@ -377,6 +384,13 @@ impl ReadyTransactions { } } + /// Clears the internal state + pub fn clear(&mut self) { + self.provided_markers.clear(); + self.ready_tx.write().clear(); + self.independent_transactions.clear(); + } + /// Returns true if the transaction is part of the queue. pub fn contains(&self, hash: &TxHash) -> bool { self.ready_tx.read().contains_key(hash) From 1ca9b854ef261d167b802da4a11665f6c3e26c4b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 14 Apr 2024 14:37:03 +0200 Subject: [PATCH 162/430] chore(deps): weekly `cargo update` (#7655) Updating git repository `https://github.com/alloy-rs/alloy` Updating git repository `https://github.com/paradigmxyz/evm-inspectors` Updating allocator-api2 v0.2.16 -> v0.2.18 Updating alloy-chains v0.1.15 -> v0.1.16 Updating anyhow v1.0.81 -> v1.0.82 Updating async-trait v0.1.79 -> v0.1.80 Updating aws-config v1.1.9 -> v1.2.0 Updating aws-credential-types v1.1.8 -> v1.2.0 Updating aws-runtime v1.1.8 -> v1.2.0 Updating aws-sdk-kms v1.19.0 -> v1.21.0 Updating aws-sdk-sso v1.18.0 -> v1.20.0 Updating aws-sdk-ssooidc v1.18.0 -> v1.20.0 Updating aws-sdk-sts v1.18.0 -> v1.20.0 Updating aws-smithy-runtime v1.2.1 -> v1.3.0 Updating aws-smithy-runtime-api v1.3.0 -> v1.4.0 Updating aws-types v1.1.8 -> v1.2.0 Updating bumpalo v3.15.4 -> v3.16.0 Updating cc v1.0.90 -> v1.0.94 Updating clap_complete v4.5.1 -> v4.5.2 Adding cmake v0.1.50 Updating comfy-table v7.1.0 -> v7.1.1 Adding constant_time_eq v0.3.0 Updating crc v3.0.1 -> v3.2.1 Adding deflate64 v0.1.8 Updating either v1.10.0 -> v1.11.0 Updating encoding_rs v0.8.33 -> v0.8.34 Updating event-listener v5.2.0 -> v5.3.0 Updating figment v0.10.15 -> v0.10.16 Updating foundry-compilers v0.3.14 -> v0.3.15 Adding fs4 v0.8.2 Updating getrandom v0.2.12 -> v0.2.14 Updating gix-trace v0.1.8 -> v0.1.9 Updating gix-utils v0.1.11 -> v0.1.12 Updating half v2.4.0 -> v2.4.1 Adding hyper-tls v0.6.0 Updating jobserver v0.1.28 -> v0.1.30 Adding libz-ng-sys v1.1.15 Updating num v0.4.1 -> v0.4.2 Updating quote v1.0.35 -> v1.0.36 Updating reqwest v0.12.2 -> v0.12.3 Updating rustls-pemfile v2.1.1 -> v2.1.2 Updating rustversion v1.0.14 -> v1.0.15 Updating scale-info v2.11.1 -> v2.11.2 Updating scale-info-derive v2.11.1 -> v2.11.2 Updating serde_repr v0.1.18 -> v0.1.19 Adding simd-adler32 v0.3.7 Adding svm-rs v0.5.1 Updating svm-rs-builds v0.4.1 -> v0.5.1 Updating time v0.3.34 -> v0.3.36 Updating time-macros v0.2.17 -> v0.2.18 Adding typed-arena v2.0.2 Updating windows-targets v0.52.4 -> v0.52.5 Updating windows_aarch64_gnullvm v0.52.4 -> v0.52.5 Updating windows_aarch64_msvc v0.52.4 -> v0.52.5 Updating windows_i686_gnu v0.52.4 -> v0.52.5 Adding windows_i686_gnullvm v0.52.5 Updating windows_i686_msvc v0.52.4 -> v0.52.5 Updating windows_x86_64_gnu v0.52.4 -> v0.52.5 Updating windows_x86_64_gnullvm v0.52.4 -> v0.52.5 Updating windows_x86_64_msvc v0.52.4 -> v0.52.5 Updating winnow v0.6.5 -> v0.6.6 Adding winreg v0.52.0 Adding zip_next v1.0.1 Adding zopfli v0.8.0 Adding zstd v0.13.1 Adding zstd-safe v7.1.0 note: pass `--verbose` to see 179 unchanged dependencies behind latest Co-authored-by: mattsse --- Cargo.lock | 445 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 306 insertions(+), 139 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6471888fd..a7b888b03 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,15 +61,15 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "alloy-chains" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e96c81b05c893348760f232c4cc6a6a77fd91cfb09885d4eaad25cd03bd7732e" +checksum = "40646aa7f01e396139cf0d6c3a7475eeb8094a0f41d8199f10860c8aef09d2f1" dependencies = [ "num_enum", "serde", @@ -127,7 +127,7 @@ dependencies = [ "proptest", "serde", "serde_json", - "winnow 0.6.5", + "winnow 0.6.6", ] [[package]] @@ -208,7 +208,7 @@ dependencies = [ "derive_arbitrary", "derive_more", "ethereum_ssz", - "getrandom 0.2.12", + "getrandom 0.2.14", "hex-literal", "itoa", "k256", @@ -473,7 +473,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "715f4d09a330cc181fc7c361b5c5c2766408fa59a0bac60349dcb7baabd404cc" dependencies = [ - "winnow 0.6.5", + "winnow 0.6.6", ] [[package]] @@ -514,7 +514,7 @@ source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be9 dependencies = [ "alloy-json-rpc", "alloy-transport", - "reqwest 0.12.2", + "reqwest 0.12.3", "serde_json", "tower", "url", @@ -684,7 +684,7 @@ dependencies = [ "clap", "clap_complete", "clap_complete_fig", - "crc 3.0.1", + "crc 3.2.1", "ctrlc", "ethereum-forkid", "ethers", @@ -775,15 +775,18 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" [[package]] name = "arbitrary" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] [[package]] name = "ariadne" @@ -941,7 +944,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" dependencies = [ "concurrent-queue", - "event-listener 5.2.0", + "event-listener 5.3.0", "event-listener-strategy 0.5.1", "futures-core", "pin-project-lite", @@ -1008,9 +1011,9 @@ checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" [[package]] name = "async-trait" -version = "0.1.79" +version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", @@ -1078,9 +1081,9 @@ checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "aws-config" -version = "1.1.9" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297b64446175a73987cedc3c438d79b2a654d0fff96f65ff530fbe039347644c" +checksum = "e2a89e0000cde82447155d64eeb71720b933b4396a6fbbebad3f8b4f88ca7b54" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1109,9 +1112,9 @@ dependencies = [ [[package]] name = "aws-credential-types" -version = "1.1.8" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa8587ae17c8e967e4b05a62d495be2fb7701bec52a97f7acfe8a29f938384c8" +checksum = "e16838e6c9e12125face1c1eff1343c75e3ff540de98ff7ebd61874a89bcfeb9" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -1121,9 +1124,9 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.1.8" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b13dc54b4b49f8288532334bba8f87386a40571c47c37b1304979b556dc613c8" +checksum = "f4963ac9ff2d33a4231b3806c1c69f578f221a9cabb89ad2bde62ce2b442c8a7" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -1144,9 +1147,9 @@ dependencies = [ [[package]] name = "aws-sdk-kms" -version = "1.19.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a4e610d67363f6846c903ebca4ce65439033d5ec2a5d8effc96d5eaa53355" +checksum = "d1747213c6bb8fae0f388157e07e144fd442c1e28cfd9c4e257b1b6ee26c4a54" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1166,9 +1169,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.18.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "019a07902c43b03167ea5df0182f0cb63fae89f9a9682c44d18cf2e4a042cb34" +checksum = "32fcc572fd5c58489ec205ec3e4e5f7d63018898a485cbf922a462af496bc300" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1188,9 +1191,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.18.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04c46ee08a48a7f4eaa4ad201dcc1dd537b49c50859d14d4510e00ad9d3f9af2" +checksum = "5b6275fa8684a1192754221173b1f7a7c1260d6b0571cc2b8af09468eb0cffe5" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1210,9 +1213,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.18.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f752ac730125ca6017f72f9db5ec1772c9ecc664f87aa7507a7d81b023c23713" +checksum = "30acd58272fd567e4853c5075d838be1626b59057e0249c9be5a1a7eb13bf70f" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1306,9 +1309,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c53572b4cd934ee5e8461ad53caa36e9d246aaef42166e3ac539e206a925d330" +checksum = "de34bcfa1fb3c82a80e252a753db34a6658e07f23d3a5b3fc96919518fa7a3f5" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -1332,9 +1335,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime-api" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccb2b3a7030dc9a3c9a08ce0b25decea5130e9db19619d4dffbbff34f75fe850" +checksum = "4cc56a5c96ec741de6c5e6bf1ce6948be969d6506dfa9c39cffc284e31e4979b" dependencies = [ "aws-smithy-async", "aws-smithy-types", @@ -1381,9 +1384,9 @@ dependencies = [ [[package]] name = "aws-types" -version = "1.1.8" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dbf2f3da841a8930f159163175cf6a3d16ddde517c1b0fba7aa776822800f40" +checksum = "5a43b56df2c529fe44cb4d92bd64d0479883fb9608ff62daede4df5405381814" dependencies = [ "aws-credential-types", "aws-smithy-async", @@ -1632,9 +1635,9 @@ checksum = "b4ae4235e6dac0694637c763029ecea1a2ec9e4e06ec2729bd21ba4d9c863eb7" [[package]] name = "bumpalo" -version = "3.15.4" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "byte-slice-cast" @@ -1814,9 +1817,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.90" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" +checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7" dependencies = [ "jobserver", "libc", @@ -1882,7 +1885,7 @@ dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -1949,9 +1952,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.1" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "885e4d7d5af40bfb99ae6f9433e292feac98d452dcb3ec3d25dfe7552b77da8c" +checksum = "dd79504325bf38b10165b02e89b4347300f855f273c4cb30c4a3209e6583275e" dependencies = [ "clap", ] @@ -2008,6 +2011,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", +] + [[package]] name = "coins-bip32" version = "0.8.7" @@ -2069,7 +2081,7 @@ dependencies = [ "async-trait", "byteorder", "cfg-if", - "getrandom 0.2.12", + "getrandom 0.2.14", "hex", "hidapi-rusb", "js-sys", @@ -2118,13 +2130,13 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "comfy-table" -version = "7.1.0" +version = "7.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c64043d6c7b7a4c58e39e7efccfdea7b93d885a795d0c054a69dbbf4dd52686" +checksum = "b34115915337defe99b2aff5c2ce6771e5fbc4079f4b506301f5cf394c8452f7" dependencies = [ "crossterm", - "strum 0.25.0", - "strum_macros 0.25.3", + "strum 0.26.2", + "strum_macros 0.26.2", "unicode-width", ] @@ -2187,6 +2199,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +[[package]] +name = "constant_time_eq" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" + [[package]] name = "convert_case" version = "0.4.0" @@ -2229,9 +2247,9 @@ dependencies = [ [[package]] name = "crc" -version = "3.0.1" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" dependencies = [ "crc-catalog", ] @@ -2449,6 +2467,12 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" +[[package]] +name = "deflate64" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83ace6c86376be0b6cdcf3fb41882e81d94b31587573d1cfa9d01cd06bba210d" + [[package]] name = "der" version = "0.7.9" @@ -2671,9 +2695,9 @@ dependencies = [ [[package]] name = "either" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "elasticlunr-rs" @@ -2724,9 +2748,9 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] @@ -3177,9 +3201,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b5fb89194fa3cad959b833185b3063ba881dbfc7030680b314250779fb4cc91" +checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" dependencies = [ "concurrent-queue", "parking", @@ -3202,7 +3226,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "332f51cb23d20b0de8458b86580878211da09bcd4503cb579c225b3d124cabb3" dependencies = [ - "event-listener 5.2.0", + "event-listener 5.3.0", "pin-project-lite", ] @@ -3309,9 +3333,9 @@ dependencies = [ [[package]] name = "figment" -version = "0.10.15" +version = "0.10.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7270677e7067213e04f323b55084586195f18308cd7546cfac9f873344ccceb6" +checksum = "fdefe49ed1057d124dc81a0681c30dd07de56ad96e32adc7b64e8f28eaab31c4" dependencies = [ "atomic", "parking_lot", @@ -3361,6 +3385,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", + "libz-ng-sys", "miniz_oxide", ] @@ -3601,7 +3626,7 @@ dependencies = [ "alloy-json-abi", "alloy-primitives", "foundry-compilers", - "reqwest 0.12.2", + "reqwest 0.12.3", "semver 1.0.22", "serde", "serde_json", @@ -3737,7 +3762,7 @@ dependencies = [ "pretty_assertions", "rand 0.8.5", "reqwest 0.11.27", - "reqwest 0.12.2", + "reqwest 0.12.3", "rustc-hash", "semver 1.0.22", "serde", @@ -3754,9 +3779,9 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd3323f90e9f256a2c359dbb1cc7e1b7e4fef9e04e5a82693895e959a6efe010" +checksum = "2c1990477446ea72d80da26951cf7ba68652f9e5b481e3a469f09c4b120f647f" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3779,7 +3804,7 @@ dependencies = [ "serde_json", "sha2", "solang-parser", - "svm-rs 0.4.1", + "svm-rs 0.5.1", "svm-rs-builds", "tempfile", "thiserror", @@ -3808,7 +3833,7 @@ dependencies = [ "path-slash", "pretty_assertions", "regex", - "reqwest 0.12.2", + "reqwest 0.12.3", "revm-primitives", "semver 1.0.22", "serde", @@ -4065,6 +4090,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "fs4" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21dabded2e32cd57ded879041205c60a4a4c4bab47bd0fd2fa8b01f30849f02b" +dependencies = [ + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "fs_extra" version = "1.3.0" @@ -4254,9 +4289,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if", "js-sys", @@ -4465,15 +4500,15 @@ dependencies = [ [[package]] name = "gix-trace" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b838b2db8f62c9447d483a4c28d251b67fee32741a82cb4d35e9eb4e9fdc5ab" +checksum = "f924267408915fddcd558e3f37295cc7d6a3e50f8bd8b606cee0808c3915157e" [[package]] name = "gix-utils" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0066432d4c277f9877f091279a597ea5331f68ca410efc874f0bdfb1cd348f92" +checksum = "35192df7fd0fa112263bad8021e2df7167df4cc2a6e6d15892e1e55621d3d4dc" dependencies = [ "fastrand", "unicode-normalization", @@ -4552,9 +4587,9 @@ dependencies = [ [[package]] name = "half" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" dependencies = [ "cfg-if", "crunchy", @@ -4840,6 +4875,22 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper 1.2.0", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.3" @@ -5129,9 +5180,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.28" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" +checksum = "685a7d121ee3f65ae4fddd72b25a04bb36b6af81bc0828f7d5434c0fe60fa3a2" dependencies = [ "libc", ] @@ -5296,6 +5347,16 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "libz-ng-sys" +version = "1.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6409efc61b12687963e602df8ecf70e8ddacf95bc6576bcf16e3ac6328083c5" +dependencies = [ + "cmake", + "libc", +] + [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -5618,9 +5679,9 @@ dependencies = [ [[package]] name = "num" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +checksum = "3135b08af27d103b0a51f2ae0f8632117b7b185ccf931445affa8df530576a41" dependencies = [ "num-bigint", "num-complex", @@ -6536,9 +6597,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -6618,7 +6679,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.12", + "getrandom 0.2.14", ] [[package]] @@ -6692,7 +6753,7 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ - "getrandom 0.2.12", + "getrandom 0.2.14", "libredox", "thiserror", ] @@ -6763,7 +6824,7 @@ dependencies = [ "http-body 0.4.6", "hyper 0.14.28", "hyper-rustls 0.24.2", - "hyper-tls", + "hyper-tls 0.5.0", "ipnet", "js-sys", "log", @@ -6789,17 +6850,18 @@ dependencies = [ "wasm-bindgen-futures", "web-sys", "webpki-roots 0.25.4", - "winreg", + "winreg 0.50.0", ] [[package]] name = "reqwest" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d66674f2b6fb864665eea7a3c1ac4e3dfacd2fda83cf6f935a612e01b0e3338" +checksum = "3e6cc1e89e689536eb5aeede61520e874df5a4707df811cd5da4aa5fbb2aae19" dependencies = [ - "base64 0.21.7", + "base64 0.22.0", "bytes", + "futures-channel", "futures-core", "futures-util", "http 1.1.0", @@ -6807,23 +6869,26 @@ dependencies = [ "http-body-util", "hyper 1.2.0", "hyper-rustls 0.26.0", + "hyper-tls 0.6.0", "hyper-util", "ipnet", "js-sys", "log", "mime", + "native-tls", "once_cell", "percent-encoding", "pin-project-lite", "rustls 0.22.3", "rustls-native-certs 0.7.0", - "rustls-pemfile 1.0.4", + "rustls-pemfile 2.1.2", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", "tokio", + "tokio-native-tls", "tokio-rustls 0.25.0", "tower-service", "url", @@ -6831,7 +6896,7 @@ dependencies = [ "wasm-bindgen-futures", "web-sys", "webpki-roots 0.26.1", - "winreg", + "winreg 0.52.0", ] [[package]] @@ -6945,7 +7010,7 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.12", + "getrandom 0.2.14", "libc", "spin 0.9.8", "untrusted 0.9.0", @@ -7139,7 +7204,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" dependencies = [ "openssl-probe", - "rustls-pemfile 2.1.1", + "rustls-pemfile 2.1.2", "rustls-pki-types", "schannel", "security-framework", @@ -7156,11 +7221,11 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f48172685e6ff52a556baa527774f61fcaa884f59daf3375c62a3f1cd2549dab" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" dependencies = [ - "base64 0.21.7", + "base64 0.22.0", "rustls-pki-types", ] @@ -7193,9 +7258,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" [[package]] name = "rusty-fork" @@ -7258,9 +7323,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.11.1" +version = "2.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "788745a868b0e751750388f4e6546eb921ef714a4317fa6954f7cde114eb2eb7" +checksum = "7c453e59a955f81fb62ee5d596b450383d699f152d350e9d23a0db2adb78e4c0" dependencies = [ "cfg-if", "derive_more", @@ -7270,9 +7335,9 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.11.1" +version = "2.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dc2f4e8bc344b9fc3d5f74f72c2e55bfc38d28dc2ebc69c194a3df424e4d9ac" +checksum = "18cf6c6447f813ef19eb450e985bcce6705f9ce7660db221b59093d15c79c4b7" dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", @@ -7501,9 +7566,9 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", @@ -7665,6 +7730,12 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "similar" version = "2.5.0" @@ -7900,7 +7971,7 @@ checksum = "9bd5e919f01c9280dce59ab66296449d0e9144b8472b8892fbacf9612998b653" dependencies = [ "const-hex", "dirs 5.0.1", - "fs4", + "fs4 0.7.0", "once_cell", "reqwest 0.11.27", "semver 1.0.22", @@ -7912,17 +7983,37 @@ dependencies = [ "zip", ] +[[package]] +name = "svm-rs" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c912d2f0dfbf9d8ba683b3181c4bd6d042bac9279d5c062346c253c1eadf46e2" +dependencies = [ + "const-hex", + "dirs 5.0.1", + "fs4 0.8.2", + "once_cell", + "reqwest 0.12.3", + "semver 1.0.22", + "serde", + "serde_json", + "sha2", + "thiserror", + "url", + "zip_next", +] + [[package]] name = "svm-rs-builds" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bcf7abc816dd67daf88fccfb835118b0e71cf8cc3e1d0e120893e139799df6c" +checksum = "ddc1e420b6e969cb161e3b1758ea50cbde308c82fbfcb322979eae71c5cc947a" dependencies = [ "build_const", "const-hex", "semver 1.0.22", "serde_json", - "svm-rs 0.4.1", + "svm-rs 0.5.1", ] [[package]] @@ -8110,9 +8201,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.34" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", @@ -8133,9 +8224,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ "num-conv", "time-core", @@ -8358,7 +8449,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.5", + "winnow 0.6.6", ] [[package]] @@ -8542,6 +8633,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" @@ -8683,7 +8780,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ - "getrandom 0.2.12", + "getrandom 0.2.14", "serde", ] @@ -8966,7 +9063,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -8993,7 +9090,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -9028,17 +9125,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -9055,9 +9153,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -9073,9 +9171,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -9091,9 +9189,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -9109,9 +9213,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -9127,9 +9231,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -9145,9 +9249,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -9163,9 +9267,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" @@ -9178,9 +9282,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" +checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352" dependencies = [ "memchr", ] @@ -9195,6 +9299,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "winreg" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "ws_stream_wasm" version = "0.7.4" @@ -9290,7 +9404,7 @@ dependencies = [ "aes", "byteorder", "bzip2", - "constant_time_eq", + "constant_time_eq 0.1.5", "crc32fast", "crossbeam-utils", "flate2", @@ -9298,7 +9412,42 @@ dependencies = [ "pbkdf2 0.11.0", "sha1", "time", - "zstd", + "zstd 0.11.2+zstd.1.5.2", +] + +[[package]] +name = "zip_next" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658758d431446f97e25f129b30c97646db8799b30f00aaf10379b4fa343d4ded" +dependencies = [ + "aes", + "arbitrary", + "byteorder", + "bzip2", + "constant_time_eq 0.3.0", + "crc32fast", + "crossbeam-utils", + "deflate64", + "flate2", + "hmac", + "pbkdf2 0.12.2", + "sha1", + "time", + "zopfli", + "zstd 0.13.1", +] + +[[package]] +name = "zopfli" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1f48f3508a3a3f2faee01629564400bc12260f6214a056d06a3aaaa6ef0736" +dependencies = [ + "crc32fast", + "log", + "simd-adler32", + "typed-arena", ] [[package]] @@ -9307,7 +9456,16 @@ version = "0.11.2+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" dependencies = [ - "zstd-safe", + "zstd-safe 5.0.2+zstd.1.5.2", +] + +[[package]] +name = "zstd" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a" +dependencies = [ + "zstd-safe 7.1.0", ] [[package]] @@ -9320,6 +9478,15 @@ dependencies = [ "zstd-sys", ] +[[package]] +name = "zstd-safe" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" +dependencies = [ + "zstd-sys", +] + [[package]] name = "zstd-sys" version = "2.0.10+zstd.1.5.6" From 1122df5450f2b2577f4a054dc10a3d986dfadf3a Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 15 Apr 2024 15:32:38 +0400 Subject: [PATCH 163/430] chore: enable rustls for foundry-common (#7664) --- crates/common/Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 5e7eebf82..7dc6fa3ba 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -74,3 +74,7 @@ num-format.workspace = true foundry-macros.workspace = true pretty_assertions.workspace = true tokio = { version = "1", features = ["rt-multi-thread", "macros"] } + +[features] +default = ["rustls"] +rustls = ["reqwest_ethers/rustls-tls-native-roots"] From 43587e2ba2c95c489476d55e9c6a928be40333cc Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 15 Apr 2024 13:34:01 +0200 Subject: [PATCH 164/430] fix: set OP enveloped tx field (#7649) * fix: set OP enveloped tx field * use is enabled * fix * fmt --------- Co-authored-by: Arsenii Kulikov --- crates/anvil/src/eth/backend/executor.rs | 14 ++++++++------ crates/anvil/src/eth/backend/mem/mod.rs | 9 ++++++++- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index d3b54be15..2d5d55500 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -118,7 +118,7 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' let difficulty = self.block_env.difficulty; let beneficiary = self.block_env.coinbase; let timestamp = self.block_env.timestamp.to::(); - let base_fee = if (self.cfg_env.handler_cfg.spec_id as u8) >= (SpecId::LONDON as u8) { + let base_fee = if self.cfg_env.handler_cfg.spec_id.is_enabled_in(SpecId::LONDON) { Some(self.block_env.basefee.to::()) } else { None @@ -208,11 +208,13 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' } fn env_for(&self, tx: &PendingTransaction) -> EnvWithHandlerCfg { - EnvWithHandlerCfg::new_with_cfg_env( - self.cfg_env.clone(), - self.block_env.clone(), - tx.to_revm_tx_env(), - ) + let mut tx_env = tx.to_revm_tx_env(); + if self.cfg_env.handler_cfg.is_optimism { + tx_env.optimism.enveloped_tx = + Some(alloy_rlp::encode(&tx.transaction.transaction).into()); + } + + EnvWithHandlerCfg::new_with_cfg_env(self.cfg_env.clone(), self.block_env.clone(), tx_env) } } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index d1fa2007d..86d946f2d 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -81,7 +81,7 @@ use futures::channel::mpsc::{unbounded, UnboundedSender}; use parking_lot::{Mutex, RwLock}; use revm::{ db::WrapDatabaseRef, - primitives::{HashMap, ResultAndState}, + primitives::{HashMap, OptimismFields, ResultAndState}, }; use std::{ collections::BTreeMap, @@ -838,6 +838,12 @@ impl Backend { > { let mut env = self.next_env(); env.tx = tx.pending_transaction.to_revm_tx_env(); + + if env.handler_cfg.is_optimism { + env.tx.optimism.enveloped_tx = + Some(alloy_rlp::encode(&tx.pending_transaction.transaction.transaction).into()); + } + let db = self.db.read().await; let mut inspector = Inspector::default(); let mut evm = self.new_evm_with_inspector_ref(&*db, env, &mut inspector); @@ -1128,6 +1134,7 @@ impl Backend { chain_id: None, nonce, access_list: access_list.unwrap_or_default().flattened(), + optimism: OptimismFields { enveloped_tx: Some(Bytes::new()), ..Default::default() }, ..Default::default() }; From 94e940c5e2bdeb07e6083e899d2bac04948bdc5c Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 15 Apr 2024 20:22:34 +0400 Subject: [PATCH 165/430] fix: simplify `run_test` (#7670) fix: simplify run_test --- crates/forge/src/runner.rs | 129 ++++++++++++++++--------------------- 1 file changed, 55 insertions(+), 74 deletions(-) diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 508ee9234..26af21787 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -334,82 +334,63 @@ impl<'a> ContractRunner<'a> { // Run unit test let mut executor = self.executor.clone(); - let start = Instant::now(); - let debug_arena; - let (reverted, reason, gas, stipend, coverage, state_changeset, breakpoints) = - match executor.execute_test( - self.sender, - address, - func, - &[], - U256::ZERO, - Some(self.revert_decoder), - ) { - Ok(res) => { - let RawCallResult { - reverted, - gas_used: gas, - stipend, - logs: execution_logs, - traces: execution_trace, - coverage: execution_coverage, - labels: new_labels, - state_changeset, - debug, - cheatcodes, - .. - } = res.raw; - - let breakpoints = cheatcodes.map(|c| c.breakpoints).unwrap_or_default(); - traces.extend(execution_trace.map(|traces| (TraceKind::Execution, traces))); - labeled_addresses.extend(new_labels); - logs.extend(execution_logs); - debug_arena = debug; - coverage = merge_coverages(coverage, execution_coverage); - - (reverted, None, gas, stipend, coverage, state_changeset, breakpoints) - } - Err(EvmError::Execution(err)) => { - let ExecutionErr { raw, reason } = *err; - traces.extend(raw.traces.map(|traces| (TraceKind::Execution, traces))); - labeled_addresses.extend(raw.labels); - logs.extend(raw.logs); - debug_arena = raw.debug; - ( - raw.reverted, - Some(reason), - raw.gas_used, - raw.stipend, - None, - raw.state_changeset, - Default::default(), - ) - } - Err(EvmError::SkipError) => { - return TestResult { - status: TestStatus::Skipped, - reason: None, - decoded_logs: decode_console_logs(&logs), - traces, - labeled_addresses, - kind: TestKind::Standard(0), - duration: start.elapsed(), - ..Default::default() - } + let start: Instant = Instant::now(); + let (raw_call_result, reason) = match executor.execute_test( + self.sender, + address, + func, + &[], + U256::ZERO, + Some(self.revert_decoder), + ) { + Ok(res) => (res.raw, None), + Err(EvmError::Execution(err)) => (err.raw, Some(err.reason)), + Err(EvmError::SkipError) => { + return TestResult { + status: TestStatus::Skipped, + reason: None, + decoded_logs: decode_console_logs(&logs), + traces, + labeled_addresses, + kind: TestKind::Standard(0), + duration: start.elapsed(), + ..Default::default() } - Err(err) => { - return TestResult { - status: TestStatus::Failure, - reason: Some(err.to_string()), - decoded_logs: decode_console_logs(&logs), - traces, - labeled_addresses, - kind: TestKind::Standard(0), - duration: start.elapsed(), - ..Default::default() - } + } + Err(err) => { + return TestResult { + status: TestStatus::Failure, + reason: Some(err.to_string()), + decoded_logs: decode_console_logs(&logs), + traces, + labeled_addresses, + kind: TestKind::Standard(0), + duration: start.elapsed(), + ..Default::default() } - }; + } + }; + + let RawCallResult { + reverted, + gas_used: gas, + stipend, + logs: execution_logs, + traces: execution_trace, + coverage: execution_coverage, + labels: new_labels, + state_changeset, + debug, + cheatcodes, + .. + } = raw_call_result; + + let breakpoints = cheatcodes.map(|c| c.breakpoints).unwrap_or_default(); + let debug_arena = debug; + traces.extend(execution_trace.map(|traces| (TraceKind::Execution, traces))); + labeled_addresses.extend(new_labels); + logs.extend(execution_logs); + coverage = merge_coverages(coverage, execution_coverage); let success = executor.is_success( setup.address, From 46f51c9b8c48124d3e92ec590b654cb76e628dd5 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 15 Apr 2024 20:22:55 +0400 Subject: [PATCH 166/430] feat: allow including libraries into coverage report (#7663) --- crates/forge/bin/cmd/coverage.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index 29538164b..7e119fe47 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -64,6 +64,10 @@ pub struct CoverageArgs { )] report_file: Option, + /// Whether to include libraries in the coverage report. + #[arg(long)] + include_libs: bool, + #[command(flatten)] filter: FilterArgs, @@ -162,7 +166,8 @@ impl CoverageArgs { report.add_source(version.clone(), source_file.id as usize, path.clone()); // Filter out dependencies - if project_paths.has_library_ancestor(std::path::Path::new(&path)) { + if !self.include_libs && project_paths.has_library_ancestor(std::path::Path::new(&path)) + { continue } From 8e00b6670349e67334cb05c898625bdf0df60be2 Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Mon, 15 Apr 2024 19:42:08 +0200 Subject: [PATCH 167/430] Upgrade `foundry-block-explorers` to `0.2.6` (#7672) bump foundry-block-explorers version to include https://github.com/foundry-rs/block-explorers/pull/40 --- Cargo.lock | 4 ++-- Cargo.toml | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a7b888b03..6f2446602 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3618,9 +3618,9 @@ dependencies = [ [[package]] name = "foundry-block-explorers" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee75d972291181ae98bd1b48647ca8d8832159012b240ca1b7225085d4a63f00" +checksum = "b64cb03e297eb85b9f84a195fec7390a5e9805d9b82b11b2af57f65fc6d4ceb7" dependencies = [ "alloy-chains", "alloy-json-abi", diff --git a/Cargo.toml b/Cargo.toml index 82e29e6f2..6c19cff94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -137,7 +137,7 @@ foundry-wallets = { path = "crates/wallets" } foundry-linking = { path = "crates/linking" } # solc & compilation utilities -foundry-block-explorers = { version = "0.2.5", default-features = false } +foundry-block-explorers = { version = "0.2.6", default-features = false } foundry-compilers = { version = "0.3.14", default-features = false } ## revm @@ -146,7 +146,7 @@ revm = { version = "8", default-features = false } revm-primitives = { version = "3", default-features = false } revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "413b892", features = [ "serde", -] } +] } ## ethers ethers = { version = "2.0.14", default-features = false } @@ -192,7 +192,10 @@ solang-parser = "=0.3.3" ## misc arrayvec = "0.7" base64 = "0.22" -chrono = { version = "0.4", default-features = false, features = ["clock", "std"] } +chrono = { version = "0.4", default-features = false, features = [ + "clock", + "std", +] } color-eyre = "0.6" derive_more = "0.99" evm-disassembler = "0.5" From 9bce256dc6f7894c34eb5e2ae39729e20fc0dbae Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Mon, 15 Apr 2024 15:08:47 -0400 Subject: [PATCH 168/430] fix(verify): improve err handling for unset etherscan api key (#7673) * fix(verify): improve err handling for unset etherscan api key * use: if let Some * nits --- crates/verify/src/bytecode.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index f801ff771..d84acbccc 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -135,8 +135,12 @@ impl VerifyBytecodeArgs { self.etherscan_opts.chain = Some(chain); self.etherscan_opts.key = config.get_etherscan_config_with_chain(Some(chain))?.map(|c| c.key); - // Create etherscan client - let etherscan = Client::new(chain, self.etherscan_opts.key.clone().unwrap())?; + + // If etherscan key is not set, we can't proceed with etherscan verification + let Some(key) = self.etherscan_opts.key.clone() else { + eyre::bail!("Etherscan API key is required for verification"); + }; + let etherscan = Client::new(chain, key)?; // Get the constructor args using `source_code` endpoint let source_code = etherscan.contract_source_code(self.address).await?; @@ -170,7 +174,7 @@ impl VerifyBytecodeArgs { if provided_constructor_args != constructor_args.to_string() && !self.json { println!( "{}", - Paint::red("The provider constructor args do not match the constructor args from etherscan. This will result in a mismatch - Using the args from etherscan").bold(), + Paint::red("The provided constructor args do not match the constructor args from etherscan. This will result in a mismatch - Using the args from etherscan").bold(), ); } From ee47bb01ee8aa042639cc9ae86a2a3cf6ab9d037 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 15 Apr 2024 21:48:39 +0200 Subject: [PATCH 169/430] chore: add tracing to signature client (#7674) --- crates/common/src/selectors.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/crates/common/src/selectors.rs b/crates/common/src/selectors.rs index cda4ccb6c..2537b2c0c 100644 --- a/crates/common/src/selectors.rs +++ b/crates/common/src/selectors.rs @@ -56,6 +56,7 @@ impl SignEthClient { } async fn get_text(&self, url: &str) -> reqwest::Result { + trace!(%url, "GET"); self.inner .get(url) .send() @@ -73,11 +74,12 @@ impl SignEthClient { } /// Sends a new post request - async fn post_json( + async fn post_json( &self, url: &str, body: &T, ) -> reqwest::Result { + trace!(%url, body=?serde_json::to_string(body), "POST"); self.inner .post(url) .json(body) @@ -146,7 +148,7 @@ impl SignEthClient { .await? .pop() // Not returning on the previous line ensures a vector with exactly 1 element .unwrap() - .ok_or(eyre::eyre!("No signature found")) + .ok_or_else(|| eyre::eyre!("No signature found")) } /// Decodes the given function or event selectors using https://api.openchain.xyz @@ -166,6 +168,9 @@ impl SignEthClient { return Ok(vec![]); } + debug!(len = selectors.len(), "decoding selectors"); + trace!(?selectors, "decoding selectors"); + // exit early if spurious connection self.ensure_not_spurious()?; From 1535a699a425d558cd80a9c9c079c9a828494c07 Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Tue, 16 Apr 2024 12:00:58 +0200 Subject: [PATCH 170/430] fix(traces): Etherscan traces are only resolved for first instance of test run (#7675) * handle resolved contracts * inline resolved into initial for-loop --- crates/evm/traces/src/identifier/etherscan.rs | 43 +++++++++++++------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/crates/evm/traces/src/identifier/etherscan.rs b/crates/evm/traces/src/identifier/etherscan.rs index 50c273d07..11996be3a 100644 --- a/crates/evm/traces/src/identifier/etherscan.rs +++ b/crates/evm/traces/src/identifier/etherscan.rs @@ -104,6 +104,7 @@ impl TraceIdentifier for EtherscanIdentifier { return Vec::new() } + let mut identities = Vec::new(); let mut fetcher = EtherscanFetcher::new( self.client.clone(), Duration::from_secs(1), @@ -112,28 +113,42 @@ impl TraceIdentifier for EtherscanIdentifier { ); for (addr, _) in addresses { - if !self.contracts.contains_key(addr) { - fetcher.push(*addr); - } - } - - let fut = fetcher - .map(|(address, metadata)| { + if let Some(metadata) = self.contracts.get(addr) { let label = metadata.contract_name.clone(); let abi = metadata.abi().ok().map(Cow::Owned); - self.contracts.insert(address, metadata); - AddressIdentity { - address, + identities.push(AddressIdentity { + address: *addr, label: Some(label.clone()), contract: Some(label), abi, artifact_id: None, - } - }) - .collect(); + }); + } else { + fetcher.push(*addr); + } + } + + let fetched_identities = RuntimeOrHandle::new().block_on( + fetcher + .map(|(address, metadata)| { + let label = metadata.contract_name.clone(); + let abi = metadata.abi().ok().map(Cow::Owned); + self.contracts.insert(address, metadata); + + AddressIdentity { + address, + label: Some(label.clone()), + contract: Some(label), + abi, + artifact_id: None, + } + }) + .collect::>>(), + ); - RuntimeOrHandle::new().block_on(fut) + identities.extend(fetched_identities); + identities } } From f8a9d5e4151e6f9f22a277f7213a20d6f7a68472 Mon Sep 17 00:00:00 2001 From: Trevor Johnson <27569194+trevorgjohnson@users.noreply.github.com> Date: Tue, 16 Apr 2024 05:01:38 -0500 Subject: [PATCH 171/430] chore(fork): adjust `chain_id` when updating local env with fork (#7679) * chore: adjust "chain_id" when updating local env with fork * test: add test to check that 'chainId' is updated automatically after fork switch --- crates/evm/core/src/backend/mod.rs | 1 + testdata/default/cheats/Fork.t.sol | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index dbd5071ee..e924d19ef 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -1774,6 +1774,7 @@ impl Default for BackendInner { pub(crate) fn update_current_env_with_fork_env(current: &mut Env, fork: Env) { current.block = fork.block; current.cfg = fork.cfg; + current.tx.chain_id = fork.tx.chain_id; } /// Clones the data of the given `accounts` from the `active` database into the `fork_db` diff --git a/testdata/default/cheats/Fork.t.sol b/testdata/default/cheats/Fork.t.sol index 2ff4a6432..950865eac 100644 --- a/testdata/default/cheats/Fork.t.sol +++ b/testdata/default/cheats/Fork.t.sol @@ -111,4 +111,10 @@ contract ForkTest is DSTest { uint256 expected = block.chainid; assertEq(newChainId, expected); } + + // ensures forks change chain ids automatically + function testCanAutoUpdateChainId() public { + vm.createSelectFork("https://polygon-pokt.nodies.app"); // Polygon mainnet RPC URL + assertEq(block.chainid, 137); + } } From 958a850026bf3dddbf934e76d989c1a541503ffc Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Tue, 16 Apr 2024 19:49:08 +0400 Subject: [PATCH 172/430] feat: coverage for constructors (#7661) * wip * better naming --- crates/common/src/contracts.rs | 50 ++++++++++------ crates/evm/coverage/src/analysis.rs | 5 +- crates/evm/coverage/src/lib.rs | 15 ++++- crates/evm/evm/src/executors/invariant/mod.rs | 17 +++--- crates/evm/fuzz/src/strategies/state.rs | 12 ++-- crates/evm/traces/src/decoder/mod.rs | 4 +- crates/evm/traces/src/identifier/local.rs | 16 +++--- crates/evm/traces/src/lib.rs | 4 +- crates/forge/bin/cmd/coverage.rs | 47 +++++++++------ crates/forge/src/coverage.rs | 32 +++++++---- crates/forge/src/multi_runner.rs | 33 +++++++++-- crates/script/src/artifacts.rs | 8 --- crates/script/src/build.rs | 57 +++++++------------ crates/script/src/execute.rs | 12 +--- crates/script/src/lib.rs | 31 ++++------ crates/script/src/simulate.rs | 19 ++----- crates/script/src/transaction.rs | 18 +++--- crates/script/src/verify.rs | 9 +-- 18 files changed, 211 insertions(+), 178 deletions(-) delete mode 100644 crates/script/src/artifacts.rs diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index c406d0e45..d400e0a08 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -1,7 +1,7 @@ //! Commonly used contract types and functions. use alloy_json_abi::{Event, Function, JsonAbi}; -use alloy_primitives::{hex, Address, Selector, B256}; +use alloy_primitives::{Address, Bytes, Selector, B256}; use eyre::Result; use foundry_compilers::{ artifacts::{CompactContractBytecode, ContractBytecodeSome}, @@ -9,26 +9,40 @@ use foundry_compilers::{ }; use std::{ collections::BTreeMap, - fmt, ops::{Deref, DerefMut}, }; -type ArtifactWithContractRef<'a> = (&'a ArtifactId, &'a (JsonAbi, Vec)); +/// Container for commonly used contract data. +#[derive(Debug, Clone)] +pub struct ContractData { + /// Contract name. + pub name: String, + /// Contract ABI. + pub abi: JsonAbi, + /// Contract creation code. + pub bytecode: Bytes, + /// Contract runtime code. + pub deployed_bytecode: Bytes, +} -/// Wrapper type that maps an artifact to a contract ABI and bytecode. -#[derive(Clone, Default)] -pub struct ContractsByArtifact(pub BTreeMap)>); +type ArtifactWithContractRef<'a> = (&'a ArtifactId, &'a ContractData); -impl fmt::Debug for ContractsByArtifact { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_map().entries(self.iter().map(|(k, (v1, v2))| (k, (v1, hex::encode(v2))))).finish() - } -} +/// Wrapper type that maps an artifact to a contract ABI and bytecode. +#[derive(Clone, Default, Debug)] +pub struct ContractsByArtifact(pub BTreeMap); impl ContractsByArtifact { /// Finds a contract which has a similar bytecode as `code`. - pub fn find_by_code(&self, code: &[u8]) -> Option { - self.iter().find(|(_, (_, known_code))| bytecode_diff_score(known_code, code) <= 0.1) + pub fn find_by_creation_code(&self, code: &[u8]) -> Option { + self.iter() + .find(|(_, contract)| bytecode_diff_score(contract.bytecode.as_ref(), code) <= 0.1) + } + + /// Finds a contract which has a similar deployed bytecode as `code`. + pub fn find_by_deployed_code(&self, code: &[u8]) -> Option { + self.iter().find(|(_, contract)| { + bytecode_diff_score(contract.deployed_bytecode.as_ref(), code) <= 0.1 + }) } /// Finds a contract which has the same contract name or identifier as `id`. If more than one is @@ -51,14 +65,14 @@ impl ContractsByArtifact { let mut funcs = BTreeMap::new(); let mut events = BTreeMap::new(); let mut errors_abi = JsonAbi::new(); - for (_name, (abi, _code)) in self.iter() { - for func in abi.functions() { + for (_name, contract) in self.iter() { + for func in contract.abi.functions() { funcs.insert(func.selector(), func.clone()); } - for event in abi.events() { + for event in contract.abi.events() { events.insert(event.selector(), event.clone()); } - for error in abi.errors() { + for error in contract.abi.errors() { errors_abi.errors.entry(error.name.clone()).or_default().push(error.clone()); } } @@ -67,7 +81,7 @@ impl ContractsByArtifact { } impl Deref for ContractsByArtifact { - type Target = BTreeMap)>; + type Target = BTreeMap; fn deref(&self) -> &Self::Target { &self.0 diff --git a/crates/evm/coverage/src/analysis.rs b/crates/evm/coverage/src/analysis.rs index a136f2d79..fad5b6576 100644 --- a/crates/evm/coverage/src/analysis.rs +++ b/crates/evm/coverage/src/analysis.rs @@ -45,13 +45,10 @@ impl<'a> ContractVisitor<'a> { let name: String = node.attribute("name").ok_or_else(|| eyre::eyre!("Function has no name"))?; - // TODO(onbjerg): Re-enable constructor parsing when we walk both the deployment and runtime - // sourcemaps. Currently this fails because we are trying to look for anchors in the runtime - // sourcemap. // TODO(onbjerg): Figure out why we cannot find anchors for the receive function let kind: String = node.attribute("kind").ok_or_else(|| eyre::eyre!("Function has no kind"))?; - if kind == "constructor" || kind == "receive" { + if kind == "receive" { return Ok(()) } diff --git a/crates/evm/coverage/src/lib.rs b/crates/evm/coverage/src/lib.rs index 04ecaf948..7a8f51c2e 100644 --- a/crates/evm/coverage/src/lib.rs +++ b/crates/evm/coverage/src/lib.rs @@ -37,7 +37,7 @@ pub struct CoverageReport { /// All coverage items for the codebase, keyed by the compiler version. pub items: HashMap>, /// All item anchors for the codebase, keyed by their contract ID. - pub anchors: HashMap>, + pub anchors: HashMap, Vec)>, /// All the bytecode hits for the codebase pub bytecode_hits: HashMap, /// The bytecode -> source mappings @@ -70,7 +70,10 @@ impl CoverageReport { } /// Add anchors to this report - pub fn add_anchors(&mut self, anchors: HashMap>) { + pub fn add_anchors( + &mut self, + anchors: HashMap, Vec)>, + ) { self.anchors.extend(anchors); } @@ -124,7 +127,12 @@ impl CoverageReport { /// /// This function should only be called *after* all the relevant sources have been processed and /// added to the map (see [add_source]). - pub fn add_hit_map(&mut self, contract_id: &ContractId, hit_map: &HitMap) -> Result<()> { + pub fn add_hit_map( + &mut self, + contract_id: &ContractId, + hit_map: &HitMap, + is_deployed_code: bool, + ) -> Result<()> { // Add bytecode level hits let e = self .bytecode_hits @@ -139,6 +147,7 @@ impl CoverageReport { // Add source level hits if let Some(anchors) = self.anchors.get(contract_id) { + let anchors = if is_deployed_code { &anchors.1 } else { &anchors.0 }; for anchor in anchors { if let Some(hits) = hit_map.hits.get(&anchor.instruction) { self.items diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index eb4cecb38..2a7000ec1 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -435,8 +435,9 @@ impl<'a> InvariantExecutor<'a> { } // Exclude any artifact without mutable functions. - for (artifact, (abi, _)) in self.project_contracts.iter() { - if abi + for (artifact, contract) in self.project_contracts.iter() { + if contract + .abi .functions() .filter(|func| { !matches!( @@ -474,12 +475,14 @@ impl<'a> InvariantExecutor<'a> { contract: String, selectors: &[FixedBytes<4>], ) -> eyre::Result { - if let Some((artifact, (abi, _))) = + if let Some((artifact, contract_data)) = self.project_contracts.find_by_name_or_identifier(&contract)? { // Check that the selectors really exist for this contract. for selector in selectors { - abi.functions() + contract_data + .abi + .functions() .find(|func| func.selector().as_slice() == selector.as_slice()) .wrap_err(format!("{contract} does not have the selector {selector:?}"))?; } @@ -563,7 +566,7 @@ impl<'a> InvariantExecutor<'a> { // Identifiers are specified as an array, so we loop through them. for identifier in artifacts { // Try to find the contract by name or identifier in the project's contracts. - if let Some((_, (abi, _))) = + if let Some((_, contract)) = self.project_contracts.find_by_name_or_identifier(identifier)? { combined @@ -574,10 +577,10 @@ impl<'a> InvariantExecutor<'a> { let (_, contract_abi, _) = entry; // Extend the ABI's function list with the new functions. - contract_abi.functions.extend(abi.functions.clone()); + contract_abi.functions.extend(contract.abi.functions.clone()); }) // Otherwise insert it into the map. - .or_insert_with(|| (identifier.to_string(), abi.clone(), vec![])); + .or_insert_with(|| (identifier.to_string(), contract.abi.clone(), vec![])); } } } diff --git a/crates/evm/fuzz/src/strategies/state.rs b/crates/evm/fuzz/src/strategies/state.rs index f432154b9..2c5e98d9c 100644 --- a/crates/evm/fuzz/src/strategies/state.rs +++ b/crates/evm/fuzz/src/strategies/state.rs @@ -316,15 +316,17 @@ pub fn collect_created_contracts( if !setup_contracts.contains_key(address) { if let (true, Some(code)) = (&account.is_touched(), &account.info.code) { if !code.is_empty() { - if let Some((artifact, (abi, _))) = - project_contracts.find_by_code(&code.original_bytes()) + if let Some((artifact, contract)) = + project_contracts.find_by_deployed_code(&code.original_bytes()) { if let Some(functions) = - artifact_filters.get_targeted_functions(artifact, abi)? + artifact_filters.get_targeted_functions(artifact, &contract.abi)? { created_contracts.push(*address); - writable_targeted - .insert(*address, (artifact.name.clone(), abi.clone(), functions)); + writable_targeted.insert( + *address, + (artifact.name.clone(), contract.abi.clone(), functions), + ); } } } diff --git a/crates/evm/traces/src/decoder/mod.rs b/crates/evm/traces/src/decoder/mod.rs index f53d8b516..ad4e161cf 100644 --- a/crates/evm/traces/src/decoder/mod.rs +++ b/crates/evm/traces/src/decoder/mod.rs @@ -56,8 +56,8 @@ impl CallTraceDecoderBuilder { #[inline] pub fn with_known_contracts(mut self, contracts: &ContractsByArtifact) -> Self { trace!(target: "evm::traces", len=contracts.len(), "collecting known contract ABIs"); - for (abi, _) in contracts.values() { - self.decoder.collect_abi(abi, None); + for contract in contracts.values() { + self.decoder.collect_abi(&contract.abi, None); } self } diff --git a/crates/evm/traces/src/identifier/local.rs b/crates/evm/traces/src/identifier/local.rs index 193a8c526..175414a7c 100644 --- a/crates/evm/traces/src/identifier/local.rs +++ b/crates/evm/traces/src/identifier/local.rs @@ -9,7 +9,7 @@ use std::borrow::Cow; pub struct LocalTraceIdentifier<'a> { /// Known contracts to search through. known_contracts: &'a ContractsByArtifact, - /// Vector of pairs of artifact ID and the code length of the given artifact. + /// Vector of pairs of artifact ID and the runtime code length of the given artifact. ordered_ids: Vec<(&'a ArtifactId, usize)>, } @@ -17,8 +17,10 @@ impl<'a> LocalTraceIdentifier<'a> { /// Creates a new local trace identifier. #[inline] pub fn new(known_contracts: &'a ContractsByArtifact) -> Self { - let mut ordered_ids = - known_contracts.iter().map(|(id, contract)| (id, contract.1.len())).collect::>(); + let mut ordered_ids = known_contracts + .iter() + .map(|(id, contract)| (id, contract.deployed_bytecode.len())) + .collect::>(); ordered_ids.sort_by_key(|(_, len)| *len); Self { known_contracts, ordered_ids } } @@ -37,15 +39,15 @@ impl<'a> LocalTraceIdentifier<'a> { let mut min_score_id = None; let mut check = |id| { - let (abi, known_code) = self.known_contracts.get(id)?; - let score = bytecode_diff_score(known_code, code); + let contract = self.known_contracts.get(id)?; + let score = bytecode_diff_score(&contract.deployed_bytecode, code); if score == 0.0 { trace!(target: "evm::traces", "found exact match"); - return Some((id, abi)); + return Some((id, &contract.abi)); } if score < min_score { min_score = score; - min_score_id = Some((id, abi)); + min_score_id = Some((id, &contract.abi)); } None }; diff --git a/crates/evm/traces/src/lib.rs b/crates/evm/traces/src/lib.rs index 2538c03ca..81f13f95a 100644 --- a/crates/evm/traces/src/lib.rs +++ b/crates/evm/traces/src/lib.rs @@ -308,8 +308,8 @@ pub fn load_contracts( .contracts .iter() .filter_map(|(addr, name)| { - if let Ok(Some((_, (abi, _)))) = contracts.find_by_name_or_identifier(name) { - return Some((*addr, (name.clone(), abi.clone()))); + if let Ok(Some((_, contract))) = contracts.find_by_name_or_identifier(name) { + return Some((*addr, (name.clone(), contract.abi.clone()))); } None }) diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index 7e119fe47..bbd99de33 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -5,7 +5,7 @@ use eyre::{Context, Result}; use forge::{ coverage::{ analysis::SourceAnalyzer, anchors::find_anchors, BytecodeReporter, ContractId, - CoverageReport, CoverageReporter, DebugReporter, ItemAnchor, LcovReporter, SummaryReporter, + CoverageReport, CoverageReporter, DebugReporter, LcovReporter, SummaryReporter, }, inspectors::CheatsConfig, opts::EvmOpts, @@ -271,21 +271,26 @@ impl CoverageArgs { items_by_source_id.entry(item.loc.source_id).or_default().push(item_id); } - let anchors: HashMap> = source_maps + let anchors = source_maps .iter() .filter(|(contract_id, _)| contract_id.version == version) - .filter_map(|(contract_id, (_, deployed_source_map))| { + .filter_map(|(contract_id, (creation_source_map, deployed_source_map))| { + let creation_code_anchors = find_anchors( + &bytecodes.get(contract_id)?.0, + creation_source_map, + &ic_pc_maps.get(contract_id)?.0, + &source_analysis.items, + &items_by_source_id, + ); + let deployed_code_anchors = find_anchors( + &bytecodes.get(contract_id)?.1, + deployed_source_map, + &ic_pc_maps.get(contract_id)?.1, + &source_analysis.items, + &items_by_source_id, + ); // TODO: Creation source map/bytecode as well - Some(( - contract_id.clone(), - find_anchors( - &bytecodes.get(contract_id)?.1, - deployed_source_map, - &ic_pc_maps.get(contract_id)?.1, - &source_analysis.items, - &items_by_source_id, - ), - )) + Some((contract_id.clone(), (creation_code_anchors, deployed_code_anchors))) }) .collect(); report.add_items(version, source_analysis.items); @@ -345,17 +350,26 @@ impl CoverageArgs { .filter_map(|mut result| result.coverage.take()) .flat_map(|hit_maps| { hit_maps.0.into_values().filter_map(|map| { - Some((known_contracts.find_by_code(map.bytecode.as_ref())?.0, map)) + if let Some((id, _)) = + known_contracts.find_by_deployed_code(map.bytecode.as_ref()) + { + Some((id, map, true)) + } else if let Some((id, _)) = + known_contracts.find_by_creation_code(map.bytecode.as_ref()) + { + Some((id, map, false)) + } else { + None + } }) }); - for (artifact_id, hits) in data { + for (artifact_id, hits, is_deployed_code) in data { // TODO: Note down failing tests if let Some(source_id) = report.get_source_id( artifact_id.version.clone(), artifact_id.source.to_string_lossy().to_string(), ) { let source_id = *source_id; - // TODO: Distinguish between creation/runtime in a smart way report.add_hit_map( &ContractId { version: artifact_id.version.clone(), @@ -363,6 +377,7 @@ impl CoverageArgs { contract_name: artifact_id.name.clone(), }, &hits, + is_deployed_code, )?; } } diff --git a/crates/forge/src/coverage.rs b/crates/forge/src/coverage.rs index 1c79c0e33..19724bc2f 100644 --- a/crates/forge/src/coverage.rs +++ b/crates/forge/src/coverage.rs @@ -161,17 +161,27 @@ impl CoverageReporter for DebugReporter { for (contract_id, anchors) in &report.anchors { println!("Anchors for {contract_id}:"); - anchors.iter().for_each(|anchor| { - println!("- {anchor}"); - println!( - " - Refers to item: {}", - report - .items - .get(&contract_id.version) - .and_then(|items| items.get(anchor.item_id)) - .map_or("None".to_owned(), |item| item.to_string()) - ); - }); + anchors + .0 + .iter() + .map(|anchor| (false, anchor)) + .chain(anchors.1.iter().map(|anchor| (true, anchor))) + .for_each(|(is_deployed, anchor)| { + println!("- {anchor}"); + if is_deployed { + println!("- Creation code"); + } else { + println!("- Runtime code"); + } + println!( + " - Refers to item: {}", + report + .items + .get(&contract_id.version) + .and_then(|items| items.get(anchor.item_id)) + .map_or("None".to_owned(), |item| item.to_string()) + ); + }); println!(); } diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index f19e67d22..8bfb1477b 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -4,7 +4,7 @@ use crate::{result::SuiteResult, ContractRunner, TestFilter, TestOptions}; 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, ContractData, ContractsByArtifact, TestFunctionExt}; use foundry_compilers::{ artifacts::Libraries, contracts::ArtifactContracts, Artifact, ArtifactId, ProjectCompileOutput, }; @@ -328,6 +328,8 @@ impl MultiContractRunnerBuilder { continue; }; + let name = id.name.clone(); + let LinkOutput { libs_to_deploy, libraries } = linker.link_with_nonce_or_address(Default::default(), evm_opts.sender, 1, id)?; @@ -342,7 +344,15 @@ impl MultiContractRunnerBuilder { .map(|b| b.into_owned()) .filter(|b| !b.is_empty()) else { - known_contracts.insert(id.clone(), (abi.clone(), vec![])); + known_contracts.insert( + id.clone(), + ContractData { + abi: abi.clone(), + bytecode: Bytes::new(), + deployed_bytecode: Bytes::new(), + name, + }, + ); continue; }; @@ -352,17 +362,30 @@ impl MultiContractRunnerBuilder { { deployable_contracts.insert( id.clone(), - TestContract { abi: abi.clone(), bytecode, libs_to_deploy, libraries }, + TestContract { + abi: abi.clone(), + bytecode: bytecode.clone(), + libs_to_deploy, + libraries, + }, ); } if let Some(bytes) = linked_contract.get_deployed_bytecode_bytes() { - known_contracts.insert(id.clone(), (abi.clone(), bytes.into_owned().into())); + known_contracts.insert( + id.clone(), + ContractData { + abi: abi.clone(), + bytecode, + deployed_bytecode: bytes.into_owned(), + name, + }, + ); } } let revert_decoder = - RevertDecoder::new().with_abis(known_contracts.values().map(|(abi, _)| abi)); + RevertDecoder::new().with_abis(known_contracts.values().map(|c| &c.abi)); Ok(MultiContractRunner { contracts: deployable_contracts, known_contracts, diff --git a/crates/script/src/artifacts.rs b/crates/script/src/artifacts.rs deleted file mode 100644 index ebd149f49..000000000 --- a/crates/script/src/artifacts.rs +++ /dev/null @@ -1,8 +0,0 @@ -use alloy_json_abi::JsonAbi; - -/// Bundles info of an artifact -pub struct ArtifactInfo<'a> { - pub contract_name: String, - pub abi: &'a JsonAbi, - pub code: &'a Vec, -} diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index 51fde33ad..4fb88719b 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -14,12 +14,11 @@ use foundry_cli::utils::get_cached_entry_by_name; use foundry_common::{ compile::{self, ContractSources, ProjectCompiler}, provider::alloy::try_get_http_provider, - ContractsByArtifact, + ContractData, ContractsByArtifact, }; use foundry_compilers::{ - artifacts::{BytecodeObject, ContractBytecode, ContractBytecodeSome, Libraries}, + artifacts::{BytecodeObject, Libraries}, cache::SolFilesCache, - contracts::ArtifactContracts, info::ContractInfo, ArtifactId, ProjectCompileOutput, }; @@ -85,7 +84,7 @@ pub struct LinkedBuildData { /// Original build data, might be used to relink this object with different libraries. pub build_data: BuildData, /// Known fully linked contracts. - pub highlevel_known_contracts: ArtifactContracts, + pub known_contracts: ContractsByArtifact, /// Libraries used to link the contracts. pub libraries: Libraries, /// Libraries that need to be deployed by sender before script execution. @@ -102,47 +101,35 @@ impl LinkedBuildData { &link_output.libraries, )?; - let highlevel_known_contracts = build_data - .get_linker() - .get_linked_artifacts(&link_output.libraries)? - .iter() - .filter_map(|(id, contract)| { - ContractBytecodeSome::try_from(ContractBytecode::from(contract.clone())) - .ok() - .map(|tc| (id.clone(), tc)) - }) - .filter(|(_, tc)| tc.bytecode.object.is_non_empty_bytecode()) - .collect(); + let known_contracts = ContractsByArtifact( + build_data + .get_linker() + .get_linked_artifacts(&link_output.libraries)? + .into_iter() + .filter_map(|(id, contract)| { + let name = id.name.clone(); + let bytecode = contract.bytecode.and_then(|b| b.into_bytes())?; + let deployed_bytecode = + contract.deployed_bytecode.and_then(|b| b.into_bytes())?; + let abi = contract.abi?; + + Some((id, ContractData { name, abi, bytecode, deployed_bytecode })) + }) + .collect(), + ); Ok(Self { build_data, - highlevel_known_contracts, + known_contracts, libraries: link_output.libraries, predeploy_libraries: link_output.libs_to_deploy, sources, }) } - /// Flattens the contracts into (`id` -> (`JsonAbi`, `Vec`)) pairs - pub fn get_flattened_contracts(&self, deployed_code: bool) -> ContractsByArtifact { - ContractsByArtifact( - self.highlevel_known_contracts - .iter() - .filter_map(|(id, c)| { - let bytecode = if deployed_code { - c.deployed_bytecode.bytes() - } else { - c.bytecode.bytes() - }; - bytecode.cloned().map(|code| (id.clone(), (c.abi.clone(), code.into()))) - }) - .collect(), - ) - } - /// Fetches target bytecode from linked contracts. - pub fn get_target_contract(&self) -> Result { - self.highlevel_known_contracts + pub fn get_target_contract(&self) -> Result { + self.known_contracts .get(&self.build_data.target) .cloned() .ok_or_eyre("target not found in linked artifacts") diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index 03ac8bc9e..9e7695591 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -17,9 +17,8 @@ use foundry_cli::utils::{ensure_clean_constructor, needs_setup}; use foundry_common::{ fmt::{format_token, format_token_raw}, provider::alloy::{get_http_provider, RpcUrl}, - shell, ContractsByArtifact, + shell, ContractData, ContractsByArtifact, }; -use foundry_compilers::artifacts::ContractBytecodeSome; use foundry_config::{Config, NamedChain}; use foundry_debugger::Debugger; use foundry_evm::{ @@ -62,11 +61,7 @@ impl LinkedState { pub async fn prepare_execution(self) -> Result { let Self { args, script_config, script_wallets, build_data } = self; - let ContractBytecodeSome { abi, bytecode, .. } = build_data.get_target_contract()?; - - let bytecode = bytecode.into_bytes().ok_or_else(|| { - eyre::eyre!("expected fully linked bytecode, found unlinked bytecode") - })?; + let ContractData { abi, bytecode, .. } = build_data.get_target_contract()?; let (func, calldata) = args.get_method_and_calldata(&abi)?; @@ -301,8 +296,7 @@ impl ExecutedState { pub async fn prepare_simulation(self) -> Result { let returns = self.get_returns()?; - let known_contracts = self.build_data.get_flattened_contracts(true); - let decoder = self.build_trace_decoder(&known_contracts)?; + let decoder = self.build_trace_decoder(&self.build_data.known_contracts)?; let txs = self.execution_result.transactions.clone().unwrap_or_default(); let rpc_data = RpcData::from_transactions(&txs); diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 8973dc126..17f70387a 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -18,12 +18,11 @@ use foundry_cli::{opts::CoreBuildArgs, utils::LoadConfig}; use foundry_common::{ abi::{encode_function_args, get_func}, compile::SkipBuildFilter, - errors::UnlinkedByteCode, evm::{Breakpoints, EvmArgs}, provider::alloy::RpcUrl, - shell, CONTRACT_MAX_SIZE, SELECTOR_LEN, + shell, ContractsByArtifact, CONTRACT_MAX_SIZE, SELECTOR_LEN, }; -use foundry_compilers::{artifacts::ContractBytecodeSome, ArtifactId}; +use foundry_compilers::ArtifactId; use foundry_config::{ figment, figment::{ @@ -46,10 +45,9 @@ use foundry_evm::{ }; use foundry_wallets::MultiWalletOpts; use serde::{Deserialize, Serialize}; -use std::collections::{BTreeMap, HashMap}; +use std::collections::HashMap; use yansi::Paint; -mod artifacts; mod broadcast; mod build; mod execute; @@ -269,7 +267,7 @@ impl ScriptArgs { pre_simulation.args.check_contract_sizes( &pre_simulation.execution_result, - &pre_simulation.build_data.highlevel_known_contracts, + &pre_simulation.build_data.known_contracts, )?; pre_simulation.fill_metadata().await?.bundle().await? @@ -357,25 +355,18 @@ impl ScriptArgs { fn check_contract_sizes( &self, result: &ScriptResult, - known_contracts: &BTreeMap, + known_contracts: &ContractsByArtifact, ) -> Result<()> { // (name, &init, &deployed)[] let mut bytecodes: Vec<(String, &[u8], &[u8])> = vec![]; // From artifacts - for (artifact, bytecode) in known_contracts.iter() { - if bytecode.bytecode.object.is_unlinked() { - return Err(UnlinkedByteCode::Bytecode(artifact.identifier()).into()); - } - let init_code = bytecode.bytecode.object.as_bytes().unwrap(); - // Ignore abstract contracts - if let Some(ref deployed_code) = bytecode.deployed_bytecode.bytecode { - if deployed_code.object.is_unlinked() { - return Err(UnlinkedByteCode::DeployedBytecode(artifact.identifier()).into()); - } - let deployed_code = deployed_code.object.as_bytes().unwrap(); - bytecodes.push((artifact.name.clone(), init_code, deployed_code)); - } + for (artifact, contract) in known_contracts.iter() { + bytecodes.push(( + artifact.name.clone(), + &contract.bytecode, + &contract.deployed_bytecode, + )); } // From traces diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index c740e59a4..87f9b3515 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -1,5 +1,4 @@ use super::{ - artifacts::ArtifactInfo, multi_sequence::MultiChainSequence, providers::ProvidersManager, runner::ScriptRunner, @@ -18,7 +17,7 @@ use alloy_primitives::{utils::format_units, Address, U256}; use eyre::{Context, Result}; use foundry_cheatcodes::{BroadcastableTransactions, ScriptWallets}; use foundry_cli::utils::{has_different_gas_calc, now}; -use foundry_common::{get_contract_name, provider::alloy::RpcUrl, shell, ContractsByArtifact}; +use foundry_common::{get_contract_name, provider::alloy::RpcUrl, shell, ContractData}; use foundry_evm::traces::render_trace_arena; use futures::future::{join_all, try_join_all}; use parking_lot::RwLock; @@ -88,9 +87,7 @@ impl PreSimulationState { .collect::>(), ); - let contracts = self.build_data.get_flattened_contracts(false); - let address_to_abi: BTreeMap = - self.build_address_to_abi_map(&contracts); + let address_to_abi = self.build_address_to_abi_map(); let mut final_txs = VecDeque::new(); @@ -183,21 +180,17 @@ impl PreSimulationState { } /// Build mapping from contract address to its ABI, code and contract name. - fn build_address_to_abi_map<'a>( - &self, - contracts: &'a ContractsByArtifact, - ) -> BTreeMap> { + fn build_address_to_abi_map(&self) -> BTreeMap { self.execution_artifacts .decoder .contracts .iter() .filter_map(move |(addr, contract_id)| { let contract_name = get_contract_name(contract_id); - if let Ok(Some((_, (abi, code)))) = - contracts.find_by_name_or_identifier(contract_name) + if let Ok(Some((_, data))) = + self.build_data.known_contracts.find_by_name_or_identifier(contract_name) { - let info = ArtifactInfo { contract_name: contract_name.to_string(), abi, code }; - return Some((*addr, info)); + return Some((*addr, data)); } None }) diff --git a/crates/script/src/transaction.rs b/crates/script/src/transaction.rs index e74699e74..cf3921290 100644 --- a/crates/script/src/transaction.rs +++ b/crates/script/src/transaction.rs @@ -1,9 +1,9 @@ -use super::{artifacts::ArtifactInfo, ScriptResult}; +use super::ScriptResult; use alloy_dyn_abi::JsonAbiExt; use alloy_primitives::{Address, Bytes, B256}; use alloy_rpc_types::{request::TransactionRequest, WithOtherFields}; use eyre::{ContextCompat, Result, WrapErr}; -use foundry_common::{fmt::format_token_raw, provider::alloy::RpcUrl, SELECTOR_LEN}; +use foundry_common::{fmt::format_token_raw, provider::alloy::RpcUrl, ContractData, SELECTOR_LEN}; use foundry_evm::{constants::DEFAULT_CREATE2_DEPLOYER, traces::CallTraceDecoder}; use itertools::Itertools; use revm_inspectors::tracing::types::CallKind; @@ -61,7 +61,7 @@ impl TransactionWithMetadata { transaction: TransactionRequest, rpc: RpcUrl, result: &ScriptResult, - local_contracts: &BTreeMap, + local_contracts: &BTreeMap, decoder: &CallTraceDecoder, additional_contracts: Vec, is_fixed_gas_limit: bool, @@ -117,7 +117,7 @@ impl TransactionWithMetadata { &mut self, is_create2: bool, address: Address, - contracts: &BTreeMap, + contracts: &BTreeMap, ) -> Result<()> { if is_create2 { self.opcode = CallKind::Create2; @@ -126,7 +126,7 @@ impl TransactionWithMetadata { } let info = contracts.get(&address); - self.contract_name = info.map(|info| info.contract_name.clone()); + self.contract_name = info.map(|info| info.name.clone()); self.contract_address = Some(address); let Some(data) = self.transaction.input.input() else { return Ok(()) }; @@ -143,11 +143,11 @@ impl TransactionWithMetadata { }; // The constructor args start after bytecode. - let contains_constructor_args = creation_code.len() > info.code.len(); + let contains_constructor_args = creation_code.len() > info.bytecode.len(); if !contains_constructor_args { return Ok(()); } - let constructor_args = &creation_code[info.code.len()..]; + let constructor_args = &creation_code[info.bytecode.len()..]; let Some(constructor) = info.abi.constructor() else { return Ok(()) }; let values = constructor.abi_decode_input(constructor_args, false).map_err(|e| { @@ -170,7 +170,7 @@ impl TransactionWithMetadata { fn set_call( &mut self, target: Address, - local_contracts: &BTreeMap, + local_contracts: &BTreeMap, decoder: &CallTraceDecoder, ) -> Result<()> { self.opcode = CallKind::Call; @@ -184,7 +184,7 @@ impl TransactionWithMetadata { let function = if let Some(info) = local_contracts.get(&target) { // This CALL is made to a local contract. - self.contract_name = Some(info.contract_name.clone()); + self.contract_name = Some(info.name.clone()); info.abi.functions().find(|function| function.selector() == selector) } else { // This CALL is made to an external contract; try to decode it from the given decoder. diff --git a/crates/script/src/verify.rs b/crates/script/src/verify.rs index 217d880b0..c9e102004 100644 --- a/crates/script/src/verify.rs +++ b/crates/script/src/verify.rs @@ -26,7 +26,7 @@ impl BroadcastedState { let verify = VerifyBundle::new( &script_config.config.project()?, &script_config.config, - build_data.get_flattened_contracts(false), + build_data.known_contracts, args.retry, args.verifier, ); @@ -105,11 +105,12 @@ impl VerifyBundle { data: &[u8], libraries: &[String], ) -> Option { - for (artifact, (_contract, bytecode)) in self.known_contracts.iter() { + for (artifact, contract) in self.known_contracts.iter() { // If it's a CREATE2, the tx.data comes with a 32-byte salt in the beginning // of the transaction - if data.split_at(create2_offset).1.starts_with(bytecode) { - let constructor_args = data.split_at(create2_offset + bytecode.len()).1.to_vec(); + if data.split_at(create2_offset).1.starts_with(&contract.bytecode) { + let constructor_args = + data.split_at(create2_offset + contract.bytecode.len()).1.to_vec(); let contract = ContractInfo { path: Some( From b56176ebf26e17603abe67be38219fd99455f1fb Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Tue, 16 Apr 2024 18:37:06 +0200 Subject: [PATCH 173/430] feat(forge-cli): Add `--no-metadata` as CLI compiler option (#7684) * add `no_metadata`, equivalent to adding `bytecode_hash = "none" and cbor_metadata = false` * add basic smoke test for --no-metadata setting cbor_metadata to false, bytecode_hash to none * Update core.rs Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- crates/cli/src/opts/build/core.rs | 13 +++++++++++++ crates/forge/tests/cli/config.rs | 6 ++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/crates/cli/src/opts/build/core.rs b/crates/cli/src/opts/build/core.rs index 9ed8f98b3..99caaea35 100644 --- a/crates/cli/src/opts/build/core.rs +++ b/crates/cli/src/opts/build/core.rs @@ -70,6 +70,13 @@ pub struct CoreBuildArgs { #[serde(skip)] pub via_ir: bool, + /// Do not append any metadata to the bytecode. + /// + /// This is equivalent to setting `bytecode_hash` to `none` and `cbor_metadata` to `false`. + #[arg(long, help_heading = "Compiler options")] + #[serde(skip)] + pub no_metadata: bool, + /// The path to the contract artifacts folder. #[arg( long = "out", @@ -204,9 +211,15 @@ impl Provider for CoreBuildArgs { dict.insert("via_ir".to_string(), true.into()); } + if self.no_metadata { + dict.insert("bytecode_hash".to_string(), "none".into()); + dict.insert("cbor_metadata".to_string(), false.into()); + } + if self.force { dict.insert("force".to_string(), self.force.into()); } + // we need to ensure no_cache set accordingly if self.no_cache { dict.insert("cache".to_string(), false.into()); diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 22d885af4..8c69fcc4f 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -2,7 +2,7 @@ use alloy_primitives::{Address, B256, U256}; use foundry_cli::utils as forge_utils; -use foundry_compilers::artifacts::{OptimizerDetails, RevertStrings, YulDetails}; +use foundry_compilers::artifacts::{BytecodeHash, OptimizerDetails, RevertStrings, YulDetails}; use foundry_config::{ cache::{CachedChains, CachedEndpoints, StorageCachingConfig}, fs_permissions::{FsAccessPermission, PathPermission}, @@ -305,8 +305,10 @@ forgetest_init!(can_get_evm_opts, |prj, _cmd| { // checks that we can set various config values forgetest_init!(can_set_config_values, |prj, _cmd| { - let config = prj.config_from_output(["--via-ir"]); + let config = prj.config_from_output(["--via-ir", "--no-metadata"]); assert!(config.via_ir); + assert_eq!(config.cbor_metadata, false); + assert_eq!(config.bytecode_hash, BytecodeHash::None); }); // tests that solc can be explicitly set From 9207b93b4338e587d522f81007f6989717c99708 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 16 Apr 2024 20:01:00 +0200 Subject: [PATCH 174/430] feat(debugger): update ratatui, use `List` (#7676) * feat(debugger): update ratatui, use `List` * refactor(debugger): event handlers * fmt --- Cargo.lock | 80 +++++---- crates/debugger/Cargo.toml | 2 +- crates/debugger/src/tui/context.rs | 274 ++++++++++++++--------------- crates/debugger/src/tui/draw.rs | 133 +++++--------- crates/debugger/src/tui/mod.rs | 5 +- 5 files changed, 227 insertions(+), 267 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6f2446602..e550e31f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,7 +73,7 @@ checksum = "40646aa7f01e396139cf0d6c3a7475eeb8094a0f41d8199f10860c8aef09d2f1" dependencies = [ "num_enum", "serde", - "strum 0.26.2", + "strum", ] [[package]] @@ -1815,6 +1815,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" +[[package]] +name = "castaway" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc" +dependencies = [ + "rustversion", +] + [[package]] name = "cc" version = "1.0.94" @@ -1866,7 +1875,7 @@ dependencies = [ "serde_json", "serial_test", "solang-parser", - "strum 0.26.2", + "strum", "tikv-jemallocator", "time", "tokio", @@ -2135,8 +2144,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b34115915337defe99b2aff5c2ce6771e5fbc4079f4b506301f5cf394c8452f7" dependencies = [ "crossterm", - "strum 0.26.2", - "strum_macros 0.26.2", + "strum", + "strum_macros", "unicode-width", ] @@ -2152,6 +2161,19 @@ dependencies = [ "winapi", ] +[[package]] +name = "compact_str" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "ryu", + "static_assertions", +] + [[package]] name = "concurrent-queue" version = "2.4.0" @@ -3042,7 +3064,7 @@ dependencies = [ "rlp", "serde", "serde_json", - "strum 0.26.2", + "strum", "syn 2.0.58", "tempfile", "thiserror", @@ -3476,7 +3498,7 @@ dependencies = [ "serde_json", "similar", "solang-parser", - "strum 0.26.2", + "strum", "svm-rs 0.4.1", "tempfile", "thiserror", @@ -3711,7 +3733,7 @@ dependencies = [ "regex", "serde", "strsim 0.10.0", - "strum 0.26.2", + "strum", "tempfile", "tokio", "tracing", @@ -6702,18 +6724,20 @@ dependencies = [ [[package]] name = "ratatui" -version = "0.24.0" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ebc917cfb527a566c37ecb94c7e3fd098353516fb4eb6bea17015ade0182425" +checksum = "a564a852040e82671dc50a37d88f3aa83bbc690dfc6844cfe7a2591620206a80" dependencies = [ "bitflags 2.5.0", "cassowary", + "compact_str", "crossterm", "indoc", - "itertools 0.11.0", + "itertools 0.12.1", "lru", "paste", - "strum 0.25.0", + "stability", + "strum", "unicode-segmentation", "unicode-width", ] @@ -7830,6 +7854,16 @@ dependencies = [ "der", ] +[[package]] +name = "stability" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ff9eaf853dec4c8802325d8b6d3dffa86cc707fd7a1a4cdbf416e13b061787a" +dependencies = [ + "quote", + "syn 2.0.58", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -7880,35 +7914,13 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "strum" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" -dependencies = [ - "strum_macros 0.25.3", -] - [[package]] name = "strum" version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" dependencies = [ - "strum_macros 0.26.2", -] - -[[package]] -name = "strum_macros" -version = "0.25.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.58", + "strum_macros", ] [[package]] diff --git a/crates/debugger/Cargo.toml b/crates/debugger/Cargo.toml index 3c1fe8480..bc3c746e3 100644 --- a/crates/debugger/Cargo.toml +++ b/crates/debugger/Cargo.toml @@ -20,6 +20,6 @@ alloy-primitives.workspace = true crossterm = "0.27" eyre.workspace = true -ratatui = { version = "0.24.0", default-features = false, features = ["crossterm"] } +ratatui = { version = "0.26", default-features = false, features = ["crossterm"] } revm.workspace = true tracing.workspace = true diff --git a/crates/debugger/src/tui/context.rs b/crates/debugger/src/tui/context.rs index b0da8a0c5..58a8e57f4 100644 --- a/crates/debugger/src/tui/context.rs +++ b/crates/debugger/src/tui/context.rs @@ -5,13 +5,11 @@ use alloy_primitives::Address; use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers, MouseEvent, MouseEventKind}; use foundry_evm_core::debug::{DebugNodeFlat, DebugStep}; use revm_inspectors::tracing::types::CallKind; -use std::{cell::RefCell, ops::ControlFlow}; +use std::ops::ControlFlow; /// This is currently used to remember last scroll position so screen doesn't wiggle as much. #[derive(Default)] pub(crate) struct DrawMemory { - // TODO - pub(crate) current_startline: RefCell, pub(crate) inner_call_index: usize, pub(crate) current_buf_startline: usize, pub(crate) current_stack_startline: usize, @@ -120,6 +118,13 @@ impl<'a> DebuggerContext<'a> { self.opcode_list.extend(debug_steps.iter().map(DebugStep::pretty_opcode)); } + fn gen_opcode_list_if_necessary(&mut self) { + if self.last_index != self.draw_memory.inner_call_index { + self.gen_opcode_list(); + self.last_index = self.draw_memory.inner_call_index; + } + } + fn active_buffer(&self) -> &[u8] { match self.active_buffer { BufferKind::Memory => &self.current_step().memory, @@ -131,19 +136,18 @@ impl<'a> DebuggerContext<'a> { impl DebuggerContext<'_> { pub(crate) fn handle_event(&mut self, event: Event) -> ControlFlow { - if self.last_index != self.draw_memory.inner_call_index { - self.gen_opcode_list(); - self.last_index = self.draw_memory.inner_call_index; - } - - match event { + let ret = match event { Event::Key(event) => self.handle_key_event(event), Event::Mouse(event) => self.handle_mouse_event(event), _ => ControlFlow::Continue(()), - } + }; + // Generate the list after the event has been handled. + self.gen_opcode_list_if_necessary(); + ret } fn handle_key_event(&mut self, event: KeyEvent) -> ControlFlow { + // Breakpoints if let KeyCode::Char(c) = event.code { if c.is_alphabetic() && self.key_buffer.starts_with('\'') { self.handle_breakpoint(c); @@ -151,154 +155,130 @@ impl DebuggerContext<'_> { } } + let control = event.modifiers.contains(KeyModifiers::CONTROL); + match event.code { // Exit KeyCode::Char('q') => return ControlFlow::Break(ExitReason::CharExit), - // Move down - KeyCode::Char('j') | KeyCode::Down => { - // Grab number of times to do it - for _ in 0..buffer_as_number(&self.key_buffer, 1) { - if event.modifiers.contains(KeyModifiers::CONTROL) { - let max_buf = (self.active_buffer().len() / 32).saturating_sub(1); - if self.draw_memory.current_buf_startline < max_buf { - self.draw_memory.current_buf_startline += 1; - } - } else if self.current_step < self.opcode_list.len() - 1 { - self.current_step += 1; - } else if self.draw_memory.inner_call_index < self.debug_arena().len() - 1 { - self.draw_memory.inner_call_index += 1; - self.current_step = 0; - } - } - self.key_buffer.clear(); - } - KeyCode::Char('J') => { - for _ in 0..buffer_as_number(&self.key_buffer, 1) { - let max_stack = self.current_step().stack.len().saturating_sub(1); - if self.draw_memory.current_stack_startline < max_stack { - self.draw_memory.current_stack_startline += 1; - } + + // Scroll up the memory buffer + KeyCode::Char('k') | KeyCode::Up if control => self.repeat(|this| { + this.draw_memory.current_buf_startline = + this.draw_memory.current_buf_startline.saturating_sub(1); + }), + // Scroll down the memory buffer + KeyCode::Char('j') | KeyCode::Down if control => self.repeat(|this| { + let max_buf = (this.active_buffer().len() / 32).saturating_sub(1); + if this.draw_memory.current_buf_startline < max_buf { + this.draw_memory.current_buf_startline += 1; } - self.key_buffer.clear(); - } + }), + // Move up - KeyCode::Char('k') | KeyCode::Up => { - for _ in 0..buffer_as_number(&self.key_buffer, 1) { - if event.modifiers.contains(KeyModifiers::CONTROL) { - self.draw_memory.current_buf_startline = - self.draw_memory.current_buf_startline.saturating_sub(1); - } else if self.current_step > 0 { - self.current_step -= 1; - } else if self.draw_memory.inner_call_index > 0 { - self.draw_memory.inner_call_index -= 1; - self.current_step = self.debug_steps().len() - 1; - } - } - self.key_buffer.clear(); - } - KeyCode::Char('K') => { - for _ in 0..buffer_as_number(&self.key_buffer, 1) { - self.draw_memory.current_stack_startline = - self.draw_memory.current_stack_startline.saturating_sub(1); + KeyCode::Char('k') | KeyCode::Up => self.repeat(Self::step_back), + // Move down + KeyCode::Char('j') | KeyCode::Down => self.repeat(Self::step), + + // Scroll up the stack + KeyCode::Char('K') => self.repeat(|this| { + this.draw_memory.current_stack_startline = + this.draw_memory.current_stack_startline.saturating_sub(1); + }), + // Scroll down the stack + KeyCode::Char('J') => self.repeat(|this| { + let max_stack = this.current_step().stack.len().saturating_sub(1); + if this.draw_memory.current_stack_startline < max_stack { + this.draw_memory.current_stack_startline += 1; } - self.key_buffer.clear(); - } + }), + + // Cycle buffers KeyCode::Char('b') => { self.active_buffer = self.active_buffer.next(); self.draw_memory.current_buf_startline = 0; } + // Go to top of file KeyCode::Char('g') => { self.draw_memory.inner_call_index = 0; self.current_step = 0; - self.key_buffer.clear(); } + // Go to bottom of file KeyCode::Char('G') => { self.draw_memory.inner_call_index = self.debug_arena().len() - 1; - self.current_step = self.debug_steps().len() - 1; - self.key_buffer.clear(); + self.current_step = self.n_steps() - 1; } + // Go to previous call KeyCode::Char('c') => { self.draw_memory.inner_call_index = self.draw_memory.inner_call_index.saturating_sub(1); - self.current_step = self.debug_steps().len() - 1; - self.key_buffer.clear(); + self.current_step = self.n_steps() - 1; } + // Go to next call KeyCode::Char('C') => { if self.debug_arena().len() > self.draw_memory.inner_call_index + 1 { self.draw_memory.inner_call_index += 1; self.current_step = 0; } - self.key_buffer.clear(); } + // Step forward - KeyCode::Char('s') => { - for _ in 0..buffer_as_number(&self.key_buffer, 1) { - let remaining_ops = self.opcode_list[self.current_step..].to_vec(); - self.current_step += remaining_ops - .iter() - .enumerate() - .find_map(|(i, op)| { - if i < remaining_ops.len() - 1 { - match ( - op.contains("JUMP") && op != "JUMPDEST", - &*remaining_ops[i + 1], - ) { - (true, "JUMPDEST") => Some(i + 1), - _ => None, - } - } else { - None - } - }) - .unwrap_or(self.opcode_list.len() - 1); - if self.current_step > self.opcode_list.len() { - self.current_step = self.opcode_list.len() - 1 - }; + KeyCode::Char('s') => self.repeat(|this| { + let remaining_ops = &this.opcode_list[this.current_step..]; + if let Some((i, _)) = remaining_ops.iter().enumerate().skip(1).find(|&(i, op)| { + let prev = &remaining_ops[i - 1]; + let prev_is_jump = prev.contains("JUMP") && prev != "JUMPDEST"; + let is_jumpdest = op == "JUMPDEST"; + prev_is_jump && is_jumpdest + }) { + this.current_step += i; } - self.key_buffer.clear(); - } + }), + // Step backwards - KeyCode::Char('a') => { - for _ in 0..buffer_as_number(&self.key_buffer, 1) { - let prev_ops = self.opcode_list[..self.current_step].to_vec(); - self.current_step = prev_ops - .iter() - .enumerate() - .rev() - .find_map(|(i, op)| { - if i > 0 { - match ( - prev_ops[i - 1].contains("JUMP") && - prev_ops[i - 1] != "JUMPDEST", - &**op, - ) { - (true, "JUMPDEST") => Some(i - 1), - _ => None, - } - } else { - None - } - }) - .unwrap_or_default(); - } - self.key_buffer.clear(); - } - // toggle stack labels + KeyCode::Char('a') => self.repeat(|this| { + let ops = &this.opcode_list[..this.current_step]; + this.current_step = ops + .iter() + .enumerate() + .skip(1) + .rev() + .find(|&(i, op)| { + let prev = &ops[i - 1]; + let prev_is_jump = prev.contains("JUMP") && prev != "JUMPDEST"; + let is_jumpdest = op == "JUMPDEST"; + prev_is_jump && is_jumpdest + }) + .map(|(i, _)| i) + .unwrap_or_default(); + }), + + // Toggle stack labels KeyCode::Char('t') => self.stack_labels = !self.stack_labels, - // toggle memory utf8 decoding + + // Toggle memory UTF-8 decoding KeyCode::Char('m') => self.buf_utf = !self.buf_utf, - // toggle help notice + + // Toggle help notice KeyCode::Char('h') => self.show_shortcuts = !self.show_shortcuts, + + // Numbers for repeating commands or breakpoints KeyCode::Char( other @ ('0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '\''), - ) => self.key_buffer.push(other), - _ => self.key_buffer.clear(), + ) => { + // Early return to not clear the buffer. + self.key_buffer.push(other); + return ControlFlow::Continue(()); + } + + // Unknown/unhandled key code + _ => {} }; + self.key_buffer.clear(); ControlFlow::Continue(()) } @@ -321,37 +301,47 @@ impl DebuggerContext<'_> { fn handle_mouse_event(&mut self, event: MouseEvent) -> ControlFlow { match event.kind { - MouseEventKind::ScrollUp => { - if self.current_step > 0 { - self.current_step -= 1; - } else if self.draw_memory.inner_call_index > 0 { - self.draw_memory.inner_call_index -= 1; - self.draw_memory.current_buf_startline = 0; - self.draw_memory.current_stack_startline = 0; - self.current_step = self.debug_steps().len() - 1; - } - } - MouseEventKind::ScrollDown => { - if self.current_step < self.opcode_list.len() - 1 { - self.current_step += 1; - } else if self.draw_memory.inner_call_index < self.debug_arena().len() - 1 { - self.draw_memory.inner_call_index += 1; - self.draw_memory.current_buf_startline = 0; - self.draw_memory.current_stack_startline = 0; - self.current_step = 0; - } - } + MouseEventKind::ScrollUp => self.step_back(), + MouseEventKind::ScrollDown => self.step(), _ => {} } ControlFlow::Continue(()) } + + fn step_back(&mut self) { + if self.current_step > 0 { + self.current_step -= 1; + } else if self.draw_memory.inner_call_index > 0 { + self.draw_memory.inner_call_index -= 1; + self.current_step = self.n_steps() - 1; + } + } + + fn step(&mut self) { + if self.current_step < self.n_steps() - 1 { + self.current_step += 1; + } else if self.draw_memory.inner_call_index < self.debug_arena().len() - 1 { + self.draw_memory.inner_call_index += 1; + self.current_step = 0; + } + } + + /// Calls a closure `f` the number of times specified in the key buffer, and at least once. + fn repeat(&mut self, mut f: impl FnMut(&mut Self)) { + for _ in 0..buffer_as_number(&self.key_buffer) { + f(self); + } + } + + fn n_steps(&self) -> usize { + self.debug_steps().len() + } } /// Grab number from buffer. Used for something like '10k' to move up 10 operations -fn buffer_as_number(s: &str, default_value: usize) -> usize { - match s.parse() { - Ok(num) if num >= 1 => num, - _ => default_value, - } +fn buffer_as_number(s: &str) -> usize { + const MIN: usize = 1; + const MAX: usize = 100_000; + s.parse().unwrap_or(MIN).clamp(MIN, MAX) } diff --git a/crates/debugger/src/tui/draw.rs b/crates/debugger/src/tui/draw.rs index 8d91f2803..b80a8a77a 100644 --- a/crates/debugger/src/tui/draw.rs +++ b/crates/debugger/src/tui/draw.rs @@ -9,11 +9,11 @@ use ratatui::{ style::{Color, Modifier, Style}, terminal::Frame, text::{Line, Span, Text}, - widgets::{Block, Borders, Paragraph, Wrap}, + widgets::{Block, Borders, List, ListItem, ListState, Paragraph, Wrap}, }; use revm::interpreter::opcode; use revm_inspectors::tracing::types::CallKind; -use std::{cmp, collections::VecDeque, fmt::Write, io}; +use std::{collections::VecDeque, fmt::Write, io}; impl DebuggerContext<'_> { /// Draws the TUI layout and subcomponents to the given terminal. @@ -91,25 +91,25 @@ impl DebuggerContext<'_> { // constraints, so the `else` branch is unreachable. // Split off footer. - let [app, footer] = Layout::new() - .constraints([Constraint::Ratio(100 - h_height, 100), Constraint::Ratio(h_height, 100)]) - .direction(Direction::Vertical) - .split(area)[..] - else { + let [app, footer] = Layout::new( + Direction::Vertical, + [Constraint::Ratio(100 - h_height, 100), Constraint::Ratio(h_height, 100)], + ) + .split(area)[..] else { unreachable!() }; // Split the app in 4 vertically to construct all the panes. - let [op_pane, stack_pane, memory_pane, src_pane] = Layout::new() - .direction(Direction::Vertical) - .constraints([ + let [op_pane, stack_pane, memory_pane, src_pane] = Layout::new( + Direction::Vertical, + [ Constraint::Ratio(1, 6), Constraint::Ratio(1, 6), Constraint::Ratio(1, 6), Constraint::Ratio(3, 6), - ]) - .split(app)[..] - else { + ], + ) + .split(app)[..] else { unreachable!() }; @@ -138,37 +138,34 @@ impl DebuggerContext<'_> { let h_height = if self.show_shortcuts { 4 } else { 0 }; // Split off footer. - let [app, footer] = Layout::new() - .direction(Direction::Vertical) - .constraints([Constraint::Ratio(100 - h_height, 100), Constraint::Ratio(h_height, 100)]) - .split(area)[..] - else { + let [app, footer] = Layout::new( + Direction::Vertical, + [Constraint::Ratio(100 - h_height, 100), Constraint::Ratio(h_height, 100)], + ) + .split(area)[..] else { unreachable!() }; // Split app in 2 horizontally. - let [app_left, app_right] = Layout::new() - .direction(Direction::Horizontal) - .constraints([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]) - .split(app)[..] + let [app_left, app_right] = + Layout::new(Direction::Horizontal, [Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]) + .split(app)[..] else { unreachable!() }; // Split left pane in 2 vertically to opcode list and source. - let [op_pane, src_pane] = Layout::new() - .direction(Direction::Vertical) - .constraints([Constraint::Ratio(1, 4), Constraint::Ratio(3, 4)]) - .split(app_left)[..] + let [op_pane, src_pane] = + Layout::new(Direction::Vertical, [Constraint::Ratio(1, 4), Constraint::Ratio(3, 4)]) + .split(app_left)[..] else { unreachable!() }; // Split right pane horizontally to construct stack and memory. - let [stack_pane, memory_pane] = Layout::new() - .direction(Direction::Vertical) - .constraints([Constraint::Ratio(1, 4), Constraint::Ratio(3, 4)]) - .split(app_right)[..] + let [stack_pane, memory_pane] = + Layout::new(Direction::Vertical, [Constraint::Ratio(1, 4), Constraint::Ratio(3, 4)]) + .split(app_right)[..] else { unreachable!() }; @@ -380,63 +377,22 @@ impl DebuggerContext<'_> { } fn draw_op_list(&self, f: &mut Frame<'_>, area: Rect) { - let height = area.height as i32; - let extra_top_lines = height / 2; - // Absolute minimum start line - let abs_min_start = 0; - // Adjust for weird scrolling for max top line - let abs_max_start = (self.opcode_list.len() as i32 - 1) - (height / 2); - // actual minimum start line - let mut min_start = - cmp::max(self.current_step as i32 - height + extra_top_lines, abs_min_start) as usize; - - // actual max start line - let mut max_start = cmp::max( - cmp::min(self.current_step as i32 - extra_top_lines, abs_max_start), - abs_min_start, - ) as usize; - - // Sometimes, towards end of file, maximum and minim lines have swapped values. Swap if the - // case - if min_start > max_start { - std::mem::swap(&mut min_start, &mut max_start); - } - - let prev_start = *self.draw_memory.current_startline.borrow(); - let display_start = prev_start.clamp(min_start, max_start); - *self.draw_memory.current_startline.borrow_mut() = display_start; - - let max_pc = self.debug_steps().iter().map(|step| step.pc).max().unwrap_or(0); + let debug_steps = self.debug_steps(); + let max_pc = debug_steps.iter().map(|step| step.pc).max().unwrap_or(0); let max_pc_len = hex_digits(max_pc); - let debug_steps = self.debug_steps(); - let mut lines = Vec::new(); - let mut add_new_line = |line_number: usize| { - let mut line = String::with_capacity(64); - - let is_current_step = line_number == self.current_step; - if line_number < self.debug_steps().len() { - let step = &debug_steps[line_number]; - write!(line, "{:0>max_pc_len$x}|", step.pc).unwrap(); - line.push_str(if is_current_step { "▶" } else { " " }); - if let Some(op) = self.opcode_list.get(line_number) { - line.push_str(op); + let items = debug_steps + .iter() + .enumerate() + .map(|(i, step)| { + let mut content = String::with_capacity(64); + write!(content, "{:0>max_pc_len$x}|", step.pc).unwrap(); + if let Some(op) = self.opcode_list.get(i) { + content.push_str(op); } - } else { - line.push_str("END CALL"); - } - - let bg_color = if is_current_step { Color::DarkGray } else { Color::Reset }; - let style = Style::new().fg(Color::White).bg(bg_color); - lines.push(Line::from(Span::styled(line, style))); - }; - - for number in display_start..self.opcode_list.len() { - add_new_line(number); - } - - // Add one more "phantom" line so we see line where current segment execution ends - add_new_line(self.opcode_list.len()); + ListItem::new(Span::styled(content, Style::new().fg(Color::White))) + }) + .collect::>(); let title = format!( "Address: {} | PC: {} | Gas used in call: {}", @@ -445,8 +401,13 @@ impl DebuggerContext<'_> { self.current_step().total_gas_used, ); let block = Block::default().title(title).borders(Borders::ALL); - let paragraph = Paragraph::new(lines).block(block).wrap(Wrap { trim: true }); - f.render_widget(paragraph, area); + let list = List::new(items) + .block(block) + .highlight_symbol("▶") + .highlight_style(Style::new().fg(Color::White).bg(Color::DarkGray)) + .scroll_padding(1); + let mut state = ListState::default().with_selected(Some(self.current_step)); + f.render_stateful_widget(list, area, &mut state); } fn draw_stack(&self, f: &mut Frame<'_>, area: Rect) { diff --git a/crates/debugger/src/tui/mod.rs b/crates/debugger/src/tui/mod.rs index 16964e768..1fac3d051 100644 --- a/crates/debugger/src/tui/mod.rs +++ b/crates/debugger/src/tui/mod.rs @@ -115,16 +115,13 @@ impl Debugger { .spawn(move || Self::event_listener(tx)) .expect("failed to spawn thread"); - // Draw the initial state. - cx.draw(terminal)?; - // Start the event loop. loop { + cx.draw(terminal)?; match cx.handle_event(rx.recv()?) { ControlFlow::Continue(()) => {} ControlFlow::Break(reason) => return Ok(reason), } - cx.draw(terminal)?; } } From 8466c09c2402030f6d7e73d8490d3987bec847c6 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Tue, 16 Apr 2024 22:01:27 +0400 Subject: [PATCH 175/430] feat: coverage for modifiers (#7669) --- crates/evm/coverage/src/analysis.rs | 34 +++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/crates/evm/coverage/src/analysis.rs b/crates/evm/coverage/src/analysis.rs index fad5b6576..040da5a0f 100644 --- a/crates/evm/coverage/src/analysis.rs +++ b/crates/evm/coverage/src/analysis.rs @@ -33,8 +33,14 @@ impl<'a> ContractVisitor<'a> { pub fn visit(mut self, contract_ast: Node) -> eyre::Result { // Find all functions and walk their AST for node in contract_ast.nodes { - if node.node_type == NodeType::FunctionDefinition { - self.visit_function_definition(node.clone())?; + match &node.node_type { + NodeType::FunctionDefinition => { + self.visit_function_definition(node)?; + } + NodeType::ModifierDefinition => { + self.visit_modifier_definition(node)?; + } + _ => {} } } @@ -65,6 +71,23 @@ impl<'a> ContractVisitor<'a> { } } + fn visit_modifier_definition(&mut self, mut node: Node) -> eyre::Result<()> { + let name: String = + node.attribute("name").ok_or_else(|| eyre::eyre!("Modifier has no name"))?; + + match node.body.take() { + Some(body) => { + self.push_item(CoverageItem { + kind: CoverageItemKind::Function { name }, + loc: self.source_location_for(&node.src), + hits: 0, + }); + self.visit_block(*body) + } + _ => Ok(()), + } + } + fn visit_block(&mut self, node: Node) -> eyre::Result<()> { let statements: Vec = node.attribute("statements").unwrap_or_default(); @@ -90,7 +113,6 @@ impl<'a> ContractVisitor<'a> { NodeType::Break | NodeType::Continue | NodeType::EmitStatement | - NodeType::PlaceholderStatement | NodeType::RevertStatement | NodeType::YulAssignment | NodeType::YulBreak | @@ -104,6 +126,9 @@ impl<'a> ContractVisitor<'a> { Ok(()) } + // Skip placeholder statements as they are never referenced in source maps. + NodeType::PlaceholderStatement => Ok(()), + // Return with eventual subcall NodeType::Return => { self.push_item(CoverageItem { @@ -338,12 +363,13 @@ impl<'a> ContractVisitor<'a> { NodeType::ForStatement | NodeType::IfStatement | NodeType::InlineAssembly | - NodeType::PlaceholderStatement | NodeType::Return | NodeType::RevertStatement | NodeType::TryStatement | NodeType::VariableDeclarationStatement | NodeType::WhileStatement => self.visit_statement(node), + // Skip placeholder statements as they are never referenced in source maps. + NodeType::PlaceholderStatement => Ok(()), _ => { warn!("unexpected node type, expected block or statement: {:?}", node.node_type); Ok(()) From 24536cd778ddaa39a50d765f411b2b7668b6d3cb Mon Sep 17 00:00:00 2001 From: clabby Date: Tue, 16 Apr 2024 14:53:43 -0400 Subject: [PATCH 176/430] fix(cheatcodes): `expectSafeMemory` + `stopExpectSafeMemory` (#7686) * fix(cheatcodes): `expectSafeMemory` w/ new `forge-std` Fixes the `stopExpectSafeMemory` by allowing for the memory allocation of the `stopExpectSafeMemory` selector as well as the potentially out-of-bounds read performed in the `CALL` operation. Currently, forge reports incorrectly that memory safety was violated in a memory safe region of a test, if the free memory pointer was updated to `[exclusiveUpperBound-31, exclusiveUpperBound]`. To fix this, we allow for `MSTORE` operations that store the selector bytes for `stopExpectSafeMemory` outside of the allowed region, as well as `CALL` operations that are to the cheatcode address and contain the `stopExpectSafeMemory` selector in the first 4 bytes of the call arguments. * use `SELECTOR_LEN` const --- crates/cheatcodes/src/inspector.rs | 57 ++++++++++++++++++++++--- testdata/default/cheats/MemSafety.t.sol | 18 ++++++++ 2 files changed, 70 insertions(+), 5 deletions(-) diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 34a3d9260..ed6aa5cf4 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -12,14 +12,15 @@ use crate::{ self, ExpectedCallData, ExpectedCallTracker, ExpectedCallType, ExpectedEmit, ExpectedRevert, ExpectedRevertKind, }, - CheatsConfig, CheatsCtxt, Error, Result, Vm, + CheatsConfig, CheatsCtxt, DynCheatcode, Error, Result, Vm, Vm::AccountAccess, }; use alloy_primitives::{Address, Bytes, Log, B256, U256}; use alloy_rpc_types::request::{TransactionInput, TransactionRequest}; use alloy_sol_types::{SolInterface, SolValue}; -use foundry_common::{evm::Breakpoints, provider::alloy::RpcUrl}; +use foundry_common::{evm::Breakpoints, provider::alloy::RpcUrl, SELECTOR_LEN}; use foundry_evm_core::{ + abi::Vm::stopExpectSafeMemoryCall, backend::{DatabaseError, DatabaseExt, RevertDiagnostic}, constants::{CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, HARDHAT_CONSOLE_ADDRESS}, }; @@ -601,6 +602,16 @@ impl Inspector for Cheatcodes { if !ranges.iter().any(|range| { range.contains(&offset) && range.contains(&(offset + 31)) }) { + // SPECIAL CASE: When the compiler attempts to store the selector for + // `stopExpectSafeMemory`, this is allowed. It will do so at the current free memory + // pointer, which could have been updated to the exclusive upper bound during + // execution. + let value = try_or_continue!(interpreter.stack().peek(1)).to_be_bytes::<32>(); + let selector = stopExpectSafeMemoryCall {}.cheatcode().func.selector_bytes; + if value[0..SELECTOR_LEN] == selector { + return + } + disallowed_mem_write(offset, 32, interpreter, ranges); return } @@ -640,11 +651,48 @@ impl Inspector for Cheatcodes { // OPERATIONS WITH OFFSET AND SIZE ON STACK // //////////////////////////////////////////////////////////////// + opcode::CALL => { + // The destination offset of the operation is the fifth element on the stack. + let dest_offset = try_or_continue!(interpreter.stack().peek(5)).saturating_to::(); + + // The size of the data that will be copied is the sixth element on the stack. + let size = try_or_continue!(interpreter.stack().peek(6)).saturating_to::(); + + // If none of the allowed ranges contain [dest_offset, dest_offset + size), + // memory outside of the expected ranges has been touched. If the opcode + // only reads from memory, this is okay as long as the memory is not expanded. + let fail_cond = !ranges.iter().any(|range| { + range.contains(&dest_offset) && + range.contains(&(dest_offset + size.saturating_sub(1))) + }); + + // If the failure condition is met, set the output buffer to a revert string + // that gives information about the allowed ranges and revert. + if fail_cond { + // SPECIAL CASE: When a call to `stopExpectSafeMemory` is performed, this is allowed. + // It allocated calldata at the current free memory pointer, and will attempt to read + // from this memory region to perform the call. + let to = Address::from_word(try_or_continue!(interpreter.stack().peek(1)).to_be_bytes::<32>().into()); + if to == CHEATCODE_ADDRESS { + let args_offset = try_or_continue!(interpreter.stack().peek(3)).saturating_to::(); + let args_size = try_or_continue!(interpreter.stack().peek(4)).saturating_to::(); + let selector = stopExpectSafeMemoryCall {}.cheatcode().func.selector_bytes; + let memory_word = interpreter.shared_memory.slice(args_offset, args_size); + if memory_word[0..SELECTOR_LEN] == selector { + return + } + } + + disallowed_mem_write(dest_offset, size, interpreter, ranges); + return + } + } + $(opcode::$opcode => { - // The destination offset of the operation is at the top of the stack. + // The destination offset of the operation. let dest_offset = try_or_continue!(interpreter.stack().peek($offset_depth)).saturating_to::(); - // The size of the data that will be copied is the third item on the stack. + // The size of the data that will be copied. let size = try_or_continue!(interpreter.stack().peek($size_depth)).saturating_to::(); // If none of the allowed ranges contain [dest_offset, dest_offset + size), @@ -678,7 +726,6 @@ impl Inspector for Cheatcodes { (CODECOPY, 0, 2, true), (RETURNDATACOPY, 0, 2, true), (EXTCODECOPY, 1, 3, true), - (CALL, 5, 6, true), (CALLCODE, 5, 6, true), (STATICCALL, 4, 5, true), (DELEGATECALL, 4, 5, true), diff --git a/testdata/default/cheats/MemSafety.t.sol b/testdata/default/cheats/MemSafety.t.sol index 05444e39a..096d8ac47 100644 --- a/testdata/default/cheats/MemSafety.t.sol +++ b/testdata/default/cheats/MemSafety.t.sol @@ -738,6 +738,24 @@ contract MemSafetyTest is DSTest { vm.stopExpectSafeMemory(); } + /// @dev Tests that the `stopExpectSafeMemory` cheatcode can still be called if the free memory pointer was + /// updated to the exclusive upper boundary during execution. + function testStopExpectSafeMemory_freeMemUpdate() public { + uint64 initPtr; + assembly { + initPtr := mload(0x40) + } + + vm.expectSafeMemory(initPtr, initPtr + 0x20); + assembly { + // write outside of allowed range, this should revert + mstore(initPtr, 0x01) + mstore(0x40, add(initPtr, 0x20)) + } + + vm.stopExpectSafeMemory(); + } + //////////////////////////////////////////////////////////////// // HELPERS // //////////////////////////////////////////////////////////////// From 8513f619ca6781fe62d59b1bf2a8bb1bbab19927 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 16 Apr 2024 21:29:09 +0200 Subject: [PATCH 177/430] fix: hotfix cast logs subscribe (#7688) * fix: hotfix cast logs subscribe * fix features --- Cargo.lock | 9 +++++++++ crates/anvil/Cargo.toml | 2 +- crates/cast/Cargo.toml | 2 +- crates/cast/bin/cmd/logs.rs | 9 +++++++++ crates/forge/Cargo.toml | 2 +- 5 files changed, 21 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e550e31f8..748783a37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -235,6 +235,9 @@ dependencies = [ "alloy-rpc-types", "alloy-rpc-types-trace", "alloy-transport", + "alloy-transport-http", + "alloy-transport-ipc", + "alloy-transport-ws", "async-stream", "async-trait", "auto_impl", @@ -242,9 +245,11 @@ dependencies = [ "futures", "futures-utils-wasm", "lru", + "reqwest 0.12.3", "serde_json", "tokio", "tracing", + "url", ] [[package]] @@ -297,14 +302,18 @@ dependencies = [ "alloy-pubsub", "alloy-transport", "alloy-transport-http", + "alloy-transport-ipc", + "alloy-transport-ws", "futures", "pin-project", + "reqwest 0.12.3", "serde", "serde_json", "tokio", "tokio-stream", "tower", "tracing", + "url", ] [[package]] diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index 76611fc80..cdf7c2f9b 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -47,7 +47,7 @@ alloy-sol-types = { workspace = true, features = ["std"] } alloy-dyn-abi = { workspace = true, features = ["std", "eip712"] } alloy-rpc-types.workspace = true alloy-rpc-types-trace.workspace = true -alloy-provider = { workspace = true, features = ["pubsub"] } +alloy-provider = { workspace = true, features = ["reqwest", "ws", "ipc"] } alloy-transport.workspace = true alloy-chains.workspace = true alloy-genesis.workspace = true diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index 6b6dbae51..04160fa3f 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -34,7 +34,7 @@ alloy-dyn-abi.workspace = true alloy-json-abi.workspace = true alloy-primitives.workspace = true alloy-rlp.workspace = true -alloy-provider = { workspace = true, features = ["pubsub"] } +alloy-provider = { workspace = true, features = ["reqwest", "ws", "ipc"] } alloy-transport.workspace = true alloy-rpc-types.workspace = true alloy-json-rpc.workspace = true diff --git a/crates/cast/bin/cmd/logs.rs b/crates/cast/bin/cmd/logs.rs index e97521fe9..dd66176e8 100644 --- a/crates/cast/bin/cmd/logs.rs +++ b/crates/cast/bin/cmd/logs.rs @@ -1,5 +1,6 @@ use alloy_dyn_abi::{DynSolType, DynSolValue, Specifier}; use alloy_json_abi::Event; +use alloy_network::AnyNetwork; use alloy_primitives::{Address, B256}; use alloy_rpc_types::{BlockId, BlockNumberOrTag, Filter, FilterBlockOption, FilterSet, Topic}; use cast::Cast; @@ -93,6 +94,14 @@ impl LogsArgs { return Ok(()) } + // FIXME: this is a hotfix for + // currently the alloy `eth_subscribe` impl does not work with all transports, so we use + // the builtin transport here for now + let url = config.get_rpc_url_or_localhost_http()?; + let provider = alloy_provider::ProviderBuilder::<_, _, AnyNetwork>::default() + .on_builtin(url.as_ref()) + .await?; + let cast = Cast::new(&provider); let mut stdout = io::stdout(); cast.subscribe(filter, &mut stdout, json).await?; diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index b7508ae4b..039b956cc 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -51,7 +51,7 @@ alloy-dyn-abi.workspace = true alloy-json-abi.workspace = true alloy-primitives = { workspace = true, features = ["serde"] } alloy-rpc-types.workspace = true -alloy-provider.workspace = true +alloy-provider = { workspace = true, features = ["reqwest", "ws", "ipc"] } alloy-network.workspace = true alloy-transport.workspace = true alloy-signer.workspace = true From 424a95e36e53a340b5f6def7ae5a52df846d861e Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 16 Apr 2024 22:33:58 +0200 Subject: [PATCH 178/430] feat: add alias for personal_sign (#7687) --- crates/anvil/core/src/eth/mod.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/crates/anvil/core/src/eth/mod.rs b/crates/anvil/core/src/eth/mod.rs index a8a07ba92..4c485f56b 100644 --- a/crates/anvil/core/src/eth/mod.rs +++ b/crates/anvil/core/src/eth/mod.rs @@ -139,7 +139,7 @@ pub enum EthRequest { EthGetProof(Address, Vec, Option), /// The sign method calculates an Ethereum specific signature with: - #[cfg_attr(feature = "serde", serde(rename = "eth_sign"))] + #[cfg_attr(feature = "serde", serde(rename = "eth_sign", alias = "personal_sign"))] EthSign(Address, Bytes), #[cfg_attr(feature = "serde", serde(rename = "eth_signTransaction"))] @@ -1518,6 +1518,18 @@ true}]}"#; let _req = serde_json::from_value::(value).unwrap(); } + #[test] + fn test_eth_sign() { + let s = r#"{"method": "eth_sign", "params": +["0xd84de507f3fada7df80908082d3239466db55a71", "0x00"]}"#; + let value: serde_json::Value = serde_json::from_str(s).unwrap(); + let _req = serde_json::from_value::(value).unwrap(); + let s = r#"{"method": "personal_sign", "params": +["0xd84de507f3fada7df80908082d3239466db55a71", "0x00"]}"#; + let value: serde_json::Value = serde_json::from_str(s).unwrap(); + let _req = serde_json::from_value::(value).unwrap(); + } + #[test] fn test_eth_sign_typed_data() { let s = r#"{"method":"eth_signTypedData_v4","params":["0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", {"types":{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}],"Person":[{"name":"name","type":"string"},{"name":"wallet","type":"address"}],"Mail":[{"name":"from","type":"Person"},{"name":"to","type":"Person"},{"name":"contents","type":"string"}]},"primaryType":"Mail","domain":{"name":"Ether Mail","version":"1","chainId":1,"verifyingContract":"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"},"message":{"from":{"name":"Cow","wallet":"0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"},"to":{"name":"Bob","wallet":"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"},"contents":"Hello, Bob!"}}]}"#; From 9079fb66e9483e8af6d3eff6d69e4b036d7c9691 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 16 Apr 2024 22:34:05 +0200 Subject: [PATCH 179/430] fix: always set optimizer details (#7690) --- crates/config/src/lib.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 51769ab31..39408326e 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -1028,11 +1028,18 @@ impl Config { } /// Returns the `Optimizer` based on the configured settings + /// + /// Note: optimizer details can be set independently of `enabled` + /// See also: + /// and pub fn optimizer(&self) -> Optimizer { - // only configure optimizer settings if optimizer is enabled - let details = if self.optimizer { self.optimizer_details.clone() } else { None }; - - Optimizer { enabled: Some(self.optimizer), runs: Some(self.optimizer_runs), details } + Optimizer { + enabled: Some(self.optimizer), + runs: Some(self.optimizer_runs), + // we always set the details because `enabled` is effectively a specific details profile + // that can still be modified + details: self.optimizer_details.clone(), + } } /// returns the [`foundry_compilers::ConfigurableArtifacts`] for this config, that includes the From e4ab9f460e92586fc4d4f6c9e00d8cda0c2dabf0 Mon Sep 17 00:00:00 2001 From: Matt Solomon Date: Tue, 16 Apr 2024 16:59:23 -0700 Subject: [PATCH 180/430] build(foundryup): allow caller to override rust flags (#7691) --- foundryup/foundryup | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/foundryup/foundryup b/foundryup/foundryup index 8143c3e23..f1e40b648 100755 --- a/foundryup/foundryup +++ b/foundryup/foundryup @@ -8,7 +8,7 @@ FOUNDRY_MAN_DIR="$FOUNDRY_DIR/share/man/man1" BINS=(forge cast anvil chisel) -export RUSTFLAGS="-C target-cpu=native" +export RUSTFLAGS="${RUSTFLAGS:--C target-cpu=native}" main() { need_cmd git @@ -292,21 +292,21 @@ download() { fi } -# Banner Function for Foundry +# Banner Function for Foundry banner() { printf ' .xOx.xOx.xOx.xOx.xOx.xOx.xOx.xOx.xOx.xOx.xOx.xOx.xOx.xOx.xOx.xOx.xOx.xOx - + ╔═╗ ╔═╗ ╦ ╦ ╔╗╔ ╔╦╗ ╦═╗ ╦ ╦ Portable and modular toolkit - ╠╣ ║ ║ ║ ║ ║║║ ║║ ╠╦╝ ╚╦╝ for Ethereum Application Development + ╠╣ ║ ║ ║ ║ ║║║ ║║ ╠╦╝ ╚╦╝ for Ethereum Application Development ╚ ╚═╝ ╚═╝ ╝╚╝ ═╩╝ ╩╚═ ╩ written in Rust. .xOx.xOx.xOx.xOx.xOx.xOx.xOx.xOx.xOx.xOx.xOx.xOx.xOx.xOx.xOx.xOx.xOx.xOx Repo : https://github.com/foundry-rs/ -Book : https://book.getfoundry.sh/ -Chat : https://t.me/foundry_rs/ +Book : https://book.getfoundry.sh/ +Chat : https://t.me/foundry_rs/ Support : https://t.me/foundry_support/ Contribute : https://github.com/orgs/foundry-rs/projects/2/ From 46abc420efd68d289ed809bc53fb41159f3b13e9 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Wed, 17 Apr 2024 12:41:47 +0300 Subject: [PATCH 181/430] chore(cheatcodes): solc 0.6.2 compatibility (#7694) --- crates/cheatcodes/assets/cheatcodes.json | 4 ++-- crates/cheatcodes/spec/src/vm.rs | 4 ++-- testdata/cheats/Vm.sol | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index c8b5d3d2a..1f2518226 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -4915,7 +4915,7 @@ "func": { "id": "indexOf", "description": "Returns the index of the first occurrence of a `key` in an `input` string.\nReturns `NOT_FOUND` (i.e. `type(uint256).max`) if the `key` is not found.\nReturns 0 in case of an empty `key`.", - "declaration": "function indexOf(string memory input, string memory key) external pure returns (uint256);", + "declaration": "function indexOf(string calldata input, string calldata key) external pure returns (uint256);", "visibility": "external", "mutability": "pure", "signature": "indexOf(string,string)", @@ -4935,7 +4935,7 @@ "func": { "id": "isContext", "description": "Returns true if `forge` command was executed in given context.", - "declaration": "function isContext(ForgeContext context) external view returns (bool isContext);", + "declaration": "function isContext(ForgeContext context) external view returns (bool result);", "visibility": "external", "mutability": "view", "signature": "isContext(uint8)", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 03a2cd090..01d1290a6 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1642,7 +1642,7 @@ interface Vm { /// Returns true if `forge` command was executed in given context. #[cheatcode(group = Environment)] - function isContext(ForgeContext context) external view returns (bool isContext); + function isContext(ForgeContext context) external view returns (bool result); // ======== Scripts ======== @@ -1751,7 +1751,7 @@ interface Vm { /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `key` is not found. /// Returns 0 in case of an empty `key`. #[cheatcode(group = String)] - function indexOf(string memory input, string memory key) external pure returns (uint256); + function indexOf(string calldata input, string calldata key) external pure returns (uint256); // ======== JSON Parsing and Manipulation ======== diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 44f0d52ac..65c657843 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -241,8 +241,8 @@ interface Vm { function getNonce(address account) external view returns (uint64 nonce); function getNonce(Wallet calldata wallet) external returns (uint64 nonce); function getRecordedLogs() external returns (Log[] memory logs); - function indexOf(string memory input, string memory key) external pure returns (uint256); - function isContext(ForgeContext context) external view returns (bool isContext); + function indexOf(string calldata input, string calldata key) external pure returns (uint256); + function isContext(ForgeContext context) external view returns (bool result); function isDir(string calldata path) external returns (bool result); function isFile(string calldata path) external returns (bool result); function isPersistent(address account) external view returns (bool persistent); From 0a4d246261bc51e3f9c4f0b0c90938f3d3c659bf Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 17 Apr 2024 18:36:15 +0400 Subject: [PATCH 182/430] feat: optimize compilation by reading AST (#7599) * feat: optimize compiler runs by reading AST * clippy * fallback to solc * fmt * clippy * update fixtures * fix for windows * wip * bump compilers * clippy + fmt * fix --- Cargo.lock | 4 +- Cargo.toml | 2 +- crates/cli/src/utils/cmd.rs | 24 ++++++----- crates/common/src/compile.rs | 23 ++-------- crates/config/src/lib.rs | 1 + crates/forge/bin/cmd/create.rs | 22 ++++++---- crates/forge/bin/cmd/flatten.rs | 4 +- crates/forge/bin/cmd/selectors.rs | 9 +++- .../can_create_template_contract.stdout | 2 +- .../fixtures/can_create_using_unlocked.stdout | 2 +- .../can_create_with_constructor_args.stdout | 2 +- crates/script/src/build.rs | 43 +++---------------- 12 files changed, 53 insertions(+), 85 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 748783a37..5095c7b0d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3810,9 +3810,9 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1990477446ea72d80da26951cf7ba68652f9e5b481e3a469f09c4b120f647f" +checksum = "8caca67f174741b05c2f4188d3ee93420342130eb2a768abb9b885bb8b918df2" dependencies = [ "alloy-json-abi", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index 6c19cff94..622b2cf3f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -138,7 +138,7 @@ foundry-linking = { path = "crates/linking" } # solc & compilation utilities foundry-block-explorers = { version = "0.2.6", default-features = false } -foundry-compilers = { version = "0.3.14", default-features = false } +foundry-compilers = { version = "0.3.17", default-features = false } ## revm # no default features to avoid c-kzg diff --git a/crates/cli/src/utils/cmd.rs b/crates/cli/src/utils/cmd.rs index e58fd937d..475fae2ed 100644 --- a/crates/cli/src/utils/cmd.rs +++ b/crates/cli/src/utils/cmd.rs @@ -5,7 +5,6 @@ use foundry_common::{cli_warn, fs, TestFunctionExt}; use foundry_compilers::{ artifacts::{CompactBytecode, CompactDeployedBytecode}, cache::{CacheEntry, SolFilesCache}, - info::ContractInfo, utils::read_json_file, Artifact, ProjectCompileOutput, }; @@ -20,7 +19,11 @@ use foundry_evm::{ render_trace_arena, CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, Traces, }, }; -use std::{fmt::Write, path::PathBuf, str::FromStr}; +use std::{ + fmt::Write, + path::{Path, PathBuf}, + str::FromStr, +}; use yansi::Paint; /// Given a `Project`'s output, removes the matching ABI, Bytecode and @@ -28,16 +31,17 @@ use yansi::Paint; #[track_caller] pub fn remove_contract( output: &mut ProjectCompileOutput, - info: &ContractInfo, + path: &Path, + name: &str, ) -> Result<(JsonAbi, CompactBytecode, CompactDeployedBytecode)> { - let contract = if let Some(contract) = output.remove_contract(info) { + let contract = if let Some(contract) = output.remove(path.to_string_lossy(), name) { contract } else { - let mut err = format!("could not find artifact: `{}`", info.name); + let mut err = format!("could not find artifact: `{}`", name); if let Some(suggestion) = - super::did_you_mean(&info.name, output.artifacts().map(|(name, _)| name)).pop() + super::did_you_mean(name, output.artifacts().map(|(name, _)| name)).pop() { - if suggestion != info.name { + if suggestion != name { err = format!( r#"{err} @@ -50,17 +54,17 @@ pub fn remove_contract( let abi = contract .get_abi() - .ok_or_else(|| eyre::eyre!("contract {} does not contain abi", info))? + .ok_or_else(|| eyre::eyre!("contract {} does not contain abi", name))? .into_owned(); let bin = contract .get_bytecode() - .ok_or_else(|| eyre::eyre!("contract {} does not contain bytecode", info))? + .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", info))? + .ok_or_else(|| eyre::eyre!("contract {} does not contain deployed bytecode", name))? .into_owned(); Ok((abi, bin, runtime)) diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index baf73c992..a498a61b3 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -8,8 +8,8 @@ use foundry_compilers::{ artifacts::{BytecodeObject, ContractBytecodeSome, Libraries}, remappings::Remapping, report::{BasicStdoutReporter, NoReporter, Report}, - Artifact, ArtifactId, FileFilter, Graph, Project, ProjectCompileOutput, ProjectPathsConfig, - Solc, SolcConfig, + Artifact, ArtifactId, FileFilter, Project, ProjectCompileOutput, ProjectPathsConfig, Solc, + SolcConfig, }; use foundry_linking::Linker; use num_format::{Locale, ToFormattedString}; @@ -470,27 +470,12 @@ pub struct ContractInfo { /// If `verify` and it's a standalone script, throw error. Only allowed for projects. /// /// **Note:** this expects the `target_path` to be absolute -pub fn compile_target_with_filter( +pub fn compile_target( target_path: &Path, project: &Project, quiet: bool, - verify: bool, - skip: Vec, ) -> Result { - let graph = Graph::resolve(&project.paths)?; - - // Checking if it's a standalone script, or part of a project. - let mut compiler = ProjectCompiler::new().quiet(quiet); - if !skip.is_empty() { - compiler = compiler.filter(Box::new(SkipBuildFilters::new(skip)?)); - } - if !graph.files().contains_key(target_path) { - if verify { - eyre::bail!("You can only verify deployments from inside a project! Make sure it exists with `forge tree`."); - } - compiler = compiler.files([target_path.into()]); - } - compiler.compile(project) + ProjectCompiler::new().quiet(quiet).files([target_path.into()]).compile(project) } /// Compiles an Etherscan source from metadata by creating a project. diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 39408326e..12a588d1c 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -4553,6 +4553,7 @@ mod tests { stack_allocation: None, optimizer_steps: Some("dhfoDgvulfnTUtnIf".to_string()), }), + simple_counter_for_loop_unchecked_increment: None, }), ..Default::default() }; diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index e42a17057..2212c4e98 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -15,9 +15,11 @@ use foundry_cli::{ utils::{self, read_constructor_args_file, remove_contract, LoadConfig}, }; use foundry_common::{ - compile::ProjectCompiler, fmt::parse_tokens, provider::alloy::estimate_eip1559_fees, + compile::{self}, + fmt::parse_tokens, + provider::alloy::estimate_eip1559_fees, }; -use foundry_compilers::{artifacts::BytecodeObject, info::ContractInfo, utils::canonicalized}; +use foundry_compilers::{artifacts::BytecodeObject, info::ContractInfo, utils::canonicalize}; use serde_json::json; use std::{borrow::Borrow, marker::PhantomData, path::PathBuf, sync::Arc}; @@ -84,15 +86,17 @@ impl CreateArgs { pub async fn run(mut self) -> Result<()> { // Find Project & Compile let project = self.opts.project()?; - let mut output = - ProjectCompiler::new().quiet_if(self.json || self.opts.silent).compile(&project)?; - if let Some(ref mut path) = self.contract.path { - // paths are absolute in the project's output - *path = canonicalized(project.root().join(&path)).to_string_lossy().to_string(); - } + let target_path = if let Some(ref mut path) = self.contract.path { + canonicalize(project.root().join(path))? + } else { + project.find_contract_path(&self.contract.name)? + }; + + let mut output = + compile::compile_target(&target_path, &project, self.json || self.opts.silent)?; - let (abi, bin, _) = remove_contract(&mut output, &self.contract)?; + let (abi, bin, _) = remove_contract(&mut output, &target_path, &self.contract.name)?; let bin = match bin.object { BytecodeObject::Bytecode(_) => bin.object, diff --git a/crates/forge/bin/cmd/flatten.rs b/crates/forge/bin/cmd/flatten.rs index c61369320..c1351d06d 100644 --- a/crates/forge/bin/cmd/flatten.rs +++ b/crates/forge/bin/cmd/flatten.rs @@ -4,7 +4,7 @@ use foundry_cli::{ opts::{CoreBuildArgs, ProjectPathsArgs}, utils::LoadConfig, }; -use foundry_common::{compile::ProjectCompiler, fs}; +use foundry_common::{compile::compile_target, fs}; use foundry_compilers::{error::SolcError, flatten::Flattener}; use std::path::PathBuf; @@ -42,7 +42,7 @@ impl FlattenArgs { let project = config.ephemeral_no_artifacts_project()?; let target_path = dunce::canonicalize(target_path)?; - let compiler_output = ProjectCompiler::new().files([target_path.clone()]).compile(&project); + let compiler_output = compile_target(&target_path, &project, false); let flattened = match compiler_output { Ok(compiler_output) => { diff --git a/crates/forge/bin/cmd/selectors.rs b/crates/forge/bin/cmd/selectors.rs index 1ee251082..8e07fb1f6 100644 --- a/crates/forge/bin/cmd/selectors.rs +++ b/crates/forge/bin/cmd/selectors.rs @@ -6,7 +6,7 @@ use foundry_cli::{ utils::FoundryPathExt, }; use foundry_common::{ - compile::ProjectCompiler, + compile::{compile_target, ProjectCompiler}, selectors::{import_selectors, SelectorImportData}, }; use foundry_compilers::{artifacts::output_selection::ContractOutputSelection, info::ContractInfo}; @@ -71,7 +71,12 @@ impl SelectorsSubcommands { }; let project = build_args.project()?; - let output = ProjectCompiler::new().quiet(true).compile(&project)?; + let output = if let Some(name) = &contract { + let target_path = project.find_contract_path(name)?; + compile_target(&target_path, &project, false)? + } else { + ProjectCompiler::new().compile(&project)? + }; let artifacts = if all { output .into_artifacts_with_files() diff --git a/crates/forge/tests/fixtures/can_create_template_contract.stdout b/crates/forge/tests/fixtures/can_create_template_contract.stdout index adb787a44..533c92727 100644 --- a/crates/forge/tests/fixtures/can_create_template_contract.stdout +++ b/crates/forge/tests/fixtures/can_create_template_contract.stdout @@ -1,4 +1,4 @@ -Compiling 27 files with 0.8.23 +Compiling 1 files with 0.8.23 Solc 0.8.23 finished in 2.27s Compiler run successful! Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 diff --git a/crates/forge/tests/fixtures/can_create_using_unlocked.stdout b/crates/forge/tests/fixtures/can_create_using_unlocked.stdout index 34a5fb9f7..1f8b60d6f 100644 --- a/crates/forge/tests/fixtures/can_create_using_unlocked.stdout +++ b/crates/forge/tests/fixtures/can_create_using_unlocked.stdout @@ -1,4 +1,4 @@ -Compiling 27 files with 0.8.23 +Compiling 1 files with 0.8.23 Solc 0.8.23 finished in 1.95s Compiler run successful! Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 diff --git a/crates/forge/tests/fixtures/can_create_with_constructor_args.stdout b/crates/forge/tests/fixtures/can_create_with_constructor_args.stdout index 0fb83d06f..299ad2f2d 100644 --- a/crates/forge/tests/fixtures/can_create_with_constructor_args.stdout +++ b/crates/forge/tests/fixtures/can_create_with_constructor_args.stdout @@ -1,4 +1,4 @@ -Compiling 28 files with 0.8.23 +Compiling 1 files with 0.8.23 Solc 0.8.23 finished in 2.82s Compiler run successful! Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index 4fb88719b..ea00dc592 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -8,17 +8,15 @@ use crate::{ use alloy_primitives::{Address, Bytes}; use alloy_provider::Provider; -use eyre::{Context, OptionExt, Result}; +use eyre::{OptionExt, Result}; use foundry_cheatcodes::ScriptWallets; -use foundry_cli::utils::get_cached_entry_by_name; use foundry_common::{ - compile::{self, ContractSources, ProjectCompiler}, + compile::{self, ContractSources}, provider::alloy::try_get_http_provider, ContractData, ContractsByArtifact, }; use foundry_compilers::{ artifacts::{BytecodeObject, Libraries}, - cache::SolFilesCache, info::ContractInfo, ArtifactId, ProjectCompileOutput, }; @@ -149,7 +147,6 @@ impl PreprocessedState { pub fn compile(self) -> Result { let Self { args, script_config, script_wallets } = self; let project = script_config.config.project()?; - let filters = args.skip.clone().unwrap_or_default(); let mut target_name = args.target_contract.clone(); @@ -157,46 +154,18 @@ impl PreprocessedState { // Otherwise, parse input as : and use the path from the contract info, if // present. let target_path = if let Ok(path) = dunce::canonicalize(&args.path) { - Some(path) + path } else { let contract = ContractInfo::from_str(&args.path)?; target_name = Some(contract.name.clone()); if let Some(path) = contract.path { - Some(dunce::canonicalize(path)?) + dunce::canonicalize(path)? } else { - None + project.find_contract_path(contract.name.as_str())? } }; - // If we've found target path above, only compile it. - // Otherwise, compile everything to match contract by name later. - let output = if let Some(target_path) = target_path.clone() { - compile::compile_target_with_filter( - &target_path, - &project, - args.opts.silent, - args.verify, - filters, - ) - } else if !project.paths.has_input_files() { - Err(eyre::eyre!("The project doesn't have any input files. Make sure the `script` directory is configured properly in foundry.toml. Otherwise, provide the path to the file.")) - } else { - ProjectCompiler::new().compile(&project) - }?; - - // If we still don't have target path, find it by name in the compilation cache. - let target_path = if let Some(target_path) = target_path { - target_path - } else { - let target_name = target_name.clone().expect("was set above"); - let cache = SolFilesCache::read_joined(&project.paths) - .wrap_err("Could not open compiler cache")?; - let (path, _) = get_cached_entry_by_name(&cache, &target_name) - .wrap_err("Could not find target contract in cache")?; - path - }; - - let target_path = project.root().join(target_path); + let output = compile::compile_target(&target_path, &project, args.opts.silent)?; let mut target_id: Option = None; From 19871fcde773659568a141f0755dc8658f117536 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 18 Apr 2024 00:34:45 +0400 Subject: [PATCH 183/430] fix: better artifacts management for `getCode` (#7685) * fix: better artifacts management * simplify * Update crates/common/src/contracts.rs Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * Arc --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- crates/cheatcodes/src/config.rs | 9 +- crates/cheatcodes/src/fs.rs | 74 ++++----- crates/common/src/contracts.rs | 21 +++ .../evm/evm/src/executors/invariant/error.rs | 2 +- .../evm/evm/src/executors/invariant/funcs.rs | 2 +- crates/evm/traces/src/lib.rs | 12 +- crates/forge/bin/cmd/coverage.rs | 55 +++--- crates/forge/bin/cmd/test/mod.rs | 76 ++++----- crates/forge/src/multi_runner.rs | 157 ++++++++---------- crates/forge/src/result.rs | 15 +- crates/forge/src/runner.rs | 24 ++- crates/forge/tests/it/repros.rs | 28 ++-- crates/forge/tests/it/test_helpers.rs | 24 ++- crates/script/src/build.rs | 29 +--- crates/script/src/execute.rs | 2 +- crates/script/src/lib.rs | 12 +- 16 files changed, 258 insertions(+), 284 deletions(-) diff --git a/crates/cheatcodes/src/config.rs b/crates/cheatcodes/src/config.rs index f93ab4f58..a2b9a5f5b 100644 --- a/crates/cheatcodes/src/config.rs +++ b/crates/cheatcodes/src/config.rs @@ -1,8 +1,8 @@ use super::Result; use crate::{script::ScriptWallets, Vm::Rpc}; use alloy_primitives::Address; -use foundry_common::fs::normalize_path; -use foundry_compilers::{utils::canonicalize, ArtifactId, ProjectPathsConfig}; +use foundry_common::{fs::normalize_path, ContractsByArtifact}; +use foundry_compilers::{utils::canonicalize, ProjectPathsConfig}; use foundry_config::{ cache::StorageCachingConfig, fs_permissions::FsAccessKind, Config, FsPermissions, ResolvedRpcEndpoints, @@ -12,6 +12,7 @@ use semver::Version; use std::{ collections::HashMap, path::{Path, PathBuf}, + sync::Arc, time::Duration, }; @@ -47,7 +48,7 @@ pub struct CheatsConfig { /// Artifacts which are guaranteed to be fresh (either recompiled or cached). /// If Some, `vm.getDeployedCode` invocations are validated to be in scope of this list. /// If None, no validation is performed. - pub available_artifacts: Option>, + pub available_artifacts: Option>, /// Version of the script/test contract which is currently running. pub running_version: Option, } @@ -57,7 +58,7 @@ impl CheatsConfig { pub fn new( config: &Config, evm_opts: EvmOpts, - available_artifacts: Option>, + available_artifacts: Option>, script_wallets: Option, running_version: Option, ) -> Self { diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index 9b22c4d8d..cecfc8822 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -4,7 +4,7 @@ use super::string::parse; use crate::{Cheatcode, Cheatcodes, Result, Vm::*}; use alloy_dyn_abi::DynSolType; use alloy_json_abi::ContractObject; -use alloy_primitives::U256; +use alloy_primitives::{Bytes, U256}; use alloy_sol_types::SolValue; use dialoguer::{Input, Password}; use foundry_common::fs; @@ -251,24 +251,14 @@ impl Cheatcode for writeLineCall { impl Cheatcode for getCodeCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { artifactPath: path } = self; - let object = read_bytecode(state, path)?; - if let Some(bin) = object.bytecode { - Ok(bin.abi_encode()) - } else { - Err(fmt_err!("No bytecode for contract. Is it abstract or unlinked?")) - } + Ok(get_artifact_code(state, path, false)?.abi_encode()) } } impl Cheatcode for getDeployedCodeCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { artifactPath: path } = self; - let object = read_bytecode(state, path)?; - if let Some(bin) = object.deployed_bytecode { - Ok(bin.abi_encode()) - } else { - Err(fmt_err!("No deployed bytecode for contract. Is it abstract or unlinked?")) - } + Ok(get_artifact_code(state, path, true)?.abi_encode()) } } @@ -282,9 +272,9 @@ impl Cheatcode for getDeployedCodeCall { /// - `path/to/contract.sol:0.8.23` /// - `ContractName` /// - `ContractName:0.8.23` -fn get_artifact_path(state: &Cheatcodes, path: &str) -> Result { - if path.ends_with(".json") { - Ok(PathBuf::from(path)) +fn get_artifact_code(state: &Cheatcodes, path: &str, deployed: bool) -> Result { + let path = if path.ends_with(".json") { + PathBuf::from(path) } else { let mut parts = path.split(':'); @@ -314,11 +304,11 @@ fn get_artifact_path(state: &Cheatcodes, path: &str) -> Result { None }; - // Use available artifacts list if available - if let Some(available_ids) = &state.config.available_artifacts { - let filtered = available_ids + // Use available artifacts list if present + if let Some(artifacts) = &state.config.available_artifacts { + let filtered = artifacts .iter() - .filter(|id| { + .filter(|(id, _)| { // name might be in the form of "Counter.0.8.23" let id_name = id.name.split('.').next().unwrap(); @@ -356,7 +346,7 @@ fn get_artifact_path(state: &Cheatcodes, path: &str) -> Result { .and_then(|version| { let filtered = filtered .into_iter() - .filter(|id| id.version == *version) + .filter(|(id, _)| id.version == *version) .collect::>(); (filtered.len() == 1).then_some(filtered[0]) @@ -365,31 +355,33 @@ fn get_artifact_path(state: &Cheatcodes, path: &str) -> Result { } }?; - Ok(artifact.path.clone()) + if deployed { + return Ok(artifact.1.deployed_bytecode.clone()) + } else { + return Ok(artifact.1.bytecode.clone()) + } } else { - let path_in_artifacts = - match (file.map(|f| f.to_string_lossy().to_string()), contract_name) { - (Some(file), Some(contract_name)) => Ok(format!("{file}/{contract_name}.json")), - (None, Some(contract_name)) => { - Ok(format!("{contract_name}.sol/{contract_name}.json")) - } - (Some(file), None) => { - let name = file.replace(".sol", ""); - Ok(format!("{file}/{name}.json")) - } - _ => Err(fmt_err!("Invalid artifact path")), - }?; - Ok(state.config.paths.artifacts.join(path_in_artifacts)) + match (file.map(|f| f.to_string_lossy().to_string()), contract_name) { + (Some(file), Some(contract_name)) => { + PathBuf::from(format!("{file}/{contract_name}.json")) + } + (None, Some(contract_name)) => { + PathBuf::from(format!("{contract_name}.sol/{contract_name}.json")) + } + (Some(file), None) => { + let name = file.replace(".sol", ""); + PathBuf::from(format!("{file}/{name}.json")) + } + _ => return Err(fmt_err!("Invalid artifact path")), + } } - } -} + }; -/// Reads the bytecode object(s) from the matching artifact -fn read_bytecode(state: &Cheatcodes, path: &str) -> Result { - let path = get_artifact_path(state, path)?; let path = state.config.ensure_path_allowed(path, FsAccessKind::Read)?; let data = fs::read_to_string(path)?; - serde_json::from_str::(&data).map_err(Into::into) + let artifact = serde_json::from_str::(&data)?; + let maybe_bytecode = if deployed { artifact.deployed_bytecode } else { artifact.bytecode }; + maybe_bytecode.ok_or_else(|| fmt_err!("No bytecode for contract. Is it abstract or unlinked?")) } impl Cheatcode for ffiCall { diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index d400e0a08..561d7229b 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -32,6 +32,27 @@ type ArtifactWithContractRef<'a> = (&'a ArtifactId, &'a ContractData); pub struct ContractsByArtifact(pub BTreeMap); impl ContractsByArtifact { + /// Creates a new instance by collecting all artifacts with present bytecode from an iterator. + /// + /// It is recommended to use this method with an output of + /// [foundry_linking::Linker::get_linked_artifacts]. + pub fn new(artifacts: impl IntoIterator) -> Self { + Self( + artifacts + .into_iter() + .filter_map(|(id, artifact)| { + let name = id.name.clone(); + let bytecode = artifact.bytecode.and_then(|b| b.into_bytes())?; + let deployed_bytecode = + artifact.deployed_bytecode.and_then(|b| b.into_bytes())?; + let abi = artifact.abi?; + + Some((id, ContractData { name, abi, bytecode, deployed_bytecode })) + }) + .collect(), + ) + } + /// Finds a contract which has a similar bytecode as `code`. pub fn find_by_creation_code(&self, code: &[u8]) -> Option { self.iter() diff --git a/crates/evm/evm/src/executors/invariant/error.rs b/crates/evm/evm/src/executors/invariant/error.rs index 0695ad6c4..ae122d0fa 100644 --- a/crates/evm/evm/src/executors/invariant/error.rs +++ b/crates/evm/evm/src/executors/invariant/error.rs @@ -149,7 +149,7 @@ impl FailedInvariantCaseData { pub fn replay( &self, mut executor: Executor, - known_contracts: Option<&ContractsByArtifact>, + known_contracts: &ContractsByArtifact, mut ided_contracts: ContractsByAddress, logs: &mut Vec, traces: &mut Traces, diff --git a/crates/evm/evm/src/executors/invariant/funcs.rs b/crates/evm/evm/src/executors/invariant/funcs.rs index daa326b0c..b3a913fb3 100644 --- a/crates/evm/evm/src/executors/invariant/funcs.rs +++ b/crates/evm/evm/src/executors/invariant/funcs.rs @@ -71,7 +71,7 @@ pub fn assert_invariants( pub fn replay_run( invariant_contract: &InvariantContract<'_>, mut executor: Executor, - known_contracts: Option<&ContractsByArtifact>, + known_contracts: &ContractsByArtifact, mut ided_contracts: ContractsByAddress, logs: &mut Vec, traces: &mut Traces, diff --git a/crates/evm/traces/src/lib.rs b/crates/evm/traces/src/lib.rs index 81f13f95a..a52fd3b42 100644 --- a/crates/evm/traces/src/lib.rs +++ b/crates/evm/traces/src/lib.rs @@ -12,7 +12,7 @@ use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; use foundry_evm_core::constants::CHEATCODE_ADDRESS; use futures::{future::BoxFuture, FutureExt}; use serde::{Deserialize, Serialize}; -use std::{collections::BTreeMap, fmt::Write}; +use std::fmt::Write; use yansi::{Color, Paint}; /// Call trace address identifiers. @@ -293,12 +293,8 @@ fn trace_color(trace: &CallTrace) -> Color { } /// Given a list of traces and artifacts, it returns a map connecting address to abi -pub fn load_contracts( - traces: Traces, - known_contracts: Option<&ContractsByArtifact>, -) -> ContractsByAddress { - let Some(contracts) = known_contracts else { return BTreeMap::new() }; - let mut local_identifier = LocalTraceIdentifier::new(contracts); +pub fn load_contracts(traces: Traces, known_contracts: &ContractsByArtifact) -> ContractsByAddress { + let mut local_identifier = LocalTraceIdentifier::new(known_contracts); let mut decoder = CallTraceDecoderBuilder::new().build(); for (_, trace) in &traces { decoder.identify(trace, &mut local_identifier); @@ -308,7 +304,7 @@ pub fn load_contracts( .contracts .iter() .filter_map(|(addr, name)| { - if let Ok(Some((_, contract))) = contracts.find_by_name_or_identifier(name) { + if let Ok(Some((_, contract))) = known_contracts.find_by_name_or_identifier(name) { return Some((*addr, (name.clone(), contract.abi.clone()))); } None diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index bbd99de33..fc919934f 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -7,7 +7,6 @@ use forge::{ analysis::SourceAnalyzer, anchors::find_anchors, BytecodeReporter, ContractId, CoverageReport, CoverageReporter, DebugReporter, LcovReporter, SummaryReporter, }, - inspectors::CheatsConfig, opts::EvmOpts, result::SuiteResult, revm::primitives::SpecId, @@ -28,7 +27,11 @@ use foundry_compilers::{ use foundry_config::{Config, SolcReq}; use rustc_hash::FxHashMap; use semver::Version; -use std::{collections::HashMap, path::PathBuf, sync::mpsc::channel}; +use std::{ + collections::HashMap, + path::PathBuf, + sync::{mpsc::channel, Arc}, +}; use yansi::Paint; /// A map, keyed by contract ID, to a tuple of the deployment source map and the runtime source map. @@ -101,7 +104,7 @@ impl CoverageArgs { let report = self.prepare(&config, output.clone())?; p_println!(!self.opts.silent => "Running tests..."); - self.collect(project, output, report, config, evm_opts).await + self.collect(project, output, report, Arc::new(config), evm_opts).await } /// Builds the project. @@ -308,29 +311,20 @@ impl CoverageArgs { project: Project, output: ProjectCompileOutput, mut report: CoverageReport, - config: Config, + config: Arc, evm_opts: EvmOpts, ) -> Result<()> { let root = project.paths.root; - let artifact_ids = output.artifact_ids().map(|(id, _)| id).collect(); - // Build the contract runner let env = evm_opts.evm_env().await?; - let mut runner = MultiContractRunnerBuilder::default() + let mut runner = MultiContractRunnerBuilder::new(config.clone()) .initial_balance(evm_opts.initial_balance) .evm_spec(config.evm_spec_id()) .sender(evm_opts.sender) .with_fork(evm_opts.get_fork(&config, env.clone())) - .with_cheats_config(CheatsConfig::new( - &config, - evm_opts.clone(), - Some(artifact_ids), - None, - None, - )) .with_test_options(TestOptions { - fuzz: config.fuzz, + fuzz: config.fuzz.clone(), invariant: config.invariant, ..Default::default() }) @@ -338,31 +332,32 @@ impl CoverageArgs { .build(&root, output, env, evm_opts)?; // Run tests - let known_contracts = runner.known_contracts.clone(); let filter = self.filter; let (tx, rx) = channel::<(String, SuiteResult)>(); let handle = tokio::task::spawn_blocking(move || runner.test(&filter, tx)); // Add hit data to the coverage report - let data = rx - .into_iter() - .flat_map(|(_, suite)| suite.test_results.into_values()) - .filter_map(|mut result| result.coverage.take()) - .flat_map(|hit_maps| { - hit_maps.0.into_values().filter_map(|map| { + let data = rx.into_iter().flat_map(|(_, suite)| { + let mut hits = Vec::new(); + for (_, mut result) in suite.test_results { + let Some(hit_maps) = result.coverage.take() else { continue }; + + for map in hit_maps.0.into_values() { if let Some((id, _)) = - known_contracts.find_by_deployed_code(map.bytecode.as_ref()) + suite.known_contracts.find_by_deployed_code(map.bytecode.as_ref()) { - Some((id, map, true)) + hits.push((id.clone(), map, true)); } else if let Some((id, _)) = - known_contracts.find_by_creation_code(map.bytecode.as_ref()) + suite.known_contracts.find_by_creation_code(map.bytecode.as_ref()) { - Some((id, map, false)) - } else { - None + hits.push((id.clone(), map, false)); } - }) - }); + } + } + + hits + }); + for (artifact_id, hits, is_deployed_code) in data { // TODO: Note down failing tests if let Some(source_id) = report.get_source_id( diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 16f5ec516..4a120adc7 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -5,7 +5,6 @@ use eyre::Result; use forge::{ decode::decode_console_logs, gas_report::GasReport, - inspectors::CheatsConfig, multi_runner::matches_contract, result::{SuiteResult, TestOutcome, TestStatus}, traces::{identifier::SignaturesIdentifier, CallTraceDecoderBuilder, TraceKind}, @@ -35,7 +34,7 @@ use regex::Regex; use std::{ collections::{BTreeMap, BTreeSet}, path::PathBuf, - sync::mpsc::channel, + sync::{mpsc::channel, Arc}, time::Instant, }; use watchexec::config::{InitConfig, RuntimeConfig}; @@ -274,21 +273,14 @@ impl TestArgs { // Clone the output only if we actually need it later for the debugger. let output_clone = should_debug.then(|| output.clone()); - let artifact_ids = output.artifact_ids().map(|(id, _)| id).collect(); + let config = Arc::new(config); - let runner = MultiContractRunnerBuilder::default() + let runner = MultiContractRunnerBuilder::new(config.clone()) .set_debug(should_debug) .initial_balance(evm_opts.initial_balance) .evm_spec(config.evm_spec_id()) .sender(evm_opts.sender) .with_fork(evm_opts.get_fork(&config, env.clone())) - .with_cheats_config(CheatsConfig::new( - &config, - evm_opts.clone(), - Some(artifact_ids), - None, - None, // populated separately for each test contract - )) .with_test_options(test_options) .enable_isolation(evm_opts.isolate) .build(project_root, output, env, evm_opts)?; @@ -328,7 +320,7 @@ impl TestArgs { .debug_arenas(test_result.debug.as_slice()) .sources(sources) .breakpoints(test_result.breakpoints.clone()); - if let Some(decoder) = &outcome.decoder { + if let Some(decoder) = &outcome.last_run_decoder { builder = builder.decoder(decoder); } let mut debugger = builder.build(); @@ -342,7 +334,7 @@ impl TestArgs { pub async fn run_tests( &self, mut runner: MultiContractRunner, - config: Config, + config: Arc, verbosity: u8, filter: &ProjectPathsAwareFilter, ) -> eyre::Result { @@ -367,15 +359,7 @@ impl TestArgs { return Ok(TestOutcome::new(results, self.allow_failure)); } - // Set up trace identifiers. - let known_contracts = runner.known_contracts.clone(); let remote_chain_id = runner.evm_opts.get_remote_chain_id(); - let mut identifier = TraceIdentifiers::new().with_local(&known_contracts); - - // Avoid using etherscan for gas report as we decode more traces and this will be expensive. - if !self.gas_report { - identifier = identifier.with_etherscan(&config, remote_chain_id)?; - } // Run tests. let (tx, rx) = channel::<(String, SuiteResult)>(); @@ -385,24 +369,9 @@ impl TestArgs { move || runner.test(&filter, tx) }); - let mut gas_report = - self.gas_report.then(|| GasReport::new(config.gas_reports, config.gas_reports_ignore)); - - // Build the trace decoder. - let mut builder = CallTraceDecoderBuilder::new() - .with_known_contracts(&known_contracts) - .with_verbosity(verbosity); - // Signatures are of no value for gas reports. - if !self.gas_report { - builder = builder.with_signature_identifier(SignaturesIdentifier::new( - Config::foundry_cache_dir(), - config.offline, - )?); - } - let mut decoder = builder.build(); - - // We identify addresses if we're going to print *any* trace or gas report. - let identify_addresses = verbosity >= 3 || self.gas_report || self.debug.is_some(); + let mut gas_report = self + .gas_report + .then(|| GasReport::new(config.gas_reports.clone(), config.gas_reports_ignore.clone())); let mut outcome = TestOutcome::empty(self.allow_failure); @@ -410,6 +379,32 @@ impl TestArgs { for (contract_name, suite_result) in rx { let tests = &suite_result.test_results; + // Set up trace identifiers. + let known_contracts = suite_result.known_contracts.clone(); + let mut identifier = TraceIdentifiers::new().with_local(&known_contracts); + + // Avoid using etherscan for gas report as we decode more traces and this will be + // expensive. + if !self.gas_report { + identifier = identifier.with_etherscan(&config, remote_chain_id)?; + } + + // Build the trace decoder. + let mut builder = CallTraceDecoderBuilder::new() + .with_known_contracts(&known_contracts) + .with_verbosity(verbosity); + // Signatures are of no value for gas reports. + if !self.gas_report { + builder = builder.with_signature_identifier(SignaturesIdentifier::new( + Config::foundry_cache_dir(), + config.offline, + )?); + } + let mut decoder = builder.build(); + + // We identify addresses if we're going to print *any* trace or gas report. + let identify_addresses = verbosity >= 3 || self.gas_report || self.debug.is_some(); + // Print suite header. println!(); for warning in suite_result.warnings.iter() { @@ -515,6 +510,7 @@ impl TestArgs { // Add the suite result to the outcome. outcome.results.insert(contract_name, suite_result); + outcome.last_run_decoder = Some(decoder); // Stop processing the remaining suites if any test failed and `fail_fast` is set. if self.fail_fast && any_test_failed { @@ -525,8 +521,6 @@ impl TestArgs { trace!(target: "forge::test", len=outcome.results.len(), %any_test_failed, "done with results"); - outcome.decoder = Some(decoder); - if let Some(gas_report) = gas_report { let finalized = gas_report.finalize(); shell::println(&finalized)?; diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 8bfb1477b..9fed56982 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -4,10 +4,9 @@ use crate::{result::SuiteResult, ContractRunner, TestFilter, TestOptions}; use alloy_json_abi::{Function, JsonAbi}; use alloy_primitives::{Address, Bytes, U256}; use eyre::Result; -use foundry_common::{get_contract_name, ContractData, ContractsByArtifact, TestFunctionExt}; -use foundry_compilers::{ - artifacts::Libraries, contracts::ArtifactContracts, Artifact, ArtifactId, ProjectCompileOutput, -}; +use foundry_common::{get_contract_name, ContractsByArtifact, TestFunctionExt}; +use foundry_compilers::{artifacts::Libraries, Artifact, ArtifactId, ProjectCompileOutput}; +use foundry_config::Config; use foundry_evm::{ backend::Backend, decode::RevertDecoder, executors::ExecutorBuilder, fork::CreateFork, inspectors::CheatsConfig, opts::EvmOpts, revm, @@ -16,6 +15,7 @@ use foundry_linking::{LinkOutput, Linker}; use rayon::prelude::*; use revm::primitives::SpecId; use std::{ + borrow::Borrow, collections::BTreeMap, fmt::Debug, path::Path, @@ -39,8 +39,6 @@ pub struct MultiContractRunner { /// Mapping of contract name to JsonAbi, creation bytecode and library bytecode which /// needs to be deployed & linked against pub contracts: DeployableContracts, - /// Compiled contracts by name that have an JsonAbi and runtime bytecode - pub known_contracts: ContractsByArtifact, /// The EVM instance used in the test runner pub evm_opts: EvmOpts, /// The configured evm @@ -51,12 +49,10 @@ pub struct MultiContractRunner { pub revert_decoder: RevertDecoder, /// The address which will be used as the `from` field in all EVM calls pub sender: Option
, - /// A map of contract names to absolute source file paths - pub source_paths: BTreeMap, /// The fork to use at launch pub fork: Option, - /// Additional cheatcode inspector related settings derived from the `Config` - pub cheats_config: Arc, + /// Project config. + pub config: Arc, /// Whether to collect coverage info pub coverage: bool, /// Whether to collect debug info @@ -65,6 +61,8 @@ pub struct MultiContractRunner { pub test_options: TestOptions, /// Whether to enable call isolation pub isolation: bool, + /// Output of the project compilation + pub output: ProjectCompileOutput, } impl MultiContractRunner { @@ -180,8 +178,18 @@ impl MultiContractRunner { let identifier = artifact_id.identifier(); let mut span_name = identifier.as_str(); - let mut cheats_config = self.cheats_config.as_ref().clone(); - cheats_config.running_version = Some(artifact_id.version.clone()); + let linker = + Linker::new(self.config.project_paths().root, self.output.artifact_ids().collect()); + let linked_contracts = linker.get_linked_artifacts(&contract.libraries).unwrap_or_default(); + let known_contracts = Arc::new(ContractsByArtifact::new(linked_contracts)); + + let cheats_config = CheatsConfig::new( + &self.config, + self.evm_opts.clone(), + Some(known_contracts.clone()), + None, + Some(artifact_id.version.clone()), + ); let executor = ExecutorBuilder::new() .inspectors(|stack| { @@ -212,7 +220,7 @@ impl MultiContractRunner { &self.revert_decoder, self.debug, ); - let r = runner.run_tests(filter, &self.test_options, Some(&self.known_contracts)); + let r = runner.run_tests(filter, &self.test_options, known_contracts); debug!(duration=?r.duration, "executed all tests in contract"); @@ -221,7 +229,7 @@ impl MultiContractRunner { } /// Builder used for instantiating the multi-contract runner -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug)] #[must_use = "builders do nothing unless you call `build` on them"] pub struct MultiContractRunnerBuilder { /// The address which will be used to deploy the initial contracts and send all @@ -233,8 +241,8 @@ pub struct MultiContractRunnerBuilder { pub evm_spec: Option, /// The fork to use at launch pub fork: Option, - /// Additional cheatcode inspector related settings derived from the `Config` - pub cheats_config: Option, + /// Project config. + pub config: Arc, /// Whether or not to collect coverage info pub coverage: bool, /// Whether or not to collect debug info @@ -246,6 +254,20 @@ pub struct MultiContractRunnerBuilder { } impl MultiContractRunnerBuilder { + pub fn new(config: Arc) -> Self { + Self { + config, + sender: Default::default(), + initial_balance: Default::default(), + evm_spec: Default::default(), + fork: Default::default(), + coverage: Default::default(), + debug: Default::default(), + isolation: Default::default(), + test_options: Default::default(), + } + } + pub fn sender(mut self, sender: Address) -> Self { self.sender = Some(sender); self @@ -266,11 +288,6 @@ impl MultiContractRunnerBuilder { self } - pub fn with_cheats_config(mut self, cheats_config: CheatsConfig) -> Self { - self.cheats_config = Some(cheats_config); - self - } - pub fn with_test_options(mut self, test_options: TestOptions) -> Self { self.test_options = Some(test_options); self @@ -300,107 +317,71 @@ impl MultiContractRunnerBuilder { env: revm::primitives::Env, evm_opts: EvmOpts, ) -> Result { - // This is just the contracts compiled, but we need to merge this with the read cached - // artifacts. - let contracts = output - .with_stripped_file_prefixes(root) - .into_artifacts() - .map(|(i, c)| (i, c.into_contract_bytecode())) - .collect::(); - - let source_paths = contracts - .iter() - .map(|(i, _)| (i.identifier(), root.join(&i.source).to_string_lossy().into())) - .collect::>(); + let output = output.with_stripped_file_prefixes(root); + let linker = Linker::new(root, output.artifact_ids().collect()); - let linker = Linker::new( - root, - contracts.iter().map(|(id, artifact)| (id.clone(), artifact)).collect(), - ); + // Build revert decoder from ABIs of all artifacts. + let abis = linker + .contracts + .iter() + .filter_map(|(_, contract)| contract.abi.as_ref().map(|abi| abi.borrow())); + let revert_decoder = RevertDecoder::new().with_abis(abis); // Create a mapping of name => (abi, deployment code, Vec) let mut deployable_contracts = DeployableContracts::default(); - let mut known_contracts = ContractsByArtifact::default(); - - for (id, contract) in contracts.iter() { + for (id, contract) in linker.contracts.iter() { let Some(abi) = contract.abi.as_ref() else { continue; }; - let name = id.name.clone(); - - let LinkOutput { libs_to_deploy, libraries } = - linker.link_with_nonce_or_address(Default::default(), evm_opts.sender, 1, id)?; - - let linked_contract = linker.link(id, &libraries)?; - - // get bytes if deployable, else add to known contracts and continue. - // interfaces and abstract contracts should be known to enable fuzzing of their ABI - // but they should not be deployable and their source code should be skipped by the - // debugger and linker. - let Some(bytecode) = linked_contract - .get_bytecode_bytes() - .map(|b| b.into_owned()) - .filter(|b| !b.is_empty()) - else { - known_contracts.insert( - id.clone(), - ContractData { - abi: abi.clone(), - bytecode: Bytes::new(), - deployed_bytecode: Bytes::new(), - name, - }, - ); - continue; - }; - - // if it's a test, add it to deployable contracts + // if it's a test, link it and add to deployable contracts if abi.constructor.as_ref().map(|c| c.inputs.is_empty()).unwrap_or(true) && abi.functions().any(|func| func.name.is_test() || func.name.is_invariant_test()) { + let LinkOutput { libs_to_deploy, libraries } = linker.link_with_nonce_or_address( + Default::default(), + evm_opts.sender, + 1, + id, + )?; + + let linked_contract = linker.link(id, &libraries)?; + + let Some(bytecode) = linked_contract + .get_bytecode_bytes() + .map(|b| b.into_owned()) + .filter(|b| !b.is_empty()) + else { + continue; + }; + deployable_contracts.insert( id.clone(), TestContract { - abi: abi.clone(), - bytecode: bytecode.clone(), + abi: abi.clone().into_owned(), + bytecode, libs_to_deploy, libraries, }, ); } - - if let Some(bytes) = linked_contract.get_deployed_bytecode_bytes() { - known_contracts.insert( - id.clone(), - ContractData { - abi: abi.clone(), - bytecode, - deployed_bytecode: bytes.into_owned(), - name, - }, - ); - } } - let revert_decoder = - RevertDecoder::new().with_abis(known_contracts.values().map(|c| &c.abi)); Ok(MultiContractRunner { contracts: deployable_contracts, - known_contracts, evm_opts, env, evm_spec: self.evm_spec.unwrap_or(SpecId::MERGE), sender: self.sender, revert_decoder, - source_paths, fork: self.fork, - cheats_config: self.cheats_config.unwrap_or_default().into(), + config: self.config, coverage: self.coverage, debug: self.debug, test_options: self.test_options.unwrap_or_default(), isolation: self.isolation, + output, }) } } diff --git a/crates/forge/src/result.rs b/crates/forge/src/result.rs index a50e59b95..09c3661dc 100644 --- a/crates/forge/src/result.rs +++ b/crates/forge/src/result.rs @@ -1,7 +1,9 @@ //! Test outcomes. use alloy_primitives::{Address, Log}; -use foundry_common::{evm::Breakpoints, get_contract_name, get_file_name, shell}; +use foundry_common::{ + evm::Breakpoints, get_contract_name, get_file_name, shell, ContractsByArtifact, +}; use foundry_compilers::artifacts::Libraries; use foundry_evm::{ coverage::HitMaps, @@ -14,6 +16,7 @@ use serde::{Deserialize, Serialize}; use std::{ collections::{BTreeMap, HashMap}, fmt::{self, Write}, + sync::Arc, time::Duration, }; use yansi::Paint; @@ -34,7 +37,7 @@ pub struct TestOutcome { /// This is `None` if traces and logs were not decoded. /// /// Note that `Address` fields only contain the last executed test case's data. - pub decoder: Option, + pub last_run_decoder: Option, /// The gas report, if requested. pub gas_report: Option, } @@ -42,7 +45,7 @@ pub struct TestOutcome { impl TestOutcome { /// Creates a new test outcome with the given results. pub fn new(results: BTreeMap, allow_failure: bool) -> Self { - Self { results, allow_failure, decoder: None, gas_report: None } + Self { results, allow_failure, last_run_decoder: None, gas_report: None } } /// Creates a new empty test outcome. @@ -196,6 +199,9 @@ pub struct SuiteResult { pub warnings: Vec, /// Libraries used to link test contract. pub libraries: Libraries, + /// Contracts linked with correct libraries. + #[serde(skip)] + pub known_contracts: Arc, } impl SuiteResult { @@ -204,8 +210,9 @@ impl SuiteResult { test_results: BTreeMap, warnings: Vec, libraries: Libraries, + known_contracts: Arc, ) -> Self { - Self { duration, test_results, warnings, libraries } + Self { duration, test_results, warnings, libraries, known_contracts } } /// Returns an iterator over all individual succeeding tests and their names. diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 26af21787..ad335e767 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -30,6 +30,7 @@ use rayon::prelude::*; use std::{ borrow::Cow, collections::{BTreeMap, HashMap}, + sync::Arc, time::Instant, }; @@ -178,7 +179,7 @@ impl<'a> ContractRunner<'a> { mut self, filter: &dyn TestFilter, test_options: &TestOptions, - known_contracts: Option<&ContractsByArtifact>, + known_contracts: Arc, ) -> SuiteResult { info!("starting tests"); let start = Instant::now(); @@ -207,6 +208,7 @@ impl<'a> ContractRunner<'a> { .into(), warnings, self.contract.libraries.clone(), + known_contracts, ) } @@ -244,6 +246,7 @@ impl<'a> ContractRunner<'a> { .into(), warnings, self.contract.libraries.clone(), + known_contracts, ) } @@ -265,7 +268,7 @@ impl<'a> ContractRunner<'a> { ); let identified_contracts = - has_invariants.then(|| load_contracts(setup.traces.clone(), known_contracts)); + has_invariants.then(|| load_contracts(setup.traces.clone(), &known_contracts)); let test_results = functions .par_iter() .map(|&func| { @@ -281,7 +284,7 @@ impl<'a> ContractRunner<'a> { setup, *invariant_config, func, - known_contracts, + &known_contracts, identified_contracts.as_ref().unwrap(), ) } else if func.is_fuzz_test() { @@ -299,8 +302,13 @@ impl<'a> ContractRunner<'a> { .collect::>(); let duration = start.elapsed(); - let suite_result = - SuiteResult::new(duration, test_results, warnings, self.contract.libraries.clone()); + let suite_result = SuiteResult::new( + duration, + test_results, + warnings, + self.contract.libraries.clone(), + known_contracts, + ); info!( duration=?suite_result.duration, "done. {}/{} successful", @@ -430,12 +438,10 @@ impl<'a> ContractRunner<'a> { setup: TestSetup, invariant_config: InvariantConfig, func: &Function, - known_contracts: Option<&ContractsByArtifact>, + known_contracts: &ContractsByArtifact, identified_contracts: &ContractsByAddress, ) -> TestResult { trace!(target: "forge::test::fuzz", "executing invariant test for {:?}", func.name); - let empty = ContractsByArtifact::default(); - let project_contracts = known_contracts.unwrap_or(&empty); let TestSetup { address, logs, traces, labeled_addresses, coverage, .. } = setup; // First, run the test normally to see if it needs to be skipped. @@ -466,7 +472,7 @@ impl<'a> ContractRunner<'a> { runner, invariant_config, identified_contracts, - project_contracts, + known_contracts, ); let invariant_contract = diff --git a/crates/forge/tests/it/repros.rs b/crates/forge/tests/it/repros.rs index b1f8efc49..6f9576765 100644 --- a/crates/forge/tests/it/repros.rs +++ b/crates/forge/tests/it/repros.rs @@ -1,5 +1,7 @@ //! Regression tests for previous issues. +use std::sync::Arc; + use crate::{ config::*, test_helpers::{ForgeTestData, TEST_DATA_DEFAULT}, @@ -8,7 +10,7 @@ use alloy_dyn_abi::{DecodedEvent, DynSolValue, EventExt}; use alloy_json_abi::Event; use alloy_primitives::{address, Address, U256}; use forge::result::TestStatus; -use foundry_config::{fs_permissions::PathPermission, FsPermissions}; +use foundry_config::{fs_permissions::PathPermission, Config, FsPermissions}; use foundry_evm::{ constants::HARDHAT_CONSOLE_ADDRESS, traces::{CallKind, CallTraceDecoder, DecodedCallData, TraceKind}, @@ -290,10 +292,12 @@ test_repro!(6538); // https://github.com/foundry-rs/foundry/issues/6554 test_repro!(6554; |config| { - let mut cheats_config = config.runner.cheats_config.as_ref().clone(); - let path = cheats_config.root.join("out/default/Issue6554.t.sol"); - cheats_config.fs_permissions.add(PathPermission::read_write(path)); - config.runner.cheats_config = std::sync::Arc::new(cheats_config); + let path = config.runner.config.__root.0.join("out/default/Issue6554.t.sol"); + + let mut prj_config = Config::clone(&config.runner.config); + prj_config.fs_permissions.add(PathPermission::read_write(path)); + config.runner.config = Arc::new(prj_config); + }); // https://github.com/foundry-rs/foundry/issues/6759 @@ -307,16 +311,18 @@ test_repro!(6616); // https://github.com/foundry-rs/foundry/issues/5529 test_repro!(5529; |config| { - let mut cheats_config = config.runner.cheats_config.as_ref().clone(); - cheats_config.always_use_create_2_factory = true; - config.runner.cheats_config = std::sync::Arc::new(cheats_config); + let mut prj_config = Config::clone(&config.runner.config); + prj_config.always_use_create_2_factory = true; + config.runner.evm_opts.always_use_create_2_factory = true; + config.runner.config = Arc::new(prj_config); }); // https://github.com/foundry-rs/foundry/issues/6634 test_repro!(6634; |config| { - let mut cheats_config = config.runner.cheats_config.as_ref().clone(); - cheats_config.always_use_create_2_factory = true; - config.runner.cheats_config = std::sync::Arc::new(cheats_config); + let mut prj_config = Config::clone(&config.runner.config); + prj_config.always_use_create_2_factory = true; + config.runner.evm_opts.always_use_create_2_factory = true; + config.runner.config = Arc::new(prj_config); }); test_repro!(7481); diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index 204df223b..f65a3c116 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -2,8 +2,8 @@ use alloy_primitives::U256; use forge::{ - inspectors::CheatsConfig, revm::primitives::SpecId, MultiContractRunner, - MultiContractRunnerBuilder, TestOptions, TestOptionsBuilder, + revm::primitives::SpecId, MultiContractRunner, MultiContractRunnerBuilder, TestOptions, + TestOptionsBuilder, }; use foundry_compilers::{ artifacts::{Libraries, Settings}, @@ -23,6 +23,7 @@ use std::{ env, fmt, io::Write, path::{Path, PathBuf}, + sync::Arc, }; pub const RE_PATH_SEPARATOR: &str = "/"; @@ -187,7 +188,7 @@ impl ForgeTestData { /// Builds a base runner pub fn base_runner(&self) -> MultiContractRunnerBuilder { init_tracing(); - let mut runner = MultiContractRunnerBuilder::default() + 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() { @@ -222,17 +223,14 @@ impl ForgeTestData { let env = opts.local_evm_env(); let output = self.output.clone(); - let artifact_ids = output.artifact_ids().map(|(id, _)| id).collect(); - self.base_runner() - .with_cheats_config(CheatsConfig::new( - &config, - opts.clone(), - Some(artifact_ids), - None, - None, - )) + + let sender = config.sender; + + let mut builder = self.base_runner(); + builder.config = Arc::new(config); + builder .enable_isolation(opts.isolate) - .sender(config.sender) + .sender(sender) .with_test_options(self.test_opts.clone()) .build(root, output, env, opts.clone()) .unwrap() diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index ea00dc592..bdc940097 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -31,9 +31,6 @@ pub struct BuildData { pub output: ProjectCompileOutput, /// Id of target contract artifact. pub target: ArtifactId, - /// Artifact ids of the contracts. Passed to cheatcodes to enable usage of - /// `vm.getDeployedCode`. - pub artifact_ids: Vec, } impl BuildData { @@ -99,21 +96,8 @@ impl LinkedBuildData { &link_output.libraries, )?; - let known_contracts = ContractsByArtifact( - build_data - .get_linker() - .get_linked_artifacts(&link_output.libraries)? - .into_iter() - .filter_map(|(id, contract)| { - let name = id.name.clone(); - let bytecode = contract.bytecode.and_then(|b| b.into_bytes())?; - let deployed_bytecode = - contract.deployed_bytecode.and_then(|b| b.into_bytes())?; - let abi = contract.abi?; - - Some((id, ContractData { name, abi, bytecode, deployed_bytecode })) - }) - .collect(), + let known_contracts = ContractsByArtifact::new( + build_data.get_linker().get_linked_artifacts(&link_output.libraries)?, ); Ok(Self { @@ -169,8 +153,6 @@ impl PreprocessedState { let mut target_id: Option = None; - let artifact_ids = output.artifact_ids().map(|(id, _)| id).collect(); - // Find target artfifact id by name and path in compilation artifacts. for (id, contract) in output.artifact_ids().filter(|(id, _)| id.source == target_path) { if let Some(name) = &target_name { @@ -207,12 +189,7 @@ impl PreprocessedState { args, script_config, script_wallets, - build_data: BuildData { - output, - target, - project_root: project.root().clone(), - artifact_ids, - }, + build_data: BuildData { output, target, project_root: project.root().clone() }, }) } } diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index 9e7695591..1239cda1f 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -94,7 +94,7 @@ impl PreExecutionState { let mut runner = self .script_config .get_runner_with_cheatcodes( - self.build_data.build_data.artifact_ids.clone(), + self.build_data.known_contracts.clone(), self.script_wallets.clone(), self.args.debug, self.build_data.build_data.target.clone(), diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 17f70387a..dc4e05e1a 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -45,7 +45,7 @@ use foundry_evm::{ }; use foundry_wallets::MultiWalletOpts; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; +use std::{collections::HashMap, sync::Arc}; use yansi::Paint; mod broadcast; @@ -545,17 +545,17 @@ impl ScriptConfig { async fn get_runner_with_cheatcodes( &mut self, - artifact_ids: Vec, + known_contracts: ContractsByArtifact, script_wallets: ScriptWallets, debug: bool, target: ArtifactId, ) -> Result { - self._get_runner(Some((artifact_ids, script_wallets, target)), debug).await + self._get_runner(Some((known_contracts, script_wallets, target)), debug).await } async fn _get_runner( &mut self, - cheats_data: Option<(Vec, ScriptWallets, ArtifactId)>, + cheats_data: Option<(ContractsByArtifact, ScriptWallets, ArtifactId)>, debug: bool, ) -> Result { trace!("preparing script runner"); @@ -584,7 +584,7 @@ impl ScriptConfig { .spec(self.config.evm_spec_id()) .gas_limit(self.evm_opts.gas_limit()); - if let Some((artifact_ids, script_wallets, target)) = cheats_data { + if let Some((known_contracts, script_wallets, target)) = cheats_data { builder = builder.inspectors(|stack| { stack .debug(debug) @@ -592,7 +592,7 @@ impl ScriptConfig { CheatsConfig::new( &self.config, self.evm_opts.clone(), - Some(artifact_ids), + Some(Arc::new(known_contracts)), Some(script_wallets), Some(target.version), ) From 63072bec5225654001a83fac44f789bd958ce491 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 18 Apr 2024 00:38:32 +0400 Subject: [PATCH 184/430] refactor: inject call to CREATE2 factory through custom revm handler (#7653) * wip * wip * add docs * clippy * update doc * simplify logic * review fixes * doc * review fixes * fix --- crates/anvil/src/eth/backend/mem/inspector.rs | 5 +- crates/anvil/src/eth/backend/mem/mod.rs | 3 +- crates/cheatcodes/src/inspector.rs | 171 ++++-------------- crates/evm/core/src/backend/cow.rs | 7 +- crates/evm/core/src/backend/mod.rs | 11 +- crates/evm/core/src/lib.rs | 24 +++ crates/evm/core/src/utils.rs | 148 ++++++++++++++- crates/evm/evm/src/inspectors/stack.rs | 18 ++ crates/evm/evm/src/lib.rs | 2 +- 9 files changed, 231 insertions(+), 158 deletions(-) diff --git a/crates/anvil/src/eth/backend/mem/inspector.rs b/crates/anvil/src/eth/backend/mem/inspector.rs index 6e38ac61d..6ea16a340 100644 --- a/crates/anvil/src/eth/backend/mem/inspector.rs +++ b/crates/anvil/src/eth/backend/mem/inspector.rs @@ -6,13 +6,14 @@ use foundry_evm::{ call_inspectors, decode::decode_console_logs, inspectors::{LogCollector, TracingInspector}, - revm, revm::{ + self, interpreter::{CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter}, primitives::U256, EvmContext, }, traces::TracingInspectorConfig, + InspectorExt, }; /// The [`revm::Inspector`] used when transacting in the evm @@ -136,6 +137,8 @@ impl revm::Inspector for Inspector { } } +impl InspectorExt for Inspector {} + /// Prints all the logs #[inline] pub fn print_logs(logs: &[Log]) { diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 86d946f2d..3b5fdc73f 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -76,6 +76,7 @@ use foundry_evm::{ }, }, utils::new_evm_with_inspector_ref, + InspectorExt, }; use futures::channel::mpsc::{unbounded, UnboundedSender}; use parking_lot::{Mutex, RwLock}; @@ -819,7 +820,7 @@ impl Backend { ) -> revm::Evm<'_, I, WrapDatabaseRef> where DB: revm::DatabaseRef, - I: revm::Inspector>, + I: InspectorExt>, { let mut evm = new_evm_with_inspector_ref(db, env, inspector); if let Some(ref factory) = self.precompile_factory { diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index ed6aa5cf4..64d2e70a4 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -21,8 +21,9 @@ use alloy_sol_types::{SolInterface, SolValue}; use foundry_common::{evm::Breakpoints, provider::alloy::RpcUrl, SELECTOR_LEN}; use foundry_evm_core::{ abi::Vm::stopExpectSafeMemoryCall, - backend::{DatabaseError, DatabaseExt, RevertDiagnostic}, - constants::{CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, HARDHAT_CONSOLE_ADDRESS}, + backend::{DatabaseExt, RevertDiagnostic}, + constants::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS}, + InspectorExt, }; use itertools::Itertools; use revm::{ @@ -1327,22 +1328,19 @@ impl Inspector for Cheatcodes { ecx.env.tx.caller = broadcast.new_origin; if ecx.journaled_state.depth() == broadcast.depth { - let (bytecode, to, nonce) = process_broadcast_create( - broadcast.new_origin, - call.init_code.clone(), - ecx, - call, - ); + call.caller = broadcast.new_origin; let is_fixed_gas_limit = check_if_fixed_gas_limit(ecx, call.gas_limit); + let account = &ecx.journaled_state.state()[&broadcast.new_origin]; + self.broadcastable_transactions.push_back(BroadcastableTransaction { rpc: ecx.db.active_fork_url(), transaction: TransactionRequest { from: Some(broadcast.new_origin), - to, + to: None, value: Some(call.value), - input: TransactionInput::new(bytecode), - nonce: Some(nonce), + input: TransactionInput::new(call.init_code.clone()), + nonce: Some(account.info.nonce), gas: if is_fixed_gas_limit { Some(call.gas_limit as u128) } else { @@ -1351,6 +1349,7 @@ impl Inspector for Cheatcodes { ..Default::default() }, }); + let kind = match call.scheme { CreateScheme::Create => "create", CreateScheme::Create2 { .. } => "create2", @@ -1360,29 +1359,6 @@ impl Inspector for Cheatcodes { } } - // Apply the Create2 deployer - if self.broadcast.is_some() || self.config.always_use_create_2_factory { - match apply_create2_deployer( - ecx, - call, - self.prank.as_ref(), - self.broadcast.as_ref(), - self.recorded_account_diffs_stack.as_mut(), - ) { - Ok(_) => {} - Err(err) => { - return Some(CreateOutcome { - result: InterpreterResult { - result: InstructionResult::Revert, - output: Error::encode(err), - gas, - }, - address: None, - }) - } - }; - } - // allow cheatcodes from the address of the new contract // Compute the address *after* any possible broadcast updates, so it's based on the updated // call inputs @@ -1526,6 +1502,29 @@ impl Inspector for Cheatcodes { } } +impl InspectorExt for Cheatcodes { + fn should_use_create2_factory( + &mut self, + ecx: &mut EvmContext, + inputs: &mut CreateInputs, + ) -> bool { + if let CreateScheme::Create2 { .. } = inputs.scheme { + let target_depth = if let Some(prank) = &self.prank { + prank.depth + } else if let Some(broadcast) = &self.broadcast { + broadcast.depth + } else { + 1 + }; + + ecx.journaled_state.depth() == target_depth && + (self.broadcast.is_some() || self.config.always_use_create_2_factory) + } else { + false + } + } +} + /// Helper that expands memory, stores a revert string pertaining to a disallowed memory write, /// and sets the return range to the revert string's location in memory. /// @@ -1554,110 +1553,6 @@ fn disallowed_mem_write( }; } -/// Applies the default CREATE2 deployer for contract creation. -/// -/// This function is invoked during the contract creation process and updates the caller of the -/// contract creation transaction to be the `DEFAULT_CREATE2_DEPLOYER` if the `CreateScheme` is -/// `Create2` and the current execution depth matches the depth at which the `prank` or `broadcast` -/// was started, or the default depth of 1 if no prank or broadcast is currently active. -/// -/// Returns a `DatabaseError::MissingCreate2Deployer` if the `DEFAULT_CREATE2_DEPLOYER` account is -/// not found or if it does not have any associated bytecode. -fn apply_create2_deployer( - ecx: &mut InnerEvmContext, - call: &mut CreateInputs, - prank: Option<&Prank>, - broadcast: Option<&Broadcast>, - diffs_stack: Option<&mut Vec>>, -) -> Result<(), DB::Error> { - if let CreateScheme::Create2 { salt } = call.scheme { - let mut base_depth = 1; - if let Some(prank) = &prank { - base_depth = prank.depth; - } else if let Some(broadcast) = &broadcast { - base_depth = broadcast.depth; - } - - // If the create scheme is Create2 and the depth equals the broadcast/prank/default - // depth, then use the default create2 factory as the deployer - if ecx.journaled_state.depth() == base_depth { - // Record the call to the create2 factory in the state diff - if let Some(recorded_account_diffs_stack) = diffs_stack { - let calldata = [&salt.to_be_bytes::<32>()[..], &call.init_code[..]].concat(); - recorded_account_diffs_stack.push(vec![AccountAccess { - chainInfo: crate::Vm::ChainInfo { - forkId: ecx.db.active_fork_id().unwrap_or_default(), - chainId: U256::from(ecx.env.cfg.chain_id), - }, - accessor: call.caller, - account: DEFAULT_CREATE2_DEPLOYER, - kind: crate::Vm::AccountAccessKind::Call, - initialized: true, - oldBalance: U256::ZERO, // updated on create_end - newBalance: U256::ZERO, // updated on create_end - value: call.value, - data: calldata.into(), - reverted: false, - deployedCode: Bytes::new(), // updated on create_end - storageAccesses: vec![], // updated on create_end - depth: ecx.journaled_state.depth(), - }]) - } - - // Sanity checks for our CREATE2 deployer - // TODO: use ecx.load_account - let info = - &ecx.journaled_state.load_account(DEFAULT_CREATE2_DEPLOYER, &mut ecx.db)?.0.info; - match &info.code { - Some(code) if code.is_empty() => return Err(DatabaseError::MissingCreate2Deployer), - None if ecx.db.code_by_hash(info.code_hash)?.is_empty() => { - return Err(DatabaseError::MissingCreate2Deployer) - } - _ => {} - } - - call.caller = DEFAULT_CREATE2_DEPLOYER; - } - } - Ok(()) -} - -/// Processes the creation of a new contract when broadcasting, preparing the necessary data for the -/// transaction to deploy the contract. -/// -/// Returns the transaction calldata and the target address. -/// -/// If the CreateScheme is Create, then this function returns the input bytecode without -/// modification and no address since it will be filled in later. If the CreateScheme is Create2, -/// then this function returns the calldata for the call to the create2 deployer which must be the -/// salt and init code concatenated. -fn process_broadcast_create( - broadcast_sender: Address, - bytecode: Bytes, - ecx: &mut InnerEvmContext, - call: &mut CreateInputs, -) -> (Bytes, Option
, u64) { - call.caller = broadcast_sender; - match call.scheme { - CreateScheme::Create => { - (bytecode, None, ecx.journaled_state.account(broadcast_sender).info.nonce) - } - CreateScheme::Create2 { salt } => { - // We have to increment the nonce of the user address, since this create2 will be done - // by the create2_deployer - let account = ecx.journaled_state.state().get_mut(&broadcast_sender).unwrap(); - let prev = account.info.nonce; - // Touch account to ensure that incremented nonce is committed - account.mark_touch(); - account.info.nonce += 1; - debug!(target: "cheatcodes", address=%broadcast_sender, nonce=prev+1, prev, "incremented nonce in create2"); - // Proxy deployer requires the data to be `salt ++ init_code` - let calldata = [&salt.to_be_bytes::<32>()[..], &bytecode[..]].concat(); - (calldata.into(), Some(DEFAULT_CREATE2_DEPLOYER), prev) - } - } -} - // Determines if the gas limit on a given call was manually set in the script and should therefore // not be overwritten by later estimations fn check_if_fixed_gas_limit( diff --git a/crates/evm/core/src/backend/cow.rs b/crates/evm/core/src/backend/cow.rs index 59410541a..9b53fb4ee 100644 --- a/crates/evm/core/src/backend/cow.rs +++ b/crates/evm/core/src/backend/cow.rs @@ -6,6 +6,7 @@ use crate::{ RevertSnapshotAction, }, fork::{CreateFork, ForkId}, + InspectorExt, }; use alloy_genesis::GenesisAccount; use alloy_primitives::{Address, B256, U256}; @@ -16,7 +17,7 @@ use revm::{ Account, AccountInfo, Bytecode, Env, EnvWithHandlerCfg, HashMap as Map, ResultAndState, SpecId, }, - Database, DatabaseCommit, Inspector, JournaledState, + Database, DatabaseCommit, JournaledState, }; use std::{borrow::Cow, collections::BTreeMap}; @@ -58,7 +59,7 @@ impl<'a> CowBackend<'a> { /// /// Note: in case there are any cheatcodes executed that modify the environment, this will /// update the given `env` with the new values. - pub fn inspect<'b, I: Inspector<&'b mut Self>>( + pub fn inspect<'b, I: InspectorExt<&'b mut Self>>( &'b mut self, env: &mut EnvWithHandlerCfg, inspector: I, @@ -176,7 +177,7 @@ impl<'a> DatabaseExt for CowBackend<'a> { self.backend_mut(env).roll_fork_to_transaction(id, transaction, env, journaled_state) } - fn transact>( + fn transact>( &mut self, id: Option, transaction: B256, diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index e924d19ef..90e27444b 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -5,6 +5,7 @@ use crate::{ fork::{CreateFork, ForkId, MultiFork, SharedBackend}, snapshot::Snapshots, utils::configure_tx_env, + InspectorExt, }; use alloy_genesis::GenesisAccount; use alloy_primitives::{b256, keccak256, Address, B256, U256}; @@ -19,7 +20,7 @@ use revm::{ Account, AccountInfo, Bytecode, CreateScheme, Env, EnvWithHandlerCfg, HashMap as Map, Log, ResultAndState, SpecId, State, StorageSlot, TransactTo, KECCAK_EMPTY, }, - Database, DatabaseCommit, Inspector, JournaledState, + Database, DatabaseCommit, JournaledState, }; use std::{ collections::{BTreeMap, HashMap, HashSet}, @@ -188,7 +189,7 @@ pub trait DatabaseExt: Database { ) -> eyre::Result<()>; /// Fetches the given transaction for the fork and executes it, committing the state in the DB - fn transact>( + fn transact>( &mut self, id: Option, transaction: B256, @@ -780,7 +781,7 @@ impl Backend { /// /// Note: in case there are any cheatcodes executed that modify the environment, this will /// update the given `env` with the new values. - pub fn inspect<'a, I: Inspector<&'a mut Self>>( + pub fn inspect<'a, I: InspectorExt<&'a mut Self>>( &'a mut self, env: &mut EnvWithHandlerCfg, inspector: I, @@ -1228,7 +1229,7 @@ impl DatabaseExt for Backend { Ok(()) } - fn transact>( + fn transact>( &mut self, maybe_id: Option, transaction: B256, @@ -1868,7 +1869,7 @@ fn update_env_block(env: &mut Env, fork_block: u64, block: &Block) { /// Executes the given transaction and commits state changes to the database _and_ the journaled /// state, with an optional inspector -fn commit_transaction>( +fn commit_transaction>( tx: WithOtherFields, mut env: EnvWithHandlerCfg, journaled_state: &mut JournaledState, diff --git a/crates/evm/core/src/lib.rs b/crates/evm/core/src/lib.rs index 9d26c9421..ef28ad922 100644 --- a/crates/evm/core/src/lib.rs +++ b/crates/evm/core/src/lib.rs @@ -4,6 +4,10 @@ #![warn(unused_crate_dependencies)] +use auto_impl::auto_impl; +use revm::{inspectors::NoOpInspector, interpreter::CreateInputs, Database, EvmContext, Inspector}; +use revm_inspectors::access_list::AccessListInspector; + #[macro_use] extern crate tracing; @@ -19,3 +23,23 @@ pub mod opcodes; pub mod opts; pub mod snapshot; pub mod utils; + +/// An extension trait that allows us to add additional hooks to Inspector for later use in +/// handlers. +#[auto_impl(&mut, Box)] +pub trait InspectorExt: Inspector { + /// Determines whether the `DEFAULT_CREATE2_DEPLOYER` should be used for a CREATE2 frame. + /// + /// If this function returns true, we'll replace CREATE2 frame with a CALL frame to CREATE2 + /// factory. + fn should_use_create2_factory( + &mut self, + _context: &mut EvmContext, + _inputs: &mut CreateInputs, + ) -> bool { + false + } +} + +impl InspectorExt for NoOpInspector {} +impl InspectorExt for AccessListInspector {} diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index ee8a8a4d4..d1e73a1fa 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -1,17 +1,23 @@ +pub use crate::ic::*; +use crate::{constants::DEFAULT_CREATE2_DEPLOYER, InspectorExt}; use alloy_json_abi::{Function, JsonAbi}; -use alloy_primitives::{FixedBytes, U256}; +use alloy_primitives::{Address, FixedBytes, U256}; use alloy_rpc_types::{Block, Transaction}; use eyre::ContextCompat; +pub use foundry_compilers::utils::RuntimeOrHandle; use foundry_config::NamedChain; +pub use revm::primitives::State as StateChangeset; use revm::{ db::WrapDatabaseRef, - primitives::{SpecId, TransactTo}, + handler::register::EvmHandler, + interpreter::{ + return_ok, CallContext, CallInputs, CallScheme, CreateInputs, CreateOutcome, Gas, + InstructionResult, InterpreterResult, Transfer, + }, + primitives::{CreateScheme, EVMError, SpecId, TransactTo, KECCAK_EMPTY}, + FrameOrResult, FrameResult, }; - -pub use foundry_compilers::utils::RuntimeOrHandle; -pub use revm::primitives::State as StateChangeset; - -pub use crate::ic::*; +use std::{cell::RefCell, rc::Rc, sync::Arc}; /// Depending on the configured chain id and block number this should apply any specific changes /// @@ -99,6 +105,129 @@ pub fn gas_used(spec: SpecId, spent: u64, refunded: u64) -> u64 { spent - (refunded).min(spent / refund_quotient) } +fn get_create2_factory_call_inputs(salt: U256, inputs: CreateInputs) -> CallInputs { + let calldata = [&salt.to_be_bytes::<32>()[..], &inputs.init_code[..]].concat(); + CallInputs { + contract: DEFAULT_CREATE2_DEPLOYER, + transfer: Transfer { + source: inputs.caller, + target: DEFAULT_CREATE2_DEPLOYER, + value: inputs.value, + }, + input: calldata.into(), + gas_limit: inputs.gas_limit, + context: CallContext { + caller: inputs.caller, + address: DEFAULT_CREATE2_DEPLOYER, + code_address: DEFAULT_CREATE2_DEPLOYER, + apparent_value: inputs.value, + scheme: CallScheme::Call, + }, + is_static: false, + return_memory_offset: 0..0, + } +} + +/// Used for routing certain CREATE2 invocations through [DEFAULT_CREATE2_DEPLOYER]. +/// +/// Overrides create hook with CALL frame if [InspectorExt::should_use_create2_factory] returns +/// true. Keeps track of overriden frames and handles outcome in the overriden insert_call_outcome +/// hook by inserting decoded address directly into interpreter. +/// +/// Should be installed after [revm::inspector_handle_register] and before any other registers. +pub fn create2_handler_register>( + handler: &mut EvmHandler<'_, I, DB>, +) { + let create2_overrides = Rc::>>::new(RefCell::new(Vec::new())); + + let create2_overrides_inner = create2_overrides.clone(); + let old_handle = handler.execution.create.clone(); + handler.execution.create = + Arc::new(move |ctx, mut inputs| -> Result> { + let CreateScheme::Create2 { salt } = inputs.scheme else { + return old_handle(ctx, inputs); + }; + if !ctx.external.should_use_create2_factory(&mut ctx.evm, &mut inputs) { + return old_handle(ctx, inputs); + } + + // Sanity check that CREATE2 deployer exists. + let code_hash = ctx.evm.load_account(DEFAULT_CREATE2_DEPLOYER)?.0.info.code_hash; + if code_hash == KECCAK_EMPTY { + return Ok(FrameOrResult::Result(FrameResult::Create(CreateOutcome { + result: InterpreterResult { + result: InstructionResult::Revert, + output: "missing CREATE2 deployer".into(), + gas: Gas::new(inputs.gas_limit), + }, + address: None, + }))) + } + + // Generate call inputs for CREATE2 factory. + let mut call_inputs = get_create2_factory_call_inputs(salt, *inputs); + + // Call inspector to change input or return outcome. + if let Some(outcome) = ctx.external.call(&mut ctx.evm, &mut call_inputs) { + create2_overrides_inner + .borrow_mut() + .push((ctx.evm.journaled_state.depth(), call_inputs.clone())); + return Ok(FrameOrResult::Result(FrameResult::Call(outcome))); + } + + // Push data about current override to the stack. + create2_overrides_inner + .borrow_mut() + .push((ctx.evm.journaled_state.depth(), call_inputs.clone())); + + let mut frame_or_result = ctx.evm.make_call_frame(&call_inputs); + + if let Ok(FrameOrResult::Frame(frame)) = &mut frame_or_result { + ctx.external + .initialize_interp(&mut frame.frame_data_mut().interpreter, &mut ctx.evm) + } + frame_or_result + }); + + let create2_overrides_inner = create2_overrides.clone(); + let old_handle = handler.execution.insert_call_outcome.clone(); + + handler.execution.insert_call_outcome = + Arc::new(move |ctx, frame, shared_memory, mut outcome| { + // If we are on the depth of the latest override, handle the outcome. + if create2_overrides_inner + .borrow() + .last() + .map_or(false, |(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); + + // Decode address from output. + let address = match outcome.instruction_result() { + return_ok!() => Address::try_from(outcome.output().as_ref()) + .map_err(|_| { + outcome.result = InterpreterResult { + result: InstructionResult::Revert, + output: "invalid CREATE2 factory output".into(), + gas: Gas::new(call_inputs.gas_limit), + }; + }) + .ok(), + _ => None, + }; + frame + .frame_data_mut() + .interpreter + .insert_create_outcome(CreateOutcome { address, result: outcome.result }); + + Ok(()) + } else { + old_handle(ctx, frame, shared_memory, outcome) + } + }); +} + /// Creates a new EVM with the given inspector. pub fn new_evm_with_inspector<'a, DB, I>( db: DB, @@ -107,7 +236,7 @@ pub fn new_evm_with_inspector<'a, DB, I>( ) -> revm::Evm<'a, I, DB> where DB: revm::Database, - I: revm::Inspector, + I: InspectorExt, { // NOTE: We could use `revm::Evm::builder()` here, but on the current patch it has some // performance issues. @@ -115,6 +244,7 @@ where let context = revm::Context::new(revm::EvmContext::new_with_env(db, env), inspector); let mut handler = revm::Handler::new(handler_cfg); handler.append_handler_register_plain(revm::inspector_handle_register); + handler.append_handler_register_plain(create2_handler_register); revm::Evm::new(context, handler) } @@ -126,7 +256,7 @@ pub fn new_evm_with_inspector_ref<'a, DB, I>( ) -> revm::Evm<'a, I, WrapDatabaseRef> where DB: revm::DatabaseRef, - I: revm::Inspector>, + I: InspectorExt>, { new_evm_with_inspector(WrapDatabaseRef(db), env, inspector) } diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index a38bf0eee..627ca2438 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -6,6 +6,7 @@ use alloy_primitives::{Address, Bytes, Log, U256}; use foundry_evm_core::{ backend::{update_state, DatabaseExt}, debug::DebugArena, + InspectorExt, }; use foundry_evm_coverage::HitMaps; use foundry_evm_traces::CallTraceArena; @@ -764,3 +765,20 @@ impl Inspector<&mut DB> for InspectorStack { )); } } + +impl InspectorExt<&mut DB> for InspectorStack { + fn should_use_create2_factory( + &mut self, + ecx: &mut EvmContext<&mut DB>, + inputs: &mut CreateInputs, + ) -> bool { + call_inspectors_adjust_depth!( + [&mut self.cheatcodes], + |inspector| { inspector.should_use_create2_factory(ecx, inputs).then_some(true) }, + self, + ecx + ); + + false + } +} diff --git a/crates/evm/evm/src/lib.rs b/crates/evm/evm/src/lib.rs index aa5386b3e..a699b6bf8 100644 --- a/crates/evm/evm/src/lib.rs +++ b/crates/evm/evm/src/lib.rs @@ -10,7 +10,7 @@ extern crate tracing; pub mod executors; pub mod inspectors; -pub use foundry_evm_core::{backend, constants, debug, decode, fork, opts, utils}; +pub use foundry_evm_core::{backend, constants, debug, decode, fork, opts, utils, InspectorExt}; pub use foundry_evm_coverage as coverage; pub use foundry_evm_fuzz as fuzz; pub use foundry_evm_traces as traces; From e5b8fc98eed13e0ffa93c41c712a16054c044b33 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Thu, 18 Apr 2024 11:37:20 +0300 Subject: [PATCH 185/430] chore: fix test clippy (#7700) --- crates/cheatcodes/src/fs.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index cecfc8822..a729d53bb 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -545,7 +545,6 @@ fn prompt( mod tests { use super::*; use crate::CheatsConfig; - use alloy_primitives::Bytes; use std::sync::Arc; fn cheats() -> Cheatcodes { From 1c59fcc03b08d6672d49209da7854fd68bf17b57 Mon Sep 17 00:00:00 2001 From: William Cheung Date: Thu, 18 Apr 2024 11:18:42 -0400 Subject: [PATCH 186/430] feat(forge): clone verified contracts as a foundry project (#7576) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat:support forge clone * feat: update configuration based on metadate from EtherScan 👷 * doc: update the documenation for * add dump_sources function * fix: add existing remapping into remappings.txt * apply remapping on libraries * add tests * feat: update remappings in config file * add two more test cases * fix library remapping bug * test: add e2e test cases for forge clone * test: fix rate limit issue for forge clone tests * feat: disable git by default for forge clone * dump clone.toml metadata in cloned projects * add storage layout to the clone metadata * dump clone metadata in a hidden, readonly, compact json file * add constructor arguments in clone metadata * fix: typo field name * fix: bug in remapping * fix: remapping disorder for verified foundry contracts * fix clippy and fmt warnings * fmt in the foundry way * chore: restore files to be consistent with foundry fmt style * cherry pick bug fixes from tweak branch * fix: remove the dependency of Etherscan in tests * chore: move mockall to dev dependency, only mock in test build * feat: use camelCase in .clone.meta * doc: add comments to explain forge clone * fix: import file not found error * chore: remove uncessary dependency fix: fix a foundry config bug regarding generating project_paths * chore: refactor the test code a bit * Update crates/forge/bin/cmd/clone.rs Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * Update crates/forge/bin/cmd/clone.rs Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * chore: change string as address in CloneArg * test: add one basic forgetest for clone * feat: dump remappings into remappings.txt by default chore: break a large function into multiple small one to improve readability * feat: improve UX and make --quiet true quiet * test: add one more forgetest! for clone * fix minor issues suggested in code review * fix: incorrect assertion for project paths * test: add default etherscan api keys and remove sleep in tests * test: add more etherscan api keys * fix: revoke the unnecessary changes in config.rs * feat: bump foundry-compilers to 0.3.16 * chore: refactor code and clean some comments * fix: path disorder on windows * touchups --------- Co-authored-by: Zhuo Zhang Co-authored-by: Zhuo Zhang <14835483+ZhangZhuoSJTU@users.noreply.github.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: Matthias Seitz --- Cargo.lock | 74 ++ crates/common/src/rpc.rs | 22 + crates/config/src/lib.rs | 2 +- crates/forge/Cargo.toml | 3 + crates/forge/bin/cmd/clone.rs | 726 ++++++++++++++++++ crates/forge/bin/cmd/init.rs | 16 +- crates/forge/bin/cmd/mod.rs | 1 + crates/forge/bin/main.rs | 1 + crates/forge/bin/opts.rs | 9 +- crates/forge/tests/cli/cmd.rs | 36 + .../creation_data.json | 5 + .../metadata.json | 78 ++ .../creation_data.json | 5 + .../metadata.json | 16 + .../creation_data.json | 5 + .../metadata.json | 193 +++++ .../creation_data.json | 5 + .../metadata.json | 129 ++++ .../creation_data.json | 5 + .../metadata.json | 63 ++ .../creation_data.json | 5 + .../metadata.json | 69 ++ .../creation_data.json | 5 + .../metadata.json | 148 ++++ 24 files changed, 1609 insertions(+), 12 deletions(-) create mode 100644 crates/forge/bin/cmd/clone.rs create mode 100644 testdata/etherscan/0x044b75f554b886A065b9567891e45c79542d7357/creation_data.json create mode 100644 testdata/etherscan/0x044b75f554b886A065b9567891e45c79542d7357/metadata.json create mode 100644 testdata/etherscan/0x35Fb958109b70799a8f9Bc2a8b1Ee4cC62034193/creation_data.json create mode 100644 testdata/etherscan/0x35Fb958109b70799a8f9Bc2a8b1Ee4cC62034193/metadata.json create mode 100644 testdata/etherscan/0x3a23F943181408EAC424116Af7b7790c94Cb97a5/creation_data.json create mode 100644 testdata/etherscan/0x3a23F943181408EAC424116Af7b7790c94Cb97a5/metadata.json create mode 100644 testdata/etherscan/0x71356E37e0368Bd10bFDbF41dC052fE5FA24cD05/creation_data.json create mode 100644 testdata/etherscan/0x71356E37e0368Bd10bFDbF41dC052fE5FA24cD05/metadata.json create mode 100644 testdata/etherscan/0x8B3D32cf2bb4d0D16656f4c0b04Fa546274f1545/creation_data.json create mode 100644 testdata/etherscan/0x8B3D32cf2bb4d0D16656f4c0b04Fa546274f1545/metadata.json create mode 100644 testdata/etherscan/0x9AB6b21cDF116f611110b048987E58894786C244/creation_data.json create mode 100644 testdata/etherscan/0x9AB6b21cDF116f611110b048987E58894786C244/metadata.json create mode 100644 testdata/etherscan/0xDb53f47aC61FE54F456A4eb3E09832D08Dd7BEec/creation_data.json create mode 100644 testdata/etherscan/0xDb53f47aC61FE54F456A4eb3E09832D08Dd7BEec/metadata.json diff --git a/Cargo.lock b/Cargo.lock index 5095c7b0d..3d2f8c7d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2698,6 +2698,12 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + [[package]] name = "dunce" version = "1.0.4" @@ -3490,6 +3496,7 @@ dependencies = [ "hyper 0.14.28", "indicatif", "itertools 0.12.1", + "mockall", "once_cell", "opener", "parking_lot", @@ -3513,6 +3520,8 @@ dependencies = [ "thiserror", "tikv-jemallocator", "tokio", + "toml 0.8.12", + "toml_edit 0.22.9", "tower-http", "tracing", "tracing-subscriber", @@ -4101,6 +4110,12 @@ dependencies = [ "tracing", ] +[[package]] +name = "fragile" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" + [[package]] name = "fs2" version = "0.4.3" @@ -5597,6 +5612,33 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "mockall" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43766c2b5203b10de348ffe19f7e54564b64f3d6018ff7648d1e2d6d3a0f0a48" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "lazy_static", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af7cbce79ec385a1d4f54baa90a76401eb15d9cab93685f62e7e9f942aa00ae2" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "native-tls" version = "0.2.11" @@ -6423,6 +6465,32 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +[[package]] +name = "predicates" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8" +dependencies = [ + "anstyle", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" + +[[package]] +name = "predicates-tree" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" +dependencies = [ + "predicates-core", + "termtree", +] + [[package]] name = "pretty_assertions" version = "1.4.0" @@ -8161,6 +8229,12 @@ dependencies = [ "phf_codegen 0.11.2", ] +[[package]] +name = "termtree" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" + [[package]] name = "thiserror" version = "1.0.58" diff --git a/crates/common/src/rpc.rs b/crates/common/src/rpc.rs index eb5321335..63a701a4f 100644 --- a/crates/common/src/rpc.rs +++ b/crates/common/src/rpc.rs @@ -51,6 +51,22 @@ static ALCHEMY_MAINNET_KEYS: Lazy> = Lazy::new(|| { keys }); +// List of etherscan keys for mainnet +static ETHERSCAN_MAINNET_KEYS: Lazy> = Lazy::new(|| { + let mut keys = vec![ + "MCAUM7WPE9XP5UQMZPCKIBUJHPM1C24FP6", + "JW6RWCG2C5QF8TANH4KC7AYIF1CX7RB5D1", + "ZSMDY6BI2H55MBE3G9CUUQT4XYUDBB6ZSK", + "4FYHTY429IXYMJNS4TITKDMUKW5QRYDX61", + "QYKNT5RHASZ7PGQE68FNQWH99IXVTVVD2I", + "VXMQ117UN58Y4RHWUB8K1UGCEA7UQEWK55", + ]; + + keys.shuffle(&mut rand::thread_rng()); + + keys +}); + /// counts how many times a rpc endpoint was requested for _mainnet_ static NEXT_RPC_ENDPOINT: AtomicUsize = AtomicUsize::new(0); @@ -111,6 +127,12 @@ pub fn next_ws_archive_rpc_endpoint() -> String { format!("wss://eth-mainnet.alchemyapi.io/v2/{}", ALCHEMY_MAINNET_KEYS[idx]) } +/// Returns the next etherscan api key +pub fn next_etherscan_api_key() -> String { + let idx = next() % ETHERSCAN_MAINNET_KEYS.len(); + ETHERSCAN_MAINNET_KEYS[idx].to_string() +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 12a588d1c..65923ced7 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -767,7 +767,7 @@ impl Config { self.rpc_storage_caching.enable_for_endpoint(endpoint) } - /// Returns the `ProjectPathsConfig` sub set of the config. + /// Returns the `ProjectPathsConfig` sub set of the config. /// /// **NOTE**: this uses the paths as they are and does __not__ modify them, see /// `[Self::sanitized]` diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 039b956cc..30b597576 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -79,6 +79,8 @@ solang-parser.workspace = true strum = { workspace = true, features = ["derive"] } thiserror = "1" tokio = { version = "1", features = ["time"] } +toml = { version = "0.8", features = ["preserve_order"] } +toml_edit = "0.22.4" watchexec = "2.3.2" evm-disassembler.workspace = true rustc-hash.workspace = true @@ -96,6 +98,7 @@ tikv-jemallocator = { workspace = true, optional = true } anvil.workspace = true foundry-test-utils.workspace = true +mockall = "0.12" criterion = "0.5" globset = "0.4" paste = "1.0" diff --git a/crates/forge/bin/cmd/clone.rs b/crates/forge/bin/cmd/clone.rs new file mode 100644 index 000000000..a15418d1d --- /dev/null +++ b/crates/forge/bin/cmd/clone.rs @@ -0,0 +1,726 @@ +use super::{init::InitArgs, install::DependencyInstallOpts}; +use alloy_primitives::{Address, Bytes, ChainId, TxHash}; +use clap::{Parser, ValueHint}; +use eyre::Result; +use foundry_block_explorers::{ + contract::{ContractCreationData, ContractMetadata, Metadata}, + errors::EtherscanError, + Client, +}; +use foundry_cli::{opts::EtherscanOpts, p_println, utils::Git}; +use foundry_common::{compile::ProjectCompiler, fs}; +use foundry_compilers::{ + artifacts::{output_selection::ContractOutputSelection, Settings, StorageLayout}, + remappings::{RelativeRemapping, Remapping}, + ConfigurableContractArtifact, ProjectCompileOutput, ProjectPathsConfig, +}; +use foundry_config::{Chain, Config}; +use std::{ + fs::read_dir, + path::{Path, PathBuf}, + time::Duration, +}; + +/// CloneMetadata stores the metadata that are not included by `foundry.toml` but necessary for a +/// cloned contract. The metadata can be serialized to a metadata file in the cloned project root. +#[derive(Debug, Clone, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CloneMetadata { + /// The path to the source file that contains the contract declaration. + /// The path is relative to the root directory of the project. + pub path: PathBuf, + /// The name of the contract in the file. + pub target_contract: String, + /// The address of the contract on the blockchian. + pub address: Address, + /// The chain id. + pub chain_id: ChainId, + /// The transaction hash of the creation transaction. + pub creation_transaction: TxHash, + /// The address of the deployer, i.e., sender of the creation transaction. + pub deployer: Address, + /// The constructor arguments of the contract on chain. + pub constructor_arguments: Bytes, + /// The storage layout of the contract on chain. + pub storage_layout: StorageLayout, +} + +/// CLI arguments for `forge clone`. +/// +/// `forge clone` clones an on-chain contract from block explorers (e.g., Etherscan) in the +/// following steps: +/// 1. Fetch the contract source code from the block explorer. +/// 2. Initialize a empty foundry project at the `root` directory specified in `CloneArgs`. +/// 3. Dump the contract sources to the source directory. +/// 4. Update the `foundry.toml` configuration file with the compiler settings from Etherscan. +/// 5. Try compile the cloned contract, so that we can get the original storage layout. This +/// original storage layout is preserved in the `CloneMetadata` so that if the user later +/// modifies the contract, it is possible to quickly check the storage layout compatibility with +/// the original on-chain contract. +/// 6. Dump the `CloneMetadata` to the root directory of the cloned project as `.clone.meta` file. +#[derive(Clone, Debug, Parser)] +pub struct CloneArgs { + /// The contract address to clone. + pub address: Address, + + /// The root directory of the cloned project. + #[arg(value_hint = ValueHint::DirPath, default_value = ".", value_name = "PATH")] + pub root: PathBuf, + + /// Do not generaet the remappings.txt file. Instead, keep the remappings in the configuration. + #[arg(long)] + pub no_remappings_txt: bool, + + #[command(flatten)] + pub etherscan: EtherscanOpts, + + #[command(flatten)] + pub opts: DependencyInstallOpts, +} + +impl CloneArgs { + pub async fn run(self) -> Result<()> { + let CloneArgs { address, root, opts, etherscan, no_remappings_txt } = self; + + // step 0. get the chain and api key from the config + let config = Config::from(ðerscan); + let chain = config.chain.unwrap_or_default(); + let etherscan_api_key = config.get_etherscan_api_key(Some(chain)).unwrap_or_default(); + let client = Client::new(chain, etherscan_api_key.clone())?; + + // step 1. get the metadata from client + p_println!(!opts.quiet => "Downloading the source code of {} from Etherscan...", address); + let meta = Self::collect_metadata_from_client(address, &client).await?; + + // step 2. initialize an empty project + Self::init_an_empty_project(&root, opts)?; + // canonicalize the root path + // note that at this point, the root directory must have been created + let root = dunce::canonicalize(&root)?; + + // step 3. parse the metadata + Self::parse_metadata(&meta, chain, &root, no_remappings_txt).await?; + + // step 4. collect the compilation metadata + // if the etherscan api key is not set, we need to wait for 3 seconds between calls + p_println!(!opts.quiet => "Collecting the creation information of {} from Etherscan...", address); + if etherscan_api_key.is_empty() { + p_println!(!opts.quiet => "Waiting for 5 seconds to avoid rate limit..."); + tokio::time::sleep(Duration::from_secs(5)).await; + } + Self::collect_compilation_metadata(&meta, chain, address, &root, &client, opts.quiet) + .await?; + + // step 5. git add and commit the changes if needed + if !opts.no_commit { + let git = Git::new(&root).quiet(opts.quiet); + git.add(Some("--all"))?; + let msg = format!("chore: forge clone {}", address); + git.commit(&msg)?; + } + + Ok(()) + } + + /// Collect the metadata of the contract from the block explorer. + /// + /// * `address` - the address of the contract to be cloned. + /// * `client` - the client of the block explorer. + pub(crate) async fn collect_metadata_from_client( + address: Address, + client: &C, + ) -> Result { + let mut meta = client.contract_source_code(address).await?; + eyre::ensure!(meta.items.len() == 1, "contract not found or ill-formed"); + let meta = meta.items.remove(0); + eyre::ensure!(!meta.is_vyper(), "Vyper contracts are not supported"); + Ok(meta) + } + + /// Initialize an empty project at the root directory. + /// + /// * `root` - the root directory of the project. + /// * `enable_git` - whether to enable git for the project. + /// * `quiet` - whether to print messages. + pub(crate) fn init_an_empty_project(root: &Path, opts: DependencyInstallOpts) -> Result<()> { + // let's try to init the project with default init args + let init_args = InitArgs { root: root.to_path_buf(), opts, ..Default::default() }; + init_args.run().map_err(|e| eyre::eyre!("Project init error: {:?}", e))?; + + // remove the unnecessary example contracts + // XXX (ZZ): this is a temporary solution until we have a proper way to remove contracts, + // e.g., add a field in the InitArgs to control the example contract generation + fs::remove_file(root.join("src/Counter.sol"))?; + fs::remove_file(root.join("test/Counter.t.sol"))?; + fs::remove_file(root.join("script/Counter.s.sol"))?; + + Ok(()) + } + + /// Collect the compilation metadata of the cloned contract. + /// This function compiles the cloned contract and collects the compilation metadata. + /// + /// * `meta` - the metadata of the contract (from Etherscam). + /// * `chain` - the chain where the contract to be cloned locates. + /// * `address` - the address of the contract to be cloned. + /// * `root` - the root directory of the cloned project. + /// * `client` - the client of the block explorer. + pub(crate) async fn collect_compilation_metadata( + meta: &Metadata, + chain: Chain, + address: Address, + root: &PathBuf, + client: &C, + quiet: bool, + ) -> Result<()> { + // compile the cloned contract + let compile_output = compile_project(root, quiet)?; + let (main_file, main_artifact) = find_main_contract(&compile_output, &meta.contract_name)?; + let main_file = main_file.strip_prefix(root)?.to_path_buf(); + let storage_layout = + main_artifact.storage_layout.to_owned().expect("storage layout not found"); + + // dump the metadata to the root directory + let creation_tx = client.contract_creation_data(address).await?; + let clone_meta = CloneMetadata { + path: main_file, + target_contract: meta.contract_name.clone(), + address, + chain_id: chain.id(), + creation_transaction: creation_tx.transaction_hash, + deployer: creation_tx.contract_creator, + constructor_arguments: meta.constructor_arguments.clone(), + storage_layout, + }; + let metadata_content = serde_json::to_string(&clone_meta)?; + let metadata_file = root.join(".clone.meta"); + fs::write(&metadata_file, metadata_content)?; + let mut perms = std::fs::metadata(&metadata_file)?.permissions(); + perms.set_readonly(true); + std::fs::set_permissions(&metadata_file, perms)?; + + Ok(()) + } + + /// Download and parse the source code from Etherscan. + /// + /// * `chain` - the chain where the contract to be cloned locates. + /// * `address` - the address of the contract to be cloned. + /// * `root` - the root directory to clone the contract into as a foundry project. + /// * `client` - the client of the block explorer. + /// * `no_remappings_txt` - whether to generate the remappings.txt file. + pub(crate) async fn parse_metadata( + meta: &Metadata, + chain: Chain, + root: &PathBuf, + no_remappings_txt: bool, + ) -> Result<()> { + // dump sources and update the remapping in configuration + let Settings { remappings: original_remappings, .. } = meta.settings()?; + let (remappings, strip_old_src) = dump_sources(meta, root)?; + Config::update_at(root, |config, doc| { + let profile = config.profile.as_str().as_str(); + let mut remapping_array = toml_edit::Array::new(); + // original remappings + for r in original_remappings.iter() { + // we should update its remapped path in the same way as we dump sources + // i.e., remove prefix `contracts` (if any) and add prefix `src` + let mut r = r.to_owned(); + if strip_old_src { + let new_path = PathBuf::from(r.path.clone()) + .strip_prefix("contracts") + .map(|p| p.to_path_buf()) + .unwrap_or(PathBuf::from(r.path)); + r.path = PathBuf::from("src").join(new_path).to_string_lossy().to_string(); + } + remapping_array.push(r.to_string()); + } + // new remappings + for r in remappings { + remapping_array.push(r.to_string()); + } + doc[Config::PROFILE_SECTION][profile]["remappings"] = toml_edit::value(remapping_array); + + true + })?; + + // update configuration + Config::update_at(root, |config, doc| { + update_config_by_metadata(config, doc, meta, chain).is_ok() + })?; + + // write remappings to remappings.txt if necessary + if !no_remappings_txt { + let remappings_txt = root.join("remappings.txt"); + eyre::ensure!( + !remappings_txt.exists(), + "remappings.txt already exists, please remove it first" + ); + + Config::update_at(root, |config, doc| { + let remappings_txt_content = + config.remappings.iter().map(|r| r.to_string()).collect::>().join("\n"); + if fs::write(&remappings_txt, remappings_txt_content).is_err() { + return false + } + + let profile = config.profile.as_str().as_str(); + if let Some(elem) = doc[Config::PROFILE_SECTION][profile].as_table_mut() { + elem.remove_entry("remappings"); + true + } else { + false + } + })?; + } + + Ok(()) + } +} + +/// Update the configuration file with the metadata. +/// This function will update the configuration file with the metadata from the contract. +/// It will update the following fields: +/// - `auto_detect_solc` to `false` +/// - `solc_version` to the value from the metadata +/// - `evm_version` to the value from the metadata +/// - `via_ir` to the value from the metadata +/// - `libraries` to the value from the metadata +/// - `metadata` to the value from the metadata +/// - `cbor_metadata`, `use_literal_content`, and `bytecode_hash` +/// - `optimizer` to the value from the metadata +/// - `optimizer_runs` to the value from the metadata +/// - `optimizer_details` to the value from the metadata +/// - `yul_details`, `yul`, etc. +/// - `simpleCounterForLoopUncheckedIncrement` is ignored for now +/// - `remappings` and `stop_after` are pre-validated to be empty and None, respectively +/// - `model_checker`, `debug`, and `output_selection` are ignored for now +/// +/// Detailed information can be found from the following link: +/// - https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options +/// - https://docs.soliditylang.org/en/latest/using-the-compiler.html#compiler-input-and-output-json-description +fn update_config_by_metadata( + config: &Config, + doc: &mut toml_edit::DocumentMut, + meta: &Metadata, + chain: Chain, +) -> Result<()> { + let profile = config.profile.as_str().as_str(); + + // macro to update the config if the value exists + macro_rules! update_if_needed { + ([$($key:expr),+], $value:expr) => { + { + if let Some(value) = $value { + let mut current = &mut doc[Config::PROFILE_SECTION][profile]; + $( + if let Some(nested_doc) = current.get_mut(&$key) { + current = nested_doc; + } else { + return Err(eyre::eyre!("cannot find the key: {}", $key)); + } + )+ + *current = toml_edit::value(value); + } + } + }; + } + + // update the chain id + doc[Config::PROFILE_SECTION][profile]["chain_id"] = toml_edit::value(chain.id() as i64); + + // disable auto detect solc and set the solc version + doc[Config::PROFILE_SECTION][profile]["auto_detect_solc"] = toml_edit::value(false); + let version = meta.compiler_version()?; + doc[Config::PROFILE_SECTION][profile]["solc_version"] = + toml_edit::value(format!("{}.{}.{}", version.major, version.minor, version.patch)); + + // get optimizer settings + // we ignore `model_checker`, `debug`, and `output_selection` for now, + // it seems they do not have impacts on the actual compilation + let Settings { optimizer, libraries, evm_version, via_ir, stop_after, metadata, .. } = + meta.settings()?; + eyre::ensure!(stop_after.is_none(), "stop_after should be None"); + + update_if_needed!(["evm_version"], evm_version.map(|v| v.to_string())); + update_if_needed!(["via_ir"], via_ir); + + // update metadata if needed + if let Some(metadata) = metadata { + update_if_needed!(["cbor_metadata"], metadata.cbor_metadata); + update_if_needed!(["use_literal_content"], metadata.use_literal_content); + update_if_needed!(["bytecode_hash"], metadata.bytecode_hash.map(|v| v.to_string())); + } + + // update optimizer settings if needed + update_if_needed!(["optimizer"], optimizer.enabled); + update_if_needed!(["optimizer_runs"], optimizer.runs.map(|v| v as i64)); + // update optimizer details if needed + if let Some(detail) = optimizer.details { + doc[Config::PROFILE_SECTION][profile]["optimizer_details"] = toml_edit::table(); + + update_if_needed!(["optimizer_details", "peephole"], detail.peephole); + update_if_needed!(["optimizer_details", "inliner"], detail.inliner); + update_if_needed!(["optimizer_details", "jumpdestRemover"], detail.jumpdest_remover); + update_if_needed!(["optimizer_details", "orderLiterals"], detail.order_literals); + update_if_needed!(["optimizer_details", "deduplicate"], detail.deduplicate); + update_if_needed!(["optimizer_details", "cse"], detail.cse); + update_if_needed!(["optimizer_details", "constantOptimizer"], detail.constant_optimizer); + update_if_needed!( + ["optimizer_details", "simpleCounterForLoopUncheckedIncrement"], + detail.simple_counter_for_loop_unchecked_increment + ); + update_if_needed!(["optimizer_details", "yul"], detail.yul); + + if let Some(yul_detail) = detail.yul_details { + doc[Config::PROFILE_SECTION][profile]["optimizer_details"]["yulDetails"] = + toml_edit::table(); + update_if_needed!( + ["optimizer_details", "yulDetails", "stackAllocation"], + yul_detail.stack_allocation + ); + update_if_needed!( + ["optimizer_details", "yulDetails", "optimizerSteps"], + yul_detail.optimizer_steps + ); + } + } + + // apply remapping on libraries + let path_config = config.project_paths(); + let libraries = libraries + .with_applied_remappings(&path_config) + .with_stripped_file_prefixes(&path_config.root); + + // update libraries + let mut lib_array = toml_edit::Array::new(); + for (path_to_lib, info) in libraries.libs { + for (lib_name, address) in info { + lib_array.push(format!("{}:{}:{}", path_to_lib.to_str().unwrap(), lib_name, address)); + } + } + doc[Config::PROFILE_SECTION][profile]["libraries"] = toml_edit::value(lib_array); + + Ok(()) +} + +/// Dump the contract sources to the root directory. +/// The sources are dumped to the `src` directory. +/// IO errors may be returned. +/// A list of remappings is returned, as well as a boolean indicating whether the old `contract` +/// or `src` directories are stripped. +fn dump_sources(meta: &Metadata, root: &PathBuf) -> Result<(Vec, bool)> { + // get config + let path_config = ProjectPathsConfig::builder().build_with_root(root); + // we will canonicalize the sources directory later + let src_dir = &path_config.sources; + let contract_name = &meta.contract_name; + let source_tree = meta.source_tree(); + + // then we move the sources to the correct directories + // we will first load existing remappings if necessary + // make sure this happens before dumping sources + let mut remappings: Vec = Remapping::find_many(root); + // we also load the original remappings from the metadata + remappings.extend(meta.settings()?.remappings); + + // first we dump the sources to a temporary directory + let tmp_dump_dir = root.join("raw_sources"); + source_tree + .write_to(&tmp_dump_dir) + .map_err(|e| eyre::eyre!("failed to dump sources: {}", e))?; + + // check whether we need to strip the `contract` or `src` directories in the original sources. + // They are the source directories in foundry or hardhat projects. We do not want to preserve + // them in the cloned project. + // If there is any other directory other than `src` `contracts` or `lib`, we should not strip. + let strip_old_src = std::fs::read_dir(tmp_dump_dir.join(contract_name))?.all(|e| { + let Ok(e) = e else { return false }; + let folder_name = e.file_name().to_string_lossy().to_string(); + ["contracts", "src", "lib"].contains(&folder_name.as_str()) + }); + // move contract sources to the `src` directory + for entry in std::fs::read_dir(tmp_dump_dir.join(contract_name))? { + if std::fs::metadata(root.join(src_dir)).is_err() { + std::fs::create_dir(root.join(src_dir))?; + } + let entry = entry?; + let folder_name = entry.file_name().to_string_lossy().to_string(); + // special handling for contracts and src directories: we flatten them. + if strip_old_src && (folder_name.as_str() == "contracts" || folder_name.as_str() == "src") { + // move all sub folders in contracts to src + for e in read_dir(entry.path())? { + let e = e?; + let dest = src_dir.join(e.file_name()); + std::fs::rename(e.path(), &dest)?; + remappings.push(Remapping { + context: None, + name: format!("{}/{}", folder_name, e.file_name().to_string_lossy()), + path: dest.to_string_lossy().to_string(), + }); + } + } else { + // move the other folders to src + let dest = src_dir.join(entry.file_name()); + std::fs::rename(entry.path(), &dest)?; + remappings.push(Remapping { + context: None, + name: entry.file_name().to_string_lossy().to_string(), + path: dest.to_string_lossy().to_string(), + }); + } + } + + // remove the temporary directory + std::fs::remove_dir_all(tmp_dump_dir)?; + + Ok((remappings.into_iter().map(|r| r.into_relative(root)).collect(), strip_old_src)) +} + +/// Compile the project in the root directory, and return the compilation result. +pub fn compile_project(root: &PathBuf, quiet: bool) -> Result { + let mut config = Config::load_with_root(root).sanitized(); + config.extra_output.push(ContractOutputSelection::StorageLayout); + let project = config.project()?; + let compiler = ProjectCompiler::new().quiet_if(quiet); + compiler.compile(&project) +} + +/// Find the artifact of the contract with the specified name. +/// This function returns the path to the source file and the artifact. +pub fn find_main_contract<'a>( + compile_output: &'a ProjectCompileOutput, + contract: &str, +) -> Result<(PathBuf, &'a ConfigurableContractArtifact)> { + let mut rv = None; + for (f, c, a) in compile_output.artifacts_with_files() { + if contract == c { + // it is possible that we have multiple contracts with the same name + // in different files + // instead of throwing an error, we should handle this case in the future + if rv.is_some() { + return Err(eyre::eyre!("multiple contracts with the same name found")); + } + rv = Some((PathBuf::from(f), a)); + } + } + rv.ok_or(eyre::eyre!("contract not found")) +} + +#[cfg(test)] +use mockall::automock; +/// EtherscanClient is a trait that defines the methods to interact with Etherscan. +/// It is defined as a wrapper of the `foundry_block_explorers::Client` to allow mocking. +#[cfg_attr(test, automock)] +pub(crate) trait EtherscanClient { + async fn contract_source_code( + &self, + address: Address, + ) -> std::result::Result; + async fn contract_creation_data( + &self, + address: Address, + ) -> std::result::Result; +} + +impl EtherscanClient for Client { + #[inline] + async fn contract_source_code( + &self, + address: Address, + ) -> std::result::Result { + self.contract_source_code(address).await + } + + #[inline] + async fn contract_creation_data( + &self, + address: Address, + ) -> std::result::Result { + self.contract_creation_data(address).await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use foundry_common::rpc::next_etherscan_api_key; + use foundry_compilers::Artifact; + use hex::ToHex; + use std::collections::BTreeMap; + + fn assert_successful_compilation(root: &PathBuf) -> ProjectCompileOutput { + println!("project_root: {:#?}", root); + compile_project(root, false).expect("compilation failure") + } + + fn assert_compilation_result( + compiled: ProjectCompileOutput, + contract_name: &str, + stripped_creation_code: &str, + ) { + compiled.compiled_contracts_by_compiler_version().iter().for_each(|(_, contracts)| { + contracts.iter().for_each(|(name, contract)| { + if name == contract_name { + let compiled_creation_code = + contract.get_bytecode_object().expect("creation code not found"); + let compiled_creation_code: String = compiled_creation_code.encode_hex(); + assert!( + compiled_creation_code.starts_with(stripped_creation_code), + "inconsistent creation code" + ); + } + }); + }); + } + + fn mock_etherscan(address: Address) -> impl super::EtherscanClient { + // load mock data + let mut mocked_data = BTreeMap::new(); + let data_folder = + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../testdata/etherscan"); + // iterate each sub folder + for entry in std::fs::read_dir(data_folder).expect("failed to read test data folder") { + let entry = entry.expect("failed to read test data entry"); + let addr: Address = entry.file_name().to_string_lossy().parse().unwrap(); + let contract_data_dir = entry.path(); + // the metadata.json file contains the metadata of the contract + let metadata_file = contract_data_dir.join("metadata.json"); + let metadata: ContractMetadata = + serde_json::from_str(&std::fs::read_to_string(metadata_file).unwrap()) + .expect("failed to parse metadata.json"); + // the creation_data.json file contains the creation data of the contract + let creation_data_file = contract_data_dir.join("creation_data.json"); + let creation_data: ContractCreationData = + serde_json::from_str(&std::fs::read_to_string(creation_data_file).unwrap()) + .expect("failed to parse creation_data.json"); + // insert the data to the map + mocked_data.insert(addr, (metadata, creation_data)); + } + + let (metadata, creation_data) = mocked_data.get(&address).unwrap(); + let metadata = metadata.clone(); + let creation_data = *creation_data; + let mut mocked_client = super::MockEtherscanClient::new(); + mocked_client + .expect_contract_source_code() + .times(1) + .returning(move |_| Ok(metadata.clone())); + mocked_client + .expect_contract_creation_data() + .times(1) + .returning(move |_| Ok(creation_data)); + mocked_client + } + + /// Fetch the metadata and creation data from Etherscan and dump them to the testdata folder. + #[tokio::test(flavor = "multi_thread")] + #[ignore = "this test is used to dump mock data from Etherscan"] + async fn test_dump_mock_data() { + let address: Address = "0x9ab6b21cdf116f611110b048987e58894786c244".parse().unwrap(); + let data_folder = PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../../testdata/etherscan") + .join(address.to_string()); + // create folder if not exists + std::fs::create_dir_all(&data_folder).unwrap(); + // create metadata.json and creation_data.json + let client = Client::new(Chain::mainnet(), next_etherscan_api_key()).unwrap(); + let meta = client.contract_source_code(address).await.unwrap(); + // dump json + let json = serde_json::to_string_pretty(&meta).unwrap(); + // write to metadata.json + std::fs::write(data_folder.join("metadata.json"), json).unwrap(); + let creation_data = client.contract_creation_data(address).await.unwrap(); + // dump json + let json = serde_json::to_string_pretty(&creation_data).unwrap(); + // write to creation_data.json + std::fs::write(data_folder.join("creation_data.json"), json).unwrap(); + } + + /// Run the clone command with the specified contract address and assert the compilation. + async fn one_test_case(address: Address, check_compilation_result: bool) { + let mut project_root = tempfile::tempdir().unwrap().path().to_path_buf(); + let client = mock_etherscan(address); + let meta = CloneArgs::collect_metadata_from_client(address, &client).await.unwrap(); + CloneArgs::init_an_empty_project(&project_root, DependencyInstallOpts::default()).unwrap(); + project_root = dunce::canonicalize(&project_root).unwrap(); + CloneArgs::parse_metadata(&meta, Chain::mainnet(), &project_root, false).await.unwrap(); + CloneArgs::collect_compilation_metadata( + &meta, + Chain::mainnet(), + address, + &project_root, + &client, + false, + ) + .await + .unwrap(); + let rv = assert_successful_compilation(&project_root); + if check_compilation_result { + let (contract_name, stripped_creation_code) = + pick_creation_info(&address.to_string()).expect("creation code not found"); + assert_compilation_result(rv, contract_name, stripped_creation_code); + } + std::fs::remove_dir_all(project_root).unwrap(); + } + + #[tokio::test(flavor = "multi_thread")] + async fn test_clone_single_file_contract() { + let address = "0x35Fb958109b70799a8f9Bc2a8b1Ee4cC62034193".parse().unwrap(); + one_test_case(address, true).await + } + + #[tokio::test(flavor = "multi_thread")] + async fn test_clone_contract_with_optimization_details() { + let address = "0x8B3D32cf2bb4d0D16656f4c0b04Fa546274f1545".parse().unwrap(); + one_test_case(address, true).await + } + + #[tokio::test(flavor = "multi_thread")] + async fn test_clone_contract_with_libraries() { + let address = "0xDb53f47aC61FE54F456A4eb3E09832D08Dd7BEec".parse().unwrap(); + one_test_case(address, true).await + } + + #[tokio::test(flavor = "multi_thread")] + async fn test_clone_contract_with_metadata() { + let address = "0x71356E37e0368Bd10bFDbF41dC052fE5FA24cD05".parse().unwrap(); + one_test_case(address, true).await + } + + #[tokio::test(flavor = "multi_thread")] + async fn test_clone_contract_with_relative_import() { + let address = "0x3a23F943181408EAC424116Af7b7790c94Cb97a5".parse().unwrap(); + one_test_case(address, false).await + } + + #[tokio::test(flavor = "multi_thread")] + async fn test_clone_contract_with_original_remappings() { + let address = "0x9ab6b21cdf116f611110b048987e58894786c244".parse().unwrap(); + one_test_case(address, false).await + } + + #[tokio::test(flavor = "multi_thread")] + async fn test_clone_contract_with_relative_import2() { + let address = "0x044b75f554b886A065b9567891e45c79542d7357".parse().unwrap(); + one_test_case(address, false).await + } + + fn pick_creation_info(address: &str) -> Option<(&'static str, &'static str)> { + for (addr, contract_name, creation_code) in CREATION_ARRAY.iter() { + if address == *addr { + return Some((contract_name, creation_code)); + } + } + + None + } + + // remember to remove CBOR metadata from the creation code + const CREATION_ARRAY: [(&str, &str, &str); 4] = [ + ("0x35Fb958109b70799a8f9Bc2a8b1Ee4cC62034193", "BearXNFTStaking", "608060405234801561001057600080fd5b50613000806100206000396000f3fe608060405234801561001057600080fd5b50600436106102265760003560e01c80638129fc1c11610130578063bca35a71116100b8578063dada55011161007c578063dada550114610458578063f2fde38b1461046b578063f83d08ba1461047e578063fbb0022714610486578063fccd7f721461048e57600080fd5b8063bca35a71146103fa578063bf9befb11461040d578063c89d5b8b14610416578063d5d423001461041e578063d976e09f1461042657600080fd5b8063b1c92f95116100ff578063b1c92f95146103c5578063b549445c146103ce578063b81f8e89146103d6578063b9ade5b7146103de578063ba0848db146103e757600080fd5b80638129fc1c146103905780638da5cb5b14610398578063aaed083b146103a9578063b10dcc93146103b257600080fd5b8063367c164e116101b35780635923489b116101825780635923489b146103245780636e2751211461034f578063706ce3e114610362578063715018a614610375578063760a2e8a1461037d57600080fd5b8063367c164e146102bd57806338ff8a85146102d05780633a17f4f0146102f1578063426233601461030457600080fd5b8063206635e7116101fa578063206635e71461026d5780632afe761a146102805780632bd30f1114610289578063305f839a146102ab57806333ddacd1146102b457600080fd5b8062944f621461022b5780630d00368b146102405780630e8feed41461025c578063120957fd14610264575b600080fd5b61023e610239366004612aa4565b6104bc565b005b61024960735481565b6040519081526020015b60405180910390f35b61023e61053a565b610249606d5481565b61023e61027b366004612b2c565b61057e565b610249606f5481565b60785461029b90610100900460ff1681565b6040519015158152602001610253565b61024960715481565b61024960765481565b61023e6102cb366004612bc2565b6105d1565b6102e36102de366004612aa4565b610829565b604051610253929190612c16565b61023e6102ff366004612aa4565b6109e1565b610317610312366004612aa4565b610a56565b6040516102539190612c2f565b606a54610337906001600160a01b031681565b6040516001600160a01b039091168152602001610253565b6102e361035d366004612aa4565b610b4c565b606b54610337906001600160a01b031681565b61023e610cf8565b61029b61038b366004612aa4565b610d2e565b61023e610dc2565b6033546001600160a01b0316610337565b61024960705481565b61023e6103c0366004612b2c565b610fc0565b610249606e5481565b61023e611236565b61023e6112bb565b61024960725481565b6102e36103f5366004612aa4565b6112ef565b61023e610408366004612aa4565b61149b565b610249606c5481565b610249611510565b606f54610249565b610439610434366004612aa4565b611594565b6040805192151583526001600160a01b03909116602083015201610253565b606954610337906001600160a01b031681565b61023e610479366004612aa4565b6115cf565b61023e611667565b61023e6116a0565b6104a161049c366004612aa4565b6116e1565b60408051938452602084019290925290820152606001610253565b6033546001600160a01b031633146104ef5760405162461bcd60e51b81526004016104e690612c42565b60405180910390fd5b606b546001600160a01b03163b6105185760405162461bcd60e51b81526004016104e690612c77565b606b80546001600160a01b0319166001600160a01b0392909216919091179055565b600061054533611594565b509050806105655760405162461bcd60e51b81526004016104e690612cae565b61056e33610d2e565b61057b5761057b33611c73565b50565b610587336116e1565b505060765560005b81518110156105cd576105bb8282815181106105ad576105ad612cda565b602002602001015133611d7b565b806105c581612d06565b91505061058f565b5050565b606a54604051636eb1769f60e11b815233600482015273871770e3e03bfaefa3597056e540a1a9c9ac7f6b602482015282916001600160a01b03169063dd62ed3e90604401602060405180830381865afa158015610633573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106579190612d21565b10156106bb5760405162461bcd60e51b815260206004820152602d60248201527f596f75206861766520746f20617070726f766520726f6f747820746f2073746160448201526c1ada5b99c818dbdb9d1c9858dd609a1b60648201526084016104e6565b606a546040516323b872dd60e01b815233600482015273871770e3e03bfaefa3597056e540a1a9c9ac7f6b6024820152604481018390526001600160a01b03909116906323b872dd906064016020604051808303816000875af1158015610726573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061074a9190612d3a565b50606a546040516326c7e79d60e21b8152600481018390526001600160a01b0390911690639b1f9e7490602401600060405180830381600087803b15801561079157600080fd5b505af11580156107a5573d6000803e3d6000fd5b5050606b546001600160a01b031691506379c650689050336107c88460056120a1565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087803b15801561080e57600080fd5b505af1158015610822573d6000803e3d6000fd5b5050505050565b600060606000805b6001600160a01b0385166000908152607460205260409020548110156108bc576001600160a01b0385166000908152607460205260409020805461089791908390811061088057610880612cda565b906000526020600020906005020160000154612129565b156108aa576108a7600183612d5c565b91505b806108b481612d06565b915050610831565b5060008167ffffffffffffffff8111156108d8576108d8612ac1565b604051908082528060200260200182016040528015610901578160200160208202803683370190505b5090506000805b6001600160a01b0387166000908152607460205260409020548110156109d5576001600160a01b0387166000908152607460205260409020805461095791908390811061088057610880612cda565b156109c3576001600160a01b038716600090815260746020526040902080548290811061098657610986612cda565b9060005260206000209060050201600001548383815181106109aa576109aa612cda565b60209081029190910101526109c0826001612154565b91505b806109cd81612d06565b915050610908565b50919590945092505050565b6033546001600160a01b03163314610a0b5760405162461bcd60e51b81526004016104e690612c42565b6069546001600160a01b03163b610a345760405162461bcd60e51b81526004016104e690612c77565b606980546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b0381166000908152607460205260408120546060919067ffffffffffffffff811115610a8b57610a8b612ac1565b604051908082528060200260200182016040528015610ab4578160200160208202803683370190505b50905060005b6001600160a01b038416600090815260746020526040902054811015610b45576001600160a01b0384166000908152607460205260409020805482908110610b0457610b04612cda565b906000526020600020906005020160000154828281518110610b2857610b28612cda565b602090810291909101015280610b3d81612d06565b915050610aba565b5092915050565b600060606000805b6001600160a01b038516600090815260746020526040902054811015610bdf576001600160a01b03851660009081526074602052604090208054610bba919083908110610ba357610ba3612cda565b9060005260206000209060050201600001546121b3565b15610bcd57610bca826001612154565b91505b80610bd781612d06565b915050610b54565b5060008167ffffffffffffffff811115610bfb57610bfb612ac1565b604051908082528060200260200182016040528015610c24578160200160208202803683370190505b5090506000805b6001600160a01b0387166000908152607460205260409020548110156109d5576001600160a01b03871660009081526074602052604090208054610c7a919083908110610ba357610ba3612cda565b15610ce6576001600160a01b0387166000908152607460205260409020805482908110610ca957610ca9612cda565b906000526020600020906005020160000154838381518110610ccd57610ccd612cda565b6020908102919091010152610ce3826001612154565b91505b80610cf081612d06565b915050610c2b565b6033546001600160a01b03163314610d225760405162461bcd60e51b81526004016104e690612c42565b610d2c60006121d0565b565b60006001815b6001600160a01b038416600090815260746020526040902054811015610b45576001600160a01b03841660009081526074602052604081208054610d9a919084908110610d8357610d83612cda565b906000526020600020906005020160010154612222565b9050603c8111610daa5750610db0565b60009250505b80610dba81612d06565b915050610d34565b600054610100900460ff16610ddd5760005460ff1615610de1565b303b155b610e445760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084016104e6565b600054610100900460ff16158015610e66576000805461ffff19166101011790555b610e6e61223c565b606580546001600160a01b0319908116737a250d5630b4cf539739df2c5dacb4c659f2488d1790915560668054821673c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2179055620151806067556312cc030060685560698054821673e22e1e620dffb03065cd77db0162249c0c91bf01179055606a8054821673d718ad25285d65ef4d79262a6cd3aea6a8e01023179055606b80549091167399cfdf48d0ba4885a73786148a2f89d86c7021701790556000606c5568056bc75e2d63100000606d556802b5e3af16b1880000606e55690257058e269742680000606f819055681b1ae4d6e2ef5000006070819055610bb8607181905591610f709190612d74565b610f7a9190612d8b565b607255607154606e54606d54610f909190612d74565b610f9a9190612d8b565b60735560006076556078805460ff19169055801561057b576000805461ff001916905550565b6000610fcb33611594565b50905080610feb5760405162461bcd60e51b81526004016104e690612cae565b607854610100900460ff161561102c5760405162461bcd60e51b8152602060048201526006602482015265131bd8dad95960d21b60448201526064016104e6565b600061103733610a56565b90508051835111156110775760405162461bcd60e51b81526020600482015260096024820152684964206572726f727360b81b60448201526064016104e6565b6000805b84518110156110fd5760005b83518110156110ea578381815181106110a2576110a2612cda565b60200260200101518683815181106110bc576110bc612cda565b602002602001015114156110d8576110d5836001612154565b92505b806110e281612d06565b915050611087565b50806110f581612d06565b91505061107b565b50835181141561123057835161112761111e82678ac7230489e800006120a1565b606f5490612154565b606f55611132612273565b600060768190555b855181101561122d5760695486516001600160a01b03909116906323b872dd90309033908a908690811061117057611170612cda565b60209081029190910101516040516001600160e01b031960e086901b1681526001600160a01b0393841660048201529290911660248301526044820152606401600060405180830381600087803b1580156111ca57600080fd5b505af11580156111de573d6000803e3d6000fd5b5050606c80549250905060006111f383612dad565b919050555061121b3387838151811061120e5761120e612cda565b602002602001015161229b565b8061122581612d06565b91505061113a565b50505b50505050565b60785460ff166112ac5760005b60755481101561057b576001607760006075848154811061126657611266612cda565b6000918252602080832091909101546001600160a01b031683528201929092526040019020805460ff1916911515919091179055806112a481612d06565b915050611243565b6078805460ff19166001179055565b60006112c633611594565b509050806112e65760405162461bcd60e51b81526004016104e690612cae565b61057b33612470565b600060606000805b6001600160a01b038516600090815260746020526040902054811015611382576001600160a01b0385166000908152607460205260409020805461135d91908390811061134657611346612cda565b906000526020600020906005020160000154612574565b156113705761136d600183612d5c565b91505b8061137a81612d06565b9150506112f7565b5060008167ffffffffffffffff81111561139e5761139e612ac1565b6040519080825280602002602001820160405280156113c7578160200160208202803683370190505b5090506000805b6001600160a01b0387166000908152607460205260409020548110156109d5576001600160a01b0387166000908152607460205260409020805461141d91908390811061134657611346612cda565b15611489576001600160a01b038716600090815260746020526040902080548290811061144c5761144c612cda565b90600052602060002090600502016000015483838151811061147057611470612cda565b6020908102919091010152611486826001612154565b91505b8061149381612d06565b9150506113ce565b6033546001600160a01b031633146114c55760405162461bcd60e51b81526004016104e690612c42565b606a546001600160a01b03163b6114ee5760405162461bcd60e51b81526004016104e690612c77565b606a80546001600160a01b0319166001600160a01b0392909216919091179055565b6000806064606f5460016115249190612dc4565b61152e9190612d8b565b611539906001612d5c565b606d546115469190612dc4565b90506000606d54826115589190612d74565b905060006001606d548361156c9190612d8b565b6115769190612d8b565b611581906001612dc4565b61158c906064612dc4565b949350505050565b6001600160a01b038116600090815260776020526040812054819060ff161515600114156115c457506001929050565b506000928392509050565b6033546001600160a01b031633146115f95760405162461bcd60e51b81526004016104e690612c42565b6001600160a01b03811661165e5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016104e6565b61057b816121d0565b73d0d725208fd36be1561050fc1dd6a651d7ea7c89331415610d2c576078805461ff001981166101009182900460ff1615909102179055565b60006116ab33611594565b509050806116cb5760405162461bcd60e51b81526004016104e690612cae565b6116d433610d2e565b61057b5761057b336125aa565b600080808080808087816116f482610829565b509050600061170283610b4c565b50905060058110611a3b57600160005b6001600160a01b038516600090815260746020526040902054811015611a0e576001600160a01b0385166000908152607460205260408120805461177891908490811061176157611761612cda565b906000526020600020906005020160040154612222565b6001600160a01b038716600090815260746020526040902080549192506117a99184908110610ba357610ba3612cda565b806117de57506001600160a01b038616600090815260746020526040902080546117de91908490811061088057610880612cda565b80156117ea5750600181105b156117f457600092505b82801561181857506001600160a01b03861660009081526074602052604090205415155b156118715761186a8561182c83600a612dc4565b6118369190612dc4565b6118648661184585600a612dc4565b61184f9190612dc4565b60765461186490670de0b6b3a7640000612798565b90612154565b995061188e565b8261188e5760765461188b90670de0b6b3a7640000612798565b99505b6001600160a01b038616600090815260746020526040902080546118bd91908490811061088057610880612cda565b15611950576001600160a01b038616600090815260746020526040812080546119089190859081106118f1576118f1612cda565b906000526020600020906005020160020154612222565b90508061192861192182680ad78ebc5ac6200000612dc4565b8c90612154565b9a5061194761194082680ad78ebc5ac6200000612dc4565b8b90612154565b995050506119fb565b6001600160a01b0386166000908152607460205260409020805461197f919084908110610ba357610ba3612cda565b156119fb576001600160a01b038616600090815260746020526040812080546119b39190859081106118f1576118f1612cda565b905060008190506119d76002606d54846119cd9190612dc4565b6119219190612d8b565b9a506119f66002606d54836119ec9190612dc4565b6119409190612d8b565b995050505b5080611a0681612d06565b915050611712565b508515611a3557606b54606654611a32916001600160a01b039081169116886127da565b94505b50611c4f565b60005b6001600160a01b038416600090815260746020526040902054811015611c28576001600160a01b03841660009081526074602052604090208054611a8d91908390811061088057610880612cda565b15611b9f576001600160a01b03841660009081526074602052604081208054611ac191908490811061176157611761612cda565b9050611ad8611ad182600a612dc4565b8a90612154565b98506000611b1560746000886001600160a01b03166001600160a01b0316815260200190815260200160002084815481106118f1576118f1612cda565b9050611b2d611ad182680ad78ebc5ac6200000612dc4565b98506000611b8160746000896001600160a01b03166001600160a01b031681526020019081526020016000208581548110611b6a57611b6a612cda565b906000526020600020906005020160030154612222565b9050611b99611ad182680ad78ebc5ac6200000612dc4565b98505050505b6001600160a01b03841660009081526074602052604090208054611bce919083908110610ba357610ba3612cda565b15611c16576001600160a01b03841660009081526074602052604081208054611c0291908490811061176157611761612cda565b9050611c12611ad182600a612dc4565b9850505b80611c2081612d06565b915050611a3e565b508415611c4f57606b54606654611c4c916001600160a01b039081169116876127da565b93505b611c6187670de0b6b3a7640000612dc4565b9b959a50929850939650505050505050565b6000611c7e826116e1565b5091505080156105cd57606b5460405163a9059cbb60e01b81526001600160a01b038481166004830152602482018490529091169063a9059cbb906044016020604051808303816000875af1158015611cdb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cff9190612d3a565b5060005b6001600160a01b038316600090815260746020526040902054811015611d76576001600160a01b0383166000908152607460205260409020805442919083908110611d5057611d50612cda565b600091825260209091206002600590920201015580611d6e81612d06565b915050611d03565b505050565b607054606f5410611db857607354606d6000828254611d9a9190612d74565b9091555050606f54611db490678ac7230489e80000612905565b606f555b6069546040516331a9108f60e11b8152600481018490526001600160a01b03838116921690636352211e90602401602060405180830381865afa158015611e03573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e279190612de3565b6001600160a01b031614611e7d5760405162461bcd60e51b815260206004820152601e60248201527f596f7520617265206e6f742061206f776e6572206f6620746865206e6674000060448201526064016104e6565b60695460405163e985e9c560e01b81523360048201523060248201526001600160a01b039091169063e985e9c590604401602060405180830381865afa158015611ecb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611eef9190612d3a565b1515600114611f575760405162461bcd60e51b815260206004820152602e60248201527f596f752073686f756c6420617070726f7665206e667420746f2074686520737460448201526d185ada5b99c818dbdb9d1c9858dd60921b60648201526084016104e6565b6069546040516323b872dd60e01b81526001600160a01b03838116600483015230602483015260448201859052909116906323b872dd90606401600060405180830381600087803b158015611fab57600080fd5b505af1158015611fbf573d6000803e3d6000fd5b505050506000611fce82611594565b50905060006040518060a001604052808581526020014281526020014281526020014281526020014281525090506120126001606c5461215490919063ffffffff16565b606c556001600160a01b03831660009081526074602090815260408083208054600181810183559185529383902085516005909502019384559184015191830191909155820151600282015560608201516003820155608082015160049091015581611230576001600160a01b0383166000908152607760205260409020805460ff1916600117905550505050565b6000826120b057506000612123565b60006120bc8385612dc4565b9050826120c98583612d8b565b146121205760405162461bcd60e51b815260206004820152602160248201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6044820152607760f81b60648201526084016104e6565b90505b92915050565b60008064e8d4a510008310158015612146575064e8d4a510058311155b156121235750600192915050565b6000806121618385612d5c565b9050838110156121205760405162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f77000000000060448201526064016104e6565b600080610e7483116121c757506001612123565b50600092915050565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6067546000906122328342612d74565b6121239190612d8b565b600054610100900460ff166122635760405162461bcd60e51b81526004016104e690612e00565b61226b612947565b610d2c61296e565b61227c33612470565b61228533610d2e565b610d2c5761229233611c73565b610d2c336125aa565b60005b6001600160a01b038316600090815260746020526040902054811015612428576001600160a01b03831660009081526074602052604090208054839190839081106122eb576122eb612cda565b9060005260206000209060050201600001541415612416576001600160a01b0383166000908152607460205260409020805461232990600190612d74565b8154811061233957612339612cda565b906000526020600020906005020160746000856001600160a01b03166001600160a01b03168152602001908152602001600020828154811061237d5761237d612cda565b60009182526020808320845460059093020191825560018085015490830155600280850154908301556003808501549083015560049384015493909101929092556001600160a01b03851681526074909152604090208054806123e2576123e2612e4b565b6000828152602081206005600019909301928302018181556001810182905560028101829055600381018290556004015590555b8061242081612d06565b91505061229e565b506001600160a01b0382166000908152607460205260409020546105cd576001600160a01b038216600090815260746020526040812061246791612a3f565b6105cd8261299e565b600061247b826116e1565b509091505080156105cd57606a5460405163a9059cbb60e01b81526001600160a01b038481166004830152602482018490529091169063a9059cbb906044016020604051808303816000875af11580156124d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124fd9190612d3a565b5060005b6001600160a01b038316600090815260746020526040902054811015611d76576001600160a01b038316600090815260746020526040902080544291908390811061254e5761254e612cda565b60009182526020909120600460059092020101558061256c81612d06565b915050612501565b60006509184e72a00682101561258c57506000919050565b6509184e72b4b38211156125a257506000919050565b506001919050565b60006125b5826116e1565b5091505080156105cd57606b5460655460405163095ea7b360e01b81526001600160a01b0391821660048201526024810184905291169063095ea7b3906044016020604051808303816000875af1158015612614573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126389190612d3a565b5060408051600280825260608083018452926020830190803683375050606b5482519293506001600160a01b03169183915060009061267957612679612cda565b6001600160a01b0392831660209182029290920101526066548251911690829060019081106126aa576126aa612cda565b6001600160a01b03928316602091820292909201015260655460405163791ac94760e01b815291169063791ac947906126f0908590600090869089904290600401612e9a565b600060405180830381600087803b15801561270a57600080fd5b505af115801561271e573d6000803e3d6000fd5b5050505060005b6001600160a01b038416600090815260746020526040902054811015611230576001600160a01b038416600090815260746020526040902080544291908390811061277257612772612cda565b60009182526020909120600360059092020101558061279081612d06565b915050612725565b600061212083836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f0000000000008152506129d7565b6040805160028082526060808301845260009390929190602083019080368337019050509050848160008151811061281457612814612cda565b60200260200101906001600160a01b031690816001600160a01b031681525050838160018151811061284857612848612cda565b6001600160a01b03928316602091820292909201015260655460405163d06ca61f60e01b8152600092919091169063d06ca61f9061288c9087908690600401612ed6565b600060405180830381865afa1580156128a9573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526128d19190810190612eef565b905080600183516128e29190612d74565b815181106128f2576128f2612cda565b6020026020010151925050509392505050565b600061212083836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250612a0e565b600054610100900460ff16610d2c5760405162461bcd60e51b81526004016104e690612e00565b600054610100900460ff166129955760405162461bcd60e51b81526004016104e690612e00565b610d2c336121d0565b6000806129aa83611594565b915091508115611d76576001600160a01b03166000908152607760205260409020805460ff191690555050565b600081836129f85760405162461bcd60e51b81526004016104e69190612f75565b506000612a058486612d8b565b95945050505050565b60008184841115612a325760405162461bcd60e51b81526004016104e69190612f75565b506000612a058486612d74565b508054600082556005029060005260206000209081019061057b91905b80821115612a8b5760008082556001820181905560028201819055600382018190556004820155600501612a5c565b5090565b6001600160a01b038116811461057b57600080fd5b600060208284031215612ab657600080fd5b813561212081612a8f565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715612b0057612b00612ac1565b604052919050565b600067ffffffffffffffff821115612b2257612b22612ac1565b5060051b60200190565b60006020808385031215612b3f57600080fd5b823567ffffffffffffffff811115612b5657600080fd5b8301601f81018513612b6757600080fd5b8035612b7a612b7582612b08565b612ad7565b81815260059190911b82018301908381019087831115612b9957600080fd5b928401925b82841015612bb757833582529284019290840190612b9e565b979650505050505050565b600060208284031215612bd457600080fd5b5035919050565b600081518084526020808501945080840160005b83811015612c0b57815187529582019590820190600101612bef565b509495945050505050565b82815260406020820152600061158c6040830184612bdb565b6020815260006121206020830184612bdb565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60208082526017908201527f41646472657373206973206e6f7420636f6e7472616374000000000000000000604082015260600190565b6020808252601290820152712cb7ba9030b932903737ba1039ba30b5b2b960711b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000600019821415612d1a57612d1a612cf0565b5060010190565b600060208284031215612d3357600080fd5b5051919050565b600060208284031215612d4c57600080fd5b8151801515811461212057600080fd5b60008219821115612d6f57612d6f612cf0565b500190565b600082821015612d8657612d86612cf0565b500390565b600082612da857634e487b7160e01b600052601260045260246000fd5b500490565b600081612dbc57612dbc612cf0565b506000190190565b6000816000190483118215151615612dde57612dde612cf0565b500290565b600060208284031215612df557600080fd5b815161212081612a8f565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b634e487b7160e01b600052603160045260246000fd5b600081518084526020808501945080840160005b83811015612c0b5781516001600160a01b031687529582019590820190600101612e75565b85815284602082015260a060408201526000612eb960a0830186612e61565b6001600160a01b0394909416606083015250608001529392505050565b82815260406020820152600061158c6040830184612e61565b60006020808385031215612f0257600080fd5b825167ffffffffffffffff811115612f1957600080fd5b8301601f81018513612f2a57600080fd5b8051612f38612b7582612b08565b81815260059190911b82018301908381019087831115612f5757600080fd5b928401925b82841015612bb757835182529284019290840190612f5c565b600060208083528351808285015260005b81811015612fa257858101830151858201604001528201612f86565b81811115612fb4576000604083870101525b50601f01601f191692909201604001939250505056fe"), + ("0x8B3D32cf2bb4d0D16656f4c0b04Fa546274f1545", "GovernorCharlieDelegate", "608060405234801561001057600080fd5b50613e45806100206000396000f3fe60806040526004361061031a5760003560e01c80637b3c71d3116101ab578063d50572ee116100f7578063f0843ba811610095578063fc176c041161006f578063fc176c0414610b82578063fc4eee4214610ba2578063fc66ff1414610bb8578063fe0d94c114610bd857600080fd5b8063f0843ba814610b12578063f2b0653714610b32578063f682e04c14610b6257600080fd5b8063de7bc127116100d1578063de7bc127146109ec578063deaaa7cc14610a02578063e23a9a5214610a36578063e837159c14610afc57600080fd5b8063d50572ee146109a0578063da35c664146109b6578063ddf0b009146109cc57600080fd5b8063a6d8784a11610164578063c1a287e21161013e578063c1a287e214610933578063c4d66de81461094a578063c5a8425d1461096a578063c9fb9e871461098a57600080fd5b8063a6d8784a146108e7578063abaac6a8146108fd578063b58131b01461091d57600080fd5b80637b3c71d31461083c5780637bdbe4d01461085c5780637cae57bb14610871578063806bd5811461088757806386d37e8b146108a757806399533365146108c757600080fd5b80632fedff591161026a5780633e4f49e61161022357806350442098116101fd578063504420981461074657806356781388146107665780635c60da1b1461078657806366176743146107be57600080fd5b80633e4f49e6146106d957806340e58ee5146107065780634d6733d21461072657600080fd5b80632fedff59146105ee578063328dd9821461060e57806338bd0dda1461063e5780633932abb11461066b5780633af32abf146106815780633bccf4fd146106b957600080fd5b8063158ef93e116102d757806318b62629116102b157806318b626291461056e5780631dfb1b5a1461058457806320606b70146105a457806324bc1a64146105d857600080fd5b8063158ef93e146104f757806317977c611461052157806317ba1b8b1461054e57600080fd5b8063013cf08b1461031f57806302a251a31461042857806306fdde031461044c5780630825f38f146104a25780630ea2d98c146104b7578063140499ea146104d7575b600080fd5b34801561032b57600080fd5b506103b361033a3660046132ee565b60096020819052600091825260409091208054600182015460028301546007840154600885015495850154600a860154600b870154600c880154600d890154600e9099015497996001600160a01b0390971698959794969593949293919260ff808316936101008404821693620100009004909116918d565b604080519d8e526001600160a01b03909c1660208e01529a8c019990995260608b019790975260808a019590955260a089019390935260c088019190915260e08701521515610100860152151561012085015215156101408401526101608301526101808201526101a0015b60405180910390f35b34801561043457600080fd5b5061043e60045481565b60405190815260200161041f565b34801561045857600080fd5b506104956040518060400160405280601a81526020017f496e7465726573742050726f746f636f6c20476f7665726e6f7200000000000081525081565b60405161041f9190613363565b6104b56104b0366004613450565b610beb565b005b3480156104c357600080fd5b506104b56104d23660046132ee565b610e61565b3480156104e357600080fd5b506104b56104f23660046134d6565b610ec6565b34801561050357600080fd5b506012546105119060ff1681565b604051901515815260200161041f565b34801561052d57600080fd5b5061043e61053c3660046134d6565b600a6020526000908152604090205481565b34801561055a57600080fd5b506104b56105693660046132ee565b610f07565b34801561057a57600080fd5b5061043e600f5481565b34801561059057600080fd5b506104b561059f3660046132ee565b610f64565b3480156105b057600080fd5b5061043e7f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a86681565b3480156105e457600080fd5b5061043e60015481565b3480156105fa57600080fd5b506104b56106093660046132ee565b610fc1565b34801561061a57600080fd5b5061062e6106293660046132ee565b61101e565b60405161041f94939291906135ba565b34801561064a57600080fd5b5061043e6106593660046134d6565b600d6020526000908152604090205481565b34801561067757600080fd5b5061043e60035481565b34801561068d57600080fd5b5061051161069c3660046134d6565b6001600160a01b03166000908152600d6020526040902054421090565b3480156106c557600080fd5b506104b56106d4366004613623565b6112af565b3480156106e557600080fd5b506106f96106f43660046132ee565b611516565b60405161041f9190613687565b34801561071257600080fd5b506104b56107213660046132ee565b61169e565b34801561073257600080fd5b506104b56107413660046136af565b611b80565b34801561075257600080fd5b506104b56107613660046132ee565b611c45565b34801561077257600080fd5b506104b56107813660046136d9565b611ca2565b34801561079257600080fd5b506000546107a6906001600160a01b031681565b6040516001600160a01b03909116815260200161041f565b3480156107ca57600080fd5b506108146107d9366004613705565b601160209081526000928352604080842090915290825290205460ff808216916101008104909116906201000090046001600160601b031683565b60408051931515845260ff90921660208401526001600160601b03169082015260600161041f565b34801561084857600080fd5b506104b5610857366004613728565b611d09565b34801561086857600080fd5b5061043e600a81565b34801561087d57600080fd5b5061043e600c5481565b34801561089357600080fd5b506104b56108a23660046132ee565b611d58565b3480156108b357600080fd5b506104b56108c23660046132ee565b611db5565b3480156108d357600080fd5b506104b56108e23660046134d6565b611e12565b3480156108f357600080fd5b5061043e60155481565b34801561090957600080fd5b506104b56109183660046132ee565b611e8b565b34801561092957600080fd5b5061043e60055481565b34801561093f57600080fd5b5061043e6212750081565b34801561095657600080fd5b506104b56109653660046134d6565b611ee8565b34801561097657600080fd5b50600e546107a6906001600160a01b031681565b34801561099657600080fd5b5061043e60135481565b3480156109ac57600080fd5b5061043e60025481565b3480156109c257600080fd5b5061043e60075481565b3480156109d857600080fd5b506104b56109e73660046132ee565b611fd9565b3480156109f857600080fd5b5061043e60105481565b348015610a0e57600080fd5b5061043e7f150214d74d59b7d1e90c73fc22ef3d991dd0a76b046543d4d80ab92d2a50328f81565b348015610a4257600080fd5b50610acc610a51366004613705565b60408051606081018252600080825260208201819052918101919091525060009182526011602090815260408084206001600160a01b03939093168452918152918190208151606081018352905460ff8082161515835261010082041693820193909352620100009092046001600160601b03169082015290565b6040805182511515815260208084015160ff1690820152918101516001600160601b03169082015260600161041f565b348015610b0857600080fd5b5061043e60145481565b348015610b1e57600080fd5b506104b5610b2d3660046132ee565b61238f565b348015610b3e57600080fd5b50610511610b4d3660046132ee565b600b6020526000908152604090205460ff1681565b348015610b6e57600080fd5b5061043e610b7d3660046139b0565b6123ec565b348015610b8e57600080fd5b506104b5610b9d3660046132ee565b612a48565b348015610bae57600080fd5b5061043e60065481565b348015610bc457600080fd5b506008546107a6906001600160a01b031681565b6104b5610be63660046132ee565b612aa5565b60008585858585604051602001610c06959493929190613a91565b60408051601f1981840301815291815281516020928301206000818152600b90935291205490915060ff16610c7b5760405162461bcd60e51b81526020600482015260166024820152753a3c103430b9b713ba103132b2b71038bab2bab2b21760511b60448201526064015b60405180910390fd5b81421015610ccb5760405162461bcd60e51b815260206004820152601d60248201527f7478206861736e2774207375727061737365642074696d656c6f636b2e0000006044820152606401610c72565b610cd86212750083613af3565b421115610d165760405162461bcd60e51b815260206004820152600c60248201526b3a3c1034b99039ba30b6329760a11b6044820152606401610c72565b6000818152600b60205260409020805460ff191690558351606090610d3c575082610d68565b848051906020012084604051602001610d56929190613b0b565b60405160208183030381529060405290505b6000876001600160a01b03168783604051610d839190613b3c565b60006040518083038185875af1925050503d8060008114610dc0576040519150601f19603f3d011682016040523d82523d6000602084013e610dc5565b606091505b5050905080610e0f5760405162461bcd60e51b81526020600482015260166024820152753a3c1032bc32b1baba34b7b7103932bb32b93a32b21760511b6044820152606401610c72565b876001600160a01b0316837fa560e3198060a2f10670c1ec5b403077ea6ae93ca8de1c32b451dc1a943cd6e789898989604051610e4f9493929190613b58565b60405180910390a35050505050505050565b333014610e805760405162461bcd60e51b8152600401610c7290613b95565b600480549082905560408051828152602081018490527f7e3f7f0708a84de9203036abaa450dccc85ad5ff52f78c170f3edb55cf5e882891015b60405180910390a15050565b333014610ee55760405162461bcd60e51b8152600401610c7290613b95565b600880546001600160a01b0319166001600160a01b0392909216919091179055565b333014610f265760405162461bcd60e51b8152600401610c7290613b95565b600580549082905560408051828152602081018490527fccb45da8d5717e6c4544694297c4ba5cf151d455c9bb0ed4fc7a38411bc054619101610eba565b333014610f835760405162461bcd60e51b8152600401610c7290613b95565b600380549082905560408051828152602081018490527fc565b045403dc03c2eea82b81a0465edad9e2e7fc4d97e11421c209da93d7a939101610eba565b333014610fe05760405162461bcd60e51b8152600401610c7290613b95565b601480549082905560408051828152602081018490527f519a192fe8db9e38785eb494c69f530ddb21b9e34322f8d08fe29bd3849749889101610eba565b606080606080600060096000878152602001908152602001600020905080600301816004018260050183600601838054806020026020016040519081016040528092919081815260200182805480156110a057602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611082575b50505050509350828054806020026020016040519081016040528092919081815260200182805480156110f257602002820191906000526020600020905b8154815260200190600101908083116110de575b5050505050925081805480602002602001604051908101604052809291908181526020016000905b828210156111c657838290600052602060002001805461113990613bcc565b80601f016020809104026020016040519081016040528092919081815260200182805461116590613bcc565b80156111b25780601f10611187576101008083540402835291602001916111b2565b820191906000526020600020905b81548152906001019060200180831161119557829003601f168201915b50505050508152602001906001019061111a565b50505050915080805480602002602001604051908101604052809291908181526020016000905b8282101561129957838290600052602060002001805461120c90613bcc565b80601f016020809104026020016040519081016040528092919081815260200182805461123890613bcc565b80156112855780601f1061125a57610100808354040283529160200191611285565b820191906000526020600020905b81548152906001019060200180831161126857829003601f168201915b5050505050815260200190600101906111ed565b5050505090509450945094509450509193509193565b604080518082018252601a81527f496e7465726573742050726f746f636f6c20476f7665726e6f7200000000000060209182015281517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866818301527f75a838dcd8ee5903cc7f4a5799344d0080864f57a6e9911f8bdfb4c8ddce9b5481840152466060820152306080808301919091528351808303909101815260a0820184528051908301207f150214d74d59b7d1e90c73fc22ef3d991dd0a76b046543d4d80ab92d2a50328f60c083015260e0820189905260ff8816610100808401919091528451808403909101815261012083019094528351939092019290922061190160f01b6101408401526101428301829052610162830181905290916000906101820160408051601f198184030181528282528051602091820120600080855291840180845281905260ff8a169284019290925260608301889052608083018790529092509060019060a0016020604051602081039080840390855afa15801561143c573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661149f5760405162461bcd60e51b815260206004820181905260248201527f63617374566f746542795369673a20696e76616c6964207369676e61747572656044820152606401610c72565b88816001600160a01b03167fb8e138887d0aa13bab447e82de9d5c1777041ecd21ca36ba824ff1e6c07ddda48a6114d7858e8e612c90565b6040805160ff90931683526001600160601b039091166020830152606090820181905260009082015260800160405180910390a3505050505050505050565b6000816007541015801561152b575060065482115b6115775760405162461bcd60e51b815260206004820152601a60248201527f73746174653a20696e76616c69642070726f706f73616c2069640000000000006044820152606401610c72565b600082815260096020908152604080832060018101546001600160a01b03168452600d90925290912054600c82015442919091109060ff16156115be575060029392505050565b816007015443116115d3575060009392505050565b816008015443116115e8575060019392505050565b8080156115fc575081600d015482600a0154115b80611618575080158015611618575081600a0154826009015411155b80611633575080158015611633575081600d01548260090154105b15611642575060039392505050565b6002820154611655575060049392505050565b600c820154610100900460ff1615611671575060079392505050565b6212750082600201546116849190613af3565b4210611694575060069392505050565b5060059392505050565b60076116a982611516565b60078111156116ba576116ba613671565b14156117085760405162461bcd60e51b815260206004820152601d60248201527f63616e742063616e63656c2065786563757465642070726f706f73616c0000006044820152606401610c72565b600081815260096020526040902060018101546001600160a01b0316336001600160a01b0316146119755760018101546001600160a01b03166000908152600d6020526040902054421015611878576005546008546001838101546001600160a01b039283169263782d6fe1929116906117829043613c07565b6040516001600160e01b031960e085901b1681526001600160a01b039092166004830152602482015260440160206040518083038186803b1580156117c657600080fd5b505afa1580156117da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117fe9190613c1e565b6001600160601b03161080156118275750600e546001600160a01b0316336001600160a01b0316145b6118735760405162461bcd60e51b815260206004820152601c60248201527f63616e63656c3a2077686974656c69737465642070726f706f736572000000006044820152606401610c72565b611975565b6005546008546001838101546001600160a01b039283169263782d6fe1929116906118a39043613c07565b6040516001600160e01b031960e085901b1681526001600160a01b039092166004830152602482015260440160206040518083038186803b1580156118e757600080fd5b505afa1580156118fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061191f9190613c1e565b6001600160601b0316106119755760405162461bcd60e51b815260206004820181905260248201527f63616e63656c3a2070726f706f7365722061626f7665207468726573686f6c646044820152606401610c72565b600c8101805460ff1916600117905560005b6003820154811015611b5057611b3e8260030182815481106119ab576119ab613c47565b6000918252602090912001546004840180546001600160a01b0390921691849081106119d9576119d9613c47565b90600052602060002001548460050184815481106119f9576119f9613c47565b906000526020600020018054611a0e90613bcc565b80601f0160208091040260200160405190810160405280929190818152602001828054611a3a90613bcc565b8015611a875780601f10611a5c57610100808354040283529160200191611a87565b820191906000526020600020905b815481529060010190602001808311611a6a57829003601f168201915b5050505050856006018581548110611aa157611aa1613c47565b906000526020600020018054611ab690613bcc565b80601f0160208091040260200160405190810160405280929190818152602001828054611ae290613bcc565b8015611b2f5780601f10611b0457610100808354040283529160200191611b2f565b820191906000526020600020905b815481529060010190602001808311611b1257829003601f168201915b50505050508660020154612f12565b80611b4881613c5d565b915050611987565b5060405182907f789cf55be980739dad1d0699b93b58e806b51c9d96619bfa8fe0a28abaa7b30c90600090a25050565b333014611b9f5760405162461bcd60e51b8152600401610c7290613b95565b42601554611bad9190613af3565b8110611bf45760405162461bcd60e51b81526020600482015260166024820152750caf0e0d2e4c2e8d2dedc40caf0c6cacac8e640dac2f60531b6044820152606401610c72565b6001600160a01b0382166000818152600d6020908152604091829020849055815192835282018390527f4e7b7545bc5744d0e30425959f4687475774b6c7edad77d24cb51c7d967d45159101610eba565b333014611c645760405162461bcd60e51b8152600401610c7290613b95565b601080549082905560408051828152602081018490527f2a61b867418a359864adca8bb250ea65ee8bd41dbfd0279198d8e7552d4a27c29101610eba565b81337fb8e138887d0aa13bab447e82de9d5c1777041ecd21ca36ba824ff1e6c07ddda483611cd1838583612c90565b6040805160ff90931683526001600160601b039091166020830152606090820181905260009082015260800160405180910390a35050565b83337fb8e138887d0aa13bab447e82de9d5c1777041ecd21ca36ba824ff1e6c07ddda485611d38838583612c90565b8686604051611d4a9493929190613c78565b60405180910390a350505050565b333014611d775760405162461bcd60e51b8152600401610c7290613b95565b601380549082905560408051828152602081018490527f8cb5451eee8feb516cec9cd600201bbc31a30886d70c841a085a3fa69a4294d19101610eba565b333014611dd45760405162461bcd60e51b8152600401610c7290613b95565b600180549082905560408051828152602081018490527fa74554b0f53da47d07ec571d712428b3720460f54f81375fbcf78f6b5f72e7ed9101610eba565b333014611e315760405162461bcd60e51b8152600401610c7290613b95565b600e80546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f80a07e73e552148844a9c216d9724212d609cfa54e9c1a2e97203bdd2c4ad3419101610eba565b333014611eaa5760405162461bcd60e51b8152600401610c7290613b95565b600f80549082905560408051828152602081018490527f80a384652af83fc00bfd40ef94edda7ede83e7db39931b2c889821573f314e239101610eba565b60125460ff1615611f3b5760405162461bcd60e51b815260206004820152601860248201527f616c7265616479206265656e20696e697469616c697a656400000000000000006044820152606401610c72565b600880546001600160a01b0319166001600160a01b0392909216919091179055619d8060045561335460035569d3c21bcecceda10000006005556202a300600c5560006007556a084595161401484a00000060019081556a21165458500521280000006002556119aa600f5561a8c06010556a01a784379d99db420000006013556146506014556301e133806015556012805460ff19169091179055565b6004611fe482611516565b6007811115611ff557611ff5613671565b146120425760405162461bcd60e51b815260206004820152601f60248201527f63616e206f6e6c792062652071756575656420696620737563636565646564006044820152606401610c72565b6000818152600960205260408120600e8101549091906120629042613af3565b905060005b600383015481101561234d57600b600084600301838154811061208c5761208c613c47565b6000918252602090912001546004860180546001600160a01b0390921691859081106120ba576120ba613c47565b90600052602060002001548660050185815481106120da576120da613c47565b906000526020600020018760060186815481106120f9576120f9613c47565b9060005260206000200187604051602001612118959493929190613d62565b60408051601f198184030181529181528151602092830120835290820192909252016000205460ff161561218e5760405162461bcd60e51b815260206004820152601760248201527f70726f706f73616c20616c7265616479207175657565640000000000000000006044820152606401610c72565b61233a8360030182815481106121a6576121a6613c47565b6000918252602090912001546004850180546001600160a01b0390921691849081106121d4576121d4613c47565b90600052602060002001548560050184815481106121f4576121f4613c47565b90600052602060002001805461220990613bcc565b80601f016020809104026020016040519081016040528092919081815260200182805461223590613bcc565b80156122825780601f1061225757610100808354040283529160200191612282565b820191906000526020600020905b81548152906001019060200180831161226557829003601f168201915b505050505086600601858154811061229c5761229c613c47565b9060005260206000200180546122b190613bcc565b80601f01602080910402602001604051908101604052809291908181526020018280546122dd90613bcc565b801561232a5780601f106122ff5761010080835404028352916020019161232a565b820191906000526020600020905b81548152906001019060200180831161230d57829003601f168201915b50505050508688600e0154612fac565b508061234581613c5d565b915050612067565b506002820181905560405181815283907f9a2e42fd6722813d69113e7d0079d3d940171428df7373df9c7f7617cfda28929060200160405180910390a2505050565b3330146123ae5760405162461bcd60e51b8152600401610c7290613b95565b600280549082905560408051828152602081018490527fc2adf06da6765dba7faaccde4c0ce3f91c35dd3390e7f0b6bc2844202c9fa9529101610eba565b6000600154600014156124365760405162461bcd60e51b8152602060048201526012602482015271436861726c6965206e6f742061637469766560701b6044820152606401610c72565b6005546008546001600160a01b031663782d6fe133612456600143613c07565b6040516001600160e01b031960e085901b1681526001600160a01b039092166004830152602482015260440160206040518083038186803b15801561249a57600080fd5b505afa1580156124ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124d29190613c1e565b6001600160601b03161015806124ec57506124ec3361069c565b6125385760405162461bcd60e51b815260206004820152601e60248201527f766f7465732062656c6f772070726f706f73616c207468726573686f6c6400006044820152606401610c72565b8551875114801561254a575084518751145b8015612557575083518751145b6125a35760405162461bcd60e51b815260206004820152601a60248201527f696e666f726d6174696f6e206172697479206d69736d617463680000000000006044820152606401610c72565b86516125e85760405162461bcd60e51b81526020600482015260146024820152736d7573742070726f7669646520616374696f6e7360601b6044820152606401610c72565b600a8751111561262d5760405162461bcd60e51b815260206004820152601060248201526f746f6f206d616e7920616374696f6e7360801b6044820152606401610c72565b336000908152600a6020526040902054801561271657600061264e82611516565b9050600181600781111561266457612664613671565b14156126b25760405162461bcd60e51b815260206004820152601e60248201527f6f6e65206c6976652070726f706f73616c207065722070726f706f73657200006044820152606401610c72565b60008160078111156126c6576126c6613671565b14156127145760405162461bcd60e51b815260206004820152601e60248201527f6f6e65206c6976652070726f706f73616c207065722070726f706f73657200006044820152606401610c72565b505b6007805490600061272683613c5d565b9190505550600060405180610220016040528060075481526020016127483390565b6001600160a01b03168152602001600081526020018a8152602001898152602001888152602001878152602001600354436127839190613af3565b8152602001600454600354436127999190613af3565b6127a39190613af3565b815260200160008152602001600081526020016000815260200160001515815260200160001515815260200185151581526020016001548152602001600c5481525090508380156127fa57506127f83361069c565b155b1561282c574360e08201819052600f5461281391613af3565b6101008201526002546101e08201526010546102008201525b6128353361069c565b15612876576013546101e08201526014546128509043613af3565b60e08201526004546014546128659043613af3565b61286f9190613af3565b6101008201525b805160009081526009602090815260409182902083518155818401516001820180546001600160a01b0319166001600160a01b03909216919091179055918301516002830155606083015180518493926128d792600385019291019061309d565b50608082015180516128f3916004840191602090910190613102565b5060a0820151805161290f91600584019160209091019061313d565b5060c0820151805161292b916006840191602090910190613196565b5060e08281015160078301556101008084015160088401556101208401516009840155610140840151600a80850191909155610160850151600b850155610180850151600c850180546101a08801516101c089015161ffff1990921693151561ff0019169390931792151585029290921762ff0000191662010000921515929092029190911790556101e0850151600d85015561020090940151600e9093019290925583516020808601516001600160a01b0316600090815294905260409384902055830151835191840151925190923392917f7d84a6263ae0d98d3329bd7b46bb4e8d6f98cd35a7adb45c274c8b7fd5ebd5e091612a33918f918f918f918f918f90613d9b565b60405180910390a45198975050505050505050565b333014612a675760405162461bcd60e51b8152600401610c7290613b95565b600c80549082905560408051828152602081018490527fed0229422af39d4d7d33f7a27d31d6f5cb20ec628293da58dd6e8a528ed466be9101610eba565b6005612ab082611516565b6007811115612ac157612ac1613671565b14612b0e5760405162461bcd60e51b815260206004820152601c60248201527f63616e206f6e6c792062652065786563276420696620717565756564000000006044820152606401610c72565b6000818152600960205260408120600c8101805461ff001916610100179055905b6003820154811015612c6057306001600160a01b0316630825f38f836004018381548110612b5f57612b5f613c47565b9060005260206000200154846003018481548110612b7f57612b7f613c47565b6000918252602090912001546004860180546001600160a01b039092169186908110612bad57612bad613c47565b9060005260206000200154866005018681548110612bcd57612bcd613c47565b90600052602060002001876006018781548110612bec57612bec613c47565b9060005260206000200188600201546040518763ffffffff1660e01b8152600401612c1b959493929190613d62565b6000604051808303818588803b158015612c3457600080fd5b505af1158015612c48573d6000803e3d6000fd5b50505050508080612c5890613c5d565b915050612b2f565b5060405182907f712ae1383f79ac853f8d882153778e0260ef8f03b504e2866e0593e04d2b291f90600090a25050565b60006001612c9d84611516565b6007811115612cae57612cae613671565b14612cee5760405162461bcd60e51b815260206004820152601060248201526f1d9bdd1a5b99c81a5cc818db1bdcd95960821b6044820152606401610c72565b60028260ff161115612d365760405162461bcd60e51b8152602060048201526011602482015270696e76616c696420766f7465207479706560781b6044820152606401610c72565b6000838152600960209081526040808320601183528184206001600160a01b0389168552909252909120805460ff1615612da85760405162461bcd60e51b81526020600482015260136024820152721d9bdd195c88185b1c9958591e481d9bdd1959606a1b6044820152606401610c72565b600854600783015460405163782d6fe160e01b81526000926001600160a01b03169163782d6fe191612df2918b916004016001600160a01b03929092168252602082015260400190565b60206040518083038186803b158015612e0a57600080fd5b505afa158015612e1e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e429190613c1e565b905060ff8516612e6f57806001600160601b031683600a0154612e659190613af3565b600a840155612ec9565b8460ff1660011415612e9e57806001600160601b03168360090154612e949190613af3565b6009840155612ec9565b8460ff1660021415612ec957806001600160601b031683600b0154612ec39190613af3565b600b8401555b81546001600160601b03821662010000026dffffffffffffffffffffffff00001960ff88166101000261ffff199093169290921760011791909116179091559150509392505050565b60008585858585604051602001612f2d959493929190613a91565b60408051601f1981840301815282825280516020918201206000818152600b909252919020805460ff1916905591506001600160a01b0387169082907f2fffc091a501fd91bfbff27141450d3acb40fb8e6d8382b243ec7a812a3aaf8790612f9c908990899089908990613b58565b60405180910390a3505050505050565b6000612fb88242613af3565b831015612ffd5760405162461bcd60e51b815260206004820152601360248201527236bab9ba1039b0ba34b9b33c903232b630bc9760691b6044820152606401610c72565b60008787878787604051602001613018959493929190613a91565b60408051601f1981840301815282825280516020918201206000818152600b909252919020805460ff1916600117905591506001600160a01b0389169082907f76e2796dc3a81d57b0e8504b647febcbeeb5f4af818e164f11eef8131a6a763f9061308a908b908b908b908b90613b58565b60405180910390a3979650505050505050565b8280548282559060005260206000209081019282156130f2579160200282015b828111156130f257825182546001600160a01b0319166001600160a01b039091161782556020909201916001909101906130bd565b506130fe9291506131ef565b5090565b8280548282559060005260206000209081019282156130f2579160200282015b828111156130f2578251825591602001919060010190613122565b82805482825590600052602060002090810192821561318a579160200282015b8281111561318a578251805161317a918491602090910190613204565b509160200191906001019061315d565b506130fe929150613277565b8280548282559060005260206000209081019282156131e3579160200282015b828111156131e357825180516131d3918491602090910190613204565b50916020019190600101906131b6565b506130fe929150613294565b5b808211156130fe57600081556001016131f0565b82805461321090613bcc565b90600052602060002090601f01602090048101928261323257600085556130f2565b82601f1061324b57805160ff19168380011785556130f2565b828001600101855582156130f257918201828111156130f2578251825591602001919060010190613122565b808211156130fe57600061328b82826132b1565b50600101613277565b808211156130fe5760006132a882826132b1565b50600101613294565b5080546132bd90613bcc565b6000825580601f106132cd575050565b601f0160209004906000526020600020908101906132eb91906131ef565b50565b60006020828403121561330057600080fd5b5035919050565b60005b8381101561332257818101518382015260200161330a565b83811115613331576000848401525b50505050565b6000815180845261334f816020860160208601613307565b601f01601f19169290920160200192915050565b6020815260006133766020830184613337565b9392505050565b80356001600160a01b038116811461339457600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff811182821017156133d8576133d8613399565b604052919050565b600082601f8301126133f157600080fd5b813567ffffffffffffffff81111561340b5761340b613399565b61341e601f8201601f19166020016133af565b81815284602083860101111561343357600080fd5b816020850160208301376000918101602001919091529392505050565b600080600080600060a0868803121561346857600080fd5b6134718661337d565b945060208601359350604086013567ffffffffffffffff8082111561349557600080fd5b6134a189838a016133e0565b945060608801359150808211156134b757600080fd5b506134c4888289016133e0565b95989497509295608001359392505050565b6000602082840312156134e857600080fd5b6133768261337d565b600081518084526020808501945080840160005b8381101561352a5781516001600160a01b031687529582019590820190600101613505565b509495945050505050565b600081518084526020808501945080840160005b8381101561352a57815187529582019590820190600101613549565b600081518084526020808501808196508360051b8101915082860160005b858110156135ad57828403895261359b848351613337565b98850198935090840190600101613583565b5091979650505050505050565b6080815260006135cd60808301876134f1565b82810360208401526135df8187613535565b905082810360408401526135f38186613565565b905082810360608401526136078185613565565b979650505050505050565b803560ff8116811461339457600080fd5b600080600080600060a0868803121561363b57600080fd5b8535945061364b60208701613612565b935061365960408701613612565b94979396509394606081013594506080013592915050565b634e487b7160e01b600052602160045260246000fd5b60208101600883106136a957634e487b7160e01b600052602160045260246000fd5b91905290565b600080604083850312156136c257600080fd5b6136cb8361337d565b946020939093013593505050565b600080604083850312156136ec57600080fd5b823591506136fc60208401613612565b90509250929050565b6000806040838503121561371857600080fd5b823591506136fc6020840161337d565b6000806000806060858703121561373e57600080fd5b8435935061374e60208601613612565b9250604085013567ffffffffffffffff8082111561376b57600080fd5b818701915087601f83011261377f57600080fd5b81358181111561378e57600080fd5b8860208285010111156137a057600080fd5b95989497505060200194505050565b600067ffffffffffffffff8211156137c9576137c9613399565b5060051b60200190565b600082601f8301126137e457600080fd5b813560206137f96137f4836137af565b6133af565b82815260059290921b8401810191818101908684111561381857600080fd5b8286015b8481101561383a5761382d8161337d565b835291830191830161381c565b509695505050505050565b600082601f83011261385657600080fd5b813560206138666137f4836137af565b82815260059290921b8401810191818101908684111561388557600080fd5b8286015b8481101561383a5780358352918301918301613889565b600082601f8301126138b157600080fd5b813560206138c16137f4836137af565b82815260059290921b840181019181810190868411156138e057600080fd5b8286015b8481101561383a57803567ffffffffffffffff8111156139045760008081fd5b6139128986838b01016133e0565b8452509183019183016138e4565b600082601f83011261393157600080fd5b813560206139416137f4836137af565b82815260059290921b8401810191818101908684111561396057600080fd5b8286015b8481101561383a57803567ffffffffffffffff8111156139845760008081fd5b6139928986838b01016133e0565b845250918301918301613964565b8035801515811461339457600080fd5b60008060008060008060c087890312156139c957600080fd5b863567ffffffffffffffff808211156139e157600080fd5b6139ed8a838b016137d3565b97506020890135915080821115613a0357600080fd5b613a0f8a838b01613845565b96506040890135915080821115613a2557600080fd5b613a318a838b016138a0565b95506060890135915080821115613a4757600080fd5b613a538a838b01613920565b94506080890135915080821115613a6957600080fd5b50613a7689828a016133e0565b925050613a8560a088016139a0565b90509295509295509295565b60018060a01b038616815284602082015260a060408201526000613ab860a0830186613337565b8281036060840152613aca8186613337565b9150508260808301529695505050505050565b634e487b7160e01b600052601160045260246000fd5b60008219821115613b0657613b06613add565b500190565b6001600160e01b0319831681528151600090613b2e816004850160208701613307565b919091016004019392505050565b60008251613b4e818460208701613307565b9190910192915050565b848152608060208201526000613b716080830186613337565b8281036040840152613b838186613337565b91505082606083015295945050505050565b60208082526017908201527f6d75737420636f6d652066726f6d2074686520676f762e000000000000000000604082015260600190565b600181811c90821680613be057607f821691505b60208210811415613c0157634e487b7160e01b600052602260045260246000fd5b50919050565b600082821015613c1957613c19613add565b500390565b600060208284031215613c3057600080fd5b81516001600160601b038116811461337657600080fd5b634e487b7160e01b600052603260045260246000fd5b6000600019821415613c7157613c71613add565b5060010190565b60ff851681526001600160601b038416602082015260606040820152816060820152818360808301376000818301608090810191909152601f909201601f191601019392505050565b8054600090600181811c9080831680613cdb57607f831692505b6020808410821415613cfd57634e487b7160e01b600052602260045260246000fd5b838852818015613d145760018114613d2857613d56565b60ff19861689830152604089019650613d56565b876000528160002060005b86811015613d4e5781548b8201850152908501908301613d33565b8a0183019750505b50505050505092915050565b60018060a01b038616815284602082015260a060408201526000613d8960a0830186613cc1565b8281036060840152613aca8186613cc1565b60c081526000613dae60c08301896134f1565b8281036020840152613dc08189613535565b90508281036040840152613dd48188613565565b90508281036060840152613de88187613565565b905084608084015282810360a0840152613e028185613337565b999850505050505050505056fe"), + ("0xDb53f47aC61FE54F456A4eb3E09832D08Dd7BEec", "PoolExercise", "6101c06040523480156200001257600080fd5b50604051620030713803806200307183398101604081905262000035916200016a565b6001600160a01b038681166101005285811660805284811660a05283811660c052821660e052600f81900b61012052858585858585620000846000808062000101602090811b6200011917901c565b6101408181525050620000a660016000806200010160201b620001191760201c565b6101608181525050620000c860026000806200010160201b620001191760201c565b6101808181525050620000ea60036000806200010160201b620001191760201c565b6101a05250620002319a5050505050505050505050565b600081600f0b6080846001600160401b0316901b60f88660078111156200012c576200012c620001f4565b6200013992911b6200020a565b6200014591906200020a565b949350505050565b80516001600160a01b03811681146200016557600080fd5b919050565b60008060008060008060c087890312156200018457600080fd5b6200018f876200014d565b95506200019f602088016200014d565b9450620001af604088016200014d565b9350620001bf606088016200014d565b9250620001cf608088016200014d565b915060a087015180600f0b8114620001e657600080fd5b809150509295509295509295565b634e487b7160e01b600052602160045260246000fd5b600082198211156200022c57634e487b7160e01b600052601160045260246000fd5b500190565b60805160a05160c05160e05161010051610120516101405161016051610180516101a051612d6f6200030260003960008181610e1c015261137b015260008181610e420152818161135101526113f4015260008181611327015281816114b801526122c90152600081816112fe015281816113cb0152818161148f015281816114e101526122ef0152600081816103a8015281816107ed0152610cda0152600050506000818161176601526117b201526000610485015260008181611964015261212c015260005050612d6f6000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063477130981461003b578063b50e7ee314610050575b600080fd5b61004e610049366004612986565b610063565b005b61004e61005e3660046129c7565b610109565b336001600160a01b038416146100f9576001600160a01b03831660009081527f1799cf914cb0cb442ca7c7ac709ee40d0cb89e87351dc08d517fbda27d50c68c6020908152604080832033845290915290205460ff166100f95760405162461bcd60e51b815260206004820152600c60248201526b1b9bdd08185c1c1c9bdd995960a21b60448201526064015b60405180910390fd5b61010483838361015f565b505050565b6101156000838361015f565b5050565b600081600f0b60808467ffffffffffffffff16901b60f8866007811115610142576101426129e9565b61014d92911b612a15565b6101579190612a15565b949350505050565b608082901c8260006001600160a01b0386161560f883901c600481600781111561018b5761018b6129e9565b14806101a8575060068160078111156101a6576101a66129e9565b145b6101e35760405162461bcd60e51b815260206004820152600c60248201526b696e76616c6964207479706560a01b60448201526064016100f0565b8115806101f95750428567ffffffffffffffff16105b6102335760405162461bcd60e51b815260206004820152600b60248201526a1b9bdd08195e1c1a5c995960aa1b60448201526064016100f0565b6004816007811115610247576102476129e9565b149250506000610262600080516020612d1a83398151915290565b9050600061026f826104c0565b9050428667ffffffffffffffff16101561029a576102978267ffffffffffffffff8816610526565b90505b82806102be5750836102b45784600f0b81600f0b126102be565b84600f0b81600f0b135b6102f45760405162461bcd60e51b81526020600482015260076024820152666e6f742049544d60c81b60448201526064016100f0565b6000841561033a5785600f0b82600f0b1315610335576103328861032984610320600f82900b8b61061e565b600f0b90610659565b600f0b906106b1565b90505b610367565b85600f0b82600f0b12156103675761036461035d89610329600f8a900b8661061e565b8490610719565b90505b6000841561038c5761037b89838c89610754565b6103859082612a15565b9050610454565b6103978b8b8b6108cb565b600082156103ff576103d58c6103d07f0000000000000000000000000000000000000000000000000000000000000000600f0b866106b1565b610a5d565b90506103e18183612a15565b91506103ff8c6103f089610a8c565b6103fa8487612a2d565b610ae1565b604080518c8152602081018c9052908101849052606081018290526001600160a01b038d16907f31939b125e073bbdbf69ac6eb0cb59489894a9bea509d658589af5917b53cca19060800160405180910390a2505b610474898361046e6104678a6000610bb1565b8c8c610119565b89610be4565b61047e9082612a15565b90506104b37f00000000000000000000000000000000000000000000000000000000000000006104ad88610e13565b83610e67565b5050505050505050505050565b60004282600c015414156104de576104d88242610e82565b92915050565b6104e782610eb0565b90506104f38242610e82565b600f0b61050557610505824283610fd3565b42600c830155610516826001611051565b610521826000611051565b919050565b600080610535610e1084612a5a565b600881901c6000818152601287016020526040812054929350909160ff84169190821b821c90610568620e100042612a5a565b90505b811580156105795750808411155b156105a65760128801600061058d86612a7c565b955085815260200190815260200160002054915061056b565b600060805b80156105d25783811c156105ca576105c38183612a15565b93811c9391505b60011c6105ab565b5060118901600060018360086105e88a84612a15565b6105f392911b612a2d565b6105fd9190612a2d565b8152602081019190915260400160002054600f0b9998505050505050505050565b6000600f82810b9084900b0360016001607f1b03198112801590610649575060016001607f1b038113155b61065257600080fd5b9392505050565b600081600f0b6000141561066c57600080fd5b600082600f0b604085600f0b901b8161068757610687612a44565b05905060016001607f1b03198112801590610649575060016001607f1b0381131561065257600080fd5b6000816106c0575060006104d8565b600083600f0b12156106d157600080fd5b600f83900b6001600160801b038316810260401c90608084901c026001600160c01b0381111561070057600080fd5b60401b811981111561071157600080fd5b019392505050565b600080610737838560030160149054906101000a900460ff166110e1565b9050610157818560030160159054906101000a900460ff166110f7565b60008281527fb31c2c74f86ca3ce94d901f5f5bbe66f7161eec2f7b5aa0b75a86371436424eb602052604081205b85156108c25760006107a9600161079884611112565b6107a29190612a2d565b839061111c565b905060006107b78287611128565b9050878111156107c45750865b600080881561084657896107d8848b612a97565b6107e29190612a5a565b9150610815846103d07f0000000000000000000000000000000000000000000000000000000000000000600f0b856106b1565b90506108218187612a15565b955061082d828a612a2d565b98506108468461083c89610a8c565b6103fa8486612a2d565b610850838b612a2d565b99506001600160a01b0384167f31939b125e073bbdbf69ac6eb0cb59489894a9bea509d658589af5917b53cca189856108898587612a2d565b604080519384526020840192909252908201526060810184905260800160405180910390a26108b98489856108cb565b50505050610782565b50949350505050565b6001600160a01b03831661092d5760405162461bcd60e51b815260206004820152602360248201527f455243313135353a206275726e2066726f6d20746865207a65726f206164647260448201526265737360e81b60648201526084016100f0565b61095b3384600061093d866111db565b610946866111db565b60405180602001604052806000815250611226565b60008281527f1799cf914cb0cb442ca7c7ac709ee40d0cb89e87351dc08d517fbda27d50c68b602090815260408083206001600160a01b038716845291829052909120548211156109fc5760405162461bcd60e51b815260206004820152602560248201527f455243313135353a206275726e20616d6f756e7420657863656564732062616c604482015264616e63657360d81b60648201526084016100f0565b6001600160a01b03841660008181526020838152604080832080548790039055805187815291820186905291929133917fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62910160405180910390a450505050565b600080610a6984611762565b9050612710610a788285612a97565b610a829190612a5a565b6101579084612a2d565b600081610ab157600080516020612d1a833981519152546001600160a01b03166104d8565b50507fbbd6af8edd89d04327b00c29df7f272b9b1ae01bf6d9c54a784f935706df52ec546001600160a01b031690565b80610aeb57505050565b60405163a9059cbb60e01b81526001600160a01b0384811660048301526024820183905283169063a9059cbb90604401602060405180830381600087803b158015610b3557600080fd5b505af1158015610b49573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b6d9190612ab6565b6101045760405162461bcd60e51b8152602060048201526015602482015274115490cc8c081d1c985b9cd9995c8819985a5b1959605a1b60448201526064016100f0565b60008215610bcf5781610bc5576005610bc8565b60045b90506104d8565b81610bdb576007610652565b60069392505050565b60008281527fb31c2c74f86ca3ce94d901f5f5bbe66f7161eec2f7b5aa0b75a86371436424eb60205260408120835b8615610e09576000610c3a6001610c2985611112565b610c339190612a2d565b849061111c565b90506000610c488288611128565b905088811115610c555750875b600089610c62838b612a97565b610c6c9190612a5a565b9050610c78818a612a2d565b9850610c84828b612a2d565b9950600087610cc35781610cb4610c9f600f88900b866106b1565b600080516020612d1a83398151915290610719565b610cbe9190612a2d565b610ccd565b610ccd8284612a2d565b90506000610d02856103d07f0000000000000000000000000000000000000000000000000000000000000000600f0b856106b1565b9050610d0e8189612a15565b975082610d2a600080516020612d1a833981519152878c61182c565b15610d5457610d4386610d3d8486612a2d565b8c611869565b610d4d8282612a15565b9050610d7d565b610d7086610d618c610e13565b610d6b8587612a2d565b610e67565b610d7a8382612a15565b90505b610d97600080516020612d1a833981519152878c8461192c565b610da2868c876108cb565b6001600160a01b0386167f69a2ef6bf9e7ff92cbf1b71963ba1751b1abe8f99e3b3aae2ab99e416df614938c610dd88587612a2d565b60408051928352602083019190915281018890526060810185905260800160405180910390a2505050505050610c13565b5050949350505050565b600081610e40577f00000000000000000000000000000000000000000000000000000000000000006104d8565b7f000000000000000000000000000000000000000000000000000000000000000092915050565b61010483838360405180602001604052806000815250611a6c565b60006011830181610e95610e1085612a5a565b8152602081019190915260400160002054600f0b9392505050565b6000808260030160009054906101000a90046001600160a01b03166001600160a01b03166350d25bcd6040518163ffffffff1660e01b815260040160206040518083038186803b158015610f0357600080fd5b505afa158015610f17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f3b9190612ad8565b905060008360020160009054906101000a90046001600160a01b03166001600160a01b03166350d25bcd6040518163ffffffff1660e01b815260040160206040518083038186803b158015610f8f57600080fd5b505afa158015610fa3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fc79190612ad8565b90506101578282611b93565b6000610fe1610e1084612a5a565b6000818152601186016020526040902080546001600160801b0319166001600160801b038516179055905061101a60ff80831690612a2d565b6001901b846012016000600884901c815260200190815260200160002060008282546110469190612a15565b909155505050505050565b80151560009081526013830160205260409020805415806110725750805442105b1561107c57505050565b60006110888484611c2e565b90506110c384826110bd6110b286600101546110ad898b611c9890919063ffffffff16565b6110e1565b600f86900b90611cc6565b86611cf9565b50501515600090815260139091016020526040812081815560010155565b6000610652836110f284600a612bd5565b611d76565b600061065261110783600a612bd5565b600f85900b906106b1565b60006104d8825490565b60006106528383611dad565b60006001600160a01b0383166111945760405162461bcd60e51b815260206004820152602b60248201527f455243313135353a2062616c616e636520717565727920666f7220746865207a60448201526a65726f206164647265737360a81b60648201526084016100f0565b7f1799cf914cb0cb442ca7c7ac709ee40d0cb89e87351dc08d517fbda27d50c68b6000928352602090815260408084206001600160a01b0395909516845293905250205490565b6040805160018082528183019092526060916000919060208083019080368337019050509050828160008151811061121557611215612be4565b602090810291909101015292915050565b611234868686868686611e33565b600080516020612d1a83398151915260005b845181101561175857600085828151811061126357611263612be4565b60200260200101519050600085838151811061128157611281612be4565b60200260200101519050806000141561129b575050611746565b6001600160a01b0389166112b8576112b66015850183612011565b505b6001600160a01b0388161580156112e857506000828152600080516020612cfa8339815191526020526040902054155b156112fc576112fa601585018361201d565b505b7f000000000000000000000000000000000000000000000000000000000000000082148061134957507f000000000000000000000000000000000000000000000000000000000000000082145b8061137357507f000000000000000000000000000000000000000000000000000000000000000082145b8061139d57507f000000000000000000000000000000000000000000000000000000000000000082145b1561148d576001600160a01b038916158015906113c257506001600160a01b03881615155b1561148d5760007f000000000000000000000000000000000000000000000000000000000000000083148061141657507f000000000000000000000000000000000000000000000000000000000000000083145b6001600160a01b038b166000908152600d870160209081526040808320841515845290915290205490915042906114509062015180612a15565b1061148b5760405162461bcd60e51b815260206004820152600b60248201526a1b1a5c481b1bd8dac80c5960aa1b60448201526064016100f0565b505b7f00000000000000000000000000000000000000000000000000000000000000008214806114da57507f000000000000000000000000000000000000000000000000000000000000000082145b15611682577f00000000000000000000000000000000000000000000000000000000000000008214600061150e8683612029565b90506001600160a01b038b161561163857600061152b8c86611128565b9050818111801561154557506115418285612a15565b8111155b156115dd576001600160a01b038c166000908152601488016020908152604080832086151580855260138c01845282852054855290835281842090845290915290205484906115949083612a2d565b10156115d25760405162461bcd60e51b815260206004820152600d60248201526c496e7375662062616c616e636560981b60448201526064016100f0565b6115dd878d85612043565b6001600160a01b038b161561163657611611878d858c8a8151811061160457611604612be4565b602002602001015161192c565b611636878c858c8a8151811061162957611629612be4565b60200260200101516120f4565b505b6001600160a01b038a161561167f5760006116538b86611128565b905081811115801561166d57508161166b8583612a15565b115b1561167d5761167d878c85612218565b505b50505b60f882901c826001600160a01b038b16158015906116a857506001600160a01b038a1615155b80156116e0575060058260078111156116c3576116c36129e9565b14806116e0575060078260078111156116de576116de6129e9565b145b1561174157600060058360078111156116fb576116fb6129e9565b1490506000816117225761171d611716600f85900b876106b1565b8990610719565b611724565b845b9050611732888e848461192c565b61173e888d84846120f4565b50505b505050505b8061175081612a7c565b915050611246565b5050505050505050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031615610521576040516303793c8d60e11b81526001600160a01b0383811660048301527f000000000000000000000000000000000000000000000000000000000000000016906306f2791a9060240160206040518083038186803b1580156117f457600080fd5b505afa158015611808573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104d89190612ad8565b6001600160a01b0382166000908152600e840160209081526040808320841515845290915281205480158061186057504281115b95945050505050565b600080516020612d1a83398151915261188b84611885846122c0565b85610e67565b60006101048061189b8142612a5a565b6118a59190612a97565b6118af9190612a15565b6001600160a01b03861660009081526014840160209081526040808320848452825280832087151584529091528120805492935086929091906118f3908490612a15565b90915550508215156000908152601383016020526040812060018101805491928792611920908490612a15565b90915550505550505050565b6001600160a01b03808416600090815260178601602090815260408083208615158452825280832054601889019092529091205490917f00000000000000000000000000000000000000000000000000000000000000001663edaf7d5b863087866119978982612a2d565b6040516001600160e01b031960e088901b1681526001600160a01b03958616600482015294909316602485015290151560448401526064830152608482015260a4810184905260c401600060405180830381600087803b1580156119fa57600080fd5b505af1158015611a0e573d6000803e3d6000fd5b505050508282611a1e9190612a2d565b6001600160a01b038616600090815260178801602090815260408083208815158452909152902055611a508382612a2d565b9315156000908152601890960160205250506040909320555050565b6001600160a01b038416611acc5760405162461bcd60e51b815260206004820152602160248201527f455243313135353a206d696e7420746f20746865207a65726f206164647265736044820152607360f81b60648201526084016100f0565b611aeb33600086611adc876111db565b611ae5876111db565b86611226565b60008381527f1799cf914cb0cb442ca7c7ac709ee40d0cb89e87351dc08d517fbda27d50c68b602090815260408083206001600160a01b0388168452918290528220805491928592611b3e908490612a15565b909155505060408051858152602081018590526001600160a01b0387169160009133917fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62910160405180910390a45050505050565b600081611b9f57600080fd5b600080841215611bb457836000039350600190505b6000831215611bc65760009290920391155b6000611bd28585612314565b90508115611c00576001607f1b816001600160801b03161115611bf457600080fd5b60000391506104d89050565b60016001607f1b03816001600160801b03161115611c1d57600080fd5b91506104d89050565b505092915050565b600080611c4b83611c40576001611c43565b60005b600080610119565b831515600090815260138601602052604090206001015490915061015790600080516020612cfa83398151915260008481526020919091526040902054611c929190612a2d565b6110ad86865b600081611cb3576003830154600160a81b900460ff16610652565b505060030154600160a01b900460ff1690565b6000600f83810b9083900b0160016001607f1b03198112801590610649575060016001607f1b0381131561065257600080fd5b6000611d058583612476565b90506000611d16868387878761248f565b9050611d23868285612595565b60408051600f83810b825287810b602083015286900b818301529051841515917f4e23621c6f591f14bf9505cb8326b45af9dc6c5569fd608de2a7a2ddd6146b2e919081900360600190a2505050505050565b600081611d8257600080fd5b6000611d8e8484612314565b905060016001607f1b036001600160801b038216111561065257600080fd5b81546000908210611e0b5760405162461bcd60e51b815260206004820152602260248201527f456e756d657261626c655365743a20696e646578206f7574206f6620626f756e604482015261647360f01b60648201526084016100f0565b826000018281548110611e2057611e20612be4565b9060005260206000200154905092915050565b836001600160a01b0316856001600160a01b031614612009576001600160a01b0385811660009081527fb31c2c74f86ca3ce94d901f5f5bbe66f7161eec2f7b5aa0b75a86371436424ec602052604080822092871682528120600080516020612cfa833981519152927fb31c2c74f86ca3ce94d901f5f5bbe66f7161eec2f7b5aa0b75a86371436424eb929091905b87518110156104b3576000878281518110611edf57611edf612be4565b602002602001015190506000811115611ff6576000898381518110611f0657611f06612be4565b6020026020010151905060006001600160a01b03168c6001600160a01b03161415611f545760008181526020889052604081208054849290611f49908490612a15565b90915550611f8a9050565b81611f5f8d83611128565b1415611f8a576000818152602087905260409020611f7d908d6125ec565b50611f88858261201d565b505b6001600160a01b038b16611fc15760008181526020889052604081208054849290611fb6908490612a2d565b90915550611ff49050565b611fcb8b82611128565b611ff4576000818152602087905260409020611fe7908c612601565b50611ff28482612011565b505b505b508061200181612a7c565b915050611ec2565b505050505050565b60006106528383612612565b60006106528383612661565b60008161203a578260040154610652565b50506005015490565b6001600160a01b03821661205657600080fd5b8015156000908152600f8401602090815260408083206010870190925290912061208184838361274c565b61208c575050505050565b6001600160a01b0393841660008181526020838152604080832080549683528184208054978a16808652838620805499909b166001600160a01b0319998a168117909b5599855295909252822080548616909717909655528054821690558254169091555050565b6001600160a01b03808416600090815260178601602090815260408083208615158452825280832054601889019092529091205490917f00000000000000000000000000000000000000000000000000000000000000001663edaf7d5b8630878661215f8982612a15565b6040516001600160e01b031960e088901b1681526001600160a01b03958616600482015294909316602485015290151560448401526064830152608482015260a4810184905260c401600060405180830381600087803b1580156121c257600080fd5b505af11580156121d6573d6000803e3d6000fd5b5050505082826121e69190612a15565b6001600160a01b038616600090815260178801602090815260408083208815158452909152902055611a508382612a15565b6001600160a01b03821661222b57600080fd5b8015156000908152600f8401602090815260408083206010870190925290912061225684838361274c565b15612262575050505050565b60008080526020828152604080832080546001600160a01b0390811680865296845282852080546001600160a01b03199081169a909216998a1790558885529490925282208054841690941790935580528154169092179091555050565b6000816122ed577f00000000000000000000000000000000000000000000000000000000000000006104d8565b7f000000000000000000000000000000000000000000000000000000000000000092915050565b60008161232057600080fd5b60006001600160c01b03841161234b5782604085901b8161234357612343612a44565b049050612462565b60c084811c6401000000008110612364576020918201911c5b620100008110612376576010918201911c5b6101008110612387576008918201911c5b60108110612397576004918201911c5b600481106123a7576002918201911c5b600281106123b6576001820191505b60bf820360018603901c6001018260ff0387901b816123d7576123d7612a44565b0492506001600160801b038311156123ee57600080fd5b608085901c83026001600160801b038616840260c088901c604089901b8281101561241a576001820391505b608084901b92900382811015612431576001820391505b829003608084901c821461244757612447612bfa565b88818161245657612456612a44565b04870196505050505050505b6001600160801b0381111561065257600080fd5b60006124828383612798565b90506106528382846127bf565b600080826124a4576019870154600f0b6124b4565b6019870154600160801b9004600f0b5b905080600f0b600014156124cc57506008860154600f0b5b60405163e101a89b60e01b8152600f87810b600483015286810b602483015285810b604483015282900b6064820152730f6e8ef18fb5bb61d545fee60f779d8aed60408f9063e101a89b9060840160206040518083038186803b15801561253257600080fd5b505af4158015612546573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061256a9190612c10565b915067b33333333333333382600f0b121561258b5767b33333333333333391505b5095945050505050565b80156125c5576009830180546001600160801b0384166001600160801b031990911617905542600b840155505050565b6008830180546001600160801b03808516600160801b02911617905542600a840155505050565b6000610652836001600160a01b038416612661565b6000610652836001600160a01b0384165b6000818152600183016020526040812054612659575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556104d8565b5060006104d8565b60008181526001830160205260408120548015612742576000612685600183612a2d565b8554909150600090869061269b90600190612a2d565b815481106126ab576126ab612be4565b90600052602060002001549050808660000183815481106126ce576126ce612be4565b6000918252602090912001556126e5826001612a15565b6000828152600188016020526040902055855486908061270757612707612c33565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506104d8565b60009150506104d8565b6001600160a01b0383811660009081526020849052604081205490911615158061015757506000808052602083905260409020546001600160a01b039081169085161490509392505050565b6000816127b3576008830154600160801b9004600f0b610652565b505060090154600f0b90565b600080826127d15784600a01546127d7565b84600b01545b6127e19042612a2d565b905061a8c0811115612800576127f961a8c082612a2d565b9050612809565b83915050610652565b600061281782613840611d76565b9050600061282a85611c40576001611c43565b851515600090815260188901602090815260408083205460138c01835281842060010154858552600080516020612cfa83398151915290935290832054939450926128889161287891612a2d565b6128829084612a2d565b83611d76565b6040805161012081018252600f87810b82528b810b602083015283900b8183015267b333333333333333606082015267e666666666666666608082018190526801000000000000000060a0830181905260c083015260e082015268056fc2a2c515da32ea6101008201529051634916d70d60e01b8152919250730f6e8ef18fb5bb61d545fee60f779d8aed60408f91634916d70d9161292991600401612c49565b60206040518083038186803b15801561294157600080fd5b505af4158015612955573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129799190612c10565b9998505050505050505050565b60008060006060848603121561299b57600080fd5b83356001600160a01b03811681146129b257600080fd5b95602085013595506040909401359392505050565b600080604083850312156129da57600080fd5b50508035926020909101359150565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60008219821115612a2857612a286129ff565b500190565b600082821015612a3f57612a3f6129ff565b500390565b634e487b7160e01b600052601260045260246000fd5b600082612a7757634e487b7160e01b600052601260045260246000fd5b500490565b6000600019821415612a9057612a906129ff565b5060010190565b6000816000190483118215151615612ab157612ab16129ff565b500290565b600060208284031215612ac857600080fd5b8151801515811461065257600080fd5b600060208284031215612aea57600080fd5b5051919050565b600181815b80851115612b2c578160001904821115612b1257612b126129ff565b80851615612b1f57918102915b93841c9390800290612af6565b509250929050565b600082612b43575060016104d8565b81612b50575060006104d8565b8160018114612b665760028114612b7057612b8c565b60019150506104d8565b60ff841115612b8157612b816129ff565b50506001821b6104d8565b5060208310610133831016604e8410600b8410161715612baf575081810a6104d8565b612bb98383612af1565b8060001904821115612bcd57612bcd6129ff565b029392505050565b600061065260ff841683612b34565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052600160045260246000fd5b600060208284031215612c2257600080fd5b815180600f0b811461065257600080fd5b634e487b7160e01b600052603160045260246000fd5b6000610120820190508251600f0b82526020830151600f0b60208301526040830151612c7a6040840182600f0b9052565b506060830151612c8f6060840182600f0b9052565b506080830151612ca46080840182600f0b9052565b5060a0830151612cb960a0840182600f0b9052565b5060c0830151612cce60c0840182600f0b9052565b5060e0830151612ce360e0840182600f0b9052565b5061010080840151611c2682850182600f0b905256feb31c2c74f86ca3ce94d901f5f5bbe66f7161eec2f7b5aa0b75a86371436424eabbd6af8edd89d04327b00c29df7f272b9b1ae01bf6d9c54a784f935706df52eb"), + ("0x71356E37e0368Bd10bFDbF41dC052fE5FA24cD05", "MainchainGatewayV2", "608060405234801561001057600080fd5b506000805460ff1916905561582e806200002b6000396000f3fe60806040526004361061032d5760003560e01c80639157921c116101a5578063b2975794116100ec578063d547741f11610095578063dafae4081161006f578063dafae4081461096e578063dff525e11461098e578063e400327c146109ae578063e75235b8146109ce5761033c565b8063d547741f14610901578063d55ed10314610921578063d64af2a61461094e5761033c565b8063cdb67444116100c6578063cdb674441461089c578063cdf64a76146108b4578063d19773d2146108d45761033c565b8063b29757941461082f578063b9c362091461085c578063ca15c8731461087c5761033c565b8063a3912ec81161014e578063affed0e011610128578063affed0e0146107cc578063b1a2567e146107e2578063b1d08a03146108025761033c565b8063a3912ec81461033a578063ab7965661461077f578063ac78dfe8146107ac5761033c565b8063994390891161017f57806399439089146107155780639dcc4da314610735578063a217fddf1461076a5761033c565b80639157921c1461068f57806391d14854146106af57806393c5678f146106f55761033c565b806336568abe116102745780635c975abb1161021d5780637de5dedd116101f75780637de5dedd146106115780638456cb59146106265780638f34e3471461063b5780639010d07c1461066f5761033c565b80635c975abb146105ac5780636932be98146105c45780636c1ce670146105f15761033c565b80634d0d66731161024e5780634d0d66731461052f5780634d493f4e1461054f57806359122f6b1461057f5761033c565b806336568abe146104e75780633f4ba83a146105075780634b14557e1461051c5761033c565b80631d4a7210116102d65780632f2ff15d116102b05780632f2ff15d1461049b578063302d12db146104bb5780633644e515146104d25761033c565b80631d4a721014610428578063248a9ca3146104555780632dfdf0b5146104855761033c565b8063180ff1e911610307578063180ff1e9146103d55780631a8e55b0146103e85780631b6e7594146104085761033c565b806301ffc9a71461034457806317ce2dd41461037957806317fcb39b1461039d5761033c565b3661033c5761033a6109e6565b005b61033a6109e6565b34801561035057600080fd5b5061036461035f366004614843565b610a69565b60405190151581526020015b60405180910390f35b34801561038557600080fd5b5061038f60755481565b604051908152602001610370565b3480156103a957600080fd5b506074546103bd906001600160a01b031681565b6040516001600160a01b039091168152602001610370565b61033a6103e33660046148f4565b610aad565b3480156103f457600080fd5b5061033a6104033660046149e6565b610dbd565b34801561041457600080fd5b5061033a610423366004614a52565b610e8f565b34801561043457600080fd5b5061038f610443366004614aec565b603e6020526000908152604090205481565b34801561046157600080fd5b5061038f610470366004614b09565b60009081526072602052604090206001015490565b34801561049157600080fd5b5061038f60765481565b3480156104a757600080fd5b5061033a6104b6366004614b22565b610f64565b3480156104c757600080fd5b5061038f620f424081565b3480156104de57600080fd5b5060775461038f565b3480156104f357600080fd5b5061033a610502366004614b22565b610f8f565b34801561051357600080fd5b5061033a61101b565b61033a61052a366004614b52565b611083565b34801561053b57600080fd5b5061036461054a366004614b7d565b6110e1565b34801561055b57600080fd5b5061036461056a366004614b09565b607a6020526000908152604090205460ff1681565b34801561058b57600080fd5b5061038f61059a366004614aec565b603a6020526000908152604090205481565b3480156105b857600080fd5b5060005460ff16610364565b3480156105d057600080fd5b5061038f6105df366004614b09565b60796020526000908152604090205481565b3480156105fd57600080fd5b5061036461060c366004614c06565b61118c565b34801561061d57600080fd5b5061038f61119f565b34801561063257600080fd5b5061033a611234565b34801561064757600080fd5b5061038f7f5e5712e902fff5e704bc4d506ad976718319e019e9d2a872528a01a85db433e481565b34801561067b57600080fd5b506103bd61068a366004614c32565b61129c565b34801561069b57600080fd5b5061033a6106aa366004614c54565b6112b4565b3480156106bb57600080fd5b506103646106ca366004614b22565b60009182526072602090815260408084206001600160a01b0393909316845291905290205460ff1690565b34801561070157600080fd5b5061033a6107103660046149e6565b6115ca565b34801561072157600080fd5b506003546103bd906001600160a01b031681565b34801561074157600080fd5b50610755610750366004614c32565b611696565b60408051928352602083019190915201610370565b34801561077657600080fd5b5061038f600081565b34801561078b57600080fd5b5061038f61079a366004614aec565b603c6020526000908152604090205481565b3480156107b857600080fd5b506103646107c7366004614b09565b61172f565b3480156107d857600080fd5b5061038f60045481565b3480156107ee57600080fd5b5061033a6107fd3660046149e6565b6117ce565b34801561080e57600080fd5b5061038f61081d366004614aec565b60396020526000908152604090205481565b34801561083b57600080fd5b5061084f61084a366004614aec565b61189a565b6040516103709190614ca5565b34801561086857600080fd5b50610755610877366004614c32565b611992565b34801561088857600080fd5b5061038f610897366004614b09565b611a17565b3480156108a857600080fd5b50603754603854610755565b3480156108c057600080fd5b5061033a6108cf366004614aec565b611a2e565b3480156108e057600080fd5b5061038f6108ef366004614aec565b603b6020526000908152604090205481565b34801561090d57600080fd5b5061033a61091c366004614b22565b611a97565b34801561092d57600080fd5b5061038f61093c366004614aec565b603d6020526000908152604090205481565b34801561095a57600080fd5b5061033a610969366004614aec565b611abd565b34801561097a57600080fd5b50610364610989366004614b09565b611b26565b34801561099a57600080fd5b5061033a6109a9366004614cd2565b611bbd565b3480156109ba57600080fd5b5061033a6109c93660046149e6565b611cc7565b3480156109da57600080fd5b50600154600254610755565b60005460ff1615610a315760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b60448201526064015b60405180910390fd5b6074546001600160a01b03163314610a6757610a4b614802565b338152604080820151349101528051610a65908290611d93565b505b565b60006001600160e01b031982167f5a05180f000000000000000000000000000000000000000000000000000000001480610aa75750610aa78261210a565b92915050565b607154610100900460ff16610ac85760715460ff1615610acc565b303b155b610b3e5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610a28565b607154610100900460ff16158015610b60576071805461ffff19166101011790555b610b6b60008d612171565b6075899055610b798b61217b565b610b828a6121dd565b610c29604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f159f52c1e3a2b6a6aad3950adf713516211484e0516dad685ea662a094b7c43b918101919091527fad7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a560608201524660808201523060a082015260c00160408051601f198184030181529190528051602090910120607755565b610c338887612238565b5050610c3f87876122f8565b5050610c496123d3565b6000610c558680614da6565b90501115610d1657610c7e610c6a8680614da6565b610c776020890189614da6565b8787612467565b610ca4610c8b8680614da6565b8660005b602002810190610c9f9190614da6565b612666565b610cca610cb18680614da6565b8660015b602002810190610cc59190614da6565b612779565b610cf0610cd78680614da6565b8660025b602002810190610ceb9190614da6565b61288c565b610d16610cfd8680614da6565b8660035b602002810190610d119190614da6565b612a30565b60005b610d266040870187614da6565b9050811015610d9c57610d8a7f5e5712e902fff5e704bc4d506ad976718319e019e9d2a872528a01a85db433e4610d606040890189614da6565b84818110610d7057610d70614d90565b9050602002016020810190610d859190614aec565b612b43565b80610d9481614e06565b915050610d19565b508015610daf576071805461ff00191690555b505050505050505050505050565b6000805160206157b9833981519152546001600160a01b03163314610e1d5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b82610e7d5760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b610e8984848484612779565b50505050565b6000805160206157b9833981519152546001600160a01b03163314610eef5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b84610f4e5760405162461bcd60e51b815260206004820152602960248201527f4d61696e636861696e4761746577617956323a20717565727920666f7220656d60448201526870747920617272617960b81b6064820152608401610a28565b610f5c868686868686612467565b505050505050565b600082815260726020526040902060010154610f808133612b65565b610f8a8383612b43565b505050565b6001600160a01b038116331461100d5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c6600000000000000000000000000000000006064820152608401610a28565b6110178282612be5565b5050565b6000805160206157b9833981519152546001600160a01b0316331461107b5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b610a67612c07565b60005460ff16156110c95760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610a28565b610a656110db36839003830183614ec0565b33611d93565b6000805460ff16156111285760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610a28565b611184848484808060200260200160405190810160405280939291908181526020016000905b8282101561117a5761116b60608302860136819003810190614f13565b8152602001906001019061114e565b5050505050612ca3565b949350505050565b600061119883836133bc565b9392505050565b600061122f600360009054906101000a90046001600160a01b03166001600160a01b031663926323d56040518163ffffffff1660e01b815260040160206040518083038186803b1580156111f257600080fd5b505afa158015611206573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061122a9190614f89565b613480565b905090565b6000805160206157b9833981519152546001600160a01b031633146112945760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b610a676134b6565b60008281526073602052604081206111989083613531565b7f5e5712e902fff5e704bc4d506ad976718319e019e9d2a872528a01a85db433e46112df8133612b65565b60006112f86112f336859003850185614ff0565b61353d565b905061130c6112f336859003850185614ff0565b8335600090815260796020526040902054146113765760405162461bcd60e51b815260206004820152602360248201527f4d61696e636861696e4761746577617956323a20696e76616c696420726563656044820152621a5c1d60ea1b6064820152608401610a28565b82356000908152607a602052604090205460ff166113fc5760405162461bcd60e51b815260206004820152603160248201527f4d61696e636861696e4761746577617956323a20717565727920666f7220617060448201527f70726f766564207769746864726177616c0000000000000000000000000000006064820152608401610a28565b82356000908152607a602052604090819020805460ff19169055517fd639511b37b3b002cca6cfe6bca0d833945a5af5a045578a0627fc43b79b26309061144690839086906150c4565b60405180910390a160006114606080850160608601614aec565b9050600061147661012086016101008701615151565b600181111561148757611487614c71565b141561154f5760006114a2368690038601610100870161516e565b6001600160a01b0383166000908152603b60205260409020549091506114ce90610140870135906135c6565b604082015260006114e8368790038701610100880161516e565b60408301519091506114ff9061014088013561518a565b604082015260745461151f908390339086906001600160a01b03166135e0565b6115486115326060880160408901614aec565b60745483919086906001600160a01b03166135e0565b505061158b565b61158b6115626060860160408701614aec565b60745483906001600160a01b03166115833689900389016101008a0161516e565b9291906135e0565b7f21e88e956aa3e086f6388e899965cef814688f99ad8bb29b08d396571016372d82856040516115bc9291906150c4565b60405180910390a150505050565b6000805160206157b9833981519152546001600160a01b0316331461162a5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b8261168a5760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b610e8984848484612666565b6000806116b86000805160206157b9833981519152546001600160a01b031690565b6001600160a01b0316336001600160a01b0316146117115760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b61171b84846122f8565b90925090506117286123d3565b9250929050565b6003546040805163926323d560e01b815290516000926001600160a01b03169163926323d5916004808301926020929190829003018186803b15801561177457600080fd5b505afa158015611788573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117ac9190614f89565b6037546117b991906151a1565b6038546117c690846151a1565b101592915050565b6000805160206157b9833981519152546001600160a01b0316331461182e5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b8261188e5760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b610e898484848461288c565b60408051808201909152600080825260208201526001600160a01b0382166000908152607860205260409081902081518083019092528054829060ff1660018111156118e8576118e8614c71565b60018111156118f9576118f9614c71565b815290546001600160a01b036101009091048116602092830152908201519192501661198d5760405162461bcd60e51b815260206004820152602560248201527f4d61696e636861696e4761746577617956323a20756e737570706f727465642060448201527f746f6b656e0000000000000000000000000000000000000000000000000000006064820152608401610a28565b919050565b6000806119b46000805160206157b9833981519152546001600160a01b031690565b6001600160a01b0316336001600160a01b031614611a0d5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b61171b8484612238565b6000818152607360205260408120610aa790613a13565b6000805160206157b9833981519152546001600160a01b03163314611a8e5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b610a65816121dd565b600082815260726020526040902060010154611ab38133612b65565b610f8a8383612be5565b6000805160206157b9833981519152546001600160a01b03163314611b1d5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b610a658161217b565b6003546040805163926323d560e01b815290516000926001600160a01b03169163926323d5916004808301926020929190829003018186803b158015611b6b57600080fd5b505afa158015611b7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ba39190614f89565b600154611bb091906151a1565b6002546117c690846151a1565b6000805160206157b9833981519152546001600160a01b03163314611c1d5760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b85611c7c5760405162461bcd60e51b815260206004820152602960248201527f4d61696e636861696e4761746577617956323a20717565727920666f7220656d60448201526870747920617272617960b81b6064820152608401610a28565b611c8a878787878787612467565b611c978787836000610c8f565b611ca48787836001610cb5565b611cb18787836002610cdb565b611cbe8787836003610d01565b50505050505050565b6000805160206157b9833981519152546001600160a01b03163314611d275760405162461bcd60e51b815260206004820152602260248201526000805160206157d983398151915260448201526132b960f11b6064820152608401610a28565b82611d875760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b610e8984848484612a30565b604080518082018252600080825260208201526074549184015190916001600160a01b031690611dc290613a1d565b60208401516001600160a01b0316611ee1573484604001516040015114611e375760405162461bcd60e51b815260206004820152602360248201527f4d61696e636861696e4761746577617956323a20696e76616c69642072657175604482015262195cdd60ea1b6064820152608401610a28565b611e408161189a565b6040850151519092506001811115611e5a57611e5a614c71565b82516001811115611e6d57611e6d614c71565b14611ecd5760405162461bcd60e51b815260206004820152602a60248201527f4d61696e636861696e4761746577617956323a20696e76616c696420746f6b656044820152691b881cdd185b99185c9960b21b6064820152608401610a28565b6001600160a01b0381166020850152612087565b3415611f3b5760405162461bcd60e51b815260206004820152602360248201527f4d61696e636861696e4761746577617956323a20696e76616c69642072657175604482015262195cdd60ea1b6064820152608401610a28565b611f48846020015161189a565b6040850151519092506001811115611f6257611f62614c71565b82516001811115611f7557611f75614c71565b14611fd55760405162461bcd60e51b815260206004820152602a60248201527f4d61696e636861696e4761746577617956323a20696e76616c696420746f6b656044820152691b881cdd185b99185c9960b21b6064820152608401610a28565b60208401516040850151611fec9185903090613ac7565b83602001516001600160a01b0316816001600160a01b031614156120875760408481015181015190517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101919091526001600160a01b03821690632e1a7d4d90602401600060405180830381600087803b15801561206e57600080fd5b505af1158015612082573d6000803e3d6000fd5b505050505b607680546000918261209883614e06565b91905055905060006120bf858386602001516075548a613ce190949392919063ffffffff16565b90507fd7b25068d9dc8d00765254cfb7f5070f98d263c8d68931d937c7362fa738048b6120eb8261353d565b826040516120fa9291906151c0565b60405180910390a1505050505050565b60006001600160e01b031982167f7965db0b000000000000000000000000000000000000000000000000000000001480610aa757507f01ffc9a7000000000000000000000000000000000000000000000000000000006001600160e01b0319831614610aa7565b6110178282612b43565b6074805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383169081179091556040519081527f9d2334c23be647e994f27a72c5eee42a43d5bdcfe15bb88e939103c2b114cbaf906020015b60405180910390a150565b6003805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383169081179091556040519081527fef40dc07567635f84f5edbd2f8dbc16b40d9d282dd8e7e6f4ff58236b6836169906020016121d2565b6000808284111561228b5760405162461bcd60e51b815260206004820152601c60248201527f4761746577617956323a20696e76616c6964207468726573686f6c64000000006044820152606401610a28565b505060018054600280549285905583905560048054919291849186919060006122b383614e06565b9091555060408051868152602081018690527f976f8a9c5bdf8248dec172376d6e2b80a8e3df2f0328e381c6db8e1cf138c0f891015b60405180910390a49250929050565b600080828411156123715760405162461bcd60e51b815260206004820152602760248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c696420746860448201527f726573686f6c64000000000000000000000000000000000000000000000000006064820152608401610a28565b5050603780546038805492859055839055600480549192918491869190600061239983614e06565b9091555060408051868152602081018690527f31312c97b89cc751b832d98fd459b967a2c3eef3b49757d1cf5ebaa12bb6eee191016122e9565b6002546037546123e391906151a1565b6038546001546123f391906151a1565b1115610a675760405162461bcd60e51b815260206004820152602860248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c696420746860448201527f726573686f6c64730000000000000000000000000000000000000000000000006064820152608401610a28565b848314801561247557508481145b6124e75760405162461bcd60e51b815260206004820152602860248201527f4d61696e636861696e4761746577617956323a20696e76616c6964206172726160448201527f79206c656e6774680000000000000000000000000000000000000000000000006064820152608401610a28565b60005b8581101561262c5784848281811061250457612504614d90565b90506020020160208101906125199190614aec565b6078600089898581811061252f5761252f614d90565b90506020020160208101906125449190614aec565b6001600160a01b039081168252602082019290925260400160002080547fffffffffffffffffffffff0000000000000000000000000000000000000000ff1661010093909216929092021790558282828181106125a3576125a3614d90565b90506020020160208101906125b89190615151565b607860008989858181106125ce576125ce614d90565b90506020020160208101906125e39190614aec565b6001600160a01b031681526020810191909152604001600020805460ff19166001838181111561261557612615614c71565b02179055508061262481614e06565b9150506124ea565b507fa4f03cc9c0e0aeb5b71b4ec800702753f65748c2cf3064695ba8e8b46be704448686868686866040516120fa969594939291906152c1565b8281146126c85760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b60005b83811015612743578282828181106126e5576126e5614d90565b905060200201356039600087878581811061270257612702614d90565b90506020020160208101906127179190614aec565b6001600160a01b031681526020810191909152604001600020558061273b81614e06565b9150506126cb565b507f80bc635c452ae67f12f9b6f12ad4daa6dbbc04eeb9ebb87d354ce10c0e210dc0848484846040516115bc9493929190615339565b8281146127db5760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b60005b83811015612856578282828181106127f8576127f8614d90565b90506020020135603a600087878581811061281557612815614d90565b905060200201602081019061282a9190614aec565b6001600160a01b031681526020810191909152604001600020558061284e81614e06565b9150506127de565b507f64557254143204d91ba2d95acb9fda1e5fea55f77efd028685765bc1e94dd4b5848484846040516115bc9493929190615339565b8281146128ee5760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b60005b838110156129fa57620f424083838381811061290f5761290f614d90565b90506020020135111561298a5760405162461bcd60e51b815260206004820152602860248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c696420706560448201527f7263656e746167650000000000000000000000000000000000000000000000006064820152608401610a28565b82828281811061299c5761299c614d90565b90506020020135603b60008787858181106129b9576129b9614d90565b90506020020160208101906129ce9190614aec565b6001600160a01b03168152602081019190915260400160002055806129f281614e06565b9150506128f1565b507fb05f5de88ae0294ebb6f67c5af2fcbbd593cc6bdfe543e2869794a4c8ce3ea50848484846040516115bc9493929190615339565b828114612a925760405162461bcd60e51b815260206004820152602a60248201527f5769746864726177616c4c696d69746174696f6e3a20696e76616c69642061726044820152690e4c2f240d8cadccee8d60b31b6064820152608401610a28565b60005b83811015612b0d57828282818110612aaf57612aaf614d90565b90506020020135603c6000878785818110612acc57612acc614d90565b9050602002016020810190612ae19190614aec565b6001600160a01b0316815260208101919091526040016000205580612b0581614e06565b915050612a95565b507fb5d2963614d72181b4df1f993d45b83edf42fa19710f0204217ba1b3e183bb73848484846040516115bc9493929190615339565b612b4d8282613db6565b6000828152607360205260409020610f8a9082613e58565b60008281526072602090815260408083206001600160a01b038516845290915290205460ff1661101757612ba3816001600160a01b03166014613e6d565b612bae836020613e6d565b604051602001612bbf9291906153d0565b60408051601f198184030181529082905262461bcd60e51b8252610a2891600401615451565b612bef828261404e565b6000828152607360205260409020610f8a90826140d1565b60005460ff16612c595760405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606401610a28565b6000805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b6000823561014084013582612cbe6080870160608801614aec565b9050612cdb612cd6368890038801610100890161516e565b613a1d565b6001612ced6040880160208901615151565b6001811115612cfe57612cfe614c71565b14612d715760405162461bcd60e51b815260206004820152602860248201527f4d61696e636861696e4761746577617956323a20696e76616c6964207265636560448201527f697074206b696e640000000000000000000000000000000000000000000000006064820152608401610a28565b60808601354614612de95760405162461bcd60e51b8152602060048201526024808201527f4d61696e636861696e4761746577617956323a20696e76616c6964206368616960448201527f6e206964000000000000000000000000000000000000000000000000000000006064820152608401610a28565b6000612dfe61084a6080890160608a01614aec565b9050612e1261012088016101008901615151565b6001811115612e2357612e23614c71565b81516001811115612e3657612e36614c71565b148015612e675750612e4e60e0880160c08901614aec565b6001600160a01b031681602001516001600160a01b0316145b612ebf5760405162461bcd60e51b815260206004820152602360248201527f4d61696e636861696e4761746577617956323a20696e76616c696420726563656044820152621a5c1d60ea1b6064820152608401610a28565b60008481526079602052604090205415612f415760405162461bcd60e51b815260206004820152603260248201527f4d61696e636861696e4761746577617956323a20717565727920666f7220707260448201527f6f636573736564207769746864726177616c00000000000000000000000000006064820152608401610a28565b6001612f5561012089016101008a01615151565b6001811115612f6657612f66614c71565b1480612f795750612f7782846133bc565b155b612feb5760405162461bcd60e51b815260206004820152603260248201527f4d61696e636861696e4761746577617956323a2072656163686564206461696c60448201527f79207769746864726177616c206c696d697400000000000000000000000000006064820152608401610a28565b6000612fff6112f3368a90038a018a614ff0565b9050600061300f607754836140e6565b6003549091506001600160a01b0316600061303d6130356101208d016101008e01615151565b878985614142565b60408051606081018252600080825260208201819052918101829052919b50919250819081906000805b8f5181101561323c578f818151811061308257613082614d90565b6020908102919091018101518051818301516040808401518151600081529586018083528f905260ff9093169085015260608401526080830152935060019060a0016020604051602081039080840390855afa1580156130e6573d6000803e3d6000fd5b505050602060405103519450846001600160a01b0316846001600160a01b0316106131795760405162461bcd60e51b815260206004820152602160248201527f4d61696e636861696e4761746577617956323a20696e76616c6964206f72646560448201527f72000000000000000000000000000000000000000000000000000000000000006064820152608401610a28565b6040517f953865650000000000000000000000000000000000000000000000000000000081526001600160a01b03808716600483015286955089169063953865659060240160206040518083038186803b1580156131d657600080fd5b505afa1580156131ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061320e9190614f89565b6132189083615484565b915086821061322a576001955061323c565b8061323481614e06565b915050613067565b50846132b05760405162461bcd60e51b815260206004820152603660248201527f4d61696e636861696e4761746577617956323a20717565727920666f7220696e60448201527f73756666696369656e7420766f746520776569676874000000000000000000006064820152608401610a28565b50505060008a81526079602052604090208690555050881561332c576000888152607a602052604090819020805460ff19166001179055517f89e52969465b1f1866fc5d46fd62de953962e9cb33552443cd999eba05bd20dc906133179086908e906150c4565b60405180910390a15050505050505050610aa7565b6133368688614233565b61337561334960608d0160408e01614aec565b87607460009054906101000a90046001600160a01b03168e61010001803603810190611583919061516e565b7f21e88e956aa3e086f6388e899965cef814688f99ad8bb29b08d396571016372d848c6040516133a69291906150c4565b60405180910390a1505050505050505092915050565b6001600160a01b0382166000908152603a602052604081205482106133e357506000610aa7565b60006133f2620151804261549c565b6001600160a01b0385166000908152603e60205260409020549091508111156134385750506001600160a01b0382166000908152603c6020526040902054811015610aa7565b6001600160a01b0384166000908152603d602052604090205461345c908490615484565b6001600160a01b0385166000908152603c602052604090205411159150610aa79050565b600060025460016002548460015461349891906151a1565b6134a29190615484565b6134ac919061518a565b610aa7919061549c565b60005460ff16156134fc5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610a28565b6000805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258612c863390565b600061119883836142c3565b60007fb9d1fe7c9deeec5dc90a2f47ff1684239519f2545b2228d3d91fb27df3189eea60001b8260000151836020015161357a85604001516142ed565b61358786606001516142ed565b6135948760800151614350565b6040516020016135a9969594939291906154be565b604051602081830303815290604052805190602001209050919050565b6000620f42406135d683856151a1565b611198919061549c565b6000816001600160a01b0316836001600160a01b031614156136905760408086015190516001600160a01b0386169180156108fc02916000818181858888f1935050505061368b57816001600160a01b031663d0e30db086604001516040518263ffffffff1660e01b81526004016000604051808303818588803b15801561366757600080fd5b505af115801561367b573d6000803e3d6000fd5b505050505061368b858585614393565b613a0c565b6000855160018111156136a5576136a5614c71565b1415613866576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000906001600160a01b038516906370a082319060240160206040518083038186803b15801561370657600080fd5b505afa15801561371a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061373e9190614f89565b9050856040015181101561385557836001600160a01b03166340c10f193083896040015161376c919061518a565b6040516001600160a01b03909216602483015260448201526064016040516020818303038152906040529060e01b6020820180516001600160e01b0383818316178352505050506040516137c091906154f8565b6000604051808303816000865af19150503d80600081146137fd576040519150601f19603f3d011682016040523d82523d6000602084013e613802565b606091505b505080925050816138555760405162461bcd60e51b815260206004820152601b60248201527f546f6b656e3a204552433230206d696e74696e67206661696c656400000000006044820152606401610a28565b613860868686614393565b50613a0c565b60018551600181111561387b5761387b614c71565b141561399e5761389083858760200151614437565b61368b57602085810151604080516001600160a01b038881166024830152604480830194909452825180830390940184526064909101825292820180516001600160e01b03167f40c10f1900000000000000000000000000000000000000000000000000000000179052519185169161390991906154f8565b6000604051808303816000865af19150503d8060008114613946576040519150601f19603f3d011682016040523d82523d6000602084013e61394b565b606091505b5050809150508061368b5760405162461bcd60e51b815260206004820152601c60248201527f546f6b656e3a20455243373231206d696e74696e67206661696c6564000000006044820152606401610a28565b60405162461bcd60e51b815260206004820152602160248201527f546f6b656e3a20756e737570706f7274656420746f6b656e207374616e64617260448201527f64000000000000000000000000000000000000000000000000000000000000006064820152608401610a28565b5050505050565b6000610aa7825490565b600081516001811115613a3257613a32614c71565b148015613a43575060008160400151115b8015613a5157506020810151155b80613a7b5750600181516001811115613a6c57613a6c614c71565b148015613a7b57506040810151155b610a655760405162461bcd60e51b815260206004820152601360248201527f546f6b656e3a20696e76616c696420696e666f000000000000000000000000006044820152606401610a28565b600060608186516001811115613adf57613adf614c71565b1415613bbd5760408681015181516001600160a01b038881166024830152878116604483015260648083019390935283518083039093018352608490910183526020820180516001600160e01b03166323b872dd60e01b179052915191851691613b4991906154f8565b6000604051808303816000865af19150503d8060008114613b86576040519150601f19603f3d011682016040523d82523d6000602084013e613b8b565b606091505b509092509050818015613bb6575080511580613bb6575080806020019051810190613bb69190615514565b9150613c84565b600186516001811115613bd257613bd2614c71565b141561399e57602086810151604080516001600160a01b0389811660248301528881166044830152606480830194909452825180830390940184526084909101825292820180516001600160e01b03166323b872dd60e01b1790525191851691613c3c91906154f8565b6000604051808303816000865af19150503d8060008114613c79576040519150601f19603f3d011682016040523d82523d6000602084013e613c7e565b606091505b50909250505b81610f5c57613c92866144e2565b613ca6866001600160a01b03166014613e6d565b613cba866001600160a01b03166014613e6d565b613cce866001600160a01b03166014613e6d565b604051602001612bbf9493929190615536565b613d516040805160a08101825260008082526020808301829052835160608082018652838252818301849052818601849052848601919091528451808201865283815280830184905280860184905281850152845190810185528281529081018290529283015290608082015290565b83815260006020820181905250604080820180516001600160a01b039788169052602080890151825190891690820152905146908301528751606084018051918916909152805195909716940193909352935182015292909201516080820152919050565b60008281526072602090815260408083206001600160a01b038516845290915290205460ff166110175760008281526072602090815260408083206001600160a01b03851684529091529020805460ff19166001179055613e143390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000611198836001600160a01b03841661454f565b60606000613e7c8360026151a1565b613e87906002615484565b67ffffffffffffffff811115613e9f57613e9f614e21565b6040519080825280601f01601f191660200182016040528015613ec9576020820181803683370190505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110613f0057613f00614d90565b60200101906001600160f81b031916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110613f4b57613f4b614d90565b60200101906001600160f81b031916908160001a9053506000613f6f8460026151a1565b613f7a906001615484565b90505b6001811115613fff577f303132333435363738396162636465660000000000000000000000000000000085600f1660108110613fbb57613fbb614d90565b1a60f81b828281518110613fd157613fd1614d90565b60200101906001600160f81b031916908160001a90535060049490941c93613ff881615606565b9050613f7d565b5083156111985760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610a28565b60008281526072602090815260408083206001600160a01b038516845290915290205460ff16156110175760008281526072602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6000611198836001600160a01b03841661459e565b604080517f19010000000000000000000000000000000000000000000000000000000000006020808301919091526022820185905260428083018590528351808403909101815260629092019092528051910120600090611198565b6000806000836001600160a01b031663926323d56040518163ffffffff1660e01b815260040160206040518083038186803b15801561418057600080fd5b505afa158015614194573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141b89190614f89565b90506141c381613480565b925060008760018111156141d9576141d9614c71565b1415614229576001600160a01b038616600090815260396020526040902054851061420a5761420781614691565b92505b6001600160a01b0386166000908152603a602052604090205485101591505b5094509492505050565b6000614242620151804261549c565b6001600160a01b0384166000908152603e6020526040902054909150811115614291576001600160a01b03929092166000908152603e6020908152604080832094909455603d90529190912055565b6001600160a01b0383166000908152603d6020526040812080548492906142b9908490615484565b9091555050505050565b60008260000182815481106142da576142da614d90565b9060005260206000200154905092915050565b805160208083015160408085015190516000946135a9947f353bdd8d69b9e3185b3972e08b03845c0c14a21a390215302776a7a34b0e87649491939192019384526001600160a01b03928316602085015291166040830152606082015260800190565b805160208083015160408085015190516000946135a9947f1e2b74b2a792d5c0f0b6e59b037fa9d43d84fbb759337f0112fcc15ca414fc8d94919391920161561d565b600080845160018111156143a9576143a9614c71565b14156143c5576143be828486604001516146a9565b90506143ef565b6001845160018111156143da576143da614c71565b141561399e576143be82848660200151614437565b80610e89576143fd846144e2565b614411846001600160a01b03166014613e6d565b614425846001600160a01b03166014613e6d565b604051602001612bbf93929190615648565b604080513060248201526001600160a01b038481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b1790529151600092861691614495916154f8565b6000604051808303816000865af19150503d80600081146144d2576040519150601f19603f3d011682016040523d82523d6000602084013e6144d7565b606091505b509095945050505050565b606061450d826000015160018111156144fd576144fd614c71565b6001600160a01b03166001613e6d565b61451a8360200151614795565b6145278460400151614795565b604051602001614539939291906156d9565b6040516020818303038152906040529050919050565b600081815260018301602052604081205461459657508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610aa7565b506000610aa7565b600081815260018301602052604081205480156146875760006145c260018361518a565b85549091506000906145d69060019061518a565b905081811461463b5760008660000182815481106145f6576145f6614d90565b906000526020600020015490508087600001848154811061461957614619614d90565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061464c5761464c6157a2565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610aa7565b6000915050610aa7565b600060385460016038548460375461349891906151a1565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b03167fa9059cbb0000000000000000000000000000000000000000000000000000000017905291516000926060929087169161471f91906154f8565b6000604051808303816000865af19150503d806000811461475c576040519150601f19603f3d011682016040523d82523d6000602084013e614761565b606091505b50909250905081801561478c57508051158061478c57508080602001905181019061478c9190615514565b95945050505050565b6060816147d557505060408051808201909152600481527f3078303000000000000000000000000000000000000000000000000000000000602082015290565b8160005b81156147f857806147e981614e06565b915050600882901c91506147d9565b6111848482613e6d565b604080516060810182526000808252602082015290810161483e6040805160608101909152806000815260200160008152602001600081525090565b905290565b60006020828403121561485557600080fd5b81356001600160e01b03198116811461119857600080fd5b6001600160a01b0381168114610a6557600080fd5b803561198d8161486d565b8060608101831015610aa757600080fd5b8060808101831015610aa757600080fd5b60008083601f8401126148c157600080fd5b50813567ffffffffffffffff8111156148d957600080fd5b6020830191508360208260051b850101111561172857600080fd5b60008060008060008060008060008060006101408c8e03121561491657600080fd5b61491f8c614882565b9a5061492d60208d01614882565b995061493b60408d01614882565b985060608c0135975060808c0135965060a08c0135955060c08c0135945067ffffffffffffffff8060e08e0135111561497357600080fd5b6149838e60e08f01358f0161488d565b9450806101008e0135111561499757600080fd5b6149a88e6101008f01358f0161489e565b9350806101208e013511156149bc57600080fd5b506149ce8d6101208e01358e016148af565b81935080925050509295989b509295989b9093969950565b600080600080604085870312156149fc57600080fd5b843567ffffffffffffffff80821115614a1457600080fd5b614a20888389016148af565b90965094506020870135915080821115614a3957600080fd5b50614a46878288016148af565b95989497509550505050565b60008060008060008060608789031215614a6b57600080fd5b863567ffffffffffffffff80821115614a8357600080fd5b614a8f8a838b016148af565b90985096506020890135915080821115614aa857600080fd5b614ab48a838b016148af565b90965094506040890135915080821115614acd57600080fd5b50614ada89828a016148af565b979a9699509497509295939492505050565b600060208284031215614afe57600080fd5b81356111988161486d565b600060208284031215614b1b57600080fd5b5035919050565b60008060408385031215614b3557600080fd5b823591506020830135614b478161486d565b809150509250929050565b600060a08284031215614b6457600080fd5b50919050565b60006101608284031215614b6457600080fd5b60008060006101808486031215614b9357600080fd5b614b9d8585614b6a565b925061016084013567ffffffffffffffff80821115614bbb57600080fd5b818601915086601f830112614bcf57600080fd5b813581811115614bde57600080fd5b876020606083028501011115614bf357600080fd5b6020830194508093505050509250925092565b60008060408385031215614c1957600080fd5b8235614c248161486d565b946020939093013593505050565b60008060408385031215614c4557600080fd5b50508035926020909101359150565b60006101608284031215614c6757600080fd5b6111988383614b6a565b634e487b7160e01b600052602160045260246000fd5b60028110610a6557634e487b7160e01b600052602160045260246000fd5b81516040820190614cb581614c87565b808352506001600160a01b03602084015116602083015292915050565b60008060008060008060006080888a031215614ced57600080fd5b873567ffffffffffffffff80821115614d0557600080fd5b614d118b838c016148af565b909950975060208a0135915080821115614d2a57600080fd5b614d368b838c016148af565b909750955060408a0135915080821115614d4f57600080fd5b614d5b8b838c016148af565b909550935060608a0135915080821115614d7457600080fd5b50614d818a828b0161489e565b91505092959891949750929550565b634e487b7160e01b600052603260045260246000fd5b6000808335601e19843603018112614dbd57600080fd5b83018035915067ffffffffffffffff821115614dd857600080fd5b6020019150600581901b360382131561172857600080fd5b634e487b7160e01b600052601160045260246000fd5b6000600019821415614e1a57614e1a614df0565b5060010190565b634e487b7160e01b600052604160045260246000fd5b6040516060810167ffffffffffffffff81118282101715614e6857634e487b7160e01b600052604160045260246000fd5b60405290565b60028110610a6557600080fd5b600060608284031215614e8d57600080fd5b614e95614e37565b90508135614ea281614e6e565b80825250602082013560208201526040820135604082015292915050565b600060a08284031215614ed257600080fd5b614eda614e37565b8235614ee58161486d565b81526020830135614ef58161486d565b6020820152614f078460408501614e7b565b60408201529392505050565b600060608284031215614f2557600080fd5b6040516060810181811067ffffffffffffffff82111715614f5657634e487b7160e01b600052604160045260246000fd5b604052823560ff81168114614f6a57600080fd5b8152602083810135908201526040928301359281019290925250919050565b600060208284031215614f9b57600080fd5b5051919050565b600060608284031215614fb457600080fd5b614fbc614e37565b90508135614fc98161486d565b81526020820135614fd98161486d565b806020830152506040820135604082015292915050565b6000610160828403121561500357600080fd5b60405160a0810181811067ffffffffffffffff8211171561503457634e487b7160e01b600052604160045260246000fd5b60405282358152602083013561504981614e6e565b602082015261505b8460408501614fa2565b604082015261506d8460a08501614fa2565b6060820152615080846101008501614e7b565b60808201529392505050565b80356150978161486d565b6001600160a01b0390811683526020820135906150b38261486d565b166020830152604090810135910152565b6000610180820190508382528235602083015260208301356150e581614e6e565b6150ee81614c87565b80604084015250615105606083016040850161508c565b61511560c0830160a0850161508c565b61012061010084013561512781614e6e565b61513081614c87565b81840152830135610140808401919091529092013561016090910152919050565b60006020828403121561516357600080fd5b813561119881614e6e565b60006060828403121561518057600080fd5b6111988383614e7b565b60008282101561519c5761519c614df0565b500390565b60008160001904831182151516156151bb576151bb614df0565b500290565b6000610180820190508382528251602083015260208301516151e181614c87565b6040838101919091528381015180516001600160a01b03908116606086015260208201511660808501529081015160a084015250606083015180516001600160a01b0390811660c085015260208201511660e08401526040810151610100840152506080830151805161525381614c87565b6101208401526020810151610140840152604001516101609092019190915292915050565b8183526000602080850194508260005b858110156152b657813561529b8161486d565b6001600160a01b031687529582019590820190600101615288565b509495945050505050565b6060815260006152d560608301888a615278565b6020838203818501526152e982888a615278565b8481036040860152858152869250810160005b8681101561532a57833561530f81614e6e565b61531881614c87565b825292820192908201906001016152fc565b509a9950505050505050505050565b60408152600061534d604083018688615278565b82810360208401528381527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff84111561538557600080fd5b8360051b80866020840137600091016020019081529695505050505050565b60005b838110156153bf5781810151838201526020016153a7565b83811115610e895750506000910152565b7f416363657373436f6e74726f6c3a206163636f756e74200000000000000000008152600083516154088160178501602088016153a4565b7f206973206d697373696e6720726f6c652000000000000000000000000000000060179184019182015283516154458160288401602088016153a4565b01602801949350505050565b60208152600082518060208401526154708160408501602087016153a4565b601f01601f19169190910160400192915050565b6000821982111561549757615497614df0565b500190565b6000826154b957634e487b7160e01b600052601260045260246000fd5b500490565b8681526020810186905260c081016154d586614c87565b8560408301528460608301528360808301528260a0830152979650505050505050565b6000825161550a8184602087016153a4565b9190910192915050565b60006020828403121561552657600080fd5b8151801515811461119857600080fd5b7f546f6b656e3a20636f756c64206e6f74207472616e7366657220000000000000815260008551602061556f82601a8601838b016153a4565b7f2066726f6d200000000000000000000000000000000000000000000000000000601a9285019283015286516155aa81838501848b016153a4565b630103a37960e51b92018181019290925285516155cd81602485018985016153a4565b660103a37b5b2b7160cd1b6024939091019283015284516155f481602b85018489016153a4565b91909101602b01979650505050505050565b60008161561557615615614df0565b506000190190565b8481526080810161562d85614c87565b84602083015283604083015282606083015295945050505050565b7f546f6b656e3a20636f756c64206e6f74207472616e736665722000000000000081526000845161568081601a8501602089016153a4565b630103a37960e51b601a9184019182015284516156a481601e8401602089016153a4565b660103a37b5b2b7160cd1b601e929091019182015283516156cc8160258401602088016153a4565b0160250195945050505050565b7f546f6b656e496e666f280000000000000000000000000000000000000000000081526000845161571181600a8501602089016153a4565b80830190507f2c0000000000000000000000000000000000000000000000000000000000000080600a830152855161575081600b850160208a016153a4565b600b920191820152835161576b81600c8401602088016153a4565b7f2900000000000000000000000000000000000000000000000000000000000000600c9290910191820152600d0195945050505050565b634e487b7160e01b600052603160045260246000fdfeb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610348617350726f787941646d696e3a20756e617574686f72697a65642073656e64") + ]; +} diff --git a/crates/forge/bin/cmd/init.rs b/crates/forge/bin/cmd/init.rs index 96144dc63..d05c8a7bd 100644 --- a/crates/forge/bin/cmd/init.rs +++ b/crates/forge/bin/cmd/init.rs @@ -9,36 +9,36 @@ use std::path::{Path, PathBuf}; use yansi::Paint; /// CLI arguments for `forge init`. -#[derive(Clone, Debug, Parser)] +#[derive(Clone, Debug, Default, Parser)] pub struct InitArgs { /// The root directory of the new project. #[arg(value_hint = ValueHint::DirPath, default_value = ".", value_name = "PATH")] - root: PathBuf, + pub root: PathBuf, /// The template to start from. #[arg(long, short)] - template: Option, + pub template: Option, /// Branch argument that can only be used with template option. /// If not specified, the default branch is used. #[arg(long, short, requires = "template")] - branch: Option, + pub branch: Option, /// Do not install dependencies from the network. #[arg(long, conflicts_with = "template", visible_alias = "no-deps")] - offline: bool, + pub offline: bool, /// Create the project even if the specified root directory is not empty. #[arg(long, conflicts_with = "template")] - force: bool, + pub force: bool, /// Create a .vscode/settings.json file with Solidity settings, and generate a remappings.txt /// file. #[arg(long, conflicts_with = "template")] - vscode: bool, + pub vscode: bool, #[command(flatten)] - opts: DependencyInstallOpts, + pub opts: DependencyInstallOpts, } impl InitArgs { diff --git a/crates/forge/bin/cmd/mod.rs b/crates/forge/bin/cmd/mod.rs index b01366aa7..c8d1dbb0e 100644 --- a/crates/forge/bin/cmd/mod.rs +++ b/crates/forge/bin/cmd/mod.rs @@ -42,6 +42,7 @@ pub mod bind; pub mod build; pub mod cache; +pub mod clone; pub mod config; pub mod coverage; pub mod create; diff --git a/crates/forge/bin/main.rs b/crates/forge/bin/main.rs index 05589f84a..1c7095026 100644 --- a/crates/forge/bin/main.rs +++ b/crates/forge/bin/main.rs @@ -55,6 +55,7 @@ fn main() -> Result<()> { ForgeSubcommand::Debug(cmd) => utils::block_on(cmd.run()), ForgeSubcommand::VerifyContract(args) => utils::block_on(args.run()), ForgeSubcommand::VerifyCheck(args) => utils::block_on(args.run()), + ForgeSubcommand::Clone(cmd) => utils::block_on(cmd.run()), ForgeSubcommand::Cache(cmd) => match cmd.sub { CacheSubcommands::Clean(cmd) => cmd.run(), CacheSubcommands::Ls(cmd) => cmd.run(), diff --git a/crates/forge/bin/opts.rs b/crates/forge/bin/opts.rs index 56308e167..6ca78da0f 100644 --- a/crates/forge/bin/opts.rs +++ b/crates/forge/bin/opts.rs @@ -1,7 +1,7 @@ use crate::cmd::{ - bind::BindArgs, build::BuildArgs, cache::CacheArgs, config, coverage, create::CreateArgs, - debug::DebugArgs, doc::DocArgs, flatten, fmt::FmtArgs, geiger, generate, init::InitArgs, - inspect, install::InstallArgs, remappings::RemappingArgs, remove::RemoveArgs, + bind::BindArgs, build::BuildArgs, cache::CacheArgs, clone::CloneArgs, config, coverage, + create::CreateArgs, debug::DebugArgs, doc::DocArgs, flatten, fmt::FmtArgs, geiger, generate, + init::InitArgs, inspect, install::InstallArgs, remappings::RemappingArgs, remove::RemoveArgs, selectors::SelectorsSubcommands, snapshot, test, tree, update, }; use clap::{Parser, Subcommand, ValueHint}; @@ -52,6 +52,9 @@ pub enum ForgeSubcommand { #[command(visible_aliases = ["b", "compile"])] Build(BuildArgs), + /// Clone a contract from Etherscan. + Clone(CloneArgs), + /// Debugs a single smart contract as a script. #[command(visible_alias = "d")] Debug(DebugArgs), diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index e081b3d61..b42d93cfd 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -1,6 +1,7 @@ //! Contains various tests for checking forge's commands use crate::constants::*; +use foundry_common::rpc::next_etherscan_api_key; use foundry_compilers::{artifacts::Metadata, remappings::Remapping, ConfigurableContractArtifact}; use foundry_config::{parse_with_profile, BasicConfig, Chain, Config, SolidityErrorCode}; use foundry_test_utils::{ @@ -432,6 +433,41 @@ forgetest!(fail_init_nonexistent_template, |prj, cmd| { cmd.assert_non_empty_stderr(); }); +// checks that clone works +forgetest!(can_clone, |prj, cmd| { + prj.wipe(); + + let foundry_toml = prj.root().join(Config::FILE_NAME); + assert!(!foundry_toml.exists()); + + cmd.args([ + "clone", + "--etherscan-api-key", + next_etherscan_api_key().as_str(), + "0x044b75f554b886A065b9567891e45c79542d7357", + ]) + .arg(prj.root()); + cmd.assert_non_empty_stdout(); + + let s = read_string(&foundry_toml); + let _config: BasicConfig = parse_with_profile(&s).unwrap().unwrap().1; +}); + +// Checks that quiet mode does not print anything for clone +forgetest!(can_clone_quiet, |prj, cmd| { + prj.wipe(); + + cmd.args([ + "clone", + "--etherscan-api-key", + next_etherscan_api_key().as_str(), + "--quiet", + "0xDb53f47aC61FE54F456A4eb3E09832D08Dd7BEec", + ]) + .arg(prj.root()); + cmd.assert_empty_stdout(); +}); + // checks that `clean` removes dapptools style paths forgetest!(can_clean, |prj, cmd| { prj.assert_create_dirs_exists(); diff --git a/testdata/etherscan/0x044b75f554b886A065b9567891e45c79542d7357/creation_data.json b/testdata/etherscan/0x044b75f554b886A065b9567891e45c79542d7357/creation_data.json new file mode 100644 index 000000000..d709187b8 --- /dev/null +++ b/testdata/etherscan/0x044b75f554b886A065b9567891e45c79542d7357/creation_data.json @@ -0,0 +1,5 @@ +{ + "contractAddress": "0x044b75f554b886a065b9567891e45c79542d7357", + "contractCreator": "0xf87bc5535602077d340806d71f805ea9907a843d", + "txHash": "0x9a89d2f5528bf07661e92f3f78a3311396f11f15da19e3ec4d880be1ad1a4bec" +} \ No newline at end of file diff --git a/testdata/etherscan/0x044b75f554b886A065b9567891e45c79542d7357/metadata.json b/testdata/etherscan/0x044b75f554b886A065b9567891e45c79542d7357/metadata.json new file mode 100644 index 000000000..12f7f6a3b --- /dev/null +++ b/testdata/etherscan/0x044b75f554b886A065b9567891e45c79542d7357/metadata.json @@ -0,0 +1,78 @@ +[ + { + "SourceCode": { + "language": "Solidity", + "sources": { + "contracts/InputStream.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\n\npragma solidity 0.8.10;\n\nlibrary InputStream {\n function createStream(bytes memory data) internal pure returns (uint256 stream) {\n assembly {\n stream := mload(0x40)\n mstore(0x40, add(stream, 64))\n mstore(stream, data)\n let length := mload(data)\n mstore(add(stream, 32), add(data, length))\n }\n }\n\n function isNotEmpty(uint256 stream) internal pure returns (bool) {\n uint256 pos;\n uint256 finish;\n assembly {\n pos := mload(stream)\n finish := mload(add(stream, 32))\n }\n return pos < finish;\n }\n\n function readUint8(uint256 stream) internal pure returns (uint8 res) {\n assembly {\n let pos := mload(stream)\n pos := add(pos, 1)\n res := mload(pos)\n mstore(stream, pos)\n }\n }\n\n function readUint16(uint256 stream) internal pure returns (uint16 res) {\n assembly {\n let pos := mload(stream)\n pos := add(pos, 2)\n res := mload(pos)\n mstore(stream, pos)\n }\n }\n\n function readUint32(uint256 stream) internal pure returns (uint32 res) {\n assembly {\n let pos := mload(stream)\n pos := add(pos, 4)\n res := mload(pos)\n mstore(stream, pos)\n }\n }\n\n function readUint(uint256 stream) internal pure returns (uint256 res) {\n assembly {\n let pos := mload(stream)\n pos := add(pos, 32)\n res := mload(pos)\n mstore(stream, pos)\n }\n }\n\n function readAddress(uint256 stream) internal pure returns (address res) {\n assembly {\n let pos := mload(stream)\n pos := add(pos, 20)\n res := mload(pos)\n mstore(stream, pos)\n }\n }\n\n function readBytes(uint256 stream) internal pure returns (bytes memory res) {\n assembly {\n let pos := mload(stream)\n res := add(pos, 32)\n let length := mload(res)\n mstore(stream, add(res, length))\n }\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../extensions/draft-IERC20Permit.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n function safePermit(\n IERC20Permit token,\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal {\n uint256 nonceBefore = token.nonces(owner);\n token.permit(owner, spender, value, deadline, v, r, s);\n uint256 nonceAfter = token.nonces(owner);\n require(nonceAfter == nonceBefore + 1, \"SafeERC20: permit did not succeed\");\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "interfaces/IBentoBoxMinimal.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\n\npragma solidity >=0.8.0;\n\nstruct Rebase {\n uint128 elastic;\n uint128 base;\n}\n\nstruct StrategyData {\n uint64 strategyStartDate;\n uint64 targetPercentage;\n uint128 balance; // the balance of the strategy that BentoBox thinks is in there\n}\n\n/// @notice A rebasing library\nlibrary RebaseLibrary {\n /// @notice Calculates the base value in relationship to `elastic` and `total`.\n function toBase(Rebase memory total, uint256 elastic) internal pure returns (uint256 base) {\n if (total.elastic == 0) {\n base = elastic;\n } else {\n base = (elastic * total.base) / total.elastic;\n }\n }\n\n /// @notice Calculates the elastic value in relationship to `base` and `total`.\n function toElastic(Rebase memory total, uint256 base) internal pure returns (uint256 elastic) {\n if (total.base == 0) {\n elastic = base;\n } else {\n elastic = (base * total.elastic) / total.base;\n }\n }\n}\n\n/// @notice Minimal BentoBox vault interface.\n/// @dev `token` is aliased as `address` from `IERC20` for simplicity.\ninterface IBentoBoxMinimal {\n /// @notice Balance per ERC-20 token per account in shares.\n function balanceOf(address, address) external view returns (uint256);\n\n /// @dev Helper function to represent an `amount` of `token` in shares.\n /// @param token The ERC-20 token.\n /// @param amount The `token` amount.\n /// @param roundUp If the result `share` should be rounded up.\n /// @return share The token amount represented in shares.\n function toShare(\n address token,\n uint256 amount,\n bool roundUp\n ) external view returns (uint256 share);\n\n /// @dev Helper function to represent shares back into the `token` amount.\n /// @param token The ERC-20 token.\n /// @param share The amount of shares.\n /// @param roundUp If the result should be rounded up.\n /// @return amount The share amount back into native representation.\n function toAmount(\n address token,\n uint256 share,\n bool roundUp\n ) external view returns (uint256 amount);\n\n /// @notice Registers this contract so that users can approve it for BentoBox.\n function registerProtocol() external;\n\n /// @notice Deposit an amount of `token` represented in either `amount` or `share`.\n /// @param token The ERC-20 token to deposit.\n /// @param from which account to pull the tokens.\n /// @param to which account to push the tokens.\n /// @param amount Token amount in native representation to deposit.\n /// @param share Token amount represented in shares to deposit. Takes precedence over `amount`.\n /// @return amountOut The amount deposited.\n /// @return shareOut The deposited amount represented in shares.\n function deposit(\n address token,\n address from,\n address to,\n uint256 amount,\n uint256 share\n ) external payable returns (uint256 amountOut, uint256 shareOut);\n\n /// @notice Withdraws an amount of `token` from a user account.\n /// @param token_ The ERC-20 token to withdraw.\n /// @param from which user to pull the tokens.\n /// @param to which user to push the tokens.\n /// @param amount of tokens. Either one of `amount` or `share` needs to be supplied.\n /// @param share Like above, but `share` takes precedence over `amount`.\n function withdraw(\n address token_,\n address from,\n address to,\n uint256 amount,\n uint256 share\n ) external returns (uint256 amountOut, uint256 shareOut);\n\n /// @notice Transfer shares from a user account to another one.\n /// @param token The ERC-20 token to transfer.\n /// @param from which user to pull the tokens.\n /// @param to which user to push the tokens.\n /// @param share The amount of `token` in shares.\n function transfer(\n address token,\n address from,\n address to,\n uint256 share\n ) external;\n\n /// @dev Reads the Rebase `totals`from storage for a given token\n function totals(address token) external view returns (Rebase memory total);\n\n function strategyData(address token) external view returns (StrategyData memory total);\n\n /// @dev Approves users' BentoBox assets to a \"master\" contract.\n function setMasterContractApproval(\n address user,\n address masterContract,\n bool approved,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n function harvest(\n address token,\n bool balance,\n uint256 maxChangeAmount\n ) external;\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\n * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\n *\n * _Available since v4.8._\n */\n function verifyCallResultFromTarget(\n address target,\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n if (success) {\n if (returndata.length == 0) {\n // only check isContract if the call was successful and the return data is empty\n // otherwise we already know that it was a contract\n require(isContract(target), \"Address: call to non-contract\");\n }\n return returndata;\n } else {\n _revert(returndata, errorMessage);\n }\n }\n\n /**\n * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason or using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n _revert(returndata, errorMessage);\n }\n }\n\n function _revert(bytes memory returndata, string memory errorMessage) private pure {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n /// @solidity memory-safe-assembly\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n}\n" + }, + "interfaces/IPool.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n\npragma solidity >=0.5.0;\npragma experimental ABIEncoderV2;\n\n/// @notice Trident pool interface.\ninterface IPool {\n /// @notice Executes a swap from one token to another.\n /// @dev The input tokens must've already been sent to the pool.\n /// @param data ABI-encoded params that the pool requires.\n /// @return finalAmountOut The amount of output tokens that were sent to the user.\n function swap(bytes calldata data) external returns (uint256 finalAmountOut);\n\n /// @notice Executes a swap from one token to another with a callback.\n /// @dev This function allows borrowing the output tokens and sending the input tokens in the callback.\n /// @param data ABI-encoded params that the pool requires.\n /// @return finalAmountOut The amount of output tokens that were sent to the user.\n function flashSwap(bytes calldata data) external returns (uint256 finalAmountOut);\n\n /// @notice Mints liquidity tokens.\n /// @param data ABI-encoded params that the pool requires.\n /// @return liquidity The amount of liquidity tokens that were minted for the user.\n function mint(bytes calldata data) external returns (uint256 liquidity);\n\n /// @notice Burns liquidity tokens.\n /// @dev The input LP tokens must've already been sent to the pool.\n /// @param data ABI-encoded params that the pool requires.\n /// @return withdrawnAmounts The amount of various output tokens that were sent to the user.\n function burn(bytes calldata data) external returns (TokenAmount[] memory withdrawnAmounts);\n\n /// @notice Burns liquidity tokens for a single output token.\n /// @dev The input LP tokens must've already been sent to the pool.\n /// @param data ABI-encoded params that the pool requires.\n /// @return amountOut The amount of output tokens that were sent to the user.\n function burnSingle(bytes calldata data) external returns (uint256 amountOut);\n\n /// @return A unique identifier for the pool type.\n function poolIdentifier() external pure returns (bytes32);\n\n /// @return An array of tokens supported by the pool.\n function getAssets() external view returns (address[] memory);\n\n /// @notice Simulates a trade and returns the expected output.\n /// @dev The pool does not need to include a trade simulator directly in itself - it can use a library.\n /// @param data ABI-encoded params that the pool requires.\n /// @return finalAmountOut The amount of output tokens that will be sent to the user if the trade is executed.\n function getAmountOut(bytes calldata data) external view returns (uint256 finalAmountOut);\n\n /// @notice Simulates a trade and returns the expected output.\n /// @dev The pool does not need to include a trade simulator directly in itself - it can use a library.\n /// @param data ABI-encoded params that the pool requires.\n /// @return finalAmountIn The amount of input tokens that are required from the user if the trade is executed.\n function getAmountIn(bytes calldata data) external view returns (uint256 finalAmountIn);\n\n /// @dev This event must be emitted on all swaps.\n event Swap(address indexed recipient, address indexed tokenIn, address indexed tokenOut, uint256 amountIn, uint256 amountOut);\n\n /// @dev This struct frames output tokens for burns.\n struct TokenAmount {\n address token;\n uint256 amount;\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n}\n" + }, + "contracts/RouteProcessor2.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\n\npragma solidity 0.8.10;\n\nimport '../interfaces/IUniswapV2Pair.sol';\nimport '../interfaces/IUniswapV3Pool.sol';\nimport '../interfaces/ITridentCLPool.sol';\nimport '../interfaces/IBentoBoxMinimal.sol';\nimport '../interfaces/IPool.sol';\nimport '../interfaces/IWETH.sol';\nimport './InputStream.sol';\nimport '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';\n\naddress constant NATIVE_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\naddress constant IMPOSSIBLE_POOL_ADDRESS = 0x0000000000000000000000000000000000000001;\n\n/// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)\nuint160 constant MIN_SQRT_RATIO = 4295128739;\n/// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)\nuint160 constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;\n\n/// @title A route processor for the Sushi Aggregator\n/// @author Ilya Lyalin\ncontract RouteProcessor2 {\n using SafeERC20 for IERC20;\n using InputStream for uint256;\n\n IBentoBoxMinimal public immutable bentoBox;\n address private lastCalledPool;\n\n uint private unlocked = 1;\n modifier lock() {\n require(unlocked == 1, 'RouteProcessor is locked');\n unlocked = 2;\n _;\n unlocked = 1;\n }\n\n constructor(address _bentoBox) {\n bentoBox = IBentoBoxMinimal(_bentoBox);\n lastCalledPool = IMPOSSIBLE_POOL_ADDRESS;\n }\n\n /// @notice For native unwrapping\n receive() external payable {}\n\n /// @notice Processes the route generated off-chain. Has a lock\n /// @param tokenIn Address of the input token\n /// @param amountIn Amount of the input token\n /// @param tokenOut Address of the output token\n /// @param amountOutMin Minimum amount of the output token\n /// @return amountOut Actual amount of the output token\n function processRoute(\n address tokenIn,\n uint256 amountIn,\n address tokenOut,\n uint256 amountOutMin,\n address to,\n bytes memory route\n ) external payable lock returns (uint256 amountOut) {\n return processRouteInternal(tokenIn, amountIn, tokenOut, amountOutMin, to, route);\n }\n\n /// @notice Transfers some value to and then processes the route\n /// @param transferValueTo Address where the value should be transferred\n /// @param amountValueTransfer How much value to transfer\n /// @param tokenIn Address of the input token\n /// @param amountIn Amount of the input token\n /// @param tokenOut Address of the output token\n /// @param amountOutMin Minimum amount of the output token\n /// @return amountOut Actual amount of the output token\n function transferValueAndprocessRoute(\n address payable transferValueTo,\n uint256 amountValueTransfer,\n address tokenIn,\n uint256 amountIn,\n address tokenOut,\n uint256 amountOutMin,\n address to,\n bytes memory route\n ) external payable lock returns (uint256 amountOut) {\n (bool success, bytes memory returnBytes) = transferValueTo.call{value: amountValueTransfer}('');\n require(success, string(abi.encodePacked(returnBytes)));\n return processRouteInternal(tokenIn, amountIn, tokenOut, amountOutMin, to, route);\n }\n\n /// @notice Processes the route generated off-chain\n /// @param tokenIn Address of the input token\n /// @param amountIn Amount of the input token\n /// @param tokenOut Address of the output token\n /// @param amountOutMin Minimum amount of the output token\n /// @return amountOut Actual amount of the output token\n function processRouteInternal(\n address tokenIn,\n uint256 amountIn,\n address tokenOut,\n uint256 amountOutMin,\n address to,\n bytes memory route\n ) private returns (uint256 amountOut) {\n uint256 balanceInInitial = tokenIn == NATIVE_ADDRESS ? address(this).balance : IERC20(tokenIn).balanceOf(msg.sender);\n uint256 balanceOutInitial = tokenOut == NATIVE_ADDRESS ? address(to).balance : IERC20(tokenOut).balanceOf(to);\n\n uint256 stream = InputStream.createStream(route);\n while (stream.isNotEmpty()) {\n uint8 commandCode = stream.readUint8();\n if (commandCode == 1) processMyERC20(stream);\n else if (commandCode == 2) processUserERC20(stream, amountIn);\n else if (commandCode == 3) processNative(stream);\n else if (commandCode == 4) processOnePool(stream);\n else if (commandCode == 5) processInsideBento(stream);\n else revert('RouteProcessor: Unknown command code');\n }\n\n uint256 balanceInFinal = tokenIn == NATIVE_ADDRESS ? address(this).balance : IERC20(tokenIn).balanceOf(msg.sender);\n require(balanceInFinal + amountIn >= balanceInInitial, 'RouteProcessor: Minimal imput balance violation');\n\n uint256 balanceOutFinal = tokenOut == NATIVE_ADDRESS ? address(to).balance : IERC20(tokenOut).balanceOf(to);\n require(balanceOutFinal >= balanceOutInitial + amountOutMin, 'RouteProcessor: Minimal ouput balance violation');\n\n amountOut = balanceOutFinal - balanceOutInitial;\n }\n\n /// @notice Processes native coin: call swap for all pools that swap from native coin\n /// @param stream Streamed process program\n function processNative(uint256 stream) private {\n uint256 amountTotal = address(this).balance;\n distributeAndSwap(stream, address(this), NATIVE_ADDRESS, amountTotal);\n }\n\n /// @notice Processes ERC20 token from this contract balance:\n /// @notice Call swap for all pools that swap from this token\n /// @param stream Streamed process program\n function processMyERC20(uint256 stream) private {\n address token = stream.readAddress();\n uint256 amountTotal = IERC20(token).balanceOf(address(this));\n unchecked {\n if (amountTotal > 0) amountTotal -= 1; // slot undrain protection\n }\n distributeAndSwap(stream, address(this), token, amountTotal);\n }\n \n /// @notice Processes ERC20 token from msg.sender balance:\n /// @notice Call swap for all pools that swap from this token\n /// @param stream Streamed process program\n /// @param amountTotal Amount of tokens to take from msg.sender\n function processUserERC20(uint256 stream, uint256 amountTotal) private {\n address token = stream.readAddress();\n distributeAndSwap(stream, msg.sender, token, amountTotal);\n }\n\n /// @notice Distributes amountTotal to several pools according to their shares and calls swap for each pool\n /// @param stream Streamed process program\n /// @param from Where to take liquidity for swap\n /// @param tokenIn Input token\n /// @param amountTotal Total amount of tokenIn for swaps \n function distributeAndSwap(uint256 stream, address from, address tokenIn, uint256 amountTotal) private {\n uint8 num = stream.readUint8();\n unchecked {\n for (uint256 i = 0; i < num; ++i) {\n uint16 share = stream.readUint16();\n uint256 amount = (amountTotal * share) / 65535;\n amountTotal -= amount;\n swap(stream, from, tokenIn, amount);\n }\n }\n }\n\n /// @notice Processes ERC20 token for cases when the token has only one output pool\n /// @notice In this case liquidity is already at pool balance. This is an optimization\n /// @notice Call swap for all pools that swap from this token\n /// @param stream Streamed process program\n function processOnePool(uint256 stream) private {\n address token = stream.readAddress();\n swap(stream, address(this), token, 0);\n }\n\n /// @notice Processes Bento tokens \n /// @notice Call swap for all pools that swap from this token\n /// @param stream Streamed process program\n function processInsideBento(uint256 stream) private {\n address token = stream.readAddress();\n uint8 num = stream.readUint8();\n\n uint256 amountTotal = bentoBox.balanceOf(token, address(this));\n unchecked {\n if (amountTotal > 0) amountTotal -= 1; // slot undrain protection\n for (uint256 i = 0; i < num; ++i) {\n uint16 share = stream.readUint16();\n uint256 amount = (amountTotal * share) / 65535;\n amountTotal -= amount;\n swap(stream, address(this), token, amount);\n }\n }\n }\n\n /// @notice Makes swap\n /// @param stream Streamed process program\n /// @param from Where to take liquidity for swap\n /// @param tokenIn Input token\n /// @param amountIn Amount of tokenIn to take for swap\n function swap(uint256 stream, address from, address tokenIn, uint256 amountIn) private {\n uint8 poolType = stream.readUint8();\n if (poolType == 0) swapUniV2(stream, from, tokenIn, amountIn);\n else if (poolType == 1) swapUniV3(stream, from, tokenIn, amountIn);\n else if (poolType == 2) wrapNative(stream, from, tokenIn, amountIn);\n else if (poolType == 3) bentoBridge(stream, from, tokenIn, amountIn);\n else if (poolType == 4) swapTrident(stream, from, tokenIn, amountIn);\n else if (poolType == 5) swapTridentCL(stream, from, tokenIn, amountIn);\n else revert('RouteProcessor: Unknown pool type');\n }\n\n /// @notice Wraps/unwraps native token\n /// @param stream [direction & fake, recipient, wrapToken?]\n /// @param from Where to take liquidity for swap\n /// @param tokenIn Input token\n /// @param amountIn Amount of tokenIn to take for swap\n function wrapNative(uint256 stream, address from, address tokenIn, uint256 amountIn) private {\n uint8 directionAndFake = stream.readUint8();\n address to = stream.readAddress();\n\n if (directionAndFake & 1 == 1) { // wrap native\n address wrapToken = stream.readAddress();\n if (directionAndFake & 2 == 0) IWETH(wrapToken).deposit{value: amountIn}();\n if (to != address(this)) IERC20(wrapToken).safeTransfer(to, amountIn);\n } else { // unwrap native\n if (directionAndFake & 2 == 0) {\n if (from != address(this)) IERC20(tokenIn).safeTransferFrom(from, address(this), amountIn);\n IWETH(tokenIn).withdraw(amountIn);\n }\n payable(to).transfer(address(this).balance);\n }\n }\n\n /// @notice Bridge/unbridge tokens to/from Bento\n /// @param stream [direction, recipient]\n /// @param from Where to take liquidity for swap\n /// @param tokenIn Input token\n /// @param amountIn Amount of tokenIn to take for swap\n function bentoBridge(uint256 stream, address from, address tokenIn, uint256 amountIn) private {\n uint8 direction = stream.readUint8();\n address to = stream.readAddress();\n\n if (direction > 0) { // outside to Bento\n // deposit to arbitrary recipient is possible only from address(bentoBox)\n if (amountIn != 0) {\n if (from == address(this)) IERC20(tokenIn).safeTransfer(address(bentoBox), amountIn);\n else IERC20(tokenIn).safeTransferFrom(from, address(bentoBox), amountIn);\n } else {\n // tokens already are at address(bentoBox)\n amountIn = IERC20(tokenIn).balanceOf(address(bentoBox)) +\n bentoBox.strategyData(tokenIn).balance -\n bentoBox.totals(tokenIn).elastic;\n }\n bentoBox.deposit(tokenIn, address(bentoBox), to, amountIn, 0);\n } else { // Bento to outside\n if (amountIn > 0) {\n bentoBox.transfer(tokenIn, from, address(this), amountIn);\n } else amountIn = bentoBox.balanceOf(tokenIn, address(this));\n bentoBox.withdraw(tokenIn, address(this), to, 0, amountIn);\n }\n }\n\n /// @notice UniswapV2 pool swap\n /// @param stream [pool, direction, recipient]\n /// @param from Where to take liquidity for swap\n /// @param tokenIn Input token\n /// @param amountIn Amount of tokenIn to take for swap\n function swapUniV2(uint256 stream, address from, address tokenIn, uint256 amountIn) private {\n address pool = stream.readAddress();\n uint8 direction = stream.readUint8();\n address to = stream.readAddress();\n\n (uint256 r0, uint256 r1, ) = IUniswapV2Pair(pool).getReserves();\n require(r0 > 0 && r1 > 0, 'Wrong pool reserves');\n (uint256 reserveIn, uint256 reserveOut) = direction == 1 ? (r0, r1) : (r1, r0);\n\n if (amountIn != 0) {\n if (from == address(this)) IERC20(tokenIn).safeTransfer(pool, amountIn);\n else IERC20(tokenIn).safeTransferFrom(from, pool, amountIn);\n } else amountIn = IERC20(tokenIn).balanceOf(pool) - reserveIn; // tokens already were transferred\n\n uint256 amountInWithFee = amountIn * 997;\n uint256 amountOut = (amountInWithFee * reserveOut) / (reserveIn * 1000 + amountInWithFee);\n (uint256 amount0Out, uint256 amount1Out) = direction == 1 ? (uint256(0), amountOut) : (amountOut, uint256(0));\n IUniswapV2Pair(pool).swap(amount0Out, amount1Out, to, new bytes(0));\n }\n\n /// @notice Trident pool swap\n /// @param stream [pool, swapData]\n /// @param from Where to take liquidity for swap\n /// @param tokenIn Input token\n /// @param amountIn Amount of tokenIn to take for swap\n function swapTrident(uint256 stream, address from, address tokenIn, uint256 amountIn) private {\n address pool = stream.readAddress();\n bytes memory swapData = stream.readBytes();\n\n if (amountIn != 0) {\n bentoBox.transfer(tokenIn, from, pool, amountIn);\n }\n \n IPool(pool).swap(swapData);\n }\n\n /// @notice UniswapV3 pool swap\n /// @param stream [pool, direction, recipient]\n /// @param from Where to take liquidity for swap\n /// @param tokenIn Input token\n /// @param amountIn Amount of tokenIn to take for swap\n function swapUniV3(uint256 stream, address from, address tokenIn, uint256 amountIn) private {\n address pool = stream.readAddress();\n bool zeroForOne = stream.readUint8() > 0;\n address recipient = stream.readAddress();\n\n lastCalledPool = pool;\n IUniswapV3Pool(pool).swap(\n recipient,\n zeroForOne,\n int256(amountIn),\n zeroForOne ? MIN_SQRT_RATIO + 1 : MAX_SQRT_RATIO - 1,\n abi.encode(tokenIn, from)\n );\n require(lastCalledPool == IMPOSSIBLE_POOL_ADDRESS, 'RouteProcessor.swapUniV3: unexpected'); // Just to be sure\n }\n\n /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.\n /// @dev In the implementation you must pay the pool tokens owed for the swap.\n /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.\n /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.\n /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by\n /// the end of the swap. If positive, the callback must send that amount of token0 to the pool.\n /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by\n /// the end of the swap. If positive, the callback must send that amount of token1 to the pool.\n /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call\n function uniswapV3SwapCallback(\n int256 amount0Delta,\n int256 amount1Delta,\n bytes calldata data\n ) external {\n require(msg.sender == lastCalledPool, 'RouteProcessor.uniswapV3SwapCallback: call from unknown source');\n lastCalledPool = IMPOSSIBLE_POOL_ADDRESS;\n (address tokenIn, address from) = abi.decode(data, (address, address));\n int256 amount = amount0Delta > 0 ? amount0Delta : amount1Delta;\n require(amount > 0, 'RouteProcessor.uniswapV3SwapCallback: not positive amount');\n\n if (from == address(this)) IERC20(tokenIn).safeTransfer(msg.sender, uint256(amount));\n else IERC20(tokenIn).safeTransferFrom(from, msg.sender, uint256(amount));\n }\n\n /// @notice TridentCL pool swap\n /// @param stream [pool, direction, recipient]\n /// @param from Where to take liquidity for swap\n /// @param tokenIn Input token\n /// @param amountIn Amount of tokenIn to take for swap\n function swapTridentCL(uint256 stream, address from, address tokenIn, uint256 amountIn) private {\n address pool = stream.readAddress();\n bool zeroForOne = stream.readUint8() > 0;\n address recipient = stream.readAddress();\n\n lastCalledPool = pool;\n ITridentCLPool(pool).swap(\n recipient,\n zeroForOne,\n int256(amountIn),\n zeroForOne ? MIN_SQRT_RATIO + 1 : MAX_SQRT_RATIO - 1,\n false,\n abi.encode(tokenIn, from)\n );\n require(lastCalledPool == IMPOSSIBLE_POOL_ADDRESS, 'RouteProcessor.swapTridentCL: unexpected'); // Just to be sure\n }\n\n /// @notice Called to `msg.sender` after executing a swap via ITridentCLPool#swap.\n /// @dev In the implementation you must pay the pool tokens owed for the swap.\n /// The caller of this method must be checked to be a TridentCLPool deployed by the canonical TridentCLFactory.\n /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.\n /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by\n /// the end of the swap. If positive, the callback must send that amount of token0 to the pool.\n /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by\n /// the end of the swap. If positive, the callback must send that amount of token1 to the pool.\n /// @param data Any data passed through by the caller via the ITridentCLPoolActions#swap call\n function tridentCLSwapCallback(\n int256 amount0Delta,\n int256 amount1Delta,\n bytes calldata data\n ) external {\n require(msg.sender == lastCalledPool, 'RouteProcessor.TridentCLSwapCallback: call from unknown source');\n lastCalledPool = IMPOSSIBLE_POOL_ADDRESS;\n (address tokenIn, address from) = abi.decode(data, (address, address));\n int256 amount = amount0Delta > 0 ? amount0Delta : amount1Delta;\n require(amount > 0, 'RouteProcessor.TridentCLSwapCallback: not positive amount');\n\n if (from == address(this)) IERC20(tokenIn).safeTransfer(msg.sender, uint256(amount));\n else IERC20(tokenIn).safeTransferFrom(from, msg.sender, uint256(amount));\n }\n}\n" + }, + "interfaces/ITridentCLPool.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n\npragma solidity 0.8.10;\n\ninterface ITridentCLPool {\n function token0() external returns (address);\n function token1() external returns (address);\n\n function swap(\n address recipient,\n bool zeroForOne,\n int256 amountSpecified,\n uint160 sqrtPriceLimitX96,\n bool unwrapBento,\n bytes calldata data\n ) external returns (int256 amount0, int256 amount1);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n */\ninterface IERC20Permit {\n /**\n * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\n * given ``owner``'s signed approval.\n *\n * IMPORTANT: The same issues {IERC20-approve} has related to transaction\n * ordering also apply here.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `deadline` must be a timestamp in the future.\n * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\n * over the EIP712-formatted function arguments.\n * - the signature must use ``owner``'s current nonce (see {nonces}).\n *\n * For more information on the signature format, see the\n * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\n * section].\n */\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n /**\n * @dev Returns the current nonce for `owner`. This value must be\n * included whenever a signature is generated for {permit}.\n *\n * Every successful call to {permit} increases ``owner``'s nonce by one. This\n * prevents a signature from being used multiple times.\n */\n function nonces(address owner) external view returns (uint256);\n\n /**\n * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\n */\n // solhint-disable-next-line func-name-mixedcase\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n" + }, + "interfaces/IUniswapV2Pair.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0\n\npragma solidity >=0.5.0;\n\ninterface IUniswapV2Pair {\n event Approval(address indexed owner, address indexed spender, uint value);\n event Transfer(address indexed from, address indexed to, uint value);\n\n function name() external pure returns (string memory);\n function symbol() external pure returns (string memory);\n function decimals() external pure returns (uint8);\n function totalSupply() external view returns (uint);\n function balanceOf(address owner) external view returns (uint);\n function allowance(address owner, address spender) external view returns (uint);\n\n function approve(address spender, uint value) external returns (bool);\n function transfer(address to, uint value) external returns (bool);\n function transferFrom(address from, address to, uint value) external returns (bool);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n function PERMIT_TYPEHASH() external pure returns (bytes32);\n function nonces(address owner) external view returns (uint);\n\n function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;\n\n event Mint(address indexed sender, uint amount0, uint amount1);\n event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);\n event Swap(\n address indexed sender,\n uint amount0In,\n uint amount1In,\n uint amount0Out,\n uint amount1Out,\n address indexed to\n );\n event Sync(uint112 reserve0, uint112 reserve1);\n\n function MINIMUM_LIQUIDITY() external pure returns (uint);\n function factory() external view returns (address);\n function token0() external view returns (address);\n function token1() external view returns (address);\n function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);\n function price0CumulativeLast() external view returns (uint);\n function price1CumulativeLast() external view returns (uint);\n function kLast() external view returns (uint);\n\n function mint(address to) external returns (uint liquidity);\n function burn(address to) external returns (uint amount0, uint amount1);\n function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;\n function skim(address to) external;\n function sync() external;\n\n function initialize(address, address) external;\n}" + }, + "interfaces/IUniswapV3Pool.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n\npragma solidity 0.8.10;\n\ninterface IUniswapV3Pool {\n function token0() external returns (address);\n function token1() external returns (address);\n\n function swap(\n address recipient,\n bool zeroForOne,\n int256 amountSpecified,\n uint160 sqrtPriceLimitX96,\n bytes calldata data\n ) external returns (int256 amount0, int256 amount1);\n}\n" + }, + "interfaces/IWETH.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n\npragma solidity 0.8.10;\n\ninterface IWETH {\n function deposit() external payable;\n\n function transfer(address to, uint256 value) external returns (bool);\n\n function withdraw(uint256) external;\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 10000000 + }, + "outputSelection": { + "*": { + "*": [ + "evm.bytecode", + "evm.deployedBytecode", + "devdoc", + "userdoc", + "metadata", + "abi" + ] + } + }, + "metadata": { + "useLiteralContent": true + }, + "libraries": {} + } + }, + "ABI": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_bentoBox\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"bentoBox\",\"outputs\":[{\"internalType\":\"contract IBentoBoxMinimal\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"tokenIn\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amountIn\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"tokenOut\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amountOutMin\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"route\",\"type\":\"bytes\"}],\"name\":\"processRoute\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"amountOut\",\"type\":\"uint256\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address payable\",\"name\":\"transferValueTo\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amountValueTransfer\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"tokenIn\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amountIn\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"tokenOut\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amountOutMin\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"route\",\"type\":\"bytes\"}],\"name\":\"transferValueAndprocessRoute\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"amountOut\",\"type\":\"uint256\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"amount0Delta\",\"type\":\"int256\"},{\"internalType\":\"int256\",\"name\":\"amount1Delta\",\"type\":\"int256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"tridentCLSwapCallback\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"amount0Delta\",\"type\":\"int256\"},{\"internalType\":\"int256\",\"name\":\"amount1Delta\",\"type\":\"int256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"uniswapV3SwapCallback\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]", + "ContractName": "RouteProcessor2", + "CompilerVersion": "v0.8.10+commit.fc410830", + "OptimizationUsed": 1, + "Runs": 10000000, + "ConstructorArguments": "0x000000000000000000000000f5bce5077908a1b7370b9ae04adc565ebd643966", + "EVMVersion": "Default", + "Library": "", + "LicenseType": "", + "Proxy": 0, + "SwarmSource": "" + } +] \ No newline at end of file diff --git a/testdata/etherscan/0x35Fb958109b70799a8f9Bc2a8b1Ee4cC62034193/creation_data.json b/testdata/etherscan/0x35Fb958109b70799a8f9Bc2a8b1Ee4cC62034193/creation_data.json new file mode 100644 index 000000000..2c3135353 --- /dev/null +++ b/testdata/etherscan/0x35Fb958109b70799a8f9Bc2a8b1Ee4cC62034193/creation_data.json @@ -0,0 +1,5 @@ +{ + "contractAddress": "0x35fb958109b70799a8f9bc2a8b1ee4cc62034193", + "contractCreator": "0x3e32324277e96b69750bc6f7c4ba27e122413e07", + "txHash": "0x41e3517f8262b55e1eb1707ba0760b603a70e89ea4a86eff56072fcc80c3d0a1" +} \ No newline at end of file diff --git a/testdata/etherscan/0x35Fb958109b70799a8f9Bc2a8b1Ee4cC62034193/metadata.json b/testdata/etherscan/0x35Fb958109b70799a8f9Bc2a8b1Ee4cC62034193/metadata.json new file mode 100644 index 000000000..7601e4d6e --- /dev/null +++ b/testdata/etherscan/0x35Fb958109b70799a8f9Bc2a8b1Ee4cC62034193/metadata.json @@ -0,0 +1,16 @@ +[ + { + "SourceCode": "/**\r\n *Submitted for verification at Etherscan.io on 2022-02-19\r\n*/\r\n\r\n/**\r\n *Submitted for verification at Etherscan.io on 2022-02-18\r\n*/\r\n\r\n/**\r\n *Submitted for verification at Etherscan.io on 2022-02-14\r\n*/\r\n\r\n/**\r\n *Submitted for verification at Etherscan.io on 2022-02-10\r\n*/\r\n\r\n/**\r\n *Submitted for verification at Etherscan.io on 2022-02-09\r\n*/\r\n\r\n/**\r\n *Submitted for verification at Etherscan.io on 2022-02-08\r\n*/\r\n\r\n/**\r\n *Submitted for verification at Etherscan.io on 2022-02-05\r\n*/\r\n\r\n/**\r\n *Submitted for verification at Etherscan.io on 2022-01-22\r\n*/\r\n\r\n// SPDX-License-Identifier: UNLICENSED\r\n/*\r\nmade by cty0312\r\n2022.01.22\r\n**/\r\n\r\npragma solidity >=0.7.0 <0.9.0;\r\n\r\nlibrary StringsUpgradeable {\r\n bytes16 private constant _HEX_SYMBOLS = \"0123456789abcdef\";\r\n\r\n /**\r\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\r\n */\r\n function toString(uint256 value) internal pure returns (string memory) {\r\n // Inspired by OraclizeAPI's implementation - MIT licence\r\n // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol\r\n\r\n if (value == 0) {\r\n return \"0\";\r\n }\r\n uint256 temp = value;\r\n uint256 digits;\r\n while (temp != 0) {\r\n digits++;\r\n temp /= 10;\r\n }\r\n bytes memory buffer = new bytes(digits);\r\n while (value != 0) {\r\n digits -= 1;\r\n buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));\r\n value /= 10;\r\n }\r\n return string(buffer);\r\n }\r\n\r\n /**\r\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\r\n */\r\n function toHexString(uint256 value) internal pure returns (string memory) {\r\n if (value == 0) {\r\n return \"0x00\";\r\n }\r\n uint256 temp = value;\r\n uint256 length = 0;\r\n while (temp != 0) {\r\n length++;\r\n temp >>= 8;\r\n }\r\n return toHexString(value, length);\r\n }\r\n\r\n /**\r\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\r\n */\r\n function toHexString(uint256 value, uint256 length)\r\n internal\r\n pure\r\n returns (string memory)\r\n {\r\n bytes memory buffer = new bytes(2 * length + 2);\r\n buffer[0] = \"0\";\r\n buffer[1] = \"x\";\r\n for (uint256 i = 2 * length + 1; i > 1; --i) {\r\n buffer[i] = _HEX_SYMBOLS[value & 0xf];\r\n value >>= 4;\r\n }\r\n require(value == 0, \"Strings: hex length insufficient\");\r\n return string(buffer);\r\n }\r\n}\r\n\r\nlibrary AddressUpgradeable {\r\n /**\r\n * @dev Returns true if `account` is a contract.\r\n *\r\n * [IMPORTANT]\r\n * ====\r\n * It is unsafe to assume that an address for which this function returns\r\n * false is an externally-owned account (EOA) and not a contract.\r\n *\r\n * Among others, `isContract` will return false for the following\r\n * types of addresses:\r\n *\r\n * - an externally-owned account\r\n * - a contract in construction\r\n * - an address where a contract will be created\r\n * - an address where a contract lived, but was destroyed\r\n * ====\r\n */\r\n function isContract(address account) internal view returns (bool) {\r\n // This method relies on extcodesize, which returns 0 for contracts in\r\n // construction, since the code is only stored at the end of the\r\n // constructor execution.\r\n\r\n uint256 size;\r\n assembly {\r\n size := extcodesize(account)\r\n }\r\n return size > 0;\r\n }\r\n\r\n /**\r\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\r\n * `recipient`, forwarding all available gas and reverting on errors.\r\n *\r\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\r\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\r\n * imposed by `transfer`, making them unable to receive funds via\r\n * `transfer`. {sendValue} removes this limitation.\r\n *\r\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\r\n *\r\n * IMPORTANT: because control is transferred to `recipient`, care must be\r\n * taken to not create reentrancy vulnerabilities. Consider using\r\n * {ReentrancyGuard} or the\r\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\r\n */\r\n function sendValue(address payable recipient, uint256 amount) internal {\r\n require(\r\n address(this).balance >= amount,\r\n \"Address: insufficient balance\"\r\n );\r\n\r\n (bool success, ) = recipient.call{value: amount}(\"\");\r\n require(\r\n success,\r\n \"Address: unable to send value, recipient may have reverted\"\r\n );\r\n }\r\n\r\n /**\r\n * @dev Performs a Solidity function call using a low level `call`. A\r\n * plain `call` is an unsafe replacement for a function call: use this\r\n * function instead.\r\n *\r\n * If `target` reverts with a revert reason, it is bubbled up by this\r\n * function (like regular Solidity function calls).\r\n *\r\n * Returns the raw returned data. To convert to the expected return value,\r\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\r\n *\r\n * Requirements:\r\n *\r\n * - `target` must be a contract.\r\n * - calling `target` with `data` must not revert.\r\n *\r\n * _Available since v3.1._\r\n */\r\n function functionCall(address target, bytes memory data)\r\n internal\r\n returns (bytes memory)\r\n {\r\n return functionCall(target, data, \"Address: low-level call failed\");\r\n }\r\n\r\n /**\r\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\r\n * `errorMessage` as a fallback revert reason when `target` reverts.\r\n *\r\n * _Available since v3.1._\r\n */\r\n function functionCall(\r\n address target,\r\n bytes memory data,\r\n string memory errorMessage\r\n ) internal returns (bytes memory) {\r\n return functionCallWithValue(target, data, 0, errorMessage);\r\n }\r\n\r\n /**\r\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\r\n * but also transferring `value` wei to `target`.\r\n *\r\n * Requirements:\r\n *\r\n * - the calling contract must have an ETH balance of at least `value`.\r\n * - the called Solidity function must be `payable`.\r\n *\r\n * _Available since v3.1._\r\n */\r\n function functionCallWithValue(\r\n address target,\r\n bytes memory data,\r\n uint256 value\r\n ) internal returns (bytes memory) {\r\n return\r\n functionCallWithValue(\r\n target,\r\n data,\r\n value,\r\n \"Address: low-level call with value failed\"\r\n );\r\n }\r\n\r\n /**\r\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\r\n * with `errorMessage` as a fallback revert reason when `target` reverts.\r\n *\r\n * _Available since v3.1._\r\n */\r\n function functionCallWithValue(\r\n address target,\r\n bytes memory data,\r\n uint256 value,\r\n string memory errorMessage\r\n ) internal returns (bytes memory) {\r\n require(\r\n address(this).balance >= value,\r\n \"Address: insufficient balance for call\"\r\n );\r\n require(isContract(target), \"Address: call to non-contract\");\r\n\r\n (bool success, bytes memory returndata) = target.call{value: value}(\r\n data\r\n );\r\n return verifyCallResult(success, returndata, errorMessage);\r\n }\r\n\r\n /**\r\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\r\n * but performing a static call.\r\n *\r\n * _Available since v3.3._\r\n */\r\n function functionStaticCall(address target, bytes memory data)\r\n internal\r\n view\r\n returns (bytes memory)\r\n {\r\n return\r\n functionStaticCall(\r\n target,\r\n data,\r\n \"Address: low-level static call failed\"\r\n );\r\n }\r\n\r\n /**\r\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\r\n * but performing a static call.\r\n *\r\n * _Available since v3.3._\r\n */\r\n function functionStaticCall(\r\n address target,\r\n bytes memory data,\r\n string memory errorMessage\r\n ) internal view returns (bytes memory) {\r\n require(isContract(target), \"Address: static call to non-contract\");\r\n\r\n (bool success, bytes memory returndata) = target.staticcall(data);\r\n return verifyCallResult(success, returndata, errorMessage);\r\n }\r\n\r\n /**\r\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\r\n * revert reason using the provided one.\r\n *\r\n * _Available since v4.3._\r\n */\r\n function verifyCallResult(\r\n bool success,\r\n bytes memory returndata,\r\n string memory errorMessage\r\n ) internal pure returns (bytes memory) {\r\n if (success) {\r\n return returndata;\r\n } else {\r\n // Look for revert reason and bubble it up if present\r\n if (returndata.length > 0) {\r\n // The easiest way to bubble the revert reason is using memory via assembly\r\n\r\n assembly {\r\n let returndata_size := mload(returndata)\r\n revert(add(32, returndata), returndata_size)\r\n }\r\n } else {\r\n revert(errorMessage);\r\n }\r\n }\r\n }\r\n}\r\n\r\nlibrary SafeMathUpgradeable {\r\n /**\r\n * @dev Returns the addition of two unsigned integers, reverting on\r\n * overflow.\r\n *\r\n * Counterpart to Solidity's `+` operator.\r\n *\r\n * Requirements:\r\n *\r\n * - Addition cannot overflow.\r\n */\r\n function add(uint256 a, uint256 b) internal pure returns (uint256) {\r\n uint256 c = a + b;\r\n require(c >= a, \"SafeMath: addition overflow\");\r\n\r\n return c;\r\n }\r\n\r\n /**\r\n * @dev Returns the subtraction of two unsigned integers, reverting on\r\n * overflow (when the result is negative).\r\n *\r\n * Counterpart to Solidity's `-` operator.\r\n *\r\n * Requirements:\r\n *\r\n * - Subtraction cannot overflow.\r\n */\r\n function sub(uint256 a, uint256 b) internal pure returns (uint256) {\r\n return sub(a, b, \"SafeMath: subtraction overflow\");\r\n }\r\n\r\n /**\r\n * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\r\n * overflow (when the result is negative).\r\n *\r\n * Counterpart to Solidity's `-` operator.\r\n *\r\n * Requirements:\r\n *\r\n * - Subtraction cannot overflow.\r\n */\r\n function sub(\r\n uint256 a,\r\n uint256 b,\r\n string memory errorMessage\r\n ) internal pure returns (uint256) {\r\n require(b <= a, errorMessage);\r\n uint256 c = a - b;\r\n\r\n return c;\r\n }\r\n\r\n /**\r\n * @dev Returns the multiplication of two unsigned integers, reverting on\r\n * overflow.\r\n *\r\n * Counterpart to Solidity's `*` operator.\r\n *\r\n * Requirements:\r\n *\r\n * - Multiplication cannot overflow.\r\n */\r\n function mul(uint256 a, uint256 b) internal pure returns (uint256) {\r\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\r\n // benefit is lost if 'b' is also tested.\r\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\r\n if (a == 0) {\r\n return 0;\r\n }\r\n\r\n uint256 c = a * b;\r\n require(c / a == b, \"SafeMath: multiplication overflow\");\r\n\r\n return c;\r\n }\r\n\r\n /**\r\n * @dev Returns the integer division of two unsigned integers. Reverts on\r\n * division by zero. The result is rounded towards zero.\r\n *\r\n * Counterpart to Solidity's `/` operator. Note: this function uses a\r\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\r\n * uses an invalid opcode to revert (consuming all remaining gas).\r\n *\r\n * Requirements:\r\n *\r\n * - The divisor cannot be zero.\r\n */\r\n function div(uint256 a, uint256 b) internal pure returns (uint256) {\r\n return div(a, b, \"SafeMath: division by zero\");\r\n }\r\n\r\n /**\r\n * @dev Returns the integer division of two unsigned integers. Reverts with custom message on\r\n * division by zero. The result is rounded towards zero.\r\n *\r\n * Counterpart to Solidity's `/` operator. Note: this function uses a\r\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\r\n * uses an invalid opcode to revert (consuming all remaining gas).\r\n *\r\n * Requirements:\r\n *\r\n * - The divisor cannot be zero.\r\n */\r\n function div(\r\n uint256 a,\r\n uint256 b,\r\n string memory errorMessage\r\n ) internal pure returns (uint256) {\r\n require(b > 0, errorMessage);\r\n uint256 c = a / b;\r\n // assert(a == b * c + a % b); // There is no case in which this doesn't hold\r\n\r\n return c;\r\n }\r\n\r\n /**\r\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\r\n * Reverts when dividing by zero.\r\n *\r\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\r\n * opcode (which leaves remaining gas untouched) while Solidity uses an\r\n * invalid opcode to revert (consuming all remaining gas).\r\n *\r\n * Requirements:\r\n *\r\n * - The divisor cannot be zero.\r\n */\r\n function mod(uint256 a, uint256 b) internal pure returns (uint256) {\r\n return mod(a, b, \"SafeMath: modulo by zero\");\r\n }\r\n\r\n /**\r\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\r\n * Reverts with custom message when dividing by zero.\r\n *\r\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\r\n * opcode (which leaves remaining gas untouched) while Solidity uses an\r\n * invalid opcode to revert (consuming all remaining gas).\r\n *\r\n * Requirements:\r\n *\r\n * - The divisor cannot be zero.\r\n */\r\n function mod(\r\n uint256 a,\r\n uint256 b,\r\n string memory errorMessage\r\n ) internal pure returns (uint256) {\r\n require(b != 0, errorMessage);\r\n return a % b;\r\n }\r\n}\r\n\r\nabstract contract Initializable {\r\n /**\r\n * @dev Indicates that the contract has been initialized.\r\n */\r\n bool private _initialized;\r\n\r\n /**\r\n * @dev Indicates that the contract is in the process of being initialized.\r\n */\r\n bool private _initializing;\r\n\r\n /**\r\n * @dev Modifier to protect an initializer function from being invoked twice.\r\n */\r\n modifier initializer() {\r\n // If the contract is initializing we ignore whether _initialized is set in order to support multiple\r\n // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the\r\n // contract may have been reentered.\r\n require(\r\n _initializing ? _isConstructor() : !_initialized,\r\n \"Initializable: contract is already initialized\"\r\n );\r\n\r\n bool isTopLevelCall = !_initializing;\r\n if (isTopLevelCall) {\r\n _initializing = true;\r\n _initialized = true;\r\n }\r\n\r\n _;\r\n\r\n if (isTopLevelCall) {\r\n _initializing = false;\r\n }\r\n }\r\n\r\n /**\r\n * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the\r\n * {initializer} modifier, directly or indirectly.\r\n */\r\n modifier onlyInitializing() {\r\n require(_initializing, \"Initializable: contract is not initializing\");\r\n _;\r\n }\r\n\r\n function _isConstructor() private view returns (bool) {\r\n return !AddressUpgradeable.isContract(address(this));\r\n }\r\n}\r\n\r\nabstract contract ContextUpgradeable is Initializable {\r\n function __Context_init() internal onlyInitializing {\r\n __Context_init_unchained();\r\n }\r\n\r\n function __Context_init_unchained() internal onlyInitializing {}\r\n\r\n function _msgSender() internal view virtual returns (address) {\r\n return msg.sender;\r\n }\r\n\r\n function _msgData() internal view virtual returns (bytes calldata) {\r\n return msg.data;\r\n }\r\n\r\n uint256[50] private __gap;\r\n}\r\n\r\nabstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {\r\n address private _owner;\r\n\r\n event OwnershipTransferred(\r\n address indexed previousOwner,\r\n address indexed newOwner\r\n );\r\n\r\n /**\r\n * @dev Initializes the contract setting the deployer as the initial owner.\r\n */\r\n function __Ownable_init() internal onlyInitializing {\r\n __Context_init_unchained();\r\n __Ownable_init_unchained();\r\n }\r\n\r\n function __Ownable_init_unchained() internal onlyInitializing {\r\n _transferOwnership(_msgSender());\r\n }\r\n\r\n /**\r\n * @dev Returns the address of the current owner.\r\n */\r\n function owner() public view virtual returns (address) {\r\n return _owner;\r\n }\r\n\r\n /**\r\n * @dev Throws if called by any account other than the owner.\r\n */\r\n modifier onlyOwner() {\r\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\r\n _;\r\n }\r\n\r\n /**\r\n * @dev Leaves the contract without owner. It will not be possible to call\r\n * `onlyOwner` functions anymore. Can only be called by the current owner.\r\n *\r\n * NOTE: Renouncing ownership will leave the contract without an owner,\r\n * thereby removing any functionality that is only available to the owner.\r\n */\r\n function renounceOwnership() public virtual onlyOwner {\r\n _transferOwnership(address(0));\r\n }\r\n\r\n /**\r\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\r\n * Can only be called by the current owner.\r\n */\r\n function transferOwnership(address newOwner) public virtual onlyOwner {\r\n require(\r\n newOwner != address(0),\r\n \"Ownable: new owner is the zero address\"\r\n );\r\n _transferOwnership(newOwner);\r\n }\r\n\r\n /**\r\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\r\n * Internal function without access restriction.\r\n */\r\n function _transferOwnership(address newOwner) internal virtual {\r\n address oldOwner = _owner;\r\n _owner = newOwner;\r\n emit OwnershipTransferred(oldOwner, newOwner);\r\n }\r\n\r\n uint256[49] private __gap;\r\n}\r\n\r\ninterface IERC165Upgradeable {\r\n /**\r\n * @dev Returns true if this contract implements the interface defined by\r\n * `interfaceId`. See the corresponding\r\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\r\n * to learn more about how these ids are created.\r\n *\r\n * This function call must use less than 30 000 gas.\r\n */\r\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\r\n}\r\n\r\ninterface IERC721Upgradeable is IERC165Upgradeable {\r\n /**\r\n * @dev Emitted when `tokenId` token is transferred from `from` to `to`.\r\n */\r\n event Transfer(\r\n address indexed from,\r\n address indexed to,\r\n uint256 indexed tokenId\r\n );\r\n\r\n /**\r\n * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.\r\n */\r\n event Approval(\r\n address indexed owner,\r\n address indexed approved,\r\n uint256 indexed tokenId\r\n );\r\n\r\n /**\r\n * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.\r\n */\r\n event ApprovalForAll(\r\n address indexed owner,\r\n address indexed operator,\r\n bool approved\r\n );\r\n\r\n /**\r\n * @dev Returns the number of tokens in ``owner``'s account.\r\n */\r\n function balanceOf(address owner) external view returns (uint256 balance);\r\n\r\n /**\r\n * @dev Returns the owner of the `tokenId` token.\r\n *\r\n * Requirements:\r\n *\r\n * - `tokenId` must exist.\r\n */\r\n function ownerOf(uint256 tokenId) external view returns (address owner);\r\n\r\n /**\r\n * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\r\n * are aware of the ERC721 protocol to prevent tokens from being forever locked.\r\n *\r\n * Requirements:\r\n *\r\n * - `from` cannot be the zero address.\r\n * - `to` cannot be the zero address.\r\n * - `tokenId` token must exist and be owned by `from`.\r\n * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.\r\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\r\n *\r\n * Emits a {Transfer} event.\r\n */\r\n function safeTransferFrom(\r\n address from,\r\n address to,\r\n uint256 tokenId\r\n ) external;\r\n\r\n /**\r\n * @dev Transfers `tokenId` token from `from` to `to`.\r\n *\r\n * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.\r\n *\r\n * Requirements:\r\n *\r\n * - `from` cannot be the zero address.\r\n * - `to` cannot be the zero address.\r\n * - `tokenId` token must be owned by `from`.\r\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\r\n *\r\n * Emits a {Transfer} event.\r\n */\r\n function transferFrom(\r\n address from,\r\n address to,\r\n uint256 tokenId\r\n ) external;\r\n\r\n /**\r\n * @dev Gives permission to `to` to transfer `tokenId` token to another account.\r\n * The approval is cleared when the token is transferred.\r\n *\r\n * Only a single account can be approved at a time, so approving the zero address clears previous approvals.\r\n *\r\n * Requirements:\r\n *\r\n * - The caller must own the token or be an approved operator.\r\n * - `tokenId` must exist.\r\n *\r\n * Emits an {Approval} event.\r\n */\r\n function approve(address to, uint256 tokenId) external;\r\n\r\n /**\r\n * @dev Returns the account approved for `tokenId` token.\r\n *\r\n * Requirements:\r\n *\r\n * - `tokenId` must exist.\r\n */\r\n function getApproved(uint256 tokenId)\r\n external\r\n view\r\n returns (address operator);\r\n\r\n /**\r\n * @dev Approve or remove `operator` as an operator for the caller.\r\n * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.\r\n *\r\n * Requirements:\r\n *\r\n * - The `operator` cannot be the caller.\r\n *\r\n * Emits an {ApprovalForAll} event.\r\n */\r\n function setApprovalForAll(address operator, bool _approved) external;\r\n\r\n /**\r\n * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.\r\n *\r\n * See {setApprovalForAll}\r\n */\r\n function isApprovedForAll(address owner, address operator)\r\n external\r\n view\r\n returns (bool);\r\n\r\n /**\r\n * @dev Safely transfers `tokenId` token from `from` to `to`.\r\n *\r\n * Requirements:\r\n *\r\n * - `from` cannot be the zero address.\r\n * - `to` cannot be the zero address.\r\n * - `tokenId` token must exist and be owned by `from`.\r\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\r\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\r\n *\r\n * Emits a {Transfer} event.\r\n */\r\n function safeTransferFrom(\r\n address from,\r\n address to,\r\n uint256 tokenId,\r\n bytes calldata data\r\n ) external;\r\n}\r\n\r\ninterface IERC20Upgradeable {\r\n /**\r\n * @dev Returns the amount of tokens in existence.\r\n */\r\n function totalSupply() external view returns (uint256);\r\n\r\n /**\r\n * @dev Returns the amount of tokens owned by `account`.\r\n */\r\n function balanceOf(address account) external view returns (uint256);\r\n\r\n /**\r\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\r\n *\r\n * Returns a boolean value indicating whether the operation succeeded.\r\n *\r\n * Emits a {Transfer} event.\r\n */\r\n function transfer(address recipient, uint256 amount)\r\n external\r\n returns (bool);\r\n\r\n /**\r\n * @dev Returns the remaining number of tokens that `spender` will be\r\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\r\n * zero by default.\r\n *\r\n * This value changes when {approve} or {transferFrom} are called.\r\n */\r\n function allowance(address owner, address spender)\r\n external\r\n view\r\n returns (uint256);\r\n\r\n /**\r\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\r\n *\r\n * Returns a boolean value indicating whether the operation succeeded.\r\n *\r\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\r\n * that someone may use both the old and the new allowance by unfortunate\r\n * transaction ordering. One possible solution to mitigate this race\r\n * condition is to first reduce the spender's allowance to 0 and set the\r\n * desired value afterwards:\r\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\r\n *\r\n * Emits an {Approval} event.\r\n */\r\n function approve(address spender, uint256 amount) external returns (bool);\r\n\r\n /**\r\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\r\n * allowance mechanism. `amount` is then deducted from the caller's\r\n * allowance.\r\n *\r\n * Returns a boolean value indicating whether the operation succeeded.\r\n *\r\n * Emits a {Transfer} event.\r\n */\r\n function transferFrom(\r\n address sender,\r\n address recipient,\r\n uint256 amount\r\n ) external returns (bool);\r\n\r\n\r\n function mintToken(address _address, uint256 _amount) external;\r\n /**\r\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\r\n * another (`to`).\r\n *\r\n * Note that `value` may be zero.\r\n */\r\n event Transfer(address indexed from, address indexed to, uint256 value);\r\n\r\n /**\r\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\r\n * a call to {approve}. `value` is the new allowance.\r\n */\r\n event Approval(\r\n address indexed owner,\r\n address indexed spender,\r\n uint256 value\r\n );\r\n}\r\n\r\ninterface IUniswapV2Factory {\r\n event PairCreated(\r\n address indexed token0,\r\n address indexed token1,\r\n address pair,\r\n uint256\r\n );\r\n\r\n function feeTo() external view returns (address);\r\n\r\n function feeToSetter() external view returns (address);\r\n\r\n function getPair(address tokenA, address tokenB)\r\n external\r\n view\r\n returns (address pair);\r\n\r\n function allPairs(uint256) external view returns (address pair);\r\n\r\n function allPairsLength() external view returns (uint256);\r\n\r\n function createPair(address tokenA, address tokenB)\r\n external\r\n returns (address pair);\r\n\r\n function setFeeTo(address) external;\r\n\r\n function setFeeToSetter(address) external;\r\n}\r\n\r\ninterface IUniswapV2Pair {\r\n event Approval(\r\n address indexed owner,\r\n address indexed spender,\r\n uint256 value\r\n );\r\n event Transfer(address indexed from, address indexed to, uint256 value);\r\n\r\n function name() external pure returns (string memory);\r\n\r\n function symbol() external pure returns (string memory);\r\n\r\n function decimals() external pure returns (uint8);\r\n\r\n function totalSupply() external view returns (uint256);\r\n\r\n function balanceOf(address owner) external view returns (uint256);\r\n\r\n function allowance(address owner, address spender)\r\n external\r\n view\r\n returns (uint256);\r\n\r\n function approve(address spender, uint256 value) external returns (bool);\r\n\r\n function transfer(address to, uint256 value) external returns (bool);\r\n\r\n function transferFrom(\r\n address from,\r\n address to,\r\n uint256 value\r\n ) external returns (bool);\r\n\r\n function DOMAIN_SEPARATOR() external view returns (bytes32);\r\n\r\n function PERMIT_TYPEHASH() external pure returns (bytes32);\r\n\r\n function nonces(address owner) external view returns (uint256);\r\n\r\n function permit(\r\n address owner,\r\n address spender,\r\n uint256 value,\r\n uint256 deadline,\r\n uint8 v,\r\n bytes32 r,\r\n bytes32 s\r\n ) external;\r\n\r\n event Mint(address indexed sender, uint256 amount0, uint256 amount1);\r\n event Burn(\r\n address indexed sender,\r\n uint256 amount0,\r\n uint256 amount1,\r\n address indexed to\r\n );\r\n event Swap(\r\n address indexed sender,\r\n uint256 amount0In,\r\n uint256 amount1In,\r\n uint256 amount0Out,\r\n uint256 amount1Out,\r\n address indexed to\r\n );\r\n event Sync(uint112 reserve0, uint112 reserve1);\r\n\r\n function MINIMUM_LIQUIDITY() external pure returns (uint256);\r\n\r\n function factory() external view returns (address);\r\n\r\n function token0() external view returns (address);\r\n\r\n function token1() external view returns (address);\r\n\r\n function getReserves()\r\n external\r\n view\r\n returns (\r\n uint112 reserve0,\r\n uint112 reserve1,\r\n uint32 blockTimestampLast\r\n );\r\n\r\n function price0CumulativeLast() external view returns (uint256);\r\n\r\n function price1CumulativeLast() external view returns (uint256);\r\n\r\n function kLast() external view returns (uint256);\r\n\r\n function mint(address to) external returns (uint256 liquidity);\r\n\r\n function burn(address to)\r\n external\r\n returns (uint256 amount0, uint256 amount1);\r\n\r\n function swap(\r\n uint256 amount0Out,\r\n uint256 amount1Out,\r\n address to,\r\n bytes calldata data\r\n ) external;\r\n\r\n function skim(address to) external;\r\n\r\n function sync() external;\r\n\r\n function initialize(address, address) external;\r\n}\r\n\r\ninterface IUniswapV2Router01 {\r\n function factory() external pure returns (address);\r\n\r\n function WETH() external pure returns (address);\r\n\r\n function addLiquidity(\r\n address tokenA,\r\n address tokenB,\r\n uint256 amountADesired,\r\n uint256 amountBDesired,\r\n uint256 amountAMin,\r\n uint256 amountBMin,\r\n address to,\r\n uint256 deadline\r\n )\r\n external\r\n returns (\r\n uint256 amountA,\r\n uint256 amountB,\r\n uint256 liquidity\r\n );\r\n\r\n function addLiquidityETH(\r\n address token,\r\n uint256 amountTokenDesired,\r\n uint256 amountTokenMin,\r\n uint256 amountETHMin,\r\n address to,\r\n uint256 deadline\r\n )\r\n external\r\n payable\r\n returns (\r\n uint256 amountToken,\r\n uint256 amountETH,\r\n uint256 liquidity\r\n );\r\n\r\n function removeLiquidity(\r\n address tokenA,\r\n address tokenB,\r\n uint256 liquidity,\r\n uint256 amountAMin,\r\n uint256 amountBMin,\r\n address to,\r\n uint256 deadline\r\n ) external returns (uint256 amountA, uint256 amountB);\r\n\r\n function removeLiquidityETH(\r\n address token,\r\n uint256 liquidity,\r\n uint256 amountTokenMin,\r\n uint256 amountETHMin,\r\n address to,\r\n uint256 deadline\r\n ) external returns (uint256 amountToken, uint256 amountETH);\r\n\r\n function removeLiquidityWithPermit(\r\n address tokenA,\r\n address tokenB,\r\n uint256 liquidity,\r\n uint256 amountAMin,\r\n uint256 amountBMin,\r\n address to,\r\n uint256 deadline,\r\n bool approveMax,\r\n uint8 v,\r\n bytes32 r,\r\n bytes32 s\r\n ) external returns (uint256 amountA, uint256 amountB);\r\n\r\n function removeLiquidityETHWithPermit(\r\n address token,\r\n uint256 liquidity,\r\n uint256 amountTokenMin,\r\n uint256 amountETHMin,\r\n address to,\r\n uint256 deadline,\r\n bool approveMax,\r\n uint8 v,\r\n bytes32 r,\r\n bytes32 s\r\n ) external returns (uint256 amountToken, uint256 amountETH);\r\n\r\n function swapExactTokensForTokens(\r\n uint256 amountIn,\r\n uint256 amountOutMin,\r\n address[] calldata path,\r\n address to,\r\n uint256 deadline\r\n ) external returns (uint256[] memory amounts);\r\n\r\n function swapTokensForExactTokens(\r\n uint256 amountOut,\r\n uint256 amountInMax,\r\n address[] calldata path,\r\n address to,\r\n uint256 deadline\r\n ) external returns (uint256[] memory amounts);\r\n\r\n function swapExactETHForTokens(\r\n uint256 amountOutMin,\r\n address[] calldata path,\r\n address to,\r\n uint256 deadline\r\n ) external payable returns (uint256[] memory amounts);\r\n\r\n function swapTokensForExactETH(\r\n uint256 amountOut,\r\n uint256 amountInMax,\r\n address[] calldata path,\r\n address to,\r\n uint256 deadline\r\n ) external returns (uint256[] memory amounts);\r\n\r\n function swapExactTokensForETH(\r\n uint256 amountIn,\r\n uint256 amountOutMin,\r\n address[] calldata path,\r\n address to,\r\n uint256 deadline\r\n ) external returns (uint256[] memory amounts);\r\n\r\n function swapETHForExactTokens(\r\n uint256 amountOut,\r\n address[] calldata path,\r\n address to,\r\n uint256 deadline\r\n ) external payable returns (uint256[] memory amounts);\r\n\r\n function quote(\r\n uint256 amountA,\r\n uint256 reserveA,\r\n uint256 reserveB\r\n ) external pure returns (uint256 amountB);\r\n\r\n function getAmountOut(\r\n uint256 amountIn,\r\n uint256 reserveIn,\r\n uint256 reserveOut\r\n ) external pure returns (uint256 amountOut);\r\n\r\n function getAmountIn(\r\n uint256 amountOut,\r\n uint256 reserveIn,\r\n uint256 reserveOut\r\n ) external pure returns (uint256 amountIn);\r\n\r\n function getAmountsOut(uint256 amountIn, address[] calldata path)\r\n external\r\n view\r\n returns (uint256[] memory amounts);\r\n\r\n function getAmountsIn(uint256 amountOut, address[] calldata path)\r\n external\r\n view\r\n returns (uint256[] memory amounts);\r\n}\r\n\r\ninterface IUniswapV2Router is IUniswapV2Router01 {\r\n function removeLiquidityETHSupportingFeeOnTransferTokens(\r\n address token,\r\n uint256 liquidity,\r\n uint256 amountTokenMin,\r\n uint256 amountETHMin,\r\n address to,\r\n uint256 deadline\r\n ) external returns (uint256 amountETH);\r\n\r\n function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(\r\n address token,\r\n uint256 liquidity,\r\n uint256 amountTokenMin,\r\n uint256 amountETHMin,\r\n address to,\r\n uint256 deadline,\r\n bool approveMax,\r\n uint8 v,\r\n bytes32 r,\r\n bytes32 s\r\n ) external returns (uint256 amountETH);\r\n\r\n function swapExactTokensForTokensSupportingFeeOnTransferTokens(\r\n uint256 amountIn,\r\n uint256 amountOutMin,\r\n address[] calldata path,\r\n address to,\r\n uint256 deadline\r\n ) external;\r\n\r\n function swapExactETHForTokensSupportingFeeOnTransferTokens(\r\n uint256 amountOutMin,\r\n address[] calldata path,\r\n address to,\r\n uint256 deadline\r\n ) external payable;\r\n\r\n function swapExactTokensForETHSupportingFeeOnTransferTokens(\r\n uint256 amountIn,\r\n uint256 amountOutMin,\r\n address[] calldata path,\r\n address to,\r\n uint256 deadline\r\n ) external;\r\n}\r\n\r\ninterface IERC20MetadataUpgradeable is IERC20Upgradeable {\r\n /**\r\n * @dev Returns the name of the token.\r\n */\r\n function name() external view returns (string memory);\r\n\r\n /**\r\n * @dev Returns the symbol of the token.\r\n */\r\n function symbol() external view returns (string memory);\r\n\r\n /**\r\n * @dev Returns the decimals places of the token.\r\n */\r\n function decimals() external view returns (uint8);\r\n}\r\n\r\nabstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable, IERC20MetadataUpgradeable {\r\n using SafeMathUpgradeable for uint256;\r\n\r\n mapping(address => uint256) private _balances;\r\n\r\n mapping(address => mapping(address => uint256)) private _allowances;\r\n\r\n uint256 private _totalSupply;\r\n\r\n string private _name;\r\n string private _symbol;\r\n\r\n /**\r\n * @dev Sets the values for {name} and {symbol}.\r\n *\r\n * The default value of {decimals} is 18. To select a different value for\r\n * {decimals} you should overload it.\r\n *\r\n * All two of these values are immutable: they can only be set once during\r\n * construction.\r\n */\r\n function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {\r\n __ERC20_init_unchained(name_, symbol_);\r\n }\r\n\r\n function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {\r\n _name = name_;\r\n _symbol = symbol_;\r\n }\r\n\r\n /**\r\n * @dev Returns the name of the token.\r\n */\r\n function name() public view virtual override returns (string memory) {\r\n return _name;\r\n }\r\n\r\n /**\r\n * @dev Returns the symbol of the token, usually a shorter version of the\r\n * name.\r\n */\r\n function symbol() public view virtual override returns (string memory) {\r\n return _symbol;\r\n }\r\n\r\n /**\r\n * @dev Returns the number of decimals used to get its user representation.\r\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\r\n * be displayed to a user as `5.05` (`505 / 10 ** 2`).\r\n *\r\n * Tokens usually opt for a value of 18, imitating the relationship between\r\n * Ether and Wei. This is the value {ERC20} uses, unless this function is\r\n * overridden;\r\n *\r\n * NOTE: This information is only used for _display_ purposes: it in\r\n * no way affects any of the arithmetic of the contract, including\r\n * {IERC20-balanceOf} and {IERC20-transfer}.\r\n */\r\n function decimals() public view virtual override returns (uint8) {\r\n return 18;\r\n }\r\n\r\n /**\r\n * @dev See {IERC20-totalSupply}.\r\n */\r\n function totalSupply() public view virtual override returns (uint256) {\r\n return _totalSupply;\r\n }\r\n\r\n /**\r\n * @dev See {IERC20-balanceOf}.\r\n */\r\n function balanceOf(address account) public view virtual override returns (uint256) {\r\n return _balances[account];\r\n }\r\n\r\n /**\r\n * @dev See {IERC20-transfer}.\r\n *\r\n * Requirements:\r\n *\r\n * - `to` cannot be the zero address.\r\n * - the caller must have a balance of at least `amount`.\r\n */\r\n function transfer(address to, uint256 amount) public virtual override returns (bool) {\r\n address owner = _msgSender();\r\n _transfer(owner, to, amount);\r\n return true;\r\n }\r\n\r\n /**\r\n * @dev See {IERC20-allowance}.\r\n */\r\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\r\n return _allowances[owner][spender];\r\n }\r\n\r\n /**\r\n * @dev See {IERC20-approve}.\r\n *\r\n * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on\r\n * `transferFrom`. This is semantically equivalent to an infinite approval.\r\n *\r\n * Requirements:\r\n *\r\n * - `spender` cannot be the zero address.\r\n */\r\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\r\n address owner = _msgSender();\r\n _approve(owner, spender, amount);\r\n return true;\r\n }\r\n\r\n /**\r\n * @dev See {IERC20-transferFrom}.\r\n *\r\n * Emits an {Approval} event indicating the updated allowance. This is not\r\n * required by the EIP. See the note at the beginning of {ERC20}.\r\n *\r\n * NOTE: Does not update the allowance if the current allowance\r\n * is the maximum `uint256`.\r\n *\r\n * Requirements:\r\n *\r\n * - `from` and `to` cannot be the zero address.\r\n * - `from` must have a balance of at least `amount`.\r\n * - the caller must have allowance for ``from``'s tokens of at least\r\n * `amount`.\r\n */\r\n function transferFrom(\r\n address from,\r\n address to,\r\n uint256 amount\r\n ) public virtual override returns (bool) {\r\n address spender = _msgSender();\r\n _spendAllowance(from, spender, amount);\r\n _transfer(from, to, amount);\r\n return true;\r\n }\r\n\r\n /**\r\n * @dev Atomically increases the allowance granted to `spender` by the caller.\r\n *\r\n * This is an alternative to {approve} that can be used as a mitigation for\r\n * problems described in {IERC20-approve}.\r\n *\r\n * Emits an {Approval} event indicating the updated allowance.\r\n *\r\n * Requirements:\r\n *\r\n * - `spender` cannot be the zero address.\r\n */\r\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\r\n address owner = _msgSender();\r\n _approve(owner, spender, _allowances[owner][spender] + addedValue);\r\n return true;\r\n }\r\n\r\n /**\r\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\r\n *\r\n * This is an alternative to {approve} that can be used as a mitigation for\r\n * problems described in {IERC20-approve}.\r\n *\r\n * Emits an {Approval} event indicating the updated allowance.\r\n *\r\n * Requirements:\r\n *\r\n * - `spender` cannot be the zero address.\r\n * - `spender` must have allowance for the caller of at least\r\n * `subtractedValue`.\r\n */\r\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\r\n address owner = _msgSender();\r\n uint256 currentAllowance = _allowances[owner][spender];\r\n require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\r\n unchecked {\r\n _approve(owner, spender, currentAllowance - subtractedValue);\r\n }\r\n\r\n return true;\r\n }\r\n\r\n /**\r\n * @dev Moves `amount` of tokens from `sender` to `recipient`.\r\n *\r\n * This internal function is equivalent to {transfer}, and can be used to\r\n * e.g. implement automatic token fees, slashing mechanisms, etc.\r\n *\r\n * Emits a {Transfer} event.\r\n *\r\n * Requirements:\r\n *\r\n * - `from` cannot be the zero address.\r\n * - `to` cannot be the zero address.\r\n * - `from` must have a balance of at least `amount`.\r\n */\r\n function _transfer(\r\n address from,\r\n address to,\r\n uint256 amount\r\n ) internal virtual {\r\n require(from != address(0), \"ERC20: transfer from the zero address\");\r\n require(to != address(0), \"ERC20: transfer to the zero address\");\r\n\r\n _beforeTokenTransfer(from, to, amount);\r\n\r\n uint256 fromBalance = _balances[from];\r\n require(fromBalance >= amount, \"ERC20: transfer amount exceeds balance\");\r\n unchecked {\r\n _balances[from] = fromBalance - amount;\r\n }\r\n _balances[to] += amount;\r\n\r\n emit Transfer(from, to, amount);\r\n\r\n _afterTokenTransfer(from, to, amount);\r\n }\r\n\r\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\r\n * the total supply.\r\n *\r\n * Emits a {Transfer} event with `from` set to the zero address.\r\n *\r\n * Requirements:\r\n *\r\n * - `account` cannot be the zero address.\r\n */\r\n function _mint(address account, uint256 amount) internal virtual {\r\n require(account != address(0), \"ERC20: mint to the zero address\");\r\n\r\n _beforeTokenTransfer(address(0), account, amount);\r\n\r\n _totalSupply += amount;\r\n _balances[account] += amount;\r\n emit Transfer(address(0), account, amount);\r\n\r\n _afterTokenTransfer(address(0), account, amount);\r\n }\r\n\r\n /**\r\n * @dev Destroys `amount` tokens from `account`, reducing the\r\n * total supply.\r\n *\r\n * Emits a {Transfer} event with `to` set to the zero address.\r\n *\r\n * Requirements:\r\n *\r\n * - `account` cannot be the zero address.\r\n * - `account` must have at least `amount` tokens.\r\n */\r\n function _burn(uint256 amount) public virtual {\r\n // require(_balances[msg.sender] >= amount,'insufficient balance!');\r\n\r\n // _beforeTokenTransfer(msg.sender, address(0x000000000000000000000000000000000000dEaD), amount);\r\n\r\n // _balances[msg.sender] = _balances[msg.sender].sub(amount, \"ERC20: burn amount exceeds balance\");\r\n // _totalSupply = _totalSupply.sub(amount);\r\n // emit Transfer(msg.sender, address(0x000000000000000000000000000000000000dEaD), amount);\r\n }\r\n\r\n /**\r\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\r\n *\r\n * This internal function is equivalent to `approve`, and can be used to\r\n * e.g. set automatic allowances for certain subsystems, etc.\r\n *\r\n * Emits an {Approval} event.\r\n *\r\n * Requirements:\r\n *\r\n * - `owner` cannot be the zero address.\r\n * - `spender` cannot be the zero address.\r\n */\r\n function _approve(\r\n address owner,\r\n address spender,\r\n uint256 amount\r\n ) internal virtual {\r\n require(owner != address(0), \"ERC20: approve from the zero address\");\r\n require(spender != address(0), \"ERC20: approve to the zero address\");\r\n\r\n _allowances[owner][spender] = amount;\r\n emit Approval(owner, spender, amount);\r\n }\r\n\r\n /**\r\n * @dev Spend `amount` form the allowance of `owner` toward `spender`.\r\n *\r\n * Does not update the allowance amount in case of infinite allowance.\r\n * Revert if not enough allowance is available.\r\n *\r\n * Might emit an {Approval} event.\r\n */\r\n function _spendAllowance(\r\n address owner,\r\n address spender,\r\n uint256 amount\r\n ) internal virtual {\r\n uint256 currentAllowance = allowance(owner, spender);\r\n if (currentAllowance != type(uint256).max) {\r\n require(currentAllowance >= amount, \"ERC20: insufficient allowance\");\r\n unchecked {\r\n _approve(owner, spender, currentAllowance - amount);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * @dev Hook that is called before any transfer of tokens. This includes\r\n * minting and burning.\r\n *\r\n * Calling conditions:\r\n *\r\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\r\n * will be transferred to `to`.\r\n * - when `from` is zero, `amount` tokens will be minted for `to`.\r\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\r\n * - `from` and `to` are never both zero.\r\n *\r\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\r\n */\r\n function _beforeTokenTransfer(\r\n address from,\r\n address to,\r\n uint256 amount\r\n ) internal virtual {}\r\n\r\n /**\r\n * @dev Hook that is called after any transfer of tokens. This includes\r\n * minting and burning.\r\n *\r\n * Calling conditions:\r\n *\r\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\r\n * has been transferred to `to`.\r\n * - when `from` is zero, `amount` tokens have been minted for `to`.\r\n * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\r\n * - `from` and `to` are never both zero.\r\n *\r\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\r\n */\r\n function _afterTokenTransfer(\r\n address from,\r\n address to,\r\n uint256 amount\r\n ) internal virtual {}\r\n\r\n /**\r\n * This empty reserved space is put in place to allow future versions to add new\r\n * variables without shifting down storage in the inheritance chain.\r\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\r\n */\r\n uint256[45] private __gap;\r\n}\r\n\r\ncontract BearXNFTStaking is OwnableUpgradeable {\r\n using SafeMathUpgradeable for uint256;\r\n using AddressUpgradeable for address;\r\n\r\n //-------------constant value------------------//\r\n address private UNISWAP_V2_ROUTER;\r\n address private WETH;\r\n uint256 DURATION_FOR_REWARDS;\r\n uint256 DURATION_FOR_STOP_REWARDS;\r\n\r\n address public BearXNFTAddress;\r\n address public ROOTxTokenAddress;\r\n address public SROOTxTokenAddress;\r\n uint256 public totalStakes;\r\n\r\n uint256 public MaxSROOTXrate;\r\n uint256 public MinSROOTXrate;\r\n uint256 public MaxRate;\r\n uint256 public MinRate;\r\n uint256 public maxprovision;\r\n uint256 public RateValue;\r\n uint256 public SROOTRateValue;\r\n\r\n struct stakingInfo {\r\n uint256 nft_id;\r\n uint256 stakedDate;\r\n uint256 claimedDate_SROOT;\r\n uint256 claimedDate_WETH;\r\n uint256 claimedDate_ROOTX;\r\n }\r\n\r\n mapping(address => stakingInfo[]) internal stakes;\r\n address[] internal stakers;\r\n uint256 public RootX_Store;\r\n\r\n // 1.29 update\r\n mapping(address => bool) internal m_stakers;\r\n bool ismaptransfered;\r\n\r\n // 2.15 update\r\n bool public islocked;\r\n\r\n function initialize() public initializer{\r\n __Ownable_init();\r\n UNISWAP_V2_ROUTER = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;\r\n WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;\r\n DURATION_FOR_REWARDS = 1 days;\r\n DURATION_FOR_STOP_REWARDS = 3650 days;\r\n BearXNFTAddress = 0xE22e1e620dffb03065CD77dB0162249c0c91bf01;\r\n ROOTxTokenAddress = 0xd718Ad25285d65eF4D79262a6CD3AEA6A8e01023;\r\n SROOTxTokenAddress = 0x99CFDf48d0ba4885A73786148A2f89d86c702170;\r\n totalStakes = 0; \r\n MaxSROOTXrate = 100*10**18;\r\n MinSROOTXrate = 50*10**18;\r\n MaxRate = 11050*10**18;\r\n MinRate = 500*10**18; \r\n maxprovision = 3000;\r\n RateValue = ((MaxRate - MinRate) / maxprovision);\r\n SROOTRateValue = (( MaxSROOTXrate - MinSROOTXrate ) / maxprovision);\r\n RootX_Store=0;\r\n ismaptransfered = false;\r\n }\r\n // 1.29 update\r\n function transferStakers() public {\r\n if(!ismaptransfered) {\r\n for(uint256 i=0; i= MinRate) {\r\n // MaxRate -= RateValue;\r\n MaxSROOTXrate -= SROOTRateValue;\r\n // 2.1 updated\r\n MaxRate = MaxRate.sub(10*(10**18));\r\n }\r\n require(\r\n IERC721Upgradeable(BearXNFTAddress).ownerOf(_id) == _addr,\r\n \"You are not a owner of the nft\"\r\n );\r\n require(\r\n IERC721Upgradeable(BearXNFTAddress).isApprovedForAll(msg.sender, address(this)) ==\r\n true,\r\n \"You should approve nft to the staking contract\"\r\n );\r\n IERC721Upgradeable(BearXNFTAddress).transferFrom(\r\n _addr,\r\n address(this),\r\n _id\r\n );\r\n // (bool _isStaker, ) = isStaker(_addr);\r\n (bool _isStaker, ) = is_m_Staker(_addr);\r\n stakingInfo memory sInfo = stakingInfo(\r\n _id,\r\n block.timestamp,\r\n block.timestamp,\r\n block.timestamp,\r\n block.timestamp\r\n );\r\n totalStakes = totalStakes.add(1);\r\n stakes[_addr].push(sInfo);\r\n if (_isStaker == false) {\r\n // addStaker(_addr);\r\n m_stakers[_addr] = true;\r\n }\r\n }\r\n\r\n function unStake(uint256[] memory _ids) public isOnlyStaker {\r\n // 2.15 update\r\n\r\n require(!islocked, \"Locked\");\r\n uint256[] memory ownerIds = stakeOf(msg.sender);\r\n require(_ids.length <= ownerIds.length, \"Id errors\");\r\n uint256 temp = 0;\r\n\r\n for(uint256 i=0; i<_ids.length;i++) {\r\n for(uint256 j=0;j 0) {\r\n IERC20Upgradeable(ROOTxTokenAddress).transfer(_addr, Rootamount);\r\n\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n stakes[_addr][i].claimedDate_ROOTX = block.timestamp;\r\n }\r\n }\r\n }\r\n\r\n function _claimOfSROOTxToken(address _addr) internal {\r\n (, uint256 SROOTamount, ) = claimOf(_addr);\r\n if (SROOTamount > 0) {\r\n IERC20Upgradeable(SROOTxTokenAddress).transfer(_addr, SROOTamount);\r\n\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n stakes[_addr][i].claimedDate_SROOT = block.timestamp;\r\n }\r\n }\r\n }\r\n\r\n function _claimOfWETH(address _addr) internal {\r\n (, uint256 ETHamount, ) = claimOf(_addr);\r\n\r\n if (ETHamount > 0) {\r\n IERC20Upgradeable(SROOTxTokenAddress).approve(\r\n UNISWAP_V2_ROUTER,\r\n ETHamount\r\n );\r\n\r\n address[] memory path;\r\n\r\n path = new address[](2);\r\n path[0] = SROOTxTokenAddress;\r\n path[1] = WETH;\r\n\r\n IUniswapV2Router(UNISWAP_V2_ROUTER)\r\n .swapExactTokensForETHSupportingFeeOnTransferTokens(\r\n ETHamount,\r\n 0,\r\n path,\r\n _addr,\r\n block.timestamp\r\n );\r\n\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n stakes[_addr][i].claimedDate_WETH = block.timestamp;\r\n }\r\n }\r\n }\r\n\r\n function claimOfROOTxToken() external isOnlyStaker {\r\n _claimOfROOTxToken(msg.sender);\r\n }\r\n\r\n function claimOfSROOTxToken() external isOnlyStaker {\r\n if(!isVested(msg.sender)){\r\n _claimOfSROOTxToken(msg.sender);\r\n }\r\n }\r\n\r\n function claimOfWETH() external isOnlyStaker {\r\n if(!isVested(msg.sender)) {\r\n _claimOfWETH(msg.sender);\r\n }\r\n\r\n }\r\n\r\n // 2.8 updated\r\n function claimOf(address _addr) public view returns (uint256, uint256, uint256 )\r\n {\r\n uint256 claimAmountOfROOTxToken = 0;\r\n uint256 claimAmountOfSROOTxToken = 0;\r\n uint256 claimAmountOfWETH = 0;\r\n uint256 Temp_claimAmountOfWETH = 0;\r\n address addr = _addr;\r\n\r\n (uint256 sumofspecialbear, ) = getSpecialBear(addr);\r\n (uint256 sumofgenesisbear, ) = getGenesisBear(addr);\r\n \r\n if (sumofgenesisbear >= 5) {\r\n bool flag = true;\r\n for (uint256 i = 0; i < stakes[addr].length; i++) {\r\n ///ROOTX\r\n uint256 dd_root = calDay(stakes[addr][i].claimedDate_ROOTX);\r\n if ((isGenesisBear(stakes[addr][i].nft_id) || isSpecialBear(stakes[addr][i].nft_id)) && dd_root <1) {\r\n flag = false;\r\n }\r\n if (flag && stakes[addr].length != 0) {\r\n claimAmountOfROOTxToken = RootX_Store.div(10**18).add(10*dd_root*sumofgenesisbear).add(10*dd_root*sumofspecialbear);\r\n }\r\n else if(!flag) {\r\n claimAmountOfROOTxToken = RootX_Store.div(10**18);\r\n }\r\n /// SROOTX and WETH\r\n if (isSpecialBear(stakes[addr][i].nft_id)) {\r\n uint256 dd_srootx = calDay(stakes[addr][i].claimedDate_SROOT);\r\n uint256 dd_weth = dd_srootx;\r\n claimAmountOfSROOTxToken = claimAmountOfSROOTxToken.add(200 * (10**18) * dd_srootx);\r\n claimAmountOfWETH = claimAmountOfWETH.add(200 * (10**18) * dd_weth);\r\n } else if (isGenesisBear(stakes[addr][i].nft_id)) {\r\n uint256 dd_srootx = calDay(stakes[addr][i].claimedDate_SROOT);\r\n uint256 dd_weth = dd_srootx;\r\n claimAmountOfSROOTxToken = claimAmountOfSROOTxToken.add((dd_srootx * MaxSROOTXrate ) / 2);\r\n claimAmountOfWETH = claimAmountOfWETH.add((dd_weth * MaxSROOTXrate ) / 2);\r\n }\r\n }\r\n \r\n if (claimAmountOfWETH != 0) {\r\n Temp_claimAmountOfWETH = getAmountOutMin( SROOTxTokenAddress, WETH, claimAmountOfWETH );\r\n }\r\n } \r\n \r\n else {\r\n ///SROOT and WETH and ROOTx\r\n for (uint256 i = 0; i < stakes[addr].length; i++) {\r\n if (isSpecialBear(stakes[addr][i].nft_id)) {\r\n uint256 dd_root = calDay(stakes[addr][i].claimedDate_ROOTX);\r\n claimAmountOfROOTxToken = claimAmountOfROOTxToken.add(dd_root * 10);\r\n uint256 dd_sroot = calDay(stakes[addr][i].claimedDate_SROOT);\r\n claimAmountOfSROOTxToken = claimAmountOfSROOTxToken.add(200 * (10**18) * dd_sroot);\r\n uint256 dd_weth = calDay(stakes[addr][i].claimedDate_WETH);\r\n claimAmountOfWETH = claimAmountOfWETH.add( 200 * (10**18) * dd_weth );\r\n }\r\n if (isGenesisBear(stakes[addr][i].nft_id)) {\r\n uint256 dd_root = calDay(stakes[addr][i].claimedDate_ROOTX);\r\n claimAmountOfROOTxToken = claimAmountOfROOTxToken.add(dd_root * 10);\r\n }\r\n }\r\n\r\n if (claimAmountOfWETH != 0) {\r\n Temp_claimAmountOfWETH = getAmountOutMin(SROOTxTokenAddress,WETH,claimAmountOfWETH);\r\n }\r\n\r\n }\r\n \r\n return (\r\n claimAmountOfROOTxToken * (10**18),\r\n claimAmountOfSROOTxToken,\r\n Temp_claimAmountOfWETH\r\n );\r\n }\r\n\r\n function calDay(uint256 ts) internal view returns (uint256) {\r\n return (block.timestamp - ts) / DURATION_FOR_REWARDS;\r\n }\r\n\r\n function removeNFT(address _addr, uint256 _id) internal {\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n if (stakes[_addr][i].nft_id == _id) {\r\n stakes[_addr][i] = stakes[_addr][stakes[_addr].length - 1];\r\n stakes[_addr].pop();\r\n }\r\n }\r\n if (stakes[_addr].length <= 0) {\r\n delete stakes[_addr];\r\n removeStaker(_addr);\r\n }\r\n }\r\n\r\n function isGenesisBear(uint256 _id) internal pure returns (bool) {\r\n bool returned;\r\n if (_id >= 0 && _id <= 3700) {\r\n returned = true;\r\n } else {\r\n returned = false;\r\n }\r\n return returned;\r\n }\r\n\r\n function isSpecialBear(uint256 _id) internal pure returns (bool) {\r\n bool returned;\r\n if (_id >= 1000000000000 && _id <= 1000000000005) {\r\n returned = true;\r\n }\r\n return returned;\r\n }\r\n\r\n function getSpecialBear(address _addr)\r\n public\r\n view\r\n returns (uint256, uint256[] memory)\r\n {\r\n uint256 sumofspecialbear = 0;\r\n\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n if (isSpecialBear(stakes[_addr][i].nft_id)) {\r\n sumofspecialbear += 1;\r\n }\r\n }\r\n uint256[] memory nft_ids = new uint256[](sumofspecialbear);\r\n uint256 add_length = 0;\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n if (isSpecialBear(stakes[_addr][i].nft_id)) {\r\n nft_ids[add_length] = (stakes[_addr][i].nft_id);\r\n add_length = add_length.add(1);\r\n }\r\n }\r\n return (sumofspecialbear, nft_ids);\r\n }\r\n\r\n function getGenesisBear(address _addr)\r\n public\r\n view\r\n returns (uint256, uint256[] memory)\r\n {\r\n uint256 sumofgenesisbear = 0;\r\n\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n if (isGenesisBear(stakes[_addr][i].nft_id)) {\r\n sumofgenesisbear = sumofgenesisbear.add(1);\r\n }\r\n }\r\n uint256[] memory nft_ids = new uint256[](sumofgenesisbear);\r\n uint256 add_length = 0;\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n if (isGenesisBear(stakes[_addr][i].nft_id)) {\r\n nft_ids[add_length] = (stakes[_addr][i].nft_id);\r\n add_length = add_length.add(1);\r\n }\r\n }\r\n return (sumofgenesisbear, nft_ids);\r\n }\r\n\r\n function isMiniBear(uint256 _id) internal pure returns (bool) {\r\n if (_id < 10000000000006) return false;\r\n if (_id > 10000000005299) return false;\r\n else return true;\r\n }\r\n\r\n function getMiniBear(address _addr)\r\n public\r\n view\r\n returns (uint256, uint256[] memory)\r\n {\r\n uint256 sumofminibear = 0;\r\n\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n if (isMiniBear(stakes[_addr][i].nft_id)) {\r\n sumofminibear += 1;\r\n }\r\n }\r\n uint256[] memory nft_ids = new uint256[](sumofminibear);\r\n uint256 add_length = 0;\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n if (isMiniBear(stakes[_addr][i].nft_id)) {\r\n nft_ids[add_length] = (stakes[_addr][i].nft_id);\r\n add_length = add_length.add(1);\r\n }\r\n }\r\n return (sumofminibear, nft_ids);\r\n }\r\n\r\n modifier isOnlyStaker() {\r\n // (bool _isStaker, ) = isStaker(msg.sender);\r\n (bool _isStaker, ) = is_m_Staker(msg.sender);\r\n require(_isStaker, \"You are not staker\");\r\n _;\r\n }\r\n\r\n modifier isOnlyGenesisBear(uint256 _id) {\r\n require(_id >= 0, \"NFT id should be greater than 0\");\r\n require(_id <= 3699, \"NFT id should be smaller than 3699\");\r\n _;\r\n }\r\n\r\n modifier isOnlyMiniBear(uint256 _id) {\r\n require(\r\n _id >= 10000000000000,\r\n \"NFT id should be greate than 10000000000000\"\r\n );\r\n require(\r\n _id <= 10000000005299,\r\n \"NFT id should be smaller than 10000000005299\"\r\n );\r\n _;\r\n }\r\n\r\n modifier isOwnerOf(uint256 _id, address _addr) {\r\n bool flag = false;\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n if (stakes[_addr][i].nft_id == _id) flag = true;\r\n }\r\n if (flag) _;\r\n }\r\n\r\n function isVested(address _addr) public view returns (bool) {\r\n bool status = true;\r\n\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n uint256 dd = calDay(stakes[_addr][i].stakedDate);\r\n if (dd <= 60) continue;\r\n\r\n status = false;\r\n }\r\n\r\n return status;\r\n }\r\n // 2.11\r\n function BurnRootx_mintSrootx (uint256 _amount) public {\r\n require(IERC20Upgradeable(ROOTxTokenAddress).allowance(msg.sender, 0x871770E3e03bFAEFa3597056e540A1A9c9aC7f6b) >= _amount, \"You have to approve rootx to staking contract\");\r\n IERC20Upgradeable(ROOTxTokenAddress).transferFrom(msg.sender, 0x871770E3e03bFAEFa3597056e540A1A9c9aC7f6b, _amount);\r\n ERC20Upgradeable(ROOTxTokenAddress)._burn(_amount);\r\n IERC20Upgradeable(SROOTxTokenAddress).mintToken(msg.sender, _amount.mul(5));\r\n }\r\n\r\n function lock () public {\r\n if(msg.sender == 0xd0d725208fd36BE1561050Fc1DD6a651d7eA7C89) {\r\n islocked = !islocked;\r\n }\r\n }\r\n}", + "ABI": "[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"BearXNFTAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"BurnRootx_mintSrootx\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MaxRate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MaxSROOTXrate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MinRate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MinSROOTXrate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"ROOTxTokenAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"RateValue\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"RootX_Store\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SROOTRateValue\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SROOTxTokenAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"claimOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"claimOfROOTxToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"claimOfSROOTxToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"claimOfWETH\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"_ids\",\"type\":\"uint256[]\"}],\"name\":\"createStake\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAPR\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"getGenesisBear\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"getMiniBear\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"getSpecialBear\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"get_APR\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"isVested\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_address\",\"type\":\"address\"}],\"name\":\"is_m_Staker\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"islocked\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lock\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"maxprovision\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"setBearXNFTAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"setROOTxTokenAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"setSROOTxTokenAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"stakeOf\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalStakes\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"transferStakers\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"_ids\",\"type\":\"uint256[]\"}],\"name\":\"unStake\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + "ContractName": "BearXNFTStaking", + "CompilerVersion": "v0.8.11+commit.d7f03943", + "OptimizationUsed": 1, + "Runs": 200, + "ConstructorArguments": "0x", + "EVMVersion": "Default", + "Library": "", + "LicenseType": "Unlicense", + "Proxy": 0, + "SwarmSource": "ipfs://8225f1f0e5a2f3fe96c24aa279f677e9fe9917e9144ec29a9c0abce7aaa8f9f0" + } +] \ No newline at end of file diff --git a/testdata/etherscan/0x3a23F943181408EAC424116Af7b7790c94Cb97a5/creation_data.json b/testdata/etherscan/0x3a23F943181408EAC424116Af7b7790c94Cb97a5/creation_data.json new file mode 100644 index 000000000..f9f9f5fa0 --- /dev/null +++ b/testdata/etherscan/0x3a23F943181408EAC424116Af7b7790c94Cb97a5/creation_data.json @@ -0,0 +1,5 @@ +{ + "contractAddress": "0x3a23f943181408eac424116af7b7790c94cb97a5", + "contractCreator": "0xe8dd38e673a93ccfc2e3d7053efccb5c93f49365", + "txHash": "0x29328ac0edf7b080320bc8ed998fcd3e866f7eec3775b0d91a86f5d02ab83c28" +} \ No newline at end of file diff --git a/testdata/etherscan/0x3a23F943181408EAC424116Af7b7790c94Cb97a5/metadata.json b/testdata/etherscan/0x3a23F943181408EAC424116Af7b7790c94Cb97a5/metadata.json new file mode 100644 index 000000000..23f3b4ce4 --- /dev/null +++ b/testdata/etherscan/0x3a23F943181408EAC424116Af7b7790c94Cb97a5/metadata.json @@ -0,0 +1,193 @@ +[ + { + "SourceCode": { + "language": "Solidity", + "sources": { + "src/bridges/hop/interfaces/amm.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\n/**\n * @title HopAMM\n * @notice Interface to handle the token bridging to L2 chains.\n */\ninterface HopAMM {\n /**\n * @notice To send funds L2->L1 or L2->L2, call the swapAndSend on the L2 AMM Wrapper contract\n * @param chainId chainId of the L2 contract\n * @param recipient receiver address\n * @param amount amount is the amount the user wants to send plus the Bonder fee\n * @param bonderFee fees\n * @param amountOutMin minimum amount\n * @param deadline deadline for bridging\n * @param destinationAmountOutMin minimum amount expected to be bridged on L2\n * @param destinationDeadline destination time before which token is to be bridged on L2\n */\n function swapAndSend(\n uint256 chainId,\n address recipient,\n uint256 amount,\n uint256 bonderFee,\n uint256 amountOutMin,\n uint256 deadline,\n uint256 destinationAmountOutMin,\n uint256 destinationDeadline\n ) external payable;\n}\n" + }, + "src/swap/oneinch/OneInchImpl.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport \"../SwapImplBase.sol\";\nimport {SwapFailed} from \"../../errors/SocketErrors.sol\";\nimport {ONEINCH} from \"../../static/RouteIdentifiers.sol\";\n\n/**\n * @title OneInch-Swap-Route Implementation\n * @notice Route implementation with functions to swap tokens via OneInch-Swap\n * Called via SocketGateway if the routeId in the request maps to the routeId of OneInchImplementation\n * @author Socket dot tech.\n */\ncontract OneInchImpl is SwapImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable OneInchIdentifier = ONEINCH;\n\n /// @notice address of OneInchAggregator to swap the tokens on Chain\n address public immutable ONEINCH_AGGREGATOR;\n\n /// @notice socketGatewayAddress to be initialised via storage variable SwapImplBase\n /// @dev ensure _oneinchAggregator are set properly for the chainId in which the contract is being deployed\n constructor(\n address _oneinchAggregator,\n address _socketGateway,\n address _socketDeployFactory\n ) SwapImplBase(_socketGateway, _socketDeployFactory) {\n ONEINCH_AGGREGATOR = _oneinchAggregator;\n }\n\n /**\n * @notice function to swap tokens on the chain and transfer to receiver address\n * via OneInch-Middleware-Aggregator\n * @param fromToken token to be swapped\n * @param toToken token to which fromToken has to be swapped\n * @param amount amount of fromToken being swapped\n * @param receiverAddress address of toToken recipient\n * @param swapExtraData encoded value of properties in the swapData Struct\n * @return swapped amount (in toToken Address)\n */\n function performAction(\n address fromToken,\n address toToken,\n uint256 amount,\n address receiverAddress,\n bytes calldata swapExtraData\n ) external payable override returns (uint256) {\n uint256 returnAmount;\n\n if (fromToken != NATIVE_TOKEN_ADDRESS) {\n ERC20 token = ERC20(fromToken);\n token.safeTransferFrom(msg.sender, socketGateway, amount);\n token.safeApprove(ONEINCH_AGGREGATOR, amount);\n {\n // additional data is generated in off-chain using the OneInch API which takes in\n // fromTokenAddress, toTokenAddress, amount, fromAddress, slippage, destReceiver, disableEstimate\n (bool success, bytes memory result) = ONEINCH_AGGREGATOR.call(\n swapExtraData\n );\n token.safeApprove(ONEINCH_AGGREGATOR, 0);\n\n if (!success) {\n revert SwapFailed();\n }\n\n returnAmount = abi.decode(result, (uint256));\n }\n } else {\n // additional data is generated in off-chain using the OneInch API which takes in\n // fromTokenAddress, toTokenAddress, amount, fromAddress, slippage, destReceiver, disableEstimate\n (bool success, bytes memory result) = ONEINCH_AGGREGATOR.call{\n value: amount\n }(swapExtraData);\n if (!success) {\n revert SwapFailed();\n }\n returnAmount = abi.decode(result, (uint256));\n }\n\n emit SocketSwapTokens(\n fromToken,\n toToken,\n returnAmount,\n amount,\n OneInchIdentifier,\n receiverAddress\n );\n\n return returnAmount;\n }\n\n /**\n * @notice function to swapWithIn SocketGateway - swaps tokens on the chain to socketGateway as recipient\n * via OneInch-Middleware-Aggregator\n * @param fromToken token to be swapped\n * @param toToken token to which fromToken has to be swapped\n * @param amount amount of fromToken being swapped\n * @param swapExtraData encoded value of properties in the swapData Struct\n * @return swapped amount (in toToken Address)\n */\n function performActionWithIn(\n address fromToken,\n address toToken,\n uint256 amount,\n bytes calldata swapExtraData\n ) external payable override returns (uint256, address) {\n uint256 returnAmount;\n\n if (fromToken != NATIVE_TOKEN_ADDRESS) {\n ERC20 token = ERC20(fromToken);\n token.safeTransferFrom(msg.sender, socketGateway, amount);\n token.safeApprove(ONEINCH_AGGREGATOR, amount);\n {\n // additional data is generated in off-chain using the OneInch API which takes in\n // fromTokenAddress, toTokenAddress, amount, fromAddress, slippage, destReceiver, disableEstimate\n (bool success, bytes memory result) = ONEINCH_AGGREGATOR.call(\n swapExtraData\n );\n token.safeApprove(ONEINCH_AGGREGATOR, 0);\n\n if (!success) {\n revert SwapFailed();\n }\n\n returnAmount = abi.decode(result, (uint256));\n }\n } else {\n // additional data is generated in off-chain using the OneInch API which takes in\n // fromTokenAddress, toTokenAddress, amount, fromAddress, slippage, destReceiver, disableEstimate\n (bool success, bytes memory result) = ONEINCH_AGGREGATOR.call{\n value: amount\n }(swapExtraData);\n if (!success) {\n revert SwapFailed();\n }\n returnAmount = abi.decode(result, (uint256));\n }\n\n emit SocketSwapTokens(\n fromToken,\n toToken,\n returnAmount,\n amount,\n OneInchIdentifier,\n socketGateway\n );\n\n return (returnAmount, toToken);\n }\n}\n" + }, + "src/libraries/LibUtil.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport \"./LibBytes.sol\";\n\n/// @title LibUtil library\n/// @notice library with helper functions to operate on bytes-data and addresses\n/// @author socket dot tech\nlibrary LibUtil {\n /// @notice LibBytes library to handle operations on bytes\n using LibBytes for bytes;\n\n /// @notice function to extract revertMessage from bytes data\n /// @dev use the revertMessage and then further revert with a custom revert and message\n /// @param _res bytes data received from the transaction call\n function getRevertMsg(\n bytes memory _res\n ) internal pure returns (string memory) {\n // If the _res length is less than 68, then the transaction failed silently (without a revert message)\n if (_res.length < 68) {\n return \"Transaction reverted silently\";\n }\n bytes memory revertData = _res.slice(4, _res.length - 4); // Remove the selector which is the first 4 bytes\n return abi.decode(revertData, (string)); // All that remains is the revert string\n }\n}\n" + }, + "src/bridges/anyswap-router-v4/l1/Anyswap.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {BridgeImplBase} from \"../../BridgeImplBase.sol\";\nimport {ANYSWAP} from \"../../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Anyswap-V4-Route L1 Implementation\n * @notice Route implementation with functions to bridge ERC20 via Anyswap-Bridge\n * Called via SocketGateway if the routeId in the request maps to the routeId of AnyswapImplementation\n * This is the L1 implementation, so this is used when transferring from l1 to supported l1s or L1.\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\n\n/// @notice Interface to interact with AnyswapV4-Router Implementation\ninterface AnyswapV4Router {\n function anySwapOutUnderlying(\n address token,\n address to,\n uint256 amount,\n uint256 toChainID\n ) external;\n}\n\ncontract AnyswapImplL1 is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable AnyswapIdentifier = ANYSWAP;\n\n /// @notice Function-selector for ERC20-token bridging on Anyswap-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4 public immutable ANYSWAP_L1_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(uint256,uint256,bytes32,address,address,address)\"\n )\n );\n\n bytes4 public immutable ANYSWAP_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(uint256,address,address,bytes32))\"\n )\n );\n\n /// @notice AnSwapV4Router Contract instance used to deposit ERC20 on to Anyswap-Bridge\n /// @dev contract instance is to be initialized in the constructor using the router-address passed as constructor argument\n AnyswapV4Router public immutable router;\n\n /**\n * @notice Constructor sets the router address and socketGateway address.\n * @dev anyswap 4 router is immutable. so no setter function required.\n */\n constructor(\n address _router,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n router = AnyswapV4Router(_router);\n }\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct AnyswapBridgeDataNoToken {\n /// @notice destination ChainId\n uint256 toChainId;\n /// @notice address of receiver of bridged tokens\n address receiverAddress;\n /// @notice address of wrapperToken, WrappedVersion of the token being bridged\n address wrapperTokenAddress;\n /// @notice socket offchain created hash\n bytes32 metadata;\n }\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct AnyswapBridgeData {\n /// @notice destination ChainId\n uint256 toChainId;\n /// @notice address of receiver of bridged tokens\n address receiverAddress;\n /// @notice address of wrapperToken, WrappedVersion of the token being bridged\n address wrapperTokenAddress;\n /// @notice address of token being bridged\n address token;\n /// @notice socket offchain created hash\n bytes32 metadata;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in AnyswapBridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for AnyswapBridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n AnyswapBridgeData memory anyswapBridgeData = abi.decode(\n bridgeData,\n (AnyswapBridgeData)\n );\n ERC20(anyswapBridgeData.token).safeApprove(address(router), amount);\n router.anySwapOutUnderlying(\n anyswapBridgeData.wrapperTokenAddress,\n anyswapBridgeData.receiverAddress,\n amount,\n anyswapBridgeData.toChainId\n );\n\n emit SocketBridge(\n amount,\n anyswapBridgeData.token,\n anyswapBridgeData.toChainId,\n AnyswapIdentifier,\n msg.sender,\n anyswapBridgeData.receiverAddress,\n anyswapBridgeData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in AnyswapBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param anyswapBridgeData encoded data for AnyswapBridge\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n AnyswapBridgeDataNoToken calldata anyswapBridgeData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n\n ERC20(token).safeApprove(address(router), bridgeAmount);\n router.anySwapOutUnderlying(\n anyswapBridgeData.wrapperTokenAddress,\n anyswapBridgeData.receiverAddress,\n bridgeAmount,\n anyswapBridgeData.toChainId\n );\n\n emit SocketBridge(\n bridgeAmount,\n token,\n anyswapBridgeData.toChainId,\n AnyswapIdentifier,\n msg.sender,\n anyswapBridgeData.receiverAddress,\n anyswapBridgeData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via Anyswap-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount being bridged\n * @param toChainId destination ChainId\n * @param receiverAddress address of receiver of bridged tokens\n * @param token address of token being bridged\n * @param wrapperTokenAddress address of wrapperToken, WrappedVersion of the token being bridged\n */\n function bridgeERC20To(\n uint256 amount,\n uint256 toChainId,\n bytes32 metadata,\n address receiverAddress,\n address token,\n address wrapperTokenAddress\n ) external payable {\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n tokenInstance.safeApprove(address(router), amount);\n router.anySwapOutUnderlying(\n wrapperTokenAddress,\n receiverAddress,\n amount,\n toChainId\n );\n\n emit SocketBridge(\n amount,\n token,\n toChainId,\n AnyswapIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n" + }, + "src/bridges/cbridge/CelerStorageWrapper.sol": { + "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity >=0.8.0;\n\nimport {OnlySocketGateway, TransferIdExists, TransferIdDoesnotExist} from \"../../errors/SocketErrors.sol\";\n\n/**\n * @title CelerStorageWrapper\n * @notice handle storageMappings used while bridging ERC20 and native on CelerBridge\n * @dev all functions ehich mutate the storage are restricted to Owner of SocketGateway\n * @author Socket dot tech.\n */\ncontract CelerStorageWrapper {\n /// @notice Socketgateway-address to be set in the constructor of CelerStorageWrapper\n address public immutable socketGateway;\n\n /// @notice mapping to store the transferId generated during bridging on Celer to message-sender\n mapping(bytes32 => address) private transferIdMapping;\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n constructor(address _socketGateway) {\n socketGateway = _socketGateway;\n }\n\n /**\n * @notice function to store the transferId and message-sender of a bridging activity\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in CelerBridgeData struct\n * @param transferId transferId generated during the bridging of ERC20 or native on CelerBridge\n * @param transferIdAddress message sender who is making the bridging on CelerBridge\n */\n function setAddressForTransferId(\n bytes32 transferId,\n address transferIdAddress\n ) external {\n if (msg.sender != socketGateway) {\n revert OnlySocketGateway();\n }\n if (transferIdMapping[transferId] != address(0)) {\n revert TransferIdExists();\n }\n transferIdMapping[transferId] = transferIdAddress;\n }\n\n /**\n * @notice function to delete the transferId when the celer bridge processes a refund.\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in CelerBridgeData struct\n * @param transferId transferId generated during the bridging of ERC20 or native on CelerBridge\n */\n function deleteTransferId(bytes32 transferId) external {\n if (msg.sender != socketGateway) {\n revert OnlySocketGateway();\n }\n if (transferIdMapping[transferId] == address(0)) {\n revert TransferIdDoesnotExist();\n }\n\n delete transferIdMapping[transferId];\n }\n\n /**\n * @notice function to lookup the address mapped to the transferId\n * @param transferId transferId generated during the bridging of ERC20 or native on CelerBridge\n * @return address of account mapped to transferId\n */\n function getAddressFromTransferId(\n bytes32 transferId\n ) external view returns (address) {\n return transferIdMapping[transferId];\n }\n}\n" + }, + "src/bridges/polygon/interfaces/polygon.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\n/**\n * @title RootChain Manager Interface for Polygon Bridge.\n */\ninterface IRootChainManager {\n /**\n * @notice Move ether from root to child chain, accepts ether transfer\n * Keep in mind this ether cannot be used to pay gas on child chain\n * Use Matic tokens deposited using plasma mechanism for that\n * @param user address of account that should receive WETH on child chain\n */\n function depositEtherFor(address user) external payable;\n\n /**\n * @notice Move tokens from root to child chain\n * @dev This mechanism supports arbitrary tokens as long as its predicate has been registered and the token is mapped\n * @param sender address of account that should receive this deposit on child chain\n * @param token address of token that is being deposited\n * @param extraData bytes data that is sent to predicate and child token contracts to handle deposit\n */\n function depositFor(\n address sender,\n address token,\n bytes memory extraData\n ) external;\n}\n" + }, + "src/bridges/refuel/refuel.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport \"./interfaces/refuel.sol\";\nimport \"../BridgeImplBase.sol\";\nimport {REFUEL} from \"../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Refuel-Route Implementation\n * @notice Route implementation with functions to bridge Native via Refuel-Bridge\n * Called via SocketGateway if the routeId in the request maps to the routeId of RefuelImplementation\n * @author Socket dot tech.\n */\ncontract RefuelBridgeImpl is BridgeImplBase {\n bytes32 public immutable RefuelIdentifier = REFUEL;\n\n /// @notice refuelBridge-Contract address used to deposit Native on Refuel-Bridge\n address public immutable refuelBridge;\n\n /// @notice Function-selector for Native bridging via Refuel-Bridge\n /// @dev This function selector is to be used while buidling transaction-data to bridge Native tokens\n bytes4 public immutable REFUEL_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(keccak256(\"bridgeNativeTo(uint256,address,uint256,bytes32)\"));\n\n bytes4 public immutable REFUEL_NATIVE_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\"swapAndBridge(uint32,address,uint256,bytes32,bytes)\")\n );\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n /// @dev ensure _refuelBridge are set properly for the chainId in which the contract is being deployed\n constructor(\n address _refuelBridge,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n refuelBridge = _refuelBridge;\n }\n\n // Function to receive Ether. msg.data must be empty\n receive() external payable {}\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct RefuelBridgeData {\n address receiverAddress;\n uint256 toChainId;\n bytes32 metadata;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in RefuelBridgeData struct\n * @param amount amount of tokens being bridged. this must be only native\n * @param bridgeData encoded data for RefuelBridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n RefuelBridgeData memory refuelBridgeData = abi.decode(\n bridgeData,\n (RefuelBridgeData)\n );\n IRefuel(refuelBridge).depositNativeToken{value: amount}(\n refuelBridgeData.toChainId,\n refuelBridgeData.receiverAddress\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n refuelBridgeData.toChainId,\n RefuelIdentifier,\n msg.sender,\n refuelBridgeData.receiverAddress,\n refuelBridgeData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in RefuelBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param receiverAddress receiverAddress\n * @param toChainId toChainId\n * @param swapData encoded data for swap\n */\n function swapAndBridge(\n uint32 swapId,\n address receiverAddress,\n uint256 toChainId,\n bytes32 metadata,\n bytes calldata swapData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, ) = abi.decode(result, (uint256, address));\n IRefuel(refuelBridge).depositNativeToken{value: bridgeAmount}(\n toChainId,\n receiverAddress\n );\n\n emit SocketBridge(\n bridgeAmount,\n NATIVE_TOKEN_ADDRESS,\n toChainId,\n RefuelIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n\n /**\n * @notice function to handle Native bridging to receipent via Refuel-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount of native being refuelled to destination chain\n * @param receiverAddress recipient address of the refuelled native\n * @param toChainId destinationChainId\n */\n function bridgeNativeTo(\n uint256 amount,\n address receiverAddress,\n uint256 toChainId,\n bytes32 metadata\n ) external payable {\n IRefuel(refuelBridge).depositNativeToken{value: amount}(\n toChainId,\n receiverAddress\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n toChainId,\n RefuelIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n" + }, + "src/bridges/across/interfaces/across.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\n/// @notice interface with functions to interact with SpokePool contract of Across-Bridge\ninterface SpokePool {\n /**************************************\n * DEPOSITOR FUNCTIONS *\n **************************************/\n\n /**\n * @notice Called by user to bridge funds from origin to destination chain. Depositor will effectively lock\n * tokens in this contract and receive a destination token on the destination chain. The origin => destination\n * token mapping is stored on the L1 HubPool.\n * @notice The caller must first approve this contract to spend amount of originToken.\n * @notice The originToken => destinationChainId must be enabled.\n * @notice This method is payable because the caller is able to deposit native token if the originToken is\n * wrappedNativeToken and this function will handle wrapping the native token to wrappedNativeToken.\n * @param recipient Address to receive funds at on destination chain.\n * @param originToken Token to lock into this contract to initiate deposit.\n * @param amount Amount of tokens to deposit. Will be amount of tokens to receive less fees.\n * @param destinationChainId Denotes network where user will receive funds from SpokePool by a relayer.\n * @param relayerFeePct % of deposit amount taken out to incentivize a fast relayer.\n * @param quoteTimestamp Timestamp used by relayers to compute this deposit's realizedLPFeePct which is paid\n * to LP pool on HubPool.\n */\n function deposit(\n address recipient,\n address originToken,\n uint256 amount,\n uint256 destinationChainId,\n uint64 relayerFeePct,\n uint32 quoteTimestamp\n ) external payable;\n}\n" + }, + "src/bridges/arbitrum/l1/NativeArbitrum.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {L1GatewayRouter} from \"../interfaces/arbitrum.sol\";\nimport {BridgeImplBase} from \"../../BridgeImplBase.sol\";\nimport {NATIVE_ARBITRUM} from \"../../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Native Arbitrum-Route Implementation\n * @notice Route implementation with functions to bridge ERC20 via NativeArbitrum-Bridge\n * @notice Called via SocketGateway if the routeId in the request maps to the routeId of NativeArbitrum-Implementation\n * @notice This is used when transferring from ethereum chain to arbitrum via their native bridge.\n * @notice Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * @notice RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ncontract NativeArbitrumImpl is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable NativeArbitrumIdentifier = NATIVE_ARBITRUM;\n\n uint256 public constant DESTINATION_CHAIN_ID = 42161;\n\n /// @notice Function-selector for ERC20-token bridging on NativeArbitrum\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4\n public immutable NATIVE_ARBITRUM_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(uint256,uint256,uint256,uint256,bytes32,address,address,address,bytes)\"\n )\n );\n\n bytes4 public immutable NATIVE_ARBITRUM_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(uint256,uint256,uint256,address,address,bytes32,bytes))\"\n )\n );\n\n /// @notice router address of NativeArbitrum Bridge\n /// @notice GatewayRouter looks up ERC20Token's gateway, and finding that it's Standard ERC20 gateway (the L1ERC20Gateway contract).\n address public immutable router;\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n /// @dev ensure router-address are set properly for the chainId in which the contract is being deployed\n constructor(\n address _router,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n router = _router;\n }\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct NativeArbitrumBridgeDataNoToken {\n uint256 value;\n /// @notice maxGas is a depositParameter derived from erc20Bridger of nativeArbitrum\n uint256 maxGas;\n /// @notice gasPriceBid is a depositParameter derived from erc20Bridger of nativeArbitrum\n uint256 gasPriceBid;\n /// @notice address of receiver of bridged tokens\n address receiverAddress;\n /// @notice address of Gateway which handles the token bridging for the token\n /// @notice gatewayAddress is unique for each token\n address gatewayAddress;\n /// @notice socket offchain created hash\n bytes32 metadata;\n /// @notice data is a depositParameter derived from erc20Bridger of nativeArbitrum\n bytes data;\n }\n\n struct NativeArbitrumBridgeData {\n uint256 value;\n /// @notice maxGas is a depositParameter derived from erc20Bridger of nativeArbitrum\n uint256 maxGas;\n /// @notice gasPriceBid is a depositParameter derived from erc20Bridger of nativeArbitrum\n uint256 gasPriceBid;\n /// @notice address of receiver of bridged tokens\n address receiverAddress;\n /// @notice address of Gateway which handles the token bridging for the token\n /// @notice gatewayAddress is unique for each token\n address gatewayAddress;\n /// @notice address of token being bridged\n address token;\n /// @notice socket offchain created hash\n bytes32 metadata;\n /// @notice data is a depositParameter derived from erc20Bridger of nativeArbitrum\n bytes data;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in NativeArbitrumBridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for NativeArbitrumBridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n NativeArbitrumBridgeData memory nativeArbitrumBridgeData = abi.decode(\n bridgeData,\n (NativeArbitrumBridgeData)\n );\n ERC20(nativeArbitrumBridgeData.token).safeApprove(\n nativeArbitrumBridgeData.gatewayAddress,\n amount\n );\n\n L1GatewayRouter(router).outboundTransfer{\n value: nativeArbitrumBridgeData.value\n }(\n nativeArbitrumBridgeData.token,\n nativeArbitrumBridgeData.receiverAddress,\n amount,\n nativeArbitrumBridgeData.maxGas,\n nativeArbitrumBridgeData.gasPriceBid,\n nativeArbitrumBridgeData.data\n );\n\n emit SocketBridge(\n amount,\n nativeArbitrumBridgeData.token,\n DESTINATION_CHAIN_ID,\n NativeArbitrumIdentifier,\n msg.sender,\n nativeArbitrumBridgeData.receiverAddress,\n nativeArbitrumBridgeData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in NativeArbitrumBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param nativeArbitrumBridgeData encoded data for NativeArbitrumBridge\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n NativeArbitrumBridgeDataNoToken calldata nativeArbitrumBridgeData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n ERC20(token).safeApprove(\n nativeArbitrumBridgeData.gatewayAddress,\n bridgeAmount\n );\n\n L1GatewayRouter(router).outboundTransfer{\n value: nativeArbitrumBridgeData.value\n }(\n token,\n nativeArbitrumBridgeData.receiverAddress,\n bridgeAmount,\n nativeArbitrumBridgeData.maxGas,\n nativeArbitrumBridgeData.gasPriceBid,\n nativeArbitrumBridgeData.data\n );\n\n emit SocketBridge(\n bridgeAmount,\n token,\n DESTINATION_CHAIN_ID,\n NativeArbitrumIdentifier,\n msg.sender,\n nativeArbitrumBridgeData.receiverAddress,\n nativeArbitrumBridgeData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via NativeArbitrum-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount being bridged\n * @param value value\n * @param maxGas maxGas is a depositParameter derived from erc20Bridger of nativeArbitrum\n * @param gasPriceBid gasPriceBid is a depositParameter derived from erc20Bridger of nativeArbitrum\n * @param receiverAddress address of receiver of bridged tokens\n * @param token address of token being bridged\n * @param gatewayAddress address of Gateway which handles the token bridging for the token, gatewayAddress is unique for each token\n * @param data data is a depositParameter derived from erc20Bridger of nativeArbitrum\n */\n function bridgeERC20To(\n uint256 amount,\n uint256 value,\n uint256 maxGas,\n uint256 gasPriceBid,\n bytes32 metadata,\n address receiverAddress,\n address token,\n address gatewayAddress,\n bytes memory data\n ) external payable {\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n tokenInstance.safeApprove(gatewayAddress, amount);\n\n L1GatewayRouter(router).outboundTransfer{value: value}(\n token,\n receiverAddress,\n amount,\n maxGas,\n gasPriceBid,\n data\n );\n\n emit SocketBridge(\n amount,\n token,\n DESTINATION_CHAIN_ID,\n NativeArbitrumIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n" + }, + "src/bridges/across/Across.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport \"./interfaces/across.sol\";\nimport \"../BridgeImplBase.sol\";\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {ACROSS} from \"../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Across-Route Implementation\n * @notice Route implementation with functions to bridge ERC20 and Native via Across-Bridge\n * Called via SocketGateway if the routeId in the request maps to the routeId of AcrossImplementation\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ncontract AcrossImpl is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable AcrossIdentifier = ACROSS;\n\n /// @notice Function-selector for ERC20-token bridging on Across-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4 public immutable ACROSS_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(uint256,uint256,bytes32,address,address,uint32,uint64)\"\n )\n );\n\n /// @notice Function-selector for Native bridging on Across-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge Native tokens\n bytes4 public immutable ACROSS_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeNativeTo(uint256,uint256,bytes32,address,uint32,uint64)\"\n )\n );\n\n bytes4 public immutable ACROSS_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(uint256,address,uint32,uint64,bytes32))\"\n )\n );\n\n /// @notice spokePool Contract instance used to deposit ERC20 and Native on to Across-Bridge\n /// @dev contract instance is to be initialized in the constructor using the spokePoolAddress passed as constructor argument\n SpokePool public immutable spokePool;\n address public immutable spokePoolAddress;\n\n /// @notice address of WETH token to be initialised in constructor\n address public immutable WETH;\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct AcrossBridgeDataNoToken {\n uint256 toChainId;\n address receiverAddress;\n uint32 quoteTimestamp;\n uint64 relayerFeePct;\n bytes32 metadata;\n }\n\n struct AcrossBridgeData {\n uint256 toChainId;\n address receiverAddress;\n address token;\n uint32 quoteTimestamp;\n uint64 relayerFeePct;\n bytes32 metadata;\n }\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n /// @dev ensure spokepool, weth-address are set properly for the chainId in which the contract is being deployed\n constructor(\n address _spokePool,\n address _wethAddress,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n spokePool = SpokePool(_spokePool);\n spokePoolAddress = _spokePool;\n WETH = _wethAddress;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in AcrossBridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for AcrossBridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n AcrossBridgeData memory acrossBridgeData = abi.decode(\n bridgeData,\n (AcrossBridgeData)\n );\n\n if (acrossBridgeData.token == NATIVE_TOKEN_ADDRESS) {\n spokePool.deposit{value: amount}(\n acrossBridgeData.receiverAddress,\n WETH,\n amount,\n acrossBridgeData.toChainId,\n acrossBridgeData.relayerFeePct,\n acrossBridgeData.quoteTimestamp\n );\n } else {\n spokePool.deposit(\n acrossBridgeData.receiverAddress,\n acrossBridgeData.token,\n amount,\n acrossBridgeData.toChainId,\n acrossBridgeData.relayerFeePct,\n acrossBridgeData.quoteTimestamp\n );\n }\n\n emit SocketBridge(\n amount,\n acrossBridgeData.token,\n acrossBridgeData.toChainId,\n AcrossIdentifier,\n msg.sender,\n acrossBridgeData.receiverAddress,\n acrossBridgeData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in AcrossBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param acrossBridgeData encoded data for AcrossBridge\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n AcrossBridgeDataNoToken calldata acrossBridgeData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n if (token == NATIVE_TOKEN_ADDRESS) {\n spokePool.deposit{value: bridgeAmount}(\n acrossBridgeData.receiverAddress,\n WETH,\n bridgeAmount,\n acrossBridgeData.toChainId,\n acrossBridgeData.relayerFeePct,\n acrossBridgeData.quoteTimestamp\n );\n } else {\n spokePool.deposit(\n acrossBridgeData.receiverAddress,\n token,\n bridgeAmount,\n acrossBridgeData.toChainId,\n acrossBridgeData.relayerFeePct,\n acrossBridgeData.quoteTimestamp\n );\n }\n\n emit SocketBridge(\n bridgeAmount,\n token,\n acrossBridgeData.toChainId,\n AcrossIdentifier,\n msg.sender,\n acrossBridgeData.receiverAddress,\n acrossBridgeData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via Across-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount being bridged\n * @param toChainId destination ChainId\n * @param receiverAddress address of receiver of bridged tokens\n * @param token address of token being bridged\n * @param quoteTimestamp timestamp for quote and this is to be used by Across-Bridge contract\n * @param relayerFeePct feePct that will be relayed by the Bridge to the relayer\n */\n function bridgeERC20To(\n uint256 amount,\n uint256 toChainId,\n bytes32 metadata,\n address receiverAddress,\n address token,\n uint32 quoteTimestamp,\n uint64 relayerFeePct\n ) external payable {\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n spokePool.deposit(\n receiverAddress,\n address(token),\n amount,\n toChainId,\n relayerFeePct,\n quoteTimestamp\n );\n\n emit SocketBridge(\n amount,\n token,\n toChainId,\n AcrossIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n\n /**\n * @notice function to handle Native bridging to receipent via Across-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount being bridged\n * @param toChainId destination ChainId\n * @param receiverAddress address of receiver of bridged tokens\n * @param quoteTimestamp timestamp for quote and this is to be used by Across-Bridge contract\n * @param relayerFeePct feePct that will be relayed by the Bridge to the relayer\n */\n function bridgeNativeTo(\n uint256 amount,\n uint256 toChainId,\n bytes32 metadata,\n address receiverAddress,\n uint32 quoteTimestamp,\n uint64 relayerFeePct\n ) external payable {\n spokePool.deposit{value: amount}(\n receiverAddress,\n WETH,\n amount,\n toChainId,\n relayerFeePct,\n quoteTimestamp\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n toChainId,\n AcrossIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n" + }, + "src/bridges/cbridge/interfaces/ICelerStorageWrapper.sol": { + "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity >=0.8.0;\n\n/**\n * @title Celer-StorageWrapper interface\n * @notice Interface to handle storageMappings used while bridging ERC20 and native on CelerBridge\n * @dev all functions ehich mutate the storage are restricted to Owner of SocketGateway\n * @author Socket dot tech.\n */\ninterface ICelerStorageWrapper {\n /**\n * @notice function to store the transferId and message-sender of a bridging activity\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in CelerBridgeData struct\n * @param transferId transferId generated during the bridging of ERC20 or native on CelerBridge\n * @param transferIdAddress message sender who is making the bridging on CelerBridge\n */\n function setAddressForTransferId(\n bytes32 transferId,\n address transferIdAddress\n ) external;\n\n /**\n * @notice function to store the transferId and message-sender of a bridging activity\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in CelerBridgeData struct\n * @param transferId transferId generated during the bridging of ERC20 or native on CelerBridge\n */\n function deleteTransferId(bytes32 transferId) external;\n\n /**\n * @notice function to lookup the address mapped to the transferId\n * @param transferId transferId generated during the bridging of ERC20 or native on CelerBridge\n * @return address of account mapped to transferId\n */\n function getAddressFromTransferId(\n bytes32 transferId\n ) external view returns (address);\n}\n" + }, + "src/bridges/optimism/interfaces/optimism.sol": { + "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity >=0.8.0;\n\ninterface L1StandardBridge {\n /**\n * @dev Performs the logic for deposits by storing the ETH and informing the L2 ETH Gateway of\n * the deposit.\n * @param _to Account to give the deposit to on L2.\n * @param _l2Gas Gas limit required to complete the deposit on L2.\n * @param _data Optional data to forward to L2. This data is provided\n * solely as a convenience for external contracts. Aside from enforcing a maximum\n * length, these contracts provide no guarantees about its content.\n */\n function depositETHTo(\n address _to,\n uint32 _l2Gas,\n bytes calldata _data\n ) external payable;\n\n /**\n * @dev deposit an amount of ERC20 to a recipient's balance on L2.\n * @param _l1Token Address of the L1 ERC20 we are depositing\n * @param _l2Token Address of the L1 respective L2 ERC20\n * @param _to L2 address to credit the withdrawal to.\n * @param _amount Amount of the ERC20 to deposit.\n * @param _l2Gas Gas limit required to complete the deposit on L2.\n * @param _data Optional data to forward to L2. This data is provided\n * solely as a convenience for external contracts. Aside from enforcing a maximum\n * length, these contracts provide no guarantees about its content.\n */\n function depositERC20To(\n address _l1Token,\n address _l2Token,\n address _to,\n uint256 _amount,\n uint32 _l2Gas,\n bytes calldata _data\n ) external;\n}\n\ninterface OldL1TokenGateway {\n /**\n * @dev Transfer SNX to L2 First, moves the SNX into the deposit escrow\n *\n * @param _to Account to give the deposit to on L2\n * @param _amount Amount of the ERC20 to deposit.\n */\n function depositTo(address _to, uint256 _amount) external;\n\n /**\n * @dev Transfer SNX to L2 First, moves the SNX into the deposit escrow\n *\n * @param currencyKey currencyKey for the SynthToken\n * @param destination Account to give the deposit to on L2\n * @param amount Amount of the ERC20 to deposit.\n */\n function initiateSynthTransfer(\n bytes32 currencyKey,\n address destination,\n uint256 amount\n ) external;\n}\n" + }, + "src/bridges/arbitrum/interfaces/arbitrum.sol": { + "content": "// SPDX-License-Identifier: Apache-2.0\n\n/*\n * Copyright 2021, Offchain Labs, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npragma solidity >=0.8.0;\n\n/**\n * @title L1gatewayRouter for native-arbitrum\n */\ninterface L1GatewayRouter {\n /**\n * @notice outbound function to bridge ERC20 via NativeArbitrum-Bridge\n * @param _token address of token being bridged via GatewayRouter\n * @param _to recipient of the token on arbitrum chain\n * @param _amount amount of ERC20 token being bridged\n * @param _maxGas a depositParameter for bridging the token\n * @param _gasPriceBid a depositParameter for bridging the token\n * @param _data a depositParameter for bridging the token\n * @return calldata returns the output of transactioncall made on gatewayRouter\n */\n function outboundTransfer(\n address _token,\n address _to,\n uint256 _amount,\n uint256 _maxGas,\n uint256 _gasPriceBid,\n bytes calldata _data\n ) external payable returns (bytes calldata);\n}\n" + }, + "src/deployFactory/DisabledSocketRoute.sol": { + "content": "//SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {ISocketGateway} from \"../interfaces/ISocketGateway.sol\";\nimport {OnlySocketGatewayOwner} from \"../errors/SocketErrors.sol\";\n\ncontract DisabledSocketRoute {\n using SafeTransferLib for ERC20;\n\n /// @notice immutable variable to store the socketGateway address\n address public immutable socketGateway;\n error RouteDisabled();\n\n /**\n * @notice Construct the base for all BridgeImplementations.\n * @param _socketGateway Socketgateway address, an immutable variable to set.\n */\n constructor(address _socketGateway) {\n socketGateway = _socketGateway;\n }\n\n /// @notice Implementing contract needs to make use of the modifier where restricted access is to be used\n modifier isSocketGatewayOwner() {\n if (msg.sender != ISocketGateway(socketGateway).owner()) {\n revert OnlySocketGatewayOwner();\n }\n _;\n }\n\n /**\n * @notice function to rescue the ERC20 tokens in the bridge Implementation contract\n * @notice this is a function restricted to Owner of SocketGateway only\n * @param token address of ERC20 token being rescued\n * @param userAddress receipient address to which ERC20 tokens will be rescued to\n * @param amount amount of ERC20 tokens being rescued\n */\n function rescueFunds(\n address token,\n address userAddress,\n uint256 amount\n ) external isSocketGatewayOwner {\n ERC20(token).safeTransfer(userAddress, amount);\n }\n\n /**\n * @notice function to rescue the native-balance in the bridge Implementation contract\n * @notice this is a function restricted to Owner of SocketGateway only\n * @param userAddress receipient address to which native-balance will be rescued to\n * @param amount amount of native balance tokens being rescued\n */\n function rescueEther(\n address payable userAddress,\n uint256 amount\n ) external isSocketGatewayOwner {\n userAddress.transfer(amount);\n }\n\n /**\n * @notice Handle route function calls gracefully.\n */\n fallback() external payable {\n revert RouteDisabled();\n }\n\n /**\n * @notice Support receiving ether to handle refunds etc.\n */\n receive() external payable {}\n}\n" + }, + "src/bridges/polygon/NativePolygon.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport \"./interfaces/polygon.sol\";\nimport {BridgeImplBase} from \"../BridgeImplBase.sol\";\nimport {NATIVE_POLYGON} from \"../../static/RouteIdentifiers.sol\";\n\n/**\n * @title NativePolygon-Route Implementation\n * @notice This is the L1 implementation, so this is used when transferring from ethereum to polygon via their native bridge.\n * @author Socket dot tech.\n */\ncontract NativePolygonImpl is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable NativePolyonIdentifier = NATIVE_POLYGON;\n\n /// @notice destination-chain-Id for this router is always arbitrum\n uint256 public constant DESTINATION_CHAIN_ID = 137;\n\n /// @notice Function-selector for ERC20-token bridging on NativePolygon-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4\n public immutable NATIVE_POLYGON_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(keccak256(\"bridgeERC20To(uint256,bytes32,address,address)\"));\n\n /// @notice Function-selector for Native bridging on NativePolygon-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge Native tokens\n bytes4\n public immutable NATIVE_POLYGON_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(keccak256(\"bridgeNativeTo(uint256,bytes32,address)\"));\n\n bytes4 public immutable NATIVE_POLYGON_SWAP_BRIDGE_SELECTOR =\n bytes4(keccak256(\"swapAndBridge(uint32,address,bytes32,bytes)\"));\n\n /// @notice root chain manager proxy on the ethereum chain\n /// @dev to be initialised in the constructor\n IRootChainManager public immutable rootChainManagerProxy;\n\n /// @notice ERC20 Predicate proxy on the ethereum chain\n /// @dev to be initialised in the constructor\n address public immutable erc20PredicateProxy;\n\n /**\n * // @notice We set all the required addresses in the constructor while deploying the contract.\n * // These will be constant addresses.\n * // @dev Please use the Proxy addresses and not the implementation addresses while setting these\n * // @param _rootChainManagerProxy address of the root chain manager proxy on the ethereum chain\n * // @param _erc20PredicateProxy address of the ERC20 Predicate proxy on the ethereum chain.\n * // @param _socketGateway address of the socketGateway contract that calls this contract\n */\n constructor(\n address _rootChainManagerProxy,\n address _erc20PredicateProxy,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n rootChainManagerProxy = IRootChainManager(_rootChainManagerProxy);\n erc20PredicateProxy = _erc20PredicateProxy;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in NativePolygon-BridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for NativePolygon-Bridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n (address token, address receiverAddress, bytes32 metadata) = abi.decode(\n bridgeData,\n (address, address, bytes32)\n );\n\n if (token == NATIVE_TOKEN_ADDRESS) {\n IRootChainManager(rootChainManagerProxy).depositEtherFor{\n value: amount\n }(receiverAddress);\n } else {\n ERC20(token).safeApprove(erc20PredicateProxy, amount);\n\n // deposit into rootchain manager\n IRootChainManager(rootChainManagerProxy).depositFor(\n receiverAddress,\n token,\n abi.encodePacked(amount)\n );\n }\n\n emit SocketBridge(\n amount,\n token,\n DESTINATION_CHAIN_ID,\n NativePolyonIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in NativePolygon-BridgeData struct\n * @param swapId routeId for the swapImpl\n * @param receiverAddress address of the receiver\n * @param swapData encoded data for swap\n */\n function swapAndBridge(\n uint32 swapId,\n address receiverAddress,\n bytes32 metadata,\n bytes calldata swapData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n\n if (token == NATIVE_TOKEN_ADDRESS) {\n IRootChainManager(rootChainManagerProxy).depositEtherFor{\n value: bridgeAmount\n }(receiverAddress);\n } else {\n ERC20(token).safeApprove(erc20PredicateProxy, bridgeAmount);\n\n // deposit into rootchain manager\n IRootChainManager(rootChainManagerProxy).depositFor(\n receiverAddress,\n token,\n abi.encodePacked(bridgeAmount)\n );\n }\n\n emit SocketBridge(\n bridgeAmount,\n token,\n DESTINATION_CHAIN_ID,\n NativePolyonIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via NativePolygon-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount of tokens being bridged\n * @param receiverAddress recipient address\n * @param token address of token being bridged\n */\n function bridgeERC20To(\n uint256 amount,\n bytes32 metadata,\n address receiverAddress,\n address token\n ) external payable {\n ERC20 tokenInstance = ERC20(token);\n\n // set allowance for erc20 predicate\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n tokenInstance.safeApprove(erc20PredicateProxy, amount);\n\n // deposit into rootchain manager\n rootChainManagerProxy.depositFor(\n receiverAddress,\n token,\n abi.encodePacked(amount)\n );\n\n emit SocketBridge(\n amount,\n token,\n DESTINATION_CHAIN_ID,\n NativePolyonIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n\n /**\n * @notice function to handle Native bridging to receipent via NativePolygon-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount of tokens being bridged\n * @param receiverAddress recipient address\n */\n function bridgeNativeTo(\n uint256 amount,\n bytes32 metadata,\n address receiverAddress\n ) external payable {\n rootChainManagerProxy.depositEtherFor{value: amount}(receiverAddress);\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n DESTINATION_CHAIN_ID,\n NativePolyonIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n" + }, + "src/bridges/stargate/l1/Stargate.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport \"../interfaces/stargate.sol\";\nimport {BridgeImplBase} from \"../../BridgeImplBase.sol\";\nimport {STARGATE} from \"../../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Stargate-L1-Route Implementation\n * @notice Route implementation with functions to bridge ERC20 and Native via Stargate-L1-Bridge\n * Called via SocketGateway if the routeId in the request maps to the routeId of Stargate-L1-Implementation\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ncontract StargateImplL1 is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable StargateIdentifier = STARGATE;\n\n /// @notice Function-selector for ERC20-token bridging on Stargate-L1-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4\n public immutable STARGATE_L1_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(address,address,address,uint256,uint256,(uint256,uint256,uint256,uint256,bytes32,bytes,uint16))\"\n )\n );\n\n /// @notice Function-selector for Native bridging on Stargate-L1-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge Native tokens\n bytes4\n public immutable STARGATE_L1_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeNativeTo(address,address,uint16,uint256,uint256,uint256,bytes32)\"\n )\n );\n\n bytes4 public immutable STARGATE_L1_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(address,address,uint16,uint256,uint256,uint256,uint256,uint256,uint256,bytes32,bytes))\"\n )\n );\n\n /// @notice Stargate Router to bridge ERC20 tokens\n IBridgeStargate public immutable router;\n\n /// @notice Stargate Router to bridge native tokens\n IBridgeStargate public immutable routerETH;\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n /// @dev ensure router, routerEth are set properly for the chainId in which the contract is being deployed\n constructor(\n address _router,\n address _routerEth,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n router = IBridgeStargate(_router);\n routerETH = IBridgeStargate(_routerEth);\n }\n\n struct StargateBridgeExtraData {\n uint256 srcPoolId;\n uint256 dstPoolId;\n uint256 destinationGasLimit;\n uint256 minReceivedAmt;\n bytes32 metadata;\n bytes destinationPayload;\n uint16 stargateDstChainId; // stargate defines chain id in its way\n }\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct StargateBridgeDataNoToken {\n address receiverAddress;\n address senderAddress;\n uint16 stargateDstChainId; // stargate defines chain id in its way\n uint256 value;\n // a unique identifier that is uses to dedup transfers\n // this value is the a timestamp sent from frontend, but in theory can be any unique number\n uint256 srcPoolId;\n uint256 dstPoolId;\n uint256 minReceivedAmt; // defines the slippage, the min qty you would accept on the destination\n uint256 optionalValue;\n uint256 destinationGasLimit;\n bytes32 metadata;\n bytes destinationPayload;\n }\n\n struct StargateBridgeData {\n address token;\n address receiverAddress;\n address senderAddress;\n uint16 stargateDstChainId; // stargate defines chain id in its way\n uint256 value;\n // a unique identifier that is uses to dedup transfers\n // this value is the a timestamp sent from frontend, but in theory can be any unique number\n uint256 srcPoolId;\n uint256 dstPoolId;\n uint256 minReceivedAmt; // defines the slippage, the min qty you would accept on the destination\n uint256 optionalValue;\n uint256 destinationGasLimit;\n bytes32 metadata;\n bytes destinationPayload;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in Stargate-BridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for Stargate-L1-Bridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n StargateBridgeData memory stargateBridgeData = abi.decode(\n bridgeData,\n (StargateBridgeData)\n );\n\n if (stargateBridgeData.token == NATIVE_TOKEN_ADDRESS) {\n // perform bridging\n routerETH.swapETH{value: amount + stargateBridgeData.optionalValue}(\n stargateBridgeData.stargateDstChainId,\n payable(stargateBridgeData.senderAddress),\n abi.encodePacked(stargateBridgeData.receiverAddress),\n amount,\n stargateBridgeData.minReceivedAmt\n );\n } else {\n ERC20(stargateBridgeData.token).safeApprove(\n address(router),\n amount\n );\n {\n router.swap{value: stargateBridgeData.value}(\n stargateBridgeData.stargateDstChainId,\n stargateBridgeData.srcPoolId,\n stargateBridgeData.dstPoolId,\n payable(stargateBridgeData.senderAddress), // default to refund to main contract\n amount,\n stargateBridgeData.minReceivedAmt,\n IBridgeStargate.lzTxObj(\n stargateBridgeData.destinationGasLimit,\n 0, // zero amount since this is a ERC20 bridging\n \"0x\" //empty data since this is for only ERC20\n ),\n abi.encodePacked(stargateBridgeData.receiverAddress),\n stargateBridgeData.destinationPayload\n );\n }\n }\n\n emit SocketBridge(\n amount,\n stargateBridgeData.token,\n stargateBridgeData.stargateDstChainId,\n StargateIdentifier,\n msg.sender,\n stargateBridgeData.receiverAddress,\n stargateBridgeData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in Stargate-BridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param stargateBridgeData encoded data for StargateBridgeData\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n StargateBridgeDataNoToken calldata stargateBridgeData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n\n if (token == NATIVE_TOKEN_ADDRESS) {\n // perform bridging\n routerETH.swapETH{\n value: bridgeAmount + stargateBridgeData.optionalValue\n }(\n stargateBridgeData.stargateDstChainId,\n payable(stargateBridgeData.senderAddress),\n abi.encodePacked(stargateBridgeData.receiverAddress),\n bridgeAmount,\n stargateBridgeData.minReceivedAmt\n );\n } else {\n ERC20(token).safeApprove(address(router), bridgeAmount);\n {\n router.swap{value: stargateBridgeData.value}(\n stargateBridgeData.stargateDstChainId,\n stargateBridgeData.srcPoolId,\n stargateBridgeData.dstPoolId,\n payable(stargateBridgeData.senderAddress), // default to refund to main contract\n bridgeAmount,\n stargateBridgeData.minReceivedAmt,\n IBridgeStargate.lzTxObj(\n stargateBridgeData.destinationGasLimit,\n 0, // zero amount since this is a ERC20 bridging\n \"0x\" //empty data since this is for only ERC20\n ),\n abi.encodePacked(stargateBridgeData.receiverAddress),\n stargateBridgeData.destinationPayload\n );\n }\n }\n\n emit SocketBridge(\n bridgeAmount,\n token,\n stargateBridgeData.stargateDstChainId,\n StargateIdentifier,\n msg.sender,\n stargateBridgeData.receiverAddress,\n stargateBridgeData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via Stargate-L1-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param token address of token being bridged\n * @param senderAddress address of sender\n * @param receiverAddress address of recipient\n * @param amount amount of token being bridge\n * @param value value\n * @param stargateBridgeExtraData stargate bridge extradata\n */\n function bridgeERC20To(\n address token,\n address senderAddress,\n address receiverAddress,\n uint256 amount,\n uint256 value,\n StargateBridgeExtraData calldata stargateBridgeExtraData\n ) external payable {\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n tokenInstance.safeApprove(address(router), amount);\n {\n router.swap{value: value}(\n stargateBridgeExtraData.stargateDstChainId,\n stargateBridgeExtraData.srcPoolId,\n stargateBridgeExtraData.dstPoolId,\n payable(senderAddress), // default to refund to main contract\n amount,\n stargateBridgeExtraData.minReceivedAmt,\n IBridgeStargate.lzTxObj(\n stargateBridgeExtraData.destinationGasLimit,\n 0, // zero amount since this is a ERC20 bridging\n \"0x\" //empty data since this is for only ERC20\n ),\n abi.encodePacked(receiverAddress),\n stargateBridgeExtraData.destinationPayload\n );\n }\n\n emit SocketBridge(\n amount,\n token,\n stargateBridgeExtraData.stargateDstChainId,\n StargateIdentifier,\n msg.sender,\n receiverAddress,\n stargateBridgeExtraData.metadata\n );\n }\n\n /**\n * @notice function to handle Native bridging to receipent via Stargate-L1-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param receiverAddress address of receipient\n * @param senderAddress address of sender\n * @param stargateDstChainId stargate defines chain id in its way\n * @param amount amount of token being bridge\n * @param minReceivedAmt defines the slippage, the min qty you would accept on the destination\n * @param optionalValue optionalValue Native amount\n */\n function bridgeNativeTo(\n address receiverAddress,\n address senderAddress,\n uint16 stargateDstChainId,\n uint256 amount,\n uint256 minReceivedAmt,\n uint256 optionalValue,\n bytes32 metadata\n ) external payable {\n // perform bridging\n routerETH.swapETH{value: amount + optionalValue}(\n stargateDstChainId,\n payable(senderAddress),\n abi.encodePacked(receiverAddress),\n amount,\n minReceivedAmt\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n stargateDstChainId,\n StargateIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n" + }, + "src/bridges/hop/l2/HopImplL2.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport \"../interfaces/amm.sol\";\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {BridgeImplBase} from \"../../BridgeImplBase.sol\";\nimport {HOP} from \"../../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Hop-L2 Route Implementation\n * @notice This is the L2 implementation, so this is used when transferring from l2 to supported l2s\n * Called via SocketGateway if the routeId in the request maps to the routeId of HopL2-Implementation\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ncontract HopImplL2 is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable HopIdentifier = HOP;\n\n /// @notice Function-selector for ERC20-token bridging on Hop-L2-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4 public immutable HOP_L2_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(address,address,address,uint256,uint256,(uint256,uint256,uint256,uint256,uint256,bytes32))\"\n )\n );\n\n /// @notice Function-selector for Native bridging on Hop-L2-Route\n /// @dev This function selector is to be used while building transaction-data to bridge Native tokens\n bytes4 public immutable HOP_L2_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeNativeTo(address,address,uint256,uint256,uint256,uint256,uint256,uint256,uint256,bytes32)\"\n )\n );\n\n bytes4 public immutable HOP_L2_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes32))\"\n )\n );\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n constructor(\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {}\n\n /// @notice Struct to be used as a input parameter for Bridging tokens via Hop-L2-route\n /// @dev while building transactionData,values should be set in this sequence of properties in this struct\n struct HopBridgeRequestData {\n // fees passed to relayer\n uint256 bonderFee;\n // The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.\n uint256 amountOutMin;\n // The deadline for swapping in the destination AMM market. 0 if no swap is intended.\n uint256 deadline;\n // Minimum amount expected to be received or bridged to destination\n uint256 amountOutMinDestination;\n // deadline for bridging to destination\n uint256 deadlineDestination;\n // socket offchain created hash\n bytes32 metadata;\n }\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct HopBridgeDataNoToken {\n // The address receiving funds at the destination\n address receiverAddress;\n // AMM address of Hop on L2\n address hopAMM;\n // The chainId of the destination chain\n uint256 toChainId;\n // fees passed to relayer\n uint256 bonderFee;\n // The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.\n uint256 amountOutMin;\n // The deadline for swapping in the destination AMM market. 0 if no swap is intended.\n uint256 deadline;\n // Minimum amount expected to be received or bridged to destination\n uint256 amountOutMinDestination;\n // deadline for bridging to destination\n uint256 deadlineDestination;\n // socket offchain created hash\n bytes32 metadata;\n }\n\n struct HopBridgeData {\n /// @notice address of token being bridged\n address token;\n // The address receiving funds at the destination\n address receiverAddress;\n // AMM address of Hop on L2\n address hopAMM;\n // The chainId of the destination chain\n uint256 toChainId;\n // fees passed to relayer\n uint256 bonderFee;\n // The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.\n uint256 amountOutMin;\n // The deadline for swapping in the destination AMM market. 0 if no swap is intended.\n uint256 deadline;\n // Minimum amount expected to be received or bridged to destination\n uint256 amountOutMinDestination;\n // deadline for bridging to destination\n uint256 deadlineDestination;\n // socket offchain created hash\n bytes32 metadata;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in HopBridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for Hop-L2-Bridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n HopBridgeData memory hopData = abi.decode(bridgeData, (HopBridgeData));\n\n if (hopData.token == NATIVE_TOKEN_ADDRESS) {\n HopAMM(hopData.hopAMM).swapAndSend{value: amount}(\n hopData.toChainId,\n hopData.receiverAddress,\n amount,\n hopData.bonderFee,\n hopData.amountOutMin,\n hopData.deadline,\n hopData.amountOutMinDestination,\n hopData.deadlineDestination\n );\n } else {\n // perform bridging\n HopAMM(hopData.hopAMM).swapAndSend(\n hopData.toChainId,\n hopData.receiverAddress,\n amount,\n hopData.bonderFee,\n hopData.amountOutMin,\n hopData.deadline,\n hopData.amountOutMinDestination,\n hopData.deadlineDestination\n );\n }\n\n emit SocketBridge(\n amount,\n hopData.token,\n hopData.toChainId,\n HopIdentifier,\n msg.sender,\n hopData.receiverAddress,\n hopData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in HopBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param hopData encoded data for HopData\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n HopBridgeDataNoToken calldata hopData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n\n if (token == NATIVE_TOKEN_ADDRESS) {\n HopAMM(hopData.hopAMM).swapAndSend{value: bridgeAmount}(\n hopData.toChainId,\n hopData.receiverAddress,\n bridgeAmount,\n hopData.bonderFee,\n hopData.amountOutMin,\n hopData.deadline,\n hopData.amountOutMinDestination,\n hopData.deadlineDestination\n );\n } else {\n // perform bridging\n HopAMM(hopData.hopAMM).swapAndSend(\n hopData.toChainId,\n hopData.receiverAddress,\n bridgeAmount,\n hopData.bonderFee,\n hopData.amountOutMin,\n hopData.deadline,\n hopData.amountOutMinDestination,\n hopData.deadlineDestination\n );\n }\n\n emit SocketBridge(\n bridgeAmount,\n token,\n hopData.toChainId,\n HopIdentifier,\n msg.sender,\n hopData.receiverAddress,\n hopData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via Hop-L2-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param receiverAddress The address receiving funds at the destination\n * @param token token being bridged\n * @param hopAMM AMM address of Hop on L2\n * @param amount The amount being bridged\n * @param toChainId The chainId of the destination chain\n * @param hopBridgeRequestData extraData for Bridging across Hop-L2\n */\n function bridgeERC20To(\n address receiverAddress,\n address token,\n address hopAMM,\n uint256 amount,\n uint256 toChainId,\n HopBridgeRequestData calldata hopBridgeRequestData\n ) external payable {\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n\n HopAMM(hopAMM).swapAndSend(\n toChainId,\n receiverAddress,\n amount,\n hopBridgeRequestData.bonderFee,\n hopBridgeRequestData.amountOutMin,\n hopBridgeRequestData.deadline,\n hopBridgeRequestData.amountOutMinDestination,\n hopBridgeRequestData.deadlineDestination\n );\n\n emit SocketBridge(\n amount,\n token,\n toChainId,\n HopIdentifier,\n msg.sender,\n receiverAddress,\n hopBridgeRequestData.metadata\n );\n }\n\n /**\n * @notice function to handle Native bridging to receipent via Hop-L2-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param receiverAddress The address receiving funds at the destination\n * @param hopAMM AMM address of Hop on L2\n * @param amount The amount being bridged\n * @param toChainId The chainId of the destination chain\n * @param bonderFee fees passed to relayer\n * @param amountOutMin The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.\n * @param deadline The deadline for swapping in the destination AMM market. 0 if no swap is intended.\n * @param amountOutMinDestination Minimum amount expected to be received or bridged to destination\n * @param deadlineDestination deadline for bridging to destination\n */\n function bridgeNativeTo(\n address receiverAddress,\n address hopAMM,\n uint256 amount,\n uint256 toChainId,\n uint256 bonderFee,\n uint256 amountOutMin,\n uint256 deadline,\n uint256 amountOutMinDestination,\n uint256 deadlineDestination,\n bytes32 metadata\n ) external payable {\n // token address might not be indication thats why passed through extraData\n // perform bridging\n HopAMM(hopAMM).swapAndSend{value: amount}(\n toChainId,\n receiverAddress,\n amount,\n bonderFee,\n amountOutMin,\n deadline,\n amountOutMinDestination,\n deadlineDestination\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n toChainId,\n HopIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n" + }, + "src/bridges/anyswap-router-v4/l2/Anyswap.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {BridgeImplBase} from \"../../BridgeImplBase.sol\";\nimport {ANYSWAP} from \"../../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Anyswap-V4-Route L1 Implementation\n * @notice Route implementation with functions to bridge ERC20 via Anyswap-Bridge\n * Called via SocketGateway if the routeId in the request maps to the routeId of AnyswapImplementation\n * This is the L2 implementation, so this is used when transferring from l2.\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ninterface AnyswapV4Router {\n function anySwapOutUnderlying(\n address token,\n address to,\n uint256 amount,\n uint256 toChainID\n ) external;\n}\n\ncontract AnyswapL2Impl is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable AnyswapIdentifier = ANYSWAP;\n\n /// @notice Function-selector for ERC20-token bridging on Anyswap-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4 public immutable ANYSWAP_L2_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(uint256,uint256,bytes32,address,address,address)\"\n )\n );\n\n bytes4 public immutable ANYSWAP_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(uint256,address,address,bytes32))\"\n )\n );\n\n // polygon router multichain router v4\n AnyswapV4Router public immutable router;\n\n /**\n * @notice Constructor sets the router address and socketGateway address.\n * @dev anyswap v4 router is immutable. so no setter function required.\n */\n constructor(\n address _router,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n router = AnyswapV4Router(_router);\n }\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct AnyswapBridgeDataNoToken {\n /// @notice destination ChainId\n uint256 toChainId;\n /// @notice address of receiver of bridged tokens\n address receiverAddress;\n /// @notice address of wrapperToken, WrappedVersion of the token being bridged\n address wrapperTokenAddress;\n /// @notice socket offchain created hash\n bytes32 metadata;\n }\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct AnyswapBridgeData {\n /// @notice destination ChainId\n uint256 toChainId;\n /// @notice address of receiver of bridged tokens\n address receiverAddress;\n /// @notice address of wrapperToken, WrappedVersion of the token being bridged\n address wrapperTokenAddress;\n /// @notice address of token being bridged\n address token;\n /// @notice socket offchain created hash\n bytes32 metadata;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in AnyswapBridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for AnyswapBridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n AnyswapBridgeData memory anyswapBridgeData = abi.decode(\n bridgeData,\n (AnyswapBridgeData)\n );\n ERC20(anyswapBridgeData.token).safeApprove(address(router), amount);\n router.anySwapOutUnderlying(\n anyswapBridgeData.wrapperTokenAddress,\n anyswapBridgeData.receiverAddress,\n amount,\n anyswapBridgeData.toChainId\n );\n\n emit SocketBridge(\n amount,\n anyswapBridgeData.token,\n anyswapBridgeData.toChainId,\n AnyswapIdentifier,\n msg.sender,\n anyswapBridgeData.receiverAddress,\n anyswapBridgeData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in AnyswapBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param anyswapBridgeData encoded data for AnyswapBridge\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n AnyswapBridgeDataNoToken calldata anyswapBridgeData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n\n ERC20(token).safeApprove(address(router), bridgeAmount);\n router.anySwapOutUnderlying(\n anyswapBridgeData.wrapperTokenAddress,\n anyswapBridgeData.receiverAddress,\n bridgeAmount,\n anyswapBridgeData.toChainId\n );\n\n emit SocketBridge(\n bridgeAmount,\n token,\n anyswapBridgeData.toChainId,\n AnyswapIdentifier,\n msg.sender,\n anyswapBridgeData.receiverAddress,\n anyswapBridgeData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via Anyswap-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount being bridged\n * @param toChainId destination ChainId\n * @param receiverAddress address of receiver of bridged tokens\n * @param token address of token being bridged\n * @param wrapperTokenAddress address of wrapperToken, WrappedVersion of the token being bridged\n */\n function bridgeERC20To(\n uint256 amount,\n uint256 toChainId,\n bytes32 metadata,\n address receiverAddress,\n address token,\n address wrapperTokenAddress\n ) external payable {\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n tokenInstance.safeApprove(address(router), amount);\n router.anySwapOutUnderlying(\n wrapperTokenAddress,\n receiverAddress,\n amount,\n toChainId\n );\n\n emit SocketBridge(\n amount,\n token,\n toChainId,\n AnyswapIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n" + }, + "src/bridges/hyphen/Hyphen.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport \"./interfaces/hyphen.sol\";\nimport \"../BridgeImplBase.sol\";\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {HYPHEN} from \"../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Hyphen-Route Implementation\n * @notice Route implementation with functions to bridge ERC20 and Native via Hyphen-Bridge\n * Called via SocketGateway if the routeId in the request maps to the routeId of HyphenImplementation\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ncontract HyphenImpl is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable HyphenIdentifier = HYPHEN;\n\n /// @notice Function-selector for ERC20-token bridging on Hyphen-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4 public immutable HYPHEN_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\"bridgeERC20To(uint256,bytes32,address,address,uint256)\")\n );\n\n /// @notice Function-selector for Native bridging on Hyphen-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge Native tokens\n bytes4 public immutable HYPHEN_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(keccak256(\"bridgeNativeTo(uint256,bytes32,address,uint256)\"));\n\n bytes4 public immutable HYPHEN_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\"swapAndBridge(uint32,bytes,(address,uint256,bytes32))\")\n );\n\n /// @notice liquidityPoolManager - liquidityPool Manager of Hyphen used to bridge ERC20 and native\n /// @dev this is to be initialized in constructor with a valid deployed address of hyphen-liquidityPoolManager\n HyphenLiquidityPoolManager public immutable liquidityPoolManager;\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n /// @dev ensure liquidityPoolManager-address are set properly for the chainId in which the contract is being deployed\n constructor(\n address _liquidityPoolManager,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n liquidityPoolManager = HyphenLiquidityPoolManager(\n _liquidityPoolManager\n );\n }\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct HyphenData {\n /// @notice address of token being bridged\n address token;\n /// @notice address of receiver\n address receiverAddress;\n /// @notice chainId of destination\n uint256 toChainId;\n /// @notice socket offchain created hash\n bytes32 metadata;\n }\n\n struct HyphenDataNoToken {\n /// @notice address of receiver\n address receiverAddress;\n /// @notice chainId of destination\n uint256 toChainId;\n /// @notice chainId of destination\n bytes32 metadata;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in HyphenBridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for HyphenBridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n HyphenData memory hyphenData = abi.decode(bridgeData, (HyphenData));\n\n if (hyphenData.token == NATIVE_TOKEN_ADDRESS) {\n liquidityPoolManager.depositNative{value: amount}(\n hyphenData.receiverAddress,\n hyphenData.toChainId,\n \"SOCKET\"\n );\n } else {\n ERC20(hyphenData.token).safeApprove(\n address(liquidityPoolManager),\n amount\n );\n liquidityPoolManager.depositErc20(\n hyphenData.toChainId,\n hyphenData.token,\n hyphenData.receiverAddress,\n amount,\n \"SOCKET\"\n );\n }\n\n emit SocketBridge(\n amount,\n hyphenData.token,\n hyphenData.toChainId,\n HyphenIdentifier,\n msg.sender,\n hyphenData.receiverAddress,\n hyphenData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in HyphenBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param hyphenData encoded data for hyphenData\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n HyphenDataNoToken calldata hyphenData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n if (token == NATIVE_TOKEN_ADDRESS) {\n liquidityPoolManager.depositNative{value: bridgeAmount}(\n hyphenData.receiverAddress,\n hyphenData.toChainId,\n \"SOCKET\"\n );\n } else {\n ERC20(token).safeApprove(\n address(liquidityPoolManager),\n bridgeAmount\n );\n liquidityPoolManager.depositErc20(\n hyphenData.toChainId,\n token,\n hyphenData.receiverAddress,\n bridgeAmount,\n \"SOCKET\"\n );\n }\n\n emit SocketBridge(\n bridgeAmount,\n token,\n hyphenData.toChainId,\n HyphenIdentifier,\n msg.sender,\n hyphenData.receiverAddress,\n hyphenData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via Hyphen-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount to be sent\n * @param receiverAddress address of the token to bridged to the destination chain.\n * @param token address of token being bridged\n * @param toChainId chainId of destination\n */\n function bridgeERC20To(\n uint256 amount,\n bytes32 metadata,\n address receiverAddress,\n address token,\n uint256 toChainId\n ) external payable {\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n tokenInstance.safeApprove(address(liquidityPoolManager), amount);\n liquidityPoolManager.depositErc20(\n toChainId,\n token,\n receiverAddress,\n amount,\n \"SOCKET\"\n );\n\n emit SocketBridge(\n amount,\n token,\n toChainId,\n HyphenIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n\n /**\n * @notice function to handle Native bridging to receipent via Hyphen-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount to be sent\n * @param receiverAddress address of the token to bridged to the destination chain.\n * @param toChainId chainId of destination\n */\n function bridgeNativeTo(\n uint256 amount,\n bytes32 metadata,\n address receiverAddress,\n uint256 toChainId\n ) external payable {\n liquidityPoolManager.depositNative{value: amount}(\n receiverAddress,\n toChainId,\n \"SOCKET\"\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n toChainId,\n HyphenIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n" + }, + "src/bridges/optimism/l1/NativeOptimism.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport \"../interfaces/optimism.sol\";\nimport {BridgeImplBase} from \"../../BridgeImplBase.sol\";\nimport {UnsupportedInterfaceId} from \"../../../errors/SocketErrors.sol\";\nimport {NATIVE_OPTIMISM} from \"../../../static/RouteIdentifiers.sol\";\n\n/**\n * @title NativeOptimism-Route Implementation\n * @notice Route implementation with functions to bridge ERC20 and Native via NativeOptimism-Bridge\n * Tokens are bridged from Ethereum to Optimism Chain.\n * Called via SocketGateway if the routeId in the request maps to the routeId of NativeOptimism-Implementation\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ncontract NativeOptimismImpl is BridgeImplBase {\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable NativeOptimismIdentifier = NATIVE_OPTIMISM;\n\n uint256 public constant DESTINATION_CHAIN_ID = 10;\n\n /// @notice Function-selector for ERC20-token bridging on Native-Optimism-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4\n public immutable NATIVE_OPTIMISM_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(address,address,address,uint32,(bytes32,bytes32),uint256,uint256,address,bytes)\"\n )\n );\n\n /// @notice Function-selector for Native bridging on Native-Optimism-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge Native balance\n bytes4\n public immutable NATIVE_OPTIMISM_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeNativeTo(address,address,uint32,uint256,bytes32,bytes)\"\n )\n );\n\n bytes4 public immutable NATIVE_OPTIMISM_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(uint256,bytes32,bytes32,address,address,uint32,address,bytes))\"\n )\n );\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n constructor(\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {}\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct OptimismBridgeDataNoToken {\n // interfaceId to be set offchain which is used to select one of the 3 kinds of bridging (standard bridge / old standard / synthetic)\n uint256 interfaceId;\n // currencyKey of the token beingBridged\n bytes32 currencyKey;\n // socket offchain created hash\n bytes32 metadata;\n // address of receiver of bridged tokens\n address receiverAddress;\n /**\n * OptimismBridge that Performs the logic for deposits by informing the L2 Deposited Token\n * contract of the deposit and calling a handler to lock the L1 funds. (e.g. transferFrom)\n */\n address customBridgeAddress;\n // Gas limit required to complete the deposit on L2.\n uint32 l2Gas;\n // Address of the L1 respective L2 ERC20\n address l2Token;\n // additional data , for ll contracts this will be 0x data or empty data\n bytes data;\n }\n\n struct OptimismBridgeData {\n // interfaceId to be set offchain which is used to select one of the 3 kinds of bridging (standard bridge / old standard / synthetic)\n uint256 interfaceId;\n // currencyKey of the token beingBridged\n bytes32 currencyKey;\n // socket offchain created hash\n bytes32 metadata;\n // address of receiver of bridged tokens\n address receiverAddress;\n /**\n * OptimismBridge that Performs the logic for deposits by informing the L2 Deposited Token\n * contract of the deposit and calling a handler to lock the L1 funds. (e.g. transferFrom)\n */\n address customBridgeAddress;\n /// @notice address of token being bridged\n address token;\n // Gas limit required to complete the deposit on L2.\n uint32 l2Gas;\n // Address of the L1 respective L2 ERC20\n address l2Token;\n // additional data , for ll contracts this will be 0x data or empty data\n bytes data;\n }\n\n struct OptimismERC20Data {\n bytes32 currencyKey;\n bytes32 metadata;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in OptimismBridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for Optimism-Bridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n OptimismBridgeData memory optimismBridgeData = abi.decode(\n bridgeData,\n (OptimismBridgeData)\n );\n\n emit SocketBridge(\n amount,\n optimismBridgeData.token,\n DESTINATION_CHAIN_ID,\n NativeOptimismIdentifier,\n msg.sender,\n optimismBridgeData.receiverAddress,\n optimismBridgeData.metadata\n );\n if (optimismBridgeData.token == NATIVE_TOKEN_ADDRESS) {\n L1StandardBridge(optimismBridgeData.customBridgeAddress)\n .depositETHTo{value: amount}(\n optimismBridgeData.receiverAddress,\n optimismBridgeData.l2Gas,\n optimismBridgeData.data\n );\n } else {\n if (optimismBridgeData.interfaceId == 0) {\n revert UnsupportedInterfaceId();\n }\n\n ERC20(optimismBridgeData.token).safeApprove(\n optimismBridgeData.customBridgeAddress,\n amount\n );\n\n if (optimismBridgeData.interfaceId == 1) {\n // deposit into standard bridge\n L1StandardBridge(optimismBridgeData.customBridgeAddress)\n .depositERC20To(\n optimismBridgeData.token,\n optimismBridgeData.l2Token,\n optimismBridgeData.receiverAddress,\n amount,\n optimismBridgeData.l2Gas,\n optimismBridgeData.data\n );\n return;\n }\n\n // Deposit Using Old Standard - iOVM_L1TokenGateway(Example - SNX Token)\n if (optimismBridgeData.interfaceId == 2) {\n OldL1TokenGateway(optimismBridgeData.customBridgeAddress)\n .depositTo(optimismBridgeData.receiverAddress, amount);\n return;\n }\n\n if (optimismBridgeData.interfaceId == 3) {\n OldL1TokenGateway(optimismBridgeData.customBridgeAddress)\n .initiateSynthTransfer(\n optimismBridgeData.currencyKey,\n optimismBridgeData.receiverAddress,\n amount\n );\n return;\n }\n }\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in OptimismBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param optimismBridgeData encoded data for OptimismBridgeData\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n OptimismBridgeDataNoToken calldata optimismBridgeData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n\n emit SocketBridge(\n bridgeAmount,\n token,\n DESTINATION_CHAIN_ID,\n NativeOptimismIdentifier,\n msg.sender,\n optimismBridgeData.receiverAddress,\n optimismBridgeData.metadata\n );\n if (token == NATIVE_TOKEN_ADDRESS) {\n L1StandardBridge(optimismBridgeData.customBridgeAddress)\n .depositETHTo{value: bridgeAmount}(\n optimismBridgeData.receiverAddress,\n optimismBridgeData.l2Gas,\n optimismBridgeData.data\n );\n } else {\n if (optimismBridgeData.interfaceId == 0) {\n revert UnsupportedInterfaceId();\n }\n\n ERC20(token).safeApprove(\n optimismBridgeData.customBridgeAddress,\n bridgeAmount\n );\n\n if (optimismBridgeData.interfaceId == 1) {\n // deposit into standard bridge\n L1StandardBridge(optimismBridgeData.customBridgeAddress)\n .depositERC20To(\n token,\n optimismBridgeData.l2Token,\n optimismBridgeData.receiverAddress,\n bridgeAmount,\n optimismBridgeData.l2Gas,\n optimismBridgeData.data\n );\n return;\n }\n\n // Deposit Using Old Standard - iOVM_L1TokenGateway(Example - SNX Token)\n if (optimismBridgeData.interfaceId == 2) {\n OldL1TokenGateway(optimismBridgeData.customBridgeAddress)\n .depositTo(\n optimismBridgeData.receiverAddress,\n bridgeAmount\n );\n return;\n }\n\n if (optimismBridgeData.interfaceId == 3) {\n OldL1TokenGateway(optimismBridgeData.customBridgeAddress)\n .initiateSynthTransfer(\n optimismBridgeData.currencyKey,\n optimismBridgeData.receiverAddress,\n bridgeAmount\n );\n return;\n }\n }\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via NativeOptimism-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param token address of token being bridged\n * @param receiverAddress address of receiver of bridged tokens\n * @param customBridgeAddress OptimismBridge that Performs the logic for deposits by informing the L2 Deposited Token\n * contract of the deposit and calling a handler to lock the L1 funds. (e.g. transferFrom)\n * @param l2Gas Gas limit required to complete the deposit on L2.\n * @param optimismData extra data needed for optimism bridge\n * @param amount amount being bridged\n * @param interfaceId interfaceId to be set offchain which is used to select one of the 3 kinds of bridging (standard bridge / old standard / synthetic)\n * @param l2Token Address of the L1 respective L2 ERC20\n * @param data additional data , for ll contracts this will be 0x data or empty data\n */\n function bridgeERC20To(\n address token,\n address receiverAddress,\n address customBridgeAddress,\n uint32 l2Gas,\n OptimismERC20Data calldata optimismData,\n uint256 amount,\n uint256 interfaceId,\n address l2Token,\n bytes calldata data\n ) external payable {\n if (interfaceId == 0) {\n revert UnsupportedInterfaceId();\n }\n\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n tokenInstance.safeApprove(customBridgeAddress, amount);\n\n emit SocketBridge(\n amount,\n token,\n DESTINATION_CHAIN_ID,\n NativeOptimismIdentifier,\n msg.sender,\n receiverAddress,\n optimismData.metadata\n );\n if (interfaceId == 1) {\n // deposit into standard bridge\n L1StandardBridge(customBridgeAddress).depositERC20To(\n token,\n l2Token,\n receiverAddress,\n amount,\n l2Gas,\n data\n );\n return;\n }\n\n // Deposit Using Old Standard - iOVM_L1TokenGateway(Example - SNX Token)\n if (interfaceId == 2) {\n OldL1TokenGateway(customBridgeAddress).depositTo(\n receiverAddress,\n amount\n );\n return;\n }\n\n if (interfaceId == 3) {\n OldL1TokenGateway(customBridgeAddress).initiateSynthTransfer(\n optimismData.currencyKey,\n receiverAddress,\n amount\n );\n return;\n }\n }\n\n /**\n * @notice function to handle native balance bridging to receipent via NativeOptimism-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param receiverAddress address of receiver of bridged tokens\n * @param customBridgeAddress OptimismBridge that Performs the logic for deposits by informing the L2 Deposited Token\n * contract of the deposit and calling a handler to lock the L1 funds. (e.g. transferFrom)\n * @param l2Gas Gas limit required to complete the deposit on L2.\n * @param amount amount being bridged\n * @param data additional data , for ll contracts this will be 0x data or empty data\n */\n function bridgeNativeTo(\n address receiverAddress,\n address customBridgeAddress,\n uint32 l2Gas,\n uint256 amount,\n bytes32 metadata,\n bytes calldata data\n ) external payable {\n L1StandardBridge(customBridgeAddress).depositETHTo{value: amount}(\n receiverAddress,\n l2Gas,\n data\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n DESTINATION_CHAIN_ID,\n NativeOptimismIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n" + }, + "src/deployFactory/SocketDeployFactory.sol": { + "content": "//SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport \"../utils/Ownable.sol\";\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {ISocketBridgeBase} from \"../interfaces/ISocketBridgeBase.sol\";\n\n/**\n * @dev In the constructor, set up the initialization code for socket\n * contracts as well as the keccak256 hash of the given initialization code.\n * that will be used to deploy any transient contracts, which will deploy any\n * socket contracts that require the use of a constructor.\n *\n * Socket contract initialization code (29 bytes):\n *\n * 0x5860208158601c335a63aaf10f428752fa158151803b80938091923cf3\n *\n * Description:\n *\n * pc|op|name | [stack] | \n *\n * ** set the first stack item to zero - used later **\n * 00 58 getpc [0] <>\n *\n * ** set second stack item to 32, length of word returned from staticcall **\n * 01 60 push1\n * 02 20 outsize [0, 32] <>\n *\n * ** set third stack item to 0, position of word returned from staticcall **\n * 03 81 dup2 [0, 32, 0] <>\n *\n * ** set fourth stack item to 4, length of selector given to staticcall **\n * 04 58 getpc [0, 32, 0, 4] <>\n *\n * ** set fifth stack item to 28, position of selector given to staticcall **\n * 05 60 push1\n * 06 1c inpos [0, 32, 0, 4, 28] <>\n *\n * ** set the sixth stack item to msg.sender, target address for staticcall **\n * 07 33 caller [0, 32, 0, 4, 28, caller] <>\n *\n * ** set the seventh stack item to msg.gas, gas to forward for staticcall **\n * 08 5a gas [0, 32, 0, 4, 28, caller, gas] <>\n *\n * ** set the eighth stack item to selector, \"what\" to store via mstore **\n * 09 63 push4\n * 10 aaf10f42 selector [0, 32, 0, 4, 28, caller, gas, 0xaaf10f42] <>\n *\n * ** set the ninth stack item to 0, \"where\" to store via mstore ***\n * 11 87 dup8 [0, 32, 0, 4, 28, caller, gas, 0xaaf10f42, 0] <>\n *\n * ** call mstore, consume 8 and 9 from the stack, place selector in memory **\n * 12 52 mstore [0, 32, 0, 4, 0, caller, gas] <0xaaf10f42>\n *\n * ** call staticcall, consume items 2 through 7, place address in memory **\n * 13 fa staticcall [0, 1 (if successful)]
\n *\n * ** flip success bit in second stack item to set to 0 **\n * 14 15 iszero [0, 0]
\n *\n * ** push a third 0 to the stack, position of address in memory **\n * 15 81 dup2 [0, 0, 0]
\n *\n * ** place address from position in memory onto third stack item **\n * 16 51 mload [0, 0, address] <>\n *\n * ** place address to fourth stack item for extcodesize to consume **\n * 17 80 dup1 [0, 0, address, address] <>\n *\n * ** get extcodesize on fourth stack item for extcodecopy **\n * 18 3b extcodesize [0, 0, address, size] <>\n *\n * ** dup and swap size for use by return at end of init code **\n * 19 80 dup1 [0, 0, address, size, size] <>\n * 20 93 swap4 [size, 0, address, size, 0] <>\n *\n * ** push code position 0 to stack and reorder stack items for extcodecopy **\n * 21 80 dup1 [size, 0, address, size, 0, 0] <>\n * 22 91 swap2 [size, 0, address, 0, 0, size] <>\n * 23 92 swap3 [size, 0, size, 0, 0, address] <>\n *\n * ** call extcodecopy, consume four items, clone runtime code to memory **\n * 24 3c extcodecopy [size, 0] \n *\n * ** return to deploy final code in memory **\n * 25 f3 return [] *deployed!*\n */\ncontract SocketDeployFactory is Ownable {\n using SafeTransferLib for ERC20;\n address public immutable disabledRouteAddress;\n\n mapping(address => address) _implementations;\n mapping(uint256 => bool) isDisabled;\n mapping(uint256 => bool) isRouteDeployed;\n mapping(address => bool) canDisableRoute;\n\n event Deployed(address _addr);\n event DisabledRoute(address _addr);\n event Destroyed(address _addr);\n error ContractAlreadyDeployed();\n error NothingToDestroy();\n error AlreadyDisabled();\n error CannotBeDisabled();\n error OnlyDisabler();\n\n constructor(address _owner, address disabledRoute) Ownable(_owner) {\n disabledRouteAddress = disabledRoute;\n canDisableRoute[_owner] = true;\n }\n\n modifier onlyDisabler() {\n if (!canDisableRoute[msg.sender]) {\n revert OnlyDisabler();\n }\n _;\n }\n\n function addDisablerAddress(address disabler) external onlyOwner {\n canDisableRoute[disabler] = true;\n }\n\n function removeDisablerAddress(address disabler) external onlyOwner {\n canDisableRoute[disabler] = false;\n }\n\n /**\n * @notice Deploys a route contract at predetermined location\n * @notice Caller must first deploy the route contract at another location and pass its address as implementation.\n * @param routeId route identifier\n * @param implementationContract address of deployed route contract. Its byte code will be copied to predetermined location.\n */\n function deploy(\n uint256 routeId,\n address implementationContract\n ) external onlyOwner returns (address) {\n // assign the initialization code for the socket contract.\n\n bytes memory initCode = (\n hex\"5860208158601c335a63aaf10f428752fa158151803b80938091923cf3\"\n );\n\n // determine the address of the socket contract.\n address routeContractAddress = _getContractAddress(routeId);\n\n if (isRouteDeployed[routeId]) {\n revert ContractAlreadyDeployed();\n }\n\n isRouteDeployed[routeId] = true;\n\n //first we deploy the code we want to deploy on a separate address\n // store the implementation to be retrieved by the socket contract.\n _implementations[routeContractAddress] = implementationContract;\n address addr;\n assembly {\n let encoded_data := add(0x20, initCode) // load initialization code.\n let encoded_size := mload(initCode) // load init code's length.\n addr := create2(0, encoded_data, encoded_size, routeId) // routeId is used as salt\n }\n require(\n addr == routeContractAddress,\n \"Failed to deploy the new socket contract.\"\n );\n emit Deployed(addr);\n return addr;\n }\n\n /**\n * @notice Destroy the route deployed at a location.\n * @param routeId route identifier to be destroyed.\n */\n function destroy(uint256 routeId) external onlyDisabler {\n // determine the address of the socket contract.\n _destroy(routeId);\n }\n\n /**\n * @notice Deploy a disabled contract at destroyed route to handle it gracefully.\n * @param routeId route identifier to be disabled.\n */\n function disableRoute(\n uint256 routeId\n ) external onlyDisabler returns (address) {\n return _disableRoute(routeId);\n }\n\n /**\n * @notice Destroy a list of routeIds\n * @param routeIds array of routeIds to be destroyed.\n */\n function multiDestroy(uint256[] calldata routeIds) external onlyDisabler {\n for (uint32 index = 0; index < routeIds.length; ) {\n _destroy(routeIds[index]);\n unchecked {\n ++index;\n }\n }\n }\n\n /**\n * @notice Deploy a disabled contract at list of routeIds.\n * @param routeIds array of routeIds to be disabled.\n */\n function multiDisableRoute(\n uint256[] calldata routeIds\n ) external onlyDisabler {\n for (uint32 index = 0; index < routeIds.length; ) {\n _disableRoute(routeIds[index]);\n unchecked {\n ++index;\n }\n }\n }\n\n /**\n * @dev External view function for calculating a socket contract address\n * given a particular routeId.\n */\n function getContractAddress(\n uint256 routeId\n ) external view returns (address) {\n // determine the address of the socket contract.\n return _getContractAddress(routeId);\n }\n\n //those two functions are getting called by the socket Contract\n function getImplementation()\n external\n view\n returns (address implementation)\n {\n return _implementations[msg.sender];\n }\n\n function _disableRoute(uint256 routeId) internal returns (address) {\n // assign the initialization code for the socket contract.\n bytes memory initCode = (\n hex\"5860208158601c335a63aaf10f428752fa158151803b80938091923cf3\"\n );\n\n // determine the address of the socket contract.\n address routeContractAddress = _getContractAddress(routeId);\n\n if (!isRouteDeployed[routeId]) {\n revert CannotBeDisabled();\n }\n\n if (isDisabled[routeId]) {\n revert AlreadyDisabled();\n }\n\n isDisabled[routeId] = true;\n\n //first we deploy the code we want to deploy on a separate address\n // store the implementation to be retrieved by the socket contract.\n _implementations[routeContractAddress] = disabledRouteAddress;\n address addr;\n assembly {\n let encoded_data := add(0x20, initCode) // load initialization code.\n let encoded_size := mload(initCode) // load init code's length.\n addr := create2(0, encoded_data, encoded_size, routeId) // routeId is used as salt.\n }\n require(\n addr == routeContractAddress,\n \"Failed to deploy the new socket contract.\"\n );\n emit Deployed(addr);\n return addr;\n }\n\n function _destroy(uint256 routeId) internal {\n // determine the address of the socket contract.\n address routeContractAddress = _getContractAddress(routeId);\n\n if (!isRouteDeployed[routeId]) {\n revert NothingToDestroy();\n }\n ISocketBridgeBase(routeContractAddress).killme();\n emit Destroyed(routeContractAddress);\n }\n\n /**\n * @dev Internal view function for calculating a socket contract address\n * given a particular routeId.\n */\n function _getContractAddress(\n uint256 routeId\n ) internal view returns (address) {\n // determine the address of the socket contract.\n\n bytes memory initCode = (\n hex\"5860208158601c335a63aaf10f428752fa158151803b80938091923cf3\"\n );\n return\n address(\n uint160( // downcast to match the address type.\n uint256( // convert to uint to truncate upper digits.\n keccak256( // compute the CREATE2 hash using 4 inputs.\n abi.encodePacked( // pack all inputs to the hash together.\n hex\"ff\", // start with 0xff to distinguish from RLP.\n address(this), // this contract will be the caller.\n routeId, // the routeId is used as salt.\n keccak256(abi.encodePacked(initCode)) // the init code hash.\n )\n )\n )\n )\n );\n }\n\n /**\n * @notice Rescues the ERC20 token to an address\n this is a restricted function to be called by only socketGatewayOwner\n * @dev as this is a restricted to socketGatewayOwner, ensure the userAddress is a known address\n * @param token address of the ERC20 token being rescued\n * @param userAddress address to which ERC20 is to be rescued\n * @param amount amount of ERC20 tokens being rescued\n */\n function rescueFunds(\n address token,\n address userAddress,\n uint256 amount\n ) external onlyOwner {\n ERC20(token).safeTransfer(userAddress, amount);\n }\n\n /**\n * @notice Rescues the native balance to an address\n this is a restricted function to be called by only socketGatewayOwner\n * @dev as this is a restricted to socketGatewayOwner, ensure the userAddress is a known address\n * @param userAddress address to which native-balance is to be rescued\n * @param amount amount of native-balance being rescued\n */\n function rescueEther(\n address payable userAddress,\n uint256 amount\n ) external onlyOwner {\n userAddress.transfer(amount);\n }\n}\n" + }, + "src/interfaces/ISocketController.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/**\n * @title ISocketController\n * @notice Interface for SocketController functions.\n * @dev functions can be added here for invocation from external contracts or off-chain\n * only restriction is that this should have functions to manage controllers\n * @author Socket dot tech.\n */\ninterface ISocketController {\n /**\n * @notice Add controller to the socketGateway\n This is a restricted function to be called by only socketGatewayOwner\n * @dev ensure controllerAddress is a verified controller implementation address\n * @param _controllerAddress The address of controller implementation contract deployed\n * @return Id of the controller added to the controllers-mapping in socketGateway storage\n */\n function addController(\n address _controllerAddress\n ) external returns (uint32);\n\n /**\n * @notice disable controller by setting ZeroAddress to the entry in controllers-mapping\n identified by controllerId as key.\n This is a restricted function to be called by only socketGatewayOwner\n * @param _controllerId The Id of controller-implementation in the controllers mapping\n */\n function disableController(uint32 _controllerId) external;\n\n /**\n * @notice Get controllerImplementation address mapped to the controllerId\n * @param _controllerId controllerId is the key in the mapping for controllers\n * @return controller-implementation address\n */\n function getController(uint32 _controllerId) external returns (address);\n}\n" + }, + "lib/solmate/src/utils/SafeTransferLib.sol": { + "content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\nimport {ERC20} from \"../tokens/ERC20.sol\";\n\n/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.\n/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)\n/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.\n/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.\nlibrary SafeTransferLib {\n /*//////////////////////////////////////////////////////////////\n ETH OPERATIONS\n //////////////////////////////////////////////////////////////*/\n\n function safeTransferETH(address to, uint256 amount) internal {\n bool success;\n\n /// @solidity memory-safe-assembly\n assembly {\n // Transfer the ETH and store if it succeeded or not.\n success := call(gas(), to, amount, 0, 0, 0, 0)\n }\n\n require(success, \"ETH_TRANSFER_FAILED\");\n }\n\n /*//////////////////////////////////////////////////////////////\n ERC20 OPERATIONS\n //////////////////////////////////////////////////////////////*/\n\n function safeTransferFrom(\n ERC20 token,\n address from,\n address to,\n uint256 amount\n ) internal {\n bool success;\n\n /// @solidity memory-safe-assembly\n assembly {\n // Get a pointer to some free memory.\n let freeMemoryPointer := mload(0x40)\n\n // Write the abi-encoded calldata into memory, beginning with the function selector.\n mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)\n mstore(add(freeMemoryPointer, 4), from) // Append the \"from\" argument.\n mstore(add(freeMemoryPointer, 36), to) // Append the \"to\" argument.\n mstore(add(freeMemoryPointer, 68), amount) // Append the \"amount\" argument.\n\n success := and(\n // Set success to whether the call reverted, if not we check it either\n // returned exactly 1 (can't just be non-zero data), or had no return data.\n or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),\n // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.\n // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.\n // Counterintuitively, this call must be positioned second to the or() call in the\n // surrounding and() call or else returndatasize() will be zero during the computation.\n call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)\n )\n }\n\n require(success, \"TRANSFER_FROM_FAILED\");\n }\n\n function safeTransfer(\n ERC20 token,\n address to,\n uint256 amount\n ) internal {\n bool success;\n\n /// @solidity memory-safe-assembly\n assembly {\n // Get a pointer to some free memory.\n let freeMemoryPointer := mload(0x40)\n\n // Write the abi-encoded calldata into memory, beginning with the function selector.\n mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)\n mstore(add(freeMemoryPointer, 4), to) // Append the \"to\" argument.\n mstore(add(freeMemoryPointer, 36), amount) // Append the \"amount\" argument.\n\n success := and(\n // Set success to whether the call reverted, if not we check it either\n // returned exactly 1 (can't just be non-zero data), or had no return data.\n or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),\n // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.\n // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.\n // Counterintuitively, this call must be positioned second to the or() call in the\n // surrounding and() call or else returndatasize() will be zero during the computation.\n call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)\n )\n }\n\n require(success, \"TRANSFER_FAILED\");\n }\n\n function safeApprove(\n ERC20 token,\n address to,\n uint256 amount\n ) internal {\n bool success;\n\n /// @solidity memory-safe-assembly\n assembly {\n // Get a pointer to some free memory.\n let freeMemoryPointer := mload(0x40)\n\n // Write the abi-encoded calldata into memory, beginning with the function selector.\n mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)\n mstore(add(freeMemoryPointer, 4), to) // Append the \"to\" argument.\n mstore(add(freeMemoryPointer, 36), amount) // Append the \"amount\" argument.\n\n success := and(\n // Set success to whether the call reverted, if not we check it either\n // returned exactly 1 (can't just be non-zero data), or had no return data.\n or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),\n // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.\n // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.\n // Counterintuitively, this call must be positioned second to the or() call in the\n // surrounding and() call or else returndatasize() will be zero during the computation.\n call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)\n )\n }\n\n require(success, \"APPROVE_FAILED\");\n }\n}\n" + }, + "src/controllers/BaseController.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {ISocketRequest} from \"../interfaces/ISocketRequest.sol\";\nimport {ISocketRoute} from \"../interfaces/ISocketRoute.sol\";\n\n/// @title BaseController Controller\n/// @notice Base contract for all controller contracts\nabstract contract BaseController {\n /// @notice Address used to identify if it is a native token transfer or not\n address public immutable NATIVE_TOKEN_ADDRESS =\n address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);\n\n /// @notice Address used to identify if it is a Zero address\n address public immutable NULL_ADDRESS = address(0);\n\n /// @notice FunctionSelector used to delegatecall from swap to the function of bridge router implementation\n bytes4 public immutable BRIDGE_AFTER_SWAP_SELECTOR =\n bytes4(keccak256(\"bridgeAfterSwap(uint256,bytes)\"));\n\n /// @notice immutable variable to store the socketGateway address\n address public immutable socketGatewayAddress;\n\n /// @notice immutable variable with instance of SocketRoute to access route functions\n ISocketRoute public immutable socketRoute;\n\n /**\n * @notice Construct the base for all controllers.\n * @param _socketGatewayAddress Socketgateway address, an immutable variable to set.\n * @notice initialize the immutable variables of SocketRoute, SocketGateway\n */\n constructor(address _socketGatewayAddress) {\n socketGatewayAddress = _socketGatewayAddress;\n socketRoute = ISocketRoute(_socketGatewayAddress);\n }\n\n /**\n * @notice Construct the base for all BridgeImplementations.\n * @param routeId routeId mapped to the routrImplementation\n * @param data transactionData generated with arguments of bridgeRequest (offchain or by caller)\n * @return returns the bytes response of the route execution (bridging, refuel or swap executions)\n */\n function _executeRoute(\n uint32 routeId,\n bytes memory data\n ) internal returns (bytes memory) {\n (bool success, bytes memory result) = socketRoute\n .getRoute(routeId)\n .delegatecall(data);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n return result;\n }\n}\n" + }, + "src/interfaces/ISocketRoute.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/**\n * @title ISocketRoute\n * @notice Interface for routeManagement functions in SocketGateway.\n * @author Socket dot tech.\n */\ninterface ISocketRoute {\n /**\n * @notice Add route to the socketGateway\n This is a restricted function to be called by only socketGatewayOwner\n * @dev ensure routeAddress is a verified bridge or middleware implementation address\n * @param routeAddress The address of bridge or middleware implementation contract deployed\n * @return Id of the route added to the routes-mapping in socketGateway storage\n */\n function addRoute(address routeAddress) external returns (uint256);\n\n /**\n * @notice disable a route by setting ZeroAddress to the entry in routes-mapping\n identified by routeId as key.\n This is a restricted function to be called by only socketGatewayOwner\n * @param routeId The Id of route-implementation in the routes mapping\n */\n function disableRoute(uint32 routeId) external;\n\n /**\n * @notice Get routeImplementation address mapped to the routeId\n * @param routeId routeId is the key in the mapping for routes\n * @return route-implementation address\n */\n function getRoute(uint32 routeId) external view returns (address);\n}\n" + }, + "src/SocketGatewayDeployment.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\npragma experimental ABIEncoderV2;\n\nimport \"./utils/Ownable.sol\";\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {LibUtil} from \"./libraries/LibUtil.sol\";\nimport \"./libraries/LibBytes.sol\";\nimport {ISocketRoute} from \"./interfaces/ISocketRoute.sol\";\nimport {ISocketRequest} from \"./interfaces/ISocketRequest.sol\";\nimport {ISocketGateway} from \"./interfaces/ISocketGateway.sol\";\nimport {IncorrectBridgeRatios, ZeroAddressNotAllowed, ArrayLengthMismatch} from \"./errors/SocketErrors.sol\";\n\n/// @title SocketGatewayContract\n/// @notice Socketgateway is a contract with entrypoint functions for all interactions with socket liquidity layer\n/// @author Socket Team\ncontract SocketGateway is Ownable {\n using LibBytes for bytes;\n using LibBytes for bytes4;\n using SafeTransferLib for ERC20;\n\n /// @notice FunctionSelector used to delegatecall from swap to the function of bridge router implementation\n bytes4 public immutable BRIDGE_AFTER_SWAP_SELECTOR =\n bytes4(keccak256(\"bridgeAfterSwap(uint256,bytes)\"));\n\n /// @notice storage variable to keep track of total number of routes registered in socketgateway\n uint32 public routesCount = 385;\n\n /// @notice storage variable to keep track of total number of controllers registered in socketgateway\n uint32 public controllerCount;\n\n address public immutable disabledRouteAddress;\n\n uint256 public constant CENT_PERCENT = 100e18;\n\n /// @notice storage mapping for route implementation addresses\n mapping(uint32 => address) public routes;\n\n /// storage mapping for controller implemenation addresses\n mapping(uint32 => address) public controllers;\n\n // Events ------------------------------------------------------------------------------------------------------->\n\n /// @notice Event emitted when a router is added to socketgateway\n event NewRouteAdded(uint32 indexed routeId, address indexed route);\n\n /// @notice Event emitted when a route is disabled\n event RouteDisabled(uint32 indexed routeId);\n\n /// @notice Event emitted when ownership transfer is requested by socket-gateway-owner\n event OwnershipTransferRequested(\n address indexed _from,\n address indexed _to\n );\n\n /// @notice Event emitted when a controller is added to socketgateway\n event ControllerAdded(\n uint32 indexed controllerId,\n address indexed controllerAddress\n );\n\n /// @notice Event emitted when a controller is disabled\n event ControllerDisabled(uint32 indexed controllerId);\n\n constructor(address _owner, address _disabledRoute) Ownable(_owner) {\n disabledRouteAddress = _disabledRoute;\n }\n\n // Able to receive ether\n // solhint-disable-next-line no-empty-blocks\n receive() external payable {}\n\n /*******************************************\n * EXTERNAL AND PUBLIC FUNCTIONS *\n *******************************************/\n\n /**\n * @notice executes functions in the routes identified using routeId and functionSelectorData\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped\n * @dev ensure the data in routeData to be built using the function-selector defined as a\n * constant in the route implementation contract\n * @param routeId route identifier\n * @param routeData functionSelectorData generated using the function-selector defined in the route Implementation\n */\n function executeRoute(\n uint32 routeId,\n bytes calldata routeData\n ) external payable returns (bytes memory) {\n (bool success, bytes memory result) = addressAt(routeId).delegatecall(\n routeData\n );\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n return result;\n }\n\n /**\n * @notice swaps a token on sourceChain and split it across multiple bridge-recipients\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being swapped\n * @dev ensure the swap-data and bridge-data is generated using the function-selector defined as a constant in the implementation address\n * @param swapMultiBridgeRequest request\n */\n function swapAndMultiBridge(\n ISocketRequest.SwapMultiBridgeRequest calldata swapMultiBridgeRequest\n ) external payable {\n uint256 requestLength = swapMultiBridgeRequest.bridgeRouteIds.length;\n\n if (\n requestLength != swapMultiBridgeRequest.bridgeImplDataItems.length\n ) {\n revert ArrayLengthMismatch();\n }\n uint256 ratioAggregate;\n for (uint256 index = 0; index < requestLength; ) {\n ratioAggregate += swapMultiBridgeRequest.bridgeRatios[index];\n }\n\n if (ratioAggregate != CENT_PERCENT) {\n revert IncorrectBridgeRatios();\n }\n\n (bool swapSuccess, bytes memory swapResult) = addressAt(\n swapMultiBridgeRequest.swapRouteId\n ).delegatecall(swapMultiBridgeRequest.swapImplData);\n\n if (!swapSuccess) {\n assembly {\n revert(add(swapResult, 32), mload(swapResult))\n }\n }\n\n uint256 amountReceivedFromSwap = abi.decode(swapResult, (uint256));\n\n uint256 bridgedAmount;\n\n for (uint256 index = 0; index < requestLength; ) {\n uint256 bridgingAmount;\n\n // if it is the last bridge request, bridge the remaining amount\n if (index == requestLength - 1) {\n bridgingAmount = amountReceivedFromSwap - bridgedAmount;\n } else {\n // bridging amount is the multiplication of bridgeRatio and amountReceivedFromSwap\n bridgingAmount =\n (amountReceivedFromSwap *\n swapMultiBridgeRequest.bridgeRatios[index]) /\n (CENT_PERCENT);\n }\n\n // update the bridged amount, this would be used for computation for last bridgeRequest\n bridgedAmount += bridgingAmount;\n\n bytes memory bridgeImpldata = abi.encodeWithSelector(\n BRIDGE_AFTER_SWAP_SELECTOR,\n bridgingAmount,\n swapMultiBridgeRequest.bridgeImplDataItems[index]\n );\n\n (bool bridgeSuccess, bytes memory bridgeResult) = addressAt(\n swapMultiBridgeRequest.bridgeRouteIds[index]\n ).delegatecall(bridgeImpldata);\n\n if (!bridgeSuccess) {\n assembly {\n revert(add(bridgeResult, 32), mload(bridgeResult))\n }\n }\n\n unchecked {\n ++index;\n }\n }\n }\n\n /**\n * @notice sequentially executes functions in the routes identified using routeId and functionSelectorData\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped\n * @dev ensure the data in each dataItem to be built using the function-selector defined as a\n * constant in the route implementation contract\n * @param routeIds a list of route identifiers\n * @param dataItems a list of functionSelectorData generated using the function-selector defined in the route Implementation\n */\n function executeRoutes(\n uint32[] calldata routeIds,\n bytes[] calldata dataItems\n ) external payable {\n uint256 routeIdslength = routeIds.length;\n if (routeIdslength != dataItems.length) revert ArrayLengthMismatch();\n for (uint256 index = 0; index < routeIdslength; ) {\n (bool success, bytes memory result) = addressAt(routeIds[index])\n .delegatecall(dataItems[index]);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n unchecked {\n ++index;\n }\n }\n }\n\n /**\n * @notice execute a controller function identified using the controllerId in the request\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped\n * @dev ensure the data in request to be built using the function-selector defined as a\n * constant in the controller implementation contract\n * @param socketControllerRequest socketControllerRequest with controllerId to identify the\n * controllerAddress and byteData constructed using functionSelector\n * of the function being invoked\n * @return bytes data received from the call delegated to controller\n */\n function executeController(\n ISocketGateway.SocketControllerRequest calldata socketControllerRequest\n ) external payable returns (bytes memory) {\n (bool success, bytes memory result) = controllers[\n socketControllerRequest.controllerId\n ].delegatecall(socketControllerRequest.data);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n return result;\n }\n\n /**\n * @notice sequentially executes all controller requests\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped\n * @dev ensure the data in each controller-request to be built using the function-selector defined as a\n * constant in the controller implementation contract\n * @param controllerRequests a list of socketControllerRequest\n * Each controllerRequest contains controllerId to identify the controllerAddress and\n * byteData constructed using functionSelector of the function being invoked\n */\n function executeControllers(\n ISocketGateway.SocketControllerRequest[] calldata controllerRequests\n ) external payable {\n for (uint32 index = 0; index < controllerRequests.length; ) {\n (bool success, bytes memory result) = controllers[\n controllerRequests[index].controllerId\n ].delegatecall(controllerRequests[index].data);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n unchecked {\n ++index;\n }\n }\n }\n\n /**************************************\n * ADMIN FUNCTIONS *\n **************************************/\n\n /**\n * @notice Add route to the socketGateway\n This is a restricted function to be called by only socketGatewayOwner\n * @dev ensure routeAddress is a verified bridge or middleware implementation address\n * @param routeAddress The address of bridge or middleware implementation contract deployed\n * @return Id of the route added to the routes-mapping in socketGateway storage\n */\n function addRoute(\n address routeAddress\n ) external onlyOwner returns (uint32) {\n uint32 routeId = routesCount;\n routes[routeId] = routeAddress;\n\n routesCount += 1;\n\n emit NewRouteAdded(routeId, routeAddress);\n\n return routeId;\n }\n\n /**\n * @notice Give Infinite or 0 approval to bridgeRoute for the tokenAddress\n This is a restricted function to be called by only socketGatewayOwner\n */\n\n function setApprovalForRouters(\n address[] memory routeAddresses,\n address[] memory tokenAddresses,\n bool isMax\n ) external onlyOwner {\n for (uint32 index = 0; index < routeAddresses.length; ) {\n ERC20(tokenAddresses[index]).approve(\n routeAddresses[index],\n isMax ? type(uint256).max : 0\n );\n unchecked {\n ++index;\n }\n }\n }\n\n /**\n * @notice Add controller to the socketGateway\n This is a restricted function to be called by only socketGatewayOwner\n * @dev ensure controllerAddress is a verified controller implementation address\n * @param controllerAddress The address of controller implementation contract deployed\n * @return Id of the controller added to the controllers-mapping in socketGateway storage\n */\n function addController(\n address controllerAddress\n ) external onlyOwner returns (uint32) {\n uint32 controllerId = controllerCount;\n\n controllers[controllerId] = controllerAddress;\n\n controllerCount += 1;\n\n emit ControllerAdded(controllerId, controllerAddress);\n\n return controllerId;\n }\n\n /**\n * @notice disable controller by setting ZeroAddress to the entry in controllers-mapping\n identified by controllerId as key.\n This is a restricted function to be called by only socketGatewayOwner\n * @param controllerId The Id of controller-implementation in the controllers mapping\n */\n function disableController(uint32 controllerId) public onlyOwner {\n controllers[controllerId] = disabledRouteAddress;\n emit ControllerDisabled(controllerId);\n }\n\n /**\n * @notice disable a route by setting ZeroAddress to the entry in routes-mapping\n identified by routeId as key.\n This is a restricted function to be called by only socketGatewayOwner\n * @param routeId The Id of route-implementation in the routes mapping\n */\n function disableRoute(uint32 routeId) external onlyOwner {\n routes[routeId] = disabledRouteAddress;\n emit RouteDisabled(routeId);\n }\n\n /*******************************************\n * RESTRICTED RESCUE FUNCTIONS *\n *******************************************/\n\n /**\n * @notice Rescues the ERC20 token to an address\n this is a restricted function to be called by only socketGatewayOwner\n * @dev as this is a restricted to socketGatewayOwner, ensure the userAddress is a known address\n * @param token address of the ERC20 token being rescued\n * @param userAddress address to which ERC20 is to be rescued\n * @param amount amount of ERC20 tokens being rescued\n */\n function rescueFunds(\n address token,\n address userAddress,\n uint256 amount\n ) external onlyOwner {\n ERC20(token).safeTransfer(userAddress, amount);\n }\n\n /**\n * @notice Rescues the native balance to an address\n this is a restricted function to be called by only socketGatewayOwner\n * @dev as this is a restricted to socketGatewayOwner, ensure the userAddress is a known address\n * @param userAddress address to which native-balance is to be rescued\n * @param amount amount of native-balance being rescued\n */\n function rescueEther(\n address payable userAddress,\n uint256 amount\n ) external onlyOwner {\n userAddress.transfer(amount);\n }\n\n /*******************************************\n * VIEW FUNCTIONS *\n *******************************************/\n\n /**\n * @notice Get routeImplementation address mapped to the routeId\n * @param routeId routeId is the key in the mapping for routes\n * @return route-implementation address\n */\n function getRoute(uint32 routeId) public view returns (address) {\n return addressAt(routeId);\n }\n\n /**\n * @notice Get controllerImplementation address mapped to the controllerId\n * @param controllerId controllerId is the key in the mapping for controllers\n * @return controller-implementation address\n */\n function getController(uint32 controllerId) public view returns (address) {\n return controllers[controllerId];\n }\n\n function addressAt(uint32 routeId) public view returns (address) {\n if (routeId < 385) {\n if (routeId < 257) {\n if (routeId < 129) {\n if (routeId < 65) {\n if (routeId < 33) {\n if (routeId < 17) {\n if (routeId < 9) {\n if (routeId < 5) {\n if (routeId < 3) {\n if (routeId == 1) {\n return\n 0x8cd6BaCDAe46B449E2e5B34e348A4eD459c84D50;\n } else {\n return\n 0x31524750Cd865fF6A3540f232754Fb974c18585C;\n }\n } else {\n if (routeId == 3) {\n return\n 0xEd9b37342BeC8f3a2D7b000732ec87498aA6EC6a;\n } else {\n return\n 0xE8704Ef6211F8988Ccbb11badC89841808d66890;\n }\n }\n } else {\n if (routeId < 7) {\n if (routeId == 5) {\n return\n 0x9aFF58C460a461578C433e11C4108D1c4cF77761;\n } else {\n return\n 0x2D1733886cFd465B0B99F1492F40847495f334C5;\n }\n } else {\n if (routeId == 7) {\n return\n 0x715497Be4D130F04B8442F0A1F7a9312D4e54FC4;\n } else {\n return\n 0x90C8a40c38E633B5B0e0d0585b9F7FA05462CaaF;\n }\n }\n }\n } else {\n if (routeId < 13) {\n if (routeId < 11) {\n if (routeId == 9) {\n return\n 0xa402b70FCfF3F4a8422B93Ef58E895021eAdE4F6;\n } else {\n return\n 0xc1B718522E15CD42C4Ac385a929fc2B51f5B892e;\n }\n } else {\n if (routeId == 11) {\n return\n 0xa97bf2f7c26C43c010c349F52f5eA5dC49B2DD38;\n } else {\n return\n 0x969423d71b62C81d2f28d707364c9Dc4a0764c53;\n }\n }\n } else {\n if (routeId < 15) {\n if (routeId == 13) {\n return\n 0xF86729934C083fbEc8C796068A1fC60701Ea1207;\n } else {\n return\n 0xD7cC2571F5823caCA26A42690D2BE7803DD5393f;\n }\n } else {\n if (routeId == 15) {\n return\n 0x7c8837a279bbbf7d8B93413763176de9F65d5bB9;\n } else {\n return\n 0x13b81C27B588C07D04458ed7dDbdbD26D1e39bcc;\n }\n }\n }\n }\n } else {\n if (routeId < 25) {\n if (routeId < 21) {\n if (routeId < 19) {\n if (routeId == 17) {\n return\n 0x52560Ac678aFA1345D15474287d16Dc1eA3F78aE;\n } else {\n return\n 0x1E31e376551459667cd7643440c1b21CE69065A0;\n }\n } else {\n if (routeId == 19) {\n return\n 0xc57D822CB3288e7b97EF8f8af0EcdcD1B783529B;\n } else {\n return\n 0x2197A1D9Af24b4d6a64Bff95B4c29Fcd3Ff28C30;\n }\n }\n } else {\n if (routeId < 23) {\n if (routeId == 21) {\n return\n 0xE3700feAa5100041Bf6b7AdBA1f72f647809Fd00;\n } else {\n return\n 0xc02E8a0Fdabf0EeFCEA025163d90B5621E2b9948;\n }\n } else {\n if (routeId == 23) {\n return\n 0xF5144235E2926cAb3c69b30113254Fa632f72d62;\n } else {\n return\n 0xBa3F92313B00A1f7Bc53b2c24EB195c8b2F57682;\n }\n }\n }\n } else {\n if (routeId < 29) {\n if (routeId < 27) {\n if (routeId == 25) {\n return\n 0x77a6856fe1fFA5bEB55A1d2ED86E27C7c482CB76;\n } else {\n return\n 0x4826Ff4e01E44b1FCEFBfb38cd96687Eb7786b44;\n }\n } else {\n if (routeId == 27) {\n return\n 0x55FF3f5493cf5e80E76DEA7E327b9Cd8440Af646;\n } else {\n return\n 0xF430Db544bE9770503BE4aa51997aA19bBd5BA4f;\n }\n }\n } else {\n if (routeId < 31) {\n if (routeId == 29) {\n return\n 0x0f166446ce1484EE3B0663E7E67DF10F5D240115;\n } else {\n return\n 0x6365095D92537f242Db5EdFDd572745E72aC33d9;\n }\n } else {\n if (routeId == 31) {\n return\n 0x5c7BC93f06ce3eAe75ADf55E10e23d2c1dE5Bc65;\n } else {\n return\n 0xe46383bAD90d7A08197ccF08972e9DCdccCE9BA4;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 49) {\n if (routeId < 41) {\n if (routeId < 37) {\n if (routeId < 35) {\n if (routeId == 33) {\n return\n 0xf0f21710c071E3B728bdc4654c3c0b873aAaa308;\n } else {\n return\n 0x63Bc9ed3AcAAeB0332531C9fB03b0a2352E9Ff25;\n }\n } else {\n if (routeId == 35) {\n return\n 0xd1CE808625CB4007a1708824AE82CdB0ece57De9;\n } else {\n return\n 0x57BbB148112f4ba224841c3FE018884171004661;\n }\n }\n } else {\n if (routeId < 39) {\n if (routeId == 37) {\n return\n 0x037f7d6933036F34DFabd40Ff8e4D789069f92e3;\n } else {\n return\n 0xeF978c280915CfF3Dca4EDfa8932469e40ADA1e1;\n }\n } else {\n if (routeId == 39) {\n return\n 0x92ee9e071B13f7ecFD62B7DED404A16CBc223CD3;\n } else {\n return\n 0x94Ae539c186e41ed762271338Edf140414D1E442;\n }\n }\n }\n } else {\n if (routeId < 45) {\n if (routeId < 43) {\n if (routeId == 41) {\n return\n 0x30A64BBe4DdBD43dA2368EFd1eB2d80C10d84DAb;\n } else {\n return\n 0x3aEABf81c1Dc4c1b73d5B2a95410f126426FB596;\n }\n } else {\n if (routeId == 43) {\n return\n 0x25b08aB3D0C8ea4cC9d967b79688C6D98f3f563a;\n } else {\n return\n 0xea40cB15C9A3BBd27af6474483886F7c0c9AE406;\n }\n }\n } else {\n if (routeId < 47) {\n if (routeId == 45) {\n return\n 0x9580113Cc04e5a0a03359686304EF3A80b936Dd3;\n } else {\n return\n 0xD211c826d568957F3b66a3F4d9c5f68cCc66E619;\n }\n } else {\n if (routeId == 47) {\n return\n 0xCEE24D0635c4C56315d133b031984d4A6f509476;\n } else {\n return\n 0x3922e6B987983229798e7A20095EC372744d4D4c;\n }\n }\n }\n }\n } else {\n if (routeId < 57) {\n if (routeId < 53) {\n if (routeId < 51) {\n if (routeId == 49) {\n return\n 0x2d92D03413d296e1F31450479349757187F2a2b7;\n } else {\n return\n 0x0fe5308eE90FC78F45c89dB6053eA859097860CA;\n }\n } else {\n if (routeId == 51) {\n return\n 0x08Ba68e067C0505bAF0C1311E0cFB2B1B59b969c;\n } else {\n return\n 0x9bee5DdDF75C24897374f92A534B7A6f24e97f4a;\n }\n }\n } else {\n if (routeId < 55) {\n if (routeId == 53) {\n return\n 0x1FC5A90B232208704B930c1edf82FFC6ACc02734;\n } else {\n return\n 0x5b1B0417cb44c761C2a23ee435d011F0214b3C85;\n }\n } else {\n if (routeId == 55) {\n return\n 0x9d70cDaCA12A738C283020760f449D7816D592ec;\n } else {\n return\n 0x95a23b9CB830EcCFDDD5dF56A4ec665e3381Fa12;\n }\n }\n }\n } else {\n if (routeId < 61) {\n if (routeId < 59) {\n if (routeId == 57) {\n return\n 0x483a957Cf1251c20e096C35c8399721D1200A3Fc;\n } else {\n return\n 0xb4AD39Cb293b0Ec7FEDa743442769A7FF04987CD;\n }\n } else {\n if (routeId == 59) {\n return\n 0x4C543AD78c1590D81BAe09Fc5B6Df4132A2461d0;\n } else {\n return\n 0x471d5E5195c563902781734cfe1FF3981F8B6c86;\n }\n }\n } else {\n if (routeId < 63) {\n if (routeId == 61) {\n return\n 0x1B12a54B5E606D95B8B8D123c9Cb09221Ee37584;\n } else {\n return\n 0xE4127cC550baC433646a7D998775a84daC16c7f3;\n }\n } else {\n if (routeId == 63) {\n return\n 0xecb1b55AB12E7dd788D585c6C5cD61B5F87be836;\n } else {\n return\n 0xf91ef487C5A1579f70601b6D347e19756092eEBf;\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 97) {\n if (routeId < 81) {\n if (routeId < 73) {\n if (routeId < 69) {\n if (routeId < 67) {\n if (routeId == 65) {\n return\n 0x34a16a7e9BADEEFD4f056310cbE0b1423Fa1b760;\n } else {\n return\n 0x60E10E80c7680f429dBbC232830BEcd3D623c4CF;\n }\n } else {\n if (routeId == 67) {\n return\n 0x66465285B8D65362A1d86CE00fE2bE949Fd6debF;\n } else {\n return\n 0x5aB231B7e1A3A74a48f67Ab7bde5Cdd4267022E0;\n }\n }\n } else {\n if (routeId < 71) {\n if (routeId == 69) {\n return\n 0x3A1C3633eE79d43366F5c67802a746aFD6b162Ba;\n } else {\n return\n 0x0C4BfCbA8dC3C811437521a80E81e41DAF479039;\n }\n } else {\n if (routeId == 71) {\n return\n 0x6caf25d2e139C5431a1FA526EAf8d73ff2e6252C;\n } else {\n return\n 0x74ad21e09FDa68638CE14A3009A79B6D16574257;\n }\n }\n }\n } else {\n if (routeId < 77) {\n if (routeId < 75) {\n if (routeId == 73) {\n return\n 0xD4923A61008894b99cc1CD3407eF9524f02aA0Ca;\n } else {\n return\n 0x6F159b5EB823BD415886b9271aA2A723a00a1987;\n }\n } else {\n if (routeId == 75) {\n return\n 0x742a8aA42E7bfB4554dE30f4Fb07FFb6f2068863;\n } else {\n return\n 0x4AE9702d3360400E47B446e76DE063ACAb930101;\n }\n }\n } else {\n if (routeId < 79) {\n if (routeId == 77) {\n return\n 0x0E19a0a44ddA7dAD854ec5Cc867d16869c4E80F4;\n } else {\n return\n 0xE021A51968f25148F726E326C88d2556c5647557;\n }\n } else {\n if (routeId == 79) {\n return\n 0x64287BDDDaeF4d94E4599a3D882bed29E6Ada4B6;\n } else {\n return\n 0xcBB57Fd2e19cc7e9D444d5b4325A2F1047d0C73f;\n }\n }\n }\n }\n } else {\n if (routeId < 89) {\n if (routeId < 85) {\n if (routeId < 83) {\n if (routeId == 81) {\n return\n 0x373DE80DF7D82cFF6D76F29581b360C56331e957;\n } else {\n return\n 0x0466356E131AD61596a51F86BAd1C03A328960D8;\n }\n } else {\n if (routeId == 83) {\n return\n 0x01726B960992f1b74311b248E2a922fC707d43A6;\n } else {\n return\n 0x2E21bdf9A4509b89795BCE7E132f248a75814CEc;\n }\n }\n } else {\n if (routeId < 87) {\n if (routeId == 85) {\n return\n 0x769512b23aEfF842379091d3B6E4B5456F631D42;\n } else {\n return\n 0xe7eD9be946a74Ec19325D39C6EEb57887ccB2B0D;\n }\n } else {\n if (routeId == 87) {\n return\n 0xc4D01Ec357c2b511d10c15e6b6974380F0E62e67;\n } else {\n return\n 0x5bC49CC9dD77bECF2fd3A3C55611e84E69AFa3AE;\n }\n }\n }\n } else {\n if (routeId < 93) {\n if (routeId < 91) {\n if (routeId == 89) {\n return\n 0x48bcD879954fA14e7DbdAeb56F79C1e9DDcb69ec;\n } else {\n return\n 0xE929bDde21b462572FcAA4de6F49B9D3246688D0;\n }\n } else {\n if (routeId == 91) {\n return\n 0x85Aae300438222f0e3A9Bc870267a5633A9438bd;\n } else {\n return\n 0x51f72E1096a81C55cd142d66d39B688C657f9Be8;\n }\n }\n } else {\n if (routeId < 95) {\n if (routeId == 93) {\n return\n 0x3A8a05BF68ac54B01E6C0f492abF97465F3d15f9;\n } else {\n return\n 0x145aA67133F0c2C36b9771e92e0B7655f0D59040;\n }\n } else {\n if (routeId == 95) {\n return\n 0xa030315d7DB11F9892758C9e7092D841e0ADC618;\n } else {\n return\n 0xdF1f8d81a3734bdDdEfaC6Ca1596E081e57c3044;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 113) {\n if (routeId < 105) {\n if (routeId < 101) {\n if (routeId < 99) {\n if (routeId == 97) {\n return\n 0xFF2833123B58aa05d04D7fb99f5FB768B2b435F8;\n } else {\n return\n 0xc8f09c1fD751C570233765f71b0e280d74e6e743;\n }\n } else {\n if (routeId == 99) {\n return\n 0x3026DA6Ceca2E5A57A05153653D9212FFAaA49d8;\n } else {\n return\n 0xdE68Ee703dE0D11f67B0cE5891cB4a903de6D160;\n }\n }\n } else {\n if (routeId < 103) {\n if (routeId == 101) {\n return\n 0xE23a7730e81FB4E87A6D0bd9f63EE77ac86C3DA4;\n } else {\n return\n 0x8b1DBe04aD76a7d8bC079cACd3ED4D99B897F4a0;\n }\n } else {\n if (routeId == 103) {\n return\n 0xBB227240FA459b69C6889B2b8cb1BE76F118061f;\n } else {\n return\n 0xC062b9b3f0dB28BB8afAfcD4d075729344114ffe;\n }\n }\n }\n } else {\n if (routeId < 109) {\n if (routeId < 107) {\n if (routeId == 105) {\n return\n 0x553188Aa45f5FDB83EC4Ca485982F8fC082480D1;\n } else {\n return\n 0x0109d83D746EaCb6d4014953D9E12d6ca85e330b;\n }\n } else {\n if (routeId == 107) {\n return\n 0x45B1bEd29812F5bf6711074ACD180B2aeB783AD9;\n } else {\n return\n 0xdA06eC8c19aea31D77F60299678Cba40E743e1aD;\n }\n }\n } else {\n if (routeId < 111) {\n if (routeId == 109) {\n return\n 0x3cC5235c97d975a9b4FD4501B3446c981ea3D855;\n } else {\n return\n 0xa1827267d6Bd989Ff38580aE3d9deff6Acf19163;\n }\n } else {\n if (routeId == 111) {\n return\n 0x3663CAA0433A3D4171b3581Cf2410702840A735A;\n } else {\n return\n 0x7575D0a7614F655BA77C74a72a43bbd4fA6246a3;\n }\n }\n }\n }\n } else {\n if (routeId < 121) {\n if (routeId < 117) {\n if (routeId < 115) {\n if (routeId == 113) {\n return\n 0x2516Defc18bc07089c5dAFf5eafD7B0EF64611E2;\n } else {\n return\n 0xfec5FF08E20fbc107a97Af2D38BD0025b84ee233;\n }\n } else {\n if (routeId == 115) {\n return\n 0x0FB5763a87242B25243e23D73f55945fE787523A;\n } else {\n return\n 0xe4C00db89678dBf8391f430C578Ca857Dd98aDE1;\n }\n }\n } else {\n if (routeId < 119) {\n if (routeId == 117) {\n return\n 0x8F2A22061F9F35E64f14523dC1A5f8159e6a21B7;\n } else {\n return\n 0x18e4b838ae966917E20E9c9c5Ad359cDD38303bB;\n }\n } else {\n if (routeId == 119) {\n return\n 0x61ACb1d3Dcb3e3429832A164Cc0fC9849fb75A4a;\n } else {\n return\n 0x7681e3c8e7A41DCA55C257cc0d1Ae757f5530E65;\n }\n }\n }\n } else {\n if (routeId < 125) {\n if (routeId < 123) {\n if (routeId == 121) {\n return\n 0x806a2AB9748C3D1DB976550890E3f528B7E8Faec;\n } else {\n return\n 0xBDb8A5DD52C2c239fbC31E9d43B763B0197028FF;\n }\n } else {\n if (routeId == 123) {\n return\n 0x474EC9203706010B9978D6bD0b105D36755e4848;\n } else {\n return\n 0x8dfd0D829b303F2239212E591a0F92a32880f36E;\n }\n }\n } else {\n if (routeId < 127) {\n if (routeId == 125) {\n return\n 0xad4BcE9745860B1adD6F1Bd34a916f050E4c82C2;\n } else {\n return\n 0xBC701115b9fe14bC8CC5934cdC92517173e308C4;\n }\n } else {\n if (routeId == 127) {\n return\n 0x0D1918d786Db8546a11aDeD475C98370E06f255E;\n } else {\n return\n 0xee44f57cD6936DB55B99163f3Df367B01EdA785a;\n }\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 193) {\n if (routeId < 161) {\n if (routeId < 145) {\n if (routeId < 137) {\n if (routeId < 133) {\n if (routeId < 131) {\n if (routeId == 129) {\n return\n 0x63044521fe5a1e488D7eD419cD0e35b7C24F2aa7;\n } else {\n return\n 0x410085E73BD85e90d97b84A68C125aDB9F91f85b;\n }\n } else {\n if (routeId == 131) {\n return\n 0x7913fe97E07C7A397Ec274Ab1d4E2622C88EC5D1;\n } else {\n return\n 0x977f9fE93c064DCf54157406DaABC3a722e8184C;\n }\n }\n } else {\n if (routeId < 135) {\n if (routeId == 133) {\n return\n 0xCD2236468722057cFbbABad2db3DEA9c20d5B01B;\n } else {\n return\n 0x17c7287A491cf5Ff81E2678cF2BfAE4333F6108c;\n }\n } else {\n if (routeId == 135) {\n return\n 0x354D9a5Dbf96c71B79a265F03B595C6Fdc04dadd;\n } else {\n return\n 0xb4e409EB8e775eeFEb0344f9eee884cc7ed21c69;\n }\n }\n }\n } else {\n if (routeId < 141) {\n if (routeId < 139) {\n if (routeId == 137) {\n return\n 0xa1a3c4670Ad69D9be4ab2D39D1231FEC2a63b519;\n } else {\n return\n 0x4589A22199870729C1be5CD62EE93BeD858113E6;\n }\n } else {\n if (routeId == 139) {\n return\n 0x8E7b864dB26Bd6C798C38d4Ba36EbA0d6602cF11;\n } else {\n return\n 0xA2D17C7260a4CB7b9854e89Fc367E80E87872a2d;\n }\n }\n } else {\n if (routeId < 143) {\n if (routeId == 141) {\n return\n 0xC7F0EDf0A1288627b0432304918A75e9084CBD46;\n } else {\n return\n 0xE4B4EF1f9A4aBFEdB371fA7a6143993B15d4df25;\n }\n } else {\n if (routeId == 143) {\n return\n 0xfe3D84A2Ef306FEBb5452441C9BDBb6521666F6A;\n } else {\n return\n 0x8A12B6C64121920110aE58F7cd67DfEc21c6a4C3;\n }\n }\n }\n }\n } else {\n if (routeId < 153) {\n if (routeId < 149) {\n if (routeId < 147) {\n if (routeId == 145) {\n return\n 0x76c4d9aFC4717a2BAac4e5f26CccF02351f7a3DA;\n } else {\n return\n 0xd4719BA550E397aeAcca1Ad2201c1ba69024FAAf;\n }\n } else {\n if (routeId == 147) {\n return\n 0x9646126Ce025224d1682C227d915a386efc0A1Fb;\n } else {\n return\n 0x4DD8Af2E3F2044842f0247920Bc4BABb636915ea;\n }\n }\n } else {\n if (routeId < 151) {\n if (routeId == 149) {\n return\n 0x8e8a327183Af0cf8C2ece9F0ed547C42A160D409;\n } else {\n return\n 0x9D49614CaE1C685C71678CA6d8CDF7584bfd0740;\n }\n } else {\n if (routeId == 151) {\n return\n 0x5a00ef257394cbc31828d48655E3d39e9c11c93d;\n } else {\n return\n 0xC9a2751b38d3dDD161A41Ca0135C5C6c09EC1d56;\n }\n }\n }\n } else {\n if (routeId < 157) {\n if (routeId < 155) {\n if (routeId == 153) {\n return\n 0x7e1c261640a525C94Ca4f8c25b48CF754DD83590;\n } else {\n return\n 0x409Fe24ba6F6BD5aF31C1aAf8059b986A3158233;\n }\n } else {\n if (routeId == 155) {\n return\n 0x704Cf5BFDADc0f55fDBb53B6ed8B582E018A72A2;\n } else {\n return\n 0x3982bF65d7d6E77E3b6661cd6F6468c247512737;\n }\n }\n } else {\n if (routeId < 159) {\n if (routeId == 157) {\n return\n 0x3982b9f26FFD67a13Ee371e2C0a9Da338BA70E7f;\n } else {\n return\n 0x6D834AB385900c1f49055D098e90264077FbC4f2;\n }\n } else {\n if (routeId == 159) {\n return\n 0x11FE5F70779A094B7166B391e1Fb73d422eF4e4d;\n } else {\n return\n 0xD347e4E47280d21F13B73D89c6d16f867D50DD13;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 177) {\n if (routeId < 169) {\n if (routeId < 165) {\n if (routeId < 163) {\n if (routeId == 161) {\n return\n 0xb6035eDD53DDA28d8B69b4ae9836E40C80306CD7;\n } else {\n return\n 0x54c884e6f5C7CcfeCA990396c520C858c922b6CA;\n }\n } else {\n if (routeId == 163) {\n return\n 0x5eA93E240b083d686558Ed607BC013d88057cE46;\n } else {\n return\n 0x4C7131eE812De685cBe4e2cCb033d46ecD46612E;\n }\n }\n } else {\n if (routeId < 167) {\n if (routeId == 165) {\n return\n 0xc1a5Be9F0c33D8483801D702111068669f81fF91;\n } else {\n return\n 0x9E5fAb91455Be5E5b2C05967E73F456c8118B1Fc;\n }\n } else {\n if (routeId == 167) {\n return\n 0x3d9A05927223E0DC2F382831770405885e22F0d8;\n } else {\n return\n 0x6303A011fB6063f5B1681cb5a9938EA278dc6128;\n }\n }\n }\n } else {\n if (routeId < 173) {\n if (routeId < 171) {\n if (routeId == 169) {\n return\n 0xe9c60795c90C66797e4c8E97511eA07CdAda32bE;\n } else {\n return\n 0xD56cC98e69A1e13815818b466a8aA6163d84234A;\n }\n } else {\n if (routeId == 171) {\n return\n 0x47EbB9D36a6e40895316cD894E4860D774E2c531;\n } else {\n return\n 0xA5EB293629410065d14a7B1663A67829b0618292;\n }\n }\n } else {\n if (routeId < 175) {\n if (routeId == 173) {\n return\n 0x1b3B4C8146F939cE00899db8B3ddeF0062b7E023;\n } else {\n return\n 0x257Bbc11653625EbfB6A8587eF4f4FBe49828EB3;\n }\n } else {\n if (routeId == 175) {\n return\n 0x44cc979C01b5bB1eAC21301E73C37200dFD06F59;\n } else {\n return\n 0x2972fDF43352225D82754C0174Ff853819D1ef2A;\n }\n }\n }\n }\n } else {\n if (routeId < 185) {\n if (routeId < 181) {\n if (routeId < 179) {\n if (routeId == 177) {\n return\n 0x3e54144f032648A04D62d79f7B4b93FF3aC2333b;\n } else {\n return\n 0x444016102dB8adbE73C3B6703a1ea7F2f75A510D;\n }\n } else {\n if (routeId == 179) {\n return\n 0xac079143f98a6eb744Fde34541ebF243DF5B5dED;\n } else {\n return\n 0xAe9010767Fb112d29d35CEdfba2b372Ad7A308d3;\n }\n }\n } else {\n if (routeId < 183) {\n if (routeId == 181) {\n return\n 0xfE0BCcF9cCC2265D5fB3450743f17DfE57aE1e56;\n } else {\n return\n 0x04ED8C0545716119437a45386B1d691C63234C7D;\n }\n } else {\n if (routeId == 183) {\n return\n 0x636c14013e531A286Bc4C848da34585f0bB73d59;\n } else {\n return\n 0x2Fa67fc7ECC5cAA01C653d3BFeA98ecc5db9C42A;\n }\n }\n }\n } else {\n if (routeId < 189) {\n if (routeId < 187) {\n if (routeId == 185) {\n return\n 0x23e9a0FC180818aA872D2079a985217017E97bd9;\n } else {\n return\n 0x79A95c3Ef81b3ae64ee03A9D5f73e570495F164E;\n }\n } else {\n if (routeId == 187) {\n return\n 0xa7EA0E88F04a84ba0ad1E396cb07Fa3fDAD7dF6D;\n } else {\n return\n 0xd23cA1278a2B01a3C0Ca1a00d104b11c1Ebe6f42;\n }\n }\n } else {\n if (routeId < 191) {\n if (routeId == 189) {\n return\n 0x707bc4a9FA2E349AED5df4e9f5440C15aA9D14Bd;\n } else {\n return\n 0x7E290F2dd539Ac6CE58d8B4C2B944931a1fD3612;\n }\n } else {\n if (routeId == 191) {\n return\n 0x707AA5503088Ce06Ba450B6470A506122eA5c8eF;\n } else {\n return\n 0xFbB3f7BF680deeb149f4E7BC30eA3DDfa68F3C3f;\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 225) {\n if (routeId < 209) {\n if (routeId < 201) {\n if (routeId < 197) {\n if (routeId < 195) {\n if (routeId == 193) {\n return\n 0xDE74aD8cCC3dbF14992f49Cf24f36855912f4934;\n } else {\n return\n 0x409BA83df7777F070b2B50a10a41DE2468d2a3B3;\n }\n } else {\n if (routeId == 195) {\n return\n 0x5CB7Be90A5DD7CfDa54e87626e254FE8C18255B4;\n } else {\n return\n 0x0A684fE12BC64fb72B59d0771a566F49BC090356;\n }\n }\n } else {\n if (routeId < 199) {\n if (routeId == 197) {\n return\n 0xDf30048d91F8FA2bCfC54952B92bFA8e161D3360;\n } else {\n return\n 0x050825Fff032a547C47061CF0696FDB0f65AEa5D;\n }\n } else {\n if (routeId == 199) {\n return\n 0xd55e671dAC1f03d366d8535073ada5DB2Aab1Ea2;\n } else {\n return\n 0x9470C704A9616c8Cd41c595Fcd2181B6fe2183C2;\n }\n }\n }\n } else {\n if (routeId < 205) {\n if (routeId < 203) {\n if (routeId == 201) {\n return\n 0x2D9ffD275181F5865d5e11CbB4ced1521C4dF9f1;\n } else {\n return\n 0x816d28Dec10ec95DF5334f884dE85cA6215918d8;\n }\n } else {\n if (routeId == 203) {\n return\n 0xd1f87267c4A43835E666dd69Df077e578A3b6299;\n } else {\n return\n 0x39E89Bde9DACbe5468C025dE371FbDa12bDeBAB1;\n }\n }\n } else {\n if (routeId < 207) {\n if (routeId == 205) {\n return\n 0x7b40A3207956ecad6686E61EfcaC48912FcD0658;\n } else {\n return\n 0x090cF10D793B1Efba9c7D76115878814B663859A;\n }\n } else {\n if (routeId == 207) {\n return\n 0x312A59c06E41327878F2063eD0e9c282C1DA3AfC;\n } else {\n return\n 0x4F1188f46236DD6B5de11Ebf2a9fF08716E7DeB6;\n }\n }\n }\n }\n } else {\n if (routeId < 217) {\n if (routeId < 213) {\n if (routeId < 211) {\n if (routeId == 209) {\n return\n 0x0A6F9a3f4fA49909bBfb4339cbE12B42F53BbBeD;\n } else {\n return\n 0x01d13d7aCaCbB955B81935c80ffF31e14BdFa71f;\n }\n } else {\n if (routeId == 211) {\n return\n 0x691a14Fa6C7360422EC56dF5876f84d4eDD7f00A;\n } else {\n return\n 0x97Aad18d886d181a9c726B3B6aE15a0A69F5aF73;\n }\n }\n } else {\n if (routeId < 215) {\n if (routeId == 213) {\n return\n 0x2917241371D2099049Fa29432DC46735baEC33b4;\n } else {\n return\n 0x5F20F20F7890c2e383E29D4147C9695A371165f5;\n }\n } else {\n if (routeId == 215) {\n return\n 0xeC0a60e639958335662C5219A320cCEbb56C6077;\n } else {\n return\n 0x96d63CF5062975C09845d17ec672E10255866053;\n }\n }\n }\n } else {\n if (routeId < 221) {\n if (routeId < 219) {\n if (routeId == 217) {\n return\n 0xFF57429e57D383939CAB50f09ABBfB63C0e6c9AD;\n } else {\n return\n 0x18E393A7c8578fb1e235C242076E50013cDdD0d7;\n }\n } else {\n if (routeId == 219) {\n return\n 0xE7E5238AF5d61f52E9B4ACC025F713d1C0216507;\n } else {\n return\n 0x428401D4d0F25A2EE1DA4d5366cB96Ded425D9bD;\n }\n }\n } else {\n if (routeId < 223) {\n if (routeId == 221) {\n return\n 0x42E5733551ff1Ee5B48Aa9fc2B61Af9b58C812E6;\n } else {\n return\n 0x64Df9c7A0551B056d860Bc2419Ca4c1EF75320bE;\n }\n } else {\n if (routeId == 223) {\n return\n 0x46006925506145611bBf0263243D8627dAf26B0F;\n } else {\n return\n 0x8D64BE884314662804eAaB884531f5C50F4d500c;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 241) {\n if (routeId < 233) {\n if (routeId < 229) {\n if (routeId < 227) {\n if (routeId == 225) {\n return\n 0x157a62D92D07B5ce221A5429645a03bBaCE85373;\n } else {\n return\n 0xaF037D33e1F1F2F87309B425fe8a9d895Ef3722B;\n }\n } else {\n if (routeId == 227) {\n return\n 0x921D1154E494A2f7218a37ad7B17701f94b4B40e;\n } else {\n return\n 0xF282b4555186d8Dea51B8b3F947E1E0568d09bc4;\n }\n }\n } else {\n if (routeId < 231) {\n if (routeId == 229) {\n return\n 0xa794E2E1869765a4600b3DFd8a4ebcF16350f6B6;\n } else {\n return\n 0xFEFb048e20c5652F7940A49B1980E0125Ec4D358;\n }\n } else {\n if (routeId == 231) {\n return\n 0x220104b641971e9b25612a8F001bf48AbB23f1cF;\n } else {\n return\n 0xcB9D373Bb54A501B35dd3be5bF4Ba43cA31F7035;\n }\n }\n }\n } else {\n if (routeId < 237) {\n if (routeId < 235) {\n if (routeId == 233) {\n return\n 0x37D627F56e3FF36aC316372109ea82E03ac97DAc;\n } else {\n return\n 0x4E81355FfB4A271B4EA59ff78da2b61c7833161f;\n }\n } else {\n if (routeId == 235) {\n return\n 0xADd8D65cAF6Cc9ad73127B49E16eA7ac29d91e87;\n } else {\n return\n 0x630F9b95626487dfEAe3C97A44DB6C59cF35d996;\n }\n }\n } else {\n if (routeId < 239) {\n if (routeId == 237) {\n return\n 0x78CE2BC8238B679680A67FCB98C5A60E4ec17b2D;\n } else {\n return\n 0xA38D776028eD1310b9A6b086f67F788201762E21;\n }\n } else {\n if (routeId == 239) {\n return\n 0x7Bb5178827B76B86753Ed62a0d662c72cEcb1bD3;\n } else {\n return\n 0x4faC26f61C76eC5c3D43b43eDfAFF0736Ae0e3da;\n }\n }\n }\n }\n } else {\n if (routeId < 249) {\n if (routeId < 245) {\n if (routeId < 243) {\n if (routeId == 241) {\n return\n 0x791Bb49bfFA7129D6889FDB27744422Ac4571A85;\n } else {\n return\n 0x26766fFEbb5fa564777913A6f101dF019AB32afa;\n }\n } else {\n if (routeId == 243) {\n return\n 0x05e98E5e95b4ECBbbAf3258c3999Cc81ed8048Be;\n } else {\n return\n 0xC5c4621e52f1D6A1825A5ed4F95855401a3D9C6b;\n }\n }\n } else {\n if (routeId < 247) {\n if (routeId == 245) {\n return\n 0xfcb15f909BA7FC7Ea083503Fb4c1020203c107EB;\n } else {\n return\n 0xbD27603279d969c74f2486ad14E71080829DFd38;\n }\n } else {\n if (routeId == 247) {\n return\n 0xff2f756BcEcC1A55BFc09a30cc5F64720458cFCB;\n } else {\n return\n 0x3bfB968FEbC12F4e8420B2d016EfcE1E615f7246;\n }\n }\n }\n } else {\n if (routeId < 253) {\n if (routeId < 251) {\n if (routeId == 249) {\n return\n 0x982EE9Ffe23051A2ec945ed676D864fa8345222b;\n } else {\n return\n 0xe101899100785E74767d454FFF0131277BaD48d9;\n }\n } else {\n if (routeId == 251) {\n return\n 0x4F730C0c6b3B5B7d06ca511379f4Aa5BfB2E9525;\n } else {\n return\n 0x5499c36b365795e4e0Ef671aF6C2ce26D7c78265;\n }\n }\n } else {\n if (routeId < 255) {\n if (routeId == 253) {\n return\n 0x8AF51F7237Fc8fB2fc3E700488a94a0aC6Ad8b5a;\n } else {\n return\n 0xda8716df61213c0b143F2849785FB85928084857;\n }\n } else {\n if (routeId == 255) {\n return\n 0xF040Cf9b1ebD11Bf28e04e80740DF3DDe717e4f5;\n } else {\n return\n 0xB87ba32f759D14023C7520366B844dF7f0F036C2;\n }\n }\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 321) {\n if (routeId < 289) {\n if (routeId < 273) {\n if (routeId < 265) {\n if (routeId < 261) {\n if (routeId < 259) {\n if (routeId == 257) {\n return\n 0x0Edde681b8478F0c3194f468EdD2dB5e75c65CDD;\n } else {\n return\n 0x59C70900Fca06eE2aCE1BDd5A8D0Af0cc3BBA720;\n }\n } else {\n if (routeId == 259) {\n return\n 0x8041F0f180D17dD07087199632c45E17AeB0BAd5;\n } else {\n return\n 0x4fB4727064BA595995DD516b63b5921Df9B93aC6;\n }\n }\n } else {\n if (routeId < 263) {\n if (routeId == 261) {\n return\n 0x86e98b594565857eD098864F560915C0dAfd6Ea1;\n } else {\n return\n 0x70f8818E8B698EFfeCd86A513a4c87c0c380Bef6;\n }\n } else {\n if (routeId == 263) {\n return\n 0x78Ed227c8A897A21Da2875a752142dd80d865158;\n } else {\n return\n 0xd02A30BB5C3a8C51d2751A029a6fcfDE2Af9fbc6;\n }\n }\n }\n } else {\n if (routeId < 269) {\n if (routeId < 267) {\n if (routeId == 265) {\n return\n 0x0F00d5c5acb24e975e2a56730609f7F40aa763b8;\n } else {\n return\n 0xC3e2091edc2D3D9D98ba09269138b617B536834A;\n }\n } else {\n if (routeId == 267) {\n return\n 0xa6FbaF7F30867C9633908998ea8C3da28920E75C;\n } else {\n return\n 0xE6dDdcD41E2bBe8122AE32Ac29B8fbAB79CD21d9;\n }\n }\n } else {\n if (routeId < 271) {\n if (routeId == 269) {\n return\n 0x537aa8c1Ef6a8Eaf039dd6e1Eb67694a48195cE4;\n } else {\n return\n 0x96ABAC485fd2D0B03CF4a10df8BD58b8dED28300;\n }\n } else {\n if (routeId == 271) {\n return\n 0xda8e7D46d04Bd4F62705Cd80355BDB6d441DafFD;\n } else {\n return\n 0xbE50018E7a5c67E2e5f5414393e971CC96F293f2;\n }\n }\n }\n }\n } else {\n if (routeId < 281) {\n if (routeId < 277) {\n if (routeId < 275) {\n if (routeId == 273) {\n return\n 0xa1b3907D6CB542a4cbe2eE441EfFAA909FAb62C3;\n } else {\n return\n 0x6d08ee8511C0237a515013aC389e7B3968Cb1753;\n }\n } else {\n if (routeId == 275) {\n return\n 0x22faa5B5Fe43eAdbB52745e35a5cdA8bD5F96bbA;\n } else {\n return\n 0x7a673eB74D79e4868D689E7852abB5f93Ec2fD4b;\n }\n }\n } else {\n if (routeId < 279) {\n if (routeId == 277) {\n return\n 0x0b8531F8AFD4190b76F3e10deCaDb84c98b4d419;\n } else {\n return\n 0x78eABC743A93583DeE403D6b84795490e652216B;\n }\n } else {\n if (routeId == 279) {\n return\n 0x3A95D907b2a7a8604B59BccA08585F58Afe0Aa64;\n } else {\n return\n 0xf4271f0C8c9Af0F06A80b8832fa820ccE64FAda8;\n }\n }\n }\n } else {\n if (routeId < 285) {\n if (routeId < 283) {\n if (routeId == 281) {\n return\n 0x74b2DF841245C3748c0d31542e1335659a25C33b;\n } else {\n return\n 0xdFC99Fd0Ad7D16f30f295a5EEFcE029E04d0fa65;\n }\n } else {\n if (routeId == 283) {\n return\n 0xE992416b6aC1144eD8148a9632973257839027F6;\n } else {\n return\n 0x54ce55ba954E981BB1fd9399054B35Ce1f2C0816;\n }\n }\n } else {\n if (routeId < 287) {\n if (routeId == 285) {\n return\n 0xD4AB52f9e7E5B315Bd7471920baD04F405Ab1c38;\n } else {\n return\n 0x3670C990994d12837e95eE127fE2f06FD3E2104B;\n }\n } else {\n if (routeId == 287) {\n return\n 0xDcf190B09C47E4f551E30BBb79969c3FdEA1e992;\n } else {\n return\n 0xa65057B967B59677237e57Ab815B209744b9bc40;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 305) {\n if (routeId < 297) {\n if (routeId < 293) {\n if (routeId < 291) {\n if (routeId == 289) {\n return\n 0x6Efc86B40573e4C7F28659B13327D55ae955C483;\n } else {\n return\n 0x06BcC25CF8e0E72316F53631b3aA7134E9f73Ae0;\n }\n } else {\n if (routeId == 291) {\n return\n 0x710b6414E1D53882b1FCD3A168aD5Ccd435fc6D0;\n } else {\n return\n 0x5Ebb2C3d78c4e9818074559e7BaE7FCc99781DC1;\n }\n }\n } else {\n if (routeId < 295) {\n if (routeId == 293) {\n return\n 0xAf0a409c3AEe0bD08015cfb29D89E90b6e89A88F;\n } else {\n return\n 0x522559d8b99773C693B80cE06DF559036295Ce44;\n }\n } else {\n if (routeId == 295) {\n return\n 0xB65290A5Bae838aaa7825c9ECEC68041841a1B64;\n } else {\n return\n 0x801b8F2068edd5Bcb659E6BDa0c425909043C420;\n }\n }\n }\n } else {\n if (routeId < 301) {\n if (routeId < 299) {\n if (routeId == 297) {\n return\n 0x29b5F00515d093627E0B7bd0b5c8E84F6b4cDb87;\n } else {\n return\n 0x652839Ae74683cbF9f1293F1019D938F87464D3E;\n }\n } else {\n if (routeId == 299) {\n return\n 0x5Bc95dCebDDE9B79F2b6DC76121BC7936eF8D666;\n } else {\n return\n 0x90db359CEA62E53051158Ab5F99811C0a07Fe686;\n }\n }\n } else {\n if (routeId < 303) {\n if (routeId == 301) {\n return\n 0x2c3625EedadbDcDbB5330eb0d17b3C39ff269807;\n } else {\n return\n 0xC3f0324471b5c9d415acD625b8d8694a4e48e001;\n }\n } else {\n if (routeId == 303) {\n return\n 0x8C60e7E05fa0FfB6F720233736f245134685799d;\n } else {\n return\n 0x98fAF2c09aa4EBb995ad0B56152993E7291a500e;\n }\n }\n }\n }\n } else {\n if (routeId < 313) {\n if (routeId < 309) {\n if (routeId < 307) {\n if (routeId == 305) {\n return\n 0x802c1063a861414dFAEc16bacb81429FC0d40D6e;\n } else {\n return\n 0x11C4AeFCC0dC156f64195f6513CB1Fb3Be0Ae056;\n }\n } else {\n if (routeId == 307) {\n return\n 0xEff1F3258214E31B6B4F640b4389d55715C3Be2B;\n } else {\n return\n 0x47e379Abe8DDFEA4289aBa01235EFF7E93758fd7;\n }\n }\n } else {\n if (routeId < 311) {\n if (routeId == 309) {\n return\n 0x3CC26384c3eA31dDc8D9789e8872CeA6F20cD3ff;\n } else {\n return\n 0xEdd9EFa6c69108FAA4611097d643E20Ba0Ed1634;\n }\n } else {\n if (routeId == 311) {\n return\n 0xCb93525CA5f3D371F74F3D112bC19526740717B8;\n } else {\n return\n 0x7071E0124EB4438137e60dF1b8DD8Af1BfB362cF;\n }\n }\n }\n } else {\n if (routeId < 317) {\n if (routeId < 315) {\n if (routeId == 313) {\n return\n 0x4691096EB0b78C8F4b4A8091E5B66b18e1835c10;\n } else {\n return\n 0x8d953c9b2d1C2137CF95992079f3A77fCd793272;\n }\n } else {\n if (routeId == 315) {\n return\n 0xbdCc2A3Bf6e3Ba49ff86595e6b2b8D70d8368c92;\n } else {\n return\n 0x95E6948aB38c61b2D294E8Bd896BCc4cCC0713cf;\n }\n }\n } else {\n if (routeId < 319) {\n if (routeId == 317) {\n return\n 0x607b27C881fFEE4Cb95B1c5862FaE7224ccd0b4A;\n } else {\n return\n 0x09D28aFA166e566A2Ee1cB834ea8e78C7E627eD2;\n }\n } else {\n if (routeId == 319) {\n return\n 0x9c01449b38bDF0B263818401044Fb1401B29fDfA;\n } else {\n return\n 0x1F7723599bbB658c051F8A39bE2688388d22ceD6;\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 353) {\n if (routeId < 337) {\n if (routeId < 329) {\n if (routeId < 325) {\n if (routeId < 323) {\n if (routeId == 321) {\n return\n 0x52B71603f7b8A5d15B4482e965a0619aa3210194;\n } else {\n return\n 0x01c0f072CB210406653752FecFA70B42dA9173a2;\n }\n } else {\n if (routeId == 323) {\n return\n 0x3021142f021E943e57fc1886cAF58D06147D09A6;\n } else {\n return\n 0xe6f2AF38e76AB09Db59225d97d3E770942D3D842;\n }\n }\n } else {\n if (routeId < 327) {\n if (routeId == 325) {\n return\n 0x06a25554e5135F08b9e2eD1DEC1fc3CEd52e0B48;\n } else {\n return\n 0x71d75e670EE3511C8290C705E0620126B710BF8D;\n }\n } else {\n if (routeId == 327) {\n return\n 0x8b9cE142b80FeA7c932952EC533694b1DF9B3c54;\n } else {\n return\n 0xd7Be24f32f39231116B3fDc483C2A12E1521f73B;\n }\n }\n }\n } else {\n if (routeId < 333) {\n if (routeId < 331) {\n if (routeId == 329) {\n return\n 0xb40cafBC4797d4Ff64087E087F6D2e661f954CbE;\n } else {\n return\n 0xBdDCe7771EfEe81893e838f62204A4c76D72757e;\n }\n } else {\n if (routeId == 331) {\n return\n 0x5d3D299EA7Fd4F39AcDb336E26631Dfee41F9287;\n } else {\n return\n 0x6BfEE09E1Fc0684e0826A9A0dC1352a14B136FAC;\n }\n }\n } else {\n if (routeId < 335) {\n if (routeId == 333) {\n return\n 0xd0001bB8E2Cb661436093f96458a4358B5156E3c;\n } else {\n return\n 0x1867c6485CfD1eD448988368A22bfB17a7747293;\n }\n } else {\n if (routeId == 335) {\n return\n 0x8997EF9F95dF24aB67703AB6C262aABfeEBE33bD;\n } else {\n return\n 0x1e39E9E601922deD91BCFc8F78836302133465e2;\n }\n }\n }\n }\n } else {\n if (routeId < 345) {\n if (routeId < 341) {\n if (routeId < 339) {\n if (routeId == 337) {\n return\n 0x8A8ec6CeacFf502a782216774E5AF3421562C6ff;\n } else {\n return\n 0x3B8FC561df5415c8DC01e97Ee6E38435A8F9C40A;\n }\n } else {\n if (routeId == 339) {\n return\n 0xD5d5f5B37E67c43ceA663aEDADFFc3a93a2065B0;\n } else {\n return\n 0xCC8F55EC43B4f25013CE1946FBB740c43Be5B96D;\n }\n }\n } else {\n if (routeId < 343) {\n if (routeId == 341) {\n return\n 0x18f586E816eEeDbb57B8011239150367561B58Fb;\n } else {\n return\n 0xd0CD802B19c1a52501cb2f07d656e3Cd7B0Ce124;\n }\n } else {\n if (routeId == 343) {\n return\n 0xe0AeD899b39C6e4f2d83e4913a1e9e0cf6368abE;\n } else {\n return\n 0x0606e1b6c0f1A398C38825DCcc4678a7Cbc2737c;\n }\n }\n }\n } else {\n if (routeId < 349) {\n if (routeId < 347) {\n if (routeId == 345) {\n return\n 0x2d188e85b27d18EF80f16686EA1593ABF7Ed2A63;\n } else {\n return\n 0x64412292fA4A135a3300E24366E99ff59Db2eAc1;\n }\n } else {\n if (routeId == 347) {\n return\n 0x38b74c173f3733E8b90aAEf0e98B89791266149F;\n } else {\n return\n 0x36DAA49A79aaEF4E7a217A11530D3cCD84414124;\n }\n }\n } else {\n if (routeId < 351) {\n if (routeId == 349) {\n return\n 0x10f088FE2C88F90270E4449c46c8B1b232511d58;\n } else {\n return\n 0x4FeDbd25B58586838ABD17D10272697dF1dC3087;\n }\n } else {\n if (routeId == 351) {\n return\n 0x685278209248CB058E5cEe93e37f274A80Faf6eb;\n } else {\n return\n 0xDd9F8F1eeC3955f78168e2Fb2d1e808fa8A8f15b;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 369) {\n if (routeId < 361) {\n if (routeId < 357) {\n if (routeId < 355) {\n if (routeId == 353) {\n return\n 0x7392aEeFD5825aaC28817031dEEBbFaAA20983D9;\n } else {\n return\n 0x0Cc182555E00767D6FB8AD161A10d0C04C476d91;\n }\n } else {\n if (routeId == 355) {\n return\n 0x90E52837d56715c79FD592E8D58bFD20365798b2;\n } else {\n return\n 0x6F4451DE14049B6770ad5BF4013118529e68A40C;\n }\n }\n } else {\n if (routeId < 359) {\n if (routeId == 357) {\n return\n 0x89B97ef2aFAb9ed9c7f0FDb095d02E6840b52d9c;\n } else {\n return\n 0x92A5cC5C42d94d3e23aeB1214fFf43Db2B97759E;\n }\n } else {\n if (routeId == 359) {\n return\n 0x63ddc52F135A1dcBA831EAaC11C63849F018b739;\n } else {\n return\n 0x692A691533B571C2c54C1D7F8043A204b3d8120E;\n }\n }\n }\n } else {\n if (routeId < 365) {\n if (routeId < 363) {\n if (routeId == 361) {\n return\n 0x97c7492CF083969F61C6f302d45c8270391b921c;\n } else {\n return\n 0xDeFD2B8643553dAd19548eB14fd94A57F4B9e543;\n }\n } else {\n if (routeId == 363) {\n return\n 0x30645C04205cA3f670B67b02F971B088930ACB8C;\n } else {\n return\n 0xA6f80ed2d607Cd67aEB4109B64A0BEcc4D7d03CF;\n }\n }\n } else {\n if (routeId < 367) {\n if (routeId == 365) {\n return\n 0xBbbbC6c276eB3F7E674f2D39301509236001c42f;\n } else {\n return\n 0xC20E77d349FB40CE88eB01824e2873ad9f681f3C;\n }\n } else {\n if (routeId == 367) {\n return\n 0x5fCfD9a962De19294467C358C1FA55082285960b;\n } else {\n return\n 0x4D87BD6a0E4E5cc6332923cb3E85fC71b287F58A;\n }\n }\n }\n }\n } else {\n if (routeId < 377) {\n if (routeId < 373) {\n if (routeId < 371) {\n if (routeId == 369) {\n return\n 0x3AA5B757cd6Dde98214E56D57Dde7fcF0F7aB04E;\n } else {\n return\n 0xe28eFCE7192e11a2297f44059113C1fD6967b2d4;\n }\n } else {\n if (routeId == 371) {\n return\n 0x3251cAE10a1Cf246e0808D76ACC26F7B5edA0eE5;\n } else {\n return\n 0xbA2091cc9357Cf4c4F25D64F30d1b4Ba3A5a174B;\n }\n }\n } else {\n if (routeId < 375) {\n if (routeId == 373) {\n return\n 0x49c8e1Da9693692096F63C82D11b52d738566d55;\n } else {\n return\n 0xA0731615aB5FFF451031E9551367A4F7dB27b39c;\n }\n } else {\n if (routeId == 375) {\n return\n 0xFb214541888671AE1403CecC1D59763a12fc1609;\n } else {\n return\n 0x1D6bCB17642E2336405df73dF22F07688cAec020;\n }\n }\n }\n } else {\n if (routeId < 381) {\n if (routeId < 379) {\n if (routeId == 377) {\n return\n 0xfC9c0C7bfe187120fF7f4E21446161794A617a9e;\n } else {\n return\n 0xBa5bF37678EeE2dAB17AEf9D898153258252250E;\n }\n } else {\n if (routeId == 379) {\n return\n 0x7c55690bd2C9961576A32c02f8EB29ed36415Ec7;\n } else {\n return\n 0xcA40073E868E8Bc611aEc8Fe741D17E68Fe422f6;\n }\n }\n } else {\n if (routeId < 383) {\n if (routeId == 381) {\n return\n 0x31641bAFb87E9A58f78835050a7BE56921986339;\n } else {\n return\n 0xA54766424f6dA74b45EbCc5Bf0Bd1D74D2CCcaAB;\n }\n } else {\n if (routeId == 383) {\n return\n 0xc7bBa57F8C179EDDBaa62117ddA360e28f3F8252;\n } else {\n return\n 0x5e663ED97ea77d393B8858C90d0683bF180E0ffd;\n }\n }\n }\n }\n }\n }\n }\n }\n }\n\n if (routes[routeId] == address(0)) revert ZeroAddressNotAllowed();\n return routes[routeId];\n }\n\n /// @notice fallback function to handle swap, bridge execution\n /// @dev ensure routeId is converted to bytes4 and sent as msg.sig in the transaction\n fallback() external payable {\n address routeAddress = addressAt(uint32(msg.sig));\n\n bytes memory result;\n\n assembly {\n // copy function selector and any arguments\n calldatacopy(0, 4, sub(calldatasize(), 4))\n // execute function call using the facet\n result := delegatecall(\n gas(),\n routeAddress,\n 0,\n sub(calldatasize(), 4),\n 0,\n 0\n )\n // get any return value\n returndatacopy(0, 0, returndatasize())\n // return any return value or error back to the caller\n switch result\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n}\n" + }, + "src/bridges/hop/interfaces/IHopL1Bridge.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/**\n * @title L1Bridge Hop Interface\n * @notice L1 Hop Bridge, Used to transfer from L1 to L2s.\n */\ninterface IHopL1Bridge {\n /**\n * @notice `amountOutMin` and `deadline` should be 0 when no swap is intended at the destination.\n * @notice `amount` is the total amount the user wants to send including the relayer fee\n * @dev Send tokens to a supported layer-2 to mint hToken and optionally swap the hToken in the\n * AMM at the destination.\n * @param chainId The chainId of the destination chain\n * @param recipient The address receiving funds at the destination\n * @param amount The amount being sent\n * @param amountOutMin The minimum amount received after attempting to swap in the destination\n * AMM market. 0 if no swap is intended.\n * @param deadline The deadline for swapping in the destination AMM market. 0 if no\n * swap is intended.\n * @param relayer The address of the relayer at the destination.\n * @param relayerFee The amount distributed to the relayer at the destination. This is subtracted from the `amount`.\n */\n function sendToL2(\n uint256 chainId,\n address recipient,\n uint256 amount,\n uint256 amountOutMin,\n uint256 deadline,\n address relayer,\n uint256 relayerFee\n ) external payable;\n}\n" + }, + "src/bridges/refuel/interfaces/refuel.sol": { + "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity >=0.8.0;\n\n/// @notice interface with functions to interact with Refuel contract\ninterface IRefuel {\n /**\n * @notice function to deposit nativeToken to Destination-address on destinationChain\n * @param destinationChainId chainId of the Destination chain\n * @param _to recipient address\n */\n function depositNativeToken(\n uint256 destinationChainId,\n address _to\n ) external payable;\n}\n" + }, + "src/bridges/stargate/interfaces/stargate.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\n/**\n * @title IBridgeStargate Interface Contract.\n * @notice Interface used by Stargate-L1 and L2 Router implementations\n * @dev router and routerETH addresses will be distinct for L1 and L2\n */\ninterface IBridgeStargate {\n // @notice Struct to hold the additional-data for bridging ERC20 token\n struct lzTxObj {\n // gas limit to bridge the token in Stargate to destinationChain\n uint256 dstGasForCall;\n // destination nativeAmount, this is always set as 0\n uint256 dstNativeAmount;\n // destination nativeAddress, this is always set as 0x\n bytes dstNativeAddr;\n }\n\n /// @notice function in stargate bridge which is used to bridge ERC20 tokens to recipient on destinationChain\n function swap(\n uint16 _dstChainId,\n uint256 _srcPoolId,\n uint256 _dstPoolId,\n address payable _refundAddress,\n uint256 _amountLD,\n uint256 _minAmountLD,\n lzTxObj memory _lzTxParams,\n bytes calldata _to,\n bytes calldata _payload\n ) external payable;\n\n /// @notice function in stargate bridge which is used to bridge native tokens to recipient on destinationChain\n function swapETH(\n uint16 _dstChainId, // destination Stargate chainId\n address payable _refundAddress, // refund additional messageFee to this address\n bytes calldata _toAddress, // the receiver of the destination ETH\n uint256 _amountLD, // the amount, in Local Decimals, to be swapped\n uint256 _minAmountLD // the minimum amount accepted out on destination\n ) external payable;\n}\n" + }, + "src/controllers/FeesTakerController.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {BaseController} from \"./BaseController.sol\";\nimport {ISocketRequest} from \"../interfaces/ISocketRequest.sol\";\n\n/**\n * @title FeesTaker-Controller Implementation\n * @notice Controller with composed actions to deduct-fees followed by Refuel, Swap and Bridge\n * to be executed Sequentially and this is atomic\n * @author Socket dot tech.\n */\ncontract FeesTakerController is BaseController {\n using SafeTransferLib for ERC20;\n\n /// @notice event emitted upon fee-deduction to fees-taker address\n event SocketFeesDeducted(\n uint256 fees,\n address feesToken,\n address feesTaker\n );\n\n /// @notice Function-selector to invoke deduct-fees and swap token\n /// @dev This function selector is to be used while building transaction-data\n bytes4 public immutable FEES_TAKER_SWAP_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\"takeFeesAndSwap((address,address,uint256,uint32,bytes))\")\n );\n\n /// @notice Function-selector to invoke deduct-fees and bridge token\n /// @dev This function selector is to be used while building transaction-data\n bytes4 public immutable FEES_TAKER_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"takeFeesAndBridge((address,address,uint256,uint32,bytes))\"\n )\n );\n\n /// @notice Function-selector to invoke deduct-fees and bridge multiple tokens\n /// @dev This function selector is to be used while building transaction-data\n bytes4 public immutable FEES_TAKER_MULTI_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"takeFeesAndMultiBridge((address,address,uint256,uint32[],bytes[]))\"\n )\n );\n\n /// @notice Function-selector to invoke deduct-fees followed by swapping of a token and bridging the swapped bridge\n /// @dev This function selector is to be used while building transaction-data\n bytes4 public immutable FEES_TAKER_SWAP_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"takeFeeAndSwapAndBridge((address,address,uint256,uint32,bytes,uint32,bytes))\"\n )\n );\n\n /// @notice Function-selector to invoke deduct-fees refuel\n /// @notice followed by swapping of a token and bridging the swapped bridge\n /// @dev This function selector is to be used while building transaction-data\n bytes4 public immutable FEES_TAKER_REFUEL_SWAP_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"takeFeeAndRefuelAndSwapAndBridge((address,address,uint256,uint32,bytes,uint32,bytes,uint32,bytes))\"\n )\n );\n\n /// @notice socketGatewayAddress to be initialised via storage variable BaseController\n constructor(\n address _socketGatewayAddress\n ) BaseController(_socketGatewayAddress) {}\n\n /**\n * @notice function to deduct-fees to fees-taker address on source-chain and swap token\n * @dev ensure correct function selector is used to generate transaction-data for bridgeRequest\n * @param ftsRequest feesTakerSwapRequest object generated either off-chain or the calling contract using\n * the function-selector FEES_TAKER_SWAP_FUNCTION_SELECTOR\n * @return output bytes from the swap operation (last operation in the composed actions)\n */\n function takeFeesAndSwap(\n ISocketRequest.FeesTakerSwapRequest calldata ftsRequest\n ) external payable returns (bytes memory) {\n if (ftsRequest.feesToken == NATIVE_TOKEN_ADDRESS) {\n //transfer the native amount to the feeTakerAddress\n payable(ftsRequest.feesTakerAddress).transfer(\n ftsRequest.feesAmount\n );\n } else {\n //transfer feesAmount to feesTakerAddress\n ERC20(ftsRequest.feesToken).safeTransferFrom(\n msg.sender,\n ftsRequest.feesTakerAddress,\n ftsRequest.feesAmount\n );\n }\n\n emit SocketFeesDeducted(\n ftsRequest.feesAmount,\n ftsRequest.feesTakerAddress,\n ftsRequest.feesToken\n );\n\n //call bridge function (executeRoute for the swapRequestData)\n return _executeRoute(ftsRequest.routeId, ftsRequest.swapRequestData);\n }\n\n /**\n * @notice function to deduct-fees to fees-taker address on source-chain and bridge amount to destinationChain\n * @dev ensure correct function selector is used to generate transaction-data for bridgeRequest\n * @param ftbRequest feesTakerBridgeRequest object generated either off-chain or the calling contract using\n * the function-selector FEES_TAKER_BRIDGE_FUNCTION_SELECTOR\n * @return output bytes from the bridge operation (last operation in the composed actions)\n */\n function takeFeesAndBridge(\n ISocketRequest.FeesTakerBridgeRequest calldata ftbRequest\n ) external payable returns (bytes memory) {\n if (ftbRequest.feesToken == NATIVE_TOKEN_ADDRESS) {\n //transfer the native amount to the feeTakerAddress\n payable(ftbRequest.feesTakerAddress).transfer(\n ftbRequest.feesAmount\n );\n } else {\n //transfer feesAmount to feesTakerAddress\n ERC20(ftbRequest.feesToken).safeTransferFrom(\n msg.sender,\n ftbRequest.feesTakerAddress,\n ftbRequest.feesAmount\n );\n }\n\n emit SocketFeesDeducted(\n ftbRequest.feesAmount,\n ftbRequest.feesTakerAddress,\n ftbRequest.feesToken\n );\n\n //call bridge function (executeRoute for the bridgeData)\n return _executeRoute(ftbRequest.routeId, ftbRequest.bridgeRequestData);\n }\n\n /**\n * @notice function to deduct-fees to fees-taker address on source-chain and bridge amount to destinationChain\n * @notice multiple bridge-requests are to be generated and sequence and number of routeIds should match with the bridgeData array\n * @dev ensure correct function selector is used to generate transaction-data for bridgeRequest\n * @param ftmbRequest feesTakerMultiBridgeRequest object generated either off-chain or the calling contract using\n * the function-selector FEES_TAKER_MULTI_BRIDGE_FUNCTION_SELECTOR\n */\n function takeFeesAndMultiBridge(\n ISocketRequest.FeesTakerMultiBridgeRequest calldata ftmbRequest\n ) external payable {\n if (ftmbRequest.feesToken == NATIVE_TOKEN_ADDRESS) {\n //transfer the native amount to the feeTakerAddress\n payable(ftmbRequest.feesTakerAddress).transfer(\n ftmbRequest.feesAmount\n );\n } else {\n //transfer feesAmount to feesTakerAddress\n ERC20(ftmbRequest.feesToken).safeTransferFrom(\n msg.sender,\n ftmbRequest.feesTakerAddress,\n ftmbRequest.feesAmount\n );\n }\n\n emit SocketFeesDeducted(\n ftmbRequest.feesAmount,\n ftmbRequest.feesTakerAddress,\n ftmbRequest.feesToken\n );\n\n // multiple bridge-requests are to be generated and sequence and number of routeIds should match with the bridgeData array\n for (\n uint256 index = 0;\n index < ftmbRequest.bridgeRouteIds.length;\n ++index\n ) {\n //call bridge function (executeRoute for the bridgeData)\n _executeRoute(\n ftmbRequest.bridgeRouteIds[index],\n ftmbRequest.bridgeRequestDataItems[index]\n );\n }\n }\n\n /**\n * @notice function to deduct-fees to fees-taker address on source-chain followed by swap the amount on sourceChain followed by\n * bridging the swapped amount to destinationChain\n * @dev while generating implData for swap and bridgeRequests, ensure correct function selector is used\n * bridge action corresponds to the bridgeAfterSwap function of the bridgeImplementation\n * @param fsbRequest feesTakerSwapBridgeRequest object generated either off-chain or the calling contract using\n * the function-selector FEES_TAKER_SWAP_BRIDGE_FUNCTION_SELECTOR\n */\n function takeFeeAndSwapAndBridge(\n ISocketRequest.FeesTakerSwapBridgeRequest calldata fsbRequest\n ) external payable returns (bytes memory) {\n if (fsbRequest.feesToken == NATIVE_TOKEN_ADDRESS) {\n //transfer the native amount to the feeTakerAddress\n payable(fsbRequest.feesTakerAddress).transfer(\n fsbRequest.feesAmount\n );\n } else {\n //transfer feesAmount to feesTakerAddress\n ERC20(fsbRequest.feesToken).safeTransferFrom(\n msg.sender,\n fsbRequest.feesTakerAddress,\n fsbRequest.feesAmount\n );\n }\n\n emit SocketFeesDeducted(\n fsbRequest.feesAmount,\n fsbRequest.feesTakerAddress,\n fsbRequest.feesToken\n );\n\n // execute swap operation\n bytes memory swapResponseData = _executeRoute(\n fsbRequest.swapRouteId,\n fsbRequest.swapData\n );\n\n uint256 swapAmount = abi.decode(swapResponseData, (uint256));\n\n // swapped amount is to be bridged to the recipient on destinationChain\n bytes memory bridgeImpldata = abi.encodeWithSelector(\n BRIDGE_AFTER_SWAP_SELECTOR,\n swapAmount,\n fsbRequest.bridgeData\n );\n\n // execute bridge operation and return the byte-data from response of bridge operation\n return _executeRoute(fsbRequest.bridgeRouteId, bridgeImpldata);\n }\n\n /**\n * @notice function to deduct-fees to fees-taker address on source-chain followed by refuel followed by\n * swap the amount on sourceChain followed by bridging the swapped amount to destinationChain\n * @dev while generating implData for refuel, swap and bridge Requests, ensure correct function selector is used\n * bridge action corresponds to the bridgeAfterSwap function of the bridgeImplementation\n * @param frsbRequest feesTakerRefuelSwapBridgeRequest object generated either off-chain or the calling contract using\n * the function-selector FEES_TAKER_REFUEL_SWAP_BRIDGE_FUNCTION_SELECTOR\n */\n function takeFeeAndRefuelAndSwapAndBridge(\n ISocketRequest.FeesTakerRefuelSwapBridgeRequest calldata frsbRequest\n ) external payable returns (bytes memory) {\n if (frsbRequest.feesToken == NATIVE_TOKEN_ADDRESS) {\n //transfer the native amount to the feeTakerAddress\n payable(frsbRequest.feesTakerAddress).transfer(\n frsbRequest.feesAmount\n );\n } else {\n //transfer feesAmount to feesTakerAddress\n ERC20(frsbRequest.feesToken).safeTransferFrom(\n msg.sender,\n frsbRequest.feesTakerAddress,\n frsbRequest.feesAmount\n );\n }\n\n emit SocketFeesDeducted(\n frsbRequest.feesAmount,\n frsbRequest.feesTakerAddress,\n frsbRequest.feesToken\n );\n\n // refuel is also done via bridge execution via refuelRouteImplementation identified by refuelRouteId\n _executeRoute(frsbRequest.refuelRouteId, frsbRequest.refuelData);\n\n // execute swap operation\n bytes memory swapResponseData = _executeRoute(\n frsbRequest.swapRouteId,\n frsbRequest.swapData\n );\n\n uint256 swapAmount = abi.decode(swapResponseData, (uint256));\n\n // swapped amount is to be bridged to the recipient on destinationChain\n bytes memory bridgeImpldata = abi.encodeWithSelector(\n BRIDGE_AFTER_SWAP_SELECTOR,\n swapAmount,\n frsbRequest.bridgeData\n );\n\n // execute bridge operation and return the byte-data from response of bridge operation\n return _executeRoute(frsbRequest.bridgeRouteId, bridgeImpldata);\n }\n}\n" + }, + "src/errors/SocketErrors.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nerror CelerRefundNotReady();\nerror OnlySocketDeployer();\nerror OnlySocketGatewayOwner();\nerror OnlySocketGateway();\nerror OnlyOwner();\nerror OnlyNominee();\nerror TransferIdExists();\nerror TransferIdDoesnotExist();\nerror Address0Provided();\nerror SwapFailed();\nerror UnsupportedInterfaceId();\nerror InvalidCelerRefund();\nerror CelerAlreadyRefunded();\nerror IncorrectBridgeRatios();\nerror ZeroAddressNotAllowed();\nerror ArrayLengthMismatch();\n" + }, + "src/bridges/hop/l1/HopImplL1.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\nimport \"../interfaces/IHopL1Bridge.sol\";\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {BridgeImplBase} from \"../../BridgeImplBase.sol\";\nimport {HOP} from \"../../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Hop-L1 Route Implementation\n * @notice Route implementation with functions to bridge ERC20 and Native via Hop-Bridge from L1 to Supported L2s\n * Called via SocketGateway if the routeId in the request maps to the routeId of HopImplementation\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ncontract HopImplL1 is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable HopIdentifier = HOP;\n\n /// @notice Function-selector for ERC20-token bridging on Hop-L1-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4 public immutable HOP_L1_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(address,address,address,address,uint256,uint256,uint256,uint256,(uint256,bytes32))\"\n )\n );\n\n /// @notice Function-selector for Native bridging on Hop-L1-Route\n /// @dev This function selector is to be used while building transaction-data to bridge Native tokens\n bytes4 public immutable HOP_L1_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeNativeTo(address,address,address,uint256,uint256,uint256,uint256,uint256,bytes32)\"\n )\n );\n\n bytes4 public immutable HOP_L1_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(address,address,address,uint256,uint256,uint256,uint256,bytes32))\"\n )\n );\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n constructor(\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {}\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct HopDataNoToken {\n // The address receiving funds at the destination\n address receiverAddress;\n // address of the Hop-L1-Bridge to handle bridging the tokens\n address l1bridgeAddr;\n // relayerFee The amount distributed to the relayer at the destination. This is subtracted from the `_amount`.\n address relayer;\n // The chainId of the destination chain\n uint256 toChainId;\n // The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.\n uint256 amountOutMin;\n // The amount distributed to the relayer at the destination. This is subtracted from the `amount`.\n uint256 relayerFee;\n // The deadline for swapping in the destination AMM market. 0 if no swap is intended.\n uint256 deadline;\n // socket offchain created hash\n bytes32 metadata;\n }\n\n struct HopData {\n /// @notice address of token being bridged\n address token;\n // The address receiving funds at the destination\n address receiverAddress;\n // address of the Hop-L1-Bridge to handle bridging the tokens\n address l1bridgeAddr;\n // relayerFee The amount distributed to the relayer at the destination. This is subtracted from the `_amount`.\n address relayer;\n // The chainId of the destination chain\n uint256 toChainId;\n // The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.\n uint256 amountOutMin;\n // The amount distributed to the relayer at the destination. This is subtracted from the `amount`.\n uint256 relayerFee;\n // The deadline for swapping in the destination AMM market. 0 if no swap is intended.\n uint256 deadline;\n // socket offchain created hash\n bytes32 metadata;\n }\n\n struct HopERC20Data {\n uint256 deadline;\n bytes32 metadata;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in HopBridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for Hop-L1-Bridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n HopData memory hopData = abi.decode(bridgeData, (HopData));\n\n if (hopData.token == NATIVE_TOKEN_ADDRESS) {\n IHopL1Bridge(hopData.l1bridgeAddr).sendToL2{value: amount}(\n hopData.toChainId,\n hopData.receiverAddress,\n amount,\n hopData.amountOutMin,\n hopData.deadline,\n hopData.relayer,\n hopData.relayerFee\n );\n } else {\n ERC20(hopData.token).safeApprove(hopData.l1bridgeAddr, amount);\n\n // perform bridging\n IHopL1Bridge(hopData.l1bridgeAddr).sendToL2(\n hopData.toChainId,\n hopData.receiverAddress,\n amount,\n hopData.amountOutMin,\n hopData.deadline,\n hopData.relayer,\n hopData.relayerFee\n );\n }\n\n emit SocketBridge(\n amount,\n hopData.token,\n hopData.toChainId,\n HopIdentifier,\n msg.sender,\n hopData.receiverAddress,\n hopData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in HopBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param hopData encoded data for HopData\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n HopDataNoToken calldata hopData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n\n if (token == NATIVE_TOKEN_ADDRESS) {\n IHopL1Bridge(hopData.l1bridgeAddr).sendToL2{value: bridgeAmount}(\n hopData.toChainId,\n hopData.receiverAddress,\n bridgeAmount,\n hopData.amountOutMin,\n hopData.deadline,\n hopData.relayer,\n hopData.relayerFee\n );\n } else {\n ERC20(token).safeApprove(hopData.l1bridgeAddr, bridgeAmount);\n\n // perform bridging\n IHopL1Bridge(hopData.l1bridgeAddr).sendToL2(\n hopData.toChainId,\n hopData.receiverAddress,\n bridgeAmount,\n hopData.amountOutMin,\n hopData.deadline,\n hopData.relayer,\n hopData.relayerFee\n );\n }\n\n emit SocketBridge(\n bridgeAmount,\n token,\n hopData.toChainId,\n HopIdentifier,\n msg.sender,\n hopData.receiverAddress,\n hopData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via Hop-L1-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param receiverAddress The address receiving funds at the destination\n * @param token token being bridged\n * @param l1bridgeAddr address of the Hop-L1-Bridge to handle bridging the tokens\n * @param relayer The amount distributed to the relayer at the destination. This is subtracted from the `_amount`.\n * @param toChainId The chainId of the destination chain\n * @param amount The amount being sent\n * @param amountOutMin The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.\n * @param relayerFee The amount distributed to the relayer at the destination. This is subtracted from the `amount`.\n * @param hopData extra data needed to build the tx\n */\n function bridgeERC20To(\n address receiverAddress,\n address token,\n address l1bridgeAddr,\n address relayer,\n uint256 toChainId,\n uint256 amount,\n uint256 amountOutMin,\n uint256 relayerFee,\n HopERC20Data calldata hopData\n ) external payable {\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n tokenInstance.safeApprove(l1bridgeAddr, amount);\n\n // perform bridging\n IHopL1Bridge(l1bridgeAddr).sendToL2(\n toChainId,\n receiverAddress,\n amount,\n amountOutMin,\n hopData.deadline,\n relayer,\n relayerFee\n );\n\n emit SocketBridge(\n amount,\n token,\n toChainId,\n HopIdentifier,\n msg.sender,\n receiverAddress,\n hopData.metadata\n );\n }\n\n /**\n * @notice function to handle Native bridging to receipent via Hop-L1-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param receiverAddress The address receiving funds at the destination\n * @param l1bridgeAddr address of the Hop-L1-Bridge to handle bridging the tokens\n * @param relayer The amount distributed to the relayer at the destination. This is subtracted from the `_amount`.\n * @param toChainId The chainId of the destination chain\n * @param amount The amount being sent\n * @param amountOutMin The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.\n * @param relayerFee The amount distributed to the relayer at the destination. This is subtracted from the `amount`.\n * @param deadline The deadline for swapping in the destination AMM market. 0 if no swap is intended.\n */\n function bridgeNativeTo(\n address receiverAddress,\n address l1bridgeAddr,\n address relayer,\n uint256 toChainId,\n uint256 amount,\n uint256 amountOutMin,\n uint256 relayerFee,\n uint256 deadline,\n bytes32 metadata\n ) external payable {\n IHopL1Bridge(l1bridgeAddr).sendToL2{value: amount}(\n toChainId,\n receiverAddress,\n amount,\n amountOutMin,\n deadline,\n relayer,\n relayerFee\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n toChainId,\n HopIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n" + }, + "src/static/RouteIdentifiers.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\nbytes32 constant ACROSS = keccak256(\"Across\");\n\nbytes32 constant ANYSWAP = keccak256(\"Anyswap\");\n\nbytes32 constant CBRIDGE = keccak256(\"CBridge\");\n\nbytes32 constant HOP = keccak256(\"Hop\");\n\nbytes32 constant HYPHEN = keccak256(\"Hyphen\");\n\nbytes32 constant NATIVE_OPTIMISM = keccak256(\"NativeOptimism\");\n\nbytes32 constant NATIVE_ARBITRUM = keccak256(\"NativeArbitrum\");\n\nbytes32 constant NATIVE_POLYGON = keccak256(\"NativePolygon\");\n\nbytes32 constant REFUEL = keccak256(\"Refuel\");\n\nbytes32 constant STARGATE = keccak256(\"Stargate\");\n\nbytes32 constant ONEINCH = keccak256(\"OneInch\");\n\nbytes32 constant ZEROX = keccak256(\"Zerox\");\n\nbytes32 constant RAINBOW = keccak256(\"Rainbow\");\n" + }, + "src/SocketGateway.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\npragma experimental ABIEncoderV2;\n\nimport \"./utils/Ownable.sol\";\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {LibUtil} from \"./libraries/LibUtil.sol\";\nimport \"./libraries/LibBytes.sol\";\nimport {ISocketRoute} from \"./interfaces/ISocketRoute.sol\";\nimport {ISocketRequest} from \"./interfaces/ISocketRequest.sol\";\nimport {ISocketGateway} from \"./interfaces/ISocketGateway.sol\";\nimport {IncorrectBridgeRatios, ZeroAddressNotAllowed, ArrayLengthMismatch} from \"./errors/SocketErrors.sol\";\n\n/// @title SocketGatewayContract\n/// @notice Socketgateway is a contract with entrypoint functions for all interactions with socket liquidity layer\n/// @author Socket Team\ncontract SocketGatewayTemplate is Ownable {\n using LibBytes for bytes;\n using LibBytes for bytes4;\n using SafeTransferLib for ERC20;\n\n /// @notice FunctionSelector used to delegatecall from swap to the function of bridge router implementation\n bytes4 public immutable BRIDGE_AFTER_SWAP_SELECTOR =\n bytes4(keccak256(\"bridgeAfterSwap(uint256,bytes)\"));\n\n /// @notice storage variable to keep track of total number of routes registered in socketgateway\n uint32 public routesCount = 385;\n\n /// @notice storage variable to keep track of total number of controllers registered in socketgateway\n uint32 public controllerCount;\n\n address public immutable disabledRouteAddress;\n\n uint256 public constant CENT_PERCENT = 100e18;\n\n /// @notice storage mapping for route implementation addresses\n mapping(uint32 => address) public routes;\n\n /// storage mapping for controller implemenation addresses\n mapping(uint32 => address) public controllers;\n\n // Events ------------------------------------------------------------------------------------------------------->\n\n /// @notice Event emitted when a router is added to socketgateway\n event NewRouteAdded(uint32 indexed routeId, address indexed route);\n\n /// @notice Event emitted when a route is disabled\n event RouteDisabled(uint32 indexed routeId);\n\n /// @notice Event emitted when ownership transfer is requested by socket-gateway-owner\n event OwnershipTransferRequested(\n address indexed _from,\n address indexed _to\n );\n\n /// @notice Event emitted when a controller is added to socketgateway\n event ControllerAdded(\n uint32 indexed controllerId,\n address indexed controllerAddress\n );\n\n /// @notice Event emitted when a controller is disabled\n event ControllerDisabled(uint32 indexed controllerId);\n\n constructor(address _owner, address _disabledRoute) Ownable(_owner) {\n disabledRouteAddress = _disabledRoute;\n }\n\n // Able to receive ether\n // solhint-disable-next-line no-empty-blocks\n receive() external payable {}\n\n /*******************************************\n * EXTERNAL AND PUBLIC FUNCTIONS *\n *******************************************/\n\n /**\n * @notice executes functions in the routes identified using routeId and functionSelectorData\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped\n * @dev ensure the data in routeData to be built using the function-selector defined as a\n * constant in the route implementation contract\n * @param routeId route identifier\n * @param routeData functionSelectorData generated using the function-selector defined in the route Implementation\n */\n function executeRoute(\n uint32 routeId,\n bytes calldata routeData\n ) external payable returns (bytes memory) {\n (bool success, bytes memory result) = addressAt(routeId).delegatecall(\n routeData\n );\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n return result;\n }\n\n /**\n * @notice swaps a token on sourceChain and split it across multiple bridge-recipients\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being swapped\n * @dev ensure the swap-data and bridge-data is generated using the function-selector defined as a constant in the implementation address\n * @param swapMultiBridgeRequest request\n */\n function swapAndMultiBridge(\n ISocketRequest.SwapMultiBridgeRequest calldata swapMultiBridgeRequest\n ) external payable {\n uint256 requestLength = swapMultiBridgeRequest.bridgeRouteIds.length;\n\n if (\n requestLength != swapMultiBridgeRequest.bridgeImplDataItems.length\n ) {\n revert ArrayLengthMismatch();\n }\n uint256 ratioAggregate;\n for (uint256 index = 0; index < requestLength; ) {\n ratioAggregate += swapMultiBridgeRequest.bridgeRatios[index];\n }\n\n if (ratioAggregate != CENT_PERCENT) {\n revert IncorrectBridgeRatios();\n }\n\n (bool swapSuccess, bytes memory swapResult) = addressAt(\n swapMultiBridgeRequest.swapRouteId\n ).delegatecall(swapMultiBridgeRequest.swapImplData);\n\n if (!swapSuccess) {\n assembly {\n revert(add(swapResult, 32), mload(swapResult))\n }\n }\n\n uint256 amountReceivedFromSwap = abi.decode(swapResult, (uint256));\n\n uint256 bridgedAmount;\n\n for (uint256 index = 0; index < requestLength; ) {\n uint256 bridgingAmount;\n\n // if it is the last bridge request, bridge the remaining amount\n if (index == requestLength - 1) {\n bridgingAmount = amountReceivedFromSwap - bridgedAmount;\n } else {\n // bridging amount is the multiplication of bridgeRatio and amountReceivedFromSwap\n bridgingAmount =\n (amountReceivedFromSwap *\n swapMultiBridgeRequest.bridgeRatios[index]) /\n (CENT_PERCENT);\n }\n\n // update the bridged amount, this would be used for computation for last bridgeRequest\n bridgedAmount += bridgingAmount;\n\n bytes memory bridgeImpldata = abi.encodeWithSelector(\n BRIDGE_AFTER_SWAP_SELECTOR,\n bridgingAmount,\n swapMultiBridgeRequest.bridgeImplDataItems[index]\n );\n\n (bool bridgeSuccess, bytes memory bridgeResult) = addressAt(\n swapMultiBridgeRequest.bridgeRouteIds[index]\n ).delegatecall(bridgeImpldata);\n\n if (!bridgeSuccess) {\n assembly {\n revert(add(bridgeResult, 32), mload(bridgeResult))\n }\n }\n\n unchecked {\n ++index;\n }\n }\n }\n\n /**\n * @notice sequentially executes functions in the routes identified using routeId and functionSelectorData\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped\n * @dev ensure the data in each dataItem to be built using the function-selector defined as a\n * constant in the route implementation contract\n * @param routeIds a list of route identifiers\n * @param dataItems a list of functionSelectorData generated using the function-selector defined in the route Implementation\n */\n function executeRoutes(\n uint32[] calldata routeIds,\n bytes[] calldata dataItems\n ) external payable {\n uint256 routeIdslength = routeIds.length;\n if (routeIdslength != dataItems.length) revert ArrayLengthMismatch();\n for (uint256 index = 0; index < routeIdslength; ) {\n (bool success, bytes memory result) = addressAt(routeIds[index])\n .delegatecall(dataItems[index]);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n unchecked {\n ++index;\n }\n }\n }\n\n /**\n * @notice execute a controller function identified using the controllerId in the request\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped\n * @dev ensure the data in request to be built using the function-selector defined as a\n * constant in the controller implementation contract\n * @param socketControllerRequest socketControllerRequest with controllerId to identify the\n * controllerAddress and byteData constructed using functionSelector\n * of the function being invoked\n * @return bytes data received from the call delegated to controller\n */\n function executeController(\n ISocketGateway.SocketControllerRequest calldata socketControllerRequest\n ) external payable returns (bytes memory) {\n (bool success, bytes memory result) = controllers[\n socketControllerRequest.controllerId\n ].delegatecall(socketControllerRequest.data);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n return result;\n }\n\n /**\n * @notice sequentially executes all controller requests\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped\n * @dev ensure the data in each controller-request to be built using the function-selector defined as a\n * constant in the controller implementation contract\n * @param controllerRequests a list of socketControllerRequest\n * Each controllerRequest contains controllerId to identify the controllerAddress and\n * byteData constructed using functionSelector of the function being invoked\n */\n function executeControllers(\n ISocketGateway.SocketControllerRequest[] calldata controllerRequests\n ) external payable {\n for (uint32 index = 0; index < controllerRequests.length; ) {\n (bool success, bytes memory result) = controllers[\n controllerRequests[index].controllerId\n ].delegatecall(controllerRequests[index].data);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n unchecked {\n ++index;\n }\n }\n }\n\n /**************************************\n * ADMIN FUNCTIONS *\n **************************************/\n\n /**\n * @notice Add route to the socketGateway\n This is a restricted function to be called by only socketGatewayOwner\n * @dev ensure routeAddress is a verified bridge or middleware implementation address\n * @param routeAddress The address of bridge or middleware implementation contract deployed\n * @return Id of the route added to the routes-mapping in socketGateway storage\n */\n function addRoute(\n address routeAddress\n ) external onlyOwner returns (uint32) {\n uint32 routeId = routesCount;\n routes[routeId] = routeAddress;\n\n routesCount += 1;\n\n emit NewRouteAdded(routeId, routeAddress);\n\n return routeId;\n }\n\n /**\n * @notice Give Infinite or 0 approval to bridgeRoute for the tokenAddress\n This is a restricted function to be called by only socketGatewayOwner\n */\n\n function setApprovalForRouters(\n address[] memory routeAddresses,\n address[] memory tokenAddresses,\n bool isMax\n ) external onlyOwner {\n for (uint32 index = 0; index < routeAddresses.length; ) {\n ERC20(tokenAddresses[index]).approve(\n routeAddresses[index],\n isMax ? type(uint256).max : 0\n );\n unchecked {\n ++index;\n }\n }\n }\n\n /**\n * @notice Add controller to the socketGateway\n This is a restricted function to be called by only socketGatewayOwner\n * @dev ensure controllerAddress is a verified controller implementation address\n * @param controllerAddress The address of controller implementation contract deployed\n * @return Id of the controller added to the controllers-mapping in socketGateway storage\n */\n function addController(\n address controllerAddress\n ) external onlyOwner returns (uint32) {\n uint32 controllerId = controllerCount;\n\n controllers[controllerId] = controllerAddress;\n\n controllerCount += 1;\n\n emit ControllerAdded(controllerId, controllerAddress);\n\n return controllerId;\n }\n\n /**\n * @notice disable controller by setting ZeroAddress to the entry in controllers-mapping\n identified by controllerId as key.\n This is a restricted function to be called by only socketGatewayOwner\n * @param controllerId The Id of controller-implementation in the controllers mapping\n */\n function disableController(uint32 controllerId) public onlyOwner {\n controllers[controllerId] = disabledRouteAddress;\n emit ControllerDisabled(controllerId);\n }\n\n /**\n * @notice disable a route by setting ZeroAddress to the entry in routes-mapping\n identified by routeId as key.\n This is a restricted function to be called by only socketGatewayOwner\n * @param routeId The Id of route-implementation in the routes mapping\n */\n function disableRoute(uint32 routeId) external onlyOwner {\n routes[routeId] = disabledRouteAddress;\n emit RouteDisabled(routeId);\n }\n\n /*******************************************\n * RESTRICTED RESCUE FUNCTIONS *\n *******************************************/\n\n /**\n * @notice Rescues the ERC20 token to an address\n this is a restricted function to be called by only socketGatewayOwner\n * @dev as this is a restricted to socketGatewayOwner, ensure the userAddress is a known address\n * @param token address of the ERC20 token being rescued\n * @param userAddress address to which ERC20 is to be rescued\n * @param amount amount of ERC20 tokens being rescued\n */\n function rescueFunds(\n address token,\n address userAddress,\n uint256 amount\n ) external onlyOwner {\n ERC20(token).safeTransfer(userAddress, amount);\n }\n\n /**\n * @notice Rescues the native balance to an address\n this is a restricted function to be called by only socketGatewayOwner\n * @dev as this is a restricted to socketGatewayOwner, ensure the userAddress is a known address\n * @param userAddress address to which native-balance is to be rescued\n * @param amount amount of native-balance being rescued\n */\n function rescueEther(\n address payable userAddress,\n uint256 amount\n ) external onlyOwner {\n userAddress.transfer(amount);\n }\n\n /*******************************************\n * VIEW FUNCTIONS *\n *******************************************/\n\n /**\n * @notice Get routeImplementation address mapped to the routeId\n * @param routeId routeId is the key in the mapping for routes\n * @return route-implementation address\n */\n function getRoute(uint32 routeId) public view returns (address) {\n return addressAt(routeId);\n }\n\n /**\n * @notice Get controllerImplementation address mapped to the controllerId\n * @param controllerId controllerId is the key in the mapping for controllers\n * @return controller-implementation address\n */\n function getController(uint32 controllerId) public view returns (address) {\n return controllers[controllerId];\n }\n\n function addressAt(uint32 routeId) public view returns (address) {\n if (routeId < 385) {\n if (routeId < 257) {\n if (routeId < 129) {\n if (routeId < 65) {\n if (routeId < 33) {\n if (routeId < 17) {\n if (routeId < 9) {\n if (routeId < 5) {\n if (routeId < 3) {\n if (routeId == 1) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 3) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 7) {\n if (routeId == 5) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 7) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 13) {\n if (routeId < 11) {\n if (routeId == 9) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 11) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 15) {\n if (routeId == 13) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 15) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 25) {\n if (routeId < 21) {\n if (routeId < 19) {\n if (routeId == 17) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 19) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 23) {\n if (routeId == 21) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 23) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 29) {\n if (routeId < 27) {\n if (routeId == 25) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 27) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 31) {\n if (routeId == 29) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 31) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 49) {\n if (routeId < 41) {\n if (routeId < 37) {\n if (routeId < 35) {\n if (routeId == 33) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 35) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 39) {\n if (routeId == 37) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 39) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 45) {\n if (routeId < 43) {\n if (routeId == 41) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 43) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 47) {\n if (routeId == 45) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 47) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 57) {\n if (routeId < 53) {\n if (routeId < 51) {\n if (routeId == 49) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 51) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 55) {\n if (routeId == 53) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 55) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 61) {\n if (routeId < 59) {\n if (routeId == 57) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 59) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 63) {\n if (routeId == 61) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 63) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 97) {\n if (routeId < 81) {\n if (routeId < 73) {\n if (routeId < 69) {\n if (routeId < 67) {\n if (routeId == 65) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 67) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 71) {\n if (routeId == 69) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 71) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 77) {\n if (routeId < 75) {\n if (routeId == 73) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 75) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 79) {\n if (routeId == 77) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 79) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 89) {\n if (routeId < 85) {\n if (routeId < 83) {\n if (routeId == 81) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 83) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 87) {\n if (routeId == 85) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 87) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 93) {\n if (routeId < 91) {\n if (routeId == 89) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 91) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 95) {\n if (routeId == 93) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 95) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 113) {\n if (routeId < 105) {\n if (routeId < 101) {\n if (routeId < 99) {\n if (routeId == 97) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 99) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 103) {\n if (routeId == 101) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 103) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 109) {\n if (routeId < 107) {\n if (routeId == 105) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 107) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 111) {\n if (routeId == 109) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 111) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 121) {\n if (routeId < 117) {\n if (routeId < 115) {\n if (routeId == 113) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 115) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 119) {\n if (routeId == 117) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 119) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 125) {\n if (routeId < 123) {\n if (routeId == 121) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 123) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 127) {\n if (routeId == 125) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 127) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 193) {\n if (routeId < 161) {\n if (routeId < 145) {\n if (routeId < 137) {\n if (routeId < 133) {\n if (routeId < 131) {\n if (routeId == 129) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 131) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 135) {\n if (routeId == 133) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 135) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 141) {\n if (routeId < 139) {\n if (routeId == 137) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 139) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 143) {\n if (routeId == 141) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 143) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 153) {\n if (routeId < 149) {\n if (routeId < 147) {\n if (routeId == 145) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 147) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 151) {\n if (routeId == 149) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 151) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 157) {\n if (routeId < 155) {\n if (routeId == 153) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 155) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 159) {\n if (routeId == 157) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 159) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 177) {\n if (routeId < 169) {\n if (routeId < 165) {\n if (routeId < 163) {\n if (routeId == 161) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 163) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 167) {\n if (routeId == 165) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 167) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 173) {\n if (routeId < 171) {\n if (routeId == 169) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 171) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 175) {\n if (routeId == 173) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 175) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 185) {\n if (routeId < 181) {\n if (routeId < 179) {\n if (routeId == 177) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 179) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 183) {\n if (routeId == 181) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 183) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 189) {\n if (routeId < 187) {\n if (routeId == 185) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 187) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 191) {\n if (routeId == 189) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 191) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 225) {\n if (routeId < 209) {\n if (routeId < 201) {\n if (routeId < 197) {\n if (routeId < 195) {\n if (routeId == 193) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 195) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 199) {\n if (routeId == 197) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 199) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 205) {\n if (routeId < 203) {\n if (routeId == 201) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 203) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 207) {\n if (routeId == 205) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 207) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 217) {\n if (routeId < 213) {\n if (routeId < 211) {\n if (routeId == 209) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 211) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 215) {\n if (routeId == 213) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 215) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 221) {\n if (routeId < 219) {\n if (routeId == 217) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 219) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 223) {\n if (routeId == 221) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 223) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 241) {\n if (routeId < 233) {\n if (routeId < 229) {\n if (routeId < 227) {\n if (routeId == 225) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 227) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 231) {\n if (routeId == 229) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 231) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 237) {\n if (routeId < 235) {\n if (routeId == 233) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 235) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 239) {\n if (routeId == 237) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 239) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 249) {\n if (routeId < 245) {\n if (routeId < 243) {\n if (routeId == 241) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 243) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 247) {\n if (routeId == 245) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 247) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 253) {\n if (routeId < 251) {\n if (routeId == 249) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 251) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 255) {\n if (routeId == 253) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 255) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 321) {\n if (routeId < 289) {\n if (routeId < 273) {\n if (routeId < 265) {\n if (routeId < 261) {\n if (routeId < 259) {\n if (routeId == 257) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 259) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 263) {\n if (routeId == 261) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 263) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 269) {\n if (routeId < 267) {\n if (routeId == 265) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 267) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 271) {\n if (routeId == 269) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 271) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 281) {\n if (routeId < 277) {\n if (routeId < 275) {\n if (routeId == 273) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 275) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 279) {\n if (routeId == 277) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 279) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 285) {\n if (routeId < 283) {\n if (routeId == 281) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 283) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 287) {\n if (routeId == 285) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 287) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 305) {\n if (routeId < 297) {\n if (routeId < 293) {\n if (routeId < 291) {\n if (routeId == 289) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 291) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 295) {\n if (routeId == 293) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 295) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 301) {\n if (routeId < 299) {\n if (routeId == 297) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 299) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 303) {\n if (routeId == 301) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 303) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 313) {\n if (routeId < 309) {\n if (routeId < 307) {\n if (routeId == 305) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 307) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 311) {\n if (routeId == 309) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 311) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 317) {\n if (routeId < 315) {\n if (routeId == 313) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 315) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 319) {\n if (routeId == 317) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 319) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 353) {\n if (routeId < 337) {\n if (routeId < 329) {\n if (routeId < 325) {\n if (routeId < 323) {\n if (routeId == 321) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 323) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 327) {\n if (routeId == 325) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 327) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 333) {\n if (routeId < 331) {\n if (routeId == 329) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 331) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 335) {\n if (routeId == 333) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 335) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 345) {\n if (routeId < 341) {\n if (routeId < 339) {\n if (routeId == 337) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 339) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 343) {\n if (routeId == 341) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 343) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 349) {\n if (routeId < 347) {\n if (routeId == 345) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 347) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 351) {\n if (routeId == 349) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 351) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 369) {\n if (routeId < 361) {\n if (routeId < 357) {\n if (routeId < 355) {\n if (routeId == 353) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 355) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 359) {\n if (routeId == 357) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 359) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 365) {\n if (routeId < 363) {\n if (routeId == 361) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 363) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 367) {\n if (routeId == 365) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 367) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 377) {\n if (routeId < 373) {\n if (routeId < 371) {\n if (routeId == 369) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 371) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 375) {\n if (routeId == 373) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 375) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 381) {\n if (routeId < 379) {\n if (routeId == 377) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 379) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 383) {\n if (routeId == 381) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 383) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n }\n }\n }\n }\n\n if (routes[routeId] == address(0)) revert ZeroAddressNotAllowed();\n return routes[routeId];\n }\n\n /// @notice fallback function to handle swap, bridge execution\n /// @dev ensure routeId is converted to bytes4 and sent as msg.sig in the transaction\n fallback() external payable {\n address routeAddress = addressAt(uint32(msg.sig));\n\n bytes memory result;\n\n assembly {\n // copy function selector and any arguments\n calldatacopy(0, 4, sub(calldatasize(), 4))\n // execute function call using the facet\n result := delegatecall(\n gas(),\n routeAddress,\n 0,\n sub(calldatasize(), 4),\n 0,\n 0\n )\n // get any return value\n returndatacopy(0, 0, returndatasize())\n // return any return value or error back to the caller\n switch result\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n}\n" + }, + "src/swap/rainbow/Rainbow.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport \"../SwapImplBase.sol\";\nimport {Address0Provided, SwapFailed} from \"../../errors/SocketErrors.sol\";\nimport {RAINBOW} from \"../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Rainbow-Swap-Route Implementation\n * @notice Route implementation with functions to swap tokens via Rainbow-Swap\n * Called via SocketGateway if the routeId in the request maps to the routeId of RainbowImplementation\n * @author Socket dot tech.\n */\ncontract RainbowSwapImpl is SwapImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable RainbowIdentifier = RAINBOW;\n\n /// @notice unique name to identify the router, used to emit event upon successful bridging\n bytes32 public immutable NAME = keccak256(\"Rainbow-Router\");\n\n /// @notice address of rainbow-swap-aggregator to swap the tokens on Chain\n address payable public immutable rainbowSwapAggregator;\n\n /// @notice socketGatewayAddress to be initialised via storage variable SwapImplBase\n /// @notice rainbow swap aggregator contract is payable to allow ethereum swaps\n /// @dev ensure _rainbowSwapAggregator are set properly for the chainId in which the contract is being deployed\n constructor(\n address _rainbowSwapAggregator,\n address _socketGateway,\n address _socketDeployFactory\n ) SwapImplBase(_socketGateway, _socketDeployFactory) {\n rainbowSwapAggregator = payable(_rainbowSwapAggregator);\n }\n\n receive() external payable {}\n\n fallback() external payable {}\n\n /**\n * @notice function to swap tokens on the chain and transfer to receiver address\n * @notice This method is payable because the caller is doing token transfer and swap operation\n * @param fromToken address of token being Swapped\n * @param toToken address of token that recipient will receive after swap\n * @param amount amount of fromToken being swapped\n * @param receiverAddress recipient-address\n * @param swapExtraData additional Data to perform Swap via Rainbow-Aggregator\n * @return swapped amount (in toToken Address)\n */\n function performAction(\n address fromToken,\n address toToken,\n uint256 amount,\n address receiverAddress,\n bytes calldata swapExtraData\n ) external payable override returns (uint256) {\n if (fromToken == address(0)) {\n revert Address0Provided();\n }\n\n bytes memory swapCallData = abi.decode(swapExtraData, (bytes));\n\n uint256 _initialBalanceTokenOut;\n uint256 _finalBalanceTokenOut;\n\n ERC20 toTokenERC20 = ERC20(toToken);\n if (toToken != NATIVE_TOKEN_ADDRESS) {\n _initialBalanceTokenOut = toTokenERC20.balanceOf(socketGateway);\n } else {\n _initialBalanceTokenOut = address(this).balance;\n }\n\n if (fromToken != NATIVE_TOKEN_ADDRESS) {\n ERC20 token = ERC20(fromToken);\n token.safeTransferFrom(msg.sender, socketGateway, amount);\n token.safeApprove(rainbowSwapAggregator, amount);\n\n // solhint-disable-next-line\n (bool success, ) = rainbowSwapAggregator.call(swapCallData);\n\n if (!success) {\n revert SwapFailed();\n }\n\n token.safeApprove(rainbowSwapAggregator, 0);\n } else {\n (bool success, ) = rainbowSwapAggregator.call{value: amount}(\n swapCallData\n );\n if (!success) {\n revert SwapFailed();\n }\n }\n\n if (toToken != NATIVE_TOKEN_ADDRESS) {\n _finalBalanceTokenOut = toTokenERC20.balanceOf(socketGateway);\n } else {\n _finalBalanceTokenOut = address(this).balance;\n }\n\n uint256 returnAmount = _finalBalanceTokenOut - _initialBalanceTokenOut;\n\n if (toToken == NATIVE_TOKEN_ADDRESS) {\n payable(receiverAddress).transfer(returnAmount);\n } else {\n toTokenERC20.transfer(receiverAddress, returnAmount);\n }\n\n emit SocketSwapTokens(\n fromToken,\n toToken,\n returnAmount,\n amount,\n RainbowIdentifier,\n receiverAddress\n );\n\n return returnAmount;\n }\n\n /**\n * @notice function to swapWithIn SocketGateway - swaps tokens on the chain to socketGateway as recipient\n * @param fromToken token to be swapped\n * @param toToken token to which fromToken has to be swapped\n * @param amount amount of fromToken being swapped\n * @param swapExtraData encoded value of properties in the swapData Struct\n * @return swapped amount (in toToken Address)\n */\n function performActionWithIn(\n address fromToken,\n address toToken,\n uint256 amount,\n bytes calldata swapExtraData\n ) external payable override returns (uint256, address) {\n if (fromToken == address(0)) {\n revert Address0Provided();\n }\n\n bytes memory swapCallData = abi.decode(swapExtraData, (bytes));\n\n uint256 _initialBalanceTokenOut;\n uint256 _finalBalanceTokenOut;\n\n ERC20 toTokenERC20 = ERC20(toToken);\n if (toToken != NATIVE_TOKEN_ADDRESS) {\n _initialBalanceTokenOut = toTokenERC20.balanceOf(socketGateway);\n } else {\n _initialBalanceTokenOut = address(this).balance;\n }\n\n if (fromToken != NATIVE_TOKEN_ADDRESS) {\n ERC20 token = ERC20(fromToken);\n token.safeTransferFrom(msg.sender, socketGateway, amount);\n token.safeApprove(rainbowSwapAggregator, amount);\n\n // solhint-disable-next-line\n (bool success, ) = rainbowSwapAggregator.call(swapCallData);\n\n if (!success) {\n revert SwapFailed();\n }\n\n token.safeApprove(rainbowSwapAggregator, 0);\n } else {\n (bool success, ) = rainbowSwapAggregator.call{value: amount}(\n swapCallData\n );\n if (!success) {\n revert SwapFailed();\n }\n }\n\n if (toToken != NATIVE_TOKEN_ADDRESS) {\n _finalBalanceTokenOut = toTokenERC20.balanceOf(socketGateway);\n } else {\n _finalBalanceTokenOut = address(this).balance;\n }\n\n uint256 returnAmount = _finalBalanceTokenOut - _initialBalanceTokenOut;\n\n emit SocketSwapTokens(\n fromToken,\n toToken,\n returnAmount,\n amount,\n RainbowIdentifier,\n socketGateway\n );\n\n return (returnAmount, toToken);\n }\n}\n" + }, + "src/interfaces/ISocketBridgeBase.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\ninterface ISocketBridgeBase {\n function killme() external;\n}\n" + }, + "src/interfaces/ISocketRequest.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/**\n * @title ISocketRoute\n * @notice Interface with Request DataStructures to invoke controller functions.\n * @author Socket dot tech.\n */\ninterface ISocketRequest {\n struct SwapMultiBridgeRequest {\n uint32 swapRouteId;\n bytes swapImplData;\n uint32[] bridgeRouteIds;\n bytes[] bridgeImplDataItems;\n uint256[] bridgeRatios;\n bytes[] eventDataItems;\n }\n\n // Datastructure for Refuel-Swap-Bridge function\n struct RefuelSwapBridgeRequest {\n uint32 refuelRouteId;\n bytes refuelData;\n uint32 swapRouteId;\n bytes swapData;\n uint32 bridgeRouteId;\n bytes bridgeData;\n }\n\n // Datastructure for DeductFees-Swap function\n struct FeesTakerSwapRequest {\n address feesTakerAddress;\n address feesToken;\n uint256 feesAmount;\n uint32 routeId;\n bytes swapRequestData;\n }\n\n // Datastructure for DeductFees-Bridge function\n struct FeesTakerBridgeRequest {\n address feesTakerAddress;\n address feesToken;\n uint256 feesAmount;\n uint32 routeId;\n bytes bridgeRequestData;\n }\n\n // Datastructure for DeductFees-MultiBridge function\n struct FeesTakerMultiBridgeRequest {\n address feesTakerAddress;\n address feesToken;\n uint256 feesAmount;\n uint32[] bridgeRouteIds;\n bytes[] bridgeRequestDataItems;\n }\n\n // Datastructure for DeductFees-Swap-Bridge function\n struct FeesTakerSwapBridgeRequest {\n address feesTakerAddress;\n address feesToken;\n uint256 feesAmount;\n uint32 swapRouteId;\n bytes swapData;\n uint32 bridgeRouteId;\n bytes bridgeData;\n }\n\n // Datastructure for DeductFees-Refuel-Swap-Bridge function\n struct FeesTakerRefuelSwapBridgeRequest {\n address feesTakerAddress;\n address feesToken;\n uint256 feesAmount;\n uint32 refuelRouteId;\n bytes refuelData;\n uint32 swapRouteId;\n bytes swapData;\n uint32 bridgeRouteId;\n bytes bridgeData;\n }\n}\n" + }, + "src/utils/Ownable.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-only\npragma solidity ^0.8.4;\n\nimport {OnlyOwner, OnlyNominee} from \"../errors/SocketErrors.sol\";\n\nabstract contract Ownable {\n address private _owner;\n address private _nominee;\n\n event OwnerNominated(address indexed nominee);\n event OwnerClaimed(address indexed claimer);\n\n constructor(address owner_) {\n _claimOwner(owner_);\n }\n\n modifier onlyOwner() {\n if (msg.sender != _owner) {\n revert OnlyOwner();\n }\n _;\n }\n\n function owner() public view returns (address) {\n return _owner;\n }\n\n function nominee() public view returns (address) {\n return _nominee;\n }\n\n function nominateOwner(address nominee_) external {\n if (msg.sender != _owner) {\n revert OnlyOwner();\n }\n _nominee = nominee_;\n emit OwnerNominated(_nominee);\n }\n\n function claimOwner() external {\n if (msg.sender != _nominee) {\n revert OnlyNominee();\n }\n _claimOwner(msg.sender);\n }\n\n function _claimOwner(address claimer_) internal {\n _owner = claimer_;\n _nominee = address(0);\n emit OwnerClaimed(claimer_);\n }\n}\n" + }, + "lib/solmate/src/tokens/ERC20.sol": { + "content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\n/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.\n/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)\n/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)\n/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.\nabstract contract ERC20 {\n /*//////////////////////////////////////////////////////////////\n EVENTS\n //////////////////////////////////////////////////////////////*/\n\n event Transfer(address indexed from, address indexed to, uint256 amount);\n\n event Approval(address indexed owner, address indexed spender, uint256 amount);\n\n /*//////////////////////////////////////////////////////////////\n METADATA STORAGE\n //////////////////////////////////////////////////////////////*/\n\n string public name;\n\n string public symbol;\n\n uint8 public immutable decimals;\n\n /*//////////////////////////////////////////////////////////////\n ERC20 STORAGE\n //////////////////////////////////////////////////////////////*/\n\n uint256 public totalSupply;\n\n mapping(address => uint256) public balanceOf;\n\n mapping(address => mapping(address => uint256)) public allowance;\n\n /*//////////////////////////////////////////////////////////////\n EIP-2612 STORAGE\n //////////////////////////////////////////////////////////////*/\n\n uint256 internal immutable INITIAL_CHAIN_ID;\n\n bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;\n\n mapping(address => uint256) public nonces;\n\n /*//////////////////////////////////////////////////////////////\n CONSTRUCTOR\n //////////////////////////////////////////////////////////////*/\n\n constructor(\n string memory _name,\n string memory _symbol,\n uint8 _decimals\n ) {\n name = _name;\n symbol = _symbol;\n decimals = _decimals;\n\n INITIAL_CHAIN_ID = block.chainid;\n INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();\n }\n\n /*//////////////////////////////////////////////////////////////\n ERC20 LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function approve(address spender, uint256 amount) public virtual returns (bool) {\n allowance[msg.sender][spender] = amount;\n\n emit Approval(msg.sender, spender, amount);\n\n return true;\n }\n\n function transfer(address to, uint256 amount) public virtual returns (bool) {\n balanceOf[msg.sender] -= amount;\n\n // Cannot overflow because the sum of all user\n // balances can't exceed the max uint256 value.\n unchecked {\n balanceOf[to] += amount;\n }\n\n emit Transfer(msg.sender, to, amount);\n\n return true;\n }\n\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) public virtual returns (bool) {\n uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.\n\n if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;\n\n balanceOf[from] -= amount;\n\n // Cannot overflow because the sum of all user\n // balances can't exceed the max uint256 value.\n unchecked {\n balanceOf[to] += amount;\n }\n\n emit Transfer(from, to, amount);\n\n return true;\n }\n\n /*//////////////////////////////////////////////////////////////\n EIP-2612 LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) public virtual {\n require(deadline >= block.timestamp, \"PERMIT_DEADLINE_EXPIRED\");\n\n // Unchecked because the only math done is incrementing\n // the owner's nonce which cannot realistically overflow.\n unchecked {\n address recoveredAddress = ecrecover(\n keccak256(\n abi.encodePacked(\n \"\\x19\\x01\",\n DOMAIN_SEPARATOR(),\n keccak256(\n abi.encode(\n keccak256(\n \"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)\"\n ),\n owner,\n spender,\n value,\n nonces[owner]++,\n deadline\n )\n )\n )\n ),\n v,\n r,\n s\n );\n\n require(recoveredAddress != address(0) && recoveredAddress == owner, \"INVALID_SIGNER\");\n\n allowance[recoveredAddress][spender] = value;\n }\n\n emit Approval(owner, spender, value);\n }\n\n function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {\n return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();\n }\n\n function computeDomainSeparator() internal view virtual returns (bytes32) {\n return\n keccak256(\n abi.encode(\n keccak256(\"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\"),\n keccak256(bytes(name)),\n keccak256(\"1\"),\n block.chainid,\n address(this)\n )\n );\n }\n\n /*//////////////////////////////////////////////////////////////\n INTERNAL MINT/BURN LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function _mint(address to, uint256 amount) internal virtual {\n totalSupply += amount;\n\n // Cannot overflow because the sum of all user\n // balances can't exceed the max uint256 value.\n unchecked {\n balanceOf[to] += amount;\n }\n\n emit Transfer(address(0), to, amount);\n }\n\n function _burn(address from, uint256 amount) internal virtual {\n balanceOf[from] -= amount;\n\n // Cannot underflow because a user's balance\n // will never be larger than the total supply.\n unchecked {\n totalSupply -= amount;\n }\n\n emit Transfer(from, address(0), amount);\n }\n}\n" + }, + "src/controllers/RefuelSwapAndBridgeController.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {ISocketRequest} from \"../interfaces/ISocketRequest.sol\";\nimport {ISocketRoute} from \"../interfaces/ISocketRoute.sol\";\nimport {BaseController} from \"./BaseController.sol\";\n\n/**\n * @title RefuelSwapAndBridge Controller Implementation\n * @notice Controller with composed actions for Refuel,Swap and Bridge to be executed Sequentially and this is atomic\n * @author Socket dot tech.\n */\ncontract RefuelSwapAndBridgeController is BaseController {\n /// @notice Function-selector to invoke refuel-swap-bridge function\n /// @dev This function selector is to be used while buidling transaction-data\n bytes4 public immutable REFUEL_SWAP_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"refuelAndSwapAndBridge((uint32,bytes,uint32,bytes,uint32,bytes))\"\n )\n );\n\n /// @notice socketGatewayAddress to be initialised via storage variable BaseController\n constructor(\n address _socketGatewayAddress\n ) BaseController(_socketGatewayAddress) {}\n\n /**\n * @notice function to handle refuel followed by Swap and Bridge actions\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param rsbRequest Request with data to execute refuel followed by swap and bridge\n * @return output data from bridging operation\n */\n function refuelAndSwapAndBridge(\n ISocketRequest.RefuelSwapBridgeRequest calldata rsbRequest\n ) public payable returns (bytes memory) {\n _executeRoute(rsbRequest.refuelRouteId, rsbRequest.refuelData);\n\n // refuel is also a bridging activity via refuel-route-implementation\n bytes memory swapResponseData = _executeRoute(\n rsbRequest.swapRouteId,\n rsbRequest.swapData\n );\n\n uint256 swapAmount = abi.decode(swapResponseData, (uint256));\n\n //sequence of arguments for implData: amount, token, data\n // Bridging the swapAmount received in the preceeding step\n bytes memory bridgeImpldata = abi.encodeWithSelector(\n BRIDGE_AFTER_SWAP_SELECTOR,\n swapAmount,\n rsbRequest.bridgeData\n );\n\n return _executeRoute(rsbRequest.bridgeRouteId, bridgeImpldata);\n }\n}\n" + }, + "src/interfaces/ISocketGateway.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/**\n * @title ISocketGateway\n * @notice Interface for SocketGateway functions.\n * @dev functions can be added here for invocation from external contracts or off-chain\n * @author Socket dot tech.\n */\ninterface ISocketGateway {\n /**\n * @notice Request-struct for controllerRequests\n * @dev ensure the value for data is generated using the function-selectors defined in the controllerImplementation contracts\n */\n struct SocketControllerRequest {\n // controllerId is the id mapped to the controllerAddress\n uint32 controllerId;\n // transactionImplData generated off-chain or by caller using function-selector of the controllerContract\n bytes data;\n }\n\n // @notice view to get owner-address\n function owner() external view returns (address);\n}\n" + }, + "src/libraries/Pb.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity ^0.8.4;\n\n// runtime proto sol library\nlibrary Pb {\n enum WireType {\n Varint,\n Fixed64,\n LengthDelim,\n StartGroup,\n EndGroup,\n Fixed32\n }\n\n struct Buffer {\n uint256 idx; // the start index of next read. when idx=b.length, we're done\n bytes b; // hold serialized proto msg, readonly\n }\n\n // create a new in-memory Buffer object from raw msg bytes\n function fromBytes(\n bytes memory raw\n ) internal pure returns (Buffer memory buf) {\n buf.b = raw;\n buf.idx = 0;\n }\n\n // whether there are unread bytes\n function hasMore(Buffer memory buf) internal pure returns (bool) {\n return buf.idx < buf.b.length;\n }\n\n // decode current field number and wiretype\n function decKey(\n Buffer memory buf\n ) internal pure returns (uint256 tag, WireType wiretype) {\n uint256 v = decVarint(buf);\n tag = v / 8;\n wiretype = WireType(v & 7);\n }\n\n // read varint from current buf idx, move buf.idx to next read, return the int value\n function decVarint(Buffer memory buf) internal pure returns (uint256 v) {\n bytes10 tmp; // proto int is at most 10 bytes (7 bits can be used per byte)\n bytes memory bb = buf.b; // get buf.b mem addr to use in assembly\n v = buf.idx; // use v to save one additional uint variable\n assembly {\n tmp := mload(add(add(bb, 32), v)) // load 10 bytes from buf.b[buf.idx] to tmp\n }\n uint256 b; // store current byte content\n v = 0; // reset to 0 for return value\n for (uint256 i = 0; i < 10; i++) {\n assembly {\n b := byte(i, tmp) // don't use tmp[i] because it does bound check and costs extra\n }\n v |= (b & 0x7F) << (i * 7);\n if (b & 0x80 == 0) {\n buf.idx += i + 1;\n return v;\n }\n }\n revert(); // i=10, invalid varint stream\n }\n\n // read length delimited field and return bytes\n function decBytes(\n Buffer memory buf\n ) internal pure returns (bytes memory b) {\n uint256 len = decVarint(buf);\n uint256 end = buf.idx + len;\n require(end <= buf.b.length); // avoid overflow\n b = new bytes(len);\n bytes memory bufB = buf.b; // get buf.b mem addr to use in assembly\n uint256 bStart;\n uint256 bufBStart = buf.idx;\n assembly {\n bStart := add(b, 32)\n bufBStart := add(add(bufB, 32), bufBStart)\n }\n for (uint256 i = 0; i < len; i += 32) {\n assembly {\n mstore(add(bStart, i), mload(add(bufBStart, i)))\n }\n }\n buf.idx = end;\n }\n\n // move idx pass current value field, to beginning of next tag or msg end\n function skipValue(Buffer memory buf, WireType wire) internal pure {\n if (wire == WireType.Varint) {\n decVarint(buf);\n } else if (wire == WireType.LengthDelim) {\n uint256 len = decVarint(buf);\n buf.idx += len; // skip len bytes value data\n require(buf.idx <= buf.b.length); // avoid overflow\n } else {\n revert();\n } // unsupported wiretype\n }\n\n function _uint256(bytes memory b) internal pure returns (uint256 v) {\n require(b.length <= 32); // b's length must be smaller than or equal to 32\n assembly {\n v := mload(add(b, 32))\n } // load all 32bytes to v\n v = v >> (8 * (32 - b.length)); // only first b.length is valid\n }\n\n function _address(bytes memory b) internal pure returns (address v) {\n v = _addressPayable(b);\n }\n\n function _addressPayable(\n bytes memory b\n ) internal pure returns (address payable v) {\n require(b.length == 20);\n //load 32bytes then shift right 12 bytes\n assembly {\n v := div(mload(add(b, 32)), 0x1000000000000000000000000)\n }\n }\n\n function _bytes32(bytes memory b) internal pure returns (bytes32 v) {\n require(b.length == 32);\n assembly {\n v := mload(add(b, 32))\n }\n }\n}\n" + }, + "src/bridges/BridgeImplBase.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {ISocketGateway} from \"../interfaces/ISocketGateway.sol\";\nimport {ISocketRoute} from \"../interfaces/ISocketRoute.sol\";\nimport {OnlySocketGatewayOwner, OnlySocketDeployer} from \"../errors/SocketErrors.sol\";\n\n/**\n * @title Abstract Implementation Contract.\n * @notice All Bridge Implementation will follow this interface.\n */\nabstract contract BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n /// @notice Address used to identify if it is a native token transfer or not\n address public immutable NATIVE_TOKEN_ADDRESS =\n address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);\n\n /// @notice immutable variable to store the socketGateway address\n address public immutable socketGateway;\n\n /// @notice immutable variable to store the socketGateway address\n address public immutable socketDeployFactory;\n\n /// @notice immutable variable with instance of SocketRoute to access route functions\n ISocketRoute public immutable socketRoute;\n\n /// @notice FunctionSelector used to delegatecall from swap to the function of bridge router implementation\n bytes4 public immutable BRIDGE_AFTER_SWAP_SELECTOR =\n bytes4(keccak256(\"bridgeAfterSwap(uint256,bytes)\"));\n\n /****************************************\n * EVENTS *\n ****************************************/\n\n event SocketBridge(\n uint256 amount,\n address token,\n uint256 toChainId,\n bytes32 bridgeName,\n address sender,\n address receiver,\n bytes32 metadata\n );\n\n /**\n * @notice Construct the base for all BridgeImplementations.\n * @param _socketGateway Socketgateway address, an immutable variable to set.\n * @param _socketDeployFactory Socket Deploy Factory address, an immutable variable to set.\n */\n constructor(address _socketGateway, address _socketDeployFactory) {\n socketGateway = _socketGateway;\n socketDeployFactory = _socketDeployFactory;\n socketRoute = ISocketRoute(_socketGateway);\n }\n\n /****************************************\n * MODIFIERS *\n ****************************************/\n\n /// @notice Implementing contract needs to make use of the modifier where restricted access is to be used\n modifier isSocketGatewayOwner() {\n if (msg.sender != ISocketGateway(socketGateway).owner()) {\n revert OnlySocketGatewayOwner();\n }\n _;\n }\n\n /// @notice Implementing contract needs to make use of the modifier where restricted access is to be used\n modifier isSocketDeployFactory() {\n if (msg.sender != socketDeployFactory) {\n revert OnlySocketDeployer();\n }\n _;\n }\n\n /****************************************\n * RESTRICTED FUNCTIONS *\n ****************************************/\n\n /**\n * @notice function to rescue the ERC20 tokens in the bridge Implementation contract\n * @notice this is a function restricted to Owner of SocketGateway only\n * @param token address of ERC20 token being rescued\n * @param userAddress receipient address to which ERC20 tokens will be rescued to\n * @param amount amount of ERC20 tokens being rescued\n */\n function rescueFunds(\n address token,\n address userAddress,\n uint256 amount\n ) external isSocketGatewayOwner {\n ERC20(token).safeTransfer(userAddress, amount);\n }\n\n /**\n * @notice function to rescue the native-balance in the bridge Implementation contract\n * @notice this is a function restricted to Owner of SocketGateway only\n * @param userAddress receipient address to which native-balance will be rescued to\n * @param amount amount of native balance tokens being rescued\n */\n function rescueEther(\n address payable userAddress,\n uint256 amount\n ) external isSocketGatewayOwner {\n userAddress.transfer(amount);\n }\n\n function killme() external isSocketDeployFactory {\n selfdestruct(payable(msg.sender));\n }\n\n /******************************\n * VIRTUAL FUNCTIONS *\n *****************************/\n\n /**\n * @notice function to bridge which is succeeding the swap function\n * @notice this function is to be used only when bridging as a succeeding step\n * @notice All bridge implementation contracts must implement this function\n * @notice bridge-implementations will have a bridge specific struct with properties used in bridging\n * @param bridgeData encoded value of properties in the bridgeData Struct\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable virtual;\n}\n" + }, + "src/bridges/cbridge/CelerImpl.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport \"../../libraries/Pb.sol\";\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport \"./interfaces/cbridge.sol\";\nimport \"./interfaces/ICelerStorageWrapper.sol\";\nimport {TransferIdExists, InvalidCelerRefund, CelerAlreadyRefunded, CelerRefundNotReady} from \"../../errors/SocketErrors.sol\";\nimport {BridgeImplBase} from \"../BridgeImplBase.sol\";\nimport {CBRIDGE} from \"../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Celer-Route Implementation\n * @notice Route implementation with functions to bridge ERC20 and Native via Celer-Bridge\n * Called via SocketGateway if the routeId in the request maps to the routeId of CelerImplementation\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ncontract CelerImpl is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable CBridgeIdentifier = CBRIDGE;\n\n /// @notice Utility to perform operation on Buffer\n using Pb for Pb.Buffer;\n\n /// @notice Function-selector for ERC20-token bridging on Celer-Route\n /// @dev This function selector is to be used while building transaction-data to bridge ERC20 tokens\n bytes4 public immutable CELER_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(address,address,uint256,bytes32,uint64,uint64,uint32)\"\n )\n );\n\n /// @notice Function-selector for Native bridging on Celer-Route\n /// @dev This function selector is to be used while building transaction-data to bridge Native tokens\n bytes4 public immutable CELER_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeNativeTo(address,uint256,bytes32,uint64,uint64,uint32)\"\n )\n );\n\n bytes4 public immutable CELER_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(address,uint64,uint32,uint64,bytes32))\"\n )\n );\n\n /// @notice router Contract instance used to deposit ERC20 and Native on to Celer-Bridge\n /// @dev contract instance is to be initialized in the constructor using the routerAddress passed as constructor argument\n ICBridge public immutable router;\n\n /// @notice celerStorageWrapper Contract instance used to store the transferId generated during ERC20 and Native bridge on to Celer-Bridge\n /// @dev contract instance is to be initialized in the constructor using the celerStorageWrapperAddress passed as constructor argument\n ICelerStorageWrapper public immutable celerStorageWrapper;\n\n /// @notice WETH token address\n address public immutable weth;\n\n /// @notice chainId used during generation of transferId generated while bridging ERC20 and Native on to Celer-Bridge\n /// @dev this is to be initialised in the constructor\n uint64 public immutable chainId;\n\n struct WithdrawMsg {\n uint64 chainid; // tag: 1\n uint64 seqnum; // tag: 2\n address receiver; // tag: 3\n address token; // tag: 4\n uint256 amount; // tag: 5\n bytes32 refid; // tag: 6\n }\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n /// @dev ensure routerAddress, weth-address, celerStorageWrapperAddress are set properly for the chainId in which the contract is being deployed\n constructor(\n address _routerAddress,\n address _weth,\n address _celerStorageWrapperAddress,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n router = ICBridge(_routerAddress);\n celerStorageWrapper = ICelerStorageWrapper(_celerStorageWrapperAddress);\n weth = _weth;\n chainId = uint64(block.chainid);\n }\n\n // Function to receive Ether. msg.data must be empty\n receive() external payable {}\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct CelerBridgeDataNoToken {\n address receiverAddress;\n uint64 toChainId;\n uint32 maxSlippage;\n uint64 nonce;\n bytes32 metadata;\n }\n\n struct CelerBridgeData {\n address token;\n address receiverAddress;\n uint64 toChainId;\n uint32 maxSlippage;\n uint64 nonce;\n bytes32 metadata;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in CelerBridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for CelerBridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n CelerBridgeData memory celerBridgeData = abi.decode(\n bridgeData,\n (CelerBridgeData)\n );\n\n if (celerBridgeData.token == NATIVE_TOKEN_ADDRESS) {\n // transferId is generated using the request-params and nonce of the account\n // transferId should be unique for each request and this is used while handling refund from celerBridge\n bytes32 transferId = keccak256(\n abi.encodePacked(\n address(this),\n celerBridgeData.receiverAddress,\n weth,\n amount,\n celerBridgeData.toChainId,\n celerBridgeData.nonce,\n chainId\n )\n );\n\n // transferId is stored in CelerStorageWrapper with in a mapping where key is transferId and value is the msg-sender\n celerStorageWrapper.setAddressForTransferId(transferId, msg.sender);\n\n router.sendNative{value: amount}(\n celerBridgeData.receiverAddress,\n amount,\n celerBridgeData.toChainId,\n celerBridgeData.nonce,\n celerBridgeData.maxSlippage\n );\n } else {\n // transferId is generated using the request-params and nonce of the account\n // transferId should be unique for each request and this is used while handling refund from celerBridge\n bytes32 transferId = keccak256(\n abi.encodePacked(\n address(this),\n celerBridgeData.receiverAddress,\n celerBridgeData.token,\n amount,\n celerBridgeData.toChainId,\n celerBridgeData.nonce,\n chainId\n )\n );\n\n // transferId is stored in CelerStorageWrapper with in a mapping where key is transferId and value is the msg-sender\n celerStorageWrapper.setAddressForTransferId(transferId, msg.sender);\n router.send(\n celerBridgeData.receiverAddress,\n celerBridgeData.token,\n amount,\n celerBridgeData.toChainId,\n celerBridgeData.nonce,\n celerBridgeData.maxSlippage\n );\n }\n\n emit SocketBridge(\n amount,\n celerBridgeData.token,\n celerBridgeData.toChainId,\n CBridgeIdentifier,\n msg.sender,\n celerBridgeData.receiverAddress,\n celerBridgeData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in CelerBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param celerBridgeData encoded data for CelerBridgeData\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n CelerBridgeDataNoToken calldata celerBridgeData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n\n if (token == NATIVE_TOKEN_ADDRESS) {\n // transferId is generated using the request-params and nonce of the account\n // transferId should be unique for each request and this is used while handling refund from celerBridge\n bytes32 transferId = keccak256(\n abi.encodePacked(\n address(this),\n celerBridgeData.receiverAddress,\n weth,\n bridgeAmount,\n celerBridgeData.toChainId,\n celerBridgeData.nonce,\n chainId\n )\n );\n\n // transferId is stored in CelerStorageWrapper with in a mapping where key is transferId and value is the msg-sender\n celerStorageWrapper.setAddressForTransferId(transferId, msg.sender);\n\n router.sendNative{value: bridgeAmount}(\n celerBridgeData.receiverAddress,\n bridgeAmount,\n celerBridgeData.toChainId,\n celerBridgeData.nonce,\n celerBridgeData.maxSlippage\n );\n } else {\n // transferId is generated using the request-params and nonce of the account\n // transferId should be unique for each request and this is used while handling refund from celerBridge\n bytes32 transferId = keccak256(\n abi.encodePacked(\n address(this),\n celerBridgeData.receiverAddress,\n token,\n bridgeAmount,\n celerBridgeData.toChainId,\n celerBridgeData.nonce,\n chainId\n )\n );\n\n // transferId is stored in CelerStorageWrapper with in a mapping where key is transferId and value is the msg-sender\n celerStorageWrapper.setAddressForTransferId(transferId, msg.sender);\n router.send(\n celerBridgeData.receiverAddress,\n token,\n bridgeAmount,\n celerBridgeData.toChainId,\n celerBridgeData.nonce,\n celerBridgeData.maxSlippage\n );\n }\n\n emit SocketBridge(\n bridgeAmount,\n token,\n celerBridgeData.toChainId,\n CBridgeIdentifier,\n msg.sender,\n celerBridgeData.receiverAddress,\n celerBridgeData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via Celer-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param receiverAddress address of recipient\n * @param token address of token being bridged\n * @param amount amount of token for bridging\n * @param toChainId destination ChainId\n * @param nonce nonce of the sender-account address\n * @param maxSlippage maximum Slippage for the bridging\n */\n function bridgeERC20To(\n address receiverAddress,\n address token,\n uint256 amount,\n bytes32 metadata,\n uint64 toChainId,\n uint64 nonce,\n uint32 maxSlippage\n ) external payable {\n /// @notice transferId is generated using the request-params and nonce of the account\n /// @notice transferId should be unique for each request and this is used while handling refund from celerBridge\n bytes32 transferId = keccak256(\n abi.encodePacked(\n address(this),\n receiverAddress,\n token,\n amount,\n toChainId,\n nonce,\n chainId\n )\n );\n\n /// @notice stored in the CelerStorageWrapper contract\n celerStorageWrapper.setAddressForTransferId(transferId, msg.sender);\n\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n router.send(\n receiverAddress,\n token,\n amount,\n toChainId,\n nonce,\n maxSlippage\n );\n\n emit SocketBridge(\n amount,\n token,\n toChainId,\n CBridgeIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n\n /**\n * @notice function to handle Native bridging to receipent via Celer-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param receiverAddress address of recipient\n * @param amount amount of token for bridging\n * @param toChainId destination ChainId\n * @param nonce nonce of the sender-account address\n * @param maxSlippage maximum Slippage for the bridging\n */\n function bridgeNativeTo(\n address receiverAddress,\n uint256 amount,\n bytes32 metadata,\n uint64 toChainId,\n uint64 nonce,\n uint32 maxSlippage\n ) external payable {\n bytes32 transferId = keccak256(\n abi.encodePacked(\n address(this),\n receiverAddress,\n weth,\n amount,\n toChainId,\n nonce,\n chainId\n )\n );\n\n celerStorageWrapper.setAddressForTransferId(transferId, msg.sender);\n\n router.sendNative{value: amount}(\n receiverAddress,\n amount,\n toChainId,\n nonce,\n maxSlippage\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n toChainId,\n CBridgeIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n\n /**\n * @notice function to handle refund from CelerBridge-Router\n * @param _request request data generated offchain using the celer-SDK\n * @param _sigs generated offchain using the celer-SDK\n * @param _signers generated offchain using the celer-SDK\n * @param _powers generated offchain using the celer-SDK\n */\n function refundCelerUser(\n bytes calldata _request,\n bytes[] calldata _sigs,\n address[] calldata _signers,\n uint256[] calldata _powers\n ) external payable {\n WithdrawMsg memory request = decWithdrawMsg(_request);\n bytes32 transferId = keccak256(\n abi.encodePacked(\n request.chainid,\n request.seqnum,\n request.receiver,\n request.token,\n request.amount\n )\n );\n uint256 _initialNativeBalance = address(this).balance;\n uint256 _initialTokenBalance = ERC20(request.token).balanceOf(\n address(this)\n );\n if (!router.withdraws(transferId)) {\n router.withdraw(_request, _sigs, _signers, _powers);\n }\n\n if (request.receiver != socketGateway) {\n revert InvalidCelerRefund();\n }\n\n address _receiver = celerStorageWrapper.getAddressFromTransferId(\n request.refid\n );\n celerStorageWrapper.deleteTransferId(request.refid);\n\n if (_receiver == address(0)) {\n revert CelerAlreadyRefunded();\n }\n\n uint256 _nativeBalanceAfter = address(this).balance;\n uint256 _tokenBalanceAfter = ERC20(request.token).balanceOf(\n address(this)\n );\n if (_nativeBalanceAfter > _initialNativeBalance) {\n if ((_nativeBalanceAfter - _initialNativeBalance) != request.amount)\n revert CelerRefundNotReady();\n payable(_receiver).transfer(request.amount);\n return;\n }\n\n if (_tokenBalanceAfter > _initialTokenBalance) {\n if ((_tokenBalanceAfter - _initialTokenBalance) != request.amount)\n revert CelerRefundNotReady();\n ERC20(request.token).safeTransfer(_receiver, request.amount);\n return;\n }\n\n revert CelerRefundNotReady();\n }\n\n function decWithdrawMsg(\n bytes memory raw\n ) internal pure returns (WithdrawMsg memory m) {\n Pb.Buffer memory buf = Pb.fromBytes(raw);\n\n uint256 tag;\n Pb.WireType wire;\n while (buf.hasMore()) {\n (tag, wire) = buf.decKey();\n if (false) {}\n // solidity has no switch/case\n else if (tag == 1) {\n m.chainid = uint64(buf.decVarint());\n } else if (tag == 2) {\n m.seqnum = uint64(buf.decVarint());\n } else if (tag == 3) {\n m.receiver = Pb._address(buf.decBytes());\n } else if (tag == 4) {\n m.token = Pb._address(buf.decBytes());\n } else if (tag == 5) {\n m.amount = Pb._uint256(buf.decBytes());\n } else if (tag == 6) {\n m.refid = Pb._bytes32(buf.decBytes());\n } else {\n buf.skipValue(wire);\n } // skip value of unknown tag\n }\n } // end decoder WithdrawMsg\n}\n" + }, + "src/libraries/LibBytes.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n// Functions taken out from https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol\nlibrary LibBytes {\n // solhint-disable no-inline-assembly\n\n // LibBytes specific errors\n error SliceOverflow();\n error SliceOutOfBounds();\n error AddressOutOfBounds();\n error UintOutOfBounds();\n\n // -------------------------\n\n function concat(\n bytes memory _preBytes,\n bytes memory _postBytes\n ) internal pure returns (bytes memory) {\n bytes memory tempBytes;\n\n assembly {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // Store the length of the first bytes array at the beginning of\n // the memory for tempBytes.\n let length := mload(_preBytes)\n mstore(tempBytes, length)\n\n // Maintain a memory counter for the current write location in the\n // temp bytes array by adding the 32 bytes for the array length to\n // the starting location.\n let mc := add(tempBytes, 0x20)\n // Stop copying when the memory counter reaches the length of the\n // first bytes array.\n let end := add(mc, length)\n\n for {\n // Initialize a copy counter to the start of the _preBytes data,\n // 32 bytes into its memory.\n let cc := add(_preBytes, 0x20)\n } lt(mc, end) {\n // Increase both counters by 32 bytes each iteration.\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n // Write the _preBytes data into the tempBytes memory 32 bytes\n // at a time.\n mstore(mc, mload(cc))\n }\n\n // Add the length of _postBytes to the current length of tempBytes\n // and store it as the new length in the first 32 bytes of the\n // tempBytes memory.\n length := mload(_postBytes)\n mstore(tempBytes, add(length, mload(tempBytes)))\n\n // Move the memory counter back from a multiple of 0x20 to the\n // actual end of the _preBytes data.\n mc := end\n // Stop copying when the memory counter reaches the new combined\n // length of the arrays.\n end := add(mc, length)\n\n for {\n let cc := add(_postBytes, 0x20)\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n // Update the free-memory pointer by padding our last write location\n // to 32 bytes: add 31 bytes to the end of tempBytes to move to the\n // next 32 byte block, then round down to the nearest multiple of\n // 32. If the sum of the length of the two arrays is zero then add\n // one before rounding down to leave a blank 32 bytes (the length block with 0).\n mstore(\n 0x40,\n and(\n add(add(end, iszero(add(length, mload(_preBytes)))), 31),\n not(31) // Round down to the nearest 32 bytes.\n )\n )\n }\n\n return tempBytes;\n }\n\n function slice(\n bytes memory _bytes,\n uint256 _start,\n uint256 _length\n ) internal pure returns (bytes memory) {\n if (_length + 31 < _length) {\n revert SliceOverflow();\n }\n if (_bytes.length < _start + _length) {\n revert SliceOutOfBounds();\n }\n\n bytes memory tempBytes;\n\n assembly {\n switch iszero(_length)\n case 0 {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // The first word of the slice result is potentially a partial\n // word read from the original array. To read it, we calculate\n // the length of that partial word and start copying that many\n // bytes into the array. The first word we copy will start with\n // data we don't care about, but the last `lengthmod` bytes will\n // land at the beginning of the contents of the new array. When\n // we're done copying, we overwrite the full first word with\n // the actual length of the slice.\n let lengthmod := and(_length, 31)\n\n // The multiplication in the next line is necessary\n // because when slicing multiples of 32 bytes (lengthmod == 0)\n // the following copy loop was copying the origin's length\n // and then ending prematurely not copying everything it should.\n let mc := add(\n add(tempBytes, lengthmod),\n mul(0x20, iszero(lengthmod))\n )\n let end := add(mc, _length)\n\n for {\n // The multiplication in the next line has the same exact purpose\n // as the one above.\n let cc := add(\n add(\n add(_bytes, lengthmod),\n mul(0x20, iszero(lengthmod))\n ),\n _start\n )\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n mstore(tempBytes, _length)\n\n //update free-memory pointer\n //allocating the array padded to 32 bytes like the compiler does now\n mstore(0x40, and(add(mc, 31), not(31)))\n }\n //if we want a zero-length slice let's just return a zero-length array\n default {\n tempBytes := mload(0x40)\n //zero out the 32 bytes slice we are about to return\n //we need to do it because Solidity does not garbage collect\n mstore(tempBytes, 0)\n\n mstore(0x40, add(tempBytes, 0x20))\n }\n }\n\n return tempBytes;\n }\n}\n" + }, + "src/bridges/hyphen/interfaces/hyphen.sol": { + "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity >=0.8.0;\n\n/**\n * @title HyphenLiquidityPoolManager\n * @notice interface with functions to bridge ERC20 and Native via Hyphen-Bridge\n * @author Socket dot tech.\n */\ninterface HyphenLiquidityPoolManager {\n /**\n * @dev Function used to deposit tokens into pool to initiate a cross chain token transfer.\n * @param toChainId Chain id where funds needs to be transfered\n * @param tokenAddress ERC20 Token address that needs to be transfered\n * @param receiver Address on toChainId where tokens needs to be transfered\n * @param amount Amount of token being transfered\n */\n function depositErc20(\n uint256 toChainId,\n address tokenAddress,\n address receiver,\n uint256 amount,\n string calldata tag\n ) external;\n\n /**\n * @dev Function used to deposit native token into pool to initiate a cross chain token transfer.\n * @param receiver Address on toChainId where tokens needs to be transfered\n * @param toChainId Chain id where funds needs to be transfered\n */\n function depositNative(\n address receiver,\n uint256 toChainId,\n string calldata tag\n ) external payable;\n}\n" + }, + "src/swap/SwapImplBase.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {ISocketGateway} from \"../interfaces/ISocketGateway.sol\";\nimport {OnlySocketGatewayOwner, OnlySocketDeployer} from \"../errors/SocketErrors.sol\";\n\n/**\n * @title Abstract Implementation Contract.\n * @notice All Swap Implementation will follow this interface.\n * @author Socket dot tech.\n */\nabstract contract SwapImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n /// @notice Address used to identify if it is a native token transfer or not\n address public immutable NATIVE_TOKEN_ADDRESS =\n address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);\n\n /// @notice immutable variable to store the socketGateway address\n address public immutable socketGateway;\n\n /// @notice immutable variable to store the socketGateway address\n address public immutable socketDeployFactory;\n\n /// @notice FunctionSelector used to delegatecall to the performAction function of swap-router-implementation\n bytes4 public immutable SWAP_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\"performAction(address,address,uint256,address,bytes)\")\n );\n\n /// @notice FunctionSelector used to delegatecall to the performActionWithIn function of swap-router-implementation\n bytes4 public immutable SWAP_WITHIN_FUNCTION_SELECTOR =\n bytes4(keccak256(\"performActionWithIn(address,address,uint256,bytes)\"));\n\n /****************************************\n * EVENTS *\n ****************************************/\n\n event SocketSwapTokens(\n address fromToken,\n address toToken,\n uint256 buyAmount,\n uint256 sellAmount,\n bytes32 routeName,\n address receiver\n );\n\n /**\n * @notice Construct the base for all SwapImplementations.\n * @param _socketGateway Socketgateway address, an immutable variable to set.\n */\n constructor(address _socketGateway, address _socketDeployFactory) {\n socketGateway = _socketGateway;\n socketDeployFactory = _socketDeployFactory;\n }\n\n /****************************************\n * MODIFIERS *\n ****************************************/\n\n /// @notice Implementing contract needs to make use of the modifier where restricted access is to be used\n modifier isSocketGatewayOwner() {\n if (msg.sender != ISocketGateway(socketGateway).owner()) {\n revert OnlySocketGatewayOwner();\n }\n _;\n }\n\n /// @notice Implementing contract needs to make use of the modifier where restricted access is to be used\n modifier isSocketDeployFactory() {\n if (msg.sender != socketDeployFactory) {\n revert OnlySocketDeployer();\n }\n _;\n }\n\n /****************************************\n * RESTRICTED FUNCTIONS *\n ****************************************/\n\n /**\n * @notice function to rescue the ERC20 tokens in the Swap-Implementation contract\n * @notice this is a function restricted to Owner of SocketGateway only\n * @param token address of ERC20 token being rescued\n * @param userAddress receipient address to which ERC20 tokens will be rescued to\n * @param amount amount of ERC20 tokens being rescued\n */\n function rescueFunds(\n address token,\n address userAddress,\n uint256 amount\n ) external isSocketGatewayOwner {\n ERC20(token).safeTransfer(userAddress, amount);\n }\n\n /**\n * @notice function to rescue the native-balance in the Swap-Implementation contract\n * @notice this is a function restricted to Owner of SocketGateway only\n * @param userAddress receipient address to which native-balance will be rescued to\n * @param amount amount of native balance tokens being rescued\n */\n function rescueEther(\n address payable userAddress,\n uint256 amount\n ) external isSocketGatewayOwner {\n userAddress.transfer(amount);\n }\n\n function killme() external isSocketDeployFactory {\n selfdestruct(payable(msg.sender));\n }\n\n /******************************\n * VIRTUAL FUNCTIONS *\n *****************************/\n\n /**\n * @notice function to swap tokens on the chain\n * All swap implementation contracts must implement this function\n * @param fromToken token to be swapped\n * @param toToken token to which fromToken has to be swapped\n * @param amount amount of fromToken being swapped\n * @param receiverAddress recipient address of toToken\n * @param data encoded value of properties in the swapData Struct\n */\n function performAction(\n address fromToken,\n address toToken,\n uint256 amount,\n address receiverAddress,\n bytes memory data\n ) external payable virtual returns (uint256);\n\n /**\n * @notice function to swapWith - swaps tokens on the chain to socketGateway as recipient\n * All swap implementation contracts must implement this function\n * @param fromToken token to be swapped\n * @param toToken token to which fromToken has to be swapped\n * @param amount amount of fromToken being swapped\n * @param swapExtraData encoded value of properties in the swapData Struct\n */\n function performActionWithIn(\n address fromToken,\n address toToken,\n uint256 amount,\n bytes memory swapExtraData\n ) external payable virtual returns (uint256, address);\n}\n" + }, + "src/swap/zerox/ZeroXSwapImpl.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport \"../SwapImplBase.sol\";\nimport {Address0Provided, SwapFailed} from \"../../errors/SocketErrors.sol\";\nimport {ZEROX} from \"../../static/RouteIdentifiers.sol\";\n\n/**\n * @title ZeroX-Swap-Route Implementation\n * @notice Route implementation with functions to swap tokens via ZeroX-Swap\n * Called via SocketGateway if the routeId in the request maps to the routeId of ZeroX-Swap-Implementation\n * @author Socket dot tech.\n */\ncontract ZeroXSwapImpl is SwapImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable ZeroXIdentifier = ZEROX;\n\n /// @notice unique name to identify the router, used to emit event upon successful bridging\n bytes32 public immutable NAME = keccak256(\"Zerox-Router\");\n\n /// @notice address of ZeroX-Exchange-Proxy to swap the tokens on Chain\n address payable public immutable zeroXExchangeProxy;\n\n /// @notice socketGatewayAddress to be initialised via storage variable SwapImplBase\n /// @notice ZeroXExchangeProxy contract is payable to allow ethereum swaps\n /// @dev ensure _zeroXExchangeProxy are set properly for the chainId in which the contract is being deployed\n constructor(\n address _zeroXExchangeProxy,\n address _socketGateway,\n address _socketDeployFactory\n ) SwapImplBase(_socketGateway, _socketDeployFactory) {\n zeroXExchangeProxy = payable(_zeroXExchangeProxy);\n }\n\n receive() external payable {}\n\n fallback() external payable {}\n\n /**\n * @notice function to swap tokens on the chain and transfer to receiver address\n * @dev This is called only when there is a request for a swap.\n * @param fromToken token to be swapped\n * @param toToken token to which fromToken is to be swapped\n * @param amount amount to be swapped\n * @param receiverAddress address of toToken recipient\n * @param swapExtraData data required for zeroX Exchange to get the swap done\n */\n function performAction(\n address fromToken,\n address toToken,\n uint256 amount,\n address receiverAddress,\n bytes calldata swapExtraData\n ) external payable override returns (uint256) {\n if (fromToken == address(0)) {\n revert Address0Provided();\n }\n\n bytes memory swapCallData = abi.decode(swapExtraData, (bytes));\n\n uint256 _initialBalanceTokenOut;\n uint256 _finalBalanceTokenOut;\n\n ERC20 erc20ToToken = ERC20(toToken);\n if (toToken != NATIVE_TOKEN_ADDRESS) {\n _initialBalanceTokenOut = erc20ToToken.balanceOf(address(this));\n } else {\n _initialBalanceTokenOut = address(this).balance;\n }\n\n if (fromToken != NATIVE_TOKEN_ADDRESS) {\n ERC20 token = ERC20(fromToken);\n token.safeTransferFrom(msg.sender, address(this), amount);\n token.safeApprove(zeroXExchangeProxy, amount);\n\n // solhint-disable-next-line\n (bool success, ) = zeroXExchangeProxy.call(swapCallData);\n\n if (!success) {\n revert SwapFailed();\n }\n\n token.safeApprove(zeroXExchangeProxy, 0);\n } else {\n (bool success, ) = zeroXExchangeProxy.call{value: amount}(\n swapCallData\n );\n if (!success) {\n revert SwapFailed();\n }\n }\n\n if (toToken != NATIVE_TOKEN_ADDRESS) {\n _finalBalanceTokenOut = erc20ToToken.balanceOf(address(this));\n } else {\n _finalBalanceTokenOut = address(this).balance;\n }\n\n uint256 returnAmount = _finalBalanceTokenOut - _initialBalanceTokenOut;\n\n if (toToken == NATIVE_TOKEN_ADDRESS) {\n payable(receiverAddress).transfer(returnAmount);\n } else {\n erc20ToToken.transfer(receiverAddress, returnAmount);\n }\n\n emit SocketSwapTokens(\n fromToken,\n toToken,\n returnAmount,\n amount,\n ZeroXIdentifier,\n receiverAddress\n );\n\n return returnAmount;\n }\n\n /**\n * @notice function to swapWithIn SocketGateway - swaps tokens on the chain to socketGateway as recipient\n * @param fromToken token to be swapped\n * @param toToken token to which fromToken has to be swapped\n * @param amount amount of fromToken being swapped\n * @param swapExtraData encoded value of properties in the swapData Struct\n * @return swapped amount (in toToken Address)\n */\n function performActionWithIn(\n address fromToken,\n address toToken,\n uint256 amount,\n bytes calldata swapExtraData\n ) external payable override returns (uint256, address) {\n if (fromToken == address(0)) {\n revert Address0Provided();\n }\n\n bytes memory swapCallData = abi.decode(swapExtraData, (bytes));\n\n uint256 _initialBalanceTokenOut;\n uint256 _finalBalanceTokenOut;\n\n ERC20 erc20ToToken = ERC20(toToken);\n if (toToken != NATIVE_TOKEN_ADDRESS) {\n _initialBalanceTokenOut = erc20ToToken.balanceOf(address(this));\n } else {\n _initialBalanceTokenOut = address(this).balance;\n }\n\n if (fromToken != NATIVE_TOKEN_ADDRESS) {\n ERC20 token = ERC20(fromToken);\n token.safeTransferFrom(msg.sender, address(this), amount);\n token.safeApprove(zeroXExchangeProxy, amount);\n\n // solhint-disable-next-line\n (bool success, ) = zeroXExchangeProxy.call(swapCallData);\n\n if (!success) {\n revert SwapFailed();\n }\n\n token.safeApprove(zeroXExchangeProxy, 0);\n } else {\n (bool success, ) = zeroXExchangeProxy.call{value: amount}(\n swapCallData\n );\n if (!success) {\n revert SwapFailed();\n }\n }\n\n if (toToken != NATIVE_TOKEN_ADDRESS) {\n _finalBalanceTokenOut = erc20ToToken.balanceOf(address(this));\n } else {\n _finalBalanceTokenOut = address(this).balance;\n }\n\n uint256 returnAmount = _finalBalanceTokenOut - _initialBalanceTokenOut;\n\n emit SocketSwapTokens(\n fromToken,\n toToken,\n returnAmount,\n amount,\n ZeroXIdentifier,\n socketGateway\n );\n\n return (returnAmount, toToken);\n }\n}\n" + }, + "src/bridges/cbridge/interfaces/cbridge.sol": { + "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity >=0.8.0;\n\ninterface ICBridge {\n function send(\n address _receiver,\n address _token,\n uint256 _amount,\n uint64 _dstChinId,\n uint64 _nonce,\n uint32 _maxSlippage\n ) external;\n\n function sendNative(\n address _receiver,\n uint256 _amount,\n uint64 _dstChinId,\n uint64 _nonce,\n uint32 _maxSlippage\n ) external payable;\n\n function withdraws(bytes32 withdrawId) external view returns (bool);\n\n function withdraw(\n bytes calldata _wdmsg,\n bytes[] calldata _sigs,\n address[] calldata _signers,\n uint256[] calldata _powers\n ) external;\n}\n" + }, + "src/bridges/stargate/l2/Stargate.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport \"../interfaces/stargate.sol\";\nimport \"../../../errors/SocketErrors.sol\";\nimport {BridgeImplBase} from \"../../BridgeImplBase.sol\";\nimport {STARGATE} from \"../../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Stargate-L2-Route Implementation\n * @notice Route implementation with functions to bridge ERC20 and Native via Stargate-L2-Bridge\n * Called via SocketGateway if the routeId in the request maps to the routeId of Stargate-L2-Implementation\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ncontract StargateImplL2 is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable StargateIdentifier = STARGATE;\n\n /// @notice Function-selector for ERC20-token bridging on Stargate-L2-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4\n public immutable STARGATE_L2_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(address,address,address,uint256,uint256,uint256,(uint256,uint256,uint256,uint256,bytes32,bytes,uint16))\"\n )\n );\n\n bytes4 public immutable STARGATE_L1_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(address,address,uint16,uint256,uint256,uint256,uint256,uint256,uint256,bytes32,bytes))\"\n )\n );\n\n /// @notice Function-selector for Native bridging on Stargate-L2-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge Native tokens\n bytes4\n public immutable STARGATE_L2_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeNativeTo(address,address,uint16,uint256,uint256,uint256,bytes32)\"\n )\n );\n\n /// @notice Stargate Router to bridge ERC20 tokens\n IBridgeStargate public immutable router;\n\n /// @notice Stargate Router to bridge native tokens\n IBridgeStargate public immutable routerETH;\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n /// @dev ensure router, routerEth are set properly for the chainId in which the contract is being deployed\n constructor(\n address _router,\n address _routerEth,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n router = IBridgeStargate(_router);\n routerETH = IBridgeStargate(_routerEth);\n }\n\n /// @notice Struct to be used as a input parameter for Bridging tokens via Stargate-L2-route\n /// @dev while building transactionData,values should be set in this sequence of properties in this struct\n struct StargateBridgeExtraData {\n uint256 srcPoolId;\n uint256 dstPoolId;\n uint256 destinationGasLimit;\n uint256 minReceivedAmt;\n bytes32 metadata;\n bytes destinationPayload;\n uint16 stargateDstChainId; // stargate defines chain id in its way\n }\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct StargateBridgeDataNoToken {\n address receiverAddress;\n address senderAddress;\n uint16 stargateDstChainId; // stargate defines chain id in its way\n uint256 value;\n // a unique identifier that is uses to dedup transfers\n // this value is the a timestamp sent from frontend, but in theory can be any unique number\n uint256 srcPoolId;\n uint256 dstPoolId;\n uint256 minReceivedAmt; // defines the slippage, the min qty you would accept on the destination\n uint256 optionalValue;\n uint256 destinationGasLimit;\n bytes32 metadata;\n bytes destinationPayload;\n }\n\n struct StargateBridgeData {\n address token;\n address receiverAddress;\n address senderAddress;\n uint16 stargateDstChainId; // stargate defines chain id in its way\n uint256 value;\n // a unique identifier that is uses to dedup transfers\n // this value is the a timestamp sent from frontend, but in theory can be any unique number\n uint256 srcPoolId;\n uint256 dstPoolId;\n uint256 minReceivedAmt; // defines the slippage, the min qty you would accept on the destination\n uint256 optionalValue;\n uint256 destinationGasLimit;\n bytes32 metadata;\n bytes destinationPayload;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in Stargate-BridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for Stargate-L1-Bridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n StargateBridgeData memory stargateBridgeData = abi.decode(\n bridgeData,\n (StargateBridgeData)\n );\n\n if (stargateBridgeData.token == NATIVE_TOKEN_ADDRESS) {\n // perform bridging\n routerETH.swapETH{value: amount + stargateBridgeData.optionalValue}(\n stargateBridgeData.stargateDstChainId,\n payable(stargateBridgeData.senderAddress),\n abi.encodePacked(stargateBridgeData.receiverAddress),\n amount,\n stargateBridgeData.minReceivedAmt\n );\n } else {\n ERC20(stargateBridgeData.token).safeApprove(\n address(router),\n amount\n );\n {\n router.swap{value: stargateBridgeData.value}(\n stargateBridgeData.stargateDstChainId,\n stargateBridgeData.srcPoolId,\n stargateBridgeData.dstPoolId,\n payable(stargateBridgeData.senderAddress), // default to refund to main contract\n amount,\n stargateBridgeData.minReceivedAmt,\n IBridgeStargate.lzTxObj(\n stargateBridgeData.destinationGasLimit,\n 0, // zero amount since this is a ERC20 bridging\n \"0x\" //empty data since this is for only ERC20\n ),\n abi.encodePacked(stargateBridgeData.receiverAddress),\n stargateBridgeData.destinationPayload\n );\n }\n }\n\n emit SocketBridge(\n amount,\n stargateBridgeData.token,\n stargateBridgeData.stargateDstChainId,\n StargateIdentifier,\n msg.sender,\n stargateBridgeData.receiverAddress,\n stargateBridgeData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swapping.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in Stargate-BridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param stargateBridgeData encoded data for StargateBridgeData\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n StargateBridgeDataNoToken calldata stargateBridgeData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n\n if (token == NATIVE_TOKEN_ADDRESS) {\n routerETH.swapETH{\n value: bridgeAmount + stargateBridgeData.optionalValue\n }(\n stargateBridgeData.stargateDstChainId,\n payable(stargateBridgeData.senderAddress),\n abi.encodePacked(stargateBridgeData.receiverAddress),\n bridgeAmount,\n stargateBridgeData.minReceivedAmt\n );\n } else {\n ERC20(token).safeApprove(address(router), bridgeAmount);\n {\n router.swap{value: stargateBridgeData.value}(\n stargateBridgeData.stargateDstChainId,\n stargateBridgeData.srcPoolId,\n stargateBridgeData.dstPoolId,\n payable(stargateBridgeData.senderAddress), // default to refund to main contract\n bridgeAmount,\n stargateBridgeData.minReceivedAmt,\n IBridgeStargate.lzTxObj(\n stargateBridgeData.destinationGasLimit,\n 0,\n \"0x\"\n ),\n abi.encodePacked(stargateBridgeData.receiverAddress),\n stargateBridgeData.destinationPayload\n );\n }\n }\n\n emit SocketBridge(\n bridgeAmount,\n token,\n stargateBridgeData.stargateDstChainId,\n StargateIdentifier,\n msg.sender,\n stargateBridgeData.receiverAddress,\n stargateBridgeData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via Stargate-L1-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param token address of token being bridged\n * @param senderAddress address of sender\n * @param receiverAddress address of recipient\n * @param amount amount of token being bridge\n * @param value value\n * @param optionalValue optionalValue\n * @param stargateBridgeExtraData stargate bridge extradata\n */\n function bridgeERC20To(\n address token,\n address senderAddress,\n address receiverAddress,\n uint256 amount,\n uint256 value,\n uint256 optionalValue,\n StargateBridgeExtraData calldata stargateBridgeExtraData\n ) external payable {\n // token address might not be indication thats why passed through extraData\n if (token == NATIVE_TOKEN_ADDRESS) {\n // perform bridging\n routerETH.swapETH{value: amount + optionalValue}(\n stargateBridgeExtraData.stargateDstChainId,\n payable(senderAddress),\n abi.encodePacked(receiverAddress),\n amount,\n stargateBridgeExtraData.minReceivedAmt\n );\n } else {\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n tokenInstance.safeApprove(address(router), amount);\n {\n router.swap{value: value}(\n stargateBridgeExtraData.stargateDstChainId,\n stargateBridgeExtraData.srcPoolId,\n stargateBridgeExtraData.dstPoolId,\n payable(senderAddress), // default to refund to main contract\n amount,\n stargateBridgeExtraData.minReceivedAmt,\n IBridgeStargate.lzTxObj(\n stargateBridgeExtraData.destinationGasLimit,\n 0, // zero amount since this is a ERC20 bridging\n \"0x\" //empty data since this is for only ERC20\n ),\n abi.encodePacked(receiverAddress),\n stargateBridgeExtraData.destinationPayload\n );\n }\n }\n\n emit SocketBridge(\n amount,\n token,\n stargateBridgeExtraData.stargateDstChainId,\n StargateIdentifier,\n msg.sender,\n receiverAddress,\n stargateBridgeExtraData.metadata\n );\n }\n\n function bridgeNativeTo(\n address receiverAddress,\n address senderAddress,\n uint16 stargateDstChainId,\n uint256 amount,\n uint256 minReceivedAmt,\n uint256 optionalValue,\n bytes32 metadata\n ) external payable {\n // perform bridging\n routerETH.swapETH{value: amount + optionalValue}(\n stargateDstChainId,\n payable(senderAddress),\n abi.encodePacked(receiverAddress),\n amount,\n minReceivedAmt\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n stargateDstChainId,\n StargateIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 1000000 + }, + "outputSelection": { + "*": { + "*": [ + "evm.bytecode", + "evm.deployedBytecode", + "devdoc", + "userdoc", + "metadata", + "abi" + ] + } + }, + "metadata": { + "useLiteralContent": true + }, + "libraries": {} + } + }, + "ABI": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_disabledRoute\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"ArrayLengthMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectBridgeRatios\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyNominee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"controllerId\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"controllerAddress\",\"type\":\"address\"}],\"name\":\"ControllerAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"controllerId\",\"type\":\"uint32\"}],\"name\":\"ControllerDisabled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"routeId\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"route\",\"type\":\"address\"}],\"name\":\"NewRouteAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"claimer\",\"type\":\"address\"}],\"name\":\"OwnerClaimed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"nominee\",\"type\":\"address\"}],\"name\":\"OwnerNominated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"routeId\",\"type\":\"uint32\"}],\"name\":\"RouteDisabled\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"BRIDGE_AFTER_SWAP_SELECTOR\",\"outputs\":[{\"internalType\":\"bytes4\",\"name\":\"\",\"type\":\"bytes4\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"CENT_PERCENT\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"controllerAddress\",\"type\":\"address\"}],\"name\":\"addController\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"routeAddress\",\"type\":\"address\"}],\"name\":\"addRoute\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"routeId\",\"type\":\"uint32\"}],\"name\":\"addressAt\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"claimOwner\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"controllerCount\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"name\":\"controllers\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"controllerId\",\"type\":\"uint32\"}],\"name\":\"disableController\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"routeId\",\"type\":\"uint32\"}],\"name\":\"disableRoute\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"disabledRouteAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"controllerId\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"struct ISocketGateway.SocketControllerRequest\",\"name\":\"socketControllerRequest\",\"type\":\"tuple\"}],\"name\":\"executeController\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"controllerId\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"struct ISocketGateway.SocketControllerRequest[]\",\"name\":\"controllerRequests\",\"type\":\"tuple[]\"}],\"name\":\"executeControllers\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"routeId\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"routeData\",\"type\":\"bytes\"}],\"name\":\"executeRoute\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32[]\",\"name\":\"routeIds\",\"type\":\"uint32[]\"},{\"internalType\":\"bytes[]\",\"name\":\"dataItems\",\"type\":\"bytes[]\"}],\"name\":\"executeRoutes\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"controllerId\",\"type\":\"uint32\"}],\"name\":\"getController\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"routeId\",\"type\":\"uint32\"}],\"name\":\"getRoute\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"nominee_\",\"type\":\"address\"}],\"name\":\"nominateOwner\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"nominee\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address payable\",\"name\":\"userAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"rescueEther\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"userAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"rescueFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"name\":\"routes\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"routesCount\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"routeAddresses\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"tokenAddresses\",\"type\":\"address[]\"},{\"internalType\":\"bool\",\"name\":\"isMax\",\"type\":\"bool\"}],\"name\":\"setApprovalForRouters\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"swapRouteId\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"swapImplData\",\"type\":\"bytes\"},{\"internalType\":\"uint32[]\",\"name\":\"bridgeRouteIds\",\"type\":\"uint32[]\"},{\"internalType\":\"bytes[]\",\"name\":\"bridgeImplDataItems\",\"type\":\"bytes[]\"},{\"internalType\":\"uint256[]\",\"name\":\"bridgeRatios\",\"type\":\"uint256[]\"},{\"internalType\":\"bytes[]\",\"name\":\"eventDataItems\",\"type\":\"bytes[]\"}],\"internalType\":\"struct ISocketRequest.SwapMultiBridgeRequest\",\"name\":\"swapMultiBridgeRequest\",\"type\":\"tuple\"}],\"name\":\"swapAndMultiBridge\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]", + "ContractName": "SocketGateway", + "CompilerVersion": "v0.8.7+commit.e28d00a7", + "OptimizationUsed": 1, + "Runs": 1000000, + "ConstructorArguments": "0x000000000000000000000000e8dd38e673a93ccfc2e3d7053efccb5c93f493650000000000000000000000000f34a522ff82151c90679b73211955068fd854f1", + "EVMVersion": "Default", + "Library": "", + "LicenseType": "", + "Proxy": 1, + "Implementation": "0xa3c4e32af0da5efaddb20cc9fb26159f55c8c42f", + "SwarmSource": "" + } +] \ No newline at end of file diff --git a/testdata/etherscan/0x71356E37e0368Bd10bFDbF41dC052fE5FA24cD05/creation_data.json b/testdata/etherscan/0x71356E37e0368Bd10bFDbF41dC052fE5FA24cD05/creation_data.json new file mode 100644 index 000000000..b3976447f --- /dev/null +++ b/testdata/etherscan/0x71356E37e0368Bd10bFDbF41dC052fE5FA24cD05/creation_data.json @@ -0,0 +1,5 @@ +{ + "contractAddress": "0x71356e37e0368bd10bfdbf41dc052fe5fa24cd05", + "contractCreator": "0xaa1d342354d755ec515f40e7d5e83cb4184bb9ee", + "txHash": "0x1c800c2c2d5230823602cae6896a8db1ab7d1341ca697c6c64d9f0edf11dabe2" +} \ No newline at end of file diff --git a/testdata/etherscan/0x71356E37e0368Bd10bFDbF41dC052fE5FA24cD05/metadata.json b/testdata/etherscan/0x71356E37e0368Bd10bFDbF41dC052fE5FA24cD05/metadata.json new file mode 100644 index 000000000..cc890a571 --- /dev/null +++ b/testdata/etherscan/0x71356E37e0368Bd10bFDbF41dC052fE5FA24cD05/metadata.json @@ -0,0 +1,129 @@ +[ + { + "SourceCode": { + "language": "Solidity", + "sources": { + "@openzeppelin/contracts/access/IAccessControl.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev External interface of AccessControl declared to support ERC165 detection.\n */\ninterface IAccessControl {\n /**\n * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n *\n * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n * {RoleAdminChanged} not being emitted signaling this.\n *\n * _Available since v3.1._\n */\n event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n /**\n * @dev Emitted when `account` is granted `role`.\n *\n * `sender` is the account that originated the contract call, an admin role\n * bearer except when using {AccessControl-_setupRole}.\n */\n event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Emitted when `account` is revoked `role`.\n *\n * `sender` is the account that originated the contract call:\n * - if using `revokeRole`, it is the admin role bearer\n * - if using `renounceRole`, it is the role bearer (i.e. `account`)\n */\n event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) external view returns (bool);\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been granted `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) external;\n}\n" + }, + "@openzeppelin/contracts/token/ERC721/IERC721.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../../utils/introspection/IERC165.sol\";\n\n/**\n * @dev Required interface of an ERC721 compliant contract.\n */\ninterface IERC721 is IERC165 {\n /**\n * @dev Emitted when `tokenId` token is transferred from `from` to `to`.\n */\n event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);\n\n /**\n * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.\n */\n event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);\n\n /**\n * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.\n */\n event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n\n /**\n * @dev Returns the number of tokens in ``owner``'s account.\n */\n function balanceOf(address owner) external view returns (uint256 balance);\n\n /**\n * @dev Returns the owner of the `tokenId` token.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function ownerOf(uint256 tokenId) external view returns (address owner);\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\n * are aware of the ERC721 protocol to prevent tokens from being forever locked.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 tokenId\n ) external;\n\n /**\n * @dev Transfers `tokenId` token from `from` to `to`.\n *\n * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must be owned by `from`.\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 tokenId\n ) external;\n\n /**\n * @dev Gives permission to `to` to transfer `tokenId` token to another account.\n * The approval is cleared when the token is transferred.\n *\n * Only a single account can be approved at a time, so approving the zero address clears previous approvals.\n *\n * Requirements:\n *\n * - The caller must own the token or be an approved operator.\n * - `tokenId` must exist.\n *\n * Emits an {Approval} event.\n */\n function approve(address to, uint256 tokenId) external;\n\n /**\n * @dev Returns the account approved for `tokenId` token.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function getApproved(uint256 tokenId) external view returns (address operator);\n\n /**\n * @dev Approve or remove `operator` as an operator for the caller.\n * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.\n *\n * Requirements:\n *\n * - The `operator` cannot be the caller.\n *\n * Emits an {ApprovalForAll} event.\n */\n function setApprovalForAll(address operator, bool _approved) external;\n\n /**\n * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.\n *\n * See {setApprovalForAll}\n */\n function isApprovedForAll(address owner, address operator) external view returns (bool);\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 tokenId,\n bytes calldata data\n ) external;\n}\n" + }, + "@openzeppelin/contracts/access/IAccessControlEnumerable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\n\n/**\n * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.\n */\ninterface IAccessControlEnumerable is IAccessControl {\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) external view returns (address);\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) external view returns (uint256);\n}\n" + }, + "@openzeppelin/contracts/utils/StorageSlot.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/StorageSlot.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for reading and writing primitive types to specific storage slots.\n *\n * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.\n * This library helps with reading and writing to such slots without the need for inline assembly.\n *\n * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.\n *\n * Example usage to set ERC1967 implementation slot:\n * ```\n * contract ERC1967 {\n * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n *\n * function _getImplementation() internal view returns (address) {\n * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\n * }\n *\n * function _setImplementation(address newImplementation) internal {\n * require(Address.isContract(newImplementation), \"ERC1967: new implementation is not a contract\");\n * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\n * }\n * }\n * ```\n *\n * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._\n */\nlibrary StorageSlot {\n struct AddressSlot {\n address value;\n }\n\n struct BooleanSlot {\n bool value;\n }\n\n struct Bytes32Slot {\n bytes32 value;\n }\n\n struct Uint256Slot {\n uint256 value;\n }\n\n /**\n * @dev Returns an `AddressSlot` with member `value` located at `slot`.\n */\n function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {\n assembly {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns an `BooleanSlot` with member `value` located at `slot`.\n */\n function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {\n assembly {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.\n */\n function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {\n assembly {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns an `Uint256Slot` with member `value` located at `slot`.\n */\n function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {\n assembly {\n r.slot := slot\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/cryptography/ECDSA.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/ECDSA.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../Strings.sol\";\n\n/**\n * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.\n *\n * These functions can be used to verify that a message was signed by the holder\n * of the private keys of a given address.\n */\nlibrary ECDSA {\n enum RecoverError {\n NoError,\n InvalidSignature,\n InvalidSignatureLength,\n InvalidSignatureS,\n InvalidSignatureV\n }\n\n function _throwError(RecoverError error) private pure {\n if (error == RecoverError.NoError) {\n return; // no error: do nothing\n } else if (error == RecoverError.InvalidSignature) {\n revert(\"ECDSA: invalid signature\");\n } else if (error == RecoverError.InvalidSignatureLength) {\n revert(\"ECDSA: invalid signature length\");\n } else if (error == RecoverError.InvalidSignatureS) {\n revert(\"ECDSA: invalid signature 's' value\");\n } else if (error == RecoverError.InvalidSignatureV) {\n revert(\"ECDSA: invalid signature 'v' value\");\n }\n }\n\n /**\n * @dev Returns the address that signed a hashed message (`hash`) with\n * `signature` or error string. This address can then be used for verification purposes.\n *\n * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:\n * this function rejects them by requiring the `s` value to be in the lower\n * half order, and the `v` value to be either 27 or 28.\n *\n * IMPORTANT: `hash` _must_ be the result of a hash operation for the\n * verification to be secure: it is possible to craft signatures that\n * recover to arbitrary addresses for non-hashed data. A safe way to ensure\n * this is by receiving a hash of the original message (which may otherwise\n * be too long), and then calling {toEthSignedMessageHash} on it.\n *\n * Documentation for signature generation:\n * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]\n * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]\n *\n * _Available since v4.3._\n */\n function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {\n // Check the signature length\n // - case 65: r,s,v signature (standard)\n // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._\n if (signature.length == 65) {\n bytes32 r;\n bytes32 s;\n uint8 v;\n // ecrecover takes the signature parameters, and the only way to get them\n // currently is to use assembly.\n assembly {\n r := mload(add(signature, 0x20))\n s := mload(add(signature, 0x40))\n v := byte(0, mload(add(signature, 0x60)))\n }\n return tryRecover(hash, v, r, s);\n } else if (signature.length == 64) {\n bytes32 r;\n bytes32 vs;\n // ecrecover takes the signature parameters, and the only way to get them\n // currently is to use assembly.\n assembly {\n r := mload(add(signature, 0x20))\n vs := mload(add(signature, 0x40))\n }\n return tryRecover(hash, r, vs);\n } else {\n return (address(0), RecoverError.InvalidSignatureLength);\n }\n }\n\n /**\n * @dev Returns the address that signed a hashed message (`hash`) with\n * `signature`. This address can then be used for verification purposes.\n *\n * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:\n * this function rejects them by requiring the `s` value to be in the lower\n * half order, and the `v` value to be either 27 or 28.\n *\n * IMPORTANT: `hash` _must_ be the result of a hash operation for the\n * verification to be secure: it is possible to craft signatures that\n * recover to arbitrary addresses for non-hashed data. A safe way to ensure\n * this is by receiving a hash of the original message (which may otherwise\n * be too long), and then calling {toEthSignedMessageHash} on it.\n */\n function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, signature);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.\n *\n * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]\n *\n * _Available since v4.3._\n */\n function tryRecover(\n bytes32 hash,\n bytes32 r,\n bytes32 vs\n ) internal pure returns (address, RecoverError) {\n bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);\n uint8 v = uint8((uint256(vs) >> 255) + 27);\n return tryRecover(hash, v, r, s);\n }\n\n /**\n * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.\n *\n * _Available since v4.2._\n */\n function recover(\n bytes32 hash,\n bytes32 r,\n bytes32 vs\n ) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, r, vs);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Overload of {ECDSA-tryRecover} that receives the `v`,\n * `r` and `s` signature fields separately.\n *\n * _Available since v4.3._\n */\n function tryRecover(\n bytes32 hash,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal pure returns (address, RecoverError) {\n // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature\n // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines\n // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most\n // signatures from current libraries generate a unique signature with an s-value in the lower half order.\n //\n // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value\n // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or\n // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept\n // these malleable signatures as well.\n if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {\n return (address(0), RecoverError.InvalidSignatureS);\n }\n if (v != 27 && v != 28) {\n return (address(0), RecoverError.InvalidSignatureV);\n }\n\n // If the signature is valid (and not malleable), return the signer address\n address signer = ecrecover(hash, v, r, s);\n if (signer == address(0)) {\n return (address(0), RecoverError.InvalidSignature);\n }\n\n return (signer, RecoverError.NoError);\n }\n\n /**\n * @dev Overload of {ECDSA-recover} that receives the `v`,\n * `r` and `s` signature fields separately.\n */\n function recover(\n bytes32 hash,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, v, r, s);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Returns an Ethereum Signed Message, created from a `hash`. This\n * produces hash corresponding to the one signed with the\n * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]\n * JSON-RPC method as part of EIP-191.\n *\n * See {recover}.\n */\n function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {\n // 32 is the length in bytes of hash,\n // enforced by the type signature above\n return keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n32\", hash));\n }\n\n /**\n * @dev Returns an Ethereum Signed Message, created from `s`. This\n * produces hash corresponding to the one signed with the\n * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]\n * JSON-RPC method as part of EIP-191.\n *\n * See {recover}.\n */\n function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {\n return keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n\", Strings.toString(s.length), s));\n }\n\n /**\n * @dev Returns an Ethereum Signed Typed Data, created from a\n * `domainSeparator` and a `structHash`. This produces hash corresponding\n * to the one signed with the\n * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]\n * JSON-RPC method as part of EIP-712.\n *\n * See {recover}.\n */\n function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {\n return keccak256(abi.encodePacked(\"\\x19\\x01\", domainSeparator, structHash));\n }\n}\n" + }, + "contracts/v0.8/extensions/GatewayV2.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/security/Pausable.sol\";\nimport \"../interfaces/IQuorum.sol\";\nimport \"../interfaces/IWeightedValidator.sol\";\nimport \"./HasProxyAdmin.sol\";\n\nabstract contract GatewayV2 is HasProxyAdmin, Pausable, IQuorum {\n /// @dev Emitted when the validator contract address is updated.\n event ValidatorContractUpdated(IWeightedValidator);\n\n uint256 internal _num;\n uint256 internal _denom;\n\n IWeightedValidator public validatorContract;\n uint256 public nonce;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n */\n uint256[50] private ______gap;\n\n /**\n * @dev See {IQuorum-getThreshold}.\n */\n function getThreshold() external view virtual returns (uint256, uint256) {\n return (_num, _denom);\n }\n\n /**\n * @dev See {IQuorum-checkThreshold}.\n */\n function checkThreshold(uint256 _voteWeight) external view virtual returns (bool) {\n return _voteWeight * _denom >= _num * validatorContract.totalWeights();\n }\n\n /**\n * @dev See {IQuorum-setThreshold}.\n */\n function setThreshold(uint256 _numerator, uint256 _denominator)\n external\n virtual\n onlyAdmin\n returns (uint256, uint256)\n {\n return _setThreshold(_numerator, _denominator);\n }\n\n /**\n * @dev Triggers paused state.\n */\n function pause() external onlyAdmin {\n _pause();\n }\n\n /**\n * @dev Triggers unpaused state.\n */\n function unpause() external onlyAdmin {\n _unpause();\n }\n\n /**\n * @dev Sets validator contract address.\n *\n * Requirements:\n * - The method caller is admin.\n *\n * Emits the `ValidatorContractUpdated` event.\n *\n */\n function setValidatorContract(IWeightedValidator _validatorContract) external virtual onlyAdmin {\n _setValidatorContract(_validatorContract);\n }\n\n /**\n * @dev See {IQuorum-minimumVoteWeight}.\n */\n function minimumVoteWeight() public view virtual returns (uint256) {\n return _minimumVoteWeight(validatorContract.totalWeights());\n }\n\n /**\n * @dev Sets validator contract address.\n *\n * Emits the `ValidatorContractUpdated` event.\n *\n */\n function _setValidatorContract(IWeightedValidator _validatorContract) internal virtual {\n validatorContract = _validatorContract;\n emit ValidatorContractUpdated(_validatorContract);\n }\n\n /**\n * @dev Sets threshold and returns the old one.\n *\n * Emits the `ThresholdUpdated` event.\n *\n */\n function _setThreshold(uint256 _numerator, uint256 _denominator)\n internal\n virtual\n returns (uint256 _previousNum, uint256 _previousDenom)\n {\n require(_numerator <= _denominator, \"GatewayV2: invalid threshold\");\n _previousNum = _num;\n _previousDenom = _denom;\n _num = _numerator;\n _denom = _denominator;\n emit ThresholdUpdated(nonce++, _numerator, _denominator, _previousNum, _previousDenom);\n }\n\n /**\n * @dev Returns minimum vote weight.\n */\n function _minimumVoteWeight(uint256 _totalWeight) internal view virtual returns (uint256) {\n return (_num * _totalWeight + _denom - 1) / _denom;\n }\n}\n" + }, + "@openzeppelin/contracts/proxy/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (proxy/utils/Initializable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../../utils/Address.sol\";\n\n/**\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\n * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\n *\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\n *\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\n *\n * [CAUTION]\n * ====\n * Avoid leaving a contract uninitialized.\n *\n * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation\n * contract, which may impact the proxy. To initialize the implementation contract, you can either invoke the\n * initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed:\n *\n * [.hljs-theme-light.nopadding]\n * ```\n * /// @custom:oz-upgrades-unsafe-allow constructor\n * constructor() initializer {}\n * ```\n * ====\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private _initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private _initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n // If the contract is initializing we ignore whether _initialized is set in order to support multiple\n // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the\n // contract may have been reentered.\n require(_initializing ? _isConstructor() : !_initialized, \"Initializable: contract is already initialized\");\n\n bool isTopLevelCall = !_initializing;\n if (isTopLevelCall) {\n _initializing = true;\n _initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n _initializing = false;\n }\n }\n\n /**\n * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the\n * {initializer} modifier, directly or indirectly.\n */\n modifier onlyInitializing() {\n require(_initializing, \"Initializable: contract is not initializing\");\n _;\n }\n\n function _isConstructor() private view returns (bool) {\n return !Address.isContract(address(this));\n }\n}\n" + }, + "contracts/v0.8/extensions/WithdrawalLimitation.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"./GatewayV2.sol\";\n\nabstract contract WithdrawalLimitation is GatewayV2 {\n /// @dev Emitted when the high-tier vote weight threshold is updated\n event HighTierVoteWeightThresholdUpdated(\n uint256 indexed nonce,\n uint256 indexed numerator,\n uint256 indexed denominator,\n uint256 previousNumerator,\n uint256 previousDenominator\n );\n /// @dev Emitted when the thresholds for high-tier withdrawals that requires high-tier vote weights are updated\n event HighTierThresholdsUpdated(address[] tokens, uint256[] thresholds);\n /// @dev Emitted when the thresholds for locked withdrawals are updated\n event LockedThresholdsUpdated(address[] tokens, uint256[] thresholds);\n /// @dev Emitted when the fee percentages to unlock withdraw are updated\n event UnlockFeePercentagesUpdated(address[] tokens, uint256[] percentages);\n /// @dev Emitted when the daily limit thresholds are updated\n event DailyWithdrawalLimitsUpdated(address[] tokens, uint256[] limits);\n\n uint256 public constant _MAX_PERCENTAGE = 1_000_000;\n\n uint256 internal _highTierVWNum;\n uint256 internal _highTierVWDenom;\n\n /// @dev Mapping from mainchain token => the amount thresholds for high-tier withdrawals that requires high-tier vote weights\n mapping(address => uint256) public highTierThreshold;\n /// @dev Mapping from mainchain token => the amount thresholds to lock withdrawal\n mapping(address => uint256) public lockedThreshold;\n /// @dev Mapping from mainchain token => unlock fee percentages for unlocker\n /// @notice Values 0-1,000,000 map to 0%-100%\n mapping(address => uint256) public unlockFeePercentages;\n /// @dev Mapping from mainchain token => daily limit amount for withdrawal\n mapping(address => uint256) public dailyWithdrawalLimit;\n /// @dev Mapping from token address => today withdrawal amount\n mapping(address => uint256) public lastSyncedWithdrawal;\n /// @dev Mapping from token address => last date synced to record the `lastSyncedWithdrawal`\n mapping(address => uint256) public lastDateSynced;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n */\n uint256[50] private ______gap;\n\n /**\n * @dev Override {GatewayV2-setThreshold}.\n *\n * Requirements:\n * - The high-tier vote weight threshold must equal to or larger than the normal threshold.\n *\n */\n function setThreshold(uint256 _numerator, uint256 _denominator)\n external\n virtual\n override\n onlyAdmin\n returns (uint256 _previousNum, uint256 _previousDenom)\n {\n (_previousNum, _previousDenom) = _setThreshold(_numerator, _denominator);\n _verifyThresholds();\n }\n\n /**\n * @dev Returns the high-tier vote weight threshold.\n */\n function getHighTierVoteWeightThreshold() external view virtual returns (uint256, uint256) {\n return (_highTierVWNum, _highTierVWDenom);\n }\n\n /**\n * @dev Checks whether the `_voteWeight` passes the high-tier vote weight threshold.\n */\n function checkHighTierVoteWeightThreshold(uint256 _voteWeight) external view virtual returns (bool) {\n return _voteWeight * _highTierVWDenom >= _highTierVWNum * validatorContract.totalWeights();\n }\n\n /**\n * @dev Sets high-tier vote weight threshold and returns the old one.\n *\n * Requirements:\n * - The method caller is admin.\n * - The high-tier vote weight threshold must equal to or larger than the normal threshold.\n *\n * Emits the `HighTierVoteWeightThresholdUpdated` event.\n *\n */\n function setHighTierVoteWeightThreshold(uint256 _numerator, uint256 _denominator)\n external\n virtual\n onlyAdmin\n returns (uint256 _previousNum, uint256 _previousDenom)\n {\n (_previousNum, _previousDenom) = _setHighTierVoteWeightThreshold(_numerator, _denominator);\n _verifyThresholds();\n }\n\n /**\n * @dev Sets the thresholds for high-tier withdrawals that requires high-tier vote weights.\n *\n * Requirements:\n * - The method caller is admin.\n * - The arrays have the same length and its length larger than 0.\n *\n * Emits the `HighTierThresholdsUpdated` event.\n *\n */\n function setHighTierThresholds(address[] calldata _tokens, uint256[] calldata _thresholds)\n external\n virtual\n onlyAdmin\n {\n require(_tokens.length > 0, \"WithdrawalLimitation: invalid array length\");\n _setHighTierThresholds(_tokens, _thresholds);\n }\n\n /**\n * @dev Sets the amount thresholds to lock withdrawal.\n *\n * Requirements:\n * - The method caller is admin.\n * - The arrays have the same length and its length larger than 0.\n *\n * Emits the `LockedThresholdsUpdated` event.\n *\n */\n function setLockedThresholds(address[] calldata _tokens, uint256[] calldata _thresholds) external virtual onlyAdmin {\n require(_tokens.length > 0, \"WithdrawalLimitation: invalid array length\");\n _setLockedThresholds(_tokens, _thresholds);\n }\n\n /**\n * @dev Sets fee percentages to unlock withdrawal.\n *\n * Requirements:\n * - The method caller is admin.\n * - The arrays have the same length and its length larger than 0.\n *\n * Emits the `UnlockFeePercentagesUpdated` event.\n *\n */\n function setUnlockFeePercentages(address[] calldata _tokens, uint256[] calldata _percentages)\n external\n virtual\n onlyAdmin\n {\n require(_tokens.length > 0, \"WithdrawalLimitation: invalid array length\");\n _setUnlockFeePercentages(_tokens, _percentages);\n }\n\n /**\n * @dev Sets daily limit amounts for the withdrawals.\n *\n * Requirements:\n * - The method caller is admin.\n * - The arrays have the same length and its length larger than 0.\n *\n * Emits the `DailyWithdrawalLimitsUpdated` event.\n *\n */\n function setDailyWithdrawalLimits(address[] calldata _tokens, uint256[] calldata _limits) external virtual onlyAdmin {\n require(_tokens.length > 0, \"WithdrawalLimitation: invalid array length\");\n _setDailyWithdrawalLimits(_tokens, _limits);\n }\n\n /**\n * @dev Checks whether the withdrawal reaches the limitation.\n */\n function reachedWithdrawalLimit(address _token, uint256 _quantity) external view virtual returns (bool) {\n return _reachedWithdrawalLimit(_token, _quantity);\n }\n\n /**\n * @dev Sets high-tier vote weight threshold and returns the old one.\n *\n * Emits the `HighTierVoteWeightThresholdUpdated` event.\n *\n */\n function _setHighTierVoteWeightThreshold(uint256 _numerator, uint256 _denominator)\n internal\n returns (uint256 _previousNum, uint256 _previousDenom)\n {\n require(_numerator <= _denominator, \"WithdrawalLimitation: invalid threshold\");\n _previousNum = _highTierVWNum;\n _previousDenom = _highTierVWDenom;\n _highTierVWNum = _numerator;\n _highTierVWDenom = _denominator;\n emit HighTierVoteWeightThresholdUpdated(nonce++, _numerator, _denominator, _previousNum, _previousDenom);\n }\n\n /**\n * @dev Sets the thresholds for high-tier withdrawals that requires high-tier vote weights.\n *\n * Requirements:\n * - The array lengths are equal.\n *\n * Emits the `HighTierThresholdsUpdated` event.\n *\n */\n function _setHighTierThresholds(address[] calldata _tokens, uint256[] calldata _thresholds) internal virtual {\n require(_tokens.length == _thresholds.length, \"WithdrawalLimitation: invalid array length\");\n for (uint256 _i; _i < _tokens.length; _i++) {\n highTierThreshold[_tokens[_i]] = _thresholds[_i];\n }\n emit HighTierThresholdsUpdated(_tokens, _thresholds);\n }\n\n /**\n * @dev Sets the amount thresholds to lock withdrawal.\n *\n * Requirements:\n * - The array lengths are equal.\n *\n * Emits the `LockedThresholdsUpdated` event.\n *\n */\n function _setLockedThresholds(address[] calldata _tokens, uint256[] calldata _thresholds) internal virtual {\n require(_tokens.length == _thresholds.length, \"WithdrawalLimitation: invalid array length\");\n for (uint256 _i; _i < _tokens.length; _i++) {\n lockedThreshold[_tokens[_i]] = _thresholds[_i];\n }\n emit LockedThresholdsUpdated(_tokens, _thresholds);\n }\n\n /**\n * @dev Sets fee percentages to unlock withdrawal.\n *\n * Requirements:\n * - The array lengths are equal.\n * - The percentage is equal to or less than 100_000.\n *\n * Emits the `UnlockFeePercentagesUpdated` event.\n *\n */\n function _setUnlockFeePercentages(address[] calldata _tokens, uint256[] calldata _percentages) internal virtual {\n require(_tokens.length == _percentages.length, \"WithdrawalLimitation: invalid array length\");\n for (uint256 _i; _i < _tokens.length; _i++) {\n require(_percentages[_i] <= _MAX_PERCENTAGE, \"WithdrawalLimitation: invalid percentage\");\n unlockFeePercentages[_tokens[_i]] = _percentages[_i];\n }\n emit UnlockFeePercentagesUpdated(_tokens, _percentages);\n }\n\n /**\n * @dev Sets daily limit amounts for the withdrawals.\n *\n * Requirements:\n * - The array lengths are equal.\n *\n * Emits the `DailyWithdrawalLimitsUpdated` event.\n *\n */\n function _setDailyWithdrawalLimits(address[] calldata _tokens, uint256[] calldata _limits) internal virtual {\n require(_tokens.length == _limits.length, \"WithdrawalLimitation: invalid array length\");\n for (uint256 _i; _i < _tokens.length; _i++) {\n dailyWithdrawalLimit[_tokens[_i]] = _limits[_i];\n }\n emit DailyWithdrawalLimitsUpdated(_tokens, _limits);\n }\n\n /**\n * @dev Checks whether the withdrawal reaches the daily limitation.\n *\n * Requirements:\n * - The daily withdrawal threshold should not apply for locked withdrawals.\n *\n */\n function _reachedWithdrawalLimit(address _token, uint256 _quantity) internal view virtual returns (bool) {\n if (_lockedWithdrawalRequest(_token, _quantity)) {\n return false;\n }\n\n uint256 _currentDate = block.timestamp / 1 days;\n if (_currentDate > lastDateSynced[_token]) {\n return dailyWithdrawalLimit[_token] <= _quantity;\n } else {\n return dailyWithdrawalLimit[_token] <= lastSyncedWithdrawal[_token] + _quantity;\n }\n }\n\n /**\n * @dev Record withdrawal token.\n */\n function _recordWithdrawal(address _token, uint256 _quantity) internal virtual {\n uint256 _currentDate = block.timestamp / 1 days;\n if (_currentDate > lastDateSynced[_token]) {\n lastDateSynced[_token] = _currentDate;\n lastSyncedWithdrawal[_token] = _quantity;\n } else {\n lastSyncedWithdrawal[_token] += _quantity;\n }\n }\n\n /**\n * @dev Returns whether the withdrawal request is locked or not.\n */\n function _lockedWithdrawalRequest(address _token, uint256 _quantity) internal view virtual returns (bool) {\n return lockedThreshold[_token] <= _quantity;\n }\n\n /**\n * @dev Computes fee percentage.\n */\n function _computeFeePercentage(uint256 _amount, uint256 _percentage) internal view virtual returns (uint256) {\n return (_amount * _percentage) / _MAX_PERCENTAGE;\n }\n\n /**\n * @dev Returns high-tier vote weight.\n */\n function _highTierVoteWeight(uint256 _totalWeight) internal view virtual returns (uint256) {\n return (_highTierVWNum * _totalWeight + _highTierVWDenom - 1) / _highTierVWDenom;\n }\n\n /**\n * @dev Validates whether the high-tier vote weight threshold is larger than the normal threshold.\n */\n function _verifyThresholds() internal view {\n require(_num * _highTierVWDenom <= _highTierVWNum * _denom, \"WithdrawalLimitation: invalid thresholds\");\n }\n}\n" + }, + "contracts/v0.8/interfaces/MappedTokenConsumer.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"../library/Token.sol\";\n\ninterface MappedTokenConsumer {\n struct MappedToken {\n Token.Standard erc;\n address tokenAddr;\n }\n}\n" + }, + "contracts/v0.8/library/Token.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC721/IERC721.sol\";\nimport \"@openzeppelin/contracts/utils/Strings.sol\";\nimport \"../interfaces/IWETH.sol\";\n\nlibrary Token {\n enum Standard {\n ERC20,\n ERC721\n }\n struct Info {\n Standard erc;\n // For ERC20: the id must be 0 and the quantity is larger than 0.\n // For ERC721: the quantity must be 0.\n uint256 id;\n uint256 quantity;\n }\n\n // keccak256(\"TokenInfo(uint8 erc,uint256 id,uint256 quantity)\");\n bytes32 public constant INFO_TYPE_HASH = 0x1e2b74b2a792d5c0f0b6e59b037fa9d43d84fbb759337f0112fcc15ca414fc8d;\n\n /**\n * @dev Returns token info struct hash.\n */\n function hash(Info memory _info) internal pure returns (bytes32) {\n return keccak256(abi.encode(INFO_TYPE_HASH, _info.erc, _info.id, _info.quantity));\n }\n\n /**\n * @dev Validates the token info.\n */\n function validate(Info memory _info) internal pure {\n require(\n (_info.erc == Standard.ERC20 && _info.quantity > 0 && _info.id == 0) ||\n (_info.erc == Standard.ERC721 && _info.quantity == 0),\n \"Token: invalid info\"\n );\n }\n\n /**\n * @dev Transfer asset from.\n *\n * Requirements:\n * - The `_from` address must approve for the contract using this library.\n *\n */\n function transferFrom(\n Info memory _info,\n address _from,\n address _to,\n address _token\n ) internal {\n bool _success;\n bytes memory _data;\n if (_info.erc == Standard.ERC20) {\n (_success, _data) = _token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, _from, _to, _info.quantity));\n _success = _success && (_data.length == 0 || abi.decode(_data, (bool)));\n } else if (_info.erc == Standard.ERC721) {\n // bytes4(keccak256(\"transferFrom(address,address,uint256)\"))\n (_success, ) = _token.call(abi.encodeWithSelector(0x23b872dd, _from, _to, _info.id));\n } else {\n revert(\"Token: unsupported token standard\");\n }\n\n if (!_success) {\n revert(\n string(\n abi.encodePacked(\n \"Token: could not transfer \",\n toString(_info),\n \" from \",\n Strings.toHexString(uint160(_from), 20),\n \" to \",\n Strings.toHexString(uint160(_to), 20),\n \" token \",\n Strings.toHexString(uint160(_token), 20)\n )\n )\n );\n }\n }\n\n /**\n * @dev Transfers ERC721 token and returns the result.\n */\n function tryTransferERC721(\n address _token,\n address _to,\n uint256 _id\n ) internal returns (bool _success) {\n (_success, ) = _token.call(abi.encodeWithSelector(IERC721.transferFrom.selector, address(this), _to, _id));\n }\n\n /**\n * @dev Transfers ERC20 token and returns the result.\n */\n function tryTransferERC20(\n address _token,\n address _to,\n uint256 _quantity\n ) internal returns (bool _success) {\n bytes memory _data;\n (_success, _data) = _token.call(abi.encodeWithSelector(IERC20.transfer.selector, _to, _quantity));\n _success = _success && (_data.length == 0 || abi.decode(_data, (bool)));\n }\n\n /**\n * @dev Transfer assets from current address to `_to` address.\n */\n function transfer(\n Info memory _info,\n address _to,\n address _token\n ) internal {\n bool _success;\n if (_info.erc == Standard.ERC20) {\n _success = tryTransferERC20(_token, _to, _info.quantity);\n } else if (_info.erc == Standard.ERC721) {\n _success = tryTransferERC721(_token, _to, _info.id);\n } else {\n revert(\"Token: unsupported token standard\");\n }\n\n if (!_success) {\n revert(\n string(\n abi.encodePacked(\n \"Token: could not transfer \",\n toString(_info),\n \" to \",\n Strings.toHexString(uint160(_to), 20),\n \" token \",\n Strings.toHexString(uint160(_token), 20)\n )\n )\n );\n }\n }\n\n /**\n * @dev Tries minting and transfering assets.\n *\n * @notice Prioritizes transfer native token if the token is wrapped.\n *\n */\n function handleAssetTransfer(\n Info memory _info,\n address payable _to,\n address _token,\n IWETH _wrappedNativeToken\n ) internal {\n bool _success;\n if (_token == address(_wrappedNativeToken)) {\n // Try sending the native token before transferring the wrapped token\n if (!_to.send(_info.quantity)) {\n _wrappedNativeToken.deposit{ value: _info.quantity }();\n transfer(_info, _to, _token);\n }\n } else if (_info.erc == Token.Standard.ERC20) {\n uint256 _balance = IERC20(_token).balanceOf(address(this));\n\n if (_balance < _info.quantity) {\n // bytes4(keccak256(\"mint(address,uint256)\"))\n (_success, ) = _token.call(abi.encodeWithSelector(0x40c10f19, address(this), _info.quantity - _balance));\n require(_success, \"Token: ERC20 minting failed\");\n }\n\n transfer(_info, _to, _token);\n } else if (_info.erc == Token.Standard.ERC721) {\n if (!tryTransferERC721(_token, _to, _info.id)) {\n // bytes4(keccak256(\"mint(address,uint256)\"))\n (_success, ) = _token.call(abi.encodeWithSelector(0x40c10f19, _to, _info.id));\n require(_success, \"Token: ERC721 minting failed\");\n }\n } else {\n revert(\"Token: unsupported token standard\");\n }\n }\n\n /**\n * @dev Returns readable string.\n */\n function toString(Info memory _info) internal pure returns (string memory) {\n return\n string(\n abi.encodePacked(\n \"TokenInfo(\",\n Strings.toHexString(uint160(_info.erc), 1),\n \",\",\n Strings.toHexString(_info.id),\n \",\",\n Strings.toHexString(_info.quantity),\n \")\"\n )\n );\n }\n\n struct Owner {\n address addr;\n address tokenAddr;\n uint256 chainId;\n }\n\n // keccak256(\"TokenOwner(address addr,address tokenAddr,uint256 chainId)\");\n bytes32 public constant OWNER_TYPE_HASH = 0x353bdd8d69b9e3185b3972e08b03845c0c14a21a390215302776a7a34b0e8764;\n\n /**\n * @dev Returns ownership struct hash.\n */\n function hash(Owner memory _owner) internal pure returns (bytes32) {\n return keccak256(abi.encode(OWNER_TYPE_HASH, _owner.addr, _owner.tokenAddr, _owner.chainId));\n }\n}\n" + }, + "contracts/v0.8/mainchain/IMainchainGatewayV2.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"../interfaces/IWETH.sol\";\nimport \"../library/Transfer.sol\";\nimport \"../interfaces/SignatureConsumer.sol\";\nimport \"../interfaces/MappedTokenConsumer.sol\";\n\ninterface IMainchainGatewayV2 is SignatureConsumer, MappedTokenConsumer {\n /// @dev Emitted when the deposit is requested\n event DepositRequested(bytes32 receiptHash, Transfer.Receipt receipt);\n /// @dev Emitted when the assets are withdrawn\n event Withdrew(bytes32 receiptHash, Transfer.Receipt receipt);\n /// @dev Emitted when the tokens are mapped\n event TokenMapped(address[] mainchainTokens, address[] roninTokens, Token.Standard[] standards);\n /// @dev Emitted when the wrapped native token contract is updated\n event WrappedNativeTokenContractUpdated(IWETH weth);\n /// @dev Emitted when the withdrawal is locked\n event WithdrawalLocked(bytes32 receiptHash, Transfer.Receipt receipt);\n /// @dev Emitted when the withdrawal is unlocked\n event WithdrawalUnlocked(bytes32 receiptHash, Transfer.Receipt receipt);\n\n /**\n * @dev Returns the domain seperator.\n */\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n /**\n * @dev Returns deposit count.\n */\n function depositCount() external view returns (uint256);\n\n /**\n * @dev Sets the wrapped native token contract.\n *\n * Requirements:\n * - The method caller is admin.\n *\n * Emits the `WrappedNativeTokenContractUpdated` event.\n *\n */\n function setWrappedNativeTokenContract(IWETH _wrappedToken) external;\n\n /**\n * @dev Returns whether the withdrawal is locked.\n */\n function withdrawalLocked(uint256 withdrawalId) external view returns (bool);\n\n /**\n * @dev Returns the withdrawal hash.\n */\n function withdrawalHash(uint256 withdrawalId) external view returns (bytes32);\n\n /**\n * @dev Locks the assets and request deposit.\n */\n function requestDepositFor(Transfer.Request calldata _request) external payable;\n\n /**\n * @dev Withdraws based on the receipt and the validator signatures.\n * Returns whether the withdrawal is locked.\n *\n * Emits the `Withdrew` once the assets are released.\n *\n */\n function submitWithdrawal(Transfer.Receipt memory _receipt, Signature[] memory _signatures)\n external\n returns (bool _locked);\n\n /**\n * @dev Approves a specific withdrawal.\n *\n * Requirements:\n * - The method caller is a validator.\n *\n * Emits the `Withdrew` once the assets are released.\n *\n */\n function unlockWithdrawal(Transfer.Receipt calldata _receipt) external;\n\n /**\n * @dev Maps mainchain tokens to Ronin network.\n *\n * Requirement:\n * - The method caller is admin.\n * - The arrays have the same length and its length larger than 0.\n *\n * Emits the `TokenMapped` event.\n *\n */\n function mapTokens(\n address[] calldata _mainchainTokens,\n address[] calldata _roninTokens,\n Token.Standard[] calldata _standards\n ) external;\n\n /**\n * @dev Maps mainchain tokens to Ronin network and sets thresholds.\n *\n * Requirement:\n * - The method caller is admin.\n * - The arrays have the same length and its length larger than 0.\n *\n * Emits the `TokenMapped` event.\n *\n */\n function mapTokensAndThresholds(\n address[] calldata _mainchainTokens,\n address[] calldata _roninTokens,\n Token.Standard[] calldata _standards,\n uint256[][4] calldata _thresholds\n ) external;\n\n /**\n * @dev Returns token address on Ronin network.\n * @notice Reverts for unsupported token.\n */\n function getRoninToken(address _mainchainToken) external view returns (MappedToken memory _token);\n}\n" + }, + "contracts/v0.8/mainchain/MainchainGatewayV2.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\nimport \"@openzeppelin/contracts/proxy/utils/Initializable.sol\";\nimport \"../extensions/GatewayV2.sol\";\nimport \"../extensions/WithdrawalLimitation.sol\";\nimport \"../library/Transfer.sol\";\nimport \"./IMainchainGatewayV2.sol\";\n\ncontract MainchainGatewayV2 is WithdrawalLimitation, Initializable, AccessControlEnumerable, IMainchainGatewayV2 {\n using Token for Token.Info;\n using Transfer for Transfer.Request;\n using Transfer for Transfer.Receipt;\n\n /// @dev Withdrawal unlocker role hash\n bytes32 public constant WITHDRAWAL_UNLOCKER_ROLE = keccak256(\"WITHDRAWAL_UNLOCKER_ROLE\");\n\n /// @dev Wrapped native token address\n IWETH public wrappedNativeToken;\n /// @dev Ronin network id\n uint256 public roninChainId;\n /// @dev Total deposit\n uint256 public depositCount;\n /// @dev Domain seperator\n bytes32 internal _domainSeparator;\n /// @dev Mapping from mainchain token => token address on Ronin network\n mapping(address => MappedToken) internal _roninToken;\n /// @dev Mapping from withdrawal id => withdrawal hash\n mapping(uint256 => bytes32) public withdrawalHash;\n /// @dev Mapping from withdrawal id => locked\n mapping(uint256 => bool) public withdrawalLocked;\n\n fallback() external payable {\n _fallback();\n }\n\n receive() external payable {\n _fallback();\n }\n\n /**\n * @dev Initializes contract storage.\n */\n function initialize(\n address _roleSetter,\n IWETH _wrappedToken,\n IWeightedValidator _validatorContract,\n uint256 _roninChainId,\n uint256 _numerator,\n uint256 _highTierVWNumerator,\n uint256 _denominator,\n // _addresses[0]: mainchainTokens\n // _addresses[1]: roninTokens\n // _addresses[2]: withdrawalUnlockers\n address[][3] calldata _addresses,\n // _thresholds[0]: highTierThreshold\n // _thresholds[1]: lockedThreshold\n // _thresholds[2]: unlockFeePercentages\n // _thresholds[3]: dailyWithdrawalLimit\n uint256[][4] calldata _thresholds,\n Token.Standard[] calldata _standards\n ) external payable virtual initializer {\n _setupRole(DEFAULT_ADMIN_ROLE, _roleSetter);\n roninChainId = _roninChainId;\n\n _setWrappedNativeTokenContract(_wrappedToken);\n _setValidatorContract(_validatorContract);\n _updateDomainSeparator();\n _setThreshold(_numerator, _denominator);\n _setHighTierVoteWeightThreshold(_highTierVWNumerator, _denominator);\n _verifyThresholds();\n\n if (_addresses[0].length > 0) {\n // Map mainchain tokens to ronin tokens\n _mapTokens(_addresses[0], _addresses[1], _standards);\n // Sets thresholds based on the mainchain tokens\n _setHighTierThresholds(_addresses[0], _thresholds[0]);\n _setLockedThresholds(_addresses[0], _thresholds[1]);\n _setUnlockFeePercentages(_addresses[0], _thresholds[2]);\n _setDailyWithdrawalLimits(_addresses[0], _thresholds[3]);\n }\n\n // Grant role for withdrawal unlocker\n for (uint256 _i; _i < _addresses[2].length; _i++) {\n _grantRole(WITHDRAWAL_UNLOCKER_ROLE, _addresses[2][_i]);\n }\n }\n\n /**\n * @dev Receives ether without doing anything. Use this function to topup native token.\n */\n function receiveEther() external payable {}\n\n /**\n * @dev See {IMainchainGatewayV2-DOMAIN_SEPARATOR}.\n */\n function DOMAIN_SEPARATOR() external view virtual returns (bytes32) {\n return _domainSeparator;\n }\n\n /**\n * @dev See {IMainchainGatewayV2-setWrappedNativeTokenContract}.\n */\n function setWrappedNativeTokenContract(IWETH _wrappedToken) external virtual onlyAdmin {\n _setWrappedNativeTokenContract(_wrappedToken);\n }\n\n /**\n * @dev See {IMainchainGatewayV2-requestDepositFor}.\n */\n function requestDepositFor(Transfer.Request calldata _request) external payable virtual whenNotPaused {\n _requestDepositFor(_request, msg.sender);\n }\n\n /**\n * @dev See {IMainchainGatewayV2-submitWithdrawal}.\n */\n function submitWithdrawal(Transfer.Receipt calldata _receipt, Signature[] calldata _signatures)\n external\n virtual\n whenNotPaused\n returns (bool _locked)\n {\n return _submitWithdrawal(_receipt, _signatures);\n }\n\n /**\n * @dev See {IMainchainGatewayV2-unlockWithdrawal}.\n */\n function unlockWithdrawal(Transfer.Receipt calldata _receipt) external onlyRole(WITHDRAWAL_UNLOCKER_ROLE) {\n bytes32 _receiptHash = _receipt.hash();\n require(withdrawalHash[_receipt.id] == _receipt.hash(), \"MainchainGatewayV2: invalid receipt\");\n require(withdrawalLocked[_receipt.id], \"MainchainGatewayV2: query for approved withdrawal\");\n delete withdrawalLocked[_receipt.id];\n emit WithdrawalUnlocked(_receiptHash, _receipt);\n\n address _token = _receipt.mainchain.tokenAddr;\n if (_receipt.info.erc == Token.Standard.ERC20) {\n Token.Info memory _feeInfo = _receipt.info;\n _feeInfo.quantity = _computeFeePercentage(_receipt.info.quantity, unlockFeePercentages[_token]);\n Token.Info memory _withdrawInfo = _receipt.info;\n _withdrawInfo.quantity = _receipt.info.quantity - _feeInfo.quantity;\n\n _feeInfo.handleAssetTransfer(payable(msg.sender), _token, wrappedNativeToken);\n _withdrawInfo.handleAssetTransfer(payable(_receipt.mainchain.addr), _token, wrappedNativeToken);\n } else {\n _receipt.info.handleAssetTransfer(payable(_receipt.mainchain.addr), _token, wrappedNativeToken);\n }\n\n emit Withdrew(_receiptHash, _receipt);\n }\n\n /**\n * @dev See {IMainchainGatewayV2-mapTokens}.\n */\n function mapTokens(\n address[] calldata _mainchainTokens,\n address[] calldata _roninTokens,\n Token.Standard[] calldata _standards\n ) external virtual onlyAdmin {\n require(_mainchainTokens.length > 0, \"MainchainGatewayV2: query for empty array\");\n _mapTokens(_mainchainTokens, _roninTokens, _standards);\n }\n\n /**\n * @dev See {IMainchainGatewayV2-mapTokensAndThresholds}.\n */\n function mapTokensAndThresholds(\n address[] calldata _mainchainTokens,\n address[] calldata _roninTokens,\n Token.Standard[] calldata _standards,\n // _thresholds[0]: highTierThreshold\n // _thresholds[1]: lockedThreshold\n // _thresholds[2]: unlockFeePercentages\n // _thresholds[3]: dailyWithdrawalLimit\n uint256[][4] calldata _thresholds\n ) external virtual onlyAdmin {\n require(_mainchainTokens.length > 0, \"MainchainGatewayV2: query for empty array\");\n _mapTokens(_mainchainTokens, _roninTokens, _standards);\n _setHighTierThresholds(_mainchainTokens, _thresholds[0]);\n _setLockedThresholds(_mainchainTokens, _thresholds[1]);\n _setUnlockFeePercentages(_mainchainTokens, _thresholds[2]);\n _setDailyWithdrawalLimits(_mainchainTokens, _thresholds[3]);\n }\n\n /**\n * @dev See {IMainchainGatewayV2-getRoninToken}.\n */\n function getRoninToken(address _mainchainToken) public view returns (MappedToken memory _token) {\n _token = _roninToken[_mainchainToken];\n require(_token.tokenAddr != address(0), \"MainchainGatewayV2: unsupported token\");\n }\n\n /**\n * @dev Maps mainchain tokens to Ronin network.\n *\n * Requirement:\n * - The arrays have the same length.\n *\n * Emits the `TokenMapped` event.\n *\n */\n function _mapTokens(\n address[] calldata _mainchainTokens,\n address[] calldata _roninTokens,\n Token.Standard[] calldata _standards\n ) internal virtual {\n require(\n _mainchainTokens.length == _roninTokens.length && _mainchainTokens.length == _standards.length,\n \"MainchainGatewayV2: invalid array length\"\n );\n\n for (uint256 _i; _i < _mainchainTokens.length; _i++) {\n _roninToken[_mainchainTokens[_i]].tokenAddr = _roninTokens[_i];\n _roninToken[_mainchainTokens[_i]].erc = _standards[_i];\n }\n\n emit TokenMapped(_mainchainTokens, _roninTokens, _standards);\n }\n\n /**\n * @dev Submits withdrawal receipt.\n *\n * Requirements:\n * - The receipt kind is withdrawal.\n * - The receipt is to withdraw on this chain.\n * - The receipt is not used to withdraw before.\n * - The withdrawal is not reached the limit threshold.\n * - The signer weight total is larger than or equal to the minimum threshold.\n * - The signature signers are in order.\n *\n * Emits the `Withdrew` once the assets are released.\n *\n */\n function _submitWithdrawal(Transfer.Receipt calldata _receipt, Signature[] memory _signatures)\n internal\n virtual\n returns (bool _locked)\n {\n uint256 _id = _receipt.id;\n uint256 _quantity = _receipt.info.quantity;\n address _tokenAddr = _receipt.mainchain.tokenAddr;\n\n _receipt.info.validate();\n require(_receipt.kind == Transfer.Kind.Withdrawal, \"MainchainGatewayV2: invalid receipt kind\");\n require(_receipt.mainchain.chainId == block.chainid, \"MainchainGatewayV2: invalid chain id\");\n MappedToken memory _token = getRoninToken(_receipt.mainchain.tokenAddr);\n require(\n _token.erc == _receipt.info.erc && _token.tokenAddr == _receipt.ronin.tokenAddr,\n \"MainchainGatewayV2: invalid receipt\"\n );\n require(withdrawalHash[_id] == bytes32(0), \"MainchainGatewayV2: query for processed withdrawal\");\n require(\n _receipt.info.erc == Token.Standard.ERC721 || !_reachedWithdrawalLimit(_tokenAddr, _quantity),\n \"MainchainGatewayV2: reached daily withdrawal limit\"\n );\n\n bytes32 _receiptHash = _receipt.hash();\n bytes32 _receiptDigest = Transfer.receiptDigest(_domainSeparator, _receiptHash);\n IWeightedValidator _validatorContract = validatorContract;\n\n uint256 _minimumVoteWeight;\n (_minimumVoteWeight, _locked) = _computeMinVoteWeight(_receipt.info.erc, _tokenAddr, _quantity, _validatorContract);\n\n {\n bool _passed;\n address _signer;\n address _lastSigner;\n Signature memory _sig;\n uint256 _weight;\n for (uint256 _i; _i < _signatures.length; _i++) {\n _sig = _signatures[_i];\n _signer = ecrecover(_receiptDigest, _sig.v, _sig.r, _sig.s);\n require(_lastSigner < _signer, \"MainchainGatewayV2: invalid order\");\n _lastSigner = _signer;\n\n _weight += _validatorContract.getValidatorWeight(_signer);\n if (_weight >= _minimumVoteWeight) {\n _passed = true;\n break;\n }\n }\n require(_passed, \"MainchainGatewayV2: query for insufficient vote weight\");\n withdrawalHash[_id] = _receiptHash;\n }\n\n if (_locked) {\n withdrawalLocked[_id] = true;\n emit WithdrawalLocked(_receiptHash, _receipt);\n return _locked;\n }\n\n _recordWithdrawal(_tokenAddr, _quantity);\n _receipt.info.handleAssetTransfer(payable(_receipt.mainchain.addr), _tokenAddr, wrappedNativeToken);\n emit Withdrew(_receiptHash, _receipt);\n }\n\n /**\n * @dev Requests deposit made by `_requester` address.\n *\n * Requirements:\n * - The token info is valid.\n * - The `msg.value` is 0 while depositing ERC20 token.\n * - The `msg.value` is equal to deposit quantity while depositing native token.\n *\n * Emits the `DepositRequested` event.\n *\n */\n function _requestDepositFor(Transfer.Request memory _request, address _requester) internal virtual {\n MappedToken memory _token;\n address _weth = address(wrappedNativeToken);\n\n _request.info.validate();\n if (_request.tokenAddr == address(0)) {\n require(_request.info.quantity == msg.value, \"MainchainGatewayV2: invalid request\");\n _token = getRoninToken(_weth);\n require(_token.erc == _request.info.erc, \"MainchainGatewayV2: invalid token standard\");\n _request.tokenAddr = _weth;\n } else {\n require(msg.value == 0, \"MainchainGatewayV2: invalid request\");\n _token = getRoninToken(_request.tokenAddr);\n require(_token.erc == _request.info.erc, \"MainchainGatewayV2: invalid token standard\");\n _request.info.transferFrom(_requester, address(this), _request.tokenAddr);\n // Withdraw if token is WETH\n if (_weth == _request.tokenAddr) {\n IWETH(_weth).withdraw(_request.info.quantity);\n }\n }\n\n uint256 _depositId = depositCount++;\n Transfer.Receipt memory _receipt = _request.into_deposit_receipt(\n _requester,\n _depositId,\n _token.tokenAddr,\n roninChainId\n );\n\n emit DepositRequested(_receipt.hash(), _receipt);\n }\n\n /**\n * @dev Returns the minimum vote weight for the token.\n */\n function _computeMinVoteWeight(\n Token.Standard _erc,\n address _token,\n uint256 _quantity,\n IWeightedValidator _validatorContract\n ) internal virtual returns (uint256 _weight, bool _locked) {\n uint256 _totalWeights = _validatorContract.totalWeights();\n _weight = _minimumVoteWeight(_totalWeights);\n if (_erc == Token.Standard.ERC20) {\n if (highTierThreshold[_token] <= _quantity) {\n _weight = _highTierVoteWeight(_totalWeights);\n }\n _locked = _lockedWithdrawalRequest(_token, _quantity);\n }\n }\n\n /**\n * @dev Update domain seperator.\n */\n function _updateDomainSeparator() internal {\n _domainSeparator = keccak256(\n abi.encode(\n keccak256(\"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\"),\n keccak256(\"MainchainGatewayV2\"),\n keccak256(\"2\"),\n block.chainid,\n address(this)\n )\n );\n }\n\n /**\n * @dev Sets the WETH contract.\n *\n * Emits the `WrappedNativeTokenContractUpdated` event.\n *\n */\n function _setWrappedNativeTokenContract(IWETH _wrapedToken) internal {\n wrappedNativeToken = _wrapedToken;\n emit WrappedNativeTokenContractUpdated(_wrapedToken);\n }\n\n /**\n * @dev Receives ETH from WETH or creates deposit request.\n */\n function _fallback() internal virtual whenNotPaused {\n if (msg.sender != address(wrappedNativeToken)) {\n Transfer.Request memory _request;\n _request.recipientAddr = msg.sender;\n _request.info.quantity = msg.value;\n _requestDepositFor(_request, _request.recipientAddr);\n }\n }\n}\n" + }, + "contracts/v0.8/interfaces/IWeightedValidator.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"./IQuorum.sol\";\n\ninterface IWeightedValidator is IQuorum {\n struct WeightedValidator {\n address validator;\n address governor;\n uint256 weight;\n }\n\n /// @dev Emitted when the validators are added\n event ValidatorsAdded(uint256 indexed nonce, WeightedValidator[] validators);\n /// @dev Emitted when the validators are updated\n event ValidatorsUpdated(uint256 indexed nonce, WeightedValidator[] validators);\n /// @dev Emitted when the validators are removed\n event ValidatorsRemoved(uint256 indexed nonce, address[] validators);\n\n /**\n * @dev Returns validator weight of the validator.\n */\n function getValidatorWeight(address _addr) external view returns (uint256);\n\n /**\n * @dev Returns governor weight of the governor.\n */\n function getGovernorWeight(address _addr) external view returns (uint256);\n\n /**\n * @dev Returns total validator weights of the address list.\n */\n function sumValidatorWeights(address[] calldata _addrList) external view returns (uint256 _weight);\n\n /**\n * @dev Returns total governor weights of the address list.\n */\n function sumGovernorWeights(address[] calldata _addrList) external view returns (uint256 _weight);\n\n /**\n * @dev Returns the validator list attached with governor address and weight.\n */\n function getValidatorInfo() external view returns (WeightedValidator[] memory _list);\n\n /**\n * @dev Returns the validator list.\n */\n function getValidators() external view returns (address[] memory _validators);\n\n /**\n * @dev Returns the validator at `_index` position.\n */\n function validators(uint256 _index) external view returns (WeightedValidator memory);\n\n /**\n * @dev Returns total of validators.\n */\n function totalValidators() external view returns (uint256);\n\n /**\n * @dev Returns total weights.\n */\n function totalWeights() external view returns (uint256);\n\n /**\n * @dev Adds validators.\n *\n * Requirements:\n * - The weights are larger than 0.\n * - The validators are not added.\n * - The method caller is admin.\n *\n * Emits the `ValidatorsAdded` event.\n *\n */\n function addValidators(WeightedValidator[] calldata _validators) external;\n\n /**\n * @dev Updates validators.\n *\n * Requirements:\n * - The weights are larger than 0.\n * - The validators are added.\n * - The method caller is admin.\n *\n * Emits the `ValidatorsUpdated` event.\n *\n */\n function updateValidators(WeightedValidator[] calldata _validators) external;\n\n /**\n * @dev Removes validators.\n *\n * Requirements:\n * - The validators are added.\n * - The method caller is admin.\n *\n * Emits the `ValidatorsRemoved` event.\n *\n */\n function removeValidators(address[] calldata _validators) external;\n}\n" + }, + "contracts/v0.8/extensions/HasProxyAdmin.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/utils/StorageSlot.sol\";\n\nabstract contract HasProxyAdmin {\n // bytes32(uint256(keccak256(\"eip1967.proxy.admin\")) - 1));\n bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;\n\n modifier onlyAdmin() {\n require(msg.sender == _getAdmin(), \"HasProxyAdmin: unauthorized sender\");\n _;\n }\n\n /**\n * @dev Returns proxy admin.\n */\n function _getAdmin() internal view returns (address) {\n return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/introspection/IERC165.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n /**\n * @dev Returns true if this contract implements the interface defined by\n * `interfaceId`. See the corresponding\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n * to learn more about how these ids are created.\n *\n * This function call must use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n" + }, + "contracts/v0.8/interfaces/SignatureConsumer.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface SignatureConsumer {\n struct Signature {\n uint8 v;\n bytes32 r;\n bytes32 s;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Strings.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n bytes16 private constant _HEX_SYMBOLS = \"0123456789abcdef\";\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n */\n function toString(uint256 value) internal pure returns (string memory) {\n // Inspired by OraclizeAPI's implementation - MIT licence\n // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol\n\n if (value == 0) {\n return \"0\";\n }\n uint256 temp = value;\n uint256 digits;\n while (temp != 0) {\n digits++;\n temp /= 10;\n }\n bytes memory buffer = new bytes(digits);\n while (value != 0) {\n digits -= 1;\n buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));\n value /= 10;\n }\n return string(buffer);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n */\n function toHexString(uint256 value) internal pure returns (string memory) {\n if (value == 0) {\n return \"0x00\";\n }\n uint256 temp = value;\n uint256 length = 0;\n while (temp != 0) {\n length++;\n temp >>= 8;\n }\n return toHexString(value, length);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n */\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n bytes memory buffer = new bytes(2 * length + 2);\n buffer[0] = \"0\";\n buffer[1] = \"x\";\n for (uint256 i = 2 * length + 1; i > 1; --i) {\n buffer[i] = _HEX_SYMBOLS[value & 0xf];\n value >>= 4;\n }\n require(value == 0, \"Strings: hex length insufficient\");\n return string(buffer);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/introspection/ERC165.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC165.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n *\n * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.\n */\nabstract contract ERC165 is IERC165 {\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IERC165).interfaceId;\n }\n}\n" + }, + "@openzeppelin/contracts/access/AccessControlEnumerable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControlEnumerable.sol\";\nimport \"./AccessControl.sol\";\nimport \"../utils/structs/EnumerableSet.sol\";\n\n/**\n * @dev Extension of {AccessControl} that allows enumerating the members of each role.\n */\nabstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {\n using EnumerableSet for EnumerableSet.AddressSet;\n\n mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers;\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) {\n return _roleMembers[role].at(index);\n }\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) {\n return _roleMembers[role].length();\n }\n\n /**\n * @dev Overload {_grantRole} to track enumerable memberships\n */\n function _grantRole(bytes32 role, address account) internal virtual override {\n super._grantRole(role, account);\n _roleMembers[role].add(account);\n }\n\n /**\n * @dev Overload {_revokeRole} to track enumerable memberships\n */\n function _revokeRole(bytes32 role, address account) internal virtual override {\n super._revokeRole(role, account);\n _roleMembers[role].remove(account);\n }\n}\n" + }, + "contracts/v0.8/library/Transfer.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/utils/cryptography/ECDSA.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/utils/Strings.sol\";\nimport \"./Token.sol\";\n\nlibrary Transfer {\n using ECDSA for bytes32;\n\n enum Kind {\n Deposit,\n Withdrawal\n }\n\n struct Request {\n // For deposit request: Recipient address on Ronin network\n // For withdrawal request: Recipient address on mainchain network\n address recipientAddr;\n // Token address to deposit/withdraw\n // Value 0: native token\n address tokenAddr;\n Token.Info info;\n }\n\n /**\n * @dev Converts the transfer request into the deposit receipt.\n */\n function into_deposit_receipt(\n Request memory _request,\n address _requester,\n uint256 _id,\n address _roninTokenAddr,\n uint256 _roninChainId\n ) internal view returns (Receipt memory _receipt) {\n _receipt.id = _id;\n _receipt.kind = Kind.Deposit;\n _receipt.mainchain.addr = _requester;\n _receipt.mainchain.tokenAddr = _request.tokenAddr;\n _receipt.mainchain.chainId = block.chainid;\n _receipt.ronin.addr = _request.recipientAddr;\n _receipt.ronin.tokenAddr = _roninTokenAddr;\n _receipt.ronin.chainId = _roninChainId;\n _receipt.info = _request.info;\n }\n\n /**\n * @dev Converts the transfer request into the withdrawal receipt.\n */\n function into_withdrawal_receipt(\n Request memory _request,\n address _requester,\n uint256 _id,\n address _mainchainTokenAddr,\n uint256 _mainchainId\n ) internal view returns (Receipt memory _receipt) {\n _receipt.id = _id;\n _receipt.kind = Kind.Withdrawal;\n _receipt.ronin.addr = _requester;\n _receipt.ronin.tokenAddr = _request.tokenAddr;\n _receipt.ronin.chainId = block.chainid;\n _receipt.mainchain.addr = _request.recipientAddr;\n _receipt.mainchain.tokenAddr = _mainchainTokenAddr;\n _receipt.mainchain.chainId = _mainchainId;\n _receipt.info = _request.info;\n }\n\n struct Receipt {\n uint256 id;\n Kind kind;\n Token.Owner mainchain;\n Token.Owner ronin;\n Token.Info info;\n }\n\n // keccak256(\"Receipt(uint256 id,uint8 kind,TokenOwner mainchain,TokenOwner ronin,TokenInfo info)TokenInfo(uint8 erc,uint256 id,uint256 quantity)TokenOwner(address addr,address tokenAddr,uint256 chainId)\");\n bytes32 public constant TYPE_HASH = 0xb9d1fe7c9deeec5dc90a2f47ff1684239519f2545b2228d3d91fb27df3189eea;\n\n /**\n * @dev Returns token info struct hash.\n */\n function hash(Receipt memory _receipt) internal pure returns (bytes32) {\n return\n keccak256(\n abi.encode(\n TYPE_HASH,\n _receipt.id,\n _receipt.kind,\n Token.hash(_receipt.mainchain),\n Token.hash(_receipt.ronin),\n Token.hash(_receipt.info)\n )\n );\n }\n\n /**\n * @dev Returns the receipt digest.\n */\n function receiptDigest(bytes32 _domainSeparator, bytes32 _receiptHash) internal pure returns (bytes32) {\n return _domainSeparator.toTypedDataHash(_receiptHash);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Context.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n" + }, + "@openzeppelin/contracts/security/Pausable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n" + }, + "@openzeppelin/contracts/utils/structs/EnumerableSet.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 => uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n" + }, + "contracts/v0.8/interfaces/IQuorum.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IQuorum {\n /// @dev Emitted when the threshold is updated\n event ThresholdUpdated(\n uint256 indexed nonce,\n uint256 indexed numerator,\n uint256 indexed denominator,\n uint256 previousNumerator,\n uint256 previousDenominator\n );\n\n /**\n * @dev Returns the threshold.\n */\n function getThreshold() external view returns (uint256 _num, uint256 _denom);\n\n /**\n * @dev Checks whether the `_voteWeight` passes the threshold.\n */\n function checkThreshold(uint256 _voteWeight) external view returns (bool);\n\n /**\n * @dev Returns the minimum vote weight to pass the threshold.\n */\n function minimumVoteWeight() external view returns (uint256);\n\n /**\n * @dev Sets the threshold.\n *\n * Requirements:\n * - The method caller is admin.\n *\n * Emits the `ThresholdUpdated` event.\n *\n */\n function setThreshold(uint256 _numerator, uint256 _denominator)\n external\n returns (uint256 _previousNum, uint256 _previousDenom);\n}\n" + }, + "contracts/v0.8/interfaces/IWETH.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH {\n function deposit() external payable;\n\n function withdraw(uint256 _wad) external;\n\n function balanceOf(address) external view returns (uint256);\n}\n" + }, + "@openzeppelin/contracts/access/AccessControl.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControl.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\nimport \"../utils/Context.sol\";\nimport \"../utils/Strings.sol\";\nimport \"../utils/introspection/ERC165.sol\";\n\n/**\n * @dev Contract module that allows children to implement role-based access\n * control mechanisms. This is a lightweight version that doesn't allow enumerating role\n * members except through off-chain means by accessing the contract event logs. Some\n * applications may benefit from on-chain enumerability, for those cases see\n * {AccessControlEnumerable}.\n *\n * Roles are referred to by their `bytes32` identifier. These should be exposed\n * in the external API and be unique. The best way to achieve this is by\n * using `public constant` hash digests:\n *\n * ```\n * bytes32 public constant MY_ROLE = keccak256(\"MY_ROLE\");\n * ```\n *\n * Roles can be used to represent a set of permissions. To restrict access to a\n * function call, use {hasRole}:\n *\n * ```\n * function foo() public {\n * require(hasRole(MY_ROLE, msg.sender));\n * ...\n * }\n * ```\n *\n * Roles can be granted and revoked dynamically via the {grantRole} and\n * {revokeRole} functions. Each role has an associated admin role, and only\n * accounts that have a role's admin role can call {grantRole} and {revokeRole}.\n *\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\n * that only accounts with this role will be able to grant or revoke other\n * roles. More complex role relationships can be created by using\n * {_setRoleAdmin}.\n *\n * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to\n * grant and revoke this role. Extra precautions should be taken to secure\n * accounts that have been granted it.\n */\nabstract contract AccessControl is Context, IAccessControl, ERC165 {\n struct RoleData {\n mapping(address => bool) members;\n bytes32 adminRole;\n }\n\n mapping(bytes32 => RoleData) private _roles;\n\n bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;\n\n /**\n * @dev Modifier that checks that an account has a specific role. Reverts\n * with a standardized message including the required role.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n *\n * _Available since v4.1._\n */\n modifier onlyRole(bytes32 role) {\n _checkRole(role, _msgSender());\n _;\n }\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) public view virtual override returns (bool) {\n return _roles[role].members[account];\n }\n\n /**\n * @dev Revert with a standard message if `account` is missing `role`.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n */\n function _checkRole(bytes32 role, address account) internal view virtual {\n if (!hasRole(role, account)) {\n revert(\n string(\n abi.encodePacked(\n \"AccessControl: account \",\n Strings.toHexString(uint160(account), 20),\n \" is missing role \",\n Strings.toHexString(uint256(role), 32)\n )\n )\n );\n }\n }\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {\n return _roles[role].adminRole;\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _grantRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _revokeRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been revoked `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) public virtual override {\n require(account == _msgSender(), \"AccessControl: can only renounce roles for self\");\n\n _revokeRole(role, account);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event. Note that unlike {grantRole}, this function doesn't perform any\n * checks on the calling account.\n *\n * [WARNING]\n * ====\n * This function should only be called from the constructor when setting\n * up the initial roles for the system.\n *\n * Using this function in any other way is effectively circumventing the admin\n * system imposed by {AccessControl}.\n * ====\n *\n * NOTE: This function is deprecated in favor of {_grantRole}.\n */\n function _setupRole(bytes32 role, address account) internal virtual {\n _grantRole(role, account);\n }\n\n /**\n * @dev Sets `adminRole` as ``role``'s admin role.\n *\n * Emits a {RoleAdminChanged} event.\n */\n function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\n bytes32 previousAdminRole = getRoleAdmin(role);\n _roles[role].adminRole = adminRole;\n emit RoleAdminChanged(role, previousAdminRole, adminRole);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * Internal function without access restriction.\n */\n function _grantRole(bytes32 role, address account) internal virtual {\n if (!hasRole(role, account)) {\n _roles[role].members[account] = true;\n emit RoleGranted(role, account, _msgSender());\n }\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * Internal function without access restriction.\n */\n function _revokeRole(bytes32 role, address account) internal virtual {\n if (hasRole(role, account)) {\n _roles[role].members[account] = false;\n emit RoleRevoked(role, account, _msgSender());\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + } + }, + "settings": { + "evmVersion": "london", + "libraries": {}, + "metadata": { + "bytecodeHash": "ipfs", + "useLiteralContent": true + }, + "optimizer": { + "enabled": true, + "runs": 1000 + }, + "remappings": [], + "outputSelection": { + "*": { + "*": [ + "evm.bytecode", + "evm.deployedBytecode", + "devdoc", + "userdoc", + "metadata", + "abi" + ] + } + } + } + }, + "ABI": "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"tokens\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"limits\",\"type\":\"uint256[]\"}],\"name\":\"DailyWithdrawalLimitsUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"receiptHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"enum Transfer.Kind\",\"name\":\"kind\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"mainchain\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"ronin\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"enum Token.Standard\",\"name\":\"erc\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Info\",\"name\":\"info\",\"type\":\"tuple\"}],\"indexed\":false,\"internalType\":\"struct Transfer.Receipt\",\"name\":\"receipt\",\"type\":\"tuple\"}],\"name\":\"DepositRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"tokens\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"thresholds\",\"type\":\"uint256[]\"}],\"name\":\"HighTierThresholdsUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"numerator\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"denominator\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"previousNumerator\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"previousDenominator\",\"type\":\"uint256\"}],\"name\":\"HighTierVoteWeightThresholdUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"tokens\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"thresholds\",\"type\":\"uint256[]\"}],\"name\":\"LockedThresholdsUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"previousAdminRole\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"newAdminRole\",\"type\":\"bytes32\"}],\"name\":\"RoleAdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleGranted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleRevoked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"numerator\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"denominator\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"previousNumerator\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"previousDenominator\",\"type\":\"uint256\"}],\"name\":\"ThresholdUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"mainchainTokens\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"roninTokens\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"enum Token.Standard[]\",\"name\":\"standards\",\"type\":\"uint8[]\"}],\"name\":\"TokenMapped\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"tokens\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"percentages\",\"type\":\"uint256[]\"}],\"name\":\"UnlockFeePercentagesUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IWeightedValidator\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"ValidatorContractUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"receiptHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"enum Transfer.Kind\",\"name\":\"kind\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"mainchain\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"ronin\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"enum Token.Standard\",\"name\":\"erc\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Info\",\"name\":\"info\",\"type\":\"tuple\"}],\"indexed\":false,\"internalType\":\"struct Transfer.Receipt\",\"name\":\"receipt\",\"type\":\"tuple\"}],\"name\":\"WithdrawalLocked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"receiptHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"enum Transfer.Kind\",\"name\":\"kind\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"mainchain\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"ronin\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"enum Token.Standard\",\"name\":\"erc\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Info\",\"name\":\"info\",\"type\":\"tuple\"}],\"indexed\":false,\"internalType\":\"struct Transfer.Receipt\",\"name\":\"receipt\",\"type\":\"tuple\"}],\"name\":\"WithdrawalUnlocked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"receiptHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"enum Transfer.Kind\",\"name\":\"kind\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"mainchain\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"ronin\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"enum Token.Standard\",\"name\":\"erc\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Info\",\"name\":\"info\",\"type\":\"tuple\"}],\"indexed\":false,\"internalType\":\"struct Transfer.Receipt\",\"name\":\"receipt\",\"type\":\"tuple\"}],\"name\":\"Withdrew\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IWETH\",\"name\":\"weth\",\"type\":\"address\"}],\"name\":\"WrappedNativeTokenContractUpdated\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"DEFAULT_ADMIN_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"DOMAIN_SEPARATOR\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"WITHDRAWAL_UNLOCKER_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"_MAX_PERCENTAGE\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_voteWeight\",\"type\":\"uint256\"}],\"name\":\"checkHighTierVoteWeightThreshold\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_voteWeight\",\"type\":\"uint256\"}],\"name\":\"checkThreshold\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"dailyWithdrawalLimit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"depositCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getHighTierVoteWeightThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"}],\"name\":\"getRoleAdmin\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"getRoleMember\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"}],\"name\":\"getRoleMemberCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_mainchainToken\",\"type\":\"address\"}],\"name\":\"getRoninToken\",\"outputs\":[{\"components\":[{\"internalType\":\"enum Token.Standard\",\"name\":\"erc\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"}],\"internalType\":\"struct MappedTokenConsumer.MappedToken\",\"name\":\"_token\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"grantRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"hasRole\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"highTierThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_roleSetter\",\"type\":\"address\"},{\"internalType\":\"contract IWETH\",\"name\":\"_wrappedToken\",\"type\":\"address\"},{\"internalType\":\"contract IWeightedValidator\",\"name\":\"_validatorContract\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_roninChainId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_numerator\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_highTierVWNumerator\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_denominator\",\"type\":\"uint256\"},{\"internalType\":\"address[][3]\",\"name\":\"_addresses\",\"type\":\"address[][3]\"},{\"internalType\":\"uint256[][4]\",\"name\":\"_thresholds\",\"type\":\"uint256[][4]\"},{\"internalType\":\"enum Token.Standard[]\",\"name\":\"_standards\",\"type\":\"uint8[]\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"lastDateSynced\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"lastSyncedWithdrawal\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"lockedThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_mainchainTokens\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"_roninTokens\",\"type\":\"address[]\"},{\"internalType\":\"enum Token.Standard[]\",\"name\":\"_standards\",\"type\":\"uint8[]\"}],\"name\":\"mapTokens\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_mainchainTokens\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"_roninTokens\",\"type\":\"address[]\"},{\"internalType\":\"enum Token.Standard[]\",\"name\":\"_standards\",\"type\":\"uint8[]\"},{\"internalType\":\"uint256[][4]\",\"name\":\"_thresholds\",\"type\":\"uint256[][4]\"}],\"name\":\"mapTokensAndThresholds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"minimumVoteWeight\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"nonce\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_quantity\",\"type\":\"uint256\"}],\"name\":\"reachedWithdrawalLimit\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"receiveEther\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"renounceRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"recipientAddr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"enum Token.Standard\",\"name\":\"erc\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Info\",\"name\":\"info\",\"type\":\"tuple\"}],\"internalType\":\"struct Transfer.Request\",\"name\":\"_request\",\"type\":\"tuple\"}],\"name\":\"requestDepositFor\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"revokeRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"roninChainId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_tokens\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"_limits\",\"type\":\"uint256[]\"}],\"name\":\"setDailyWithdrawalLimits\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_tokens\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"_thresholds\",\"type\":\"uint256[]\"}],\"name\":\"setHighTierThresholds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_numerator\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_denominator\",\"type\":\"uint256\"}],\"name\":\"setHighTierVoteWeightThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"_previousNum\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_previousDenom\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_tokens\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"_thresholds\",\"type\":\"uint256[]\"}],\"name\":\"setLockedThresholds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_numerator\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_denominator\",\"type\":\"uint256\"}],\"name\":\"setThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"_previousNum\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_previousDenom\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_tokens\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"_percentages\",\"type\":\"uint256[]\"}],\"name\":\"setUnlockFeePercentages\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IWeightedValidator\",\"name\":\"_validatorContract\",\"type\":\"address\"}],\"name\":\"setValidatorContract\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IWETH\",\"name\":\"_wrappedToken\",\"type\":\"address\"}],\"name\":\"setWrappedNativeTokenContract\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"enum Transfer.Kind\",\"name\":\"kind\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"mainchain\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"ronin\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"enum Token.Standard\",\"name\":\"erc\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Info\",\"name\":\"info\",\"type\":\"tuple\"}],\"internalType\":\"struct Transfer.Receipt\",\"name\":\"_receipt\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"internalType\":\"struct SignatureConsumer.Signature[]\",\"name\":\"_signatures\",\"type\":\"tuple[]\"}],\"name\":\"submitWithdrawal\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"_locked\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"unlockFeePercentages\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"enum Transfer.Kind\",\"name\":\"kind\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"mainchain\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"ronin\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"enum Token.Standard\",\"name\":\"erc\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Info\",\"name\":\"info\",\"type\":\"tuple\"}],\"internalType\":\"struct Transfer.Receipt\",\"name\":\"_receipt\",\"type\":\"tuple\"}],\"name\":\"unlockWithdrawal\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"validatorContract\",\"outputs\":[{\"internalType\":\"contract IWeightedValidator\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"withdrawalHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"withdrawalLocked\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"wrappedNativeToken\",\"outputs\":[{\"internalType\":\"contract IWETH\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]", + "ContractName": "MainchainGatewayV2", + "CompilerVersion": "v0.8.9+commit.e5eed63a", + "OptimizationUsed": 1, + "Runs": 1000, + "ConstructorArguments": "0x", + "EVMVersion": "Default", + "Library": "", + "LicenseType": "MIT", + "Proxy": 0, + "SwarmSource": "" + } +] \ No newline at end of file diff --git a/testdata/etherscan/0x8B3D32cf2bb4d0D16656f4c0b04Fa546274f1545/creation_data.json b/testdata/etherscan/0x8B3D32cf2bb4d0D16656f4c0b04Fa546274f1545/creation_data.json new file mode 100644 index 000000000..fa26d9cad --- /dev/null +++ b/testdata/etherscan/0x8B3D32cf2bb4d0D16656f4c0b04Fa546274f1545/creation_data.json @@ -0,0 +1,5 @@ +{ + "contractAddress": "0x8b3d32cf2bb4d0d16656f4c0b04fa546274f1545", + "contractCreator": "0x958892b4a0512b28aaac890fc938868bbd42f064", + "txHash": "0x79820495643caf5a1e7e96578361c9ddba0e0735cd684ada7450254f6fd58f51" +} \ No newline at end of file diff --git a/testdata/etherscan/0x8B3D32cf2bb4d0D16656f4c0b04Fa546274f1545/metadata.json b/testdata/etherscan/0x8B3D32cf2bb4d0D16656f4c0b04Fa546274f1545/metadata.json new file mode 100644 index 000000000..5921b5b30 --- /dev/null +++ b/testdata/etherscan/0x8B3D32cf2bb4d0D16656f4c0b04Fa546274f1545/metadata.json @@ -0,0 +1,63 @@ +[ + { + "SourceCode": { + "language": "Solidity", + "sources": { + "contracts/governance/governor/GovernorStorage.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.9;\n\nimport \"./IIpt.sol\";\nimport \"./Structs.sol\";\n\ncontract GovernorCharlieDelegatorStorage {\n /// @notice Active brains of Governor\n address public implementation;\n}\n\n/**\n * @title Storage for Governor Charlie Delegate\n * @notice For future upgrades, do not change GovernorCharlieDelegateStorage. Create a new\n * contract which implements GovernorCharlieDelegateStorage and following the naming convention\n * GovernorCharlieDelegateStorageVX.\n */\n//solhint-disable-next-line max-states-count\ncontract GovernorCharlieDelegateStorage is GovernorCharlieDelegatorStorage {\n /// @notice The number of votes in support of a proposal required in order for a quorum to be reached and for a vote to succeed\n uint256 public quorumVotes;\n\n /// @notice The number of votes in support of a proposal required in order for an emergency quorum to be reached and for a vote to succeed\n uint256 public emergencyQuorumVotes;\n\n /// @notice The delay before voting on a proposal may take place, once proposed, in blocks\n uint256 public votingDelay;\n\n /// @notice The duration of voting on a proposal, in blocks\n uint256 public votingPeriod;\n\n /// @notice The number of votes required in order for a voter to become a proposer\n uint256 public proposalThreshold;\n\n /// @notice Initial proposal id set at become\n uint256 public initialProposalId;\n\n /// @notice The total number of proposals\n uint256 public proposalCount;\n\n /// @notice The address of the Interest Protocol governance token\n IIpt public ipt;\n\n /// @notice The official record of all proposals ever proposed\n mapping(uint256 => Proposal) public proposals;\n\n /// @notice The latest proposal for each proposer\n mapping(address => uint256) public latestProposalIds;\n\n /// @notice The latest proposal for each proposer\n mapping(bytes32 => bool) public queuedTransactions;\n\n /// @notice The proposal holding period\n uint256 public proposalTimelockDelay;\n\n /// @notice Stores the expiration of account whitelist status as a timestamp\n mapping(address => uint256) public whitelistAccountExpirations;\n\n /// @notice Address which manages whitelisted proposals and whitelist accounts\n address public whitelistGuardian;\n\n /// @notice The duration of the voting on a emergency proposal, in blocks\n uint256 public emergencyVotingPeriod;\n\n /// @notice The emergency proposal holding period\n uint256 public emergencyTimelockDelay;\n\n /// all receipts for proposal\n mapping(uint256 => mapping(address => Receipt)) public proposalReceipts;\n\n /// @notice The emergency proposal holding period\n bool public initialized;\n\n /// @notice The number of votes to reject an optimistic proposal\n uint256 public optimisticQuorumVotes; \n\n /// @notice The delay period before voting begins\n uint256 public optimisticVotingDelay; \n\n /// @notice The maximum number of seconds an address can be whitelisted for\n uint256 public maxWhitelistPeriod; \n}\n" + }, + "hardhat/console.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity >= 0.4.22 <0.9.0;\n\nlibrary console {\n\taddress constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67);\n\n\tfunction _sendLogPayload(bytes memory payload) private view {\n\t\tuint256 payloadLength = payload.length;\n\t\taddress consoleAddress = CONSOLE_ADDRESS;\n\t\tassembly {\n\t\t\tlet payloadStart := add(payload, 32)\n\t\t\tlet r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0)\n\t\t}\n\t}\n\n\tfunction log() internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log()\"));\n\t}\n\n\tfunction logInt(int p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(int)\", p0));\n\t}\n\n\tfunction logUint(uint p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint)\", p0));\n\t}\n\n\tfunction logString(string memory p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n\t}\n\n\tfunction logBool(bool p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n\t}\n\n\tfunction logAddress(address p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n\t}\n\n\tfunction logBytes(bytes memory p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes)\", p0));\n\t}\n\n\tfunction logBytes1(bytes1 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes1)\", p0));\n\t}\n\n\tfunction logBytes2(bytes2 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes2)\", p0));\n\t}\n\n\tfunction logBytes3(bytes3 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes3)\", p0));\n\t}\n\n\tfunction logBytes4(bytes4 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes4)\", p0));\n\t}\n\n\tfunction logBytes5(bytes5 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes5)\", p0));\n\t}\n\n\tfunction logBytes6(bytes6 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes6)\", p0));\n\t}\n\n\tfunction logBytes7(bytes7 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes7)\", p0));\n\t}\n\n\tfunction logBytes8(bytes8 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes8)\", p0));\n\t}\n\n\tfunction logBytes9(bytes9 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes9)\", p0));\n\t}\n\n\tfunction logBytes10(bytes10 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes10)\", p0));\n\t}\n\n\tfunction logBytes11(bytes11 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes11)\", p0));\n\t}\n\n\tfunction logBytes12(bytes12 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes12)\", p0));\n\t}\n\n\tfunction logBytes13(bytes13 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes13)\", p0));\n\t}\n\n\tfunction logBytes14(bytes14 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes14)\", p0));\n\t}\n\n\tfunction logBytes15(bytes15 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes15)\", p0));\n\t}\n\n\tfunction logBytes16(bytes16 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes16)\", p0));\n\t}\n\n\tfunction logBytes17(bytes17 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes17)\", p0));\n\t}\n\n\tfunction logBytes18(bytes18 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes18)\", p0));\n\t}\n\n\tfunction logBytes19(bytes19 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes19)\", p0));\n\t}\n\n\tfunction logBytes20(bytes20 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes20)\", p0));\n\t}\n\n\tfunction logBytes21(bytes21 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes21)\", p0));\n\t}\n\n\tfunction logBytes22(bytes22 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes22)\", p0));\n\t}\n\n\tfunction logBytes23(bytes23 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes23)\", p0));\n\t}\n\n\tfunction logBytes24(bytes24 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes24)\", p0));\n\t}\n\n\tfunction logBytes25(bytes25 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes25)\", p0));\n\t}\n\n\tfunction logBytes26(bytes26 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes26)\", p0));\n\t}\n\n\tfunction logBytes27(bytes27 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes27)\", p0));\n\t}\n\n\tfunction logBytes28(bytes28 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes28)\", p0));\n\t}\n\n\tfunction logBytes29(bytes29 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes29)\", p0));\n\t}\n\n\tfunction logBytes30(bytes30 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes30)\", p0));\n\t}\n\n\tfunction logBytes31(bytes31 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes31)\", p0));\n\t}\n\n\tfunction logBytes32(bytes32 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes32)\", p0));\n\t}\n\n\tfunction log(uint p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint)\", p0));\n\t}\n\n\tfunction log(string memory p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n\t}\n\n\tfunction log(bool p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n\t}\n\n\tfunction log(address p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n\t}\n\n\tfunction log(uint p0, uint p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint)\", p0, p1));\n\t}\n\n\tfunction log(uint p0, string memory p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string)\", p0, p1));\n\t}\n\n\tfunction log(uint p0, bool p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool)\", p0, p1));\n\t}\n\n\tfunction log(uint p0, address p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address)\", p0, p1));\n\t}\n\n\tfunction log(string memory p0, uint p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint)\", p0, p1));\n\t}\n\n\tfunction log(string memory p0, string memory p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string)\", p0, p1));\n\t}\n\n\tfunction log(string memory p0, bool p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool)\", p0, p1));\n\t}\n\n\tfunction log(string memory p0, address p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address)\", p0, p1));\n\t}\n\n\tfunction log(bool p0, uint p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint)\", p0, p1));\n\t}\n\n\tfunction log(bool p0, string memory p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string)\", p0, p1));\n\t}\n\n\tfunction log(bool p0, bool p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool)\", p0, p1));\n\t}\n\n\tfunction log(bool p0, address p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address)\", p0, p1));\n\t}\n\n\tfunction log(address p0, uint p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint)\", p0, p1));\n\t}\n\n\tfunction log(address p0, string memory p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string)\", p0, p1));\n\t}\n\n\tfunction log(address p0, bool p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool)\", p0, p1));\n\t}\n\n\tfunction log(address p0, address p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address)\", p0, p1));\n\t}\n\n\tfunction log(uint p0, uint p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, uint p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, uint p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, uint p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, string memory p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, string memory p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, string memory p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, string memory p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, bool p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, bool p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, bool p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, bool p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, address p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, address p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, address p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, address p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, uint p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, uint p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, uint p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, uint p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, string memory p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, string memory p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, string memory p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, string memory p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, bool p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, bool p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, bool p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, bool p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, address p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, address p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, address p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, address p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, uint p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, uint p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, uint p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, uint p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, string memory p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, string memory p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, string memory p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, string memory p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, bool p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, bool p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, bool p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, bool p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, address p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, address p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, address p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, address p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, uint p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, uint p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, uint p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, uint p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, string memory p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, string memory p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, string memory p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, string memory p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, bool p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, bool p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, bool p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, bool p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, address p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, address p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, address p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, address p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, uint p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,address)\", p0, p1, p2, p3));\n\t}\n\n}\n" + }, + "contracts/governance/governor/IGovernor.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.9;\n\nimport \"./Structs.sol\";\n\n/// @title interface to interact with TokenDelgator\ninterface IGovernorCharlieDelegator {\n function _setImplementation(address implementation_) external;\n\n fallback() external payable;\n\n receive() external payable;\n}\n\n/// @title interface to interact with TokenDelgate\ninterface IGovernorCharlieDelegate {\n function initialize(\n address ipt_\n ) external;\n\n function propose(\n address[] memory targets,\n uint256[] memory values,\n string[] memory signatures,\n bytes[] memory calldatas,\n string memory description,\n bool emergency\n ) external returns (uint256);\n\n function queue(uint256 proposalId) external;\n\n function execute(uint256 proposalId) external payable;\n\n function executeTransaction(\n address target,\n uint256 value,\n string memory signature,\n bytes memory data,\n uint256 eta\n ) external payable;\n\n function cancel(uint256 proposalId) external;\n\n function getActions(uint256 proposalId)\n external\n view\n returns (\n address[] memory targets,\n uint256[] memory values,\n string[] memory signatures,\n bytes[] memory calldatas\n );\n\n function getReceipt(uint256 proposalId, address voter) external view returns (Receipt memory);\n\n function state(uint256 proposalId) external view returns (ProposalState);\n\n function castVote(uint256 proposalId, uint8 support) external;\n\n function castVoteWithReason(\n uint256 proposalId,\n uint8 support,\n string calldata reason\n ) external;\n\n function castVoteBySig(\n uint256 proposalId,\n uint8 support,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n function isWhitelisted(address account) external view returns (bool);\n\n function _setDelay(uint256 proposalTimelockDelay_) external;\n\n function _setEmergencyDelay(uint256 emergencyTimelockDelay_) external;\n\n function _setVotingDelay(uint256 newVotingDelay) external;\n\n function _setVotingPeriod(uint256 newVotingPeriod) external;\n\n function _setEmergencyVotingPeriod(uint256 newEmergencyVotingPeriod) external;\n\n function _setProposalThreshold(uint256 newProposalThreshold) external;\n\n function _setQuorumVotes(uint256 newQuorumVotes) external;\n\n function _setEmergencyQuorumVotes(uint256 newEmergencyQuorumVotes) external;\n\n function _setWhitelistAccountExpiration(address account, uint256 expiration) external;\n\n function _setWhitelistGuardian(address account) external;\n\n function _setOptimisticDelay(uint256 newOptimisticVotingDelay) external;\n\n function _setOptimisticQuorumVotes(uint256 newOptimisticQuorumVotes) external;\n}\n\n/// @title interface which contains all events emitted by delegator & delegate\ninterface GovernorCharlieEvents {\n /// @notice An event emitted when a new proposal is created\n event ProposalCreated(\n uint256 indexed id,\n address indexed proposer,\n address[] targets,\n uint256[] values,\n string[] signatures,\n bytes[] calldatas,\n uint256 indexed startBlock,\n uint256 endBlock,\n string description\n );\n\n /// @notice An event emitted when a vote has been cast on a proposal\n /// @param voter The address which casted a vote\n /// @param proposalId The proposal id which was voted on\n /// @param support Support value for the vote. 0=against, 1=for, 2=abstain\n /// @param votes Number of votes which were cast by the voter\n /// @param reason The reason given for the vote by the voter\n event VoteCast(address indexed voter, uint256 indexed proposalId, uint8 support, uint256 votes, string reason);\n\n /// @notice An event emitted when a proposal has been canceled\n event ProposalCanceled(uint256 indexed id);\n\n /// @notice An event emitted when a proposal has been queued in the Timelock\n event ProposalQueued(uint256 indexed id, uint256 eta);\n\n /// @notice An event emitted when a proposal has been executed in the Timelock\n event ProposalExecuted(uint256 indexed id);\n\n /// @notice An event emitted when the voting delay is set\n event VotingDelaySet(uint256 oldVotingDelay, uint256 newVotingDelay);\n\n /// @notice An event emitted when the voting period is set\n event VotingPeriodSet(uint256 oldVotingPeriod, uint256 newVotingPeriod);\n\n /// @notice An event emitted when the emergency voting period is set\n event EmergencyVotingPeriodSet(uint256 oldEmergencyVotingPeriod, uint256 emergencyVotingPeriod);\n\n /// @notice Emitted when implementation is changed\n event NewImplementation(address oldImplementation, address newImplementation);\n\n /// @notice Emitted when proposal threshold is set\n event ProposalThresholdSet(uint256 oldProposalThreshold, uint256 newProposalThreshold);\n\n /// @notice Emitted when pendingAdmin is changed\n event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin);\n\n /// @notice Emitted when pendingAdmin is accepted, which means admin is updated\n event NewAdmin(address oldAdmin, address newAdmin);\n\n /// @notice Emitted when whitelist account expiration is set\n event WhitelistAccountExpirationSet(address account, uint256 expiration);\n\n /// @notice Emitted when the whitelistGuardian is set\n event WhitelistGuardianSet(address oldGuardian, address newGuardian);\n\n /// @notice Emitted when the a new delay is set\n event NewDelay(uint256 oldTimelockDelay, uint256 proposalTimelockDelay);\n\n /// @notice Emitted when the a new emergency delay is set\n event NewEmergencyDelay(uint256 oldEmergencyTimelockDelay, uint256 emergencyTimelockDelay);\n\n /// @notice Emitted when the quorum is updated\n event NewQuorum(uint256 oldQuorumVotes, uint256 quorumVotes);\n\n /// @notice Emitted when the emergency quorum is updated\n event NewEmergencyQuorum(uint256 oldEmergencyQuorumVotes, uint256 emergencyQuorumVotes);\n\n /// @notice An event emitted when the optimistic voting delay is set\n event OptimisticVotingDelaySet(uint256 oldOptimisticVotingDelay, uint256 optimisticVotingDelay);\n\n /// @notice Emitted when the optimistic quorum is updated\n event OptimisticQuorumVotesSet(uint256 oldOptimisticQuorumVotes, uint256 optimisticQuorumVotes);\n\n /// @notice Emitted when a transaction is canceled\n event CancelTransaction(\n bytes32 indexed txHash,\n address indexed target,\n uint256 value,\n string signature,\n bytes data,\n uint256 eta\n );\n\n /// @notice Emitted when a transaction is executed\n event ExecuteTransaction(\n bytes32 indexed txHash,\n address indexed target,\n uint256 value,\n string signature,\n bytes data,\n uint256 eta\n );\n\n /// @notice Emitted when a transaction is queued\n event QueueTransaction(\n bytes32 indexed txHash,\n address indexed target,\n uint256 value,\n string signature,\n bytes data,\n uint256 eta\n );\n}\n" + }, + "contracts/governance/governor/Structs.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.9;\n\nstruct Proposal {\n /// @notice Unique id for looking up a proposal\n uint256 id;\n /// @notice Creator of the proposal\n address proposer;\n /// @notice The timestamp that the proposal will be available for execution, set once the vote succeeds\n uint256 eta;\n /// @notice the ordered list of target addresses for calls to be made\n address[] targets;\n /// @notice The ordered list of values (i.e. msg.value) to be passed to the calls to be made\n uint256[] values;\n /// @notice The ordered list of function signatures to be called\n string[] signatures;\n /// @notice The ordered list of calldata to be passed to each call\n bytes[] calldatas;\n /// @notice The block at which voting begins: holders must delegate their votes prior to this block\n uint256 startBlock;\n /// @notice The block at which voting ends: votes must be cast prior to this block\n uint256 endBlock;\n /// @notice Current number of votes in favor of this proposal\n uint256 forVotes;\n /// @notice Current number of votes in opposition to this proposal\n uint256 againstVotes;\n /// @notice Current number of votes for abstaining for this proposal\n uint256 abstainVotes;\n /// @notice Flag marking whether the proposal has been canceled\n bool canceled;\n /// @notice Flag marking whether the proposal has been executed\n bool executed;\n /// @notice Whether the proposal is an emergency proposal\n bool emergency;\n /// @notice quorum votes requires\n uint256 quorumVotes;\n /// @notice time delay\n uint256 delay;\n}\n\n/// @notice Ballot receipt record for a voter\nstruct Receipt {\n /// @notice Whether or not a vote has been cast\n bool hasVoted;\n /// @notice Whether or not the voter supports the proposal or abstains\n uint8 support;\n /// @notice The number of votes the voter had, which were cast\n uint96 votes;\n}\n\n/// @notice Possible states that a proposal may be in\nenum ProposalState {\n Pending,\n Active,\n Canceled,\n Defeated,\n Succeeded,\n Queued,\n Expired,\n Executed\n}\n" + }, + "contracts/governance/governor/IIpt.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.9;\n\ninterface IIpt {\n function getPriorVotes(address account, uint256 blockNumber) external view returns (uint96);\n}\n" + }, + "contracts/governance/governor/GovernorDelegate.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.9;\npragma experimental ABIEncoderV2;\nimport \"hardhat/console.sol\";\n\nimport \"./IGovernor.sol\";\nimport \"./GovernorStorage.sol\";\n\ncontract GovernorCharlieDelegate is GovernorCharlieDelegateStorage, GovernorCharlieEvents, IGovernorCharlieDelegate {\n /// @notice The name of this contract\n string public constant name = \"Interest Protocol Governor\";\n\n /// @notice The maximum number of actions that can be included in a proposal\n uint256 public constant proposalMaxOperations = 10;\n\n /// @notice The EIP-712 typehash for the contract's domain\n bytes32 public constant DOMAIN_TYPEHASH =\n keccak256(\"EIP712Domain(string name,uint256 chainId,address verifyingContract)\");\n\n /// @notice The EIP-712 typehash for the ballot struct used by the contract\n bytes32 public constant BALLOT_TYPEHASH = keccak256(\"Ballot(uint256 proposalId,uint8 support)\");\n\n /// @notice The time for a proposal to be executed after passing\n uint256 public constant GRACE_PERIOD = 14 days;\n\n /**\n * @notice Used to initialize the contract during delegator contructor\n * @param ipt_ The address of the IPT token\n */\n function initialize(\n address ipt_\n ) external override {\n require(!initialized, \"already been initialized\");\n ipt = IIpt(ipt_);\n votingPeriod = 40320;\n votingDelay = 13140;\n proposalThreshold = 1000000000000000000000000;\n proposalTimelockDelay = 172800;\n proposalCount = 0;\n quorumVotes = 10000000000000000000000000;\n emergencyQuorumVotes = 40000000000000000000000000;\n emergencyVotingPeriod = 6570;\n emergencyTimelockDelay = 43200;\n optimisticQuorumVotes = 2000000000000000000000000;\n optimisticVotingDelay = 18000;\n maxWhitelistPeriod = 31536000;\n\n initialized = true;\n }\n\n /// @notice any function with this modifier will call the pay_interest() function before\n modifier onlyGov() {\n require(_msgSender() == address(this), \"must come from the gov.\");\n _;\n }\n\n /**\n * @notice Function used to propose a new proposal. Sender must have delegates above the proposal threshold\n * @param targets Target addresses for proposal calls\n * @param values Eth values for proposal calls\n * @param signatures Function signatures for proposal calls\n * @param calldatas Calldatas for proposal calls\n * @param description String description of the proposal\n * @return Proposal id of new proposal\n */\n function propose(\n address[] memory targets,\n uint256[] memory values,\n string[] memory signatures,\n bytes[] memory calldatas,\n string memory description,\n bool emergency\n ) public override returns (uint256) {\n // Reject proposals before initiating as Governor\n require(quorumVotes != 0, \"Charlie not active\");\n // Allow addresses above proposal threshold and whitelisted addresses to propose\n require(\n ipt.getPriorVotes(_msgSender(), (block.number - 1)) >= proposalThreshold || isWhitelisted(_msgSender()),\n \"votes below proposal threshold\"\n );\n require(\n targets.length == values.length && targets.length == signatures.length && targets.length == calldatas.length,\n \"information arity mismatch\"\n );\n require(targets.length != 0, \"must provide actions\");\n require(targets.length <= proposalMaxOperations, \"too many actions\");\n\n uint256 latestProposalId = latestProposalIds[_msgSender()];\n if (latestProposalId != 0) {\n ProposalState proposersLatestProposalState = state(latestProposalId);\n require(proposersLatestProposalState != ProposalState.Active, \"one live proposal per proposer\");\n require(proposersLatestProposalState != ProposalState.Pending, \"one live proposal per proposer\");\n }\n\n proposalCount++;\n Proposal memory newProposal = Proposal({\n id: proposalCount,\n proposer: _msgSender(),\n eta: 0,\n targets: targets,\n values: values,\n signatures: signatures,\n calldatas: calldatas,\n startBlock: block.number + votingDelay,\n endBlock: block.number + votingDelay + votingPeriod,\n forVotes: 0,\n againstVotes: 0,\n abstainVotes: 0,\n canceled: false,\n executed: false,\n emergency: emergency,\n quorumVotes: quorumVotes,\n delay: proposalTimelockDelay\n });\n\n //whitelist can't make emergency\n if (emergency && !isWhitelisted(_msgSender())) {\n newProposal.startBlock = block.number;\n newProposal.endBlock = block.number + emergencyVotingPeriod;\n newProposal.quorumVotes = emergencyQuorumVotes;\n newProposal.delay = emergencyTimelockDelay;\n }\n\n //whitelist can only make optimistic proposals\n if (isWhitelisted(_msgSender())) {\n newProposal.quorumVotes = optimisticQuorumVotes;\n newProposal.startBlock = block.number + optimisticVotingDelay;\n newProposal.endBlock = block.number + optimisticVotingDelay + votingPeriod;\n }\n\n proposals[newProposal.id] = newProposal;\n latestProposalIds[newProposal.proposer] = newProposal.id;\n\n emit ProposalCreated(\n newProposal.id,\n _msgSender(),\n targets,\n values,\n signatures,\n calldatas,\n newProposal.startBlock,\n newProposal.endBlock,\n description\n );\n return newProposal.id;\n }\n\n /**\n * @notice Queues a proposal of state succeeded\n * @param proposalId The id of the proposal to queue\n */\n function queue(uint256 proposalId) external override {\n require(state(proposalId) == ProposalState.Succeeded, \"can only be queued if succeeded\");\n Proposal storage proposal = proposals[proposalId];\n uint256 eta = block.timestamp + proposal.delay;\n for (uint256 i = 0; i < proposal.targets.length; i++) {\n require(\n !queuedTransactions[\n keccak256(\n abi.encode(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], eta)\n )\n ],\n \"proposal already queued\"\n );\n queueTransaction(\n proposal.targets[i],\n proposal.values[i],\n proposal.signatures[i],\n proposal.calldatas[i],\n eta,\n proposal.delay\n );\n }\n proposal.eta = eta;\n emit ProposalQueued(proposalId, eta);\n }\n\n function queueTransaction(\n address target,\n uint256 value,\n string memory signature,\n bytes memory data,\n uint256 eta,\n uint256 delay\n ) internal returns (bytes32) {\n require(eta >= (getBlockTimestamp() + delay), \"must satisfy delay.\");\n\n bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));\n queuedTransactions[txHash] = true;\n\n emit QueueTransaction(txHash, target, value, signature, data, eta);\n return txHash;\n }\n\n /**\n * @notice Executes a queued proposal if eta has passed\n * @param proposalId The id of the proposal to execute\n */\n function execute(uint256 proposalId) external payable override {\n require(state(proposalId) == ProposalState.Queued, \"can only be exec'd if queued\");\n Proposal storage proposal = proposals[proposalId];\n proposal.executed = true;\n for (uint256 i = 0; i < proposal.targets.length; i++) {\n this.executeTransaction{value: proposal.values[i]}(\n proposal.targets[i],\n proposal.values[i],\n proposal.signatures[i],\n proposal.calldatas[i],\n proposal.eta\n );\n }\n emit ProposalExecuted(proposalId);\n }\n\n function executeTransaction(\n address target,\n uint256 value,\n string memory signature,\n bytes memory data,\n uint256 eta\n ) external payable override {\n bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));\n require(queuedTransactions[txHash], \"tx hasn't been queued.\");\n require(getBlockTimestamp() >= eta, \"tx hasn't surpassed timelock.\");\n require(getBlockTimestamp() <= eta + GRACE_PERIOD, \"tx is stale.\");\n\n queuedTransactions[txHash] = false;\n\n bytes memory callData;\n\n if (bytes(signature).length == 0) {\n callData = data;\n } else {\n callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);\n }\n\n // solhint-disable-next-line avoid-low-level-calls\n (\n bool success, /*bytes memory returnData*/\n\n ) = target.call{value: value}(callData);\n require(success, \"tx execution reverted.\");\n\n emit ExecuteTransaction(txHash, target, value, signature, data, eta);\n }\n\n /**\n * @notice Cancels a proposal only if sender is the proposer, or proposer delegates dropped below proposal threshold\n * @param proposalId The id of the proposal to cancel\n */\n function cancel(uint256 proposalId) external override {\n require(state(proposalId) != ProposalState.Executed, \"cant cancel executed proposal\");\n\n Proposal storage proposal = proposals[proposalId];\n\n // Proposer can cancel\n if (_msgSender() != proposal.proposer) {\n // Whitelisted proposers can't be canceled for falling below proposal threshold\n if (isWhitelisted(proposal.proposer)) {\n require(\n (ipt.getPriorVotes(proposal.proposer, (block.number - 1)) < proposalThreshold) &&\n _msgSender() == whitelistGuardian,\n \"cancel: whitelisted proposer\"\n );\n } else {\n require(\n (ipt.getPriorVotes(proposal.proposer, (block.number - 1)) < proposalThreshold),\n \"cancel: proposer above threshold\"\n );\n }\n }\n\n proposal.canceled = true;\n for (uint256 i = 0; i < proposal.targets.length; i++) {\n cancelTransaction(\n proposal.targets[i],\n proposal.values[i],\n proposal.signatures[i],\n proposal.calldatas[i],\n proposal.eta\n );\n }\n\n emit ProposalCanceled(proposalId);\n }\n\n function cancelTransaction(\n address target,\n uint256 value,\n string memory signature,\n bytes memory data,\n uint256 eta\n ) internal {\n bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));\n queuedTransactions[txHash] = false;\n\n emit CancelTransaction(txHash, target, value, signature, data, eta);\n }\n\n /**\n * @notice Gets actions of a proposal\n * @param proposalId the id of the proposal\n * @return targets proposal targets\n * @return values proposal values\n * @return signatures proposal signatures\n * @return calldatas proposal calldatae\n */\n function getActions(uint256 proposalId)\n external\n view\n override\n returns (\n address[] memory targets,\n uint256[] memory values,\n string[] memory signatures,\n bytes[] memory calldatas\n )\n {\n Proposal storage p = proposals[proposalId];\n return (p.targets, p.values, p.signatures, p.calldatas);\n }\n\n /**\n * @notice Gets the receipt for a voter on a given proposal\n * @param proposalId the id of proposal\n * @param voter The address of the voter\n * @return The voting receipt\n */\n function getReceipt(uint256 proposalId, address voter) external view override returns (Receipt memory) {\n return proposalReceipts[proposalId][voter];\n }\n\n /**\n * @notice Gets the state of a proposal\n * @param proposalId The id of the proposal\n * @return Proposal state\n */\n // solhint-disable-next-line code-complexity\n function state(uint256 proposalId) public view override returns (ProposalState) {\n require(proposalCount >= proposalId && proposalId > initialProposalId, \"state: invalid proposal id\");\n Proposal storage proposal = proposals[proposalId];\n bool whitelisted = isWhitelisted(proposal.proposer);\n if (proposal.canceled) {\n return ProposalState.Canceled;\n } else if (block.number <= proposal.startBlock) {\n return ProposalState.Pending;\n } else if (block.number <= proposal.endBlock) {\n return ProposalState.Active;\n } else if (\n (whitelisted && proposal.againstVotes > proposal.quorumVotes) ||\n (!whitelisted && proposal.forVotes <= proposal.againstVotes) ||\n (!whitelisted && proposal.forVotes < proposal.quorumVotes)\n ) {\n return ProposalState.Defeated;\n } else if (proposal.eta == 0) {\n return ProposalState.Succeeded;\n } else if (proposal.executed) {\n return ProposalState.Executed;\n } else if (block.timestamp >= (proposal.eta + GRACE_PERIOD)) {\n return ProposalState.Expired;\n }\n return ProposalState.Queued;\n }\n\n /**\n * @notice Cast a vote for a proposal\n * @param proposalId The id of the proposal to vote on\n * @param support The support value for the vote. 0=against, 1=for, 2=abstain\n */\n function castVote(uint256 proposalId, uint8 support) external override {\n emit VoteCast(_msgSender(), proposalId, support, castVoteInternal(_msgSender(), proposalId, support), \"\");\n }\n\n /**\n * @notice Cast a vote for a proposal with a reason\n * @param proposalId The id of the proposal to vote on\n * @param support The support value for the vote. 0=against, 1=for, 2=abstain\n * @param reason The reason given for the vote by the voter\n */\n function castVoteWithReason(\n uint256 proposalId,\n uint8 support,\n string calldata reason\n ) external override {\n emit VoteCast(_msgSender(), proposalId, support, castVoteInternal(_msgSender(), proposalId, support), reason);\n }\n\n /**\n * @notice Cast a vote for a proposal by signature\n * @dev external override function that accepts EIP-712 signatures for voting on proposals.\n */\n function castVoteBySig(\n uint256 proposalId,\n uint8 support,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external override {\n bytes32 domainSeparator = keccak256(\n abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainIdInternal(), address(this))\n );\n bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));\n bytes32 digest = keccak256(abi.encodePacked(\"\\x19\\x01\", domainSeparator, structHash));\n address signatory = ecrecover(digest, v, r, s);\n require(signatory != address(0), \"castVoteBySig: invalid signature\");\n emit VoteCast(signatory, proposalId, support, castVoteInternal(signatory, proposalId, support), \"\");\n }\n\n /**\n * @notice Internal function that caries out voting logic\n * @param voter The voter that is casting their vote\n * @param proposalId The id of the proposal to vote on\n * @param support The support value for the vote. 0=against, 1=for, 2=abstain\n * @return The number of votes cast\n */\n function castVoteInternal(\n address voter,\n uint256 proposalId,\n uint8 support\n ) internal returns (uint96) {\n require(state(proposalId) == ProposalState.Active, \"voting is closed\");\n require(support <= 2, \"invalid vote type\");\n Proposal storage proposal = proposals[proposalId];\n Receipt storage receipt = proposalReceipts[proposalId][voter];\n require(receipt.hasVoted == false, \"voter already voted\");\n uint96 votes = ipt.getPriorVotes(voter, proposal.startBlock);\n\n if (support == 0) {\n proposal.againstVotes = proposal.againstVotes + votes;\n } else if (support == 1) {\n proposal.forVotes = proposal.forVotes + votes;\n } else if (support == 2) {\n proposal.abstainVotes = proposal.abstainVotes + votes;\n }\n\n receipt.hasVoted = true;\n receipt.support = support;\n receipt.votes = votes;\n\n return votes;\n }\n\n /**\n * @notice View function which returns if an account is whitelisted\n * @param account Account to check white list status of\n * @return If the account is whitelisted\n */\n function isWhitelisted(address account) public view override returns (bool) {\n return (whitelistAccountExpirations[account] > block.timestamp);\n }\n\n /**\n * @notice Governance function for setting the governance token\n * @param token_ new token addr\n */\n function _setNewToken(address token_) external onlyGov {\n ipt = IIpt(token_);\n }\n\n /**\n * @notice Used to update the timelock period\n * @param proposalTimelockDelay_ The proposal holding period\n */\n function _setDelay(uint256 proposalTimelockDelay_) public override onlyGov {\n uint256 oldTimelockDelay = proposalTimelockDelay;\n proposalTimelockDelay = proposalTimelockDelay_;\n\n emit NewDelay(oldTimelockDelay, proposalTimelockDelay);\n }\n\n /**\n * @notice Used to update the emergency timelock period\n * @param emergencyTimelockDelay_ The proposal holding period\n */\n function _setEmergencyDelay(uint256 emergencyTimelockDelay_) public override onlyGov {\n uint256 oldEmergencyTimelockDelay = emergencyTimelockDelay;\n emergencyTimelockDelay = emergencyTimelockDelay_;\n\n emit NewEmergencyDelay(oldEmergencyTimelockDelay, emergencyTimelockDelay);\n }\n\n /**\n * @notice Governance function for setting the voting delay\n * @param newVotingDelay new voting delay, in blocks\n */\n function _setVotingDelay(uint256 newVotingDelay) external override onlyGov {\n uint256 oldVotingDelay = votingDelay;\n votingDelay = newVotingDelay;\n\n emit VotingDelaySet(oldVotingDelay, votingDelay);\n }\n\n /**\n * @notice Governance function for setting the voting period\n * @param newVotingPeriod new voting period, in blocks\n */\n function _setVotingPeriod(uint256 newVotingPeriod) external override onlyGov {\n uint256 oldVotingPeriod = votingPeriod;\n votingPeriod = newVotingPeriod;\n\n emit VotingPeriodSet(oldVotingPeriod, votingPeriod);\n }\n\n /**\n * @notice Governance function for setting the emergency voting period\n * @param newEmergencyVotingPeriod new voting period, in blocks\n */\n function _setEmergencyVotingPeriod(uint256 newEmergencyVotingPeriod) external override onlyGov {\n uint256 oldEmergencyVotingPeriod = emergencyVotingPeriod;\n emergencyVotingPeriod = newEmergencyVotingPeriod;\n\n emit EmergencyVotingPeriodSet(oldEmergencyVotingPeriod, emergencyVotingPeriod);\n }\n\n /**\n * @notice Governance function for setting the proposal threshold\n * @param newProposalThreshold new proposal threshold\n */\n function _setProposalThreshold(uint256 newProposalThreshold) external override onlyGov {\n uint256 oldProposalThreshold = proposalThreshold;\n proposalThreshold = newProposalThreshold;\n\n emit ProposalThresholdSet(oldProposalThreshold, proposalThreshold);\n }\n\n /**\n * @notice Governance function for setting the quorum\n * @param newQuorumVotes new proposal quorum\n */\n function _setQuorumVotes(uint256 newQuorumVotes) external override onlyGov {\n uint256 oldQuorumVotes = quorumVotes;\n quorumVotes = newQuorumVotes;\n\n emit NewQuorum(oldQuorumVotes, quorumVotes);\n }\n\n /**\n * @notice Governance function for setting the emergency quorum\n * @param newEmergencyQuorumVotes new proposal quorum\n */\n function _setEmergencyQuorumVotes(uint256 newEmergencyQuorumVotes) external override onlyGov {\n uint256 oldEmergencyQuorumVotes = emergencyQuorumVotes;\n emergencyQuorumVotes = newEmergencyQuorumVotes;\n\n emit NewEmergencyQuorum(oldEmergencyQuorumVotes, emergencyQuorumVotes);\n }\n\n /**\n * @notice Governance function for setting the whitelist expiration as a timestamp\n * for an account. Whitelist status allows accounts to propose without meeting threshold\n * @param account Account address to set whitelist expiration for\n * @param expiration Expiration for account whitelist status as timestamp (if now < expiration, whitelisted)\n */\n function _setWhitelistAccountExpiration(address account, uint256 expiration) external override onlyGov {\n require (expiration < (maxWhitelistPeriod + block.timestamp), \"expiration exceeds max\");\n whitelistAccountExpirations[account] = expiration;\n\n emit WhitelistAccountExpirationSet(account, expiration);\n }\n\n /**\n * @notice Governance function for setting the whitelistGuardian. WhitelistGuardian can cancel proposals from whitelisted addresses\n * @param account Account to set whitelistGuardian to (0x0 to remove whitelistGuardian)\n */\n function _setWhitelistGuardian(address account) external override onlyGov {\n address oldGuardian = whitelistGuardian;\n whitelistGuardian = account;\n\n emit WhitelistGuardianSet(oldGuardian, whitelistGuardian);\n }\n\n /**\n * @notice Governance function for setting the optimistic voting delay\n * @param newOptimisticVotingDelay new optimistic voting delay, in blocks\n */\n function _setOptimisticDelay(uint256 newOptimisticVotingDelay) external override onlyGov {\n uint256 oldOptimisticVotingDelay = optimisticVotingDelay;\n optimisticVotingDelay = newOptimisticVotingDelay;\n\n emit OptimisticVotingDelaySet(oldOptimisticVotingDelay, optimisticVotingDelay);\n }\n\n /**\n * @notice Governance function for setting the optimistic quorum\n * @param newOptimisticQuorumVotes new optimistic quorum votes, in blocks\n */\n function _setOptimisticQuorumVotes(uint256 newOptimisticQuorumVotes) external override onlyGov {\n uint256 oldOptimisticQuorumVotes = optimisticQuorumVotes;\n optimisticQuorumVotes = newOptimisticQuorumVotes;\n\n emit OptimisticQuorumVotesSet(oldOptimisticQuorumVotes, optimisticQuorumVotes);\n }\n\n function getChainIdInternal() internal view returns (uint256) {\n return block.chainid;\n }\n\n function getBlockTimestamp() internal view returns (uint256) {\n // solium-disable-next-line security/no-block-members\n return block.timestamp;\n }\n\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200, + "details": { + "orderLiterals": true, + "deduplicate": true, + "cse": true, + "yul": true + } + }, + "outputSelection": { + "*": { + "*": [ + "evm.bytecode", + "evm.deployedBytecode", + "devdoc", + "userdoc", + "metadata", + "abi" + ] + } + }, + "libraries": {} + } + }, + "ABI": "[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"txHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"signature\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"eta\",\"type\":\"uint256\"}],\"name\":\"CancelTransaction\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldEmergencyVotingPeriod\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"emergencyVotingPeriod\",\"type\":\"uint256\"}],\"name\":\"EmergencyVotingPeriodSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"txHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"signature\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"eta\",\"type\":\"uint256\"}],\"name\":\"ExecuteTransaction\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldAdmin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"NewAdmin\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldTimelockDelay\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"proposalTimelockDelay\",\"type\":\"uint256\"}],\"name\":\"NewDelay\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldEmergencyTimelockDelay\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"emergencyTimelockDelay\",\"type\":\"uint256\"}],\"name\":\"NewEmergencyDelay\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldEmergencyQuorumVotes\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"emergencyQuorumVotes\",\"type\":\"uint256\"}],\"name\":\"NewEmergencyQuorum\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldImplementation\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newImplementation\",\"type\":\"address\"}],\"name\":\"NewImplementation\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldPendingAdmin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newPendingAdmin\",\"type\":\"address\"}],\"name\":\"NewPendingAdmin\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldQuorumVotes\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"quorumVotes\",\"type\":\"uint256\"}],\"name\":\"NewQuorum\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldOptimisticQuorumVotes\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"optimisticQuorumVotes\",\"type\":\"uint256\"}],\"name\":\"OptimisticQuorumVotesSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldOptimisticVotingDelay\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"optimisticVotingDelay\",\"type\":\"uint256\"}],\"name\":\"OptimisticVotingDelaySet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"ProposalCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"proposer\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"},{\"indexed\":false,\"internalType\":\"string[]\",\"name\":\"signatures\",\"type\":\"string[]\"},{\"indexed\":false,\"internalType\":\"bytes[]\",\"name\":\"calldatas\",\"type\":\"bytes[]\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"startBlock\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"endBlock\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"description\",\"type\":\"string\"}],\"name\":\"ProposalCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"ProposalExecuted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"eta\",\"type\":\"uint256\"}],\"name\":\"ProposalQueued\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldProposalThreshold\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newProposalThreshold\",\"type\":\"uint256\"}],\"name\":\"ProposalThresholdSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"txHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"signature\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"eta\",\"type\":\"uint256\"}],\"name\":\"QueueTransaction\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"voter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"support\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"votes\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"reason\",\"type\":\"string\"}],\"name\":\"VoteCast\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldVotingDelay\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newVotingDelay\",\"type\":\"uint256\"}],\"name\":\"VotingDelaySet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldVotingPeriod\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newVotingPeriod\",\"type\":\"uint256\"}],\"name\":\"VotingPeriodSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"expiration\",\"type\":\"uint256\"}],\"name\":\"WhitelistAccountExpirationSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldGuardian\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newGuardian\",\"type\":\"address\"}],\"name\":\"WhitelistGuardianSet\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"BALLOT_TYPEHASH\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"DOMAIN_TYPEHASH\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"GRACE_PERIOD\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalTimelockDelay_\",\"type\":\"uint256\"}],\"name\":\"_setDelay\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"emergencyTimelockDelay_\",\"type\":\"uint256\"}],\"name\":\"_setEmergencyDelay\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newEmergencyQuorumVotes\",\"type\":\"uint256\"}],\"name\":\"_setEmergencyQuorumVotes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newEmergencyVotingPeriod\",\"type\":\"uint256\"}],\"name\":\"_setEmergencyVotingPeriod\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token_\",\"type\":\"address\"}],\"name\":\"_setNewToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newOptimisticVotingDelay\",\"type\":\"uint256\"}],\"name\":\"_setOptimisticDelay\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newOptimisticQuorumVotes\",\"type\":\"uint256\"}],\"name\":\"_setOptimisticQuorumVotes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newProposalThreshold\",\"type\":\"uint256\"}],\"name\":\"_setProposalThreshold\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newQuorumVotes\",\"type\":\"uint256\"}],\"name\":\"_setQuorumVotes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newVotingDelay\",\"type\":\"uint256\"}],\"name\":\"_setVotingDelay\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newVotingPeriod\",\"type\":\"uint256\"}],\"name\":\"_setVotingPeriod\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"expiration\",\"type\":\"uint256\"}],\"name\":\"_setWhitelistAccountExpiration\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"_setWhitelistGuardian\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"}],\"name\":\"cancel\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"support\",\"type\":\"uint8\"}],\"name\":\"castVote\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"support\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"castVoteBySig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"support\",\"type\":\"uint8\"},{\"internalType\":\"string\",\"name\":\"reason\",\"type\":\"string\"}],\"name\":\"castVoteWithReason\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"emergencyQuorumVotes\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"emergencyTimelockDelay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"emergencyVotingPeriod\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"}],\"name\":\"execute\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"signature\",\"type\":\"string\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"eta\",\"type\":\"uint256\"}],\"name\":\"executeTransaction\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"}],\"name\":\"getActions\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"},{\"internalType\":\"string[]\",\"name\":\"signatures\",\"type\":\"string[]\"},{\"internalType\":\"bytes[]\",\"name\":\"calldatas\",\"type\":\"bytes[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"voter\",\"type\":\"address\"}],\"name\":\"getReceipt\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"hasVoted\",\"type\":\"bool\"},{\"internalType\":\"uint8\",\"name\":\"support\",\"type\":\"uint8\"},{\"internalType\":\"uint96\",\"name\":\"votes\",\"type\":\"uint96\"}],\"internalType\":\"struct Receipt\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"implementation\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"initialProposalId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"ipt_\",\"type\":\"address\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"initialized\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"ipt\",\"outputs\":[{\"internalType\":\"contract IIpt\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"isWhitelisted\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"latestProposalIds\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"maxWhitelistPeriod\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"optimisticQuorumVotes\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"optimisticVotingDelay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"proposalCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"proposalMaxOperations\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"proposalReceipts\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"hasVoted\",\"type\":\"bool\"},{\"internalType\":\"uint8\",\"name\":\"support\",\"type\":\"uint8\"},{\"internalType\":\"uint96\",\"name\":\"votes\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"proposalThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"proposalTimelockDelay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"proposals\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"proposer\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"eta\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"startBlock\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"endBlock\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"forVotes\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"againstVotes\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"abstainVotes\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"canceled\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"executed\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"emergency\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"quorumVotes\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"delay\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"},{\"internalType\":\"string[]\",\"name\":\"signatures\",\"type\":\"string[]\"},{\"internalType\":\"bytes[]\",\"name\":\"calldatas\",\"type\":\"bytes[]\"},{\"internalType\":\"string\",\"name\":\"description\",\"type\":\"string\"},{\"internalType\":\"bool\",\"name\":\"emergency\",\"type\":\"bool\"}],\"name\":\"propose\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"}],\"name\":\"queue\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"queuedTransactions\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"quorumVotes\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"}],\"name\":\"state\",\"outputs\":[{\"internalType\":\"enum ProposalState\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"votingDelay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"votingPeriod\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"whitelistAccountExpirations\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"whitelistGuardian\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + "ContractName": "GovernorCharlieDelegate", + "CompilerVersion": "v0.8.9+commit.e5eed63a", + "OptimizationUsed": 1, + "Runs": 200, + "ConstructorArguments": "0x", + "EVMVersion": "Default", + "Library": "", + "LicenseType": "", + "Proxy": 0, + "SwarmSource": "" + } +] \ No newline at end of file diff --git a/testdata/etherscan/0x9AB6b21cDF116f611110b048987E58894786C244/creation_data.json b/testdata/etherscan/0x9AB6b21cDF116f611110b048987E58894786C244/creation_data.json new file mode 100644 index 000000000..d145e56aa --- /dev/null +++ b/testdata/etherscan/0x9AB6b21cDF116f611110b048987E58894786C244/creation_data.json @@ -0,0 +1,5 @@ +{ + "contractAddress": "0x9ab6b21cdf116f611110b048987e58894786c244", + "contractCreator": "0x603d50bad151da8becf405e51a8c4abc8ba1c95e", + "txHash": "0x72be611ae1ade09242d9fc9c950a73d076f6c23514564a7b9ac730400dbaf2c0" +} \ No newline at end of file diff --git a/testdata/etherscan/0x9AB6b21cDF116f611110b048987E58894786C244/metadata.json b/testdata/etherscan/0x9AB6b21cDF116f611110b048987E58894786C244/metadata.json new file mode 100644 index 000000000..ab133f38f --- /dev/null +++ b/testdata/etherscan/0x9AB6b21cDF116f611110b048987E58894786C244/metadata.json @@ -0,0 +1,69 @@ +[ + { + "SourceCode": { + "language": "Solidity", + "sources": { + "contracts/InterestRates/InterestRatePositionManager.f.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n}\n\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)\n\n/**\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n */\ninterface IERC20Permit {\n /**\n * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\n * given ``owner``'s signed approval.\n *\n * IMPORTANT: The same issues {IERC20-approve} has related to transaction\n * ordering also apply here.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `deadline` must be a timestamp in the future.\n * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\n * over the EIP712-formatted function arguments.\n * - the signature must use ``owner``'s current nonce (see {nonces}).\n *\n * For more information on the signature format, see the\n * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\n * section].\n */\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n /**\n * @dev Returns the current nonce for `owner`. This value must be\n * included whenever a signature is generated for {permit}.\n *\n * Every successful call to {permit} increases ``owner``'s nonce by one. This\n * prevents a signature from being used multiple times.\n */\n function nonces(address owner) external view returns (uint256);\n\n /**\n * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\n */\n // solhint-disable-next-line func-name-mixedcase\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n\n/// Parameters for ERC20Permit.permit call\nstruct ERC20PermitSignature {\n IERC20Permit token;\n uint256 value;\n uint256 deadline;\n uint8 v;\n bytes32 r;\n bytes32 s;\n}\n\nlibrary PermitHelper {\n function applyPermit(\n ERC20PermitSignature calldata p,\n address owner,\n address spender\n ) internal {\n p.token.permit(owner, spender, p.value, p.deadline, p.v, p.r, p.s);\n }\n\n function applyPermits(\n ERC20PermitSignature[] calldata permits,\n address owner,\n address spender\n ) internal {\n for (uint256 i = 0; i < permits.length; i++) {\n applyPermit(permits[i], owner, spender);\n }\n }\n}\n\n// OpenZeppelin Contracts v4.4.1 (interfaces/IERC3156FlashLender.sol)\n\n// OpenZeppelin Contracts (last updated v4.7.0) (interfaces/IERC3156FlashBorrower.sol)\n\n/**\n * @dev Interface of the ERC3156 FlashBorrower, as defined in\n * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].\n *\n * _Available since v4.1._\n */\ninterface IERC3156FlashBorrower {\n /**\n * @dev Receive a flash loan.\n * @param initiator The initiator of the loan.\n * @param token The loan currency.\n * @param amount The amount of tokens lent.\n * @param fee The additional amount of tokens to repay.\n * @param data Arbitrary data structure, intended to contain user-defined parameters.\n * @return The keccak256 hash of \"IERC3156FlashBorrower.onFlashLoan\"\n */\n function onFlashLoan(\n address initiator,\n address token,\n uint256 amount,\n uint256 fee,\n bytes calldata data\n ) external returns (bytes32);\n}\n\n/**\n * @dev Interface of the ERC3156 FlashLender, as defined in\n * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].\n *\n * _Available since v4.1._\n */\ninterface IERC3156FlashLender {\n /**\n * @dev The amount of currency available to be lended.\n * @param token The loan currency.\n * @return The amount of `token` that can be borrowed.\n */\n function maxFlashLoan(address token) external view returns (uint256);\n\n /**\n * @dev The fee to be charged for a given loan.\n * @param token The loan currency.\n * @param amount The amount of tokens lent.\n * @return The amount of `token` to be charged for the loan, on top of the returned principal.\n */\n function flashFee(address token, uint256 amount) external view returns (uint256);\n\n /**\n * @dev Initiate a flash loan.\n * @param receiver The receiver of the tokens in the loan, and the receiver of the callback.\n * @param token The loan currency.\n * @param amount The amount of tokens lent.\n * @param data Arbitrary data structure, intended to contain user-defined parameters.\n */\n function flashLoan(\n IERC3156FlashBorrower receiver,\n address token,\n uint256 amount,\n bytes calldata data\n ) external returns (bool);\n}\n\n/// @dev Interface to be used by contracts that collect fees. Contains fee recipient that can be changed by owner.\ninterface IFeeCollector {\n // --- Events ---\n\n /// @dev Fee Recipient is changed to @param feeRecipient address.\n /// @param feeRecipient New fee recipient address.\n event FeeRecipientChanged(address feeRecipient);\n\n // --- Errors ---\n\n /// @dev Invalid fee recipient.\n error InvalidFeeRecipient();\n\n // --- Functions ---\n\n /// @return Address of the current fee recipient.\n function feeRecipient() external view returns (address);\n\n /// @dev Sets new fee recipient address\n /// @param newFeeRecipient Address of the new fee recipient.\n function setFeeRecipient(address newFeeRecipient) external;\n}\n\ninterface IPositionManagerDependent {\n // --- Errors ---\n\n /// @dev Position Manager cannot be zero.\n error PositionManagerCannotBeZero();\n\n /// @dev Caller is not Position Manager.\n error CallerIsNotPositionManager(address caller);\n\n // --- Functions ---\n\n /// @dev Returns address of the PositionManager contract.\n function positionManager() external view returns (address);\n}\n\n/// @dev Interface of R stablecoin token. Implements some standards like IERC20, IERC20Permit, and IERC3156FlashLender.\n/// Raft's specific implementation contains IFeeCollector and IPositionManagerDependent.\n/// PositionManager can mint and burn R when particular actions happen with user's position.\ninterface IRToken is IERC20, IERC20Permit, IERC3156FlashLender, IFeeCollector, IPositionManagerDependent {\n // --- Events ---\n\n /// @dev New R token is deployed\n /// @param positionManager Address of the PositionManager contract that is authorized to mint and burn new tokens.\n /// @param flashMintFeeRecipient Address of flash mint fee recipient.\n event RDeployed(address positionManager, address flashMintFeeRecipient);\n\n /// @dev The Flash Mint Fee Percentage has been changed.\n /// @param flashMintFeePercentage The new Flash Mint Fee Percentage value.\n event FlashMintFeePercentageChanged(uint256 flashMintFeePercentage);\n\n /// --- Errors ---\n\n /// @dev Proposed flash mint fee percentage is too big.\n /// @param feePercentage Proposed flash mint fee percentage.\n error FlashFeePercentageTooBig(uint256 feePercentage);\n\n // --- Functions ---\n\n /// @return Number representing 100 percentage.\n function PERCENTAGE_BASE() external view returns (uint256);\n\n /// @dev Mints new tokens. Callable only by PositionManager contract.\n /// @param to Address that will receive newly minted tokens.\n /// @param amount Amount of tokens to mint.\n function mint(address to, uint256 amount) external;\n\n /// @dev Mints new tokens. Callable only by PositionManager contract.\n /// @param from Address of user whose tokens are burnt.\n /// @param amount Amount of tokens to burn.\n function burn(address from, uint256 amount) external;\n\n /// @return Maximum flash mint fee percentage that can be set by owner.\n function MAX_FLASH_MINT_FEE_PERCENTAGE() external view returns (uint256);\n\n /// @return Current flash mint fee percentage.\n function flashMintFeePercentage() external view returns (uint256);\n\n /// @dev Sets new flash mint fee percentage. Callable only by owner.\n /// @notice The proposed flash mint fee percentage cannot exceed `MAX_FLASH_MINT_FEE_PERCENTAGE`.\n /// @param feePercentage New flash fee percentage.\n function setFlashMintFeePercentage(uint256 feePercentage) external;\n}\n\ninterface IPriceOracle {\n // --- Errors ---\n\n /// @dev Contract initialized with an invalid deviation parameter.\n error InvalidDeviation();\n\n // --- Types ---\n\n struct PriceOracleResponse {\n bool isBrokenOrFrozen;\n bool priceChangeAboveMax;\n uint256 price;\n }\n\n // --- Functions ---\n\n /// @dev Return price oracle response which consists the following information: oracle is broken or frozen, the\n /// price change between two rounds is more than max, and the price.\n function getPriceOracleResponse() external returns (PriceOracleResponse memory);\n\n /// @dev Maximum time period allowed since oracle latest round data timestamp, beyond which oracle is considered\n /// frozen.\n function timeout() external view returns (uint256);\n\n /// @dev Used to convert a price answer to an 18-digit precision uint.\n function TARGET_DIGITS() external view returns (uint256);\n\n /// @dev price deviation for the oracle in percentage.\n function DEVIATION() external view returns (uint256);\n}\n\ninterface IPriceFeed {\n // --- Events ---\n\n /// @dev Last good price has been updated.\n event LastGoodPriceUpdated(uint256 lastGoodPrice);\n\n /// @dev Price difference between oracles has been updated.\n /// @param priceDifferenceBetweenOracles New price difference between oracles.\n event PriceDifferenceBetweenOraclesUpdated(uint256 priceDifferenceBetweenOracles);\n\n /// @dev Primary oracle has been updated.\n /// @param primaryOracle New primary oracle.\n event PrimaryOracleUpdated(IPriceOracle primaryOracle);\n\n /// @dev Secondary oracle has been updated.\n /// @param secondaryOracle New secondary oracle.\n event SecondaryOracleUpdated(IPriceOracle secondaryOracle);\n\n // --- Errors ---\n\n /// @dev Invalid primary oracle.\n error InvalidPrimaryOracle();\n\n /// @dev Invalid secondary oracle.\n error InvalidSecondaryOracle();\n\n /// @dev Primary oracle is broken or frozen or has bad result.\n error PrimaryOracleBrokenOrFrozenOrBadResult();\n\n /// @dev Invalid price difference between oracles.\n error InvalidPriceDifferenceBetweenOracles();\n\n // --- Functions ---\n\n /// @dev Return primary oracle address.\n function primaryOracle() external returns (IPriceOracle);\n\n /// @dev Return secondary oracle address\n function secondaryOracle() external returns (IPriceOracle);\n\n /// @dev The last good price seen from an oracle by Raft.\n function lastGoodPrice() external returns (uint256);\n\n /// @dev The maximum relative price difference between two oracle responses.\n function priceDifferenceBetweenOracles() external returns (uint256);\n\n /// @dev Set primary oracle address.\n /// @param newPrimaryOracle Primary oracle address.\n function setPrimaryOracle(IPriceOracle newPrimaryOracle) external;\n\n /// @dev Set secondary oracle address.\n /// @param newSecondaryOracle Secondary oracle address.\n function setSecondaryOracle(IPriceOracle newSecondaryOracle) external;\n\n /// @dev Set the maximum relative price difference between two oracle responses.\n /// @param newPriceDifferenceBetweenOracles The maximum relative price difference between two oracle responses.\n function setPriceDifferenceBetweenOracles(uint256 newPriceDifferenceBetweenOracles) external;\n\n /// @dev Returns the latest price obtained from the Oracle. Called by Raft functions that require a current price.\n ///\n /// Also callable by anyone externally.\n /// Non-view function - it stores the last good price seen by Raft.\n ///\n /// Uses a primary oracle and a fallback oracle in case primary fails. If both fail,\n /// it uses the last good price seen by Raft.\n ///\n /// @return currentPrice Returned price.\n /// @return deviation Deviation of the reported price in percentage.\n /// @notice Actual returned price is in range `currentPrice` +/- `currentPrice * deviation / ONE`\n function fetchPrice() external returns (uint256 currentPrice, uint256 deviation);\n}\n\ninterface IERC20Indexable is IERC20, IPositionManagerDependent {\n // --- Events ---\n\n /// @dev New token is deployed.\n /// @param positionManager Address of the PositionManager contract that is authorized to mint and burn new tokens.\n event ERC20IndexableDeployed(address positionManager);\n\n /// @dev New index has been set.\n /// @param newIndex Value of the new index.\n event IndexUpdated(uint256 newIndex);\n\n // --- Errors ---\n\n /// @dev Unsupported action for ERC20Indexable contract.\n error NotSupported();\n\n // --- Functions ---\n\n /// @return Precision for token index. Represents index that is equal to 1.\n function INDEX_PRECISION() external view returns (uint256);\n\n /// @return Current index value.\n function currentIndex() external view returns (uint256);\n\n /// @dev Sets new token index. Callable only by PositionManager contract.\n /// @param backingAmount Amount of backing token that is covered by total supply.\n function setIndex(uint256 backingAmount) external;\n\n /// @dev Mints new tokens. Callable only by PositionManager contract.\n /// @param to Address that will receive newly minted tokens.\n /// @param amount Amount of tokens to mint.\n function mint(address to, uint256 amount) external;\n\n /// @dev Mints new tokens. Callable only by PositionManager contract.\n /// @param from Address of user whose tokens are burnt.\n /// @param amount Amount of tokens to burn.\n function burn(address from, uint256 amount) external;\n}\n\ninterface ISplitLiquidationCollateral {\n // --- Functions ---\n\n /// @dev Returns lowest total debt that will be split.\n function LOW_TOTAL_DEBT() external view returns (uint256);\n\n /// @dev Minimum collateralization ratio for position\n function MCR() external view returns (uint256);\n\n /// @dev Splits collateral between protocol and liquidator.\n /// @param totalCollateral Amount of collateral to split.\n /// @param totalDebt Amount of debt to split.\n /// @param price Price of collateral.\n /// @param isRedistribution True if this is a redistribution.\n /// @return collateralToSendToProtocol Amount of collateral to send to protocol.\n /// @return collateralToSentToLiquidator Amount of collateral to send to liquidator.\n function split(\n uint256 totalCollateral,\n uint256 totalDebt,\n uint256 price,\n bool isRedistribution\n )\n external\n view\n returns (uint256 collateralToSendToProtocol, uint256 collateralToSentToLiquidator);\n}\n\n/// @dev Common interface for the Position Manager.\ninterface IPositionManager is IFeeCollector {\n // --- Types ---\n\n /// @dev Information for a Raft indexable collateral token.\n /// @param collateralToken The Raft indexable collateral token.\n /// @param debtToken Corresponding Raft indexable debt token.\n /// @param priceFeed The contract that provides a price for the collateral token.\n /// @param splitLiquidation The contract that calculates collateral split in case of liquidation.\n /// @param isEnabled Whether the token can be used as collateral or not.\n /// @param lastFeeOperationTime Timestamp of the last operation for the collateral token.\n /// @param borrowingSpread The current borrowing spread.\n /// @param baseRate The current base rate.\n /// @param redemptionSpread The current redemption spread.\n /// @param redemptionRebate Percentage of the redemption fee returned to redeemed positions.\n struct CollateralTokenInfo {\n IERC20Indexable collateralToken;\n IERC20Indexable debtToken;\n IPriceFeed priceFeed;\n ISplitLiquidationCollateral splitLiquidation;\n bool isEnabled;\n uint256 lastFeeOperationTime;\n uint256 borrowingSpread;\n uint256 baseRate;\n uint256 redemptionSpread;\n uint256 redemptionRebate;\n }\n\n // --- Events ---\n\n /// @dev New position manager has been token deployed.\n /// @param rToken The R token used by the position manager.\n /// @param feeRecipient The address of fee recipient.\n event PositionManagerDeployed(IRToken rToken, address feeRecipient);\n\n /// @dev New collateral token has been added added to the system.\n /// @param collateralToken The token used as collateral.\n /// @param raftCollateralToken The Raft indexable collateral token for the given collateral token.\n /// @param raftDebtToken The Raft indexable debt token for given collateral token.\n /// @param priceFeed The contract that provides price for the collateral token.\n event CollateralTokenAdded(\n IERC20 collateralToken,\n IERC20Indexable raftCollateralToken,\n IERC20Indexable raftDebtToken,\n IPriceFeed priceFeed\n );\n\n /// @dev Collateral token has been enabled or disabled.\n /// @param collateralToken The token used as collateral.\n /// @param isEnabled True if the token is enabled, false otherwise.\n event CollateralTokenModified(IERC20 collateralToken, bool isEnabled);\n\n /// @dev A delegate has been whitelisted for a certain position.\n /// @param position The position for which the delegate was whitelisted.\n /// @param delegate The delegate which was whitelisted.\n /// @param whitelisted Specifies whether the delegate whitelisting has been enabled (true) or disabled (false).\n event DelegateWhitelisted(address indexed position, address indexed delegate, bool whitelisted);\n\n /// @dev New position has been created.\n /// @param position The address of the user opening new position.\n /// @param collateralToken The token used as collateral for the created position.\n event PositionCreated(address indexed position, IERC20 indexed collateralToken);\n\n /// @dev The position has been closed by either repayment, liquidation, or redemption.\n /// @param position The address of the user whose position is closed.\n event PositionClosed(address indexed position);\n\n /// @dev Collateral amount for the position has been changed.\n /// @param position The address of the user that has opened the position.\n /// @param collateralToken The address of the collateral token being added to position.\n /// @param collateralAmount The amount of collateral added or removed.\n /// @param isCollateralIncrease Whether the collateral is added to the position or removed from it.\n event CollateralChanged(\n address indexed position, IERC20 indexed collateralToken, uint256 collateralAmount, bool isCollateralIncrease\n );\n\n /// @dev Debt amount for position has been changed.\n /// @param position The address of the user that has opened the position.\n /// @param collateralToken The address of the collateral token backing the debt.\n /// @param debtAmount The amount of debt added or removed.\n /// @param isDebtIncrease Whether the debt is added to the position or removed from it.\n event DebtChanged(\n address indexed position, IERC20 indexed collateralToken, uint256 debtAmount, bool isDebtIncrease\n );\n\n /// @dev Borrowing fee has been paid. Emitted only if the actual fee was paid - doesn't happen with no fees are\n /// paid.\n /// @param collateralToken Collateral token used to mint R.\n /// @param position The address of position's owner that triggered the fee payment.\n /// @param feeAmount The amount of tokens paid as the borrowing fee.\n event RBorrowingFeePaid(IERC20 collateralToken, address indexed position, uint256 feeAmount);\n\n /// @dev Liquidation has been executed.\n /// @param liquidator The liquidator that executed the liquidation.\n /// @param position The address of position's owner whose position was liquidated.\n /// @param collateralToken The collateral token used for the liquidation.\n /// @param debtLiquidated The total debt that was liquidated or redistributed.\n /// @param collateralLiquidated The total collateral liquidated.\n /// @param collateralSentToLiquidator The collateral amount sent to the liquidator.\n /// @param collateralLiquidationFeePaid The total collateral paid as the liquidation fee to the fee recipient.\n /// @param isRedistribution Whether the executed liquidation was redistribution or not.\n event Liquidation(\n address indexed liquidator,\n address indexed position,\n IERC20 indexed collateralToken,\n uint256 debtLiquidated,\n uint256 collateralLiquidated,\n uint256 collateralSentToLiquidator,\n uint256 collateralLiquidationFeePaid,\n bool isRedistribution\n );\n\n /// @dev Redemption has been executed.\n /// @param redeemer User that redeemed R.\n /// @param amount Amount of R that was redeemed.\n /// @param collateralSent The amount of collateral sent to the redeemer.\n /// @param fee The amount of fee paid to the fee recipient.\n /// @param rebate Redemption rebate amount.\n event Redemption(address indexed redeemer, uint256 amount, uint256 collateralSent, uint256 fee, uint256 rebate);\n\n /// @dev Borrowing spread has been updated.\n /// @param borrowingSpread The new borrowing spread.\n event BorrowingSpreadUpdated(uint256 borrowingSpread);\n\n /// @dev Redemption rebate has been updated.\n /// @param redemptionRebate The new redemption rebate.\n event RedemptionRebateUpdated(uint256 redemptionRebate);\n\n /// @dev Redemption spread has been updated.\n /// @param collateralToken Collateral token that the spread was set for.\n /// @param redemptionSpread The new redemption spread.\n event RedemptionSpreadUpdated(IERC20 collateralToken, uint256 redemptionSpread);\n\n /// @dev Base rate has been updated.\n /// @param collateralToken Collateral token that the baser rate was updated for.\n /// @param baseRate The new base rate.\n event BaseRateUpdated(IERC20 collateralToken, uint256 baseRate);\n\n /// @dev Last fee operation time has been updated.\n /// @param collateralToken Collateral token that the baser rate was updated for.\n /// @param lastFeeOpTime The new operation time.\n event LastFeeOpTimeUpdated(IERC20 collateralToken, uint256 lastFeeOpTime);\n\n /// @dev Split liquidation collateral has been changed.\n /// @param collateralToken Collateral token whose split liquidation collateral contract is set.\n /// @param newSplitLiquidationCollateral New value that was set to be split liquidation collateral.\n event SplitLiquidationCollateralChanged(\n IERC20 collateralToken, ISplitLiquidationCollateral indexed newSplitLiquidationCollateral\n );\n\n // --- Errors ---\n\n /// @dev Max fee percentage must be between borrowing spread and 100%.\n error InvalidMaxFeePercentage();\n\n /// @dev Max fee percentage must be between 0.5% and 100%.\n error MaxFeePercentageOutOfRange();\n\n /// @dev Amount is zero.\n error AmountIsZero();\n\n /// @dev Nothing to liquidate.\n error NothingToLiquidate();\n\n /// @dev Cannot liquidate last position.\n error CannotLiquidateLastPosition();\n\n /// @dev Cannot redeem collateral below minimum debt threshold.\n /// @param collateralToken Collateral token used to redeem.\n /// @param newTotalDebt New total debt backed by collateral, which is lower than minimum debt.\n error TotalDebtCannotBeLowerThanMinDebt(IERC20 collateralToken, uint256 newTotalDebt);\n\n /// @dev Cannot redeem collateral\n /// @param collateralToken Collateral token used to redeem.\n /// @param newTotalCollateral New total collateral, which is lower than minimum collateral.\n /// @param minimumCollateral Minimum collateral required to complete redeem\n error TotalCollateralCannotBeLowerThanMinCollateral(\n IERC20 collateralToken, uint256 newTotalCollateral, uint256 minimumCollateral\n );\n\n /// @dev Fee would eat up all returned collateral.\n error FeeEatsUpAllReturnedCollateral();\n\n /// @dev Borrowing spread exceeds maximum.\n error BorrowingSpreadExceedsMaximum();\n\n /// @dev Redemption rebate exceeds maximum.\n error RedemptionRebateExceedsMaximum();\n\n /// @dev Redemption spread is out of allowed range.\n error RedemptionSpreadOutOfRange();\n\n /// @dev There must be either a collateral change or a debt change.\n error NoCollateralOrDebtChange();\n\n /// @dev There is some collateral for position that doesn't have debt.\n error InvalidPosition();\n\n /// @dev An operation that would result in ICR < MCR is not permitted.\n /// @param newICR Resulting ICR that is below MCR.\n error NewICRLowerThanMCR(uint256 newICR);\n\n /// @dev Position's net debt must be greater than minimum.\n /// @param netDebt Net debt amount that is below minimum.\n error NetDebtBelowMinimum(uint256 netDebt);\n\n /// @dev The provided delegate address is invalid.\n error InvalidDelegateAddress();\n\n /// @dev A non-whitelisted delegate cannot adjust positions.\n error DelegateNotWhitelisted();\n\n /// @dev Fee exceeded provided maximum fee percentage.\n /// @param fee The fee amount.\n /// @param amount The amount of debt or collateral.\n /// @param maxFeePercentage The maximum fee percentage.\n error FeeExceedsMaxFee(uint256 fee, uint256 amount, uint256 maxFeePercentage);\n\n /// @dev Borrower uses a different collateral token already.\n error PositionCollateralTokenMismatch();\n\n /// @dev Collateral token address cannot be zero.\n error CollateralTokenAddressCannotBeZero();\n\n /// @dev Price feed address cannot be zero.\n error PriceFeedAddressCannotBeZero();\n\n /// @dev Collateral token already added.\n error CollateralTokenAlreadyAdded();\n\n /// @dev Collateral token is not added.\n error CollateralTokenNotAdded();\n\n /// @dev Collateral token is not enabled.\n error CollateralTokenDisabled();\n\n /// @dev Split liquidation collateral cannot be zero.\n error SplitLiquidationCollateralCannotBeZero();\n\n /// @dev Cannot change collateral in case of repaying the whole debt.\n error WrongCollateralParamsForFullRepayment();\n\n // --- Functions ---\n\n /// @return The R token used by position manager.\n function rToken() external view returns (IRToken);\n\n /// @dev Retrieves information about certain collateral type.\n /// @param collateralToken The token used as collateral.\n /// @return raftCollateralToken The Raft indexable collateral token.\n /// @return raftDebtToken The Raft indexable debt token.\n /// @return priceFeed The contract that provides a price for the collateral token.\n /// @return splitLiquidation The contract that calculates collateral split in case of liquidation.\n /// @return isEnabled Whether the collateral token can be used as collateral or not.\n /// @return lastFeeOperationTime Timestamp of the last operation for the collateral token.\n /// @return borrowingSpread The current borrowing spread.\n /// @return baseRate The current base rate.\n /// @return redemptionSpread The current redemption spread.\n /// @return redemptionRebate Percentage of the redemption fee returned to redeemed positions.\n function collateralInfo(IERC20 collateralToken)\n external\n view\n returns (\n IERC20Indexable raftCollateralToken,\n IERC20Indexable raftDebtToken,\n IPriceFeed priceFeed,\n ISplitLiquidationCollateral splitLiquidation,\n bool isEnabled,\n uint256 lastFeeOperationTime,\n uint256 borrowingSpread,\n uint256 baseRate,\n uint256 redemptionSpread,\n uint256 redemptionRebate\n );\n\n /// @param collateralToken Collateral token whose raft collateral indexable token is being queried.\n /// @return Raft collateral token address for given collateral token.\n function raftCollateralToken(IERC20 collateralToken) external view returns (IERC20Indexable);\n\n /// @param collateralToken Collateral token whose raft collateral indexable token is being queried.\n /// @return Raft debt token address for given collateral token.\n function raftDebtToken(IERC20 collateralToken) external view returns (IERC20Indexable);\n\n /// @param collateralToken Collateral token whose price feed contract is being queried.\n /// @return Price feed contract address for given collateral token.\n function priceFeed(IERC20 collateralToken) external view returns (IPriceFeed);\n\n /// @param collateralToken Collateral token whose split liquidation collateral is being queried.\n /// @return Returns address of the split liquidation collateral contract.\n function splitLiquidationCollateral(IERC20 collateralToken) external view returns (ISplitLiquidationCollateral);\n\n /// @param collateralToken Collateral token whose split liquidation collateral is being queried.\n /// @return Returns whether collateral is enabled or nor.\n function collateralEnabled(IERC20 collateralToken) external view returns (bool);\n\n /// @param collateralToken Collateral token we query last operation time fee for.\n /// @return The timestamp of the latest fee operation (redemption or new R issuance).\n function lastFeeOperationTime(IERC20 collateralToken) external view returns (uint256);\n\n /// @param collateralToken Collateral token we query borrowing spread for.\n /// @return The current borrowing spread.\n function borrowingSpread(IERC20 collateralToken) external view returns (uint256);\n\n /// @param collateralToken Collateral token we query base rate for.\n /// @return rate The base rate.\n function baseRate(IERC20 collateralToken) external view returns (uint256 rate);\n\n /// @param collateralToken Collateral token we query redemption spread for.\n /// @return The current redemption spread for collateral token.\n function redemptionSpread(IERC20 collateralToken) external view returns (uint256);\n\n /// @param collateralToken Collateral token we query redemption rebate for.\n /// @return rebate Percentage of the redemption fee returned to redeemed positions.\n function redemptionRebate(IERC20 collateralToken) external view returns (uint256);\n\n /// @param collateralToken Collateral token we query redemption rate for.\n /// @return rate The current redemption rate for collateral token.\n function getRedemptionRate(IERC20 collateralToken) external view returns (uint256 rate);\n\n /// @dev Returns the collateral token that a given position used for their position.\n /// @param position The address of the borrower.\n /// @return collateralToken The collateral token of the borrower's position.\n function collateralTokenForPosition(address position) external view returns (IERC20 collateralToken);\n\n /// @dev Adds a new collateral token to the protocol.\n /// @param collateralToken The new collateral token.\n /// @param priceFeed The price feed for the collateral token.\n /// @param newSplitLiquidationCollateral split liquidation collateral contract address.\n function addCollateralToken(\n IERC20 collateralToken,\n IPriceFeed priceFeed,\n ISplitLiquidationCollateral newSplitLiquidationCollateral\n )\n external;\n\n /// @dev Adds a new collateral token to the protocol.\n /// @param collateralToken The new collateral token.\n /// @param priceFeed The price feed for the collateral token.\n /// @param newSplitLiquidationCollateral split liquidation collateral contract address.\n /// @param raftCollateralToken_ Address of raft collateral token.\n /// @param raftDebtToken_ Address of raft debt token.\n function addCollateralToken(\n IERC20 collateralToken,\n IPriceFeed priceFeed,\n ISplitLiquidationCollateral newSplitLiquidationCollateral,\n IERC20Indexable raftCollateralToken_,\n IERC20Indexable raftDebtToken_\n )\n external;\n\n /// @dev Enables or disables a collateral token. Reverts if the collateral token has not been added.\n /// @param collateralToken The collateral token.\n /// @param isEnabled Whether the collateral token can be used as collateral or not.\n function setCollateralEnabled(IERC20 collateralToken, bool isEnabled) external;\n\n /// @dev Sets the new split liquidation collateral contract.\n /// @param collateralToken Collateral token whose split liquidation collateral is being set.\n /// @param newSplitLiquidationCollateral New split liquidation collateral contract address.\n function setSplitLiquidationCollateral(\n IERC20 collateralToken,\n ISplitLiquidationCollateral newSplitLiquidationCollateral\n )\n external;\n\n /// @dev Liquidates the borrower if its position's ICR is lower than the minimum collateral ratio.\n /// @param position The address of the borrower.\n function liquidate(address position) external;\n\n /// @dev Redeems the collateral token for a given debt amount. It sends @param debtAmount R to the system and\n /// redeems the corresponding amount of collateral from as many positions as are needed to fill the redemption\n /// request.\n /// @param collateralToken The token used as collateral.\n /// @param debtAmount The amount of debt to be redeemed. Must be greater than zero.\n /// @param maxFeePercentage The maximum fee percentage to pay for the redemption.\n function redeemCollateral(IERC20 collateralToken, uint256 debtAmount, uint256 maxFeePercentage) external;\n\n /// @dev Manages the position on behalf of a given borrower.\n /// @param collateralToken The token the borrower used as collateral.\n /// @param position The address of the borrower.\n /// @param collateralChange The amount of collateral to add or remove.\n /// @param isCollateralIncrease True if the collateral is being increased, false otherwise.\n /// @param debtChange The amount of R to add or remove. In case of repayment (isDebtIncrease = false)\n /// `type(uint256).max` value can be used to repay the whole outstanding loan.\n /// @param isDebtIncrease True if the debt is being increased, false otherwise.\n /// @param maxFeePercentage The maximum fee percentage to pay for the position management.\n /// @param permitSignature Optional permit signature for tokens that support IERC20Permit interface.\n /// @notice `permitSignature` it is ignored if permit signature is not for `collateralToken`.\n /// @notice In case of full debt repayment, `isCollateralIncrease` is ignored and `collateralChange` must be 0.\n /// These values are set to `false`(collateral decrease), and the whole collateral balance of the user.\n /// @return actualCollateralChange Actual amount of collateral added/removed.\n /// Can be different to `collateralChange` in case of full repayment.\n /// @return actualDebtChange Actual amount of debt added/removed.\n /// Can be different to `debtChange` in case of passing type(uint256).max as `debtChange`.\n function managePosition(\n IERC20 collateralToken,\n address position,\n uint256 collateralChange,\n bool isCollateralIncrease,\n uint256 debtChange,\n bool isDebtIncrease,\n uint256 maxFeePercentage,\n ERC20PermitSignature calldata permitSignature\n )\n external\n returns (uint256 actualCollateralChange, uint256 actualDebtChange);\n\n /// @return The max borrowing spread.\n function MAX_BORROWING_SPREAD() external view returns (uint256);\n\n /// @return The max borrowing rate.\n function MAX_BORROWING_RATE() external view returns (uint256);\n\n /// @dev Sets the new borrowing spread.\n /// @param collateralToken Collateral token we set borrowing spread for.\n /// @param newBorrowingSpread New borrowing spread to be used.\n function setBorrowingSpread(IERC20 collateralToken, uint256 newBorrowingSpread) external;\n\n /// @param collateralToken Collateral token we query borrowing rate for.\n /// @return The current borrowing rate.\n function getBorrowingRate(IERC20 collateralToken) external view returns (uint256);\n\n /// @param collateralToken Collateral token we query borrowing rate with decay for.\n /// @return The current borrowing rate with decay.\n function getBorrowingRateWithDecay(IERC20 collateralToken) external view returns (uint256);\n\n /// @dev Returns the borrowing fee for a given debt amount.\n /// @param collateralToken Collateral token we query borrowing fee for.\n /// @param debtAmount The amount of debt.\n /// @return The borrowing fee.\n function getBorrowingFee(IERC20 collateralToken, uint256 debtAmount) external view returns (uint256);\n\n /// @dev Sets the new redemption spread.\n /// @param newRedemptionSpread New redemption spread to be used.\n function setRedemptionSpread(IERC20 collateralToken, uint256 newRedemptionSpread) external;\n\n /// @dev Sets new redemption rebate percentage.\n /// @param newRedemptionRebate Value that is being set as a redemption rebate percentage.\n function setRedemptionRebate(IERC20 collateralToken, uint256 newRedemptionRebate) external;\n\n /// @param collateralToken Collateral token we query redemption rate with decay for.\n /// @return The current redemption rate with decay.\n function getRedemptionRateWithDecay(IERC20 collateralToken) external view returns (uint256);\n\n /// @dev Returns the redemption fee for a given collateral amount.\n /// @param collateralToken Collateral token we query redemption fee for.\n /// @param collateralAmount The amount of collateral.\n /// @param priceDeviation Deviation for the reported price by oracle in percentage.\n /// @return The redemption fee.\n function getRedemptionFee(\n IERC20 collateralToken,\n uint256 collateralAmount,\n uint256 priceDeviation\n )\n external\n view\n returns (uint256);\n\n /// @dev Returns the redemption fee with decay for a given collateral amount.\n /// @param collateralToken Collateral token we query redemption fee with decay for.\n /// @param collateralAmount The amount of collateral.\n /// @return The redemption fee with decay.\n function getRedemptionFeeWithDecay(\n IERC20 collateralToken,\n uint256 collateralAmount\n )\n external\n view\n returns (uint256);\n\n /// @return Half-life of 12h (720 min).\n /// @dev (1/2) = d^720 => d = (1/2)^(1/720)\n function MINUTE_DECAY_FACTOR() external view returns (uint256);\n\n /// @dev Returns if a given delegate is whitelisted for a given borrower.\n /// @param position The address of the borrower.\n /// @param delegate The address of the delegate.\n /// @return isWhitelisted True if the delegate is whitelisted for a given borrower, false otherwise.\n function isDelegateWhitelisted(address position, address delegate) external view returns (bool isWhitelisted);\n\n /// @dev Whitelists a delegate.\n /// @param delegate The address of the delegate.\n /// @param whitelisted True if delegate is being whitelisted, false otherwise.\n function whitelistDelegate(address delegate, bool whitelisted) external;\n\n /// @return Parameter by which to divide the redeemed fraction, in order to calc the new base rate from a\n /// redemption. Corresponds to (1 / ALPHA) in the white paper.\n function BETA() external view returns (uint256);\n}\n\n/// @dev Common interface for the Position Manager.\ninterface IInterestRatePositionManager is IPositionManager {\n // --- Events ---\n\n /// @dev Fees coming from accrued interest are minted.\n /// @param collateralToken Collateral token that fees are paid for.\n /// @param amount Amount of R minted.\n event MintedFees(IERC20 collateralToken, uint256 amount);\n\n // --- Errors ---\n\n /// @dev Only registered debt token can be caller.\n /// @param sender Actual caller.\n error InvalidDebtToken(address sender);\n\n // --- Functions ---\n\n /// @dev Mints fees coming from accrued interest. Can be called only from matching debt token.\n /// @param collateralToken Collateral token to mint fees for.\n /// @param amount Amount of R to mint.\n function mintFees(IERC20 collateralToken, uint256 amount) external;\n}\n\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n enum Rounding {\n Down, // Toward negative infinity\n Up, // Toward infinity\n Zero // Toward zero\n }\n\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a > b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a == 0 ? 0 : (a - 1) / b + 1;\n }\n\n /**\n * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\n * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)\n * with further edits by Uniswap Labs also under MIT license.\n */\n function mulDiv(\n uint256 x,\n uint256 y,\n uint256 denominator\n ) internal pure returns (uint256 result) {\n unchecked {\n // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use\n // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n // variables such that product = prod1 * 2^256 + prod0.\n uint256 prod0; // Least significant 256 bits of the product\n uint256 prod1; // Most significant 256 bits of the product\n assembly {\n let mm := mulmod(x, y, not(0))\n prod0 := mul(x, y)\n prod1 := sub(sub(mm, prod0), lt(mm, prod0))\n }\n\n // Handle non-overflow cases, 256 by 256 division.\n if (prod1 == 0) {\n return prod0 / denominator;\n }\n\n // Make sure the result is less than 2^256. Also prevents denominator == 0.\n require(denominator > prod1);\n\n ///////////////////////////////////////////////\n // 512 by 256 division.\n ///////////////////////////////////////////////\n\n // Make division exact by subtracting the remainder from [prod1 prod0].\n uint256 remainder;\n assembly {\n // Compute remainder using mulmod.\n remainder := mulmod(x, y, denominator)\n\n // Subtract 256 bit number from 512 bit number.\n prod1 := sub(prod1, gt(remainder, prod0))\n prod0 := sub(prod0, remainder)\n }\n\n // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.\n // See https://cs.stackexchange.com/q/138556/92363.\n\n // Does not overflow because the denominator cannot be zero at this stage in the function.\n uint256 twos = denominator & (~denominator + 1);\n assembly {\n // Divide denominator by twos.\n denominator := div(denominator, twos)\n\n // Divide [prod1 prod0] by twos.\n prod0 := div(prod0, twos)\n\n // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.\n twos := add(div(sub(0, twos), twos), 1)\n }\n\n // Shift in bits from prod1 into prod0.\n prod0 |= prod1 * twos;\n\n // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such\n // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for\n // four bits. That is, denominator * inv = 1 mod 2^4.\n uint256 inverse = (3 * denominator) ^ 2;\n\n // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works\n // in modular arithmetic, doubling the correct bits in each step.\n inverse *= 2 - denominator * inverse; // inverse mod 2^8\n inverse *= 2 - denominator * inverse; // inverse mod 2^16\n inverse *= 2 - denominator * inverse; // inverse mod 2^32\n inverse *= 2 - denominator * inverse; // inverse mod 2^64\n inverse *= 2 - denominator * inverse; // inverse mod 2^128\n inverse *= 2 - denominator * inverse; // inverse mod 2^256\n\n // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is\n // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1\n // is no longer required.\n result = prod0 * inverse;\n return result;\n }\n }\n\n /**\n * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.\n */\n function mulDiv(\n uint256 x,\n uint256 y,\n uint256 denominator,\n Rounding rounding\n ) internal pure returns (uint256) {\n uint256 result = mulDiv(x, y, denominator);\n if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {\n result += 1;\n }\n return result;\n }\n\n /**\n * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.\n *\n * Inspired by Henry S. Warren, Jr.'s \"Hacker's Delight\" (Chapter 11).\n */\n function sqrt(uint256 a) internal pure returns (uint256) {\n if (a == 0) {\n return 0;\n }\n\n // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.\n //\n // We know that the \"msb\" (most significant bit) of our target number `a` is a power of 2 such that we have\n // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.\n //\n // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`\n // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`\n // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`\n //\n // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.\n uint256 result = 1 << (log2(a) >> 1);\n\n // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,\n // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at\n // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision\n // into the expected uint128 result.\n unchecked {\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n return min(result, a / result);\n }\n }\n\n /**\n * @notice Calculates sqrt(a), following the selected rounding direction.\n */\n function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = sqrt(a);\n return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);\n }\n }\n\n /**\n * @dev Return the log in base 2, rounded down, of a positive value.\n * Returns 0 if given 0.\n */\n function log2(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >> 128 > 0) {\n value >>= 128;\n result += 128;\n }\n if (value >> 64 > 0) {\n value >>= 64;\n result += 64;\n }\n if (value >> 32 > 0) {\n value >>= 32;\n result += 32;\n }\n if (value >> 16 > 0) {\n value >>= 16;\n result += 16;\n }\n if (value >> 8 > 0) {\n value >>= 8;\n result += 8;\n }\n if (value >> 4 > 0) {\n value >>= 4;\n result += 4;\n }\n if (value >> 2 > 0) {\n value >>= 2;\n result += 2;\n }\n if (value >> 1 > 0) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log2(value);\n return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);\n }\n }\n\n /**\n * @dev Return the log in base 10, rounded down, of a positive value.\n * Returns 0 if given 0.\n */\n function log10(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >= 10**64) {\n value /= 10**64;\n result += 64;\n }\n if (value >= 10**32) {\n value /= 10**32;\n result += 32;\n }\n if (value >= 10**16) {\n value /= 10**16;\n result += 16;\n }\n if (value >= 10**8) {\n value /= 10**8;\n result += 8;\n }\n if (value >= 10**4) {\n value /= 10**4;\n result += 4;\n }\n if (value >= 10**2) {\n value /= 10**2;\n result += 2;\n }\n if (value >= 10**1) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log10(value);\n return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);\n }\n }\n\n /**\n * @dev Return the log in base 256, rounded down, of a positive value.\n * Returns 0 if given 0.\n *\n * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n */\n function log256(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >> 128 > 0) {\n value >>= 128;\n result += 16;\n }\n if (value >> 64 > 0) {\n value >>= 64;\n result += 8;\n }\n if (value >> 32 > 0) {\n value >>= 32;\n result += 4;\n }\n if (value >> 16 > 0) {\n value >>= 16;\n result += 2;\n }\n if (value >> 8 > 0) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log256(value);\n return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);\n }\n }\n}\n\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20Metadata is IERC20 {\n /**\n * @dev Returns the name of the token.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the symbol of the token.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the decimals places of the token.\n */\n function decimals() external view returns (uint8);\n}\n\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)\n\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\n * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\n *\n * _Available since v4.8._\n */\n function verifyCallResultFromTarget(\n address target,\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n if (success) {\n if (returndata.length == 0) {\n // only check isContract if the call was successful and the return data is empty\n // otherwise we already know that it was a contract\n require(isContract(target), \"Address: call to non-contract\");\n }\n return returndata;\n } else {\n _revert(returndata, errorMessage);\n }\n }\n\n /**\n * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason or using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n _revert(returndata, errorMessage);\n }\n }\n\n function _revert(bytes memory returndata, string memory errorMessage) private pure {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n /// @solidity memory-safe-assembly\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n}\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n function safePermit(\n IERC20Permit token,\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal {\n uint256 nonceBefore = token.nonces(owner);\n token.permit(owner, spender, value, deadline, v, r, s);\n uint256 nonceAfter = token.nonces(owner);\n require(nonceAfter == nonceBefore + 1, \"SafeERC20: permit did not succeed\");\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n\nlibrary Fixed256x18 {\n uint256 internal constant ONE = 1e18; // 18 decimal places\n\n function mulDown(uint256 a, uint256 b) internal pure returns (uint256) {\n return (a * b) / ONE;\n }\n\n function mulUp(uint256 a, uint256 b) internal pure returns (uint256) {\n uint256 product = a * b;\n\n if (product == 0) {\n return 0;\n } else {\n return ((product - 1) / ONE) + 1;\n }\n }\n\n function divDown(uint256 a, uint256 b) internal pure returns (uint256) {\n return (a * ONE) / b;\n }\n\n function divUp(uint256 a, uint256 b) internal pure returns (uint256) {\n if (a == 0) {\n return 0;\n } else {\n return (((a * ONE) - 1) / b) + 1;\n }\n }\n\n function complement(uint256 x) internal pure returns (uint256) {\n return (x < ONE) ? (ONE - x) : 0;\n }\n}\n\nlibrary MathUtils {\n // --- Constants ---\n\n /// @notice Represents 100%.\n /// @dev 1e18 is the scaling factor (100% == 1e18).\n uint256 public constant _100_PERCENT = Fixed256x18.ONE;\n\n /// @notice Precision for Nominal ICR (independent of price).\n /// @dev Rationale for the value:\n /// - Making it “too high” could lead to overflows.\n /// - Making it “too low” could lead to an ICR equal to zero, due to truncation from floor division.\n ///\n /// This value of 1e20 is chosen for safety: the NICR will only overflow for numerator > ~1e39 collateralToken,\n /// and will only truncate to 0 if the denominator is at least 1e20 times greater than the numerator.\n uint256 internal constant _NICR_PRECISION = 1e20;\n\n /// @notice Number of minutes in 1000 years.\n uint256 internal constant _MINUTES_IN_1000_YEARS = 1000 * 365 days / 1 minutes;\n\n // --- Functions ---\n\n /// @notice Multiplies two decimal numbers and use normal rounding rules:\n /// - round product up if 19'th mantissa digit >= 5\n /// - round product down if 19'th mantissa digit < 5.\n /// @param x First number.\n /// @param y Second number.\n function _decMul(uint256 x, uint256 y) internal pure returns (uint256 decProd) {\n decProd = (x * y + Fixed256x18.ONE / 2) / Fixed256x18.ONE;\n }\n\n /// @notice Exponentiation function for 18-digit decimal base, and integer exponent n.\n ///\n /// @dev Uses the efficient \"exponentiation by squaring\" algorithm. O(log(n)) complexity. The exponent is capped to\n /// avoid reverting due to overflow.\n ///\n /// If a period of > 1000 years is ever used as an exponent in either of the above functions, the result will be\n /// negligibly different from just passing the cap, since the decayed base rate will be 0 for 1000 years or > 1000\n /// years.\n /// @param base The decimal base.\n /// @param exponent The exponent.\n /// @return The result of the exponentiation.\n function _decPow(uint256 base, uint256 exponent) internal pure returns (uint256) {\n if (exponent == 0) {\n return Fixed256x18.ONE;\n }\n\n uint256 y = Fixed256x18.ONE;\n uint256 x = base;\n uint256 n = Math.min(exponent, _MINUTES_IN_1000_YEARS); // cap to avoid overflow\n\n // Exponentiation-by-squaring\n while (n > 1) {\n if (n % 2 != 0) {\n y = _decMul(x, y);\n }\n x = _decMul(x, x);\n n /= 2;\n }\n\n return _decMul(x, y);\n }\n\n /// @notice Computes the Nominal Individual Collateral Ratio (NICR) for given collateral and debt. If debt is zero,\n /// it returns the maximal value for uint256 (represents \"infinite\" CR).\n /// @param collateral Collateral amount.\n /// @param debt Debt amount.\n /// @return NICR.\n function _computeNominalCR(uint256 collateral, uint256 debt) internal pure returns (uint256) {\n return debt > 0 ? collateral * _NICR_PRECISION / debt : type(uint256).max;\n }\n\n /// @notice Computes the Collateral Ratio for given collateral, debt and price. If debt is zero, it returns the\n /// maximal value for uint256 (represents \"infinite\" CR).\n /// @param collateral Collateral amount.\n /// @param debt Debt amount.\n /// @param price Collateral price.\n /// @return Collateral ratio.\n function _computeCR(uint256 collateral, uint256 debt, uint256 price) internal pure returns (uint256) {\n return debt > 0 ? collateral * price / debt : type(uint256).max;\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol)\n\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\n * instead returning `false` on failure. This behavior is nonetheless\n * conventional and does not conflict with the expectations of ERC20\n * applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20 is Context, IERC20, IERC20Metadata {\n mapping(address => uint256) private _balances;\n\n mapping(address => mapping(address => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n string private _name;\n string private _symbol;\n\n /**\n * @dev Sets the values for {name} and {symbol}.\n *\n * The default value of {decimals} is 18. To select a different value for\n * {decimals} you should overload it.\n *\n * All two of these values are immutable: they can only be set once during\n * construction.\n */\n constructor(string memory name_, string memory symbol_) {\n _name = name_;\n _symbol = symbol_;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5.05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei. This is the value {ERC20} uses, unless this function is\n * overridden;\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view virtual override returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view virtual override returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `to` cannot be the zero address.\n * - the caller must have a balance of at least `amount`.\n */\n function transfer(address to, uint256 amount) public virtual override returns (bool) {\n address owner = _msgSender();\n _transfer(owner, to, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on\n * `transferFrom`. This is semantically equivalent to an infinite approval.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\n address owner = _msgSender();\n _approve(owner, spender, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20}.\n *\n * NOTE: Does not update the allowance if the current allowance\n * is the maximum `uint256`.\n *\n * Requirements:\n *\n * - `from` and `to` cannot be the zero address.\n * - `from` must have a balance of at least `amount`.\n * - the caller must have allowance for ``from``'s tokens of at least\n * `amount`.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) public virtual override returns (bool) {\n address spender = _msgSender();\n _spendAllowance(from, spender, amount);\n _transfer(from, to, amount);\n return true;\n }\n\n /**\n * @dev Atomically increases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n address owner = _msgSender();\n _approve(owner, spender, allowance(owner, spender) + addedValue);\n return true;\n }\n\n /**\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `spender` must have allowance for the caller of at least\n * `subtractedValue`.\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n address owner = _msgSender();\n uint256 currentAllowance = allowance(owner, spender);\n require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\n unchecked {\n _approve(owner, spender, currentAllowance - subtractedValue);\n }\n\n return true;\n }\n\n /**\n * @dev Moves `amount` of tokens from `from` to `to`.\n *\n * This internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `from` must have a balance of at least `amount`.\n */\n function _transfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {\n require(from != address(0), \"ERC20: transfer from the zero address\");\n require(to != address(0), \"ERC20: transfer to the zero address\");\n\n _beforeTokenTransfer(from, to, amount);\n\n uint256 fromBalance = _balances[from];\n require(fromBalance >= amount, \"ERC20: transfer amount exceeds balance\");\n unchecked {\n _balances[from] = fromBalance - amount;\n // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by\n // decrementing then incrementing.\n _balances[to] += amount;\n }\n\n emit Transfer(from, to, amount);\n\n _afterTokenTransfer(from, to, amount);\n }\n\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n */\n function _mint(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: mint to the zero address\");\n\n _beforeTokenTransfer(address(0), account, amount);\n\n _totalSupply += amount;\n unchecked {\n // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.\n _balances[account] += amount;\n }\n emit Transfer(address(0), account, amount);\n\n _afterTokenTransfer(address(0), account, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`, reducing the\n * total supply.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n */\n function _burn(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: burn from the zero address\");\n\n _beforeTokenTransfer(account, address(0), amount);\n\n uint256 accountBalance = _balances[account];\n require(accountBalance >= amount, \"ERC20: burn amount exceeds balance\");\n unchecked {\n _balances[account] = accountBalance - amount;\n // Overflow not possible: amount <= accountBalance <= totalSupply.\n _totalSupply -= amount;\n }\n\n emit Transfer(account, address(0), amount);\n\n _afterTokenTransfer(account, address(0), amount);\n }\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n *\n * This internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n */\n function _approve(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n require(owner != address(0), \"ERC20: approve from the zero address\");\n require(spender != address(0), \"ERC20: approve to the zero address\");\n\n _allowances[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n /**\n * @dev Updates `owner` s allowance for `spender` based on spent `amount`.\n *\n * Does not update the allowance amount in case of infinite allowance.\n * Revert if not enough allowance is available.\n *\n * Might emit an {Approval} event.\n */\n function _spendAllowance(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n uint256 currentAllowance = allowance(owner, spender);\n if (currentAllowance != type(uint256).max) {\n require(currentAllowance >= amount, \"ERC20: insufficient allowance\");\n unchecked {\n _approve(owner, spender, currentAllowance - amount);\n }\n }\n }\n\n /**\n * @dev Hook that is called before any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * will be transferred to `to`.\n * - when `from` is zero, `amount` tokens will be minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n\n /**\n * @dev Hook that is called after any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * has been transferred to `to`.\n * - when `from` is zero, `amount` tokens have been minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _afterTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n}\n\n// OpenZeppelin Contracts (last updated v4.8.0) (access/Ownable2Step.sol)\n\n// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n _checkOwner();\n _;\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if the sender is not the owner.\n */\n function _checkOwner() internal view virtual {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n\n/**\n * @dev Contract module which provides access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership} and {acceptOwnership}.\n *\n * This module is used through inheritance. It will make available all functions\n * from parent (Ownable).\n */\nabstract contract Ownable2Step is Ownable {\n address private _pendingOwner;\n\n event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Returns the address of the pending owner.\n */\n function pendingOwner() public view virtual returns (address) {\n return _pendingOwner;\n }\n\n /**\n * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual override onlyOwner {\n _pendingOwner = newOwner;\n emit OwnershipTransferStarted(owner(), newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual override {\n delete _pendingOwner;\n super._transferOwnership(newOwner);\n }\n\n /**\n * @dev The new owner accepts the ownership transfer.\n */\n function acceptOwnership() external {\n address sender = _msgSender();\n require(pendingOwner() == sender, \"Ownable2Step: caller is not the new owner\");\n _transferOwnership(sender);\n }\n}\n\n/**\n * @dev Extension of {ERC20} that adds a cap to the supply of tokens.\n */\nabstract contract ERC20Capped is ERC20, Ownable2Step {\n uint256 public cap;\n\n /**\n * @dev Total supply cap has been exceeded.\n */\n error ERC20ExceededCap();\n\n /**\n * @dev The supplied cap is not a valid cap.\n */\n error ERC20InvalidCap(uint256 cap);\n\n constructor(uint256 cap_) {\n setCap(cap_);\n }\n\n /**\n * @dev Sets the value of the `cap`.\n */\n function setCap(uint256 cap_) public onlyOwner {\n if (cap_ == 0) {\n revert ERC20InvalidCap(0);\n }\n cap = cap_;\n }\n\n /**\n * @dev See {ERC20-_mint}.\n */\n function _mint(address account, uint256 amount) internal virtual override {\n if (totalSupply() + amount > cap) {\n revert ERC20ExceededCap();\n }\n super._mint(account, amount);\n }\n}\n\nabstract contract PositionManagerDependent is IPositionManagerDependent {\n // --- Immutable variables ---\n\n address public immutable override positionManager;\n\n // --- Modifiers ---\n\n modifier onlyPositionManager() {\n if (msg.sender != positionManager) {\n revert CallerIsNotPositionManager(msg.sender);\n }\n _;\n }\n\n // --- Constructor ---\n\n constructor(address positionManager_) {\n if (positionManager_ == address(0)) {\n revert PositionManagerCannotBeZero();\n }\n positionManager = positionManager_;\n }\n}\n\ncontract ERC20Indexable is IERC20Indexable, ERC20Capped, PositionManagerDependent {\n // --- Types ---\n\n using Fixed256x18 for uint256;\n\n // --- Constants ---\n\n uint256 public constant override INDEX_PRECISION = Fixed256x18.ONE;\n\n // --- Variables ---\n\n uint256 internal storedIndex;\n\n // --- Constructor ---\n\n constructor(\n address positionManager_,\n string memory name_,\n string memory symbol_,\n uint256 cap_\n )\n ERC20(name_, symbol_)\n ERC20Capped(cap_)\n PositionManagerDependent(positionManager_)\n {\n storedIndex = INDEX_PRECISION;\n emit ERC20IndexableDeployed(positionManager_);\n }\n\n // --- Functions ---\n\n function mint(address to, uint256 amount) public virtual override onlyPositionManager {\n _mint(to, amount.divUp(storedIndex));\n }\n\n function burn(address from, uint256 amount) public virtual override onlyPositionManager {\n _burn(from, amount == type(uint256).max ? ERC20.balanceOf(from) : amount.divUp(storedIndex));\n }\n\n function setIndex(uint256 backingAmount) external override onlyPositionManager {\n uint256 supply = ERC20.totalSupply();\n uint256 newIndex = (backingAmount == 0 && supply == 0) ? INDEX_PRECISION : backingAmount.divUp(supply);\n storedIndex = newIndex;\n emit IndexUpdated(newIndex);\n }\n\n function currentIndex() public view virtual override returns (uint256) {\n return storedIndex;\n }\n\n function totalSupply() public view virtual override(IERC20, ERC20) returns (uint256) {\n return ERC20.totalSupply().mulDown(currentIndex());\n }\n\n function balanceOf(address account) public view virtual override(IERC20, ERC20) returns (uint256) {\n return ERC20.balanceOf(account).mulDown(currentIndex());\n }\n\n function transfer(address, uint256) public virtual override(IERC20, ERC20) returns (bool) {\n revert NotSupported();\n }\n\n function allowance(address, address) public view virtual override(IERC20, ERC20) returns (uint256) {\n revert NotSupported();\n }\n\n function approve(address, uint256) public virtual override(IERC20, ERC20) returns (bool) {\n revert NotSupported();\n }\n\n function transferFrom(address, address, uint256) public virtual override(IERC20, ERC20) returns (bool) {\n revert NotSupported();\n }\n\n function increaseAllowance(address, uint256) public virtual override returns (bool) {\n revert NotSupported();\n }\n\n function decreaseAllowance(address, uint256) public virtual override returns (bool) {\n revert NotSupported();\n }\n}\n\nabstract contract FeeCollector is Ownable2Step, IFeeCollector {\n // --- Variables ---\n\n address public override feeRecipient;\n\n // --- Constructor ---\n\n /// @param feeRecipient_ Address of the fee recipient to initialize contract with.\n constructor(address feeRecipient_) {\n if (feeRecipient_ == address(0)) {\n revert InvalidFeeRecipient();\n }\n\n feeRecipient = feeRecipient_;\n }\n\n // --- Functions ---\n\n function setFeeRecipient(address newFeeRecipient) external onlyOwner {\n if (newFeeRecipient == address(0)) {\n revert InvalidFeeRecipient();\n }\n\n feeRecipient = newFeeRecipient;\n emit FeeRecipientChanged(newFeeRecipient);\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol)\n\n/**\n * @dev Contract module that helps prevent reentrant calls to a function.\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * Note that because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n */\nabstract contract ReentrancyGuard {\n // Booleans are more expensive than uint256 or any type that takes up a full\n // word because each write operation emits an extra SLOAD to first read the\n // slot's contents, replace the bits taken up by the boolean, and then write\n // back. This is the compiler's defense against contract upgrades and\n // pointer aliasing, and it cannot be disabled.\n\n // The values being non-zero value makes deployment a bit more expensive,\n // but in exchange the refund on every call to nonReentrant will be lower in\n // amount. Since refunds are capped to a percentage of the total\n // transaction's gas, it is best to keep them low in cases like this one, to\n // increase the likelihood of the full refund coming into effect.\n uint256 private constant _NOT_ENTERED = 1;\n uint256 private constant _ENTERED = 2;\n\n uint256 private _status;\n\n constructor() {\n _status = _NOT_ENTERED;\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and making it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n _nonReentrantBefore();\n _;\n _nonReentrantAfter();\n }\n\n function _nonReentrantBefore() private {\n // On the first call to nonReentrant, _status will be _NOT_ENTERED\n require(_status != _ENTERED, \"ReentrancyGuard: reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n _status = _ENTERED;\n }\n\n function _nonReentrantAfter() private {\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n _status = _NOT_ENTERED;\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/extensions/draft-ERC20Permit.sol)\n\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol)\n\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n bytes16 private constant _SYMBOLS = \"0123456789abcdef\";\n uint8 private constant _ADDRESS_LENGTH = 20;\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n */\n function toString(uint256 value) internal pure returns (string memory) {\n unchecked {\n uint256 length = Math.log10(value) + 1;\n string memory buffer = new string(length);\n uint256 ptr;\n /// @solidity memory-safe-assembly\n assembly {\n ptr := add(buffer, add(32, length))\n }\n while (true) {\n ptr--;\n /// @solidity memory-safe-assembly\n assembly {\n mstore8(ptr, byte(mod(value, 10), _SYMBOLS))\n }\n value /= 10;\n if (value == 0) break;\n }\n return buffer;\n }\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n */\n function toHexString(uint256 value) internal pure returns (string memory) {\n unchecked {\n return toHexString(value, Math.log256(value) + 1);\n }\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n */\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n bytes memory buffer = new bytes(2 * length + 2);\n buffer[0] = \"0\";\n buffer[1] = \"x\";\n for (uint256 i = 2 * length + 1; i > 1; --i) {\n buffer[i] = _SYMBOLS[value & 0xf];\n value >>= 4;\n }\n require(value == 0, \"Strings: hex length insufficient\");\n return string(buffer);\n }\n\n /**\n * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.\n */\n function toHexString(address addr) internal pure returns (string memory) {\n return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);\n }\n}\n\n/**\n * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.\n *\n * These functions can be used to verify that a message was signed by the holder\n * of the private keys of a given address.\n */\nlibrary ECDSA {\n enum RecoverError {\n NoError,\n InvalidSignature,\n InvalidSignatureLength,\n InvalidSignatureS,\n InvalidSignatureV // Deprecated in v4.8\n }\n\n function _throwError(RecoverError error) private pure {\n if (error == RecoverError.NoError) {\n return; // no error: do nothing\n } else if (error == RecoverError.InvalidSignature) {\n revert(\"ECDSA: invalid signature\");\n } else if (error == RecoverError.InvalidSignatureLength) {\n revert(\"ECDSA: invalid signature length\");\n } else if (error == RecoverError.InvalidSignatureS) {\n revert(\"ECDSA: invalid signature 's' value\");\n }\n }\n\n /**\n * @dev Returns the address that signed a hashed message (`hash`) with\n * `signature` or error string. This address can then be used for verification purposes.\n *\n * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:\n * this function rejects them by requiring the `s` value to be in the lower\n * half order, and the `v` value to be either 27 or 28.\n *\n * IMPORTANT: `hash` _must_ be the result of a hash operation for the\n * verification to be secure: it is possible to craft signatures that\n * recover to arbitrary addresses for non-hashed data. A safe way to ensure\n * this is by receiving a hash of the original message (which may otherwise\n * be too long), and then calling {toEthSignedMessageHash} on it.\n *\n * Documentation for signature generation:\n * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]\n * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]\n *\n * _Available since v4.3._\n */\n function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {\n if (signature.length == 65) {\n bytes32 r;\n bytes32 s;\n uint8 v;\n // ecrecover takes the signature parameters, and the only way to get them\n // currently is to use assembly.\n /// @solidity memory-safe-assembly\n assembly {\n r := mload(add(signature, 0x20))\n s := mload(add(signature, 0x40))\n v := byte(0, mload(add(signature, 0x60)))\n }\n return tryRecover(hash, v, r, s);\n } else {\n return (address(0), RecoverError.InvalidSignatureLength);\n }\n }\n\n /**\n * @dev Returns the address that signed a hashed message (`hash`) with\n * `signature`. This address can then be used for verification purposes.\n *\n * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:\n * this function rejects them by requiring the `s` value to be in the lower\n * half order, and the `v` value to be either 27 or 28.\n *\n * IMPORTANT: `hash` _must_ be the result of a hash operation for the\n * verification to be secure: it is possible to craft signatures that\n * recover to arbitrary addresses for non-hashed data. A safe way to ensure\n * this is by receiving a hash of the original message (which may otherwise\n * be too long), and then calling {toEthSignedMessageHash} on it.\n */\n function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, signature);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.\n *\n * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]\n *\n * _Available since v4.3._\n */\n function tryRecover(\n bytes32 hash,\n bytes32 r,\n bytes32 vs\n ) internal pure returns (address, RecoverError) {\n bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);\n uint8 v = uint8((uint256(vs) >> 255) + 27);\n return tryRecover(hash, v, r, s);\n }\n\n /**\n * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.\n *\n * _Available since v4.2._\n */\n function recover(\n bytes32 hash,\n bytes32 r,\n bytes32 vs\n ) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, r, vs);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Overload of {ECDSA-tryRecover} that receives the `v`,\n * `r` and `s` signature fields separately.\n *\n * _Available since v4.3._\n */\n function tryRecover(\n bytes32 hash,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal pure returns (address, RecoverError) {\n // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature\n // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines\n // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most\n // signatures from current libraries generate a unique signature with an s-value in the lower half order.\n //\n // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value\n // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or\n // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept\n // these malleable signatures as well.\n if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {\n return (address(0), RecoverError.InvalidSignatureS);\n }\n\n // If the signature is valid (and not malleable), return the signer address\n address signer = ecrecover(hash, v, r, s);\n if (signer == address(0)) {\n return (address(0), RecoverError.InvalidSignature);\n }\n\n return (signer, RecoverError.NoError);\n }\n\n /**\n * @dev Overload of {ECDSA-recover} that receives the `v`,\n * `r` and `s` signature fields separately.\n */\n function recover(\n bytes32 hash,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, v, r, s);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Returns an Ethereum Signed Message, created from a `hash`. This\n * produces hash corresponding to the one signed with the\n * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]\n * JSON-RPC method as part of EIP-191.\n *\n * See {recover}.\n */\n function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {\n // 32 is the length in bytes of hash,\n // enforced by the type signature above\n return keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n32\", hash));\n }\n\n /**\n * @dev Returns an Ethereum Signed Message, created from `s`. This\n * produces hash corresponding to the one signed with the\n * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]\n * JSON-RPC method as part of EIP-191.\n *\n * See {recover}.\n */\n function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {\n return keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n\", Strings.toString(s.length), s));\n }\n\n /**\n * @dev Returns an Ethereum Signed Typed Data, created from a\n * `domainSeparator` and a `structHash`. This produces hash corresponding\n * to the one signed with the\n * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]\n * JSON-RPC method as part of EIP-712.\n *\n * See {recover}.\n */\n function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {\n return keccak256(abi.encodePacked(\"\\x19\\x01\", domainSeparator, structHash));\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/EIP712.sol)\n\n/**\n * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.\n *\n * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,\n * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding\n * they need in their contracts using a combination of `abi.encode` and `keccak256`.\n *\n * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding\n * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA\n * ({_hashTypedDataV4}).\n *\n * The implementation of the domain separator was designed to be as efficient as possible while still properly updating\n * the chain id to protect against replay attacks on an eventual fork of the chain.\n *\n * NOTE: This contract implements the version of the encoding known as \"v4\", as implemented by the JSON RPC method\n * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].\n *\n * _Available since v3.4._\n */\nabstract contract EIP712 {\n /* solhint-disable var-name-mixedcase */\n // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to\n // invalidate the cached domain separator if the chain id changes.\n bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;\n uint256 private immutable _CACHED_CHAIN_ID;\n address private immutable _CACHED_THIS;\n\n bytes32 private immutable _HASHED_NAME;\n bytes32 private immutable _HASHED_VERSION;\n bytes32 private immutable _TYPE_HASH;\n\n /* solhint-enable var-name-mixedcase */\n\n /**\n * @dev Initializes the domain separator and parameter caches.\n *\n * The meaning of `name` and `version` is specified in\n * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:\n *\n * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.\n * - `version`: the current major version of the signing domain.\n *\n * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart\n * contract upgrade].\n */\n constructor(string memory name, string memory version) {\n bytes32 hashedName = keccak256(bytes(name));\n bytes32 hashedVersion = keccak256(bytes(version));\n bytes32 typeHash = keccak256(\n \"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\"\n );\n _HASHED_NAME = hashedName;\n _HASHED_VERSION = hashedVersion;\n _CACHED_CHAIN_ID = block.chainid;\n _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);\n _CACHED_THIS = address(this);\n _TYPE_HASH = typeHash;\n }\n\n /**\n * @dev Returns the domain separator for the current chain.\n */\n function _domainSeparatorV4() internal view returns (bytes32) {\n if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) {\n return _CACHED_DOMAIN_SEPARATOR;\n } else {\n return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);\n }\n }\n\n function _buildDomainSeparator(\n bytes32 typeHash,\n bytes32 nameHash,\n bytes32 versionHash\n ) private view returns (bytes32) {\n return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));\n }\n\n /**\n * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this\n * function returns the hash of the fully encoded EIP712 message for this domain.\n *\n * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:\n *\n * ```solidity\n * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(\n * keccak256(\"Mail(address to,string contents)\"),\n * mailTo,\n * keccak256(bytes(mailContents))\n * )));\n * address signer = ECDSA.recover(digest, signature);\n * ```\n */\n function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {\n return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);\n }\n}\n\n// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)\n\n/**\n * @title Counters\n * @author Matt Condon (@shrugs)\n * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number\n * of elements in a mapping, issuing ERC721 ids, or counting request ids.\n *\n * Include with `using Counters for Counters.Counter;`\n */\nlibrary Counters {\n struct Counter {\n // This variable should never be directly accessed by users of the library: interactions must be restricted to\n // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add\n // this feature: see https://github.com/ethereum/solidity/issues/4637\n uint256 _value; // default: 0\n }\n\n function current(Counter storage counter) internal view returns (uint256) {\n return counter._value;\n }\n\n function increment(Counter storage counter) internal {\n unchecked {\n counter._value += 1;\n }\n }\n\n function decrement(Counter storage counter) internal {\n uint256 value = counter._value;\n require(value > 0, \"Counter: decrement overflow\");\n unchecked {\n counter._value = value - 1;\n }\n }\n\n function reset(Counter storage counter) internal {\n counter._value = 0;\n }\n}\n\n/**\n * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n *\n * _Available since v3.4._\n */\nabstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {\n using Counters for Counters.Counter;\n\n mapping(address => Counters.Counter) private _nonces;\n\n // solhint-disable-next-line var-name-mixedcase\n bytes32 private constant _PERMIT_TYPEHASH =\n keccak256(\"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)\");\n /**\n * @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`.\n * However, to ensure consistency with the upgradeable transpiler, we will continue\n * to reserve a slot.\n * @custom:oz-renamed-from _PERMIT_TYPEHASH\n */\n // solhint-disable-next-line var-name-mixedcase\n bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT;\n\n /**\n * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `\"1\"`.\n *\n * It's a good idea to use the same `name` that is defined as the ERC20 token name.\n */\n constructor(string memory name) EIP712(name, \"1\") {}\n\n /**\n * @dev See {IERC20Permit-permit}.\n */\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) public virtual override {\n require(block.timestamp <= deadline, \"ERC20Permit: expired deadline\");\n\n bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));\n\n bytes32 hash = _hashTypedDataV4(structHash);\n\n address signer = ECDSA.recover(hash, v, r, s);\n require(signer == owner, \"ERC20Permit: invalid signature\");\n\n _approve(owner, spender, value);\n }\n\n /**\n * @dev See {IERC20Permit-nonces}.\n */\n function nonces(address owner) public view virtual override returns (uint256) {\n return _nonces[owner].current();\n }\n\n /**\n * @dev See {IERC20Permit-DOMAIN_SEPARATOR}.\n */\n // solhint-disable-next-line func-name-mixedcase\n function DOMAIN_SEPARATOR() external view override returns (bytes32) {\n return _domainSeparatorV4();\n }\n\n /**\n * @dev \"Consume a nonce\": return the current value and increment.\n *\n * _Available since v4.1._\n */\n function _useNonce(address owner) internal virtual returns (uint256 current) {\n Counters.Counter storage nonce = _nonces[owner];\n current = nonce.current();\n nonce.increment();\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/extensions/ERC20FlashMint.sol)\n\n/**\n * @dev Implementation of the ERC3156 Flash loans extension, as defined in\n * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].\n *\n * Adds the {flashLoan} method, which provides flash loan support at the token\n * level. By default there is no fee, but this can be changed by overriding {flashFee}.\n *\n * _Available since v4.1._\n */\nabstract contract ERC20FlashMint is ERC20, IERC3156FlashLender {\n bytes32 private constant _RETURN_VALUE = keccak256(\"ERC3156FlashBorrower.onFlashLoan\");\n\n /**\n * @dev Returns the maximum amount of tokens available for loan.\n * @param token The address of the token that is requested.\n * @return The amount of token that can be loaned.\n */\n function maxFlashLoan(address token) public view virtual override returns (uint256) {\n return token == address(this) ? type(uint256).max - ERC20.totalSupply() : 0;\n }\n\n /**\n * @dev Returns the fee applied when doing flash loans. This function calls\n * the {_flashFee} function which returns the fee applied when doing flash\n * loans.\n * @param token The token to be flash loaned.\n * @param amount The amount of tokens to be loaned.\n * @return The fees applied to the corresponding flash loan.\n */\n function flashFee(address token, uint256 amount) public view virtual override returns (uint256) {\n require(token == address(this), \"ERC20FlashMint: wrong token\");\n return _flashFee(token, amount);\n }\n\n /**\n * @dev Returns the fee applied when doing flash loans. By default this\n * implementation has 0 fees. This function can be overloaded to make\n * the flash loan mechanism deflationary.\n * @param token The token to be flash loaned.\n * @param amount The amount of tokens to be loaned.\n * @return The fees applied to the corresponding flash loan.\n */\n function _flashFee(address token, uint256 amount) internal view virtual returns (uint256) {\n // silence warning about unused variable without the addition of bytecode.\n token;\n amount;\n return 0;\n }\n\n /**\n * @dev Returns the receiver address of the flash fee. By default this\n * implementation returns the address(0) which means the fee amount will be burnt.\n * This function can be overloaded to change the fee receiver.\n * @return The address for which the flash fee will be sent to.\n */\n function _flashFeeReceiver() internal view virtual returns (address) {\n return address(0);\n }\n\n /**\n * @dev Performs a flash loan. New tokens are minted and sent to the\n * `receiver`, who is required to implement the {IERC3156FlashBorrower}\n * interface. By the end of the flash loan, the receiver is expected to own\n * amount + fee tokens and have them approved back to the token contract itself so\n * they can be burned.\n * @param receiver The receiver of the flash loan. Should implement the\n * {IERC3156FlashBorrower-onFlashLoan} interface.\n * @param token The token to be flash loaned. Only `address(this)` is\n * supported.\n * @param amount The amount of tokens to be loaned.\n * @param data An arbitrary datafield that is passed to the receiver.\n * @return `true` if the flash loan was successful.\n */\n // This function can reenter, but it doesn't pose a risk because it always preserves the property that the amount\n // minted at the beginning is always recovered and burned at the end, or else the entire function will revert.\n // slither-disable-next-line reentrancy-no-eth\n function flashLoan(\n IERC3156FlashBorrower receiver,\n address token,\n uint256 amount,\n bytes calldata data\n ) public virtual override returns (bool) {\n require(amount <= maxFlashLoan(token), \"ERC20FlashMint: amount exceeds maxFlashLoan\");\n uint256 fee = flashFee(token, amount);\n _mint(address(receiver), amount);\n require(\n receiver.onFlashLoan(msg.sender, token, amount, fee, data) == _RETURN_VALUE,\n \"ERC20FlashMint: invalid return value\"\n );\n address flashFeeReceiver = _flashFeeReceiver();\n _spendAllowance(address(receiver), address(this), amount + fee);\n if (fee == 0 || flashFeeReceiver == address(0)) {\n _burn(address(receiver), amount + fee);\n } else {\n _burn(address(receiver), amount);\n _transfer(address(receiver), flashFeeReceiver, fee);\n }\n return true;\n }\n}\n\ncontract RToken is ReentrancyGuard, ERC20Permit, ERC20FlashMint, PositionManagerDependent, FeeCollector, IRToken {\n // --- Constants ---\n\n uint256 public constant override PERCENTAGE_BASE = 10_000;\n uint256 public constant override MAX_FLASH_MINT_FEE_PERCENTAGE = 500;\n\n // --- Variables ---\n\n uint256 public override flashMintFeePercentage;\n\n // --- Constructor ---\n\n /// @dev Deploys new R token. Sets flash mint fee percentage to 0. Transfers ownership to @param feeRecipient_.\n /// @param positionManager_ Address of the PositionManager contract that is authorized to mint and burn new tokens.\n /// @param feeRecipient_ Address of flash mint fee recipient.\n constructor(\n address positionManager_,\n address feeRecipient_\n )\n ERC20Permit(\"R Stablecoin\")\n ERC20(\"R Stablecoin\", \"R\")\n PositionManagerDependent(positionManager_)\n FeeCollector(feeRecipient_)\n {\n setFlashMintFeePercentage(PERCENTAGE_BASE / 200); // 0.5%\n\n transferOwnership(feeRecipient_);\n\n emit RDeployed(positionManager_, feeRecipient_);\n }\n\n // --- Functions ---\n\n function mint(address to, uint256 amount) external override onlyPositionManager {\n _mint(to, amount);\n }\n\n function burn(address from, uint256 amount) external override onlyPositionManager {\n _burn(from, amount);\n }\n\n function setFlashMintFeePercentage(uint256 feePercentage) public override onlyOwner {\n if (feePercentage > MAX_FLASH_MINT_FEE_PERCENTAGE) {\n revert FlashFeePercentageTooBig(feePercentage);\n }\n\n flashMintFeePercentage = feePercentage;\n emit FlashMintFeePercentageChanged(flashMintFeePercentage);\n }\n\n function flashLoan(\n IERC3156FlashBorrower receiver,\n address token,\n uint256 amount,\n bytes calldata data\n )\n public\n override(ERC20FlashMint, IERC3156FlashLender)\n nonReentrant\n returns (bool)\n {\n return super.flashLoan(receiver, token, amount, data);\n }\n\n /// @dev Inherited from ERC20FlashMint. Defines maximum size of the flash mint.\n /// @param token Token to be flash minted. Returns 0 amount in case of token != address(this).\n function maxFlashLoan(address token)\n public\n view\n virtual\n override(ERC20FlashMint, IERC3156FlashLender)\n returns (uint256)\n {\n return token == address(this) ? Math.min(totalSupply() / 10, ERC20FlashMint.maxFlashLoan(address(this))) : 0;\n }\n\n /// @dev Inherited from ERC20FlashMint. Defines flash mint fee for the flash mint of @param amount tokens.\n /// @param token Token to be flash minted. Returns 0 fee in case of token != address(this).\n /// @param amount Size of the flash mint.\n function _flashFee(address token, uint256 amount) internal view virtual override returns (uint256) {\n return token == address(this) ? amount * flashMintFeePercentage / PERCENTAGE_BASE : 0;\n }\n\n /// @dev Inherited from ERC20FlashMint. Defines flash mint fee receiver.\n /// @return Address that will receive flash mint fees.\n function _flashFeeReceiver() internal view virtual override returns (address) {\n return feeRecipient;\n }\n}\n\n/// @dev Implementation of Position Manager. Current implementation does not support rebasing tokens as collateral.\ncontract PositionManager is FeeCollector, IPositionManager {\n // --- Types ---\n\n using SafeERC20 for IERC20;\n using Fixed256x18 for uint256;\n\n // --- Constants ---\n\n uint256 public constant override MINUTE_DECAY_FACTOR = 999_037_758_833_783_000;\n\n uint256 public constant override MAX_BORROWING_SPREAD = MathUtils._100_PERCENT / 100; // 1%\n uint256 public constant override MAX_BORROWING_RATE = MathUtils._100_PERCENT / 100 * 5; // 5%\n\n uint256 public constant override BETA = 2;\n\n // --- Immutables ---\n\n IRToken public immutable override rToken;\n\n // --- Variables ---\n\n mapping(address position => IERC20 collateralToken) public override collateralTokenForPosition;\n\n mapping(address position => mapping(address delegate => bool isWhitelisted)) public override isDelegateWhitelisted;\n\n mapping(IERC20 collateralToken => CollateralTokenInfo collateralTokenInfo) public override collateralInfo;\n\n // --- Modifiers ---\n\n /// @dev Checks if the collateral token has been added to the position manager, or reverts otherwise.\n /// @param collateralToken The collateral token to check.\n modifier collateralTokenExists(IERC20 collateralToken) {\n if (address(collateralInfo[collateralToken].collateralToken) == address(0)) {\n revert CollateralTokenNotAdded();\n }\n _;\n }\n\n /// @dev Checks if the collateral token has enabled, or reverts otherwise. When the condition is false, the check\n /// is skipped.\n /// @param collateralToken The collateral token to check.\n /// @param condition If true, the check will be performed.\n modifier onlyEnabledCollateralTokenWhen(IERC20 collateralToken, bool condition) {\n if (condition && !collateralInfo[collateralToken].isEnabled) {\n revert CollateralTokenDisabled();\n }\n _;\n }\n\n /// @dev Checks if the borrower has a position with the collateral token or doesn't have a position at all, or\n /// reverts otherwise.\n /// @param position The borrower to check.\n /// @param collateralToken The collateral token to check.\n modifier onlyDepositedCollateralTokenOrNew(address position, IERC20 collateralToken) {\n if (\n collateralTokenForPosition[position] != IERC20(address(0))\n && collateralTokenForPosition[position] != collateralToken\n ) {\n revert PositionCollateralTokenMismatch();\n }\n _;\n }\n\n /// @dev Checks if the max fee percentage is between the borrowing spread and 100%, or reverts otherwise. When the\n /// condition is false, the check is skipped.\n /// @param maxFeePercentage The max fee percentage to check.\n /// @param condition If true, the check will be performed.\n modifier validMaxFeePercentageWhen(uint256 maxFeePercentage, bool condition) {\n if (condition && maxFeePercentage > MathUtils._100_PERCENT) {\n revert InvalidMaxFeePercentage();\n }\n _;\n }\n\n // --- Constructor ---\n\n /// @dev Initializes the position manager.\n constructor(address rToken_) FeeCollector(msg.sender) {\n rToken = rToken_ == address(0) ? new RToken(address(this), msg.sender) : IRToken(rToken_);\n emit PositionManagerDeployed(rToken, msg.sender);\n }\n\n // --- External functions ---\n\n function managePosition(\n IERC20 collateralToken,\n address position,\n uint256 collateralChange,\n bool isCollateralIncrease,\n uint256 debtChange,\n bool isDebtIncrease,\n uint256 maxFeePercentage,\n ERC20PermitSignature calldata permitSignature\n )\n public\n virtual\n override\n collateralTokenExists(collateralToken)\n validMaxFeePercentageWhen(maxFeePercentage, isDebtIncrease)\n onlyDepositedCollateralTokenOrNew(position, collateralToken)\n onlyEnabledCollateralTokenWhen(collateralToken, isDebtIncrease && debtChange > 0)\n returns (uint256 actualCollateralChange, uint256 actualDebtChange)\n {\n if (position != msg.sender && !isDelegateWhitelisted[position][msg.sender]) {\n revert DelegateNotWhitelisted();\n }\n if (collateralChange == 0 && debtChange == 0) {\n revert NoCollateralOrDebtChange();\n }\n if (address(permitSignature.token) == address(collateralToken)) {\n PermitHelper.applyPermit(permitSignature, msg.sender, address(this));\n }\n\n CollateralTokenInfo storage collateralTokenInfo = collateralInfo[collateralToken];\n IERC20Indexable raftCollateralToken = collateralTokenInfo.collateralToken;\n IERC20Indexable raftDebtToken = collateralTokenInfo.debtToken;\n\n uint256 debtBefore = raftDebtToken.balanceOf(position);\n if (!isDebtIncrease && (debtChange == type(uint256).max || (debtBefore != 0 && debtChange == debtBefore))) {\n if (collateralChange != 0 || isCollateralIncrease) {\n revert WrongCollateralParamsForFullRepayment();\n }\n collateralChange = raftCollateralToken.balanceOf(position);\n debtChange = debtBefore;\n }\n\n _adjustDebt(position, collateralToken, raftDebtToken, debtChange, isDebtIncrease, maxFeePercentage);\n _adjustCollateral(collateralToken, raftCollateralToken, position, collateralChange, isCollateralIncrease);\n\n uint256 positionDebt = raftDebtToken.balanceOf(position);\n uint256 positionCollateral = raftCollateralToken.balanceOf(position);\n\n if (positionDebt == 0) {\n if (positionCollateral != 0) {\n revert InvalidPosition();\n }\n // position was closed, remove it\n _closePosition(raftCollateralToken, raftDebtToken, position, false);\n } else {\n _checkValidPosition(collateralToken, positionDebt, positionCollateral);\n\n if (debtBefore == 0) {\n collateralTokenForPosition[position] = collateralToken;\n emit PositionCreated(position, collateralToken);\n }\n }\n return (collateralChange, debtChange);\n }\n\n function liquidate(address position) external override {\n IERC20 collateralToken = collateralTokenForPosition[position];\n CollateralTokenInfo storage collateralTokenInfo = collateralInfo[collateralToken];\n IERC20Indexable raftCollateralToken = collateralTokenInfo.collateralToken;\n IERC20Indexable raftDebtToken = collateralTokenInfo.debtToken;\n ISplitLiquidationCollateral splitLiquidation = collateralTokenInfo.splitLiquidation;\n\n if (address(collateralToken) == address(0)) {\n revert NothingToLiquidate();\n }\n (uint256 price,) = collateralTokenInfo.priceFeed.fetchPrice();\n uint256 entireCollateral = raftCollateralToken.balanceOf(position);\n uint256 entireDebt = raftDebtToken.balanceOf(position);\n uint256 icr = MathUtils._computeCR(entireCollateral, entireDebt, price);\n\n if (icr >= splitLiquidation.MCR()) {\n revert NothingToLiquidate();\n }\n\n uint256 totalDebt = raftDebtToken.totalSupply();\n if (entireDebt == totalDebt) {\n revert CannotLiquidateLastPosition();\n }\n bool isRedistribution = icr <= MathUtils._100_PERCENT;\n\n // prettier: ignore\n (uint256 collateralLiquidationFee, uint256 collateralToSendToLiquidator) =\n splitLiquidation.split(entireCollateral, entireDebt, price, isRedistribution);\n\n if (!isRedistribution) {\n _burnRTokens(msg.sender, entireDebt);\n totalDebt -= entireDebt;\n\n // Collateral is sent to protocol as a fee only in case of liquidation\n collateralToken.safeTransfer(feeRecipient, collateralLiquidationFee);\n }\n\n collateralToken.safeTransfer(msg.sender, collateralToSendToLiquidator);\n\n _closePosition(raftCollateralToken, raftDebtToken, position, true);\n\n _updateDebtAndCollateralIndex(collateralToken, raftCollateralToken, raftDebtToken, totalDebt);\n\n emit Liquidation(\n msg.sender,\n position,\n collateralToken,\n entireDebt,\n entireCollateral,\n collateralToSendToLiquidator,\n collateralLiquidationFee,\n isRedistribution\n );\n }\n\n function redeemCollateral(\n IERC20 collateralToken,\n uint256 debtAmount,\n uint256 maxFeePercentage\n )\n public\n virtual\n override\n {\n if (maxFeePercentage > MathUtils._100_PERCENT) {\n revert MaxFeePercentageOutOfRange();\n }\n if (debtAmount == 0) {\n revert AmountIsZero();\n }\n IERC20Indexable raftDebtToken = collateralInfo[collateralToken].debtToken;\n\n uint256 newTotalDebt = raftDebtToken.totalSupply() - debtAmount;\n uint256 lowTotalDebt = collateralInfo[collateralToken].splitLiquidation.LOW_TOTAL_DEBT();\n if (newTotalDebt < lowTotalDebt) {\n revert TotalDebtCannotBeLowerThanMinDebt(collateralToken, newTotalDebt);\n }\n\n (uint256 price, uint256 deviation) = collateralInfo[collateralToken].priceFeed.fetchPrice();\n uint256 collateralToRedeem = debtAmount.divDown(price);\n uint256 totalCollateral = collateralToken.balanceOf(address(this));\n if (\n totalCollateral - collateralToRedeem == 0\n || totalCollateral - collateralToRedeem < lowTotalDebt.divDown(price)\n ) {\n revert TotalCollateralCannotBeLowerThanMinCollateral(\n collateralToken, totalCollateral - collateralToRedeem, lowTotalDebt.divDown(price)\n );\n }\n\n // Decay the baseRate due to time passed, and then increase it according to the size of this redemption.\n // Use the saved total R supply value, from before it was reduced by the redemption.\n _updateBaseRateFromRedemption(collateralToken, collateralToRedeem, price, rToken.totalSupply());\n\n // Calculate the redemption fee\n uint256 redemptionFee = getRedemptionFee(collateralToken, collateralToRedeem, deviation);\n uint256 rebate = redemptionFee.mulDown(collateralInfo[collateralToken].redemptionRebate);\n\n _checkValidFee(redemptionFee, collateralToRedeem, maxFeePercentage);\n\n // Send the redemption fee to the recipient\n collateralToken.safeTransfer(feeRecipient, redemptionFee - rebate);\n\n // Burn the total R that is cancelled with debt, and send the redeemed collateral to msg.sender\n _burnRTokens(msg.sender, debtAmount);\n\n // Send collateral to account\n collateralToken.safeTransfer(msg.sender, collateralToRedeem - redemptionFee);\n\n _updateDebtAndCollateralIndex(\n collateralToken, collateralInfo[collateralToken].collateralToken, raftDebtToken, newTotalDebt\n );\n\n emit Redemption(msg.sender, debtAmount, collateralToRedeem, redemptionFee, rebate);\n }\n\n function whitelistDelegate(address delegate, bool whitelisted) external override {\n if (delegate == address(0)) {\n revert InvalidDelegateAddress();\n }\n isDelegateWhitelisted[msg.sender][delegate] = whitelisted;\n\n emit DelegateWhitelisted(msg.sender, delegate, whitelisted);\n }\n\n function setBorrowingSpread(IERC20 collateralToken, uint256 newBorrowingSpread) external override onlyOwner {\n if (newBorrowingSpread > MAX_BORROWING_SPREAD) {\n revert BorrowingSpreadExceedsMaximum();\n }\n collateralInfo[collateralToken].borrowingSpread = newBorrowingSpread;\n emit BorrowingSpreadUpdated(newBorrowingSpread);\n }\n\n function setRedemptionRebate(IERC20 collateralToken, uint256 newRedemptionRebate) public override onlyOwner {\n if (newRedemptionRebate > MathUtils._100_PERCENT) {\n revert RedemptionRebateExceedsMaximum();\n }\n collateralInfo[collateralToken].redemptionRebate = newRedemptionRebate;\n emit RedemptionRebateUpdated(newRedemptionRebate);\n }\n\n function getRedemptionFeeWithDecay(\n IERC20 collateralToken,\n uint256 collateralAmount\n )\n external\n view\n override\n returns (uint256 redemptionFee)\n {\n redemptionFee = getRedemptionRateWithDecay(collateralToken).mulDown(collateralAmount);\n if (redemptionFee >= collateralAmount) {\n revert FeeEatsUpAllReturnedCollateral();\n }\n }\n\n // --- Public functions ---\n\n function addCollateralToken(\n IERC20 collateralToken,\n IPriceFeed priceFeed,\n ISplitLiquidationCollateral newSplitLiquidationCollateral\n )\n public\n virtual\n override\n {\n addCollateralToken(\n collateralToken,\n priceFeed,\n newSplitLiquidationCollateral,\n new ERC20Indexable(\n address(this),\n string(bytes.concat(\"Raft \", bytes(IERC20Metadata(address(collateralToken)).name()), \" collateral\")),\n string(bytes.concat(\"r\", bytes(IERC20Metadata(address(collateralToken)).symbol()), \"-c\")),\n type(uint256).max\n ),\n new ERC20Indexable(\n address(this),\n string(bytes.concat(\"Raft \", bytes(IERC20Metadata(address(collateralToken)).name()), \" debt\")),\n string(bytes.concat(\"r\", bytes(IERC20Metadata(address(collateralToken)).symbol()), \"-d\")),\n type(uint256).max\n )\n );\n }\n\n function addCollateralToken(\n IERC20 collateralToken,\n IPriceFeed priceFeed,\n ISplitLiquidationCollateral newSplitLiquidationCollateral,\n IERC20Indexable raftCollateralToken_,\n IERC20Indexable raftDebtToken_\n )\n public\n override\n onlyOwner\n {\n if (address(collateralToken) == address(0)) {\n revert CollateralTokenAddressCannotBeZero();\n }\n if (address(priceFeed) == address(0)) {\n revert PriceFeedAddressCannotBeZero();\n }\n if (address(collateralInfo[collateralToken].collateralToken) != address(0)) {\n revert CollateralTokenAlreadyAdded();\n }\n\n CollateralTokenInfo memory raftCollateralTokenInfo;\n raftCollateralTokenInfo.collateralToken = raftCollateralToken_;\n raftCollateralTokenInfo.debtToken = raftDebtToken_;\n raftCollateralTokenInfo.isEnabled = true;\n raftCollateralTokenInfo.priceFeed = priceFeed;\n\n collateralInfo[collateralToken] = raftCollateralTokenInfo;\n\n setRedemptionSpread(collateralToken, MathUtils._100_PERCENT);\n setRedemptionRebate(collateralToken, MathUtils._100_PERCENT);\n\n setSplitLiquidationCollateral(collateralToken, newSplitLiquidationCollateral);\n\n emit CollateralTokenAdded(\n collateralToken, raftCollateralTokenInfo.collateralToken, raftCollateralTokenInfo.debtToken, priceFeed\n );\n }\n\n function setCollateralEnabled(\n IERC20 collateralToken,\n bool isEnabled\n )\n public\n override\n onlyOwner\n collateralTokenExists(collateralToken)\n {\n bool previousIsEnabled = collateralInfo[collateralToken].isEnabled;\n collateralInfo[collateralToken].isEnabled = isEnabled;\n\n if (previousIsEnabled != isEnabled) {\n emit CollateralTokenModified(collateralToken, isEnabled);\n }\n }\n\n function setSplitLiquidationCollateral(\n IERC20 collateralToken,\n ISplitLiquidationCollateral newSplitLiquidationCollateral\n )\n public\n override\n onlyOwner\n {\n if (address(newSplitLiquidationCollateral) == address(0)) {\n revert SplitLiquidationCollateralCannotBeZero();\n }\n collateralInfo[collateralToken].splitLiquidation = newSplitLiquidationCollateral;\n emit SplitLiquidationCollateralChanged(collateralToken, newSplitLiquidationCollateral);\n }\n\n function setRedemptionSpread(IERC20 collateralToken, uint256 newRedemptionSpread) public override onlyOwner {\n if (newRedemptionSpread > MathUtils._100_PERCENT) {\n revert RedemptionSpreadOutOfRange();\n }\n collateralInfo[collateralToken].redemptionSpread = newRedemptionSpread;\n emit RedemptionSpreadUpdated(collateralToken, newRedemptionSpread);\n }\n\n function getRedemptionRateWithDecay(IERC20 collateralToken) public view override returns (uint256) {\n return _calcRedemptionRate(collateralToken, _calcDecayedBaseRate(collateralToken));\n }\n\n function raftCollateralToken(IERC20 collateralToken) external view override returns (IERC20Indexable) {\n return collateralInfo[collateralToken].collateralToken;\n }\n\n function raftDebtToken(IERC20 collateralToken) external view override returns (IERC20Indexable) {\n return collateralInfo[collateralToken].debtToken;\n }\n\n function priceFeed(IERC20 collateralToken) external view override returns (IPriceFeed) {\n return collateralInfo[collateralToken].priceFeed;\n }\n\n function splitLiquidationCollateral(IERC20 collateralToken) external view returns (ISplitLiquidationCollateral) {\n return collateralInfo[collateralToken].splitLiquidation;\n }\n\n function collateralEnabled(IERC20 collateralToken) external view override returns (bool) {\n return collateralInfo[collateralToken].isEnabled;\n }\n\n function lastFeeOperationTime(IERC20 collateralToken) external view override returns (uint256) {\n return collateralInfo[collateralToken].lastFeeOperationTime;\n }\n\n function borrowingSpread(IERC20 collateralToken) external view override returns (uint256) {\n return collateralInfo[collateralToken].borrowingSpread;\n }\n\n function baseRate(IERC20 collateralToken) external view override returns (uint256) {\n return collateralInfo[collateralToken].baseRate;\n }\n\n function redemptionSpread(IERC20 collateralToken) external view override returns (uint256) {\n return collateralInfo[collateralToken].redemptionSpread;\n }\n\n function redemptionRebate(IERC20 collateralToken) external view override returns (uint256) {\n return collateralInfo[collateralToken].redemptionRebate;\n }\n\n function getRedemptionRate(IERC20 collateralToken) public view override returns (uint256) {\n return _calcRedemptionRate(collateralToken, collateralInfo[collateralToken].baseRate);\n }\n\n function getRedemptionFee(\n IERC20 collateralToken,\n uint256 collateralAmount,\n uint256 priceDeviation\n )\n public\n view\n override\n returns (uint256)\n {\n return Math.min(getRedemptionRate(collateralToken) + priceDeviation, MathUtils._100_PERCENT).mulDown(\n collateralAmount\n );\n }\n\n function getBorrowingRate(IERC20 collateralToken) public view override returns (uint256) {\n return _calcBorrowingRate(collateralToken, collateralInfo[collateralToken].baseRate);\n }\n\n function getBorrowingRateWithDecay(IERC20 collateralToken) public view override returns (uint256) {\n return _calcBorrowingRate(collateralToken, _calcDecayedBaseRate(collateralToken));\n }\n\n function getBorrowingFee(IERC20 collateralToken, uint256 debtAmount) public view override returns (uint256) {\n return getBorrowingRate(collateralToken).mulDown(debtAmount);\n }\n\n // --- Helper functions ---\n\n /// @dev Adjusts the debt of a given borrower by burning or minting the corresponding amount of R and the Raft\n /// debt token. If the debt is being increased, the borrowing fee is also triggered.\n /// @param position The address of the borrower.\n /// @param debtChange The amount of R to add or remove. Must be positive.\n /// @param isDebtIncrease True if the debt is being increased, false otherwise.\n /// @param maxFeePercentage The maximum fee percentage.\n function _adjustDebt(\n address position,\n IERC20 collateralToken,\n IERC20Indexable raftDebtToken,\n uint256 debtChange,\n bool isDebtIncrease,\n uint256 maxFeePercentage\n )\n internal\n {\n if (debtChange == 0) {\n return;\n }\n\n if (isDebtIncrease) {\n uint256 totalDebtChange =\n debtChange + _triggerBorrowingFee(collateralToken, position, debtChange, maxFeePercentage);\n raftDebtToken.mint(position, totalDebtChange);\n _mintRTokens(msg.sender, debtChange);\n } else {\n raftDebtToken.burn(position, debtChange);\n _burnRTokens(msg.sender, debtChange);\n }\n\n emit DebtChanged(position, collateralToken, debtChange, isDebtIncrease);\n }\n\n /// @dev Mints R tokens\n function _mintRTokens(address to, uint256 amount) internal virtual {\n rToken.mint(to, amount);\n }\n\n /// @dev Burns R tokens\n function _burnRTokens(address from, uint256 amount) internal virtual {\n rToken.burn(from, amount);\n }\n\n /// @dev Adjusts the collateral of a given borrower by burning or minting the corresponding amount of Raft\n /// collateral token and transferring the corresponding amount of collateral token.\n /// @param collateralToken The token the borrower used as collateral.\n /// @param position The address of the borrower.\n /// @param collateralChange The amount of collateral to add or remove. Must be positive.\n /// @param isCollateralIncrease True if the collateral is being increased, false otherwise.\n function _adjustCollateral(\n IERC20 collateralToken,\n IERC20Indexable raftCollateralToken,\n address position,\n uint256 collateralChange,\n bool isCollateralIncrease\n )\n internal\n {\n if (collateralChange == 0) {\n return;\n }\n\n if (isCollateralIncrease) {\n raftCollateralToken.mint(position, collateralChange);\n collateralToken.safeTransferFrom(msg.sender, address(this), collateralChange);\n } else {\n raftCollateralToken.burn(position, collateralChange);\n collateralToken.safeTransfer(msg.sender, collateralChange);\n }\n\n emit CollateralChanged(position, collateralToken, collateralChange, isCollateralIncrease);\n }\n\n /// @dev Updates debt and collateral indexes for a given collateral token.\n /// @param collateralToken The collateral token for which to update the indexes.\n /// @param raftCollateralToken The raft collateral indexable token.\n /// @param raftDebtToken The raft debt indexable token.\n /// @param totalDebtForCollateral Totam amount of debt backed by collateral token.\n function _updateDebtAndCollateralIndex(\n IERC20 collateralToken,\n IERC20Indexable raftCollateralToken,\n IERC20Indexable raftDebtToken,\n uint256 totalDebtForCollateral\n )\n internal\n {\n raftDebtToken.setIndex(totalDebtForCollateral);\n raftCollateralToken.setIndex(collateralToken.balanceOf(address(this)));\n }\n\n function _closePosition(\n IERC20Indexable raftCollateralToken,\n IERC20Indexable raftDebtToken,\n address position,\n bool burnTokens\n )\n internal\n {\n collateralTokenForPosition[position] = IERC20(address(0));\n\n if (burnTokens) {\n raftDebtToken.burn(position, type(uint256).max);\n raftCollateralToken.burn(position, type(uint256).max);\n }\n emit PositionClosed(position);\n }\n\n // --- Borrowing & redemption fee helper functions ---\n\n /// @dev Updates the base rate from a redemption operation. Impacts on the base rate:\n /// 1. decays the base rate based on time passed since last redemption or R borrowing operation,\n /// 2. increases the base rate based on the amount redeemed, as a proportion of total supply.\n function _updateBaseRateFromRedemption(\n IERC20 collateralToken,\n uint256 collateralDrawn,\n uint256 price,\n uint256 totalDebtSupply\n )\n internal\n returns (uint256)\n {\n uint256 decayedBaseRate = _calcDecayedBaseRate(collateralToken);\n\n /* Convert the drawn collateral back to R at face value rate (1 R:1 USD), in order to get\n * the fraction of total supply that was redeemed at face value. */\n uint256 redeemedFraction = collateralDrawn * price / totalDebtSupply;\n\n uint256 newBaseRate = decayedBaseRate + redeemedFraction / BETA;\n newBaseRate = Math.min(newBaseRate, MathUtils._100_PERCENT); // cap baseRate at a maximum of 100%\n assert(newBaseRate > 0); // Base rate is always non-zero after redemption\n\n // Update the baseRate state variable\n collateralInfo[collateralToken].baseRate = newBaseRate;\n emit BaseRateUpdated(collateralToken, newBaseRate);\n\n _updateLastFeeOpTime(collateralToken);\n\n return newBaseRate;\n }\n\n function _calcRedemptionRate(IERC20 collateralToken, uint256 baseRate_) internal view returns (uint256) {\n return baseRate_ + collateralInfo[collateralToken].redemptionSpread;\n }\n\n function _calcBorrowingRate(IERC20 collateralToken, uint256 baseRate_) internal view returns (uint256) {\n return Math.min(collateralInfo[collateralToken].borrowingSpread + baseRate_, MAX_BORROWING_RATE);\n }\n\n /// @dev Updates the base rate based on time elapsed since the last redemption or R borrowing operation.\n function _decayBaseRateFromBorrowing(IERC20 collateralToken) internal {\n uint256 decayedBaseRate = _calcDecayedBaseRate(collateralToken);\n assert(decayedBaseRate <= MathUtils._100_PERCENT); // The baseRate can decay to 0\n\n collateralInfo[collateralToken].baseRate = decayedBaseRate;\n emit BaseRateUpdated(collateralToken, decayedBaseRate);\n\n _updateLastFeeOpTime(collateralToken);\n }\n\n /// @dev Update the last fee operation time only if time passed >= decay interval. This prevents base rate\n /// griefing.\n function _updateLastFeeOpTime(IERC20 collateralToken) internal {\n uint256 timePassed = block.timestamp - collateralInfo[collateralToken].lastFeeOperationTime;\n\n if (timePassed >= 1 minutes) {\n collateralInfo[collateralToken].lastFeeOperationTime = block.timestamp;\n emit LastFeeOpTimeUpdated(collateralToken, block.timestamp);\n }\n }\n\n function _calcDecayedBaseRate(IERC20 collateralToken) internal view returns (uint256) {\n uint256 minutesPassed = (block.timestamp - collateralInfo[collateralToken].lastFeeOperationTime) / 1 minutes;\n uint256 decayFactor = MathUtils._decPow(MINUTE_DECAY_FACTOR, minutesPassed);\n\n return collateralInfo[collateralToken].baseRate.mulDown(decayFactor);\n }\n\n function _triggerBorrowingFee(\n IERC20 collateralToken,\n address position,\n uint256 debtAmount,\n uint256 maxFeePercentage\n )\n internal\n virtual\n returns (uint256 borrowingFee)\n {\n _decayBaseRateFromBorrowing(collateralToken); // decay the baseRate state variable\n borrowingFee = getBorrowingFee(collateralToken, debtAmount);\n\n _checkValidFee(borrowingFee, debtAmount, maxFeePercentage);\n\n if (borrowingFee > 0) {\n _mintRTokens(feeRecipient, borrowingFee);\n emit RBorrowingFeePaid(collateralToken, position, borrowingFee);\n }\n }\n\n // --- Validation check helper functions ---\n\n function _checkValidPosition(IERC20 collateralToken, uint256 positionDebt, uint256 positionCollateral) internal {\n ISplitLiquidationCollateral splitCollateral = collateralInfo[collateralToken].splitLiquidation;\n if (positionDebt < splitCollateral.LOW_TOTAL_DEBT()) {\n revert NetDebtBelowMinimum(positionDebt);\n }\n\n (uint256 price,) = collateralInfo[collateralToken].priceFeed.fetchPrice();\n uint256 newICR = MathUtils._computeCR(positionCollateral, positionDebt, price);\n if (newICR < splitCollateral.MCR()) {\n revert NewICRLowerThanMCR(newICR);\n }\n }\n\n function _checkValidFee(uint256 fee, uint256 amount, uint256 maxFeePercentage) internal pure {\n uint256 feePercentage = fee.divDown(amount);\n\n if (feePercentage > maxFeePercentage) {\n revert FeeExceedsMaxFee(fee, amount, maxFeePercentage);\n }\n }\n}\n\ninterface IRMinter {\n /// @dev Emitted when tokens are recovered from the contract.\n /// @param token The address of the token being recovered.\n /// @param to The address receiving the recovered tokens.\n /// @param amount The amount of tokens recovered.\n event TokensRecovered(IERC20 token, address to, uint256 amount);\n\n /// @return Address of the R token.\n function r() external view returns (IRToken);\n\n /// @return Address of the Position manager contract responsible for minting R.\n function positionManager() external view returns (IPositionManager);\n\n /// @dev Recover accidentally sent tokens to the contract\n /// @param token Address of the token contract.\n /// @param to Address of the receiver of the tokens.\n /// @param amount Number of tokens to recover.\n function recoverTokens(IERC20 token, address to, uint256 amount) external;\n}\n\ninterface ILock {\n /// @dev Thrown when contract usage is locked.\n error ContractLocked();\n\n /// @dev Unauthorized call to lock/unlock.\n error Unauthorized();\n\n /// @dev Retrieves if contract is currently locked or not.\n function locked() external view returns (bool);\n\n /// @dev Retrieves address of the locker who can unlock contract.\n function locker() external view returns (address);\n\n /// @dev Unlcoks the usage of the contract.\n function unlock() external;\n\n /// @dev Locks the usage of the contract.\n function lock() external;\n}\n\nabstract contract ERC20RMinter is IRMinter, ERC20, Ownable2Step {\n using SafeERC20 for IERC20;\n\n IRToken public immutable override r;\n IPositionManager public immutable override positionManager;\n\n constructor(IRToken rToken_, string memory name_, string memory symbol_) ERC20(name_, symbol_) {\n r = rToken_;\n positionManager = IPositionManager(rToken_.positionManager());\n\n _approve(address(this), address(positionManager), type(uint256).max);\n }\n\n modifier unlockCall() {\n ILock lockContract = ILock(address(positionManager.priceFeed(IERC20(this))));\n lockContract.unlock();\n _;\n lockContract.lock();\n }\n\n function recoverTokens(IERC20 token, address to, uint256 amount) external override onlyOwner {\n token.safeTransfer(to, amount);\n emit TokensRecovered(token, to, amount);\n }\n\n function _mintR(address to, uint256 amount) internal unlockCall {\n _mint(address(this), amount);\n ERC20PermitSignature memory emptySignature;\n positionManager.managePosition(\n IERC20(address(this)),\n address(this),\n amount,\n true, // collateral increase\n amount,\n true, // debt increase\n 1e18, // 100%\n emptySignature\n );\n r.transfer(to, amount);\n }\n\n function _burnR(address from, uint256 amount) internal unlockCall {\n r.transferFrom(from, address(this), amount);\n ERC20PermitSignature memory emptySignature;\n positionManager.managePosition(\n IERC20(address(this)),\n address(this),\n amount,\n false, // collateral decrease\n amount,\n false, // debt decrease\n 1e18, // 100%\n emptySignature\n );\n _burn(address(this), amount);\n }\n}\n\ncontract InterestRateDebtToken is ERC20Indexable {\n // --- Types ---\n\n using Fixed256x18 for uint256;\n\n // --- Events ---\n\n event IndexIncreasePerSecondSet(uint256 indexIncreasePerSecond);\n\n // --- Immutables ---\n\n IERC20 immutable collateralToken;\n\n // --- Variables ---\n\n uint256 internal storedIndexUpdatedAt;\n\n uint256 public indexIncreasePerSecond;\n\n // --- Constructor ---\n\n constructor(\n address positionManager_,\n string memory name_,\n string memory symbol_,\n IERC20 collateralToken_,\n uint256 cap_,\n uint256 indexIncreasePerSecond_\n )\n ERC20Indexable(positionManager_, name_, symbol_, cap_)\n {\n storedIndexUpdatedAt = block.timestamp;\n collateralToken = collateralToken_;\n setIndexIncreasePerSecond(indexIncreasePerSecond_);\n }\n\n // --- Functions ---\n\n function mint(address to, uint256 amount) public virtual override {\n updateIndexAndPayFees();\n super.mint(to, amount);\n }\n\n function burn(address from, uint256 amount) public virtual override {\n updateIndexAndPayFees();\n super.burn(from, amount);\n }\n\n function currentIndex() public view virtual override returns (uint256) {\n return storedIndex.mulUp(INDEX_PRECISION + indexIncreasePerSecond * (block.timestamp - storedIndexUpdatedAt));\n }\n\n function updateIndexAndPayFees() public {\n uint256 currentIndex_ = currentIndex();\n _payFees(currentIndex_);\n storedIndexUpdatedAt = block.timestamp;\n storedIndex = currentIndex_;\n emit IndexUpdated(currentIndex_);\n }\n\n function setIndexIncreasePerSecond(uint256 indexIncreasePerSecond_) public onlyOwner {\n updateIndexAndPayFees();\n indexIncreasePerSecond = indexIncreasePerSecond_;\n emit IndexIncreasePerSecondSet(indexIncreasePerSecond_);\n }\n\n function unpaidFees() external view returns (uint256) {\n return _unpaidFees(currentIndex());\n }\n\n function _unpaidFees(uint256 currentIndex_) private view returns (uint256) {\n return totalSupply().mulDown(currentIndex_ - storedIndex);\n }\n\n function _payFees(uint256 currentIndex_) private {\n uint256 unpaidFees = _unpaidFees(currentIndex_);\n if (unpaidFees > 0) {\n IInterestRatePositionManager(positionManager).mintFees(collateralToken, unpaidFees);\n }\n }\n}\n\n/// @dev Implementation of Position Manager. Current implementation does not support rebasing tokens as collateral.\ncontract InterestRatePositionManager is ERC20RMinter, PositionManager, IInterestRatePositionManager {\n // --- Errors ---\n\n error Unsupported();\n\n // --- Constructor ---\n\n /// @dev Initializes the position manager.\n constructor(IRToken rToken_)\n PositionManager(address(rToken_))\n ERC20RMinter(rToken_, \"Interest Rate Posman\", \"IRPM\")\n { }\n\n // --- External functions ---\n\n function managePosition(\n IERC20 collateralToken,\n address position,\n uint256 collateralChange,\n bool isCollateralIncrease,\n uint256 debtChange,\n bool isDebtIncrease,\n uint256 maxFeePercentage,\n ERC20PermitSignature calldata permitSignature\n )\n public\n virtual\n override(IPositionManager, PositionManager)\n returns (uint256 actualCollateralChange, uint256 actualDebtChange)\n {\n if (address(permitSignature.token) == address(r)) {\n PermitHelper.applyPermit(permitSignature, msg.sender, address(this));\n }\n return super.managePosition(\n collateralToken,\n position,\n collateralChange,\n isCollateralIncrease,\n debtChange,\n isDebtIncrease,\n maxFeePercentage,\n permitSignature\n );\n }\n\n function mintFees(IERC20 collateralToken, uint256 amount) external {\n if (msg.sender != address(collateralInfo[collateralToken].debtToken)) {\n revert InvalidDebtToken(msg.sender);\n }\n _mintR(feeRecipient, amount);\n\n emit MintedFees(collateralToken, amount);\n }\n\n function redeemCollateral(IERC20, uint256, uint256) public virtual override(IPositionManager, PositionManager) {\n revert Unsupported();\n }\n\n function addCollateralToken(\n IERC20,\n IPriceFeed,\n ISplitLiquidationCollateral\n )\n public\n override(IPositionManager, PositionManager)\n {\n revert Unsupported();\n }\n\n // --- Helper functions ---\n\n function _mintRTokens(address to, uint256 amount) internal virtual override {\n _mintR(to, amount);\n }\n\n function _burnRTokens(address from, uint256 amount) internal virtual override {\n _burnR(from, amount);\n }\n\n function _triggerBorrowingFee(\n IERC20,\n address,\n uint256,\n uint256\n )\n internal\n pure\n virtual\n override\n returns (uint256)\n {\n return 0;\n }\n}\n" + } + }, + "settings": { + "remappings": [ + "@balancer-labs/=node_modules/@balancer-labs/", + "@balancer-labs/v2-interfaces/contracts/=lib/balancer-v2-monorepo/pkg/interfaces/contracts/", + "@chainlink/=node_modules/@chainlink/", + "@eth-optimism/=node_modules/@eth-optimism/", + "@openzeppelin/=node_modules/@openzeppelin/", + "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", + "@redstone-finance/=node_modules/@redstone-finance/", + "@smartcontractkit/chainlink/=lib/chainlink/contracts/src/v0.8/", + "@tempusfinance/=node_modules/@tempusfinance/", + "@tempusfinance/tempus-utils/contracts/=lib/tempus-utils/contracts/", + "balancer-v2-monorepo/=lib/balancer-v2-monorepo/", + "chainlink/=lib/chainlink/", + "ds-test/=lib/forge-std/lib/ds-test/src/", + "erc4626-tests/=lib/chainlink/contracts/foundry-lib/openzeppelin-contracts/lib/erc4626-tests/", + "eth-gas-reporter/=node_modules/eth-gas-reporter/", + "forge-std/=lib/forge-std/src/", + "hardhat/=node_modules/hardhat/", + "openzeppelin-contracts/=lib/openzeppelin-contracts/", + "tempus-utils/=lib/tempus-utils/contracts/" + ], + "optimizer": { + "enabled": true, + "runs": 200000 + }, + "metadata": { + "bytecodeHash": "ipfs", + "appendCBOR": true + }, + "outputSelection": { + "*": { + "*": [ + "evm.bytecode", + "evm.deployedBytecode", + "devdoc", + "userdoc", + "metadata", + "abi" + ] + } + }, + "evmVersion": "london", + "viaIR": true, + "libraries": {} + } + }, + "ABI": "[{\"inputs\":[{\"internalType\":\"contract IRToken\",\"name\":\"rToken_\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AmountIsZero\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BorrowingSpreadExceedsMaximum\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotLiquidateLastPosition\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CollateralTokenAddressCannotBeZero\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CollateralTokenAlreadyAdded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CollateralTokenDisabled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CollateralTokenNotAdded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DelegateNotWhitelisted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FeeEatsUpAllReturnedCollateral\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxFeePercentage\",\"type\":\"uint256\"}],\"name\":\"FeeExceedsMaxFee\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"InvalidDebtToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidDelegateAddress\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidFeeRecipient\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidMaxFeePercentage\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidPosition\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaxFeePercentageOutOfRange\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"netDebt\",\"type\":\"uint256\"}],\"name\":\"NetDebtBelowMinimum\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newICR\",\"type\":\"uint256\"}],\"name\":\"NewICRLowerThanMCR\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoCollateralOrDebtChange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NothingToLiquidate\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PositionCollateralTokenMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PriceFeedAddressCannotBeZero\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RedemptionRebateExceedsMaximum\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RedemptionSpreadOutOfRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SplitLiquidationCollateralCannotBeZero\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"newTotalCollateral\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minimumCollateral\",\"type\":\"uint256\"}],\"name\":\"TotalCollateralCannotBeLowerThanMinCollateral\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"newTotalDebt\",\"type\":\"uint256\"}],\"name\":\"TotalDebtCannotBeLowerThanMinDebt\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Unsupported\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"WrongCollateralParamsForFullRepayment\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"baseRate\",\"type\":\"uint256\"}],\"name\":\"BaseRateUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"borrowingSpread\",\"type\":\"uint256\"}],\"name\":\"BorrowingSpreadUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"collateralAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isCollateralIncrease\",\"type\":\"bool\"}],\"name\":\"CollateralChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract IERC20Indexable\",\"name\":\"raftCollateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract IERC20Indexable\",\"name\":\"raftDebtToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract IPriceFeed\",\"name\":\"priceFeed\",\"type\":\"address\"}],\"name\":\"CollateralTokenAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"}],\"name\":\"CollateralTokenModified\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"debtAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isDebtIncrease\",\"type\":\"bool\"}],\"name\":\"DebtChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"delegate\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"whitelisted\",\"type\":\"bool\"}],\"name\":\"DelegateWhitelisted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"feeRecipient\",\"type\":\"address\"}],\"name\":\"FeeRecipientChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"lastFeeOpTime\",\"type\":\"uint256\"}],\"name\":\"LastFeeOpTimeUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"liquidator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"debtLiquidated\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"collateralLiquidated\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"collateralSentToLiquidator\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"collateralLiquidationFeePaid\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isRedistribution\",\"type\":\"bool\"}],\"name\":\"Liquidation\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"MintedFees\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferStarted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"}],\"name\":\"PositionClosed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"PositionCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IRToken\",\"name\":\"rToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"feeRecipient\",\"type\":\"address\"}],\"name\":\"PositionManagerDeployed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"feeAmount\",\"type\":\"uint256\"}],\"name\":\"RBorrowingFeePaid\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"redeemer\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"collateralSent\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"rebate\",\"type\":\"uint256\"}],\"name\":\"Redemption\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"redemptionRebate\",\"type\":\"uint256\"}],\"name\":\"RedemptionRebateUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"redemptionSpread\",\"type\":\"uint256\"}],\"name\":\"RedemptionSpreadUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"contract ISplitLiquidationCollateral\",\"name\":\"newSplitLiquidationCollateral\",\"type\":\"address\"}],\"name\":\"SplitLiquidationCollateralChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"TokensRecovered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"BETA\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_BORROWING_RATE\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_BORROWING_SPREAD\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MINUTE_DECAY_FACTOR\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"contract IPriceFeed\",\"name\":\"priceFeed\",\"type\":\"address\"},{\"internalType\":\"contract ISplitLiquidationCollateral\",\"name\":\"newSplitLiquidationCollateral\",\"type\":\"address\"},{\"internalType\":\"contract IERC20Indexable\",\"name\":\"raftCollateralToken_\",\"type\":\"address\"},{\"internalType\":\"contract IERC20Indexable\",\"name\":\"raftDebtToken_\",\"type\":\"address\"}],\"name\":\"addCollateralToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"contract IPriceFeed\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"contract ISplitLiquidationCollateral\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"addCollateralToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"baseRate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"borrowingSpread\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"collateralEnabled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"collateralInfo\",\"outputs\":[{\"internalType\":\"contract IERC20Indexable\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"contract IERC20Indexable\",\"name\":\"debtToken\",\"type\":\"address\"},{\"internalType\":\"contract IPriceFeed\",\"name\":\"priceFeed\",\"type\":\"address\"},{\"internalType\":\"contract ISplitLiquidationCollateral\",\"name\":\"splitLiquidation\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"lastFeeOperationTime\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"borrowingSpread\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"baseRate\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"redemptionSpread\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"redemptionRebate\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"}],\"name\":\"collateralTokenForPosition\",\"outputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"subtractedValue\",\"type\":\"uint256\"}],\"name\":\"decreaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"feeRecipient\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"debtAmount\",\"type\":\"uint256\"}],\"name\":\"getBorrowingFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"getBorrowingRate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"getBorrowingRateWithDecay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"collateralAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"priceDeviation\",\"type\":\"uint256\"}],\"name\":\"getRedemptionFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"collateralAmount\",\"type\":\"uint256\"}],\"name\":\"getRedemptionFeeWithDecay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"redemptionFee\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"getRedemptionRate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"getRedemptionRateWithDecay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"addedValue\",\"type\":\"uint256\"}],\"name\":\"increaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"delegate\",\"type\":\"address\"}],\"name\":\"isDelegateWhitelisted\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"isWhitelisted\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"lastFeeOperationTime\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"}],\"name\":\"liquidate\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"collateralChange\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"isCollateralIncrease\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"debtChange\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"isDebtIncrease\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"maxFeePercentage\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"contract IERC20Permit\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"deadline\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"internalType\":\"struct ERC20PermitSignature\",\"name\":\"permitSignature\",\"type\":\"tuple\"}],\"name\":\"managePosition\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"actualCollateralChange\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actualDebtChange\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mintFees\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pendingOwner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"positionManager\",\"outputs\":[{\"internalType\":\"contract IPositionManager\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"priceFeed\",\"outputs\":[{\"internalType\":\"contract IPriceFeed\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"r\",\"outputs\":[{\"internalType\":\"contract IRToken\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rToken\",\"outputs\":[{\"internalType\":\"contract IRToken\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"raftCollateralToken\",\"outputs\":[{\"internalType\":\"contract IERC20Indexable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"raftDebtToken\",\"outputs\":[{\"internalType\":\"contract IERC20Indexable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"recoverTokens\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"redeemCollateral\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"redemptionRebate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"redemptionSpread\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"newBorrowingSpread\",\"type\":\"uint256\"}],\"name\":\"setBorrowingSpread\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"}],\"name\":\"setCollateralEnabled\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newFeeRecipient\",\"type\":\"address\"}],\"name\":\"setFeeRecipient\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"newRedemptionRebate\",\"type\":\"uint256\"}],\"name\":\"setRedemptionRebate\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"newRedemptionSpread\",\"type\":\"uint256\"}],\"name\":\"setRedemptionSpread\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"contract ISplitLiquidationCollateral\",\"name\":\"newSplitLiquidationCollateral\",\"type\":\"address\"}],\"name\":\"setSplitLiquidationCollateral\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"splitLiquidationCollateral\",\"outputs\":[{\"internalType\":\"contract ISplitLiquidationCollateral\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"delegate\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"whitelisted\",\"type\":\"bool\"}],\"name\":\"whitelistDelegate\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + "ContractName": "InterestRatePositionManager", + "CompilerVersion": "v0.8.19+commit.7dd6d404", + "OptimizationUsed": 1, + "Runs": 200000, + "ConstructorArguments": "0x000000000000000000000000183015a9ba6ff60230fdeadc3f43b3d788b13e21", + "EVMVersion": "london", + "Library": "", + "LicenseType": "", + "Proxy": 0, + "SwarmSource": "" + } +] \ No newline at end of file diff --git a/testdata/etherscan/0xDb53f47aC61FE54F456A4eb3E09832D08Dd7BEec/creation_data.json b/testdata/etherscan/0xDb53f47aC61FE54F456A4eb3E09832D08Dd7BEec/creation_data.json new file mode 100644 index 000000000..6a1311bb4 --- /dev/null +++ b/testdata/etherscan/0xDb53f47aC61FE54F456A4eb3E09832D08Dd7BEec/creation_data.json @@ -0,0 +1,5 @@ +{ + "contractAddress": "0xdb53f47ac61fe54f456a4eb3e09832d08dd7beec", + "contractCreator": "0xc7f8d87734ab2cbf70030ac8aa82abfe3e8126cb", + "txHash": "0x196898c69f6b1944f1011120b15c0903329d46259c8cdc0fbcad71da1fe58245" +} \ No newline at end of file diff --git a/testdata/etherscan/0xDb53f47aC61FE54F456A4eb3E09832D08Dd7BEec/metadata.json b/testdata/etherscan/0xDb53f47aC61FE54F456A4eb3E09832D08Dd7BEec/metadata.json new file mode 100644 index 000000000..83c1a0635 --- /dev/null +++ b/testdata/etherscan/0xDb53f47aC61FE54F456A4eb3E09832D08Dd7BEec/metadata.json @@ -0,0 +1,148 @@ +[ + { + "SourceCode": { + "language": "Solidity", + "sources": { + "@solidstate/contracts/token/ERC1155/base/ERC1155Base.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC1155 } from '../IERC1155.sol';\nimport { IERC1155Receiver } from '../IERC1155Receiver.sol';\nimport { ERC1155BaseInternal, ERC1155BaseStorage } from './ERC1155BaseInternal.sol';\n\n/**\n * @title Base ERC1155 contract\n * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts/ (MIT license)\n */\nabstract contract ERC1155Base is IERC1155, ERC1155BaseInternal {\n /**\n * @inheritdoc IERC1155\n */\n function balanceOf(address account, uint256 id)\n public\n view\n virtual\n override\n returns (uint256)\n {\n return _balanceOf(account, id);\n }\n\n /**\n * @inheritdoc IERC1155\n */\n function balanceOfBatch(address[] memory accounts, uint256[] memory ids)\n public\n view\n virtual\n override\n returns (uint256[] memory)\n {\n require(\n accounts.length == ids.length,\n 'ERC1155: accounts and ids length mismatch'\n );\n\n mapping(uint256 => mapping(address => uint256))\n storage balances = ERC1155BaseStorage.layout().balances;\n\n uint256[] memory batchBalances = new uint256[](accounts.length);\n\n unchecked {\n for (uint256 i; i < accounts.length; i++) {\n require(\n accounts[i] != address(0),\n 'ERC1155: batch balance query for the zero address'\n );\n batchBalances[i] = balances[ids[i]][accounts[i]];\n }\n }\n\n return batchBalances;\n }\n\n /**\n * @inheritdoc IERC1155\n */\n function isApprovedForAll(address account, address operator)\n public\n view\n virtual\n override\n returns (bool)\n {\n return ERC1155BaseStorage.layout().operatorApprovals[account][operator];\n }\n\n /**\n * @inheritdoc IERC1155\n */\n function setApprovalForAll(address operator, bool status)\n public\n virtual\n override\n {\n require(\n msg.sender != operator,\n 'ERC1155: setting approval status for self'\n );\n ERC1155BaseStorage.layout().operatorApprovals[msg.sender][\n operator\n ] = status;\n emit ApprovalForAll(msg.sender, operator, status);\n }\n\n /**\n * @inheritdoc IERC1155\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 id,\n uint256 amount,\n bytes memory data\n ) public virtual override {\n require(\n from == msg.sender || isApprovedForAll(from, msg.sender),\n 'ERC1155: caller is not owner nor approved'\n );\n _safeTransfer(msg.sender, from, to, id, amount, data);\n }\n\n /**\n * @inheritdoc IERC1155\n */\n function safeBatchTransferFrom(\n address from,\n address to,\n uint256[] memory ids,\n uint256[] memory amounts,\n bytes memory data\n ) public virtual override {\n require(\n from == msg.sender || isApprovedForAll(from, msg.sender),\n 'ERC1155: caller is not owner nor approved'\n );\n _safeTransferBatch(msg.sender, from, to, ids, amounts, data);\n }\n}\n" + }, + "@solidstate/contracts/introspection/IERC165.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @title ERC165 interface registration interface\n * @dev see https://eips.ethereum.org/EIPS/eip-165\n */\ninterface IERC165 {\n /**\n * @notice query whether contract has registered support for given interface\n * @param interfaceId interface id\n * @return bool whether interface is supported\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n" + }, + "abdk-libraries-solidity/ABDKMath64x64.sol": { + "content": "// SPDX-License-Identifier: BSD-4-Clause\n/*\n * ABDK Math 64.64 Smart Contract Library. Copyright © 2019 by ABDK Consulting.\n * Author: Mikhail Vladimirov \n */\npragma solidity ^0.8.0;\n\n/**\n * Smart contract library of mathematical functions operating with signed\n * 64.64-bit fixed point numbers. Signed 64.64-bit fixed point number is\n * basically a simple fraction whose numerator is signed 128-bit integer and\n * denominator is 2^64. As long as denominator is always the same, there is no\n * need to store it, thus in Solidity signed 64.64-bit fixed point numbers are\n * represented by int128 type holding only the numerator.\n */\nlibrary ABDKMath64x64 {\n /*\n * Minimum value signed 64.64-bit fixed point number may have. \n */\n int128 private constant MIN_64x64 = -0x80000000000000000000000000000000;\n\n /*\n * Maximum value signed 64.64-bit fixed point number may have. \n */\n int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;\n\n /**\n * Convert signed 256-bit integer number into signed 64.64-bit fixed point\n * number. Revert on overflow.\n *\n * @param x signed 256-bit integer number\n * @return signed 64.64-bit fixed point number\n */\n function fromInt (int256 x) internal pure returns (int128) {\n unchecked {\n require (x >= -0x8000000000000000 && x <= 0x7FFFFFFFFFFFFFFF);\n return int128 (x << 64);\n }\n }\n\n /**\n * Convert signed 64.64 fixed point number into signed 64-bit integer number\n * rounding down.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64-bit integer number\n */\n function toInt (int128 x) internal pure returns (int64) {\n unchecked {\n return int64 (x >> 64);\n }\n }\n\n /**\n * Convert unsigned 256-bit integer number into signed 64.64-bit fixed point\n * number. Revert on overflow.\n *\n * @param x unsigned 256-bit integer number\n * @return signed 64.64-bit fixed point number\n */\n function fromUInt (uint256 x) internal pure returns (int128) {\n unchecked {\n require (x <= 0x7FFFFFFFFFFFFFFF);\n return int128 (int256 (x << 64));\n }\n }\n\n /**\n * Convert signed 64.64 fixed point number into unsigned 64-bit integer\n * number rounding down. Revert on underflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @return unsigned 64-bit integer number\n */\n function toUInt (int128 x) internal pure returns (uint64) {\n unchecked {\n require (x >= 0);\n return uint64 (uint128 (x >> 64));\n }\n }\n\n /**\n * Convert signed 128.128 fixed point number into signed 64.64-bit fixed point\n * number rounding down. Revert on overflow.\n *\n * @param x signed 128.128-bin fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function from128x128 (int256 x) internal pure returns (int128) {\n unchecked {\n int256 result = x >> 64;\n require (result >= MIN_64x64 && result <= MAX_64x64);\n return int128 (result);\n }\n }\n\n /**\n * Convert signed 64.64 fixed point number into signed 128.128 fixed point\n * number.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 128.128 fixed point number\n */\n function to128x128 (int128 x) internal pure returns (int256) {\n unchecked {\n return int256 (x) << 64;\n }\n }\n\n /**\n * Calculate x + y. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @param y signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function add (int128 x, int128 y) internal pure returns (int128) {\n unchecked {\n int256 result = int256(x) + y;\n require (result >= MIN_64x64 && result <= MAX_64x64);\n return int128 (result);\n }\n }\n\n /**\n * Calculate x - y. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @param y signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function sub (int128 x, int128 y) internal pure returns (int128) {\n unchecked {\n int256 result = int256(x) - y;\n require (result >= MIN_64x64 && result <= MAX_64x64);\n return int128 (result);\n }\n }\n\n /**\n * Calculate x * y rounding down. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @param y signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function mul (int128 x, int128 y) internal pure returns (int128) {\n unchecked {\n int256 result = int256(x) * y >> 64;\n require (result >= MIN_64x64 && result <= MAX_64x64);\n return int128 (result);\n }\n }\n\n /**\n * Calculate x * y rounding towards zero, where x is signed 64.64 fixed point\n * number and y is signed 256-bit integer number. Revert on overflow.\n *\n * @param x signed 64.64 fixed point number\n * @param y signed 256-bit integer number\n * @return signed 256-bit integer number\n */\n function muli (int128 x, int256 y) internal pure returns (int256) {\n unchecked {\n if (x == MIN_64x64) {\n require (y >= -0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF &&\n y <= 0x1000000000000000000000000000000000000000000000000);\n return -y << 63;\n } else {\n bool negativeResult = false;\n if (x < 0) {\n x = -x;\n negativeResult = true;\n }\n if (y < 0) {\n y = -y; // We rely on overflow behavior here\n negativeResult = !negativeResult;\n }\n uint256 absoluteResult = mulu (x, uint256 (y));\n if (negativeResult) {\n require (absoluteResult <=\n 0x8000000000000000000000000000000000000000000000000000000000000000);\n return -int256 (absoluteResult); // We rely on overflow behavior here\n } else {\n require (absoluteResult <=\n 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);\n return int256 (absoluteResult);\n }\n }\n }\n }\n\n /**\n * Calculate x * y rounding down, where x is signed 64.64 fixed point number\n * and y is unsigned 256-bit integer number. Revert on overflow.\n *\n * @param x signed 64.64 fixed point number\n * @param y unsigned 256-bit integer number\n * @return unsigned 256-bit integer number\n */\n function mulu (int128 x, uint256 y) internal pure returns (uint256) {\n unchecked {\n if (y == 0) return 0;\n\n require (x >= 0);\n\n uint256 lo = (uint256 (int256 (x)) * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) >> 64;\n uint256 hi = uint256 (int256 (x)) * (y >> 128);\n\n require (hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);\n hi <<= 64;\n\n require (hi <=\n 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - lo);\n return hi + lo;\n }\n }\n\n /**\n * Calculate x / y rounding towards zero. Revert on overflow or when y is\n * zero.\n *\n * @param x signed 64.64-bit fixed point number\n * @param y signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function div (int128 x, int128 y) internal pure returns (int128) {\n unchecked {\n require (y != 0);\n int256 result = (int256 (x) << 64) / y;\n require (result >= MIN_64x64 && result <= MAX_64x64);\n return int128 (result);\n }\n }\n\n /**\n * Calculate x / y rounding towards zero, where x and y are signed 256-bit\n * integer numbers. Revert on overflow or when y is zero.\n *\n * @param x signed 256-bit integer number\n * @param y signed 256-bit integer number\n * @return signed 64.64-bit fixed point number\n */\n function divi (int256 x, int256 y) internal pure returns (int128) {\n unchecked {\n require (y != 0);\n\n bool negativeResult = false;\n if (x < 0) {\n x = -x; // We rely on overflow behavior here\n negativeResult = true;\n }\n if (y < 0) {\n y = -y; // We rely on overflow behavior here\n negativeResult = !negativeResult;\n }\n uint128 absoluteResult = divuu (uint256 (x), uint256 (y));\n if (negativeResult) {\n require (absoluteResult <= 0x80000000000000000000000000000000);\n return -int128 (absoluteResult); // We rely on overflow behavior here\n } else {\n require (absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);\n return int128 (absoluteResult); // We rely on overflow behavior here\n }\n }\n }\n\n /**\n * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit\n * integer numbers. Revert on overflow or when y is zero.\n *\n * @param x unsigned 256-bit integer number\n * @param y unsigned 256-bit integer number\n * @return signed 64.64-bit fixed point number\n */\n function divu (uint256 x, uint256 y) internal pure returns (int128) {\n unchecked {\n require (y != 0);\n uint128 result = divuu (x, y);\n require (result <= uint128 (MAX_64x64));\n return int128 (result);\n }\n }\n\n /**\n * Calculate -x. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function neg (int128 x) internal pure returns (int128) {\n unchecked {\n require (x != MIN_64x64);\n return -x;\n }\n }\n\n /**\n * Calculate |x|. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function abs (int128 x) internal pure returns (int128) {\n unchecked {\n require (x != MIN_64x64);\n return x < 0 ? -x : x;\n }\n }\n\n /**\n * Calculate 1 / x rounding towards zero. Revert on overflow or when x is\n * zero.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function inv (int128 x) internal pure returns (int128) {\n unchecked {\n require (x != 0);\n int256 result = int256 (0x100000000000000000000000000000000) / x;\n require (result >= MIN_64x64 && result <= MAX_64x64);\n return int128 (result);\n }\n }\n\n /**\n * Calculate arithmetics average of x and y, i.e. (x + y) / 2 rounding down.\n *\n * @param x signed 64.64-bit fixed point number\n * @param y signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function avg (int128 x, int128 y) internal pure returns (int128) {\n unchecked {\n return int128 ((int256 (x) + int256 (y)) >> 1);\n }\n }\n\n /**\n * Calculate geometric average of x and y, i.e. sqrt (x * y) rounding down.\n * Revert on overflow or in case x * y is negative.\n *\n * @param x signed 64.64-bit fixed point number\n * @param y signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function gavg (int128 x, int128 y) internal pure returns (int128) {\n unchecked {\n int256 m = int256 (x) * int256 (y);\n require (m >= 0);\n require (m <\n 0x4000000000000000000000000000000000000000000000000000000000000000);\n return int128 (sqrtu (uint256 (m)));\n }\n }\n\n /**\n * Calculate x^y assuming 0^0 is 1, where x is signed 64.64 fixed point number\n * and y is unsigned 256-bit integer number. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @param y uint256 value\n * @return signed 64.64-bit fixed point number\n */\n function pow (int128 x, uint256 y) internal pure returns (int128) {\n unchecked {\n bool negative = x < 0 && y & 1 == 1;\n\n uint256 absX = uint128 (x < 0 ? -x : x);\n uint256 absResult;\n absResult = 0x100000000000000000000000000000000;\n\n if (absX <= 0x10000000000000000) {\n absX <<= 63;\n while (y != 0) {\n if (y & 0x1 != 0) {\n absResult = absResult * absX >> 127;\n }\n absX = absX * absX >> 127;\n\n if (y & 0x2 != 0) {\n absResult = absResult * absX >> 127;\n }\n absX = absX * absX >> 127;\n\n if (y & 0x4 != 0) {\n absResult = absResult * absX >> 127;\n }\n absX = absX * absX >> 127;\n\n if (y & 0x8 != 0) {\n absResult = absResult * absX >> 127;\n }\n absX = absX * absX >> 127;\n\n y >>= 4;\n }\n\n absResult >>= 64;\n } else {\n uint256 absXShift = 63;\n if (absX < 0x1000000000000000000000000) { absX <<= 32; absXShift -= 32; }\n if (absX < 0x10000000000000000000000000000) { absX <<= 16; absXShift -= 16; }\n if (absX < 0x1000000000000000000000000000000) { absX <<= 8; absXShift -= 8; }\n if (absX < 0x10000000000000000000000000000000) { absX <<= 4; absXShift -= 4; }\n if (absX < 0x40000000000000000000000000000000) { absX <<= 2; absXShift -= 2; }\n if (absX < 0x80000000000000000000000000000000) { absX <<= 1; absXShift -= 1; }\n\n uint256 resultShift = 0;\n while (y != 0) {\n require (absXShift < 64);\n\n if (y & 0x1 != 0) {\n absResult = absResult * absX >> 127;\n resultShift += absXShift;\n if (absResult > 0x100000000000000000000000000000000) {\n absResult >>= 1;\n resultShift += 1;\n }\n }\n absX = absX * absX >> 127;\n absXShift <<= 1;\n if (absX >= 0x100000000000000000000000000000000) {\n absX >>= 1;\n absXShift += 1;\n }\n\n y >>= 1;\n }\n\n require (resultShift < 64);\n absResult >>= 64 - resultShift;\n }\n int256 result = negative ? -int256 (absResult) : int256 (absResult);\n require (result >= MIN_64x64 && result <= MAX_64x64);\n return int128 (result);\n }\n }\n\n /**\n * Calculate sqrt (x) rounding down. Revert if x < 0.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function sqrt (int128 x) internal pure returns (int128) {\n unchecked {\n require (x >= 0);\n return int128 (sqrtu (uint256 (int256 (x)) << 64));\n }\n }\n\n /**\n * Calculate binary logarithm of x. Revert if x <= 0.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function log_2 (int128 x) internal pure returns (int128) {\n unchecked {\n require (x > 0);\n\n int256 msb = 0;\n int256 xc = x;\n if (xc >= 0x10000000000000000) { xc >>= 64; msb += 64; }\n if (xc >= 0x100000000) { xc >>= 32; msb += 32; }\n if (xc >= 0x10000) { xc >>= 16; msb += 16; }\n if (xc >= 0x100) { xc >>= 8; msb += 8; }\n if (xc >= 0x10) { xc >>= 4; msb += 4; }\n if (xc >= 0x4) { xc >>= 2; msb += 2; }\n if (xc >= 0x2) msb += 1; // No need to shift xc anymore\n\n int256 result = msb - 64 << 64;\n uint256 ux = uint256 (int256 (x)) << uint256 (127 - msb);\n for (int256 bit = 0x8000000000000000; bit > 0; bit >>= 1) {\n ux *= ux;\n uint256 b = ux >> 255;\n ux >>= 127 + b;\n result += bit * int256 (b);\n }\n\n return int128 (result);\n }\n }\n\n /**\n * Calculate natural logarithm of x. Revert if x <= 0.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function ln (int128 x) internal pure returns (int128) {\n unchecked {\n require (x > 0);\n\n return int128 (int256 (\n uint256 (int256 (log_2 (x))) * 0xB17217F7D1CF79ABC9E3B39803F2F6AF >> 128));\n }\n }\n\n /**\n * Calculate binary exponent of x. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function exp_2 (int128 x) internal pure returns (int128) {\n unchecked {\n require (x < 0x400000000000000000); // Overflow\n\n if (x < -0x400000000000000000) return 0; // Underflow\n\n uint256 result = 0x80000000000000000000000000000000;\n\n if (x & 0x8000000000000000 > 0)\n result = result * 0x16A09E667F3BCC908B2FB1366EA957D3E >> 128;\n if (x & 0x4000000000000000 > 0)\n result = result * 0x1306FE0A31B7152DE8D5A46305C85EDEC >> 128;\n if (x & 0x2000000000000000 > 0)\n result = result * 0x1172B83C7D517ADCDF7C8C50EB14A791F >> 128;\n if (x & 0x1000000000000000 > 0)\n result = result * 0x10B5586CF9890F6298B92B71842A98363 >> 128;\n if (x & 0x800000000000000 > 0)\n result = result * 0x1059B0D31585743AE7C548EB68CA417FD >> 128;\n if (x & 0x400000000000000 > 0)\n result = result * 0x102C9A3E778060EE6F7CACA4F7A29BDE8 >> 128;\n if (x & 0x200000000000000 > 0)\n result = result * 0x10163DA9FB33356D84A66AE336DCDFA3F >> 128;\n if (x & 0x100000000000000 > 0)\n result = result * 0x100B1AFA5ABCBED6129AB13EC11DC9543 >> 128;\n if (x & 0x80000000000000 > 0)\n result = result * 0x10058C86DA1C09EA1FF19D294CF2F679B >> 128;\n if (x & 0x40000000000000 > 0)\n result = result * 0x1002C605E2E8CEC506D21BFC89A23A00F >> 128;\n if (x & 0x20000000000000 > 0)\n result = result * 0x100162F3904051FA128BCA9C55C31E5DF >> 128;\n if (x & 0x10000000000000 > 0)\n result = result * 0x1000B175EFFDC76BA38E31671CA939725 >> 128;\n if (x & 0x8000000000000 > 0)\n result = result * 0x100058BA01FB9F96D6CACD4B180917C3D >> 128;\n if (x & 0x4000000000000 > 0)\n result = result * 0x10002C5CC37DA9491D0985C348C68E7B3 >> 128;\n if (x & 0x2000000000000 > 0)\n result = result * 0x1000162E525EE054754457D5995292026 >> 128;\n if (x & 0x1000000000000 > 0)\n result = result * 0x10000B17255775C040618BF4A4ADE83FC >> 128;\n if (x & 0x800000000000 > 0)\n result = result * 0x1000058B91B5BC9AE2EED81E9B7D4CFAB >> 128;\n if (x & 0x400000000000 > 0)\n result = result * 0x100002C5C89D5EC6CA4D7C8ACC017B7C9 >> 128;\n if (x & 0x200000000000 > 0)\n result = result * 0x10000162E43F4F831060E02D839A9D16D >> 128;\n if (x & 0x100000000000 > 0)\n result = result * 0x100000B1721BCFC99D9F890EA06911763 >> 128;\n if (x & 0x80000000000 > 0)\n result = result * 0x10000058B90CF1E6D97F9CA14DBCC1628 >> 128;\n if (x & 0x40000000000 > 0)\n result = result * 0x1000002C5C863B73F016468F6BAC5CA2B >> 128;\n if (x & 0x20000000000 > 0)\n result = result * 0x100000162E430E5A18F6119E3C02282A5 >> 128;\n if (x & 0x10000000000 > 0)\n result = result * 0x1000000B1721835514B86E6D96EFD1BFE >> 128;\n if (x & 0x8000000000 > 0)\n result = result * 0x100000058B90C0B48C6BE5DF846C5B2EF >> 128;\n if (x & 0x4000000000 > 0)\n result = result * 0x10000002C5C8601CC6B9E94213C72737A >> 128;\n if (x & 0x2000000000 > 0)\n result = result * 0x1000000162E42FFF037DF38AA2B219F06 >> 128;\n if (x & 0x1000000000 > 0)\n result = result * 0x10000000B17217FBA9C739AA5819F44F9 >> 128;\n if (x & 0x800000000 > 0)\n result = result * 0x1000000058B90BFCDEE5ACD3C1CEDC823 >> 128;\n if (x & 0x400000000 > 0)\n result = result * 0x100000002C5C85FE31F35A6A30DA1BE50 >> 128;\n if (x & 0x200000000 > 0)\n result = result * 0x10000000162E42FF0999CE3541B9FFFCF >> 128;\n if (x & 0x100000000 > 0)\n result = result * 0x100000000B17217F80F4EF5AADDA45554 >> 128;\n if (x & 0x80000000 > 0)\n result = result * 0x10000000058B90BFBF8479BD5A81B51AD >> 128;\n if (x & 0x40000000 > 0)\n result = result * 0x1000000002C5C85FDF84BD62AE30A74CC >> 128;\n if (x & 0x20000000 > 0)\n result = result * 0x100000000162E42FEFB2FED257559BDAA >> 128;\n if (x & 0x10000000 > 0)\n result = result * 0x1000000000B17217F7D5A7716BBA4A9AE >> 128;\n if (x & 0x8000000 > 0)\n result = result * 0x100000000058B90BFBE9DDBAC5E109CCE >> 128;\n if (x & 0x4000000 > 0)\n result = result * 0x10000000002C5C85FDF4B15DE6F17EB0D >> 128;\n if (x & 0x2000000 > 0)\n result = result * 0x1000000000162E42FEFA494F1478FDE05 >> 128;\n if (x & 0x1000000 > 0)\n result = result * 0x10000000000B17217F7D20CF927C8E94C >> 128;\n if (x & 0x800000 > 0)\n result = result * 0x1000000000058B90BFBE8F71CB4E4B33D >> 128;\n if (x & 0x400000 > 0)\n result = result * 0x100000000002C5C85FDF477B662B26945 >> 128;\n if (x & 0x200000 > 0)\n result = result * 0x10000000000162E42FEFA3AE53369388C >> 128;\n if (x & 0x100000 > 0)\n result = result * 0x100000000000B17217F7D1D351A389D40 >> 128;\n if (x & 0x80000 > 0)\n result = result * 0x10000000000058B90BFBE8E8B2D3D4EDE >> 128;\n if (x & 0x40000 > 0)\n result = result * 0x1000000000002C5C85FDF4741BEA6E77E >> 128;\n if (x & 0x20000 > 0)\n result = result * 0x100000000000162E42FEFA39FE95583C2 >> 128;\n if (x & 0x10000 > 0)\n result = result * 0x1000000000000B17217F7D1CFB72B45E1 >> 128;\n if (x & 0x8000 > 0)\n result = result * 0x100000000000058B90BFBE8E7CC35C3F0 >> 128;\n if (x & 0x4000 > 0)\n result = result * 0x10000000000002C5C85FDF473E242EA38 >> 128;\n if (x & 0x2000 > 0)\n result = result * 0x1000000000000162E42FEFA39F02B772C >> 128;\n if (x & 0x1000 > 0)\n result = result * 0x10000000000000B17217F7D1CF7D83C1A >> 128;\n if (x & 0x800 > 0)\n result = result * 0x1000000000000058B90BFBE8E7BDCBE2E >> 128;\n if (x & 0x400 > 0)\n result = result * 0x100000000000002C5C85FDF473DEA871F >> 128;\n if (x & 0x200 > 0)\n result = result * 0x10000000000000162E42FEFA39EF44D91 >> 128;\n if (x & 0x100 > 0)\n result = result * 0x100000000000000B17217F7D1CF79E949 >> 128;\n if (x & 0x80 > 0)\n result = result * 0x10000000000000058B90BFBE8E7BCE544 >> 128;\n if (x & 0x40 > 0)\n result = result * 0x1000000000000002C5C85FDF473DE6ECA >> 128;\n if (x & 0x20 > 0)\n result = result * 0x100000000000000162E42FEFA39EF366F >> 128;\n if (x & 0x10 > 0)\n result = result * 0x1000000000000000B17217F7D1CF79AFA >> 128;\n if (x & 0x8 > 0)\n result = result * 0x100000000000000058B90BFBE8E7BCD6D >> 128;\n if (x & 0x4 > 0)\n result = result * 0x10000000000000002C5C85FDF473DE6B2 >> 128;\n if (x & 0x2 > 0)\n result = result * 0x1000000000000000162E42FEFA39EF358 >> 128;\n if (x & 0x1 > 0)\n result = result * 0x10000000000000000B17217F7D1CF79AB >> 128;\n\n result >>= uint256 (int256 (63 - (x >> 64)));\n require (result <= uint256 (int256 (MAX_64x64)));\n\n return int128 (int256 (result));\n }\n }\n\n /**\n * Calculate natural exponent of x. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function exp (int128 x) internal pure returns (int128) {\n unchecked {\n require (x < 0x400000000000000000); // Overflow\n\n if (x < -0x400000000000000000) return 0; // Underflow\n\n return exp_2 (\n int128 (int256 (x) * 0x171547652B82FE1777D0FFDA0D23A7D12 >> 128));\n }\n }\n\n /**\n * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit\n * integer numbers. Revert on overflow or when y is zero.\n *\n * @param x unsigned 256-bit integer number\n * @param y unsigned 256-bit integer number\n * @return unsigned 64.64-bit fixed point number\n */\n function divuu (uint256 x, uint256 y) private pure returns (uint128) {\n unchecked {\n require (y != 0);\n\n uint256 result;\n\n if (x <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)\n result = (x << 64) / y;\n else {\n uint256 msb = 192;\n uint256 xc = x >> 192;\n if (xc >= 0x100000000) { xc >>= 32; msb += 32; }\n if (xc >= 0x10000) { xc >>= 16; msb += 16; }\n if (xc >= 0x100) { xc >>= 8; msb += 8; }\n if (xc >= 0x10) { xc >>= 4; msb += 4; }\n if (xc >= 0x4) { xc >>= 2; msb += 2; }\n if (xc >= 0x2) msb += 1; // No need to shift xc anymore\n\n result = (x << 255 - msb) / ((y - 1 >> msb - 191) + 1);\n require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);\n\n uint256 hi = result * (y >> 128);\n uint256 lo = result * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);\n\n uint256 xh = x >> 192;\n uint256 xl = x << 64;\n\n if (xl < lo) xh -= 1;\n xl -= lo; // We rely on overflow behavior here\n lo = hi << 128;\n if (xl < lo) xh -= 1;\n xl -= lo; // We rely on overflow behavior here\n\n assert (xh == hi >> 128);\n\n result += xl / y;\n }\n\n require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);\n return uint128 (result);\n }\n }\n\n /**\n * Calculate sqrt (x) rounding down, where x is unsigned 256-bit integer\n * number.\n *\n * @param x unsigned 256-bit integer number\n * @return unsigned 128-bit integer number\n */\n function sqrtu (uint256 x) private pure returns (uint128) {\n unchecked {\n if (x == 0) return 0;\n else {\n uint256 xx = x;\n uint256 r = 1;\n if (xx >= 0x100000000000000000000000000000000) { xx >>= 128; r <<= 64; }\n if (xx >= 0x10000000000000000) { xx >>= 64; r <<= 32; }\n if (xx >= 0x100000000) { xx >>= 32; r <<= 16; }\n if (xx >= 0x10000) { xx >>= 16; r <<= 8; }\n if (xx >= 0x100) { xx >>= 8; r <<= 4; }\n if (xx >= 0x10) { xx >>= 4; r <<= 2; }\n if (xx >= 0x8) { r <<= 1; }\n r = (r + x / r) >> 1;\n r = (r + x / r) >> 1;\n r = (r + x / r) >> 1;\n r = (r + x / r) >> 1;\n r = (r + x / r) >> 1;\n r = (r + x / r) >> 1;\n r = (r + x / r) >> 1; // Seven iterations should be enough\n uint256 r1 = x / r;\n return uint128 (r < r1 ? r : r1);\n }\n }\n }\n}\n" + }, + "@solidstate/contracts/token/ERC20/metadata/IERC20Metadata.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @title ERC20 metadata interface\n */\ninterface IERC20Metadata {\n /**\n * @notice return token name\n * @return token name\n */\n function name() external view returns (string memory);\n\n /**\n * @notice return token symbol\n * @return token symbol\n */\n function symbol() external view returns (string memory);\n\n /**\n * @notice return token decimals, generally used only for display purposes\n * @return token decimals\n */\n function decimals() external view returns (uint8);\n}\n" + }, + "contracts/staking/FeeDiscountStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\r\n// For further clarification please see https://license.premia.legal\r\n\r\npragma solidity ^0.8.0;\r\n\r\nlibrary FeeDiscountStorage {\r\n bytes32 internal constant STORAGE_SLOT =\r\n keccak256(\"premia.contracts.staking.PremiaFeeDiscount\");\r\n\r\n struct UserInfo {\r\n uint256 balance; // Balance staked by user\r\n uint64 stakePeriod; // Stake period selected by user\r\n uint64 lockedUntil; // Timestamp at which the lock ends\r\n }\r\n\r\n struct Layout {\r\n // User data with xPREMIA balance staked and date at which lock ends\r\n mapping(address => UserInfo) userInfo;\r\n }\r\n\r\n function layout() internal pure returns (Layout storage l) {\r\n bytes32 slot = STORAGE_SLOT;\r\n assembly {\r\n l.slot := slot\r\n }\r\n }\r\n}\r\n" + }, + "contracts/libraries/OptionMath.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\r\n// For further clarification please see https://license.premia.legal\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport {ABDKMath64x64} from \"abdk-libraries-solidity/ABDKMath64x64.sol\";\r\n\r\nlibrary OptionMath {\r\n using ABDKMath64x64 for int128;\r\n\r\n struct QuoteArgs {\r\n int128 varianceAnnualized64x64; // 64x64 fixed point representation of annualized variance\r\n int128 strike64x64; // 64x64 fixed point representation of strike price\r\n int128 spot64x64; // 64x64 fixed point representation of spot price\r\n int128 timeToMaturity64x64; // 64x64 fixed point representation of duration of option contract (in years)\r\n int128 oldCLevel64x64; // 64x64 fixed point representation of C-Level of Pool before purchase\r\n int128 oldPoolState; // 64x64 fixed point representation of current state of the pool\r\n int128 newPoolState; // 64x64 fixed point representation of state of the pool after trade\r\n int128 steepness64x64; // 64x64 fixed point representation of Pool state delta multiplier\r\n int128 minAPY64x64; // 64x64 fixed point representation of minimum APY for capital locked up to underwrite options\r\n bool isCall; // whether to price \"call\" or \"put\" option\r\n }\r\n\r\n struct CalculateCLevelDecayArgs {\r\n int128 timeIntervalsElapsed64x64; // 64x64 fixed point representation of quantity of discrete arbitrary intervals elapsed since last update\r\n int128 oldCLevel64x64; // 64x64 fixed point representation of C-Level prior to accounting for decay\r\n int128 utilization64x64; // 64x64 fixed point representation of pool capital utilization rate\r\n int128 utilizationLowerBound64x64;\r\n int128 utilizationUpperBound64x64;\r\n int128 cLevelLowerBound64x64;\r\n int128 cLevelUpperBound64x64;\r\n int128 cConvergenceULowerBound64x64;\r\n int128 cConvergenceUUpperBound64x64;\r\n }\r\n\r\n // 64x64 fixed point integer constants\r\n int128 internal constant ONE_64x64 = 0x10000000000000000;\r\n int128 internal constant THREE_64x64 = 0x30000000000000000;\r\n\r\n // 64x64 fixed point constants used in Choudhury’s approximation of the Black-Scholes CDF\r\n int128 private constant CDF_CONST_0 = 0x09109f285df452394; // 2260 / 3989\r\n int128 private constant CDF_CONST_1 = 0x19abac0ea1da65036; // 6400 / 3989\r\n int128 private constant CDF_CONST_2 = 0x0d3c84b78b749bd6b; // 3300 / 3989\r\n\r\n /**\r\n * @notice recalculate C-Level based on change in liquidity\r\n * @param initialCLevel64x64 64x64 fixed point representation of C-Level of Pool before update\r\n * @param oldPoolState64x64 64x64 fixed point representation of liquidity in pool before update\r\n * @param newPoolState64x64 64x64 fixed point representation of liquidity in pool after update\r\n * @param steepness64x64 64x64 fixed point representation of steepness coefficient\r\n * @return 64x64 fixed point representation of new C-Level\r\n */\r\n function calculateCLevel(\r\n int128 initialCLevel64x64,\r\n int128 oldPoolState64x64,\r\n int128 newPoolState64x64,\r\n int128 steepness64x64\r\n ) external pure returns (int128) {\r\n return\r\n newPoolState64x64\r\n .sub(oldPoolState64x64)\r\n .div(\r\n oldPoolState64x64 > newPoolState64x64\r\n ? oldPoolState64x64\r\n : newPoolState64x64\r\n )\r\n .mul(steepness64x64)\r\n .neg()\r\n .exp()\r\n .mul(initialCLevel64x64);\r\n }\r\n\r\n /**\r\n * @notice calculate the price of an option using the Premia Finance model\r\n * @param args arguments of quotePrice\r\n * @return premiaPrice64x64 64x64 fixed point representation of Premia option price\r\n * @return cLevel64x64 64x64 fixed point representation of C-Level of Pool after purchase\r\n */\r\n function quotePrice(QuoteArgs memory args)\r\n external\r\n pure\r\n returns (\r\n int128 premiaPrice64x64,\r\n int128 cLevel64x64,\r\n int128 slippageCoefficient64x64\r\n )\r\n {\r\n int128 deltaPoolState64x64 = args\r\n .newPoolState\r\n .sub(args.oldPoolState)\r\n .div(args.oldPoolState)\r\n .mul(args.steepness64x64);\r\n int128 tradingDelta64x64 = deltaPoolState64x64.neg().exp();\r\n\r\n int128 blackScholesPrice64x64 = _blackScholesPrice(\r\n args.varianceAnnualized64x64,\r\n args.strike64x64,\r\n args.spot64x64,\r\n args.timeToMaturity64x64,\r\n args.isCall\r\n );\r\n\r\n cLevel64x64 = tradingDelta64x64.mul(args.oldCLevel64x64);\r\n slippageCoefficient64x64 = ONE_64x64.sub(tradingDelta64x64).div(\r\n deltaPoolState64x64\r\n );\r\n\r\n premiaPrice64x64 = blackScholesPrice64x64.mul(cLevel64x64).mul(\r\n slippageCoefficient64x64\r\n );\r\n\r\n int128 intrinsicValue64x64;\r\n\r\n if (args.isCall && args.strike64x64 < args.spot64x64) {\r\n intrinsicValue64x64 = args.spot64x64.sub(args.strike64x64);\r\n } else if (!args.isCall && args.strike64x64 > args.spot64x64) {\r\n intrinsicValue64x64 = args.strike64x64.sub(args.spot64x64);\r\n }\r\n\r\n int128 collateralValue64x64 = args.isCall\r\n ? args.spot64x64\r\n : args.strike64x64;\r\n\r\n int128 minPrice64x64 = intrinsicValue64x64.add(\r\n collateralValue64x64.mul(args.minAPY64x64).mul(\r\n args.timeToMaturity64x64\r\n )\r\n );\r\n\r\n if (minPrice64x64 > premiaPrice64x64) {\r\n premiaPrice64x64 = minPrice64x64;\r\n }\r\n }\r\n\r\n /**\r\n * @notice calculate the decay of C-Level based on heat diffusion function\r\n * @param args structured CalculateCLevelDecayArgs\r\n * @return cLevelDecayed64x64 C-Level after accounting for decay\r\n */\r\n function calculateCLevelDecay(CalculateCLevelDecayArgs memory args)\r\n external\r\n pure\r\n returns (int128 cLevelDecayed64x64)\r\n {\r\n int128 convFHighU64x64 = (args.utilization64x64 >=\r\n args.utilizationUpperBound64x64 &&\r\n args.oldCLevel64x64 <= args.cLevelLowerBound64x64)\r\n ? ONE_64x64\r\n : int128(0);\r\n\r\n int128 convFLowU64x64 = (args.utilization64x64 <=\r\n args.utilizationLowerBound64x64 &&\r\n args.oldCLevel64x64 >= args.cLevelUpperBound64x64)\r\n ? ONE_64x64\r\n : int128(0);\r\n\r\n cLevelDecayed64x64 = args\r\n .oldCLevel64x64\r\n .sub(args.cConvergenceULowerBound64x64.mul(convFLowU64x64))\r\n .sub(args.cConvergenceUUpperBound64x64.mul(convFHighU64x64))\r\n .mul(\r\n convFLowU64x64\r\n .mul(ONE_64x64.sub(args.utilization64x64))\r\n .add(convFHighU64x64.mul(args.utilization64x64))\r\n .mul(args.timeIntervalsElapsed64x64)\r\n .neg()\r\n .exp()\r\n )\r\n .add(\r\n args.cConvergenceULowerBound64x64.mul(convFLowU64x64).add(\r\n args.cConvergenceUUpperBound64x64.mul(convFHighU64x64)\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * @notice calculate the exponential decay coefficient for a given interval\r\n * @param oldTimestamp timestamp of previous update\r\n * @param newTimestamp current timestamp\r\n * @return 64x64 fixed point representation of exponential decay coefficient\r\n */\r\n function _decay(uint256 oldTimestamp, uint256 newTimestamp)\r\n internal\r\n pure\r\n returns (int128)\r\n {\r\n return\r\n ONE_64x64.sub(\r\n (-ABDKMath64x64.divu(newTimestamp - oldTimestamp, 7 days)).exp()\r\n );\r\n }\r\n\r\n /**\r\n * @notice calculate Choudhury’s approximation of the Black-Scholes CDF\r\n * @param input64x64 64x64 fixed point representation of random variable\r\n * @return 64x64 fixed point representation of the approximated CDF of x\r\n */\r\n function _N(int128 input64x64) internal pure returns (int128) {\r\n // squaring via mul is cheaper than via pow\r\n int128 inputSquared64x64 = input64x64.mul(input64x64);\r\n\r\n int128 value64x64 = (-inputSquared64x64 >> 1).exp().div(\r\n CDF_CONST_0.add(CDF_CONST_1.mul(input64x64.abs())).add(\r\n CDF_CONST_2.mul(inputSquared64x64.add(THREE_64x64).sqrt())\r\n )\r\n );\r\n\r\n return input64x64 > 0 ? ONE_64x64.sub(value64x64) : value64x64;\r\n }\r\n\r\n /**\r\n * @notice calculate the price of an option using the Black-Scholes model\r\n * @param varianceAnnualized64x64 64x64 fixed point representation of annualized variance\r\n * @param strike64x64 64x64 fixed point representation of strike price\r\n * @param spot64x64 64x64 fixed point representation of spot price\r\n * @param timeToMaturity64x64 64x64 fixed point representation of duration of option contract (in years)\r\n * @param isCall whether to price \"call\" or \"put\" option\r\n * @return 64x64 fixed point representation of Black-Scholes option price\r\n */\r\n function _blackScholesPrice(\r\n int128 varianceAnnualized64x64,\r\n int128 strike64x64,\r\n int128 spot64x64,\r\n int128 timeToMaturity64x64,\r\n bool isCall\r\n ) internal pure returns (int128) {\r\n int128 cumulativeVariance64x64 = timeToMaturity64x64.mul(\r\n varianceAnnualized64x64\r\n );\r\n int128 cumulativeVarianceSqrt64x64 = cumulativeVariance64x64.sqrt();\r\n\r\n int128 d1_64x64 = spot64x64\r\n .div(strike64x64)\r\n .ln()\r\n .add(cumulativeVariance64x64 >> 1)\r\n .div(cumulativeVarianceSqrt64x64);\r\n int128 d2_64x64 = d1_64x64.sub(cumulativeVarianceSqrt64x64);\r\n\r\n if (isCall) {\r\n return\r\n spot64x64.mul(_N(d1_64x64)).sub(strike64x64.mul(_N(d2_64x64)));\r\n } else {\r\n return\r\n -spot64x64.mul(_N(-d1_64x64)).sub(\r\n strike64x64.mul(_N(-d2_64x64))\r\n );\r\n }\r\n }\r\n}\r\n" + }, + "@solidstate/contracts/access/IERC173.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @title Contract ownership standard interface\n * @dev see https://eips.ethereum.org/EIPS/eip-173\n */\ninterface IERC173 {\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n\n /**\n * @notice get the ERC173 contract owner\n * @return conract owner\n */\n function owner() external view returns (address);\n\n /**\n * @notice transfer contract ownership to new account\n * @param account address of new owner\n */\n function transferOwnership(address account) external;\n}\n" + }, + "contracts/libraries/ABDKMath64x64Token.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\r\n// For further clarification please see https://license.premia.legal\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport {ABDKMath64x64} from \"abdk-libraries-solidity/ABDKMath64x64.sol\";\r\n\r\nlibrary ABDKMath64x64Token {\r\n using ABDKMath64x64 for int128;\r\n\r\n /**\r\n * @notice convert 64x64 fixed point representation of token amount to decimal\r\n * @param value64x64 64x64 fixed point representation of token amount\r\n * @param decimals token display decimals\r\n * @return value decimal representation of token amount\r\n */\r\n function toDecimals(int128 value64x64, uint8 decimals)\r\n internal\r\n pure\r\n returns (uint256 value)\r\n {\r\n value = value64x64.mulu(10**decimals);\r\n }\r\n\r\n /**\r\n * @notice convert decimal representation of token amount to 64x64 fixed point\r\n * @param value decimal representation of token amount\r\n * @param decimals token display decimals\r\n * @return value64x64 64x64 fixed point representation of token amount\r\n */\r\n function fromDecimals(uint256 value, uint8 decimals)\r\n internal\r\n pure\r\n returns (int128 value64x64)\r\n {\r\n value64x64 = ABDKMath64x64.divu(value, 10**decimals);\r\n }\r\n\r\n /**\r\n * @notice convert 64x64 fixed point representation of token amount to wei (18 decimals)\r\n * @param value64x64 64x64 fixed point representation of token amount\r\n * @return value wei representation of token amount\r\n */\r\n function toWei(int128 value64x64) internal pure returns (uint256 value) {\r\n value = toDecimals(value64x64, 18);\r\n }\r\n\r\n /**\r\n * @notice convert wei representation (18 decimals) of token amount to 64x64 fixed point\r\n * @param value wei representation of token amount\r\n * @return value64x64 64x64 fixed point representation of token amount\r\n */\r\n function fromWei(uint256 value) internal pure returns (int128 value64x64) {\r\n value64x64 = fromDecimals(value, 18);\r\n }\r\n}\r\n" + }, + "@solidstate/contracts/token/ERC1155/IERC1155.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC1155Internal } from './IERC1155Internal.sol';\nimport { IERC165 } from '../../introspection/IERC165.sol';\n\n/**\n * @notice ERC1155 interface\n * @dev see https://github.com/ethereum/EIPs/issues/1155\n */\ninterface IERC1155 is IERC1155Internal, IERC165 {\n /**\n * @notice query the balance of given token held by given address\n * @param account address to query\n * @param id token to query\n * @return token balance\n */\n function balanceOf(address account, uint256 id)\n external\n view\n returns (uint256);\n\n /**\n * @notice query the balances of given tokens held by given addresses\n * @param accounts addresss to query\n * @param ids tokens to query\n * @return token balances\n */\n function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)\n external\n view\n returns (uint256[] memory);\n\n /**\n * @notice query approval status of given operator with respect to given address\n * @param account address to query for approval granted\n * @param operator address to query for approval received\n * @return whether operator is approved to spend tokens held by account\n */\n function isApprovedForAll(address account, address operator)\n external\n view\n returns (bool);\n\n /**\n * @notice grant approval to or revoke approval from given operator to spend held tokens\n * @param operator address whose approval status to update\n * @param status whether operator should be considered approved\n */\n function setApprovalForAll(address operator, bool status) external;\n\n /**\n * @notice transfer tokens between given addresses, checking for ERC1155Receiver implementation if applicable\n * @param from sender of tokens\n * @param to receiver of tokens\n * @param id token ID\n * @param amount quantity of tokens to transfer\n * @param data data payload\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 id,\n uint256 amount,\n bytes calldata data\n ) external;\n\n /**\n * @notice transfer batch of tokens between given addresses, checking for ERC1155Receiver implementation if applicable\n * @param from sender of tokens\n * @param to receiver of tokens\n * @param ids list of token IDs\n * @param amounts list of quantities of tokens to transfer\n * @param data data payload\n */\n function safeBatchTransferFrom(\n address from,\n address to,\n uint256[] calldata ids,\n uint256[] calldata amounts,\n bytes calldata data\n ) external;\n}\n" + }, + "contracts/staking/IFeeDiscount.sol": { + "content": "// SPDX-License-Identifier: LGPL-3.0-or-later\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport {FeeDiscountStorage} from \"./FeeDiscountStorage.sol\";\r\n\r\ninterface IFeeDiscount {\r\n event Staked(\r\n address indexed user,\r\n uint256 amount,\r\n uint256 stakePeriod,\r\n uint256 lockedUntil\r\n );\r\n event Unstaked(address indexed user, uint256 amount);\r\n\r\n struct StakeLevel {\r\n uint256 amount; // Amount to stake\r\n uint256 discount; // Discount when amount is reached\r\n }\r\n\r\n /**\r\n * @notice Stake using IERC2612 permit\r\n * @param amount The amount of xPremia to stake\r\n * @param period The lockup period (in seconds)\r\n * @param deadline Deadline after which permit will fail\r\n * @param v V\r\n * @param r R\r\n * @param s S\r\n */\r\n function stakeWithPermit(\r\n uint256 amount,\r\n uint256 period,\r\n uint256 deadline,\r\n uint8 v,\r\n bytes32 r,\r\n bytes32 s\r\n ) external;\r\n\r\n /**\r\n * @notice Lockup xPremia for protocol fee discounts\r\n * Longer period of locking will apply a multiplier on the amount staked, in the fee discount calculation\r\n * @param amount The amount of xPremia to stake\r\n * @param period The lockup period (in seconds)\r\n */\r\n function stake(uint256 amount, uint256 period) external;\r\n\r\n /**\r\n * @notice Unstake xPremia (If lockup period has ended)\r\n * @param amount The amount of xPremia to unstake\r\n */\r\n function unstake(uint256 amount) external;\r\n\r\n //////////\r\n // View //\r\n //////////\r\n\r\n /**\r\n * Calculate the stake amount of a user, after applying the bonus from the lockup period chosen\r\n * @param user The user from which to query the stake amount\r\n * @return The user stake amount after applying the bonus\r\n */\r\n function getStakeAmountWithBonus(address user)\r\n external\r\n view\r\n returns (uint256);\r\n\r\n /**\r\n * @notice Calculate the % of fee discount for user, based on his stake\r\n * @param user The _user for which the discount is for\r\n * @return Percentage of protocol fee discount (in basis point)\r\n * Ex : 1000 = 10% fee discount\r\n */\r\n function getDiscount(address user) external view returns (uint256);\r\n\r\n /**\r\n * @notice Get stake levels\r\n * @return Stake levels\r\n * Ex : 2500 = -25%\r\n */\r\n function getStakeLevels() external returns (StakeLevel[] memory);\r\n\r\n /**\r\n * @notice Get stake period multiplier\r\n * @param period The duration (in seconds) for which tokens are locked\r\n * @return The multiplier for this staking period\r\n * Ex : 20000 = x2\r\n */\r\n function getStakePeriodMultiplier(uint256 period)\r\n external\r\n returns (uint256);\r\n\r\n /**\r\n * @notice Get staking infos of a user\r\n * @param user The user address for which to get staking infos\r\n * @return The staking infos of the user\r\n */\r\n function getUserInfo(address user)\r\n external\r\n view\r\n returns (FeeDiscountStorage.UserInfo memory);\r\n}\r\n" + }, + "@solidstate/contracts/token/ERC1155/base/ERC1155BaseInternal.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { AddressUtils } from '../../../utils/AddressUtils.sol';\nimport { IERC1155Internal } from '../IERC1155Internal.sol';\nimport { IERC1155Receiver } from '../IERC1155Receiver.sol';\nimport { ERC1155BaseStorage } from './ERC1155BaseStorage.sol';\n\n/**\n * @title Base ERC1155 internal functions\n * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts/ (MIT license)\n */\nabstract contract ERC1155BaseInternal is IERC1155Internal {\n using AddressUtils for address;\n\n /**\n * @notice query the balance of given token held by given address\n * @param account address to query\n * @param id token to query\n * @return token balance\n */\n function _balanceOf(address account, uint256 id)\n internal\n view\n virtual\n returns (uint256)\n {\n require(\n account != address(0),\n 'ERC1155: balance query for the zero address'\n );\n return ERC1155BaseStorage.layout().balances[id][account];\n }\n\n /**\n * @notice mint given quantity of tokens for given address\n * @dev ERC1155Receiver implementation is not checked\n * @param account beneficiary of minting\n * @param id token ID\n * @param amount quantity of tokens to mint\n * @param data data payload\n */\n function _mint(\n address account,\n uint256 id,\n uint256 amount,\n bytes memory data\n ) internal virtual {\n require(account != address(0), 'ERC1155: mint to the zero address');\n\n _beforeTokenTransfer(\n msg.sender,\n address(0),\n account,\n _asSingletonArray(id),\n _asSingletonArray(amount),\n data\n );\n\n mapping(address => uint256) storage balances = ERC1155BaseStorage\n .layout()\n .balances[id];\n balances[account] += amount;\n\n emit TransferSingle(msg.sender, address(0), account, id, amount);\n }\n\n /**\n * @notice mint given quantity of tokens for given address\n * @param account beneficiary of minting\n * @param id token ID\n * @param amount quantity of tokens to mint\n * @param data data payload\n */\n function _safeMint(\n address account,\n uint256 id,\n uint256 amount,\n bytes memory data\n ) internal virtual {\n _mint(account, id, amount, data);\n\n _doSafeTransferAcceptanceCheck(\n msg.sender,\n address(0),\n account,\n id,\n amount,\n data\n );\n }\n\n /**\n * @notice mint batch of tokens for given address\n * @dev ERC1155Receiver implementation is not checked\n * @param account beneficiary of minting\n * @param ids list of token IDs\n * @param amounts list of quantities of tokens to mint\n * @param data data payload\n */\n function _mintBatch(\n address account,\n uint256[] memory ids,\n uint256[] memory amounts,\n bytes memory data\n ) internal virtual {\n require(account != address(0), 'ERC1155: mint to the zero address');\n require(\n ids.length == amounts.length,\n 'ERC1155: ids and amounts length mismatch'\n );\n\n _beforeTokenTransfer(\n msg.sender,\n address(0),\n account,\n ids,\n amounts,\n data\n );\n\n mapping(uint256 => mapping(address => uint256))\n storage balances = ERC1155BaseStorage.layout().balances;\n\n for (uint256 i; i < ids.length; i++) {\n balances[ids[i]][account] += amounts[i];\n }\n\n emit TransferBatch(msg.sender, address(0), account, ids, amounts);\n }\n\n /**\n * @notice mint batch of tokens for given address\n * @param account beneficiary of minting\n * @param ids list of token IDs\n * @param amounts list of quantities of tokens to mint\n * @param data data payload\n */\n function _safeMintBatch(\n address account,\n uint256[] memory ids,\n uint256[] memory amounts,\n bytes memory data\n ) internal virtual {\n _mintBatch(account, ids, amounts, data);\n\n _doSafeBatchTransferAcceptanceCheck(\n msg.sender,\n address(0),\n account,\n ids,\n amounts,\n data\n );\n }\n\n /**\n * @notice burn given quantity of tokens held by given address\n * @param account holder of tokens to burn\n * @param id token ID\n * @param amount quantity of tokens to burn\n */\n function _burn(\n address account,\n uint256 id,\n uint256 amount\n ) internal virtual {\n require(account != address(0), 'ERC1155: burn from the zero address');\n\n _beforeTokenTransfer(\n msg.sender,\n account,\n address(0),\n _asSingletonArray(id),\n _asSingletonArray(amount),\n ''\n );\n\n mapping(address => uint256) storage balances = ERC1155BaseStorage\n .layout()\n .balances[id];\n\n unchecked {\n require(\n balances[account] >= amount,\n 'ERC1155: burn amount exceeds balances'\n );\n balances[account] -= amount;\n }\n\n emit TransferSingle(msg.sender, account, address(0), id, amount);\n }\n\n /**\n * @notice burn given batch of tokens held by given address\n * @param account holder of tokens to burn\n * @param ids token IDs\n * @param amounts quantities of tokens to burn\n */\n function _burnBatch(\n address account,\n uint256[] memory ids,\n uint256[] memory amounts\n ) internal virtual {\n require(account != address(0), 'ERC1155: burn from the zero address');\n require(\n ids.length == amounts.length,\n 'ERC1155: ids and amounts length mismatch'\n );\n\n _beforeTokenTransfer(msg.sender, account, address(0), ids, amounts, '');\n\n mapping(uint256 => mapping(address => uint256))\n storage balances = ERC1155BaseStorage.layout().balances;\n\n unchecked {\n for (uint256 i; i < ids.length; i++) {\n uint256 id = ids[i];\n require(\n balances[id][account] >= amounts[i],\n 'ERC1155: burn amount exceeds balance'\n );\n balances[id][account] -= amounts[i];\n }\n }\n\n emit TransferBatch(msg.sender, account, address(0), ids, amounts);\n }\n\n /**\n * @notice transfer tokens between given addresses\n * @dev ERC1155Receiver implementation is not checked\n * @param operator executor of transfer\n * @param sender sender of tokens\n * @param recipient receiver of tokens\n * @param id token ID\n * @param amount quantity of tokens to transfer\n * @param data data payload\n */\n function _transfer(\n address operator,\n address sender,\n address recipient,\n uint256 id,\n uint256 amount,\n bytes memory data\n ) internal virtual {\n require(\n recipient != address(0),\n 'ERC1155: transfer to the zero address'\n );\n\n _beforeTokenTransfer(\n operator,\n sender,\n recipient,\n _asSingletonArray(id),\n _asSingletonArray(amount),\n data\n );\n\n mapping(uint256 => mapping(address => uint256))\n storage balances = ERC1155BaseStorage.layout().balances;\n\n unchecked {\n uint256 senderBalance = balances[id][sender];\n require(\n senderBalance >= amount,\n 'ERC1155: insufficient balances for transfer'\n );\n balances[id][sender] = senderBalance - amount;\n }\n\n balances[id][recipient] += amount;\n\n emit TransferSingle(operator, sender, recipient, id, amount);\n }\n\n /**\n * @notice transfer tokens between given addresses\n * @param operator executor of transfer\n * @param sender sender of tokens\n * @param recipient receiver of tokens\n * @param id token ID\n * @param amount quantity of tokens to transfer\n * @param data data payload\n */\n function _safeTransfer(\n address operator,\n address sender,\n address recipient,\n uint256 id,\n uint256 amount,\n bytes memory data\n ) internal virtual {\n _transfer(operator, sender, recipient, id, amount, data);\n\n _doSafeTransferAcceptanceCheck(\n operator,\n sender,\n recipient,\n id,\n amount,\n data\n );\n }\n\n /**\n * @notice transfer batch of tokens between given addresses\n * @dev ERC1155Receiver implementation is not checked\n * @param operator executor of transfer\n * @param sender sender of tokens\n * @param recipient receiver of tokens\n * @param ids token IDs\n * @param amounts quantities of tokens to transfer\n * @param data data payload\n */\n function _transferBatch(\n address operator,\n address sender,\n address recipient,\n uint256[] memory ids,\n uint256[] memory amounts,\n bytes memory data\n ) internal virtual {\n require(\n recipient != address(0),\n 'ERC1155: transfer to the zero address'\n );\n require(\n ids.length == amounts.length,\n 'ERC1155: ids and amounts length mismatch'\n );\n\n _beforeTokenTransfer(operator, sender, recipient, ids, amounts, data);\n\n mapping(uint256 => mapping(address => uint256))\n storage balances = ERC1155BaseStorage.layout().balances;\n\n for (uint256 i; i < ids.length; i++) {\n uint256 token = ids[i];\n uint256 amount = amounts[i];\n\n unchecked {\n uint256 senderBalance = balances[token][sender];\n require(\n senderBalance >= amount,\n 'ERC1155: insufficient balances for transfer'\n );\n balances[token][sender] = senderBalance - amount;\n }\n\n balances[token][recipient] += amount;\n }\n\n emit TransferBatch(operator, sender, recipient, ids, amounts);\n }\n\n /**\n * @notice transfer batch of tokens between given addresses\n * @param operator executor of transfer\n * @param sender sender of tokens\n * @param recipient receiver of tokens\n * @param ids token IDs\n * @param amounts quantities of tokens to transfer\n * @param data data payload\n */\n function _safeTransferBatch(\n address operator,\n address sender,\n address recipient,\n uint256[] memory ids,\n uint256[] memory amounts,\n bytes memory data\n ) internal virtual {\n _transferBatch(operator, sender, recipient, ids, amounts, data);\n\n _doSafeBatchTransferAcceptanceCheck(\n operator,\n sender,\n recipient,\n ids,\n amounts,\n data\n );\n }\n\n /**\n * @notice wrap given element in array of length 1\n * @param element element to wrap\n * @return singleton array\n */\n function _asSingletonArray(uint256 element)\n private\n pure\n returns (uint256[] memory)\n {\n uint256[] memory array = new uint256[](1);\n array[0] = element;\n return array;\n }\n\n /**\n * @notice revert if applicable transfer recipient is not valid ERC1155Receiver\n * @param operator executor of transfer\n * @param from sender of tokens\n * @param to receiver of tokens\n * @param id token ID\n * @param amount quantity of tokens to transfer\n * @param data data payload\n */\n function _doSafeTransferAcceptanceCheck(\n address operator,\n address from,\n address to,\n uint256 id,\n uint256 amount,\n bytes memory data\n ) private {\n if (to.isContract()) {\n try\n IERC1155Receiver(to).onERC1155Received(\n operator,\n from,\n id,\n amount,\n data\n )\n returns (bytes4 response) {\n require(\n response == IERC1155Receiver.onERC1155Received.selector,\n 'ERC1155: ERC1155Receiver rejected tokens'\n );\n } catch Error(string memory reason) {\n revert(reason);\n } catch {\n revert('ERC1155: transfer to non ERC1155Receiver implementer');\n }\n }\n }\n\n /**\n * @notice revert if applicable transfer recipient is not valid ERC1155Receiver\n * @param operator executor of transfer\n * @param from sender of tokens\n * @param to receiver of tokens\n * @param ids token IDs\n * @param amounts quantities of tokens to transfer\n * @param data data payload\n */\n function _doSafeBatchTransferAcceptanceCheck(\n address operator,\n address from,\n address to,\n uint256[] memory ids,\n uint256[] memory amounts,\n bytes memory data\n ) private {\n if (to.isContract()) {\n try\n IERC1155Receiver(to).onERC1155BatchReceived(\n operator,\n from,\n ids,\n amounts,\n data\n )\n returns (bytes4 response) {\n require(\n response ==\n IERC1155Receiver.onERC1155BatchReceived.selector,\n 'ERC1155: ERC1155Receiver rejected tokens'\n );\n } catch Error(string memory reason) {\n revert(reason);\n } catch {\n revert('ERC1155: transfer to non ERC1155Receiver implementer');\n }\n }\n }\n\n /**\n * @notice ERC1155 hook, called before all transfers including mint and burn\n * @dev function should be overridden and new implementation must call super\n * @dev called for both single and batch transfers\n * @param operator executor of transfer\n * @param from sender of tokens\n * @param to receiver of tokens\n * @param ids token IDs\n * @param amounts quantities of tokens to transfer\n * @param data data payload\n */\n function _beforeTokenTransfer(\n address operator,\n address from,\n address to,\n uint256[] memory ids,\n uint256[] memory amounts,\n bytes memory data\n ) internal virtual {}\n}\n" + }, + "@solidstate/contracts/token/ERC20/IERC20Internal.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @title Partial ERC20 interface needed by internal functions\n */\ninterface IERC20Internal {\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n}\n" + }, + "@chainlink/contracts/src/v0.8/interfaces/AggregatorInterface.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AggregatorInterface {\n function latestAnswer()\n external\n view\n returns (\n int256\n );\n \n function latestTimestamp()\n external\n view\n returns (\n uint256\n );\n\n function latestRound()\n external\n view\n returns (\n uint256\n );\n\n function getAnswer(\n uint256 roundId\n )\n external\n view\n returns (\n int256\n );\n\n function getTimestamp(\n uint256 roundId\n )\n external\n view\n returns (\n uint256\n );\n\n event AnswerUpdated(\n int256 indexed current,\n uint256 indexed roundId,\n uint256 updatedAt\n );\n\n event NewRound(\n uint256 indexed roundId,\n address indexed startedBy,\n uint256 startedAt\n );\n}\n" + }, + "contracts/pool/PoolExercise.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\r\n// For further clarification please see https://license.premia.legal\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport {ERC1155BaseStorage} from \"@solidstate/contracts/token/ERC1155/base/ERC1155BaseStorage.sol\";\r\n\r\nimport {PoolInternal} from \"./PoolInternal.sol\";\r\nimport {IPoolExercise} from \"./IPoolExercise.sol\";\r\n\r\n/**\r\n * @title Premia option pool\r\n * @dev deployed standalone and referenced by PoolProxy\r\n */\r\ncontract PoolExercise is IPoolExercise, PoolInternal {\r\n constructor(\r\n address ivolOracle,\r\n address weth,\r\n address premiaMining,\r\n address feeReceiver,\r\n address feeDiscountAddress,\r\n int128 fee64x64\r\n )\r\n PoolInternal(\r\n ivolOracle,\r\n weth,\r\n premiaMining,\r\n feeReceiver,\r\n feeDiscountAddress,\r\n fee64x64\r\n )\r\n {}\r\n\r\n /**\r\n * @inheritdoc IPoolExercise\r\n */\r\n function exerciseFrom(\r\n address holder,\r\n uint256 longTokenId,\r\n uint256 contractSize\r\n ) external override {\r\n if (msg.sender != holder) {\r\n require(\r\n ERC1155BaseStorage.layout().operatorApprovals[holder][\r\n msg.sender\r\n ],\r\n \"not approved\"\r\n );\r\n }\r\n\r\n _exercise(holder, longTokenId, contractSize);\r\n }\r\n\r\n /**\r\n * @inheritdoc IPoolExercise\r\n */\r\n function processExpired(uint256 longTokenId, uint256 contractSize)\r\n external\r\n override\r\n {\r\n _exercise(address(0), longTokenId, contractSize);\r\n }\r\n}\r\n" + }, + "contracts/pool/IPoolExercise.sol": { + "content": "// SPDX-License-Identifier: LGPL-3.0-or-later\r\n\r\npragma solidity ^0.8.0;\r\n\r\n/**\r\n * @notice Pool interface for exercising and processing of expired options\r\n */\r\ninterface IPoolExercise {\r\n /**\r\n * @notice exercise option on behalf of holder\r\n * @param holder owner of long option tokens to exercise\r\n * @param longTokenId long option token id\r\n * @param contractSize quantity of tokens to exercise\r\n */\r\n function exerciseFrom(\r\n address holder,\r\n uint256 longTokenId,\r\n uint256 contractSize\r\n ) external;\r\n\r\n /**\r\n * @notice process expired option, freeing liquidity and distributing profits\r\n * @param longTokenId long option token id\r\n * @param contractSize quantity of tokens to process\r\n */\r\n function processExpired(uint256 longTokenId, uint256 contractSize) external;\r\n}\r\n" + }, + "contracts/pool/PoolStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\r\n// For further clarification please see https://license.premia.legal\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport {AggregatorInterface} from \"@chainlink/contracts/src/v0.8/interfaces/AggregatorInterface.sol\";\r\nimport {AggregatorV3Interface} from \"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\";\r\nimport {EnumerableSet, ERC1155EnumerableStorage} from \"@solidstate/contracts/token/ERC1155/enumerable/ERC1155EnumerableStorage.sol\";\r\n\r\nimport {ABDKMath64x64} from \"abdk-libraries-solidity/ABDKMath64x64.sol\";\r\nimport {ABDKMath64x64Token} from \"../libraries/ABDKMath64x64Token.sol\";\r\nimport {OptionMath} from \"../libraries/OptionMath.sol\";\r\n\r\nlibrary PoolStorage {\r\n using ABDKMath64x64 for int128;\r\n using PoolStorage for PoolStorage.Layout;\r\n\r\n enum TokenType {\r\n UNDERLYING_FREE_LIQ,\r\n BASE_FREE_LIQ,\r\n UNDERLYING_RESERVED_LIQ,\r\n BASE_RESERVED_LIQ,\r\n LONG_CALL,\r\n SHORT_CALL,\r\n LONG_PUT,\r\n SHORT_PUT\r\n }\r\n\r\n struct PoolSettings {\r\n address underlying;\r\n address base;\r\n address underlyingOracle;\r\n address baseOracle;\r\n }\r\n\r\n struct QuoteArgsInternal {\r\n address feePayer; // address of the fee payer\r\n uint64 maturity; // timestamp of option maturity\r\n int128 strike64x64; // 64x64 fixed point representation of strike price\r\n int128 spot64x64; // 64x64 fixed point representation of spot price\r\n uint256 contractSize; // size of option contract\r\n bool isCall; // true for call, false for put\r\n }\r\n\r\n struct QuoteResultInternal {\r\n int128 baseCost64x64; // 64x64 fixed point representation of option cost denominated in underlying currency (without fee)\r\n int128 feeCost64x64; // 64x64 fixed point representation of option fee cost denominated in underlying currency for call, or base currency for put\r\n int128 cLevel64x64; // 64x64 fixed point representation of C-Level of Pool after purchase\r\n int128 slippageCoefficient64x64; // 64x64 fixed point representation of slippage coefficient for given order size\r\n }\r\n\r\n struct BatchData {\r\n uint256 eta;\r\n uint256 totalPendingDeposits;\r\n }\r\n\r\n bytes32 internal constant STORAGE_SLOT =\r\n keccak256(\"premia.contracts.storage.Pool\");\r\n\r\n uint256 private constant C_DECAY_BUFFER = 12 hours;\r\n uint256 private constant C_DECAY_INTERVAL = 4 hours;\r\n\r\n struct Layout {\r\n // ERC20 token addresses\r\n address base;\r\n address underlying;\r\n // AggregatorV3Interface oracle addresses\r\n address baseOracle;\r\n address underlyingOracle;\r\n // token metadata\r\n uint8 underlyingDecimals;\r\n uint8 baseDecimals;\r\n // minimum amounts\r\n uint256 baseMinimum;\r\n uint256 underlyingMinimum;\r\n // deposit caps\r\n uint256 basePoolCap;\r\n uint256 underlyingPoolCap;\r\n // market state\r\n int128 _deprecated_steepness64x64;\r\n int128 cLevelBase64x64;\r\n int128 cLevelUnderlying64x64;\r\n uint256 cLevelBaseUpdatedAt;\r\n uint256 cLevelUnderlyingUpdatedAt;\r\n uint256 updatedAt;\r\n // User -> isCall -> depositedAt\r\n mapping(address => mapping(bool => uint256)) depositedAt;\r\n mapping(address => mapping(bool => uint256)) divestmentTimestamps;\r\n // doubly linked list of free liquidity intervals\r\n // isCall -> User -> User\r\n mapping(bool => mapping(address => address)) liquidityQueueAscending;\r\n mapping(bool => mapping(address => address)) liquidityQueueDescending;\r\n // minimum resolution price bucket => price\r\n mapping(uint256 => int128) bucketPrices64x64;\r\n // sequence id (minimum resolution price bucket / 256) => price update sequence\r\n mapping(uint256 => uint256) priceUpdateSequences;\r\n // isCall -> batch data\r\n mapping(bool => BatchData) nextDeposits;\r\n // user -> batch timestamp -> isCall -> pending amount\r\n mapping(address => mapping(uint256 => mapping(bool => uint256))) pendingDeposits;\r\n EnumerableSet.UintSet tokenIds;\r\n // user -> isCallPool -> total value locked of user (Used for liquidity mining)\r\n mapping(address => mapping(bool => uint256)) userTVL;\r\n // isCallPool -> total value locked\r\n mapping(bool => uint256) totalTVL;\r\n // steepness values\r\n int128 steepnessBase64x64;\r\n int128 steepnessUnderlying64x64;\r\n }\r\n\r\n function layout() internal pure returns (Layout storage l) {\r\n bytes32 slot = STORAGE_SLOT;\r\n assembly {\r\n l.slot := slot\r\n }\r\n }\r\n\r\n /**\r\n * @notice calculate ERC1155 token id for given option parameters\r\n * @param tokenType TokenType enum\r\n * @param maturity timestamp of option maturity\r\n * @param strike64x64 64x64 fixed point representation of strike price\r\n * @return tokenId token id\r\n */\r\n function formatTokenId(\r\n TokenType tokenType,\r\n uint64 maturity,\r\n int128 strike64x64\r\n ) internal pure returns (uint256 tokenId) {\r\n tokenId =\r\n (uint256(tokenType) << 248) +\r\n (uint256(maturity) << 128) +\r\n uint256(int256(strike64x64));\r\n }\r\n\r\n /**\r\n * @notice derive option maturity and strike price from ERC1155 token id\r\n * @param tokenId token id\r\n * @return tokenType TokenType enum\r\n * @return maturity timestamp of option maturity\r\n * @return strike64x64 option strike price\r\n */\r\n function parseTokenId(uint256 tokenId)\r\n internal\r\n pure\r\n returns (\r\n TokenType tokenType,\r\n uint64 maturity,\r\n int128 strike64x64\r\n )\r\n {\r\n assembly {\r\n tokenType := shr(248, tokenId)\r\n maturity := shr(128, tokenId)\r\n strike64x64 := tokenId\r\n }\r\n }\r\n\r\n function getTokenDecimals(Layout storage l, bool isCall)\r\n internal\r\n view\r\n returns (uint8 decimals)\r\n {\r\n decimals = isCall ? l.underlyingDecimals : l.baseDecimals;\r\n }\r\n\r\n /**\r\n * @notice get the total supply of free liquidity tokens, minus pending deposits\r\n * @param l storage layout struct\r\n * @param isCall whether query is for call or put pool\r\n * @return 64x64 fixed point representation of total free liquidity\r\n */\r\n function totalFreeLiquiditySupply64x64(Layout storage l, bool isCall)\r\n internal\r\n view\r\n returns (int128)\r\n {\r\n uint256 tokenId = formatTokenId(\r\n isCall ? TokenType.UNDERLYING_FREE_LIQ : TokenType.BASE_FREE_LIQ,\r\n 0,\r\n 0\r\n );\r\n\r\n return\r\n ABDKMath64x64Token.fromDecimals(\r\n ERC1155EnumerableStorage.layout().totalSupply[tokenId] -\r\n l.nextDeposits[isCall].totalPendingDeposits,\r\n l.getTokenDecimals(isCall)\r\n );\r\n }\r\n\r\n function getReinvestmentStatus(\r\n Layout storage l,\r\n address account,\r\n bool isCallPool\r\n ) internal view returns (bool) {\r\n uint256 timestamp = l.divestmentTimestamps[account][isCallPool];\r\n return timestamp == 0 || timestamp > block.timestamp;\r\n }\r\n\r\n function addUnderwriter(\r\n Layout storage l,\r\n address account,\r\n bool isCallPool\r\n ) internal {\r\n require(account != address(0));\r\n\r\n mapping(address => address) storage asc = l.liquidityQueueAscending[\r\n isCallPool\r\n ];\r\n mapping(address => address) storage desc = l.liquidityQueueDescending[\r\n isCallPool\r\n ];\r\n\r\n if (_isInQueue(account, asc, desc)) return;\r\n\r\n address last = desc[address(0)];\r\n\r\n asc[last] = account;\r\n desc[account] = last;\r\n desc[address(0)] = account;\r\n }\r\n\r\n function removeUnderwriter(\r\n Layout storage l,\r\n address account,\r\n bool isCallPool\r\n ) internal {\r\n require(account != address(0));\r\n\r\n mapping(address => address) storage asc = l.liquidityQueueAscending[\r\n isCallPool\r\n ];\r\n mapping(address => address) storage desc = l.liquidityQueueDescending[\r\n isCallPool\r\n ];\r\n\r\n if (!_isInQueue(account, asc, desc)) return;\r\n\r\n address prev = desc[account];\r\n address next = asc[account];\r\n asc[prev] = next;\r\n desc[next] = prev;\r\n delete asc[account];\r\n delete desc[account];\r\n }\r\n\r\n function isInQueue(\r\n Layout storage l,\r\n address account,\r\n bool isCallPool\r\n ) internal view returns (bool) {\r\n mapping(address => address) storage asc = l.liquidityQueueAscending[\r\n isCallPool\r\n ];\r\n mapping(address => address) storage desc = l.liquidityQueueDescending[\r\n isCallPool\r\n ];\r\n\r\n return _isInQueue(account, asc, desc);\r\n }\r\n\r\n function _isInQueue(\r\n address account,\r\n mapping(address => address) storage asc,\r\n mapping(address => address) storage desc\r\n ) private view returns (bool) {\r\n return asc[account] != address(0) || desc[address(0)] == account;\r\n }\r\n\r\n /**\r\n * @notice get current C-Level, without accounting for pending adjustments\r\n * @param l storage layout struct\r\n * @param isCall whether query is for call or put pool\r\n * @return cLevel64x64 64x64 fixed point representation of C-Level\r\n */\r\n function getRawCLevel64x64(Layout storage l, bool isCall)\r\n internal\r\n view\r\n returns (int128 cLevel64x64)\r\n {\r\n cLevel64x64 = isCall ? l.cLevelUnderlying64x64 : l.cLevelBase64x64;\r\n }\r\n\r\n /**\r\n * @notice get current C-Level, accounting for unrealized decay\r\n * @param l storage layout struct\r\n * @param isCall whether query is for call or put pool\r\n * @return cLevel64x64 64x64 fixed point representation of C-Level\r\n */\r\n function getDecayAdjustedCLevel64x64(Layout storage l, bool isCall)\r\n internal\r\n view\r\n returns (int128 cLevel64x64)\r\n {\r\n // get raw C-Level from storage\r\n cLevel64x64 = l.getRawCLevel64x64(isCall);\r\n\r\n // account for C-Level decay\r\n cLevel64x64 = l.applyCLevelDecayAdjustment(cLevel64x64, isCall);\r\n }\r\n\r\n /**\r\n * @notice calculate updated C-Level, accounting for unrealized decay\r\n * @param l storage layout struct\r\n * @param oldCLevel64x64 64x64 fixed point representation pool C-Level before accounting for decay\r\n * @param isCall whether query is for call or put pool\r\n * @return cLevel64x64 64x64 fixed point representation of C-Level of Pool after accounting for decay\r\n */\r\n function applyCLevelDecayAdjustment(\r\n Layout storage l,\r\n int128 oldCLevel64x64,\r\n bool isCall\r\n ) internal view returns (int128 cLevel64x64) {\r\n uint256 timeElapsed = block.timestamp -\r\n (isCall ? l.cLevelUnderlyingUpdatedAt : l.cLevelBaseUpdatedAt);\r\n\r\n // do not apply C decay if less than 24 hours have elapsed\r\n\r\n if (timeElapsed > C_DECAY_BUFFER) {\r\n timeElapsed -= C_DECAY_BUFFER;\r\n } else {\r\n return oldCLevel64x64;\r\n }\r\n\r\n int128 timeIntervalsElapsed64x64 = ABDKMath64x64.divu(\r\n timeElapsed,\r\n C_DECAY_INTERVAL\r\n );\r\n\r\n uint256 tokenId = formatTokenId(\r\n isCall ? TokenType.UNDERLYING_FREE_LIQ : TokenType.BASE_FREE_LIQ,\r\n 0,\r\n 0\r\n );\r\n\r\n uint256 tvl = l.totalTVL[isCall];\r\n\r\n int128 utilization = ABDKMath64x64.divu(\r\n tvl -\r\n (ERC1155EnumerableStorage.layout().totalSupply[tokenId] -\r\n l.nextDeposits[isCall].totalPendingDeposits),\r\n tvl\r\n );\r\n\r\n return\r\n OptionMath.calculateCLevelDecay(\r\n OptionMath.CalculateCLevelDecayArgs(\r\n timeIntervalsElapsed64x64,\r\n oldCLevel64x64,\r\n utilization,\r\n 0xb333333333333333, // 0.7\r\n 0xe666666666666666, // 0.9\r\n 0x10000000000000000, // 1.0\r\n 0x10000000000000000, // 1.0\r\n 0xe666666666666666, // 0.9\r\n 0x56fc2a2c515da32ea // 2e\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * @notice calculate updated C-Level, accounting for pending deposits\r\n * @param l storage layout struct\r\n * @param oldCLevel64x64 64x64 fixed point representation pool C-Level before accounting for liquidity change\r\n * @param oldLiquidity64x64 64x64 fixed point representation of previous liquidity\r\n * @param isCall whether to update C-Level for call or put pool\r\n * @return cLevel64x64 64x64 fixed point representation of C-Level\r\n * @return liquidity64x64 64x64 fixed point representation of new liquidity amount\r\n */\r\n function applyCLevelPendingDepositAdjustment(\r\n Layout storage l,\r\n int128 oldCLevel64x64,\r\n int128 oldLiquidity64x64,\r\n bool isCall\r\n ) internal view returns (int128 cLevel64x64, int128 liquidity64x64) {\r\n PoolStorage.BatchData storage batchData = l.nextDeposits[isCall];\r\n int128 pendingDeposits64x64;\r\n\r\n if (\r\n batchData.totalPendingDeposits > 0 &&\r\n batchData.eta != 0 &&\r\n block.timestamp >= batchData.eta\r\n ) {\r\n pendingDeposits64x64 = ABDKMath64x64Token.fromDecimals(\r\n batchData.totalPendingDeposits,\r\n l.getTokenDecimals(isCall)\r\n );\r\n\r\n liquidity64x64 = oldLiquidity64x64.add(pendingDeposits64x64);\r\n\r\n cLevel64x64 = l.applyCLevelLiquidityChangeAdjustment(\r\n oldCLevel64x64,\r\n oldLiquidity64x64,\r\n liquidity64x64,\r\n isCall\r\n );\r\n } else {\r\n cLevel64x64 = oldCLevel64x64;\r\n liquidity64x64 = oldLiquidity64x64;\r\n }\r\n }\r\n\r\n /**\r\n * @notice calculate updated C-Level, accounting for change in liquidity\r\n * @param l storage layout struct\r\n * @param oldCLevel64x64 64x64 fixed point representation pool C-Level before accounting for liquidity change\r\n * @param oldLiquidity64x64 64x64 fixed point representation of previous liquidity\r\n * @param newLiquidity64x64 64x64 fixed point representation of current liquidity\r\n * @param isCallPool whether to update C-Level for call or put pool\r\n * @return cLevel64x64 64x64 fixed point representation of C-Level\r\n */\r\n function applyCLevelLiquidityChangeAdjustment(\r\n Layout storage l,\r\n int128 oldCLevel64x64,\r\n int128 oldLiquidity64x64,\r\n int128 newLiquidity64x64,\r\n bool isCallPool\r\n ) internal view returns (int128 cLevel64x64) {\r\n int128 steepness64x64 = isCallPool\r\n ? l.steepnessUnderlying64x64\r\n : l.steepnessBase64x64;\r\n\r\n // fallback to deprecated storage value if side-specific value is not set\r\n if (steepness64x64 == 0) steepness64x64 = l._deprecated_steepness64x64;\r\n\r\n cLevel64x64 = OptionMath.calculateCLevel(\r\n oldCLevel64x64,\r\n oldLiquidity64x64,\r\n newLiquidity64x64,\r\n steepness64x64\r\n );\r\n\r\n if (cLevel64x64 < 0xb333333333333333) {\r\n cLevel64x64 = int128(0xb333333333333333); // 64x64 fixed point representation of 0.7\r\n }\r\n }\r\n\r\n /**\r\n * @notice set C-Level to arbitrary pre-calculated value\r\n * @param cLevel64x64 new C-Level of pool\r\n * @param isCallPool whether to update C-Level for call or put pool\r\n */\r\n function setCLevel(\r\n Layout storage l,\r\n int128 cLevel64x64,\r\n bool isCallPool\r\n ) internal {\r\n if (isCallPool) {\r\n l.cLevelUnderlying64x64 = cLevel64x64;\r\n l.cLevelUnderlyingUpdatedAt = block.timestamp;\r\n } else {\r\n l.cLevelBase64x64 = cLevel64x64;\r\n l.cLevelBaseUpdatedAt = block.timestamp;\r\n }\r\n }\r\n\r\n function setOracles(\r\n Layout storage l,\r\n address baseOracle,\r\n address underlyingOracle\r\n ) internal {\r\n require(\r\n AggregatorV3Interface(baseOracle).decimals() ==\r\n AggregatorV3Interface(underlyingOracle).decimals(),\r\n \"Pool: oracle decimals must match\"\r\n );\r\n\r\n l.baseOracle = baseOracle;\r\n l.underlyingOracle = underlyingOracle;\r\n }\r\n\r\n function fetchPriceUpdate(Layout storage l)\r\n internal\r\n view\r\n returns (int128 price64x64)\r\n {\r\n int256 priceUnderlying = AggregatorInterface(l.underlyingOracle)\r\n .latestAnswer();\r\n int256 priceBase = AggregatorInterface(l.baseOracle).latestAnswer();\r\n\r\n return ABDKMath64x64.divi(priceUnderlying, priceBase);\r\n }\r\n\r\n /**\r\n * @notice set price update for hourly bucket corresponding to given timestamp\r\n * @param l storage layout struct\r\n * @param timestamp timestamp to update\r\n * @param price64x64 64x64 fixed point representation of price\r\n */\r\n function setPriceUpdate(\r\n Layout storage l,\r\n uint256 timestamp,\r\n int128 price64x64\r\n ) internal {\r\n uint256 bucket = timestamp / (1 hours);\r\n l.bucketPrices64x64[bucket] = price64x64;\r\n l.priceUpdateSequences[bucket >> 8] += 1 << (255 - (bucket & 255));\r\n }\r\n\r\n /**\r\n * @notice get price update for hourly bucket corresponding to given timestamp\r\n * @param l storage layout struct\r\n * @param timestamp timestamp to query\r\n * @return 64x64 fixed point representation of price\r\n */\r\n function getPriceUpdate(Layout storage l, uint256 timestamp)\r\n internal\r\n view\r\n returns (int128)\r\n {\r\n return l.bucketPrices64x64[timestamp / (1 hours)];\r\n }\r\n\r\n /**\r\n * @notice get first price update available following given timestamp\r\n * @param l storage layout struct\r\n * @param timestamp timestamp to query\r\n * @return 64x64 fixed point representation of price\r\n */\r\n function getPriceUpdateAfter(Layout storage l, uint256 timestamp)\r\n internal\r\n view\r\n returns (int128)\r\n {\r\n // price updates are grouped into hourly buckets\r\n uint256 bucket = timestamp / (1 hours);\r\n // divide by 256 to get the index of the relevant price update sequence\r\n uint256 sequenceId = bucket >> 8;\r\n\r\n // get position within sequence relevant to current price update\r\n\r\n uint256 offset = bucket & 255;\r\n // shift to skip buckets from earlier in sequence\r\n uint256 sequence = (l.priceUpdateSequences[sequenceId] << offset) >>\r\n offset;\r\n\r\n // iterate through future sequences until a price update is found\r\n // sequence corresponding to current timestamp used as upper bound\r\n\r\n uint256 currentPriceUpdateSequenceId = block.timestamp / (256 hours);\r\n\r\n while (sequence == 0 && sequenceId <= currentPriceUpdateSequenceId) {\r\n sequence = l.priceUpdateSequences[++sequenceId];\r\n }\r\n\r\n // if no price update is found (sequence == 0) function will return 0\r\n // this should never occur, as each relevant external function triggers a price update\r\n\r\n // the most significant bit of the sequence corresponds to the offset of the relevant bucket\r\n\r\n uint256 msb;\r\n\r\n for (uint256 i = 128; i > 0; i >>= 1) {\r\n if (sequence >> i > 0) {\r\n msb += i;\r\n sequence >>= i;\r\n }\r\n }\r\n\r\n return l.bucketPrices64x64[((sequenceId + 1) << 8) - msb - 1];\r\n }\r\n\r\n function fromBaseToUnderlyingDecimals(Layout storage l, uint256 value)\r\n internal\r\n view\r\n returns (uint256)\r\n {\r\n int128 valueFixed64x64 = ABDKMath64x64Token.fromDecimals(\r\n value,\r\n l.baseDecimals\r\n );\r\n return\r\n ABDKMath64x64Token.toDecimals(\r\n valueFixed64x64,\r\n l.underlyingDecimals\r\n );\r\n }\r\n\r\n function fromUnderlyingToBaseDecimals(Layout storage l, uint256 value)\r\n internal\r\n view\r\n returns (uint256)\r\n {\r\n int128 valueFixed64x64 = ABDKMath64x64Token.fromDecimals(\r\n value,\r\n l.underlyingDecimals\r\n );\r\n return ABDKMath64x64Token.toDecimals(valueFixed64x64, l.baseDecimals);\r\n }\r\n}\r\n" + }, + "@solidstate/contracts/utils/AddressUtils.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nlibrary AddressUtils {\n function toString(address account) internal pure returns (string memory) {\n bytes32 value = bytes32(uint256(uint160(account)));\n bytes memory alphabet = '0123456789abcdef';\n bytes memory chars = new bytes(42);\n\n chars[0] = '0';\n chars[1] = 'x';\n\n for (uint256 i = 0; i < 20; i++) {\n chars[2 + i * 2] = alphabet[uint8(value[i + 12] >> 4)];\n chars[3 + i * 2] = alphabet[uint8(value[i + 12] & 0x0f)];\n }\n\n return string(chars);\n }\n\n function isContract(address account) internal view returns (bool) {\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n function sendValue(address payable account, uint256 amount) internal {\n (bool success, ) = account.call{ value: amount }('');\n require(success, 'AddressUtils: failed to send value');\n }\n\n function functionCall(address target, bytes memory data)\n internal\n returns (bytes memory)\n {\n return\n functionCall(target, data, 'AddressUtils: failed low-level call');\n }\n\n function functionCall(\n address target,\n bytes memory data,\n string memory error\n ) internal returns (bytes memory) {\n return _functionCallWithValue(target, data, 0, error);\n }\n\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return\n functionCallWithValue(\n target,\n data,\n value,\n 'AddressUtils: failed low-level call with value'\n );\n }\n\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory error\n ) internal returns (bytes memory) {\n require(\n address(this).balance >= value,\n 'AddressUtils: insufficient balance for call'\n );\n return _functionCallWithValue(target, data, value, error);\n }\n\n function _functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory error\n ) private returns (bytes memory) {\n require(\n isContract(target),\n 'AddressUtils: function call to non-contract'\n );\n\n (bool success, bytes memory returnData) = target.call{ value: value }(\n data\n );\n\n if (success) {\n return returnData;\n } else if (returnData.length > 0) {\n assembly {\n let returnData_size := mload(returnData)\n revert(add(32, returnData), returnData_size)\n }\n } else {\n revert(error);\n }\n }\n}\n" + }, + "@solidstate/contracts/token/ERC1155/enumerable/ERC1155Enumerable.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { EnumerableSet } from '../../../utils/EnumerableSet.sol';\nimport { ERC1155Base, ERC1155BaseInternal } from '../base/ERC1155Base.sol';\nimport { IERC1155Enumerable } from './IERC1155Enumerable.sol';\nimport { ERC1155EnumerableInternal, ERC1155EnumerableStorage } from './ERC1155EnumerableInternal.sol';\n\n/**\n * @title ERC1155 implementation including enumerable and aggregate functions\n */\nabstract contract ERC1155Enumerable is\n IERC1155Enumerable,\n ERC1155Base,\n ERC1155EnumerableInternal\n{\n using EnumerableSet for EnumerableSet.AddressSet;\n using EnumerableSet for EnumerableSet.UintSet;\n\n /**\n * @inheritdoc IERC1155Enumerable\n */\n function totalSupply(uint256 id)\n public\n view\n virtual\n override\n returns (uint256)\n {\n return ERC1155EnumerableStorage.layout().totalSupply[id];\n }\n\n /**\n * @inheritdoc IERC1155Enumerable\n */\n function totalHolders(uint256 id)\n public\n view\n virtual\n override\n returns (uint256)\n {\n return ERC1155EnumerableStorage.layout().accountsByToken[id].length();\n }\n\n /**\n * @inheritdoc IERC1155Enumerable\n */\n function accountsByToken(uint256 id)\n public\n view\n virtual\n override\n returns (address[] memory)\n {\n EnumerableSet.AddressSet storage accounts = ERC1155EnumerableStorage\n .layout()\n .accountsByToken[id];\n\n address[] memory addresses = new address[](accounts.length());\n\n for (uint256 i; i < accounts.length(); i++) {\n addresses[i] = accounts.at(i);\n }\n\n return addresses;\n }\n\n /**\n * @inheritdoc IERC1155Enumerable\n */\n function tokensByAccount(address account)\n public\n view\n virtual\n override\n returns (uint256[] memory)\n {\n EnumerableSet.UintSet storage tokens = ERC1155EnumerableStorage\n .layout()\n .tokensByAccount[account];\n\n uint256[] memory ids = new uint256[](tokens.length());\n\n for (uint256 i; i < tokens.length(); i++) {\n ids[i] = tokens.at(i);\n }\n\n return ids;\n }\n\n /**\n * @notice ERC1155 hook: update aggregate values\n * @inheritdoc ERC1155EnumerableInternal\n */\n function _beforeTokenTransfer(\n address operator,\n address from,\n address to,\n uint256[] memory ids,\n uint256[] memory amounts,\n bytes memory data\n )\n internal\n virtual\n override(ERC1155BaseInternal, ERC1155EnumerableInternal)\n {\n super._beforeTokenTransfer(operator, from, to, ids, amounts, data);\n }\n}\n" + }, + "@solidstate/contracts/token/ERC1155/IERC1155Receiver.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC165 } from '../../introspection/IERC165.sol';\n\n/**\n * @title ERC1155 transfer receiver interface\n */\ninterface IERC1155Receiver is IERC165 {\n /**\n * @notice validate receipt of ERC1155 transfer\n * @param operator executor of transfer\n * @param from sender of tokens\n * @param id token ID received\n * @param value quantity of tokens received\n * @param data data payload\n * @return function's own selector if transfer is accepted\n */\n function onERC1155Received(\n address operator,\n address from,\n uint256 id,\n uint256 value,\n bytes calldata data\n ) external returns (bytes4);\n\n /**\n * @notice validate receipt of ERC1155 batch transfer\n * @param operator executor of transfer\n * @param from sender of tokens\n * @param ids token IDs received\n * @param values quantities of tokens received\n * @param data data payload\n * @return function's own selector if transfer is accepted\n */\n function onERC1155BatchReceived(\n address operator,\n address from,\n uint256[] calldata ids,\n uint256[] calldata values,\n bytes calldata data\n ) external returns (bytes4);\n}\n" + }, + "contracts/pool/IPoolEvents.sol": { + "content": "// SPDX-License-Identifier: LGPL-3.0-or-later\r\n\r\npragma solidity ^0.8.0;\r\n\r\ninterface IPoolEvents {\r\n event Purchase(\r\n address indexed user,\r\n uint256 longTokenId,\r\n uint256 contractSize,\r\n uint256 baseCost,\r\n uint256 feeCost,\r\n int128 spot64x64\r\n );\r\n\r\n event Exercise(\r\n address indexed user,\r\n uint256 longTokenId,\r\n uint256 contractSize,\r\n uint256 exerciseValue,\r\n uint256 fee\r\n );\r\n\r\n event Underwrite(\r\n address indexed underwriter,\r\n address indexed longReceiver,\r\n uint256 shortTokenId,\r\n uint256 intervalContractSize,\r\n uint256 intervalPremium,\r\n bool isManualUnderwrite\r\n );\r\n\r\n event AssignExercise(\r\n address indexed underwriter,\r\n uint256 shortTokenId,\r\n uint256 freedAmount,\r\n uint256 intervalContractSize,\r\n uint256 fee\r\n );\r\n\r\n event Deposit(address indexed user, bool isCallPool, uint256 amount);\r\n\r\n event Withdrawal(\r\n address indexed user,\r\n bool isCallPool,\r\n uint256 depositedAt,\r\n uint256 amount\r\n );\r\n\r\n event FeeWithdrawal(bool indexed isCallPool, uint256 amount);\r\n\r\n event Annihilate(uint256 shortTokenId, uint256 amount);\r\n\r\n event UpdateCLevel(\r\n bool indexed isCall,\r\n int128 cLevel64x64,\r\n int128 oldLiquidity64x64,\r\n int128 newLiquidity64x64\r\n );\r\n\r\n event UpdateSteepness(int128 steepness64x64, bool isCallPool);\r\n}\r\n" + }, + "contracts/oracle/VolatilitySurfaceOracleStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\r\n// For further clarification please see https://license.premia.legal\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport {EnumerableSet} from \"@solidstate/contracts/utils/EnumerableSet.sol\";\r\n\r\nlibrary VolatilitySurfaceOracleStorage {\r\n bytes32 internal constant STORAGE_SLOT =\r\n keccak256(\"premia.contracts.storage.VolatilitySurfaceOracle\");\r\n\r\n uint256 internal constant COEFF_BITS = 51;\r\n uint256 internal constant COEFF_BITS_MINUS_ONE = 50;\r\n uint256 internal constant COEFF_AMOUNT = 5;\r\n // START_BIT = COEFF_BITS * (COEFF_AMOUNT - 1)\r\n uint256 internal constant START_BIT = 204;\r\n\r\n struct Update {\r\n uint256 updatedAt;\r\n bytes32 callCoefficients;\r\n bytes32 putCoefficients;\r\n }\r\n\r\n struct Layout {\r\n // Base token -> Underlying token -> Update\r\n mapping(address => mapping(address => Update)) volatilitySurfaces;\r\n // Relayer addresses which can be trusted to provide accurate option trades\r\n EnumerableSet.AddressSet whitelistedRelayers;\r\n }\r\n\r\n function layout() internal pure returns (Layout storage l) {\r\n bytes32 slot = STORAGE_SLOT;\r\n assembly {\r\n l.slot := slot\r\n }\r\n }\r\n\r\n function getCoefficients(\r\n Layout storage l,\r\n address baseToken,\r\n address underlyingToken,\r\n bool isCall\r\n ) internal view returns (bytes32) {\r\n Update storage u = l.volatilitySurfaces[baseToken][underlyingToken];\r\n return isCall ? u.callCoefficients : u.putCoefficients;\r\n }\r\n\r\n function parseVolatilitySurfaceCoefficients(bytes32 input)\r\n internal\r\n pure\r\n returns (int256[] memory coefficients)\r\n {\r\n coefficients = new int256[](COEFF_AMOUNT);\r\n\r\n // Value to add to negative numbers to cast them to int256\r\n int256 toAdd = (int256(-1) >> COEFF_BITS) << COEFF_BITS;\r\n\r\n assembly {\r\n let i := 0\r\n // Value equal to -1\r\n let mid := shl(COEFF_BITS_MINUS_ONE, 1)\r\n\r\n for {\r\n\r\n } lt(i, COEFF_AMOUNT) {\r\n\r\n } {\r\n let offset := sub(START_BIT, mul(COEFF_BITS, i))\r\n let coeff := shr(\r\n offset,\r\n sub(\r\n input,\r\n shl(\r\n add(offset, COEFF_BITS),\r\n shr(add(offset, COEFF_BITS), input)\r\n )\r\n )\r\n )\r\n\r\n // Check if value is a negative number and needs casting\r\n if or(eq(coeff, mid), gt(coeff, mid)) {\r\n coeff := add(coeff, toAdd)\r\n }\r\n\r\n // Store result in the coefficients array\r\n mstore(add(coefficients, add(0x20, mul(0x20, i))), coeff)\r\n\r\n i := add(i, 1)\r\n }\r\n }\r\n }\r\n\r\n function formatVolatilitySurfaceCoefficients(int256[5] memory coefficients)\r\n internal\r\n pure\r\n returns (bytes32 result)\r\n {\r\n for (uint256 i = 0; i < COEFF_AMOUNT; i++) {\r\n int256 max = int256(1 << COEFF_BITS_MINUS_ONE);\r\n require(\r\n coefficients[i] < max && coefficients[i] > -max,\r\n \"Out of bounds\"\r\n );\r\n }\r\n\r\n assembly {\r\n let i := 0\r\n\r\n for {\r\n\r\n } lt(i, COEFF_AMOUNT) {\r\n\r\n } {\r\n let offset := sub(START_BIT, mul(COEFF_BITS, i))\r\n let coeff := mload(add(coefficients, mul(0x20, i)))\r\n\r\n result := add(\r\n result,\r\n shl(\r\n offset,\r\n sub(coeff, shl(COEFF_BITS, shr(COEFF_BITS, coeff)))\r\n )\r\n )\r\n\r\n i := add(i, 1)\r\n }\r\n }\r\n }\r\n}\r\n" + }, + "@solidstate/contracts/token/ERC1155/enumerable/IERC1155Enumerable.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @title ERC1155 enumerable and aggregate function interface\n */\ninterface IERC1155Enumerable {\n /**\n * @notice query total minted supply of given token\n * @param id token id to query\n * @return token supply\n */\n function totalSupply(uint256 id) external view returns (uint256);\n\n /**\n * @notice query total number of holders for given token\n * @param id token id to query\n * @return quantity of holders\n */\n function totalHolders(uint256 id) external view returns (uint256);\n\n /**\n * @notice query holders of given token\n * @param id token id to query\n * @return list of holder addresses\n */\n function accountsByToken(uint256 id)\n external\n view\n returns (address[] memory);\n\n /**\n * @notice query tokens held by given address\n * @param account address to query\n * @return list of token ids\n */\n function tokensByAccount(address account)\n external\n view\n returns (uint256[] memory);\n}\n" + }, + "@solidstate/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC20Internal } from './IERC20Internal.sol';\n\n/**\n * @title ERC20 interface\n * @dev see https://github.com/ethereum/EIPs/issues/20\n */\ninterface IERC20 is IERC20Internal {\n /**\n * @notice query the total minted token supply\n * @return token supply\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @notice query the token balance of given account\n * @param account address to query\n * @return token balance\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @notice query the allowance granted from given holder to given spender\n * @param holder approver of allowance\n * @param spender recipient of allowance\n * @return token allowance\n */\n function allowance(address holder, address spender)\n external\n view\n returns (uint256);\n\n /**\n * @notice grant approval to spender to spend tokens\n * @dev prefer ERC20Extended functions to avoid transaction-ordering vulnerability (see https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729)\n * @param spender recipient of allowance\n * @param amount quantity of tokens approved for spending\n * @return success status (always true; otherwise function should revert)\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @notice transfer tokens to given recipient\n * @param recipient beneficiary of token transfer\n * @param amount quantity of tokens to transfer\n * @return success status (always true; otherwise function should revert)\n */\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n /**\n * @notice transfer tokens to given recipient on behalf of given holder\n * @param holder holder of tokens prior to transfer\n * @param recipient beneficiary of token transfer\n * @param amount quantity of tokens to transfer\n * @return success status (always true; otherwise function should revert)\n */\n function transferFrom(\n address holder,\n address recipient,\n uint256 amount\n ) external returns (bool);\n}\n" + }, + "contracts/mining/IPremiaMining.sol": { + "content": "// SPDX-License-Identifier: LGPL-3.0-or-later\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport {PremiaMiningStorage} from \"./PremiaMiningStorage.sol\";\r\n\r\ninterface IPremiaMining {\r\n function addPremiaRewards(uint256 _amount) external;\r\n\r\n function premiaRewardsAvailable() external view returns (uint256);\r\n\r\n function getTotalAllocationPoints() external view returns (uint256);\r\n\r\n function getPoolInfo(address pool, bool isCallPool)\r\n external\r\n view\r\n returns (PremiaMiningStorage.PoolInfo memory);\r\n\r\n function getPremiaPerYear() external view returns (uint256);\r\n\r\n function addPool(address _pool, uint256 _allocPoints) external;\r\n\r\n function setPoolAllocPoints(\r\n address[] memory _pools,\r\n uint256[] memory _allocPoints\r\n ) external;\r\n\r\n function pendingPremia(\r\n address _pool,\r\n bool _isCallPool,\r\n address _user\r\n ) external view returns (uint256);\r\n\r\n function updatePool(\r\n address _pool,\r\n bool _isCallPool,\r\n uint256 _totalTVL\r\n ) external;\r\n\r\n function allocatePending(\r\n address _user,\r\n address _pool,\r\n bool _isCallPool,\r\n uint256 _userTVLOld,\r\n uint256 _userTVLNew,\r\n uint256 _totalTVL\r\n ) external;\r\n\r\n function claim(\r\n address _user,\r\n address _pool,\r\n bool _isCallPool,\r\n uint256 _userTVLOld,\r\n uint256 _userTVLNew,\r\n uint256 _totalTVL\r\n ) external;\r\n}\r\n" + }, + "@solidstate/contracts/token/ERC1155/enumerable/ERC1155EnumerableStorage.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { EnumerableSet } from '../../../utils/EnumerableSet.sol';\n\nlibrary ERC1155EnumerableStorage {\n struct Layout {\n mapping(uint256 => uint256) totalSupply;\n mapping(uint256 => EnumerableSet.AddressSet) accountsByToken;\n mapping(address => EnumerableSet.UintSet) tokensByAccount;\n }\n\n bytes32 internal constant STORAGE_SLOT =\n keccak256('solidstate.contracts.storage.ERC1155Enumerable');\n\n function layout() internal pure returns (Layout storage l) {\n bytes32 slot = STORAGE_SLOT;\n assembly {\n l.slot := slot\n }\n }\n}\n" + }, + "@solidstate/contracts/token/ERC1155/enumerable/ERC1155EnumerableInternal.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { EnumerableSet } from '../../../utils/EnumerableSet.sol';\nimport { ERC1155BaseInternal, ERC1155BaseStorage } from '../base/ERC1155BaseInternal.sol';\nimport { ERC1155EnumerableStorage } from './ERC1155EnumerableStorage.sol';\n\n/**\n * @title ERC1155Enumerable internal functions\n */\nabstract contract ERC1155EnumerableInternal is ERC1155BaseInternal {\n using EnumerableSet for EnumerableSet.AddressSet;\n using EnumerableSet for EnumerableSet.UintSet;\n\n /**\n * @notice ERC1155 hook: update aggregate values\n * @inheritdoc ERC1155BaseInternal\n */\n function _beforeTokenTransfer(\n address operator,\n address from,\n address to,\n uint256[] memory ids,\n uint256[] memory amounts,\n bytes memory data\n ) internal virtual override {\n super._beforeTokenTransfer(operator, from, to, ids, amounts, data);\n\n if (from != to) {\n ERC1155EnumerableStorage.Layout storage l = ERC1155EnumerableStorage\n .layout();\n mapping(uint256 => EnumerableSet.AddressSet)\n storage tokenAccounts = l.accountsByToken;\n EnumerableSet.UintSet storage fromTokens = l.tokensByAccount[from];\n EnumerableSet.UintSet storage toTokens = l.tokensByAccount[to];\n\n for (uint256 i; i < ids.length; i++) {\n uint256 amount = amounts[i];\n\n if (amount > 0) {\n uint256 id = ids[i];\n\n if (from == address(0)) {\n l.totalSupply[id] += amount;\n } else if (_balanceOf(from, id) == amount) {\n tokenAccounts[id].remove(from);\n fromTokens.remove(id);\n }\n\n if (to == address(0)) {\n l.totalSupply[id] -= amount;\n } else if (_balanceOf(to, id) == 0) {\n tokenAccounts[id].add(to);\n toTokens.add(id);\n }\n }\n }\n }\n }\n}\n" + }, + "contracts/oracle/IVolatilitySurfaceOracle.sol": { + "content": "// SPDX-License-Identifier: LGPL-3.0-or-later\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport {VolatilitySurfaceOracleStorage} from \"./VolatilitySurfaceOracleStorage.sol\";\r\n\r\ninterface IVolatilitySurfaceOracle {\r\n function getWhitelistedRelayers() external view returns (address[] memory);\r\n\r\n function getVolatilitySurface(address baseToken, address underlyingToken)\r\n external\r\n view\r\n returns (VolatilitySurfaceOracleStorage.Update memory);\r\n\r\n function getVolatilitySurfaceCoefficientsUnpacked(\r\n address baseToken,\r\n address underlyingToken,\r\n bool isCall\r\n ) external view returns (int256[] memory);\r\n\r\n function getTimeToMaturity64x64(uint64 maturity)\r\n external\r\n view\r\n returns (int128);\r\n\r\n function getAnnualizedVolatility64x64(\r\n address baseToken,\r\n address underlyingToken,\r\n int128 spot64x64,\r\n int128 strike64x64,\r\n int128 timeToMaturity64x64,\r\n bool isCall\r\n ) external view returns (int128);\r\n\r\n function getBlackScholesPrice64x64(\r\n address baseToken,\r\n address underlyingToken,\r\n int128 strike64x64,\r\n int128 spot64x64,\r\n int128 timeToMaturity64x64,\r\n bool isCall\r\n ) external view returns (int128);\r\n\r\n function getBlackScholesPrice(\r\n address baseToken,\r\n address underlyingToken,\r\n int128 strike64x64,\r\n int128 spot64x64,\r\n int128 timeToMaturity64x64,\r\n bool isCall\r\n ) external view returns (uint256);\r\n}\r\n" + }, + "contracts/pool/PoolInternal.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\r\n// For further clarification please see https://license.premia.legal\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport {IERC173} from \"@solidstate/contracts/access/IERC173.sol\";\r\nimport {OwnableStorage} from \"@solidstate/contracts/access/OwnableStorage.sol\";\r\nimport {IERC20} from \"@solidstate/contracts/token/ERC20/IERC20.sol\";\r\nimport {ERC1155EnumerableInternal, ERC1155EnumerableStorage, EnumerableSet} from \"@solidstate/contracts/token/ERC1155/enumerable/ERC1155Enumerable.sol\";\r\nimport {IWETH} from \"@solidstate/contracts/utils/IWETH.sol\";\r\n\r\nimport {PoolStorage} from \"./PoolStorage.sol\";\r\n\r\nimport {ABDKMath64x64} from \"abdk-libraries-solidity/ABDKMath64x64.sol\";\r\nimport {ABDKMath64x64Token} from \"../libraries/ABDKMath64x64Token.sol\";\r\nimport {OptionMath} from \"../libraries/OptionMath.sol\";\r\nimport {IFeeDiscount} from \"../staking/IFeeDiscount.sol\";\r\nimport {IPoolEvents} from \"./IPoolEvents.sol\";\r\nimport {IPremiaMining} from \"../mining/IPremiaMining.sol\";\r\nimport {IVolatilitySurfaceOracle} from \"../oracle/IVolatilitySurfaceOracle.sol\";\r\n\r\n/**\r\n * @title Premia option pool\r\n * @dev deployed standalone and referenced by PoolProxy\r\n */\r\ncontract PoolInternal is IPoolEvents, ERC1155EnumerableInternal {\r\n using ABDKMath64x64 for int128;\r\n using EnumerableSet for EnumerableSet.AddressSet;\r\n using EnumerableSet for EnumerableSet.UintSet;\r\n using PoolStorage for PoolStorage.Layout;\r\n\r\n address internal immutable WETH_ADDRESS;\r\n address internal immutable PREMIA_MINING_ADDRESS;\r\n address internal immutable FEE_RECEIVER_ADDRESS;\r\n address internal immutable FEE_DISCOUNT_ADDRESS;\r\n address internal immutable IVOL_ORACLE_ADDRESS;\r\n\r\n int128 internal immutable FEE_64x64;\r\n\r\n uint256 internal immutable UNDERLYING_FREE_LIQ_TOKEN_ID;\r\n uint256 internal immutable BASE_FREE_LIQ_TOKEN_ID;\r\n\r\n uint256 internal immutable UNDERLYING_RESERVED_LIQ_TOKEN_ID;\r\n uint256 internal immutable BASE_RESERVED_LIQ_TOKEN_ID;\r\n\r\n uint256 internal constant INVERSE_BASIS_POINT = 1e4;\r\n uint256 internal constant BATCHING_PERIOD = 260;\r\n\r\n // Minimum APY for capital locked up to underwrite options.\r\n // The quote will return a minimum price corresponding to this APY\r\n int128 internal constant MIN_APY_64x64 = 0x4ccccccccccccccd; // 0.3\r\n\r\n constructor(\r\n address ivolOracle,\r\n address weth,\r\n address premiaMining,\r\n address feeReceiver,\r\n address feeDiscountAddress,\r\n int128 fee64x64\r\n ) {\r\n IVOL_ORACLE_ADDRESS = ivolOracle;\r\n WETH_ADDRESS = weth;\r\n PREMIA_MINING_ADDRESS = premiaMining;\r\n FEE_RECEIVER_ADDRESS = feeReceiver;\r\n // PremiaFeeDiscount contract address\r\n FEE_DISCOUNT_ADDRESS = feeDiscountAddress;\r\n FEE_64x64 = fee64x64;\r\n\r\n UNDERLYING_FREE_LIQ_TOKEN_ID = PoolStorage.formatTokenId(\r\n PoolStorage.TokenType.UNDERLYING_FREE_LIQ,\r\n 0,\r\n 0\r\n );\r\n BASE_FREE_LIQ_TOKEN_ID = PoolStorage.formatTokenId(\r\n PoolStorage.TokenType.BASE_FREE_LIQ,\r\n 0,\r\n 0\r\n );\r\n\r\n UNDERLYING_RESERVED_LIQ_TOKEN_ID = PoolStorage.formatTokenId(\r\n PoolStorage.TokenType.UNDERLYING_RESERVED_LIQ,\r\n 0,\r\n 0\r\n );\r\n BASE_RESERVED_LIQ_TOKEN_ID = PoolStorage.formatTokenId(\r\n PoolStorage.TokenType.BASE_RESERVED_LIQ,\r\n 0,\r\n 0\r\n );\r\n }\r\n\r\n modifier onlyProtocolOwner() {\r\n require(\r\n msg.sender == IERC173(OwnableStorage.layout().owner).owner(),\r\n \"Not protocol owner\"\r\n );\r\n _;\r\n }\r\n\r\n function _getFeeDiscount(address feePayer)\r\n internal\r\n view\r\n returns (uint256 discount)\r\n {\r\n if (FEE_DISCOUNT_ADDRESS != address(0)) {\r\n discount = IFeeDiscount(FEE_DISCOUNT_ADDRESS).getDiscount(feePayer);\r\n }\r\n }\r\n\r\n function _getFeeWithDiscount(address feePayer, uint256 fee)\r\n internal\r\n view\r\n returns (uint256)\r\n {\r\n uint256 discount = _getFeeDiscount(feePayer);\r\n return fee - ((fee * discount) / INVERSE_BASIS_POINT);\r\n }\r\n\r\n function _withdrawFees(bool isCall) internal returns (uint256 amount) {\r\n uint256 tokenId = _getReservedLiquidityTokenId(isCall);\r\n amount = _balanceOf(FEE_RECEIVER_ADDRESS, tokenId);\r\n\r\n if (amount > 0) {\r\n _burn(FEE_RECEIVER_ADDRESS, tokenId, amount);\r\n emit FeeWithdrawal(isCall, amount);\r\n }\r\n }\r\n\r\n /**\r\n * @notice calculate price of option contract\r\n * @param args structured quote arguments\r\n * @return result quote result\r\n */\r\n function _quote(PoolStorage.QuoteArgsInternal memory args)\r\n internal\r\n view\r\n returns (PoolStorage.QuoteResultInternal memory result)\r\n {\r\n require(\r\n args.strike64x64 > 0 && args.spot64x64 > 0 && args.maturity > 0,\r\n \"invalid args\"\r\n );\r\n PoolStorage.Layout storage l = PoolStorage.layout();\r\n\r\n int128 contractSize64x64 = ABDKMath64x64Token.fromDecimals(\r\n args.contractSize,\r\n l.underlyingDecimals\r\n );\r\n bool isCall = args.isCall;\r\n\r\n (int128 adjustedCLevel64x64, int128 oldLiquidity64x64) = l\r\n .applyCLevelPendingDepositAdjustment(\r\n l.getDecayAdjustedCLevel64x64(isCall),\r\n l.totalFreeLiquiditySupply64x64(isCall),\r\n isCall\r\n );\r\n\r\n require(oldLiquidity64x64 > 0, \"no liq\");\r\n\r\n int128 timeToMaturity64x64 = ABDKMath64x64.divu(\r\n args.maturity - block.timestamp,\r\n 365 days\r\n );\r\n\r\n int128 annualizedVolatility64x64 = IVolatilitySurfaceOracle(\r\n IVOL_ORACLE_ADDRESS\r\n ).getAnnualizedVolatility64x64(\r\n l.base,\r\n l.underlying,\r\n args.spot64x64,\r\n args.strike64x64,\r\n timeToMaturity64x64,\r\n isCall\r\n );\r\n\r\n require(annualizedVolatility64x64 > 0, \"vol = 0\");\r\n\r\n (\r\n int128 price64x64,\r\n int128 cLevel64x64,\r\n int128 slippageCoefficient64x64\r\n ) = OptionMath.quotePrice(\r\n OptionMath.QuoteArgs(\r\n annualizedVolatility64x64.mul(annualizedVolatility64x64),\r\n args.strike64x64,\r\n args.spot64x64,\r\n timeToMaturity64x64,\r\n adjustedCLevel64x64,\r\n oldLiquidity64x64,\r\n oldLiquidity64x64.sub(contractSize64x64),\r\n 0x10000000000000000, // 64x64 fixed point representation of 1\r\n MIN_APY_64x64,\r\n isCall\r\n )\r\n );\r\n\r\n result.baseCost64x64 = isCall\r\n ? price64x64.mul(contractSize64x64).div(args.spot64x64)\r\n : price64x64.mul(contractSize64x64);\r\n result.feeCost64x64 = result.baseCost64x64.mul(FEE_64x64);\r\n result.cLevel64x64 = cLevel64x64;\r\n result.slippageCoefficient64x64 = slippageCoefficient64x64;\r\n\r\n int128 discount = ABDKMath64x64.divu(\r\n _getFeeDiscount(args.feePayer),\r\n INVERSE_BASIS_POINT\r\n );\r\n result.feeCost64x64 -= result.feeCost64x64.mul(discount);\r\n }\r\n\r\n /**\r\n * @notice burn corresponding long and short option tokens\r\n * @param account holder of tokens to annihilate\r\n * @param maturity timestamp of option maturity\r\n * @param strike64x64 64x64 fixed point representation of strike price\r\n * @param isCall true for call, false for put\r\n * @param contractSize quantity of option contract tokens to annihilate\r\n */\r\n function _annihilate(\r\n address account,\r\n uint64 maturity,\r\n int128 strike64x64,\r\n bool isCall,\r\n uint256 contractSize\r\n ) internal {\r\n uint256 longTokenId = PoolStorage.formatTokenId(\r\n _getTokenType(isCall, true),\r\n maturity,\r\n strike64x64\r\n );\r\n uint256 shortTokenId = PoolStorage.formatTokenId(\r\n _getTokenType(isCall, false),\r\n maturity,\r\n strike64x64\r\n );\r\n\r\n _burn(account, longTokenId, contractSize);\r\n _burn(account, shortTokenId, contractSize);\r\n\r\n emit Annihilate(shortTokenId, contractSize);\r\n }\r\n\r\n /**\r\n * @notice purchase option\r\n * @param l storage layout struct\r\n * @param account recipient of purchased option\r\n * @param maturity timestamp of option maturity\r\n * @param strike64x64 64x64 fixed point representation of strike price\r\n * @param isCall true for call, false for put\r\n * @param contractSize size of option contract\r\n * @param newPrice64x64 64x64 fixed point representation of current spot price\r\n * @return baseCost quantity of tokens required to purchase long position\r\n * @return feeCost quantity of tokens required to pay fees\r\n */\r\n function _purchase(\r\n PoolStorage.Layout storage l,\r\n address account,\r\n uint64 maturity,\r\n int128 strike64x64,\r\n bool isCall,\r\n uint256 contractSize,\r\n int128 newPrice64x64\r\n ) internal returns (uint256 baseCost, uint256 feeCost) {\r\n require(maturity > block.timestamp, \"expired\");\r\n require(contractSize >= l.underlyingMinimum, \"too small\");\r\n\r\n {\r\n uint256 size = isCall\r\n ? contractSize\r\n : l.fromUnderlyingToBaseDecimals(\r\n strike64x64.mulu(contractSize)\r\n );\r\n\r\n require(\r\n size <=\r\n ERC1155EnumerableStorage.layout().totalSupply[\r\n _getFreeLiquidityTokenId(isCall)\r\n ] -\r\n l.nextDeposits[isCall].totalPendingDeposits,\r\n \"insuf liq\"\r\n );\r\n }\r\n\r\n PoolStorage.QuoteResultInternal memory quote = _quote(\r\n PoolStorage.QuoteArgsInternal(\r\n account,\r\n maturity,\r\n strike64x64,\r\n newPrice64x64,\r\n contractSize,\r\n isCall\r\n )\r\n );\r\n\r\n baseCost = ABDKMath64x64Token.toDecimals(\r\n quote.baseCost64x64,\r\n l.getTokenDecimals(isCall)\r\n );\r\n\r\n feeCost = ABDKMath64x64Token.toDecimals(\r\n quote.feeCost64x64,\r\n l.getTokenDecimals(isCall)\r\n );\r\n\r\n uint256 longTokenId = PoolStorage.formatTokenId(\r\n _getTokenType(isCall, true),\r\n maturity,\r\n strike64x64\r\n );\r\n\r\n uint256 shortTokenId = PoolStorage.formatTokenId(\r\n _getTokenType(isCall, false),\r\n maturity,\r\n strike64x64\r\n );\r\n\r\n // mint long option token for buyer\r\n _mint(account, longTokenId, contractSize);\r\n\r\n int128 oldLiquidity64x64 = l.totalFreeLiquiditySupply64x64(isCall);\r\n // burn free liquidity tokens from other underwriters\r\n _mintShortTokenLoop(\r\n l,\r\n account,\r\n contractSize,\r\n baseCost,\r\n shortTokenId,\r\n isCall\r\n );\r\n int128 newLiquidity64x64 = l.totalFreeLiquiditySupply64x64(isCall);\r\n\r\n _setCLevel(l, oldLiquidity64x64, newLiquidity64x64, isCall);\r\n\r\n // mint reserved liquidity tokens for fee receiver\r\n _mint(\r\n FEE_RECEIVER_ADDRESS,\r\n _getReservedLiquidityTokenId(isCall),\r\n feeCost\r\n );\r\n\r\n emit Purchase(\r\n account,\r\n longTokenId,\r\n contractSize,\r\n baseCost,\r\n feeCost,\r\n newPrice64x64\r\n );\r\n }\r\n\r\n /**\r\n * @notice reassign short position to new underwriter\r\n * @param l storage layout struct\r\n * @param account holder of positions to be reassigned\r\n * @param maturity timestamp of option maturity\r\n * @param strike64x64 64x64 fixed point representation of strike price\r\n * @param isCall true for call, false for put\r\n * @param contractSize quantity of option contract tokens to reassign\r\n * @param newPrice64x64 64x64 fixed point representation of current spot price\r\n * @return baseCost quantity of tokens required to reassign short position\r\n * @return feeCost quantity of tokens required to pay fees\r\n * @return amountOut quantity of liquidity freed\r\n */\r\n function _reassign(\r\n PoolStorage.Layout storage l,\r\n address account,\r\n uint64 maturity,\r\n int128 strike64x64,\r\n bool isCall,\r\n uint256 contractSize,\r\n int128 newPrice64x64\r\n )\r\n internal\r\n returns (\r\n uint256 baseCost,\r\n uint256 feeCost,\r\n uint256 amountOut\r\n )\r\n {\r\n (baseCost, feeCost) = _purchase(\r\n l,\r\n account,\r\n maturity,\r\n strike64x64,\r\n isCall,\r\n contractSize,\r\n newPrice64x64\r\n );\r\n\r\n _annihilate(account, maturity, strike64x64, isCall, contractSize);\r\n\r\n uint256 annihilateAmount = isCall\r\n ? contractSize\r\n : l.fromUnderlyingToBaseDecimals(strike64x64.mulu(contractSize));\r\n\r\n amountOut = annihilateAmount - baseCost - feeCost;\r\n }\r\n\r\n /**\r\n * @notice exercise option on behalf of holder\r\n * @dev used for processing of expired options if passed holder is zero address\r\n * @param holder owner of long option tokens to exercise\r\n * @param longTokenId long option token id\r\n * @param contractSize quantity of tokens to exercise\r\n */\r\n function _exercise(\r\n address holder,\r\n uint256 longTokenId,\r\n uint256 contractSize\r\n ) internal {\r\n uint64 maturity;\r\n int128 strike64x64;\r\n bool isCall;\r\n\r\n bool onlyExpired = holder == address(0);\r\n\r\n {\r\n PoolStorage.TokenType tokenType;\r\n (tokenType, maturity, strike64x64) = PoolStorage.parseTokenId(\r\n longTokenId\r\n );\r\n require(\r\n tokenType == PoolStorage.TokenType.LONG_CALL ||\r\n tokenType == PoolStorage.TokenType.LONG_PUT,\r\n \"invalid type\"\r\n );\r\n require(!onlyExpired || maturity < block.timestamp, \"not expired\");\r\n isCall = tokenType == PoolStorage.TokenType.LONG_CALL;\r\n }\r\n\r\n PoolStorage.Layout storage l = PoolStorage.layout();\r\n\r\n int128 spot64x64 = _update(l);\r\n\r\n if (maturity < block.timestamp) {\r\n spot64x64 = l.getPriceUpdateAfter(maturity);\r\n }\r\n\r\n require(\r\n onlyExpired ||\r\n (\r\n isCall\r\n ? (spot64x64 > strike64x64)\r\n : (spot64x64 < strike64x64)\r\n ),\r\n \"not ITM\"\r\n );\r\n\r\n uint256 exerciseValue;\r\n // option has a non-zero exercise value\r\n if (isCall) {\r\n if (spot64x64 > strike64x64) {\r\n exerciseValue = spot64x64.sub(strike64x64).div(spot64x64).mulu(\r\n contractSize\r\n );\r\n }\r\n } else {\r\n if (spot64x64 < strike64x64) {\r\n exerciseValue = l.fromUnderlyingToBaseDecimals(\r\n strike64x64.sub(spot64x64).mulu(contractSize)\r\n );\r\n }\r\n }\r\n\r\n uint256 totalFee;\r\n\r\n if (onlyExpired) {\r\n totalFee += _burnLongTokenLoop(\r\n contractSize,\r\n exerciseValue,\r\n longTokenId,\r\n isCall\r\n );\r\n } else {\r\n // burn long option tokens from sender\r\n _burn(holder, longTokenId, contractSize);\r\n\r\n uint256 fee;\r\n\r\n if (exerciseValue > 0) {\r\n fee = _getFeeWithDiscount(\r\n holder,\r\n FEE_64x64.mulu(exerciseValue)\r\n );\r\n totalFee += fee;\r\n\r\n _pushTo(holder, _getPoolToken(isCall), exerciseValue - fee);\r\n }\r\n\r\n emit Exercise(\r\n holder,\r\n longTokenId,\r\n contractSize,\r\n exerciseValue,\r\n fee\r\n );\r\n }\r\n\r\n totalFee += _burnShortTokenLoop(\r\n contractSize,\r\n exerciseValue,\r\n PoolStorage.formatTokenId(\r\n _getTokenType(isCall, false),\r\n maturity,\r\n strike64x64\r\n ),\r\n isCall\r\n );\r\n\r\n _mint(\r\n FEE_RECEIVER_ADDRESS,\r\n _getReservedLiquidityTokenId(isCall),\r\n totalFee\r\n );\r\n }\r\n\r\n function _mintShortTokenLoop(\r\n PoolStorage.Layout storage l,\r\n address buyer,\r\n uint256 contractSize,\r\n uint256 premium,\r\n uint256 shortTokenId,\r\n bool isCall\r\n ) internal {\r\n uint256 freeLiqTokenId = _getFreeLiquidityTokenId(isCall);\r\n (, , int128 strike64x64) = PoolStorage.parseTokenId(shortTokenId);\r\n\r\n uint256 toPay = isCall\r\n ? contractSize\r\n : l.fromUnderlyingToBaseDecimals(strike64x64.mulu(contractSize));\r\n\r\n while (toPay > 0) {\r\n address underwriter = l.liquidityQueueAscending[isCall][address(0)];\r\n uint256 balance = _balanceOf(underwriter, freeLiqTokenId);\r\n\r\n // If dust left, we remove underwriter and skip to next\r\n if (balance < _getMinimumAmount(l, isCall)) {\r\n l.removeUnderwriter(underwriter, isCall);\r\n continue;\r\n }\r\n\r\n if (!l.getReinvestmentStatus(underwriter, isCall)) {\r\n _burn(underwriter, freeLiqTokenId, balance);\r\n _mint(\r\n underwriter,\r\n _getReservedLiquidityTokenId(isCall),\r\n balance\r\n );\r\n _subUserTVL(l, underwriter, isCall, balance);\r\n continue;\r\n }\r\n\r\n // amount of liquidity provided by underwriter, accounting for reinvested premium\r\n uint256 intervalContractSize = ((balance -\r\n l.pendingDeposits[underwriter][l.nextDeposits[isCall].eta][\r\n isCall\r\n ]) * (toPay + premium)) / toPay;\r\n if (intervalContractSize == 0) continue;\r\n if (intervalContractSize > toPay) intervalContractSize = toPay;\r\n\r\n // amount of premium paid to underwriter\r\n uint256 intervalPremium = (premium * intervalContractSize) / toPay;\r\n premium -= intervalPremium;\r\n toPay -= intervalContractSize;\r\n _addUserTVL(l, underwriter, isCall, intervalPremium);\r\n\r\n // burn free liquidity tokens from underwriter\r\n _burn(\r\n underwriter,\r\n freeLiqTokenId,\r\n intervalContractSize - intervalPremium\r\n );\r\n\r\n if (isCall == false) {\r\n // For PUT, conversion to contract amount is done here (Prior to this line, it is token amount)\r\n intervalContractSize = l.fromBaseToUnderlyingDecimals(\r\n strike64x64.inv().mulu(intervalContractSize)\r\n );\r\n }\r\n\r\n // mint short option tokens for underwriter\r\n // toPay == 0 ? contractSize : intervalContractSize : To prevent minting less than amount,\r\n // because of rounding (Can happen for put, because of fixed point precision)\r\n _mint(\r\n underwriter,\r\n shortTokenId,\r\n toPay == 0 ? contractSize : intervalContractSize\r\n );\r\n\r\n emit Underwrite(\r\n underwriter,\r\n buyer,\r\n shortTokenId,\r\n toPay == 0 ? contractSize : intervalContractSize,\r\n intervalPremium,\r\n false\r\n );\r\n\r\n contractSize -= intervalContractSize;\r\n }\r\n }\r\n\r\n function _burnLongTokenLoop(\r\n uint256 contractSize,\r\n uint256 exerciseValue,\r\n uint256 longTokenId,\r\n bool isCall\r\n ) internal returns (uint256 totalFee) {\r\n EnumerableSet.AddressSet storage holders = ERC1155EnumerableStorage\r\n .layout()\r\n .accountsByToken[longTokenId];\r\n\r\n while (contractSize > 0) {\r\n address longTokenHolder = holders.at(holders.length() - 1);\r\n\r\n uint256 intervalContractSize = _balanceOf(\r\n longTokenHolder,\r\n longTokenId\r\n );\r\n if (intervalContractSize > contractSize)\r\n intervalContractSize = contractSize;\r\n\r\n uint256 intervalExerciseValue;\r\n\r\n uint256 fee;\r\n if (exerciseValue > 0) {\r\n intervalExerciseValue =\r\n (exerciseValue * intervalContractSize) /\r\n contractSize;\r\n\r\n fee = _getFeeWithDiscount(\r\n longTokenHolder,\r\n FEE_64x64.mulu(intervalExerciseValue)\r\n );\r\n totalFee += fee;\r\n\r\n exerciseValue -= intervalExerciseValue;\r\n _pushTo(\r\n longTokenHolder,\r\n _getPoolToken(isCall),\r\n intervalExerciseValue - fee\r\n );\r\n }\r\n\r\n contractSize -= intervalContractSize;\r\n\r\n emit Exercise(\r\n longTokenHolder,\r\n longTokenId,\r\n intervalContractSize,\r\n intervalExerciseValue - fee,\r\n fee\r\n );\r\n\r\n _burn(longTokenHolder, longTokenId, intervalContractSize);\r\n }\r\n }\r\n\r\n function _burnShortTokenLoop(\r\n uint256 contractSize,\r\n uint256 exerciseValue,\r\n uint256 shortTokenId,\r\n bool isCall\r\n ) internal returns (uint256 totalFee) {\r\n EnumerableSet.AddressSet storage underwriters = ERC1155EnumerableStorage\r\n .layout()\r\n .accountsByToken[shortTokenId];\r\n (, , int128 strike64x64) = PoolStorage.parseTokenId(shortTokenId);\r\n\r\n while (contractSize > 0) {\r\n address underwriter = underwriters.at(underwriters.length() - 1);\r\n\r\n // amount of liquidity provided by underwriter\r\n uint256 intervalContractSize = _balanceOf(\r\n underwriter,\r\n shortTokenId\r\n );\r\n if (intervalContractSize > contractSize)\r\n intervalContractSize = contractSize;\r\n\r\n // amount of value claimed by buyer\r\n uint256 intervalExerciseValue = (exerciseValue *\r\n intervalContractSize) / contractSize;\r\n exerciseValue -= intervalExerciseValue;\r\n contractSize -= intervalContractSize;\r\n\r\n uint256 freeLiq = isCall\r\n ? intervalContractSize - intervalExerciseValue\r\n : PoolStorage.layout().fromUnderlyingToBaseDecimals(\r\n strike64x64.mulu(intervalContractSize)\r\n ) - intervalExerciseValue;\r\n\r\n uint256 fee = _getFeeWithDiscount(\r\n underwriter,\r\n FEE_64x64.mulu(freeLiq)\r\n );\r\n totalFee += fee;\r\n\r\n uint256 tvlToSubtract = intervalExerciseValue;\r\n\r\n // mint free liquidity tokens for underwriter\r\n if (\r\n PoolStorage.layout().getReinvestmentStatus(underwriter, isCall)\r\n ) {\r\n _addToDepositQueue(underwriter, freeLiq - fee, isCall);\r\n tvlToSubtract += fee;\r\n } else {\r\n _mint(\r\n underwriter,\r\n _getReservedLiquidityTokenId(isCall),\r\n freeLiq - fee\r\n );\r\n tvlToSubtract += freeLiq;\r\n }\r\n\r\n _subUserTVL(\r\n PoolStorage.layout(),\r\n underwriter,\r\n isCall,\r\n tvlToSubtract\r\n );\r\n\r\n // burn short option tokens from underwriter\r\n _burn(underwriter, shortTokenId, intervalContractSize);\r\n\r\n emit AssignExercise(\r\n underwriter,\r\n shortTokenId,\r\n freeLiq - fee,\r\n intervalContractSize,\r\n fee\r\n );\r\n }\r\n }\r\n\r\n function _addToDepositQueue(\r\n address account,\r\n uint256 amount,\r\n bool isCallPool\r\n ) internal {\r\n PoolStorage.Layout storage l = PoolStorage.layout();\r\n\r\n _mint(account, _getFreeLiquidityTokenId(isCallPool), amount);\r\n\r\n uint256 nextBatch = (block.timestamp / BATCHING_PERIOD) *\r\n BATCHING_PERIOD +\r\n BATCHING_PERIOD;\r\n l.pendingDeposits[account][nextBatch][isCallPool] += amount;\r\n\r\n PoolStorage.BatchData storage batchData = l.nextDeposits[isCallPool];\r\n batchData.totalPendingDeposits += amount;\r\n batchData.eta = nextBatch;\r\n }\r\n\r\n function _processPendingDeposits(PoolStorage.Layout storage l, bool isCall)\r\n internal\r\n {\r\n PoolStorage.BatchData storage data = l.nextDeposits[isCall];\r\n\r\n if (data.eta == 0 || block.timestamp < data.eta) return;\r\n\r\n int128 oldLiquidity64x64 = l.totalFreeLiquiditySupply64x64(isCall);\r\n\r\n _setCLevel(\r\n l,\r\n oldLiquidity64x64,\r\n oldLiquidity64x64.add(\r\n ABDKMath64x64Token.fromDecimals(\r\n data.totalPendingDeposits,\r\n l.getTokenDecimals(isCall)\r\n )\r\n ),\r\n isCall\r\n );\r\n\r\n delete l.nextDeposits[isCall];\r\n }\r\n\r\n function _getFreeLiquidityTokenId(bool isCall)\r\n internal\r\n view\r\n returns (uint256 freeLiqTokenId)\r\n {\r\n freeLiqTokenId = isCall\r\n ? UNDERLYING_FREE_LIQ_TOKEN_ID\r\n : BASE_FREE_LIQ_TOKEN_ID;\r\n }\r\n\r\n function _getReservedLiquidityTokenId(bool isCall)\r\n internal\r\n view\r\n returns (uint256 reservedLiqTokenId)\r\n {\r\n reservedLiqTokenId = isCall\r\n ? UNDERLYING_RESERVED_LIQ_TOKEN_ID\r\n : BASE_RESERVED_LIQ_TOKEN_ID;\r\n }\r\n\r\n function _getPoolToken(bool isCall) internal view returns (address token) {\r\n token = isCall\r\n ? PoolStorage.layout().underlying\r\n : PoolStorage.layout().base;\r\n }\r\n\r\n function _getTokenType(bool isCall, bool isLong)\r\n internal\r\n pure\r\n returns (PoolStorage.TokenType tokenType)\r\n {\r\n if (isCall) {\r\n tokenType = isLong\r\n ? PoolStorage.TokenType.LONG_CALL\r\n : PoolStorage.TokenType.SHORT_CALL;\r\n } else {\r\n tokenType = isLong\r\n ? PoolStorage.TokenType.LONG_PUT\r\n : PoolStorage.TokenType.SHORT_PUT;\r\n }\r\n }\r\n\r\n function _getMinimumAmount(PoolStorage.Layout storage l, bool isCall)\r\n internal\r\n view\r\n returns (uint256 minimumAmount)\r\n {\r\n minimumAmount = isCall ? l.underlyingMinimum : l.baseMinimum;\r\n }\r\n\r\n function _getPoolCapAmount(PoolStorage.Layout storage l, bool isCall)\r\n internal\r\n view\r\n returns (uint256 poolCapAmount)\r\n {\r\n poolCapAmount = isCall ? l.underlyingPoolCap : l.basePoolCap;\r\n }\r\n\r\n function _setCLevel(\r\n PoolStorage.Layout storage l,\r\n int128 oldLiquidity64x64,\r\n int128 newLiquidity64x64,\r\n bool isCallPool\r\n ) internal {\r\n int128 oldCLevel64x64 = l.getDecayAdjustedCLevel64x64(isCallPool);\r\n\r\n int128 cLevel64x64 = l.applyCLevelLiquidityChangeAdjustment(\r\n oldCLevel64x64,\r\n oldLiquidity64x64,\r\n newLiquidity64x64,\r\n isCallPool\r\n );\r\n\r\n l.setCLevel(cLevel64x64, isCallPool);\r\n\r\n emit UpdateCLevel(\r\n isCallPool,\r\n cLevel64x64,\r\n oldLiquidity64x64,\r\n newLiquidity64x64\r\n );\r\n }\r\n\r\n /**\r\n * @notice calculate and store updated market state\r\n * @param l storage layout struct\r\n * @return newPrice64x64 64x64 fixed point representation of current spot price\r\n */\r\n function _update(PoolStorage.Layout storage l)\r\n internal\r\n returns (int128 newPrice64x64)\r\n {\r\n if (l.updatedAt == block.timestamp) {\r\n return (l.getPriceUpdate(block.timestamp));\r\n }\r\n\r\n newPrice64x64 = l.fetchPriceUpdate();\r\n\r\n if (l.getPriceUpdate(block.timestamp) == 0) {\r\n l.setPriceUpdate(block.timestamp, newPrice64x64);\r\n }\r\n\r\n l.updatedAt = block.timestamp;\r\n\r\n _processPendingDeposits(l, true);\r\n _processPendingDeposits(l, false);\r\n }\r\n\r\n /**\r\n * @notice transfer ERC20 tokens to message sender\r\n * @param token ERC20 token address\r\n * @param amount quantity of token to transfer\r\n */\r\n function _pushTo(\r\n address to,\r\n address token,\r\n uint256 amount\r\n ) internal {\r\n if (amount == 0) return;\r\n\r\n require(IERC20(token).transfer(to, amount), \"ERC20 transfer failed\");\r\n }\r\n\r\n /**\r\n * @notice transfer ERC20 tokens from message sender\r\n * @param from address from which tokens are pulled from\r\n * @param token ERC20 token address\r\n * @param amount quantity of token to transfer\r\n * @param skipWethDeposit if false, will not try to deposit weth from attach eth\r\n */\r\n function _pullFrom(\r\n address from,\r\n address token,\r\n uint256 amount,\r\n bool skipWethDeposit\r\n ) internal {\r\n if (!skipWethDeposit) {\r\n if (token == WETH_ADDRESS) {\r\n if (msg.value > 0) {\r\n if (msg.value > amount) {\r\n IWETH(WETH_ADDRESS).deposit{value: amount}();\r\n\r\n (bool success, ) = payable(msg.sender).call{\r\n value: msg.value - amount\r\n }(\"\");\r\n\r\n require(success, \"ETH refund failed\");\r\n\r\n amount = 0;\r\n } else {\r\n unchecked {\r\n amount -= msg.value;\r\n }\r\n\r\n IWETH(WETH_ADDRESS).deposit{value: msg.value}();\r\n }\r\n }\r\n } else {\r\n require(msg.value == 0, \"not WETH deposit\");\r\n }\r\n }\r\n\r\n if (amount > 0) {\r\n require(\r\n IERC20(token).transferFrom(from, address(this), amount),\r\n \"ERC20 transfer failed\"\r\n );\r\n }\r\n }\r\n\r\n function _mint(\r\n address account,\r\n uint256 tokenId,\r\n uint256 amount\r\n ) internal {\r\n _mint(account, tokenId, amount, \"\");\r\n }\r\n\r\n function _addUserTVL(\r\n PoolStorage.Layout storage l,\r\n address user,\r\n bool isCallPool,\r\n uint256 amount\r\n ) internal {\r\n uint256 userTVL = l.userTVL[user][isCallPool];\r\n uint256 totalTVL = l.totalTVL[isCallPool];\r\n\r\n IPremiaMining(PREMIA_MINING_ADDRESS).allocatePending(\r\n user,\r\n address(this),\r\n isCallPool,\r\n userTVL,\r\n userTVL + amount,\r\n totalTVL\r\n );\r\n\r\n l.userTVL[user][isCallPool] = userTVL + amount;\r\n l.totalTVL[isCallPool] = totalTVL + amount;\r\n }\r\n\r\n function _subUserTVL(\r\n PoolStorage.Layout storage l,\r\n address user,\r\n bool isCallPool,\r\n uint256 amount\r\n ) internal {\r\n uint256 userTVL = l.userTVL[user][isCallPool];\r\n uint256 totalTVL = l.totalTVL[isCallPool];\r\n\r\n IPremiaMining(PREMIA_MINING_ADDRESS).allocatePending(\r\n user,\r\n address(this),\r\n isCallPool,\r\n userTVL,\r\n userTVL - amount,\r\n totalTVL\r\n );\r\n l.userTVL[user][isCallPool] = userTVL - amount;\r\n l.totalTVL[isCallPool] = totalTVL - amount;\r\n }\r\n\r\n /**\r\n * @notice ERC1155 hook: track eligible underwriters\r\n * @param operator transaction sender\r\n * @param from token sender\r\n * @param to token receiver\r\n * @param ids token ids transferred\r\n * @param amounts token quantities transferred\r\n * @param data data payload\r\n */\r\n function _beforeTokenTransfer(\r\n address operator,\r\n address from,\r\n address to,\r\n uint256[] memory ids,\r\n uint256[] memory amounts,\r\n bytes memory data\r\n ) internal virtual override {\r\n super._beforeTokenTransfer(operator, from, to, ids, amounts, data);\r\n\r\n PoolStorage.Layout storage l = PoolStorage.layout();\r\n\r\n for (uint256 i; i < ids.length; i++) {\r\n uint256 id = ids[i];\r\n uint256 amount = amounts[i];\r\n\r\n if (amount == 0) continue;\r\n\r\n if (from == address(0)) {\r\n l.tokenIds.add(id);\r\n }\r\n\r\n if (\r\n to == address(0) &&\r\n ERC1155EnumerableStorage.layout().totalSupply[id] == 0\r\n ) {\r\n l.tokenIds.remove(id);\r\n }\r\n\r\n // prevent transfer of free and reserved liquidity during waiting period\r\n\r\n if (\r\n id == UNDERLYING_FREE_LIQ_TOKEN_ID ||\r\n id == BASE_FREE_LIQ_TOKEN_ID ||\r\n id == UNDERLYING_RESERVED_LIQ_TOKEN_ID ||\r\n id == BASE_RESERVED_LIQ_TOKEN_ID\r\n ) {\r\n if (from != address(0) && to != address(0)) {\r\n bool isCallPool = id == UNDERLYING_FREE_LIQ_TOKEN_ID ||\r\n id == UNDERLYING_RESERVED_LIQ_TOKEN_ID;\r\n\r\n require(\r\n l.depositedAt[from][isCallPool] + (1 days) <\r\n block.timestamp,\r\n \"liq lock 1d\"\r\n );\r\n }\r\n }\r\n\r\n if (\r\n id == UNDERLYING_FREE_LIQ_TOKEN_ID ||\r\n id == BASE_FREE_LIQ_TOKEN_ID\r\n ) {\r\n bool isCallPool = id == UNDERLYING_FREE_LIQ_TOKEN_ID;\r\n uint256 minimum = _getMinimumAmount(l, isCallPool);\r\n\r\n if (from != address(0)) {\r\n uint256 balance = _balanceOf(from, id);\r\n\r\n if (balance > minimum && balance <= amount + minimum) {\r\n require(\r\n balance -\r\n l.pendingDeposits[from][\r\n l.nextDeposits[isCallPool].eta\r\n ][isCallPool] >=\r\n amount,\r\n \"Insuf balance\"\r\n );\r\n l.removeUnderwriter(from, isCallPool);\r\n }\r\n\r\n if (to != address(0)) {\r\n _subUserTVL(l, from, isCallPool, amounts[i]);\r\n _addUserTVL(l, to, isCallPool, amounts[i]);\r\n }\r\n }\r\n\r\n if (to != address(0)) {\r\n uint256 balance = _balanceOf(to, id);\r\n\r\n if (balance <= minimum && balance + amount > minimum) {\r\n l.addUnderwriter(to, isCallPool);\r\n }\r\n }\r\n }\r\n\r\n // Update userTVL on SHORT options transfers\r\n (\r\n PoolStorage.TokenType tokenType,\r\n ,\r\n int128 strike64x64\r\n ) = PoolStorage.parseTokenId(id);\r\n\r\n if (\r\n (from != address(0) && to != address(0)) &&\r\n (tokenType == PoolStorage.TokenType.SHORT_CALL ||\r\n tokenType == PoolStorage.TokenType.SHORT_PUT)\r\n ) {\r\n bool isCall = tokenType == PoolStorage.TokenType.SHORT_CALL;\r\n uint256 collateral = isCall\r\n ? amount\r\n : l.fromUnderlyingToBaseDecimals(strike64x64.mulu(amount));\r\n\r\n _subUserTVL(l, from, isCall, collateral);\r\n _addUserTVL(l, to, isCall, collateral);\r\n }\r\n }\r\n }\r\n}\r\n" + }, + "@solidstate/contracts/token/ERC1155/base/ERC1155BaseStorage.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nlibrary ERC1155BaseStorage {\n struct Layout {\n mapping(uint256 => mapping(address => uint256)) balances;\n mapping(address => mapping(address => bool)) operatorApprovals;\n }\n\n bytes32 internal constant STORAGE_SLOT =\n keccak256('solidstate.contracts.storage.ERC1155Base');\n\n function layout() internal pure returns (Layout storage l) {\n bytes32 slot = STORAGE_SLOT;\n assembly {\n l.slot := slot\n }\n }\n}\n" + }, + "@solidstate/contracts/utils/EnumerableSet.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @title Set implementation with enumeration functions\n * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts (MIT license)\n */\nlibrary EnumerableSet {\n struct Set {\n bytes32[] _values;\n // 1-indexed to allow 0 to signify nonexistence\n mapping(bytes32 => uint256) _indexes;\n }\n\n struct Bytes32Set {\n Set _inner;\n }\n\n struct AddressSet {\n Set _inner;\n }\n\n struct UintSet {\n Set _inner;\n }\n\n function at(Bytes32Set storage set, uint256 index)\n internal\n view\n returns (bytes32)\n {\n return _at(set._inner, index);\n }\n\n function at(AddressSet storage set, uint256 index)\n internal\n view\n returns (address)\n {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n function at(UintSet storage set, uint256 index)\n internal\n view\n returns (uint256)\n {\n return uint256(_at(set._inner, index));\n }\n\n function contains(Bytes32Set storage set, bytes32 value)\n internal\n view\n returns (bool)\n {\n return _contains(set._inner, value);\n }\n\n function contains(AddressSet storage set, address value)\n internal\n view\n returns (bool)\n {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n function contains(UintSet storage set, uint256 value)\n internal\n view\n returns (bool)\n {\n return _contains(set._inner, bytes32(value));\n }\n\n function indexOf(Bytes32Set storage set, bytes32 value)\n internal\n view\n returns (uint256)\n {\n return _indexOf(set._inner, value);\n }\n\n function indexOf(AddressSet storage set, address value)\n internal\n view\n returns (uint256)\n {\n return _indexOf(set._inner, bytes32(uint256(uint160(value))));\n }\n\n function indexOf(UintSet storage set, uint256 value)\n internal\n view\n returns (uint256)\n {\n return _indexOf(set._inner, bytes32(value));\n }\n\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n function add(Bytes32Set storage set, bytes32 value)\n internal\n returns (bool)\n {\n return _add(set._inner, value);\n }\n\n function add(AddressSet storage set, address value)\n internal\n returns (bool)\n {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n function remove(Bytes32Set storage set, bytes32 value)\n internal\n returns (bool)\n {\n return _remove(set._inner, value);\n }\n\n function remove(AddressSet storage set, address value)\n internal\n returns (bool)\n {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n function remove(UintSet storage set, uint256 value)\n internal\n returns (bool)\n {\n return _remove(set._inner, bytes32(value));\n }\n\n function _at(Set storage set, uint256 index)\n private\n view\n returns (bytes32)\n {\n require(\n set._values.length > index,\n 'EnumerableSet: index out of bounds'\n );\n return set._values[index];\n }\n\n function _contains(Set storage set, bytes32 value)\n private\n view\n returns (bool)\n {\n return set._indexes[value] != 0;\n }\n\n function _indexOf(Set storage set, bytes32 value)\n private\n view\n returns (uint256)\n {\n unchecked {\n return set._indexes[value] - 1;\n }\n }\n\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n uint256 index = valueIndex - 1;\n bytes32 last = set._values[set._values.length - 1];\n\n // move last value to now-vacant index\n\n set._values[index] = last;\n set._indexes[last] = index + 1;\n\n // clear last index\n\n set._values.pop();\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n}\n" + }, + "contracts/mining/PremiaMiningStorage.sol": { + "content": "// SPDX-License-Identifier: BUSL-1.1\r\n// For further clarification please see https://license.premia.legal\r\n\r\npragma solidity ^0.8.0;\r\n\r\nlibrary PremiaMiningStorage {\r\n bytes32 internal constant STORAGE_SLOT =\r\n keccak256(\"premia.contracts.storage.PremiaMining\");\r\n\r\n // Info of each pool.\r\n struct PoolInfo {\r\n uint256 allocPoint; // How many allocation points assigned to this pool. PREMIA to distribute per block.\r\n uint256 lastRewardTimestamp; // Last timestamp that PREMIA distribution occurs\r\n uint256 accPremiaPerShare; // Accumulated PREMIA per share, times 1e12. See below.\r\n }\r\n\r\n // Info of each user.\r\n struct UserInfo {\r\n uint256 reward; // Total allocated unclaimed reward\r\n uint256 rewardDebt; // Reward debt. See explanation below.\r\n //\r\n // We do some fancy math here. Basically, any point in time, the amount of PREMIA\r\n // entitled to a user but is pending to be distributed is:\r\n //\r\n // pending reward = (user.amount * pool.accPremiaPerShare) - user.rewardDebt\r\n //\r\n // Whenever a user deposits or withdraws LP tokens to a pool. Here's what happens:\r\n // 1. The pool's `accPremiaPerShare` (and `lastRewardBlock`) gets updated.\r\n // 2. User receives the pending reward sent to his/her address.\r\n // 3. User's `amount` gets updated.\r\n // 4. User's `rewardDebt` gets updated.\r\n }\r\n\r\n struct Layout {\r\n // Total PREMIA left to distribute\r\n uint256 premiaAvailable;\r\n // Amount of premia distributed per year\r\n uint256 premiaPerYear;\r\n // pool -> isCallPool -> PoolInfo\r\n mapping(address => mapping(bool => PoolInfo)) poolInfo;\r\n // pool -> isCallPool -> user -> UserInfo\r\n mapping(address => mapping(bool => mapping(address => UserInfo))) userInfo;\r\n // Total allocation points. Must be the sum of all allocation points in all pools.\r\n uint256 totalAllocPoint;\r\n }\r\n\r\n function layout() internal pure returns (Layout storage l) {\r\n bytes32 slot = STORAGE_SLOT;\r\n assembly {\r\n l.slot := slot\r\n }\r\n }\r\n}\r\n" + }, + "@solidstate/contracts/utils/IWETH.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC20 } from '../token/ERC20/IERC20.sol';\nimport { IERC20Metadata } from '../token/ERC20/metadata/IERC20Metadata.sol';\n\n/**\n * @title WETH (Wrapped ETH) interface\n */\ninterface IWETH is IERC20, IERC20Metadata {\n /**\n * @notice convert ETH to WETH\n */\n function deposit() external payable;\n\n /**\n * @notice convert WETH to ETH\n * @dev if caller is a contract, it should have a fallback or receive function\n * @param amount quantity of WETH to convert, denominated in wei\n */\n function withdraw(uint256 amount) external;\n}\n" + }, + "@solidstate/contracts/token/ERC1155/IERC1155Internal.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC165 } from '../../introspection/IERC165.sol';\n\n/**\n * @notice Partial ERC1155 interface needed by internal functions\n */\ninterface IERC1155Internal {\n event TransferSingle(\n address indexed operator,\n address indexed from,\n address indexed to,\n uint256 id,\n uint256 value\n );\n\n event TransferBatch(\n address indexed operator,\n address indexed from,\n address indexed to,\n uint256[] ids,\n uint256[] values\n );\n\n event ApprovalForAll(\n address indexed account,\n address indexed operator,\n bool approved\n );\n}\n" + }, + "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AggregatorV3Interface {\n\n function decimals()\n external\n view\n returns (\n uint8\n );\n\n function description()\n external\n view\n returns (\n string memory\n );\n\n function version()\n external\n view\n returns (\n uint256\n );\n\n // getRoundData and latestRoundData should both raise \"No data present\"\n // if they do not have data to report, instead of returning unset values\n // which could be misinterpreted as actual reported values.\n function getRoundData(\n uint80 _roundId\n )\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n\n function latestRoundData()\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n\n}\n" + }, + "@solidstate/contracts/access/OwnableStorage.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nlibrary OwnableStorage {\n struct Layout {\n address owner;\n }\n\n bytes32 internal constant STORAGE_SLOT =\n keccak256('solidstate.contracts.storage.Ownable');\n\n function layout() internal pure returns (Layout storage l) {\n bytes32 slot = STORAGE_SLOT;\n assembly {\n l.slot := slot\n }\n }\n\n function setOwner(Layout storage l, address owner) internal {\n l.owner = owner;\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "outputSelection": { + "*": { + "*": [ + "evm.bytecode", + "evm.deployedBytecode", + "devdoc", + "userdoc", + "metadata", + "abi" + ] + } + }, + "libraries": { + "contracts/libraries/OptionMath.sol": { + "OptionMath": "0x0f6e8ef18fb5bb61d545fee60f779d8aed60408f" + } + } + } + }, + "ABI": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"ivolOracle\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"weth\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"premiaMining\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeReceiver\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeDiscountAddress\",\"type\":\"address\"},{\"internalType\":\"int128\",\"name\":\"fee64x64\",\"type\":\"int128\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"shortTokenId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Annihilate\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"ApprovalForAll\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"underwriter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"shortTokenId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"freedAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"intervalContractSize\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"}],\"name\":\"AssignExercise\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isCallPool\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Deposit\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"longTokenId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"contractSize\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"exerciseValue\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"}],\"name\":\"Exercise\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"isCallPool\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"FeeWithdrawal\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"longTokenId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"contractSize\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"baseCost\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"feeCost\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"int128\",\"name\":\"spot64x64\",\"type\":\"int128\"}],\"name\":\"Purchase\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"ids\",\"type\":\"uint256[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"}],\"name\":\"TransferBatch\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"TransferSingle\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"underwriter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"longReceiver\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"shortTokenId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"intervalContractSize\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"intervalPremium\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isManualUnderwrite\",\"type\":\"bool\"}],\"name\":\"Underwrite\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"isCall\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"int128\",\"name\":\"cLevel64x64\",\"type\":\"int128\"},{\"indexed\":false,\"internalType\":\"int128\",\"name\":\"oldLiquidity64x64\",\"type\":\"int128\"},{\"indexed\":false,\"internalType\":\"int128\",\"name\":\"newLiquidity64x64\",\"type\":\"int128\"}],\"name\":\"UpdateCLevel\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"int128\",\"name\":\"steepness64x64\",\"type\":\"int128\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isCallPool\",\"type\":\"bool\"}],\"name\":\"UpdateSteepness\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isCallPool\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"depositedAt\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Withdrawal\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"holder\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"longTokenId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"contractSize\",\"type\":\"uint256\"}],\"name\":\"exerciseFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"longTokenId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"contractSize\",\"type\":\"uint256\"}],\"name\":\"processExpired\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + "ContractName": "PoolExercise", + "CompilerVersion": "v0.8.9+commit.e5eed63a", + "OptimizationUsed": 1, + "Runs": 200, + "ConstructorArguments": "0x0000000000000000000000003a87bb29b984d672664aa1dd2d19d2e8b24f0f2a000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000009abb27581c2e46a114f8c367355851e0580e9703000000000000000000000000c4b2c51f969e0713e799de73b7f130fb7bb604cf000000000000000000000000f1bb87563a122211d40d393ebf1c633c330377f900000000000000000000000000000000000000000000000007ae147ae147ae14", + "EVMVersion": "Default", + "Library": "", + "LicenseType": "", + "Proxy": 0, + "SwarmSource": "" + } +] \ No newline at end of file From d1d04787bf5e5aa90955d497f71522569cbe9991 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 18 Apr 2024 19:39:06 +0200 Subject: [PATCH 187/430] chore: move `foundry_common::rpc` to `foundry_test_utils` (#7707) --- Cargo.lock | 4 +++- crates/anvil/Cargo.toml | 15 ++++----------- crates/anvil/tests/it/fork.rs | 3 +-- crates/cast/tests/cli/main.rs | 7 +++++-- crates/common/Cargo.toml | 8 +------- crates/common/src/lib.rs | 1 - crates/evm/core/Cargo.toml | 10 ++++------ crates/evm/core/src/fork/database.rs | 2 +- crates/forge/bin/cmd/clone.rs | 2 +- crates/forge/tests/cli/cmd.rs | 4 ++-- crates/forge/tests/cli/script.rs | 5 ++--- crates/forge/tests/cli/test_cmd.rs | 9 ++++++--- crates/forge/tests/it/fork.rs | 4 ++-- crates/test-utils/Cargo.toml | 1 + crates/test-utils/src/lib.rs | 2 ++ crates/{common => test-utils}/src/rpc.rs | 0 crates/test-utils/src/util.rs | 3 +-- 17 files changed, 36 insertions(+), 44 deletions(-) rename crates/{common => test-utils}/src/rpc.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 3d2f8c7d3..c7dcd51e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -706,6 +706,7 @@ dependencies = [ "foundry-compilers", "foundry-config", "foundry-evm", + "foundry-test-utils", "futures", "hyper 0.14.28", "itertools 0.12.1", @@ -3800,7 +3801,6 @@ dependencies = [ "num-format", "once_cell", "pretty_assertions", - "rand 0.8.5", "reqwest 0.11.27", "reqwest 0.12.3", "rustc-hash", @@ -3957,6 +3957,7 @@ dependencies = [ "foundry-compilers", "foundry-config", "foundry-macros", + "foundry-test-utils", "futures", "itertools 0.12.1", "once_cell", @@ -4072,6 +4073,7 @@ dependencies = [ "once_cell", "parking_lot", "pretty_assertions", + "rand 0.8.5", "regex", "serde_json", "tracing", diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index cdf7c2f9b..d9d443643 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -16,11 +16,7 @@ path = "src/anvil.rs" required-features = ["cli"] [build-dependencies] -vergen = { workspace = true, default-features = false, features = [ - "build", - "git", - "gitcl", -] } +vergen = { workspace = true, default-features = false, features = ["build", "git", "gitcl"] } [dependencies] # foundry internal @@ -81,11 +77,7 @@ rand = "0.8" eyre.workspace = true # cli -clap = { version = "4", features = [ - "derive", - "env", - "wrap_help", -], optional = true } +clap = { version = "4", features = ["derive", "env", "wrap_help"], optional = true } clap_complete = { version = "4", optional = true } chrono.workspace = true auto_impl = "1" @@ -102,8 +94,9 @@ alloy-json-abi.workspace = true ethers = { workspace = true, features = ["abigen", "optimism"] } ethers-core = { workspace = true, features = ["optimism"] } foundry-compilers = { workspace = true, features = ["project-util", "full"] } +foundry-test-utils.workspace = true -pretty_assertions = "1.3.0" +pretty_assertions.workspace = true tokio = { version = "1", features = ["full"] } crc = "3.0.1" diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index 5f94a95ae..717e0c578 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -23,11 +23,10 @@ use ethers::{ }; use foundry_common::{ provider::alloy::get_http_provider, - rpc, - rpc::next_http_rpc_endpoint, types::{ToAlloy, ToEthers}, }; use foundry_config::Config; +use foundry_test_utils::rpc::{self, next_http_rpc_endpoint}; use futures::StreamExt; use std::{sync::Arc, time::Duration}; diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 7df9d4b91..bdc17a2fa 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -1,7 +1,10 @@ //! Contains various tests for checking cast commands -use foundry_common::rpc::{next_http_rpc_endpoint, next_ws_rpc_endpoint}; -use foundry_test_utils::{casttest, util::OutputExt}; +use foundry_test_utils::{ + casttest, + rpc::{next_http_rpc_endpoint, next_ws_rpc_endpoint}, + util::OutputExt, +}; use std::{fs, io::Write, path::Path}; // tests `--help` is printed to std out diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 7dc6fa3ba..cafa1938a 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -24,12 +24,7 @@ reqwest_ethers = { package = "reqwest", version = "0.11", default-features = fal alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } alloy-json-abi.workspace = true -alloy-primitives = { workspace = true, features = [ - "serde", - "getrandom", - "arbitrary", - "rlp", -] } +alloy-primitives = { workspace = true, features = ["serde", "getrandom", "arbitrary", "rlp"] } alloy-rpc-types.workspace = true alloy-rpc-client.workspace = true alloy-provider.workspace = true @@ -55,7 +50,6 @@ glob = "0.3" globset = "0.4" hex.workspace = true once_cell = "1" -rand.workspace = true reqwest = { version = "0.12", default-features = false } semver = "1" serde_json.workspace = true diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index 36298ae8f..484bc89d2 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -20,7 +20,6 @@ pub mod fs; pub mod glob; pub mod provider; pub mod retry; -pub mod rpc; pub mod runtime_client; pub mod selectors; pub mod serde_helpers; diff --git a/crates/evm/core/Cargo.toml b/crates/evm/core/Cargo.toml index cf922cbce..ff106a406 100644 --- a/crates/evm/core/Cargo.toml +++ b/crates/evm/core/Cargo.toml @@ -19,12 +19,7 @@ foundry-macros.workspace = true alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } alloy-json-abi.workspace = true -alloy-primitives = { workspace = true, features = [ - "serde", - "getrandom", - "arbitrary", - "rlp", -] } +alloy-primitives = { workspace = true, features = ["serde", "getrandom", "arbitrary", "rlp"] } alloy-genesis.workspace = true alloy-provider.workspace = true alloy-transport.workspace = true @@ -59,3 +54,6 @@ thiserror = "1" tokio = { version = "1", features = ["time", "macros"] } tracing = "0.1" url = "2" + +[dev-dependencies] +foundry-test-utils.workspace = true diff --git a/crates/evm/core/src/fork/database.rs b/crates/evm/core/src/fork/database.rs index fdc34dbaf..649291a04 100644 --- a/crates/evm/core/src/fork/database.rs +++ b/crates/evm/core/src/fork/database.rs @@ -274,7 +274,7 @@ mod tests { /// `AccountInfo` #[tokio::test(flavor = "multi_thread")] async fn fork_db_insert_basic_default() { - let rpc = foundry_common::rpc::next_http_rpc_endpoint(); + let rpc = foundry_test_utils::rpc::next_http_rpc_endpoint(); let provider = get_http_provider(rpc.clone()); let meta = BlockchainDbMeta { cfg_env: Default::default(), diff --git a/crates/forge/bin/cmd/clone.rs b/crates/forge/bin/cmd/clone.rs index a15418d1d..86ef41539 100644 --- a/crates/forge/bin/cmd/clone.rs +++ b/crates/forge/bin/cmd/clone.rs @@ -544,8 +544,8 @@ impl EtherscanClient for Client { #[cfg(test)] mod tests { use super::*; - use foundry_common::rpc::next_etherscan_api_key; use foundry_compilers::Artifact; + use foundry_test_utils::rpc::next_etherscan_api_key; use hex::ToHex; use std::collections::BTreeMap; diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index b42d93cfd..0da6191b0 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -1,11 +1,11 @@ //! Contains various tests for checking forge's commands use crate::constants::*; -use foundry_common::rpc::next_etherscan_api_key; use foundry_compilers::{artifacts::Metadata, remappings::Remapping, ConfigurableContractArtifact}; use foundry_config::{parse_with_profile, BasicConfig, Chain, Config, SolidityErrorCode}; use foundry_test_utils::{ foundry_compilers::PathStyle, + rpc::next_etherscan_api_key, util::{pretty_err, read_string, OutputExt, TestCommand}, }; use semver::Version; @@ -796,7 +796,7 @@ forgetest!( .to_string(); println!("project root: \"{root}\""); - let eth_rpc_url = foundry_common::rpc::next_http_archive_rpc_endpoint(); + let eth_rpc_url = foundry_test_utils::rpc::next_http_archive_rpc_endpoint(); let dss_exec_lib = "src/DssSpell.sol:DssExecLib:0xfD88CeE74f7D78697775aBDAE53f9Da1559728E4"; cmd.args([ diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index e56ae08ef..21b5837bb 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -3,8 +3,7 @@ use crate::constants::TEMPLATE_CONTRACT; use alloy_primitives::{Address, Bytes}; use anvil::{spawn, NodeConfig}; -use foundry_common::rpc; -use foundry_test_utils::{util::OutputExt, ScriptOutcome, ScriptTester}; +use foundry_test_utils::{rpc, util::OutputExt, ScriptOutcome, ScriptTester}; use regex::Regex; use serde_json::Value; use std::{env, path::PathBuf, str::FromStr}; @@ -32,7 +31,7 @@ contract ContractScript is Script { ) .unwrap(); - let rpc = foundry_common::rpc::next_http_rpc_endpoint(); + let rpc = foundry_test_utils::rpc::next_http_rpc_endpoint(); cmd.arg("script").arg(script).args(["--fork-url", rpc.as_str(), "-vvvvv"]).assert_success(); } diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 5872cc72e..2b0784d6a 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -1,7 +1,10 @@ -//! Contains various tests for checking `forge test` -use foundry_common::rpc; +//! Contains various tests for `forge test`. + use foundry_config::Config; -use foundry_test_utils::util::{OutputExt, OTHER_SOLC_VERSION, SOLC_VERSION}; +use foundry_test_utils::{ + rpc, + util::{OutputExt, OTHER_SOLC_VERSION, SOLC_VERSION}, +}; use std::{path::PathBuf, str::FromStr}; // tests that test filters are handled correctly diff --git a/crates/forge/tests/it/fork.rs b/crates/forge/tests/it/fork.rs index c2751fe91..d3f81ff67 100644 --- a/crates/forge/tests/it/fork.rs +++ b/crates/forge/tests/it/fork.rs @@ -66,7 +66,7 @@ async fn test_rpc_fork() { /// Tests that we can launch in forking mode #[tokio::test(flavor = "multi_thread")] async fn test_launch_fork() { - let rpc_url = foundry_common::rpc::next_http_archive_rpc_endpoint(); + let rpc_url = foundry_test_utils::rpc::next_http_archive_rpc_endpoint(); let runner = TEST_DATA_DEFAULT.forked_runner(&rpc_url).await; let filter = Filter::new(".*", ".*", &format!(".*fork{RE_PATH_SEPARATOR}Launch")); TestConfig::with_filter(runner, filter).run().await; @@ -75,7 +75,7 @@ async fn test_launch_fork() { /// Smoke test that forking workings with websockets #[tokio::test(flavor = "multi_thread")] async fn test_launch_fork_ws() { - let rpc_url = foundry_common::rpc::next_ws_archive_rpc_endpoint(); + let rpc_url = foundry_test_utils::rpc::next_ws_archive_rpc_endpoint(); let runner = TEST_DATA_DEFAULT.forked_runner(&rpc_url).await; let filter = Filter::new(".*", ".*", &format!(".*fork{RE_PATH_SEPARATOR}Launch")); TestConfig::with_filter(runner, filter).run().await; diff --git a/crates/test-utils/Cargo.toml b/crates/test-utils/Cargo.toml index 09025e27c..195048711 100644 --- a/crates/test-utils/Cargo.toml +++ b/crates/test-utils/Cargo.toml @@ -29,6 +29,7 @@ serde_json.workspace = true tracing = "0.1" tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] } walkdir = "2" +rand.workspace = true [features] # feature for integration tests that test external projects diff --git a/crates/test-utils/src/lib.rs b/crates/test-utils/src/lib.rs index 042ff4aba..a4b70f493 100644 --- a/crates/test-utils/src/lib.rs +++ b/crates/test-utils/src/lib.rs @@ -6,6 +6,8 @@ extern crate tracing; // Macros useful for testing. mod macros; +pub mod rpc; + pub mod fd_lock; mod filter; diff --git a/crates/common/src/rpc.rs b/crates/test-utils/src/rpc.rs similarity index 100% rename from crates/common/src/rpc.rs rename to crates/test-utils/src/rpc.rs diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index dbb872d8a..9d9905d1c 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -194,8 +194,7 @@ impl ExtTester { test_cmd.envs(self.envs.iter().map(|(k, v)| (k, v))); if let Some(fork_block) = self.fork_block { - test_cmd - .env("FOUNDRY_ETH_RPC_URL", foundry_common::rpc::next_http_archive_rpc_endpoint()); + test_cmd.env("FOUNDRY_ETH_RPC_URL", crate::rpc::next_http_archive_rpc_endpoint()); test_cmd.env("FOUNDRY_FORK_BLOCK_NUMBER", fork_block.to_string()); } From dcea2837571ab8c263aeabaae7c888d173764e2a Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 18 Apr 2024 19:39:37 +0200 Subject: [PATCH 188/430] chore: minify JSON (#7706) --- crates/anvil/test-data/SimpleStorage.json | 118 +----- crates/anvil/test-data/emit_logs.json | 68 +-- crates/anvil/test-data/greeter.json | 45 +- crates/anvil/test-data/multicall.json | 145 +------ crates/anvil/test-data/storage_sample.json | 34 +- crates/cast/tests/fixtures/ERC20Artifact.json | 386 +----------------- crates/cast/tests/fixtures/interface.json | 142 +------ .../cast/tests/fixtures/sign_typed_data.json | 39 +- crates/evm/core/src/abi/HardhatConsole.json | 2 +- crates/evm/core/test-data/storage.json | 2 +- .../deploy.sol/31337/run-latest.json | 64 +-- .../creation_data.json | 6 +- .../metadata.json | 79 +--- .../creation_data.json | 6 +- .../metadata.json | 17 +- .../creation_data.json | 6 +- .../metadata.json | 194 +-------- .../creation_data.json | 6 +- .../metadata.json | 130 +----- .../creation_data.json | 6 +- .../metadata.json | 64 +-- .../creation_data.json | 6 +- .../metadata.json | 70 +--- .../creation_data.json | 6 +- .../metadata.json | 149 +------ 25 files changed, 25 insertions(+), 1765 deletions(-) diff --git a/crates/anvil/test-data/SimpleStorage.json b/crates/anvil/test-data/SimpleStorage.json index 3d4a8b81a..8ff4ab381 100644 --- a/crates/anvil/test-data/SimpleStorage.json +++ b/crates/anvil/test-data/SimpleStorage.json @@ -1,117 +1 @@ -{ - "abi": [ - { - "inputs": [ - { - "internalType": "string", - "name": "value", - "type": "string" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "author", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "oldAuthor", - "type": "address" - }, - { - "indexed": false, - "internalType": "string", - "name": "oldValue", - "type": "string" - }, - { - "indexed": false, - "internalType": "string", - "name": "newValue", - "type": "string" - } - ], - "name": "ValueChanged", - "type": "event" - }, - { - "inputs": [], - "name": "_hashPuzzle", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getValue", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "lastSender", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "string", - "name": "value", - "type": "string" - } - ], - "name": "setValue", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "string", - "name": "value", - "type": "string" - }, - { - "internalType": "string", - "name": "value2", - "type": "string" - } - ], - "name": "setValues", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } - ], - "bin": "60806040523480156200001157600080fd5b5060405162000d6a38038062000d6a83398181016040528101906200003791906200030f565b600073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f999b6d464c4e3383c341bdd3a22b02dda8a7e1d69c069d252e35cb2ee2f4a3c36001846040516200009a929190620004c3565b60405180910390a38060019080519060200190620000ba929190620000c2565b5050620004fe565b828054620000d0906200038f565b90600052602060002090601f016020900481019282620000f4576000855562000140565b82601f106200010f57805160ff191683800117855562000140565b8280016001018555821562000140579182015b828111156200013f57825182559160200191906001019062000122565b5b5090506200014f919062000153565b5090565b5b808211156200016e57600081600090555060010162000154565b5090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b620001db8262000190565b810181811067ffffffffffffffff82111715620001fd57620001fc620001a1565b5b80604052505050565b60006200021262000172565b9050620002208282620001d0565b919050565b600067ffffffffffffffff821115620002435762000242620001a1565b5b6200024e8262000190565b9050602081019050919050565b60005b838110156200027b5780820151818401526020810190506200025e565b838111156200028b576000848401525b50505050565b6000620002a8620002a28462000225565b62000206565b905082815260208101848484011115620002c757620002c66200018b565b5b620002d48482856200025b565b509392505050565b600082601f830112620002f457620002f362000186565b5b81516200030684826020860162000291565b91505092915050565b6000602082840312156200032857620003276200017c565b5b600082015167ffffffffffffffff81111562000349576200034862000181565b5b6200035784828501620002dc565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680620003a857607f821691505b60208210811415620003bf57620003be62000360565b5b50919050565b600082825260208201905092915050565b60008190508160005260206000209050919050565b60008154620003fa816200038f565b620004068186620003c5565b9450600182166000811462000424576001811462000437576200046e565b60ff19831686526020860193506200046e565b6200044285620003d6565b60005b83811015620004665781548189015260018201915060208101905062000445565b808801955050505b50505092915050565b600081519050919050565b60006200048f8262000477565b6200049b8185620003c5565b9350620004ad8185602086016200025b565b620004b88162000190565b840191505092915050565b60006040820190508181036000830152620004df8185620003eb565b90508181036020830152620004f5818462000482565b90509392505050565b61085c806200050e6000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c8063018ba9911461005c578063209652551461007a578063256fec88146100985780637ffaa4b6146100b657806393a09352146100d2575b600080fd5b6100646100ee565b60405161007191906103bd565b60405180910390f35b6100826100f7565b60405161008f9190610471565b60405180910390f35b6100a0610189565b6040516100ad91906104d4565b60405180910390f35b6100d060048036038101906100cb9190610638565b6101ad565b005b6100ec60048036038101906100e791906106b0565b61021f565b005b60006064905090565b60606001805461010690610728565b80601f016020809104026020016040519081016040528092919081815260200182805461013290610728565b801561017f5780601f106101545761010080835404028352916020019161017f565b820191906000526020600020905b81548152906001019060200180831161016257829003601f168201915b5050505050905090565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b81600190805190602001906101c3929190610301565b5080600290805190602001906101da929190610301565b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f999b6d464c4e3383c341bdd3a22b02dda8a7e1d69c069d252e35cb2ee2f4a3c360018460405161029f9291906107ef565b60405180910390a380600190805190602001906102bd929190610301565b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b82805461030d90610728565b90600052602060002090601f01602090048101928261032f5760008555610376565b82601f1061034857805160ff1916838001178555610376565b82800160010185558215610376579182015b8281111561037557825182559160200191906001019061035a565b5b5090506103839190610387565b5090565b5b808211156103a0576000816000905550600101610388565b5090565b6000819050919050565b6103b7816103a4565b82525050565b60006020820190506103d260008301846103ae565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156104125780820151818401526020810190506103f7565b83811115610421576000848401525b50505050565b6000601f19601f8301169050919050565b6000610443826103d8565b61044d81856103e3565b935061045d8185602086016103f4565b61046681610427565b840191505092915050565b6000602082019050818103600083015261048b8184610438565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006104be82610493565b9050919050565b6104ce816104b3565b82525050565b60006020820190506104e960008301846104c5565b92915050565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61054582610427565b810181811067ffffffffffffffff821117156105645761056361050d565b5b80604052505050565b60006105776104ef565b9050610583828261053c565b919050565b600067ffffffffffffffff8211156105a3576105a261050d565b5b6105ac82610427565b9050602081019050919050565b82818337600083830152505050565b60006105db6105d684610588565b61056d565b9050828152602081018484840111156105f7576105f6610508565b5b6106028482856105b9565b509392505050565b600082601f83011261061f5761061e610503565b5b813561062f8482602086016105c8565b91505092915050565b6000806040838503121561064f5761064e6104f9565b5b600083013567ffffffffffffffff81111561066d5761066c6104fe565b5b6106798582860161060a565b925050602083013567ffffffffffffffff81111561069a576106996104fe565b5b6106a68582860161060a565b9150509250929050565b6000602082840312156106c6576106c56104f9565b5b600082013567ffffffffffffffff8111156106e4576106e36104fe565b5b6106f08482850161060a565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061074057607f821691505b60208210811415610754576107536106f9565b5b50919050565b60008190508160005260206000209050919050565b6000815461077c81610728565b61078681866103e3565b945060018216600081146107a157600181146107b3576107e6565b60ff19831686526020860193506107e6565b6107bc8561075a565b60005b838110156107de578154818901526001820191506020810190506107bf565b808801955050505b50505092915050565b60006040820190508181036000830152610809818561076f565b9050818103602083015261081d8184610438565b9050939250505056fea2646970667358221220e37ed4b56859ad0b3a3773f57ff7b4e7a99406933fc4ff9f8ae053c52cdf3e3264736f6c63430008090033" -} \ No newline at end of file +{"abi":[{"inputs":[{"internalType":"string","name":"value","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"author","type":"address"},{"indexed":true,"internalType":"address","name":"oldAuthor","type":"address"},{"indexed":false,"internalType":"string","name":"oldValue","type":"string"},{"indexed":false,"internalType":"string","name":"newValue","type":"string"}],"name":"ValueChanged","type":"event"},{"inputs":[],"name":"_hashPuzzle","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getValue","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastSender","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"value","type":"string"}],"name":"setValue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"value","type":"string"},{"internalType":"string","name":"value2","type":"string"}],"name":"setValues","outputs":[],"stateMutability":"nonpayable","type":"function"}],"bin":"60806040523480156200001157600080fd5b5060405162000d6a38038062000d6a83398181016040528101906200003791906200030f565b600073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f999b6d464c4e3383c341bdd3a22b02dda8a7e1d69c069d252e35cb2ee2f4a3c36001846040516200009a929190620004c3565b60405180910390a38060019080519060200190620000ba929190620000c2565b5050620004fe565b828054620000d0906200038f565b90600052602060002090601f016020900481019282620000f4576000855562000140565b82601f106200010f57805160ff191683800117855562000140565b8280016001018555821562000140579182015b828111156200013f57825182559160200191906001019062000122565b5b5090506200014f919062000153565b5090565b5b808211156200016e57600081600090555060010162000154565b5090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b620001db8262000190565b810181811067ffffffffffffffff82111715620001fd57620001fc620001a1565b5b80604052505050565b60006200021262000172565b9050620002208282620001d0565b919050565b600067ffffffffffffffff821115620002435762000242620001a1565b5b6200024e8262000190565b9050602081019050919050565b60005b838110156200027b5780820151818401526020810190506200025e565b838111156200028b576000848401525b50505050565b6000620002a8620002a28462000225565b62000206565b905082815260208101848484011115620002c757620002c66200018b565b5b620002d48482856200025b565b509392505050565b600082601f830112620002f457620002f362000186565b5b81516200030684826020860162000291565b91505092915050565b6000602082840312156200032857620003276200017c565b5b600082015167ffffffffffffffff81111562000349576200034862000181565b5b6200035784828501620002dc565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680620003a857607f821691505b60208210811415620003bf57620003be62000360565b5b50919050565b600082825260208201905092915050565b60008190508160005260206000209050919050565b60008154620003fa816200038f565b620004068186620003c5565b9450600182166000811462000424576001811462000437576200046e565b60ff19831686526020860193506200046e565b6200044285620003d6565b60005b83811015620004665781548189015260018201915060208101905062000445565b808801955050505b50505092915050565b600081519050919050565b60006200048f8262000477565b6200049b8185620003c5565b9350620004ad8185602086016200025b565b620004b88162000190565b840191505092915050565b60006040820190508181036000830152620004df8185620003eb565b90508181036020830152620004f5818462000482565b90509392505050565b61085c806200050e6000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c8063018ba9911461005c578063209652551461007a578063256fec88146100985780637ffaa4b6146100b657806393a09352146100d2575b600080fd5b6100646100ee565b60405161007191906103bd565b60405180910390f35b6100826100f7565b60405161008f9190610471565b60405180910390f35b6100a0610189565b6040516100ad91906104d4565b60405180910390f35b6100d060048036038101906100cb9190610638565b6101ad565b005b6100ec60048036038101906100e791906106b0565b61021f565b005b60006064905090565b60606001805461010690610728565b80601f016020809104026020016040519081016040528092919081815260200182805461013290610728565b801561017f5780601f106101545761010080835404028352916020019161017f565b820191906000526020600020905b81548152906001019060200180831161016257829003601f168201915b5050505050905090565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b81600190805190602001906101c3929190610301565b5080600290805190602001906101da929190610301565b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f999b6d464c4e3383c341bdd3a22b02dda8a7e1d69c069d252e35cb2ee2f4a3c360018460405161029f9291906107ef565b60405180910390a380600190805190602001906102bd929190610301565b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b82805461030d90610728565b90600052602060002090601f01602090048101928261032f5760008555610376565b82601f1061034857805160ff1916838001178555610376565b82800160010185558215610376579182015b8281111561037557825182559160200191906001019061035a565b5b5090506103839190610387565b5090565b5b808211156103a0576000816000905550600101610388565b5090565b6000819050919050565b6103b7816103a4565b82525050565b60006020820190506103d260008301846103ae565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156104125780820151818401526020810190506103f7565b83811115610421576000848401525b50505050565b6000601f19601f8301169050919050565b6000610443826103d8565b61044d81856103e3565b935061045d8185602086016103f4565b61046681610427565b840191505092915050565b6000602082019050818103600083015261048b8184610438565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006104be82610493565b9050919050565b6104ce816104b3565b82525050565b60006020820190506104e960008301846104c5565b92915050565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61054582610427565b810181811067ffffffffffffffff821117156105645761056361050d565b5b80604052505050565b60006105776104ef565b9050610583828261053c565b919050565b600067ffffffffffffffff8211156105a3576105a261050d565b5b6105ac82610427565b9050602081019050919050565b82818337600083830152505050565b60006105db6105d684610588565b61056d565b9050828152602081018484840111156105f7576105f6610508565b5b6106028482856105b9565b509392505050565b600082601f83011261061f5761061e610503565b5b813561062f8482602086016105c8565b91505092915050565b6000806040838503121561064f5761064e6104f9565b5b600083013567ffffffffffffffff81111561066d5761066c6104fe565b5b6106798582860161060a565b925050602083013567ffffffffffffffff81111561069a576106996104fe565b5b6106a68582860161060a565b9150509250929050565b6000602082840312156106c6576106c56104f9565b5b600082013567ffffffffffffffff8111156106e4576106e36104fe565b5b6106f08482850161060a565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061074057607f821691505b60208210811415610754576107536106f9565b5b50919050565b60008190508160005260206000209050919050565b6000815461077c81610728565b61078681866103e3565b945060018216600081146107a157600181146107b3576107e6565b60ff19831686526020860193506107e6565b6107bc8561075a565b60005b838110156107de578154818901526001820191506020810190506107bf565b808801955050505b50505092915050565b60006040820190508181036000830152610809818561076f565b9050818103602083015261081d8184610438565b9050939250505056fea2646970667358221220e37ed4b56859ad0b3a3773f57ff7b4e7a99406933fc4ff9f8ae053c52cdf3e3264736f6c63430008090033"} \ No newline at end of file diff --git a/crates/anvil/test-data/emit_logs.json b/crates/anvil/test-data/emit_logs.json index 0113e1c49..635019ae3 100644 --- a/crates/anvil/test-data/emit_logs.json +++ b/crates/anvil/test-data/emit_logs.json @@ -1,67 +1 @@ -{ - "abi": [ - { - "inputs": [ - { - "internalType": "string", - "name": "value", - "type": "string" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "author", - "type": "address" - }, - { - "indexed": false, - "internalType": "string", - "name": "oldValue", - "type": "string" - }, - { - "indexed": false, - "internalType": "string", - "name": "newValue", - "type": "string" - } - ], - "name": "ValueChanged", - "type": "event" - }, - { - "inputs": [], - "name": "getValue", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "string", - "name": "value", - "type": "string" - } - ], - "name": "setValue", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } - ], - "bin": "608060405234801561001057600080fd5b506040516105a63803806105a68339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b50604052505081516100f6915060009060208401906100fd565b505061019e565b828054600181600116156101000203166002900490600052602060002090601f0160209004810192826101335760008555610179565b82601f1061014c57805160ff1916838001178555610179565b82800160010185558215610179579182015b8281111561017957825182559160200191906001019061015e565b50610185929150610189565b5090565b5b80821115610185576000815560010161018a565b6103f9806101ad6000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063209652551461003b57806393a09352146100b8575b600080fd5b610043610160565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561007d578181015183820152602001610065565b50505050905090810190601f1680156100aa5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61015e600480360360208110156100ce57600080fd5b8101906020810181356401000000008111156100e957600080fd5b8201836020820111156100fb57600080fd5b8035906020019184600183028401116401000000008311171561011d57600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295506101f6945050505050565b005b60008054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156101ec5780601f106101c1576101008083540402835291602001916101ec565b820191906000526020600020905b8154815290600101906020018083116101cf57829003601f168201915b5050505050905090565b60408051818152600080546002600019610100600184161502019091160492820183905233927fe826f71647b8486f2bae59832124c70792fba044036720a54ec8dacdd5df4fcb9285918190602082019060608301908690801561029b5780601f106102705761010080835404028352916020019161029b565b820191906000526020600020905b81548152906001019060200180831161027e57829003601f168201915b5050838103825284518152845160209182019186019080838360005b838110156102cf5781810151838201526020016102b7565b50505050905090810190601f1680156102fc5780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a2805161031e906000906020840190610322565b5050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282610358576000855561039e565b82601f1061037157805160ff191683800117855561039e565b8280016001018555821561039e579182015b8281111561039e578251825591602001919060010190610383565b506103aa9291506103ae565b5090565b5b808211156103aa57600081556001016103af56fea2646970667358221220c1367a0db85dfe60814cdfc5141a8fe8b95c9d051a6824343085c3ba9697244a64736f6c63430007060033" -} \ No newline at end of file +{"abi":[{"inputs":[{"internalType":"string","name":"value","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"author","type":"address"},{"indexed":false,"internalType":"string","name":"oldValue","type":"string"},{"indexed":false,"internalType":"string","name":"newValue","type":"string"}],"name":"ValueChanged","type":"event"},{"inputs":[],"name":"getValue","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"value","type":"string"}],"name":"setValue","outputs":[],"stateMutability":"nonpayable","type":"function"}],"bin":"608060405234801561001057600080fd5b506040516105a63803806105a68339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b50604052505081516100f6915060009060208401906100fd565b505061019e565b828054600181600116156101000203166002900490600052602060002090601f0160209004810192826101335760008555610179565b82601f1061014c57805160ff1916838001178555610179565b82800160010185558215610179579182015b8281111561017957825182559160200191906001019061015e565b50610185929150610189565b5090565b5b80821115610185576000815560010161018a565b6103f9806101ad6000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063209652551461003b57806393a09352146100b8575b600080fd5b610043610160565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561007d578181015183820152602001610065565b50505050905090810190601f1680156100aa5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61015e600480360360208110156100ce57600080fd5b8101906020810181356401000000008111156100e957600080fd5b8201836020820111156100fb57600080fd5b8035906020019184600183028401116401000000008311171561011d57600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295506101f6945050505050565b005b60008054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156101ec5780601f106101c1576101008083540402835291602001916101ec565b820191906000526020600020905b8154815290600101906020018083116101cf57829003601f168201915b5050505050905090565b60408051818152600080546002600019610100600184161502019091160492820183905233927fe826f71647b8486f2bae59832124c70792fba044036720a54ec8dacdd5df4fcb9285918190602082019060608301908690801561029b5780601f106102705761010080835404028352916020019161029b565b820191906000526020600020905b81548152906001019060200180831161027e57829003601f168201915b5050838103825284518152845160209182019186019080838360005b838110156102cf5781810151838201526020016102b7565b50505050905090810190601f1680156102fc5780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a2805161031e906000906020840190610322565b5050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282610358576000855561039e565b82601f1061037157805160ff191683800117855561039e565b8280016001018555821561039e579182015b8281111561039e578251825591602001919060010190610383565b506103aa9291506103ae565b5090565b5b808211156103aa57600081556001016103af56fea2646970667358221220c1367a0db85dfe60814cdfc5141a8fe8b95c9d051a6824343085c3ba9697244a64736f6c63430007060033"} \ No newline at end of file diff --git a/crates/anvil/test-data/greeter.json b/crates/anvil/test-data/greeter.json index 0892e4d43..93c50f01e 100644 --- a/crates/anvil/test-data/greeter.json +++ b/crates/anvil/test-data/greeter.json @@ -1,44 +1 @@ -{ - "bytecode": { - "object": "608060405234801561001057600080fd5b506040516104913803806104918339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b50604052505081516100f6915060009060208401906100fd565b505061019e565b828054600181600116156101000203166002900490600052602060002090601f0160209004810192826101335760008555610179565b82601f1061014c57805160ff1916838001178555610179565b82800160010185558215610179579182015b8281111561017957825182559160200191906001019061015e565b50610185929150610189565b5090565b5b80821115610185576000815560010161018a565b6102e4806101ad6000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063a41368621461003b578063cfae3217146100e3575b600080fd5b6100e16004803603602081101561005157600080fd5b81019060208101813564010000000081111561006c57600080fd5b82018360208201111561007e57600080fd5b803590602001918460018302840111640100000000831117156100a057600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610160945050505050565b005b6100eb610177565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561012557818101518382015260200161010d565b50505050905090810190601f1680156101525780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b805161017390600090602084019061020d565b5050565b60008054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156102035780601f106101d857610100808354040283529160200191610203565b820191906000526020600020905b8154815290600101906020018083116101e657829003601f168201915b5050505050905090565b828054600181600116156101000203166002900490600052602060002090601f0160209004810192826102435760008555610289565b82601f1061025c57805160ff1916838001178555610289565b82800160010185558215610289579182015b8281111561028957825182559160200191906001019061026e565b50610295929150610299565b5090565b5b80821115610295576000815560010161029a56fea26469706673582212208b9161dfd195d53618942a72a3b481d61a7b142de919925a0b34f9c986e5707e64736f6c63430007060033" - }, - "abi": [ - { - "inputs": [ - { - "internalType": "string", - "name": "_greeting", - "type": "string" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [], - "name": "greet", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "string", - "name": "_greeting", - "type": "string" - } - ], - "name": "setGreeting", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } - ] -} \ No newline at end of file +{"bytecode":{"object":"608060405234801561001057600080fd5b506040516104913803806104918339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825164010000000081118282018810171561008257600080fd5b82525081516020918201929091019080838360005b838110156100af578181015183820152602001610097565b50505050905090810190601f1680156100dc5780820380516001836020036101000a031916815260200191505b50604052505081516100f6915060009060208401906100fd565b505061019e565b828054600181600116156101000203166002900490600052602060002090601f0160209004810192826101335760008555610179565b82601f1061014c57805160ff1916838001178555610179565b82800160010185558215610179579182015b8281111561017957825182559160200191906001019061015e565b50610185929150610189565b5090565b5b80821115610185576000815560010161018a565b6102e4806101ad6000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063a41368621461003b578063cfae3217146100e3575b600080fd5b6100e16004803603602081101561005157600080fd5b81019060208101813564010000000081111561006c57600080fd5b82018360208201111561007e57600080fd5b803590602001918460018302840111640100000000831117156100a057600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610160945050505050565b005b6100eb610177565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561012557818101518382015260200161010d565b50505050905090810190601f1680156101525780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b805161017390600090602084019061020d565b5050565b60008054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156102035780601f106101d857610100808354040283529160200191610203565b820191906000526020600020905b8154815290600101906020018083116101e657829003601f168201915b5050505050905090565b828054600181600116156101000203166002900490600052602060002090601f0160209004810192826102435760008555610289565b82601f1061025c57805160ff1916838001178555610289565b82800160010185558215610289579182015b8281111561028957825182559160200191906001019061026e565b50610295929150610299565b5090565b5b80821115610295576000815560010161029a56fea26469706673582212208b9161dfd195d53618942a72a3b481d61a7b142de919925a0b34f9c986e5707e64736f6c63430007060033"},"abi":[{"inputs":[{"internalType":"string","name":"_greeting","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"greet","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_greeting","type":"string"}],"name":"setGreeting","outputs":[],"stateMutability":"nonpayable","type":"function"}]} \ No newline at end of file diff --git a/crates/anvil/test-data/multicall.json b/crates/anvil/test-data/multicall.json index e0be8fc5d..e7f7d9f11 100644 --- a/crates/anvil/test-data/multicall.json +++ b/crates/anvil/test-data/multicall.json @@ -1,144 +1 @@ -{ - "abi": [ - { - "inputs": [ - { - "components": [ - { - "internalType": "address", - "name": "target", - "type": "address" - }, - { - "internalType": "bytes", - "name": "callData", - "type": "bytes" - } - ], - "internalType": "struct Multicall.Call[]", - "name": "calls", - "type": "tuple[]" - } - ], - "name": "aggregate", - "outputs": [ - { - "internalType": "uint256", - "name": "blockNumber", - "type": "uint256" - }, - { - "internalType": "bytes[]", - "name": "returnData", - "type": "bytes[]" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "blockNumber", - "type": "uint256" - } - ], - "name": "getBlockHash", - "outputs": [ - { - "internalType": "bytes32", - "name": "blockHash", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getCurrentBlockCoinbase", - "outputs": [ - { - "internalType": "address", - "name": "coinbase", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getCurrentBlockDifficulty", - "outputs": [ - { - "internalType": "uint256", - "name": "difficulty", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getCurrentBlockGasLimit", - "outputs": [ - { - "internalType": "uint256", - "name": "gaslimit", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getCurrentBlockTimestamp", - "outputs": [ - { - "internalType": "uint256", - "name": "timestamp", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "addr", - "type": "address" - } - ], - "name": "getEthBalance", - "outputs": [ - { - "internalType": "uint256", - "name": "balance", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getLastBlockHash", - "outputs": [ - { - "internalType": "bytes32", - "name": "blockHash", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - } - ], - "bin": "608060405234801561001057600080fd5b50610abb806100206000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806372425d9d1161005b57806372425d9d1461012a57806386d516e814610148578063a8b0574e14610166578063ee82ac5e1461018457610088565b80630f28c97d1461008d578063252dba42146100ab57806327e86d6e146100dc5780634d2301cc146100fa575b600080fd5b6100956101b4565b6040516100a29190610381565b60405180910390f35b6100c560048036038101906100c091906106b0565b6101bc565b6040516100d3929190610843565b60405180910390f35b6100e461030f565b6040516100f1919061088c565b60405180910390f35b610114600480360381019061010f91906108a7565b610324565b6040516101219190610381565b60405180910390f35b610132610345565b60405161013f9190610381565b60405180910390f35b61015061034d565b60405161015d9190610381565b60405180910390f35b61016e610355565b60405161017b91906108e3565b60405180910390f35b61019e6004803603810190610199919061092a565b61035d565b6040516101ab919061088c565b60405180910390f35b600042905090565b60006060439150825167ffffffffffffffff8111156101de576101dd6103c6565b5b60405190808252806020026020018201604052801561021157816020015b60608152602001906001900390816101fc5790505b50905060005b83518110156103095760008085838151811061023657610235610957565b5b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff1686848151811061026b5761026a610957565b5b60200260200101516020015160405161028491906109c2565b6000604051808303816000865af19150503d80600081146102c1576040519150601f19603f3d011682016040523d82523d6000602084013e6102c6565b606091505b5091509150816102d557600080fd5b808484815181106102e9576102e8610957565b5b60200260200101819052505050808061030190610a08565b915050610217565b50915091565b600060014361031e9190610a51565b40905090565b60008173ffffffffffffffffffffffffffffffffffffffff16319050919050565b600044905090565b600045905090565b600041905090565b600081409050919050565b6000819050919050565b61037b81610368565b82525050565b60006020820190506103966000830184610372565b92915050565b6000604051905090565b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6103fe826103b5565b810181811067ffffffffffffffff8211171561041d5761041c6103c6565b5b80604052505050565b600061043061039c565b905061043c82826103f5565b919050565b600067ffffffffffffffff82111561045c5761045b6103c6565b5b602082029050602081019050919050565b600080fd5b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006104a78261047c565b9050919050565b6104b78161049c565b81146104c257600080fd5b50565b6000813590506104d4816104ae565b92915050565b600080fd5b600067ffffffffffffffff8211156104fa576104f96103c6565b5b610503826103b5565b9050602081019050919050565b82818337600083830152505050565b600061053261052d846104df565b610426565b90508281526020810184848401111561054e5761054d6104da565b5b610559848285610510565b509392505050565b600082601f830112610576576105756103b0565b5b813561058684826020860161051f565b91505092915050565b6000604082840312156105a5576105a4610472565b5b6105af6040610426565b905060006105bf848285016104c5565b600083015250602082013567ffffffffffffffff8111156105e3576105e2610477565b5b6105ef84828501610561565b60208301525092915050565b600061060e61060984610441565b610426565b905080838252602082019050602084028301858111156106315761063061046d565b5b835b8181101561067857803567ffffffffffffffff811115610656576106556103b0565b5b808601610663898261058f565b85526020850194505050602081019050610633565b5050509392505050565b600082601f830112610697576106966103b0565b5b81356106a78482602086016105fb565b91505092915050565b6000602082840312156106c6576106c56103a6565b5b600082013567ffffffffffffffff8111156106e4576106e36103ab565b5b6106f084828501610682565b91505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561075f578082015181840152602081019050610744565b8381111561076e576000848401525b50505050565b600061077f82610725565b6107898185610730565b9350610799818560208601610741565b6107a2816103b5565b840191505092915050565b60006107b98383610774565b905092915050565b6000602082019050919050565b60006107d9826106f9565b6107e38185610704565b9350836020820285016107f585610715565b8060005b85811015610831578484038952815161081285826107ad565b945061081d836107c1565b925060208a019950506001810190506107f9565b50829750879550505050505092915050565b60006040820190506108586000830185610372565b818103602083015261086a81846107ce565b90509392505050565b6000819050919050565b61088681610873565b82525050565b60006020820190506108a1600083018461087d565b92915050565b6000602082840312156108bd576108bc6103a6565b5b60006108cb848285016104c5565b91505092915050565b6108dd8161049c565b82525050565b60006020820190506108f860008301846108d4565b92915050565b61090781610368565b811461091257600080fd5b50565b600081359050610924816108fe565b92915050565b6000602082840312156109405761093f6103a6565b5b600061094e84828501610915565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600081905092915050565b600061099c82610725565b6109a68185610986565b93506109b6818560208601610741565b80840191505092915050565b60006109ce8284610991565b915081905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610a1382610368565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415610a4657610a456109d9565b5b600182019050919050565b6000610a5c82610368565b9150610a6783610368565b925082821015610a7a57610a796109d9565b5b82820390509291505056fea2646970667358221220e5023d90063e0939116a41565414721ba1350cd3e98b12e7b65983a039644df964736f6c634300080a0033" -} \ No newline at end of file +{"abi":[{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct Multicall.Call[]","name":"calls","type":"tuple[]"}],"name":"aggregate","outputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"bytes[]","name":"returnData","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getBlockHash","outputs":[{"internalType":"bytes32","name":"blockHash","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentBlockCoinbase","outputs":[{"internalType":"address","name":"coinbase","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentBlockDifficulty","outputs":[{"internalType":"uint256","name":"difficulty","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentBlockGasLimit","outputs":[{"internalType":"uint256","name":"gaslimit","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentBlockTimestamp","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"getEthBalance","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLastBlockHash","outputs":[{"internalType":"bytes32","name":"blockHash","type":"bytes32"}],"stateMutability":"view","type":"function"}],"bin":"608060405234801561001057600080fd5b50610abb806100206000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806372425d9d1161005b57806372425d9d1461012a57806386d516e814610148578063a8b0574e14610166578063ee82ac5e1461018457610088565b80630f28c97d1461008d578063252dba42146100ab57806327e86d6e146100dc5780634d2301cc146100fa575b600080fd5b6100956101b4565b6040516100a29190610381565b60405180910390f35b6100c560048036038101906100c091906106b0565b6101bc565b6040516100d3929190610843565b60405180910390f35b6100e461030f565b6040516100f1919061088c565b60405180910390f35b610114600480360381019061010f91906108a7565b610324565b6040516101219190610381565b60405180910390f35b610132610345565b60405161013f9190610381565b60405180910390f35b61015061034d565b60405161015d9190610381565b60405180910390f35b61016e610355565b60405161017b91906108e3565b60405180910390f35b61019e6004803603810190610199919061092a565b61035d565b6040516101ab919061088c565b60405180910390f35b600042905090565b60006060439150825167ffffffffffffffff8111156101de576101dd6103c6565b5b60405190808252806020026020018201604052801561021157816020015b60608152602001906001900390816101fc5790505b50905060005b83518110156103095760008085838151811061023657610235610957565b5b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff1686848151811061026b5761026a610957565b5b60200260200101516020015160405161028491906109c2565b6000604051808303816000865af19150503d80600081146102c1576040519150601f19603f3d011682016040523d82523d6000602084013e6102c6565b606091505b5091509150816102d557600080fd5b808484815181106102e9576102e8610957565b5b60200260200101819052505050808061030190610a08565b915050610217565b50915091565b600060014361031e9190610a51565b40905090565b60008173ffffffffffffffffffffffffffffffffffffffff16319050919050565b600044905090565b600045905090565b600041905090565b600081409050919050565b6000819050919050565b61037b81610368565b82525050565b60006020820190506103966000830184610372565b92915050565b6000604051905090565b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6103fe826103b5565b810181811067ffffffffffffffff8211171561041d5761041c6103c6565b5b80604052505050565b600061043061039c565b905061043c82826103f5565b919050565b600067ffffffffffffffff82111561045c5761045b6103c6565b5b602082029050602081019050919050565b600080fd5b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006104a78261047c565b9050919050565b6104b78161049c565b81146104c257600080fd5b50565b6000813590506104d4816104ae565b92915050565b600080fd5b600067ffffffffffffffff8211156104fa576104f96103c6565b5b610503826103b5565b9050602081019050919050565b82818337600083830152505050565b600061053261052d846104df565b610426565b90508281526020810184848401111561054e5761054d6104da565b5b610559848285610510565b509392505050565b600082601f830112610576576105756103b0565b5b813561058684826020860161051f565b91505092915050565b6000604082840312156105a5576105a4610472565b5b6105af6040610426565b905060006105bf848285016104c5565b600083015250602082013567ffffffffffffffff8111156105e3576105e2610477565b5b6105ef84828501610561565b60208301525092915050565b600061060e61060984610441565b610426565b905080838252602082019050602084028301858111156106315761063061046d565b5b835b8181101561067857803567ffffffffffffffff811115610656576106556103b0565b5b808601610663898261058f565b85526020850194505050602081019050610633565b5050509392505050565b600082601f830112610697576106966103b0565b5b81356106a78482602086016105fb565b91505092915050565b6000602082840312156106c6576106c56103a6565b5b600082013567ffffffffffffffff8111156106e4576106e36103ab565b5b6106f084828501610682565b91505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561075f578082015181840152602081019050610744565b8381111561076e576000848401525b50505050565b600061077f82610725565b6107898185610730565b9350610799818560208601610741565b6107a2816103b5565b840191505092915050565b60006107b98383610774565b905092915050565b6000602082019050919050565b60006107d9826106f9565b6107e38185610704565b9350836020820285016107f585610715565b8060005b85811015610831578484038952815161081285826107ad565b945061081d836107c1565b925060208a019950506001810190506107f9565b50829750879550505050505092915050565b60006040820190506108586000830185610372565b818103602083015261086a81846107ce565b90509392505050565b6000819050919050565b61088681610873565b82525050565b60006020820190506108a1600083018461087d565b92915050565b6000602082840312156108bd576108bc6103a6565b5b60006108cb848285016104c5565b91505092915050565b6108dd8161049c565b82525050565b60006020820190506108f860008301846108d4565b92915050565b61090781610368565b811461091257600080fd5b50565b600081359050610924816108fe565b92915050565b6000602082840312156109405761093f6103a6565b5b600061094e84828501610915565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600081905092915050565b600061099c82610725565b6109a68185610986565b93506109b6818560208601610741565b80840191505092915050565b60006109ce8284610991565b915081905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610a1382610368565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415610a4657610a456109d9565b5b600182019050919050565b6000610a5c82610368565b9150610a6783610368565b925082821015610a7a57610a796109d9565b5b82820390509291505056fea2646970667358221220e5023d90063e0939116a41565414721ba1350cd3e98b12e7b65983a039644df964736f6c634300080a0033"} \ No newline at end of file diff --git a/crates/anvil/test-data/storage_sample.json b/crates/anvil/test-data/storage_sample.json index 7e2daf48f..5241cb08e 100644 --- a/crates/anvil/test-data/storage_sample.json +++ b/crates/anvil/test-data/storage_sample.json @@ -1,33 +1 @@ -{ - "0x0000000000000000000000000000000000000000000000000000000000000022": "0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b", - "0x0000000000000000000000000000000000000000000000000000000000000023": "0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71", - "0x0000000000000000000000000000000000000000000000000000000000000024": "0xc78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c", - "0x0000000000000000000000000000000000000000000000000000000000000025": "0x536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c", - "0x0000000000000000000000000000000000000000000000000000000000000026": "0x9efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30", - "0x0000000000000000000000000000000000000000000000000000000000000027": "0xd88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa1", - "0x0000000000000000000000000000000000000000000000000000000000000028": "0x87eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c", - "0x0000000000000000000000000000000000000000000000000000000000000029": "0x26846476fd5fc54a5d43385167c95144f2643f533cc85bb9d16b782f8d7db193", - "0x000000000000000000000000000000000000000000000000000000000000002a": "0x506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1", - "0x000000000000000000000000000000000000000000000000000000000000002b": "0xffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b", - "0x000000000000000000000000000000000000000000000000000000000000002c": "0x6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220", - "0x000000000000000000000000000000000000000000000000000000000000002d": "0xb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5f", - "0x000000000000000000000000000000000000000000000000000000000000002e": "0xdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85e", - "0x000000000000000000000000000000000000000000000000000000000000002f": "0xb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784", - "0x0000000000000000000000000000000000000000000000000000000000000030": "0xd49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb", - "0x0000000000000000000000000000000000000000000000000000000000000031": "0x8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb", - "0x0000000000000000000000000000000000000000000000000000000000000032": "0x8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab", - "0x0000000000000000000000000000000000000000000000000000000000000033": "0x95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4", - "0x0000000000000000000000000000000000000000000000000000000000000034": "0xf893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17f", - "0x0000000000000000000000000000000000000000000000000000000000000035": "0xcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa", - "0x0000000000000000000000000000000000000000000000000000000000000036": "0x8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9c", - "0x0000000000000000000000000000000000000000000000000000000000000037": "0xfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167", - "0x0000000000000000000000000000000000000000000000000000000000000038": "0xe71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d7", - "0x0000000000000000000000000000000000000000000000000000000000000039": "0x31206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc0", - "0x000000000000000000000000000000000000000000000000000000000000003a": "0x21352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544", - "0x000000000000000000000000000000000000000000000000000000000000003b": "0x619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765", - "0x000000000000000000000000000000000000000000000000000000000000003c": "0x7cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4", - "0x000000000000000000000000000000000000000000000000000000000000003d": "0x848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe1", - "0x000000000000000000000000000000000000000000000000000000000000003e": "0x8869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636", - "0x000000000000000000000000000000000000000000000000000000000000003f": "0xb5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c", - "0x0000000000000000000000000000000000000000000000000000000000000040": "0x985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7" -} \ No newline at end of file +{"0x0000000000000000000000000000000000000000000000000000000000000022":"0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b","0x0000000000000000000000000000000000000000000000000000000000000023":"0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71","0x0000000000000000000000000000000000000000000000000000000000000024":"0xc78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c","0x0000000000000000000000000000000000000000000000000000000000000025":"0x536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c","0x0000000000000000000000000000000000000000000000000000000000000026":"0x9efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30","0x0000000000000000000000000000000000000000000000000000000000000027":"0xd88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa1","0x0000000000000000000000000000000000000000000000000000000000000028":"0x87eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c","0x0000000000000000000000000000000000000000000000000000000000000029":"0x26846476fd5fc54a5d43385167c95144f2643f533cc85bb9d16b782f8d7db193","0x000000000000000000000000000000000000000000000000000000000000002a":"0x506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1","0x000000000000000000000000000000000000000000000000000000000000002b":"0xffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b","0x000000000000000000000000000000000000000000000000000000000000002c":"0x6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220","0x000000000000000000000000000000000000000000000000000000000000002d":"0xb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5f","0x000000000000000000000000000000000000000000000000000000000000002e":"0xdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85e","0x000000000000000000000000000000000000000000000000000000000000002f":"0xb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784","0x0000000000000000000000000000000000000000000000000000000000000030":"0xd49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb","0x0000000000000000000000000000000000000000000000000000000000000031":"0x8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb","0x0000000000000000000000000000000000000000000000000000000000000032":"0x8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab","0x0000000000000000000000000000000000000000000000000000000000000033":"0x95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4","0x0000000000000000000000000000000000000000000000000000000000000034":"0xf893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17f","0x0000000000000000000000000000000000000000000000000000000000000035":"0xcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa","0x0000000000000000000000000000000000000000000000000000000000000036":"0x8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9c","0x0000000000000000000000000000000000000000000000000000000000000037":"0xfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167","0x0000000000000000000000000000000000000000000000000000000000000038":"0xe71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d7","0x0000000000000000000000000000000000000000000000000000000000000039":"0x31206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc0","0x000000000000000000000000000000000000000000000000000000000000003a":"0x21352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544","0x000000000000000000000000000000000000000000000000000000000000003b":"0x619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765","0x000000000000000000000000000000000000000000000000000000000000003c":"0x7cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4","0x000000000000000000000000000000000000000000000000000000000000003d":"0x848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe1","0x000000000000000000000000000000000000000000000000000000000000003e":"0x8869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636","0x000000000000000000000000000000000000000000000000000000000000003f":"0xb5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c","0x0000000000000000000000000000000000000000000000000000000000000040":"0x985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7"} \ No newline at end of file diff --git a/crates/cast/tests/fixtures/ERC20Artifact.json b/crates/cast/tests/fixtures/ERC20Artifact.json index 4f4fd6e61..508464c24 100644 --- a/crates/cast/tests/fixtures/ERC20Artifact.json +++ b/crates/cast/tests/fixtures/ERC20Artifact.json @@ -1,385 +1 @@ -{ - "abi": [ - { - "inputs": [ - { - "internalType": "string", - "name": "name", - "type": "string" - }, - { - "internalType": "string", - "name": "symbol", - "type": "string" - }, - { - "internalType": "uint8", - "name": "decimals", - "type": "uint8" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "Approval", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "Transfer", - "type": "event" - }, - { - "inputs": [], - "name": "DOMAIN_SEPARATOR", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - }, - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "allowance", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "approve", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "balanceOf", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "decimals", - "outputs": [ - { - "internalType": "uint8", - "name": "", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "mint", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "name", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "nonces", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - }, - { - "internalType": "uint8", - "name": "v", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "r", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "s", - "type": "bytes32" - } - ], - "name": "permit", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "symbol", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "totalSupply", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "transfer", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "transferFrom", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - } - ], - "bytecode": { - "object": "0x60e06040523480156200001157600080fd5b5060405162000f7738038062000f7783398101604081905262000034916200029a565b82828282600090805190602001906200004f92919062000127565b5081516200006590600190602085019062000127565b5060ff81166080524660a0526200007b6200008b565b60c0525062000400945050505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6000604051620000bf91906200035c565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b82805462000135906200031f565b90600052602060002090601f016020900481019282620001595760008555620001a4565b82601f106200017457805160ff1916838001178555620001a4565b82800160010185558215620001a4579182015b82811115620001a457825182559160200191906001019062000187565b50620001b2929150620001b6565b5090565b5b80821115620001b25760008155600101620001b7565b634e487b7160e01b600052604160045260246000fd5b600082601f830112620001f557600080fd5b81516001600160401b0380821115620002125762000212620001cd565b604051601f8301601f19908116603f011681019082821181831017156200023d576200023d620001cd565b816040528381526020925086838588010111156200025a57600080fd5b600091505b838210156200027e57858201830151818301840152908201906200025f565b83821115620002905760008385830101525b9695505050505050565b600080600060608486031215620002b057600080fd5b83516001600160401b0380821115620002c857600080fd5b620002d687838801620001e3565b94506020860151915080821115620002ed57600080fd5b50620002fc86828701620001e3565b925050604084015160ff811681146200031457600080fd5b809150509250925092565b600181811c908216806200033457607f821691505b602082108114156200035657634e487b7160e01b600052602260045260246000fd5b50919050565b600080835481600182811c9150808316806200037957607f831692505b60208084108214156200039a57634e487b7160e01b86526022600452602486fd5b818015620003b15760018114620003c357620003f2565b60ff19861689528489019650620003f2565b60008a81526020902060005b86811015620003ea5781548b820152908501908301620003cf565b505084890196505b509498975050505050505050565b60805160a05160c051610b476200043060003960006104530152600061041e015260006101440152610b476000f3fe608060405234801561001057600080fd5b50600436106100cf5760003560e01c806340c10f191161008c57806395d89b411161006657806395d89b41146101d5578063a9059cbb146101dd578063d505accf146101f0578063dd62ed3e1461020357600080fd5b806340c10f191461018057806370a08231146101955780637ecebe00146101b557600080fd5b806306fdde03146100d4578063095ea7b3146100f257806318160ddd1461011557806323b872dd1461012c578063313ce5671461013f5780633644e51514610178575b600080fd5b6100dc61022e565b6040516100e99190610856565b60405180910390f35b6101056101003660046108c7565b6102bc565b60405190151581526020016100e9565b61011e60025481565b6040519081526020016100e9565b61010561013a3660046108f1565b610328565b6101667f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff90911681526020016100e9565b61011e61041a565b61019361018e3660046108c7565b610475565b005b61011e6101a336600461092d565b60036020526000908152604090205481565b61011e6101c336600461092d565b60056020526000908152604090205481565b6100dc610483565b6101056101eb3660046108c7565b610490565b6101936101fe36600461094f565b610508565b61011e6102113660046109c2565b600460209081526000928352604080842090915290825290205481565b6000805461023b906109f5565b80601f0160208091040260200160405190810160405280929190818152602001828054610267906109f5565b80156102b45780601f10610289576101008083540402835291602001916102b4565b820191906000526020600020905b81548152906001019060200180831161029757829003601f168201915b505050505081565b3360008181526004602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906103179086815260200190565b60405180910390a350600192915050565b6001600160a01b038316600090815260046020908152604080832033845290915281205460001981146103845761035f8382610a46565b6001600160a01b03861660009081526004602090815260408083203384529091529020555b6001600160a01b038516600090815260036020526040812080548592906103ac908490610a46565b90915550506001600160a01b03808516600081815260036020526040908190208054870190555190918716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906104079087815260200190565b60405180910390a3506001949350505050565b60007f000000000000000000000000000000000000000000000000000000000000000046146104505761044b610751565b905090565b507f000000000000000000000000000000000000000000000000000000000000000090565b61047f82826107eb565b5050565b6001805461023b906109f5565b336000908152600360205260408120805483919083906104b1908490610a46565b90915550506001600160a01b038316600081815260036020526040908190208054850190555133907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906103179086815260200190565b4284101561055d5760405162461bcd60e51b815260206004820152601760248201527f5045524d49545f444541444c494e455f4558504952454400000000000000000060448201526064015b60405180910390fd5b6000600161056961041a565b6001600160a01b038a811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e08301909152805192019190912061190160f01b6101008301526101028201929092526101228101919091526101420160408051601f198184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015610675573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116158015906106ab5750876001600160a01b0316816001600160a01b0316145b6106e85760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa9a4a3a722a960911b6044820152606401610554565b6001600160a01b0390811660009081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60006040516107839190610a5d565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b80600260008282546107fd9190610af9565b90915550506001600160a01b0382166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b600060208083528351808285015260005b8181101561088357858101830151858201604001528201610867565b81811115610895576000604083870101525b50601f01601f1916929092016040019392505050565b80356001600160a01b03811681146108c257600080fd5b919050565b600080604083850312156108da57600080fd5b6108e3836108ab565b946020939093013593505050565b60008060006060848603121561090657600080fd5b61090f846108ab565b925061091d602085016108ab565b9150604084013590509250925092565b60006020828403121561093f57600080fd5b610948826108ab565b9392505050565b600080600080600080600060e0888a03121561096a57600080fd5b610973886108ab565b9650610981602089016108ab565b95506040880135945060608801359350608088013560ff811681146109a557600080fd5b9699959850939692959460a0840135945060c09093013592915050565b600080604083850312156109d557600080fd5b6109de836108ab565b91506109ec602084016108ab565b90509250929050565b600181811c90821680610a0957607f821691505b60208210811415610a2a57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b600082821015610a5857610a58610a30565b500390565b600080835481600182811c915080831680610a7957607f831692505b6020808410821415610a9957634e487b7160e01b86526022600452602486fd5b818015610aad5760018114610abe57610aeb565b60ff19861689528489019650610aeb565b60008a81526020902060005b86811015610ae35781548b820152908501908301610aca565b505084890196505b509498975050505050505050565b60008219821115610b0c57610b0c610a30565b50019056fea26469706673582212209ca55739676d094f8ef4b3b5cfcc08f600b805843c4c3027beb2b4159a02f63664736f6c634300080a0033", - "sourceMap": "113:230:22:-:0;;;148:102;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;224:4;230:6;238:8;2098:5:10;2091:4;:12;;;;;;;;;;;;:::i;:::-;-1:-1:-1;2113:16:10;;;;:6;;:16;;;;;:::i;:::-;-1:-1:-1;2139:20:10;;;;;2189:13;2170:32;;2239:24;:22;:24::i;:::-;2212:51;;-1:-1:-1;113:230:22;;-1:-1:-1;;;;;113:230:22;5507:446:10;5572:7;5669:95;5802:4;5786:22;;;;;;:::i;:::-;;;;;;;;;;5637:295;;;3635:25:23;;;;3676:18;;3669:34;;;;5830:14:10;3719:18:23;;;3712:34;5866:13:10;3762:18:23;;;3755:34;5909:4:10;3805:19:23;;;3798:61;3607:19;;5637:295:10;;;;;;;;;;;;5610:336;;;;;;5591:355;;5507:446;:::o;113:230:22:-;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;113:230:22;;;-1:-1:-1;113:230:22;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;14:127:23;75:10;70:3;66:20;63:1;56:31;106:4;103:1;96:15;130:4;127:1;120:15;146:885;200:5;253:3;246:4;238:6;234:17;230:27;220:55;;271:1;268;261:12;220:55;294:13;;-1:-1:-1;;;;;356:10:23;;;353:36;;;369:18;;:::i;:::-;444:2;438:9;412:2;498:13;;-1:-1:-1;;494:22:23;;;518:2;490:31;486:40;474:53;;;542:18;;;562:22;;;539:46;536:72;;;588:18;;:::i;:::-;628:10;624:2;617:22;663:2;655:6;648:18;685:4;675:14;;730:3;725:2;720;712:6;708:15;704:24;701:33;698:53;;;747:1;744;737:12;698:53;769:1;760:10;;779:133;793:2;790:1;787:9;779:133;;;881:14;;;877:23;;871:30;850:14;;;846:23;;839:63;804:10;;;;779:133;;;930:2;927:1;924:9;921:80;;;989:1;984:2;979;971:6;967:15;963:24;956:35;921:80;1019:6;146:885;-1:-1:-1;;;;;;146:885:23:o;1036:712::-;1142:6;1150;1158;1211:2;1199:9;1190:7;1186:23;1182:32;1179:52;;;1227:1;1224;1217:12;1179:52;1254:16;;-1:-1:-1;;;;;1319:14:23;;;1316:34;;;1346:1;1343;1336:12;1316:34;1369:61;1422:7;1413:6;1402:9;1398:22;1369:61;:::i;:::-;1359:71;;1476:2;1465:9;1461:18;1455:25;1439:41;;1505:2;1495:8;1492:16;1489:36;;;1521:1;1518;1511:12;1489:36;;1544:63;1599:7;1588:8;1577:9;1573:24;1544:63;:::i;:::-;1534:73;;;1650:2;1639:9;1635:18;1629:25;1694:4;1687:5;1683:16;1676:5;1673:27;1663:55;;1714:1;1711;1704:12;1663:55;1737:5;1727:15;;;1036:712;;;;;:::o;1753:380::-;1832:1;1828:12;;;;1875;;;1896:61;;1950:4;1942:6;1938:17;1928:27;;1896:61;2003:2;1995:6;1992:14;1972:18;1969:38;1966:161;;;2049:10;2044:3;2040:20;2037:1;2030:31;2084:4;2081:1;2074:15;2112:4;2109:1;2102:15;1966:161;;1753:380;;;:::o;2267:1104::-;2397:3;2426:1;2459:6;2453:13;2489:3;2511:1;2539:9;2535:2;2531:18;2521:28;;2599:2;2588:9;2584:18;2621;2611:61;;2665:4;2657:6;2653:17;2643:27;;2611:61;2691:2;2739;2731:6;2728:14;2708:18;2705:38;2702:165;;;-1:-1:-1;;;2766:33:23;;2822:4;2819:1;2812:15;2852:4;2773:3;2840:17;2702:165;2883:18;2910:104;;;;3028:1;3023:323;;;;2876:470;;2910:104;-1:-1:-1;;2943:24:23;;2931:37;;2988:16;;;;-1:-1:-1;2910:104:23;;3023:323;2214:1;2207:14;;;2251:4;2238:18;;3121:1;3135:165;3149:6;3146:1;3143:13;3135:165;;;3227:14;;3214:11;;;3207:35;3270:16;;;;3164:10;;3135:165;;;3139:3;;3329:6;3324:3;3320:16;3313:23;;2876:470;-1:-1:-1;3362:3:23;;2267:1104;-1:-1:-1;;;;;;;;2267:1104:23:o;3376:489::-;113:230:22;;;;;;;;;;;;;;;;;;;;;;;;", - "linkReferences": {} - }, - "deployedBytecode": { - "object": "0x608060405234801561001057600080fd5b50600436106100cf5760003560e01c806340c10f191161008c57806395d89b411161006657806395d89b41146101d5578063a9059cbb146101dd578063d505accf146101f0578063dd62ed3e1461020357600080fd5b806340c10f191461018057806370a08231146101955780637ecebe00146101b557600080fd5b806306fdde03146100d4578063095ea7b3146100f257806318160ddd1461011557806323b872dd1461012c578063313ce5671461013f5780633644e51514610178575b600080fd5b6100dc61022e565b6040516100e99190610856565b60405180910390f35b6101056101003660046108c7565b6102bc565b60405190151581526020016100e9565b61011e60025481565b6040519081526020016100e9565b61010561013a3660046108f1565b610328565b6101667f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff90911681526020016100e9565b61011e61041a565b61019361018e3660046108c7565b610475565b005b61011e6101a336600461092d565b60036020526000908152604090205481565b61011e6101c336600461092d565b60056020526000908152604090205481565b6100dc610483565b6101056101eb3660046108c7565b610490565b6101936101fe36600461094f565b610508565b61011e6102113660046109c2565b600460209081526000928352604080842090915290825290205481565b6000805461023b906109f5565b80601f0160208091040260200160405190810160405280929190818152602001828054610267906109f5565b80156102b45780601f10610289576101008083540402835291602001916102b4565b820191906000526020600020905b81548152906001019060200180831161029757829003601f168201915b505050505081565b3360008181526004602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906103179086815260200190565b60405180910390a350600192915050565b6001600160a01b038316600090815260046020908152604080832033845290915281205460001981146103845761035f8382610a46565b6001600160a01b03861660009081526004602090815260408083203384529091529020555b6001600160a01b038516600090815260036020526040812080548592906103ac908490610a46565b90915550506001600160a01b03808516600081815260036020526040908190208054870190555190918716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906104079087815260200190565b60405180910390a3506001949350505050565b60007f000000000000000000000000000000000000000000000000000000000000000046146104505761044b610751565b905090565b507f000000000000000000000000000000000000000000000000000000000000000090565b61047f82826107eb565b5050565b6001805461023b906109f5565b336000908152600360205260408120805483919083906104b1908490610a46565b90915550506001600160a01b038316600081815260036020526040908190208054850190555133907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906103179086815260200190565b4284101561055d5760405162461bcd60e51b815260206004820152601760248201527f5045524d49545f444541444c494e455f4558504952454400000000000000000060448201526064015b60405180910390fd5b6000600161056961041a565b6001600160a01b038a811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e08301909152805192019190912061190160f01b6101008301526101028201929092526101228101919091526101420160408051601f198184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015610675573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116158015906106ab5750876001600160a01b0316816001600160a01b0316145b6106e85760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa9a4a3a722a960911b6044820152606401610554565b6001600160a01b0390811660009081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60006040516107839190610a5d565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b80600260008282546107fd9190610af9565b90915550506001600160a01b0382166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b600060208083528351808285015260005b8181101561088357858101830151858201604001528201610867565b81811115610895576000604083870101525b50601f01601f1916929092016040019392505050565b80356001600160a01b03811681146108c257600080fd5b919050565b600080604083850312156108da57600080fd5b6108e3836108ab565b946020939093013593505050565b60008060006060848603121561090657600080fd5b61090f846108ab565b925061091d602085016108ab565b9150604084013590509250925092565b60006020828403121561093f57600080fd5b610948826108ab565b9392505050565b600080600080600080600060e0888a03121561096a57600080fd5b610973886108ab565b9650610981602089016108ab565b95506040880135945060608801359350608088013560ff811681146109a557600080fd5b9699959850939692959460a0840135945060c09093013592915050565b600080604083850312156109d557600080fd5b6109de836108ab565b91506109ec602084016108ab565b90509250929050565b600181811c90821680610a0957607f821691505b60208210811415610a2a57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b600082821015610a5857610a58610a30565b500390565b600080835481600182811c915080831680610a7957607f831692505b6020808410821415610a9957634e487b7160e01b86526022600452602486fd5b818015610aad5760018114610abe57610aeb565b60ff19861689528489019650610aeb565b60008a81526020902060005b86811015610ae35781548b820152908501908301610aca565b505084890196505b509498975050505050505050565b60008219821115610b0c57610b0c610a30565b50019056fea26469706673582212209ca55739676d094f8ef4b3b5cfcc08f600b805843c4c3027beb2b4159a02f63664736f6c634300080a0033", - "sourceMap": "113:230:22:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1028:18:10;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;2458:211;;;;;;:::i;:::-;;:::i;:::-;;;1218:14:23;;1211:22;1193:41;;1181:2;1166:18;2458:211:10;1053:187:23;1301:26:10;;;;;;;;;1391:25:23;;;1379:2;1364:18;1301:26:10;1245:177:23;3054:592:10;;;;;;:::i;:::-;;:::i;1080:31::-;;;;;;;;1932:4:23;1920:17;;;1902:36;;1890:2;1875:18;1080:31:10;1760:184:23;5324:177:10;;;:::i;256:85:22:-;;;;;;:::i;:::-;;:::i;:::-;;1334:44:10;;;;;;:::i;:::-;;;;;;;;;;;;;;1748:41;;;;;;:::i;:::-;;;;;;;;;;;;;;1053:20;;;:::i;2675:373::-;;;;;;:::i;:::-;;:::i;3835:1483::-;;;;;;:::i;:::-;;:::i;1385:64::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;1028:18;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;2458:211::-;2558:10;2532:4;2548:21;;;:9;:21;;;;;;;;-1:-1:-1;;;;;2548:30:10;;;;;;;;;;:39;;;2603:37;2532:4;;2548:30;;2603:37;;;;2581:6;1391:25:23;;1379:2;1364:18;;1245:177;2603:37:10;;;;;;;;-1:-1:-1;2658:4:10;2458:211;;;;:::o;3054:592::-;-1:-1:-1;;;;;3206:15:10;;3172:4;3206:15;;;:9;:15;;;;;;;;3222:10;3206:27;;;;;;;;-1:-1:-1;;3284:28:10;;3280:80;;3344:16;3354:6;3344:7;:16;:::i;:::-;-1:-1:-1;;;;;3314:15:10;;;;;;:9;:15;;;;;;;;3330:10;3314:27;;;;;;;:46;3280:80;-1:-1:-1;;;;;3371:15:10;;;;;;:9;:15;;;;;:25;;3390:6;;3371:15;:25;;3390:6;;3371:25;:::i;:::-;;;;-1:-1:-1;;;;;;;3542:13:10;;;;;;;:9;:13;;;;;;;:23;;;;;;3591:26;3542:13;;3591:26;;;;;;;3559:6;1391:25:23;;1379:2;1364:18;;1245:177;3591:26:10;;;;;;;;-1:-1:-1;3635:4:10;;3054:592;-1:-1:-1;;;;3054:592:10:o;5324:177::-;5381:7;5424:16;5407:13;:33;:87;;5470:24;:22;:24::i;:::-;5400:94;;5324:177;:::o;5407:87::-;-1:-1:-1;5443:24:10;;5324:177::o;256:85:22:-;317:17;323:2;327:6;317:5;:17::i;:::-;256:85;;:::o;1053:20:10:-;;;;;;;:::i;2675:373::-;2771:10;2745:4;2761:21;;;:9;:21;;;;;:31;;2786:6;;2761:21;2745:4;;2761:31;;2786:6;;2761:31;:::i;:::-;;;;-1:-1:-1;;;;;;;2938:13:10;;;;;;:9;:13;;;;;;;:23;;;;;;2987:32;2996:10;;2987:32;;;;2955:6;1391:25:23;;1379:2;1364:18;;1245:177;3835:1483:10;4054:15;4042:8;:27;;4034:63;;;;-1:-1:-1;;;4034:63:10;;4134:2:23;4034:63:10;;;4116:21:23;4173:2;4153:18;;;4146:30;4212:25;4192:18;;;4185:53;4255:18;;4034:63:10;;;;;;;;;4262:24;4289:805;4425:18;:16;:18::i;:::-;-1:-1:-1;;;;;4870:13:10;;;;;;;:6;:13;;;;;;;;;:15;;;;;;;;4508:449;;4552:165;4508:449;;;4571:25:23;4650:18;;;4643:43;;;;4722:15;;;4702:18;;;4695:43;4754:18;;;4747:34;;;4797:19;;;4790:35;;;;4841:19;;;;4834:35;;;4508:449:10;;;;;;;;;;4543:19:23;;;4508:449:10;;;4469:514;;;;;;;;-1:-1:-1;;;4347:658:10;;;5138:27:23;5181:11;;;5174:27;;;;5217:12;;;5210:28;;;;5254:12;;4347:658:10;;;-1:-1:-1;;4347:658:10;;;;;;;;;4316:707;;4347:658;4316:707;;;;4289:805;;;;;;;;;5504:25:23;5577:4;5565:17;;5545:18;;;5538:45;5599:18;;;5592:34;;;5642:18;;;5635:34;;;5476:19;;4289:805:10;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;4289:805:10;;-1:-1:-1;;4289:805:10;;;-1:-1:-1;;;;;;;5117:30:10;;;;;;:59;;;5171:5;-1:-1:-1;;;;;5151:25:10;:16;-1:-1:-1;;;;;5151:25:10;;5117:59;5109:86;;;;-1:-1:-1;;;5109:86:10;;5882:2:23;5109:86:10;;;5864:21:23;5921:2;5901:18;;;5894:30;-1:-1:-1;;;5940:18:23;;;5933:44;5994:18;;5109:86:10;5680:338:23;5109:86:10;-1:-1:-1;;;;;5210:27:10;;;;;;;:9;:27;;;;;;;;:36;;;;;;;;;;;;;:44;;;5280:31;1391:25:23;;;5210:36:10;;5280:31;;;;;1364:18:23;5280:31:10;;;;;;;3835:1483;;;;;;;:::o;5507:446::-;5572:7;5669:95;5802:4;5786:22;;;;;;:::i;:::-;;;;;;;;;;5637:295;;;7520:25:23;;;;7561:18;;7554:34;;;;5830:14:10;7604:18:23;;;7597:34;5866:13:10;7647:18:23;;;7640:34;5909:4:10;7690:19:23;;;7683:61;7492:19;;5637:295:10;;;;;;;;;;;;5610:336;;;;;;5591:355;;5507:446;:::o;6147:325::-;6232:6;6217:11;;:21;;;;;;;:::i;:::-;;;;-1:-1:-1;;;;;;;6384:13:10;;;;;;:9;:13;;;;;;;;:23;;;;;;6433:32;1391:25:23;;;6433:32:10;;1364:18:23;6433:32:10;;;;;;;6147:325;;:::o;14:597:23:-;126:4;155:2;184;173:9;166:21;216:6;210:13;259:6;254:2;243:9;239:18;232:34;284:1;294:140;308:6;305:1;302:13;294:140;;;403:14;;;399:23;;393:30;369:17;;;388:2;365:26;358:66;323:10;;294:140;;;452:6;449:1;446:13;443:91;;;522:1;517:2;508:6;497:9;493:22;489:31;482:42;443:91;-1:-1:-1;595:2:23;574:15;-1:-1:-1;;570:29:23;555:45;;;;602:2;551:54;;14:597;-1:-1:-1;;;14:597:23:o;616:173::-;684:20;;-1:-1:-1;;;;;733:31:23;;723:42;;713:70;;779:1;776;769:12;713:70;616:173;;;:::o;794:254::-;862:6;870;923:2;911:9;902:7;898:23;894:32;891:52;;;939:1;936;929:12;891:52;962:29;981:9;962:29;:::i;:::-;952:39;1038:2;1023:18;;;;1010:32;;-1:-1:-1;;;794:254:23:o;1427:328::-;1504:6;1512;1520;1573:2;1561:9;1552:7;1548:23;1544:32;1541:52;;;1589:1;1586;1579:12;1541:52;1612:29;1631:9;1612:29;:::i;:::-;1602:39;;1660:38;1694:2;1683:9;1679:18;1660:38;:::i;:::-;1650:48;;1745:2;1734:9;1730:18;1717:32;1707:42;;1427:328;;;;;:::o;2131:186::-;2190:6;2243:2;2231:9;2222:7;2218:23;2214:32;2211:52;;;2259:1;2256;2249:12;2211:52;2282:29;2301:9;2282:29;:::i;:::-;2272:39;2131:186;-1:-1:-1;;;2131:186:23:o;2322:693::-;2433:6;2441;2449;2457;2465;2473;2481;2534:3;2522:9;2513:7;2509:23;2505:33;2502:53;;;2551:1;2548;2541:12;2502:53;2574:29;2593:9;2574:29;:::i;:::-;2564:39;;2622:38;2656:2;2645:9;2641:18;2622:38;:::i;:::-;2612:48;;2707:2;2696:9;2692:18;2679:32;2669:42;;2758:2;2747:9;2743:18;2730:32;2720:42;;2812:3;2801:9;2797:19;2784:33;2857:4;2850:5;2846:16;2839:5;2836:27;2826:55;;2877:1;2874;2867:12;2826:55;2322:693;;;;-1:-1:-1;2322:693:23;;;;2900:5;2952:3;2937:19;;2924:33;;-1:-1:-1;3004:3:23;2989:19;;;2976:33;;2322:693;-1:-1:-1;;2322:693:23:o;3020:260::-;3088:6;3096;3149:2;3137:9;3128:7;3124:23;3120:32;3117:52;;;3165:1;3162;3155:12;3117:52;3188:29;3207:9;3188:29;:::i;:::-;3178:39;;3236:38;3270:2;3259:9;3255:18;3236:38;:::i;:::-;3226:48;;3020:260;;;;;:::o;3285:380::-;3364:1;3360:12;;;;3407;;;3428:61;;3482:4;3474:6;3470:17;3460:27;;3428:61;3535:2;3527:6;3524:14;3504:18;3501:38;3498:161;;;3581:10;3576:3;3572:20;3569:1;3562:31;3616:4;3613:1;3606:15;3644:4;3641:1;3634:15;3498:161;;3285:380;;;:::o;3670:127::-;3731:10;3726:3;3722:20;3719:1;3712:31;3762:4;3759:1;3752:15;3786:4;3783:1;3776:15;3802:125;3842:4;3870:1;3867;3864:8;3861:34;;;3875:18;;:::i;:::-;-1:-1:-1;3912:9:23;;3802:125::o;6152:1104::-;6282:3;6311:1;6344:6;6338:13;6374:3;6396:1;6424:9;6420:2;6416:18;6406:28;;6484:2;6473:9;6469:18;6506;6496:61;;6550:4;6542:6;6538:17;6528:27;;6496:61;6576:2;6624;6616:6;6613:14;6593:18;6590:38;6587:165;;;-1:-1:-1;;;6651:33:23;;6707:4;6704:1;6697:15;6737:4;6658:3;6725:17;6587:165;6768:18;6795:104;;;;6913:1;6908:323;;;;6761:470;;6795:104;-1:-1:-1;;6828:24:23;;6816:37;;6873:16;;;;-1:-1:-1;6795:104:23;;6908:323;6099:1;6092:14;;;6136:4;6123:18;;7006:1;7020:165;7034:6;7031:1;7028:13;7020:165;;;7112:14;;7099:11;;;7092:35;7155:16;;;;7049:10;;7020:165;;;7024:3;;7214:6;7209:3;7205:16;7198:23;;6761:470;-1:-1:-1;7247:3:23;;6152:1104;-1:-1:-1;;;;;;;;6152:1104:23:o;7755:128::-;7795:3;7826:1;7822:6;7819:1;7816:13;7813:39;;;7832:18;;:::i;:::-;-1:-1:-1;7868:9:23;;7755:128::o", - "linkReferences": {}, - "immutableReferences": { - "3499": [ - { - "start": 324, - "length": 32 - } - ], - "3513": [ - { - "start": 1054, - "length": 32 - } - ], - "3515": [ - { - "start": 1107, - "length": 32 - } - ] - } - }, - "methodIdentifiers": { - "DOMAIN_SEPARATOR()": "3644e515", - "allowance(address,address)": "dd62ed3e", - "approve(address,uint256)": "095ea7b3", - "balanceOf(address)": "70a08231", - "decimals()": "313ce567", - "mint(address,uint256)": "40c10f19", - "name()": "06fdde03", - "nonces(address)": "7ecebe00", - "permit(address,address,uint256,uint256,uint8,bytes32,bytes32)": "d505accf", - "symbol()": "95d89b41", - "totalSupply()": "18160ddd", - "transfer(address,uint256)": "a9059cbb", - "transferFrom(address,address,uint256)": "23b872dd" - } -} +{"abi":[{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint8","name":"decimals","type":"uint8"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}],"bytecode":{"object":"0x60e06040523480156200001157600080fd5b5060405162000f7738038062000f7783398101604081905262000034916200029a565b82828282600090805190602001906200004f92919062000127565b5081516200006590600190602085019062000127565b5060ff81166080524660a0526200007b6200008b565b60c0525062000400945050505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6000604051620000bf91906200035c565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b82805462000135906200031f565b90600052602060002090601f016020900481019282620001595760008555620001a4565b82601f106200017457805160ff1916838001178555620001a4565b82800160010185558215620001a4579182015b82811115620001a457825182559160200191906001019062000187565b50620001b2929150620001b6565b5090565b5b80821115620001b25760008155600101620001b7565b634e487b7160e01b600052604160045260246000fd5b600082601f830112620001f557600080fd5b81516001600160401b0380821115620002125762000212620001cd565b604051601f8301601f19908116603f011681019082821181831017156200023d576200023d620001cd565b816040528381526020925086838588010111156200025a57600080fd5b600091505b838210156200027e57858201830151818301840152908201906200025f565b83821115620002905760008385830101525b9695505050505050565b600080600060608486031215620002b057600080fd5b83516001600160401b0380821115620002c857600080fd5b620002d687838801620001e3565b94506020860151915080821115620002ed57600080fd5b50620002fc86828701620001e3565b925050604084015160ff811681146200031457600080fd5b809150509250925092565b600181811c908216806200033457607f821691505b602082108114156200035657634e487b7160e01b600052602260045260246000fd5b50919050565b600080835481600182811c9150808316806200037957607f831692505b60208084108214156200039a57634e487b7160e01b86526022600452602486fd5b818015620003b15760018114620003c357620003f2565b60ff19861689528489019650620003f2565b60008a81526020902060005b86811015620003ea5781548b820152908501908301620003cf565b505084890196505b509498975050505050505050565b60805160a05160c051610b476200043060003960006104530152600061041e015260006101440152610b476000f3fe608060405234801561001057600080fd5b50600436106100cf5760003560e01c806340c10f191161008c57806395d89b411161006657806395d89b41146101d5578063a9059cbb146101dd578063d505accf146101f0578063dd62ed3e1461020357600080fd5b806340c10f191461018057806370a08231146101955780637ecebe00146101b557600080fd5b806306fdde03146100d4578063095ea7b3146100f257806318160ddd1461011557806323b872dd1461012c578063313ce5671461013f5780633644e51514610178575b600080fd5b6100dc61022e565b6040516100e99190610856565b60405180910390f35b6101056101003660046108c7565b6102bc565b60405190151581526020016100e9565b61011e60025481565b6040519081526020016100e9565b61010561013a3660046108f1565b610328565b6101667f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff90911681526020016100e9565b61011e61041a565b61019361018e3660046108c7565b610475565b005b61011e6101a336600461092d565b60036020526000908152604090205481565b61011e6101c336600461092d565b60056020526000908152604090205481565b6100dc610483565b6101056101eb3660046108c7565b610490565b6101936101fe36600461094f565b610508565b61011e6102113660046109c2565b600460209081526000928352604080842090915290825290205481565b6000805461023b906109f5565b80601f0160208091040260200160405190810160405280929190818152602001828054610267906109f5565b80156102b45780601f10610289576101008083540402835291602001916102b4565b820191906000526020600020905b81548152906001019060200180831161029757829003601f168201915b505050505081565b3360008181526004602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906103179086815260200190565b60405180910390a350600192915050565b6001600160a01b038316600090815260046020908152604080832033845290915281205460001981146103845761035f8382610a46565b6001600160a01b03861660009081526004602090815260408083203384529091529020555b6001600160a01b038516600090815260036020526040812080548592906103ac908490610a46565b90915550506001600160a01b03808516600081815260036020526040908190208054870190555190918716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906104079087815260200190565b60405180910390a3506001949350505050565b60007f000000000000000000000000000000000000000000000000000000000000000046146104505761044b610751565b905090565b507f000000000000000000000000000000000000000000000000000000000000000090565b61047f82826107eb565b5050565b6001805461023b906109f5565b336000908152600360205260408120805483919083906104b1908490610a46565b90915550506001600160a01b038316600081815260036020526040908190208054850190555133907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906103179086815260200190565b4284101561055d5760405162461bcd60e51b815260206004820152601760248201527f5045524d49545f444541444c494e455f4558504952454400000000000000000060448201526064015b60405180910390fd5b6000600161056961041a565b6001600160a01b038a811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e08301909152805192019190912061190160f01b6101008301526101028201929092526101228101919091526101420160408051601f198184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015610675573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116158015906106ab5750876001600160a01b0316816001600160a01b0316145b6106e85760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa9a4a3a722a960911b6044820152606401610554565b6001600160a01b0390811660009081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60006040516107839190610a5d565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b80600260008282546107fd9190610af9565b90915550506001600160a01b0382166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b600060208083528351808285015260005b8181101561088357858101830151858201604001528201610867565b81811115610895576000604083870101525b50601f01601f1916929092016040019392505050565b80356001600160a01b03811681146108c257600080fd5b919050565b600080604083850312156108da57600080fd5b6108e3836108ab565b946020939093013593505050565b60008060006060848603121561090657600080fd5b61090f846108ab565b925061091d602085016108ab565b9150604084013590509250925092565b60006020828403121561093f57600080fd5b610948826108ab565b9392505050565b600080600080600080600060e0888a03121561096a57600080fd5b610973886108ab565b9650610981602089016108ab565b95506040880135945060608801359350608088013560ff811681146109a557600080fd5b9699959850939692959460a0840135945060c09093013592915050565b600080604083850312156109d557600080fd5b6109de836108ab565b91506109ec602084016108ab565b90509250929050565b600181811c90821680610a0957607f821691505b60208210811415610a2a57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b600082821015610a5857610a58610a30565b500390565b600080835481600182811c915080831680610a7957607f831692505b6020808410821415610a9957634e487b7160e01b86526022600452602486fd5b818015610aad5760018114610abe57610aeb565b60ff19861689528489019650610aeb565b60008a81526020902060005b86811015610ae35781548b820152908501908301610aca565b505084890196505b509498975050505050505050565b60008219821115610b0c57610b0c610a30565b50019056fea26469706673582212209ca55739676d094f8ef4b3b5cfcc08f600b805843c4c3027beb2b4159a02f63664736f6c634300080a0033","sourceMap":"113:230:22:-:0;;;148:102;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;224:4;230:6;238:8;2098:5:10;2091:4;:12;;;;;;;;;;;;:::i;:::-;-1:-1:-1;2113:16:10;;;;:6;;:16;;;;;:::i;:::-;-1:-1:-1;2139:20:10;;;;;2189:13;2170:32;;2239:24;:22;:24::i;:::-;2212:51;;-1:-1:-1;113:230:22;;-1:-1:-1;;;;;113:230:22;5507:446:10;5572:7;5669:95;5802:4;5786:22;;;;;;:::i;:::-;;;;;;;;;;5637:295;;;3635:25:23;;;;3676:18;;3669:34;;;;5830:14:10;3719:18:23;;;3712:34;5866:13:10;3762:18:23;;;3755:34;5909:4:10;3805:19:23;;;3798:61;3607:19;;5637:295:10;;;;;;;;;;;;5610:336;;;;;;5591:355;;5507:446;:::o;113:230:22:-;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;113:230:22;;;-1:-1:-1;113:230:22;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;14:127:23;75:10;70:3;66:20;63:1;56:31;106:4;103:1;96:15;130:4;127:1;120:15;146:885;200:5;253:3;246:4;238:6;234:17;230:27;220:55;;271:1;268;261:12;220:55;294:13;;-1:-1:-1;;;;;356:10:23;;;353:36;;;369:18;;:::i;:::-;444:2;438:9;412:2;498:13;;-1:-1:-1;;494:22:23;;;518:2;490:31;486:40;474:53;;;542:18;;;562:22;;;539:46;536:72;;;588:18;;:::i;:::-;628:10;624:2;617:22;663:2;655:6;648:18;685:4;675:14;;730:3;725:2;720;712:6;708:15;704:24;701:33;698:53;;;747:1;744;737:12;698:53;769:1;760:10;;779:133;793:2;790:1;787:9;779:133;;;881:14;;;877:23;;871:30;850:14;;;846:23;;839:63;804:10;;;;779:133;;;930:2;927:1;924:9;921:80;;;989:1;984:2;979;971:6;967:15;963:24;956:35;921:80;1019:6;146:885;-1:-1:-1;;;;;;146:885:23:o;1036:712::-;1142:6;1150;1158;1211:2;1199:9;1190:7;1186:23;1182:32;1179:52;;;1227:1;1224;1217:12;1179:52;1254:16;;-1:-1:-1;;;;;1319:14:23;;;1316:34;;;1346:1;1343;1336:12;1316:34;1369:61;1422:7;1413:6;1402:9;1398:22;1369:61;:::i;:::-;1359:71;;1476:2;1465:9;1461:18;1455:25;1439:41;;1505:2;1495:8;1492:16;1489:36;;;1521:1;1518;1511:12;1489:36;;1544:63;1599:7;1588:8;1577:9;1573:24;1544:63;:::i;:::-;1534:73;;;1650:2;1639:9;1635:18;1629:25;1694:4;1687:5;1683:16;1676:5;1673:27;1663:55;;1714:1;1711;1704:12;1663:55;1737:5;1727:15;;;1036:712;;;;;:::o;1753:380::-;1832:1;1828:12;;;;1875;;;1896:61;;1950:4;1942:6;1938:17;1928:27;;1896:61;2003:2;1995:6;1992:14;1972:18;1969:38;1966:161;;;2049:10;2044:3;2040:20;2037:1;2030:31;2084:4;2081:1;2074:15;2112:4;2109:1;2102:15;1966:161;;1753:380;;;:::o;2267:1104::-;2397:3;2426:1;2459:6;2453:13;2489:3;2511:1;2539:9;2535:2;2531:18;2521:28;;2599:2;2588:9;2584:18;2621;2611:61;;2665:4;2657:6;2653:17;2643:27;;2611:61;2691:2;2739;2731:6;2728:14;2708:18;2705:38;2702:165;;;-1:-1:-1;;;2766:33:23;;2822:4;2819:1;2812:15;2852:4;2773:3;2840:17;2702:165;2883:18;2910:104;;;;3028:1;3023:323;;;;2876:470;;2910:104;-1:-1:-1;;2943:24:23;;2931:37;;2988:16;;;;-1:-1:-1;2910:104:23;;3023:323;2214:1;2207:14;;;2251:4;2238:18;;3121:1;3135:165;3149:6;3146:1;3143:13;3135:165;;;3227:14;;3214:11;;;3207:35;3270:16;;;;3164:10;;3135:165;;;3139:3;;3329:6;3324:3;3320:16;3313:23;;2876:470;-1:-1:-1;3362:3:23;;2267:1104;-1:-1:-1;;;;;;;;2267:1104:23:o;3376:489::-;113:230:22;;;;;;;;;;;;;;;;;;;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x608060405234801561001057600080fd5b50600436106100cf5760003560e01c806340c10f191161008c57806395d89b411161006657806395d89b41146101d5578063a9059cbb146101dd578063d505accf146101f0578063dd62ed3e1461020357600080fd5b806340c10f191461018057806370a08231146101955780637ecebe00146101b557600080fd5b806306fdde03146100d4578063095ea7b3146100f257806318160ddd1461011557806323b872dd1461012c578063313ce5671461013f5780633644e51514610178575b600080fd5b6100dc61022e565b6040516100e99190610856565b60405180910390f35b6101056101003660046108c7565b6102bc565b60405190151581526020016100e9565b61011e60025481565b6040519081526020016100e9565b61010561013a3660046108f1565b610328565b6101667f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff90911681526020016100e9565b61011e61041a565b61019361018e3660046108c7565b610475565b005b61011e6101a336600461092d565b60036020526000908152604090205481565b61011e6101c336600461092d565b60056020526000908152604090205481565b6100dc610483565b6101056101eb3660046108c7565b610490565b6101936101fe36600461094f565b610508565b61011e6102113660046109c2565b600460209081526000928352604080842090915290825290205481565b6000805461023b906109f5565b80601f0160208091040260200160405190810160405280929190818152602001828054610267906109f5565b80156102b45780601f10610289576101008083540402835291602001916102b4565b820191906000526020600020905b81548152906001019060200180831161029757829003601f168201915b505050505081565b3360008181526004602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906103179086815260200190565b60405180910390a350600192915050565b6001600160a01b038316600090815260046020908152604080832033845290915281205460001981146103845761035f8382610a46565b6001600160a01b03861660009081526004602090815260408083203384529091529020555b6001600160a01b038516600090815260036020526040812080548592906103ac908490610a46565b90915550506001600160a01b03808516600081815260036020526040908190208054870190555190918716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906104079087815260200190565b60405180910390a3506001949350505050565b60007f000000000000000000000000000000000000000000000000000000000000000046146104505761044b610751565b905090565b507f000000000000000000000000000000000000000000000000000000000000000090565b61047f82826107eb565b5050565b6001805461023b906109f5565b336000908152600360205260408120805483919083906104b1908490610a46565b90915550506001600160a01b038316600081815260036020526040908190208054850190555133907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906103179086815260200190565b4284101561055d5760405162461bcd60e51b815260206004820152601760248201527f5045524d49545f444541444c494e455f4558504952454400000000000000000060448201526064015b60405180910390fd5b6000600161056961041a565b6001600160a01b038a811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e08301909152805192019190912061190160f01b6101008301526101028201929092526101228101919091526101420160408051601f198184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015610675573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116158015906106ab5750876001600160a01b0316816001600160a01b0316145b6106e85760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa9a4a3a722a960911b6044820152606401610554565b6001600160a01b0390811660009081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60006040516107839190610a5d565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b80600260008282546107fd9190610af9565b90915550506001600160a01b0382166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b600060208083528351808285015260005b8181101561088357858101830151858201604001528201610867565b81811115610895576000604083870101525b50601f01601f1916929092016040019392505050565b80356001600160a01b03811681146108c257600080fd5b919050565b600080604083850312156108da57600080fd5b6108e3836108ab565b946020939093013593505050565b60008060006060848603121561090657600080fd5b61090f846108ab565b925061091d602085016108ab565b9150604084013590509250925092565b60006020828403121561093f57600080fd5b610948826108ab565b9392505050565b600080600080600080600060e0888a03121561096a57600080fd5b610973886108ab565b9650610981602089016108ab565b95506040880135945060608801359350608088013560ff811681146109a557600080fd5b9699959850939692959460a0840135945060c09093013592915050565b600080604083850312156109d557600080fd5b6109de836108ab565b91506109ec602084016108ab565b90509250929050565b600181811c90821680610a0957607f821691505b60208210811415610a2a57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b600082821015610a5857610a58610a30565b500390565b600080835481600182811c915080831680610a7957607f831692505b6020808410821415610a9957634e487b7160e01b86526022600452602486fd5b818015610aad5760018114610abe57610aeb565b60ff19861689528489019650610aeb565b60008a81526020902060005b86811015610ae35781548b820152908501908301610aca565b505084890196505b509498975050505050505050565b60008219821115610b0c57610b0c610a30565b50019056fea26469706673582212209ca55739676d094f8ef4b3b5cfcc08f600b805843c4c3027beb2b4159a02f63664736f6c634300080a0033","sourceMap":"113:230:22:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1028:18:10;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;2458:211;;;;;;:::i;:::-;;:::i;:::-;;;1218:14:23;;1211:22;1193:41;;1181:2;1166:18;2458:211:10;1053:187:23;1301:26:10;;;;;;;;;1391:25:23;;;1379:2;1364:18;1301:26:10;1245:177:23;3054:592:10;;;;;;:::i;:::-;;:::i;1080:31::-;;;;;;;;1932:4:23;1920:17;;;1902:36;;1890:2;1875:18;1080:31:10;1760:184:23;5324:177:10;;;:::i;256:85:22:-;;;;;;:::i;:::-;;:::i;:::-;;1334:44:10;;;;;;:::i;:::-;;;;;;;;;;;;;;1748:41;;;;;;:::i;:::-;;;;;;;;;;;;;;1053:20;;;:::i;2675:373::-;;;;;;:::i;:::-;;:::i;3835:1483::-;;;;;;:::i;:::-;;:::i;1385:64::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;1028:18;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;2458:211::-;2558:10;2532:4;2548:21;;;:9;:21;;;;;;;;-1:-1:-1;;;;;2548:30:10;;;;;;;;;;:39;;;2603:37;2532:4;;2548:30;;2603:37;;;;2581:6;1391:25:23;;1379:2;1364:18;;1245:177;2603:37:10;;;;;;;;-1:-1:-1;2658:4:10;2458:211;;;;:::o;3054:592::-;-1:-1:-1;;;;;3206:15:10;;3172:4;3206:15;;;:9;:15;;;;;;;;3222:10;3206:27;;;;;;;;-1:-1:-1;;3284:28:10;;3280:80;;3344:16;3354:6;3344:7;:16;:::i;:::-;-1:-1:-1;;;;;3314:15:10;;;;;;:9;:15;;;;;;;;3330:10;3314:27;;;;;;;:46;3280:80;-1:-1:-1;;;;;3371:15:10;;;;;;:9;:15;;;;;:25;;3390:6;;3371:15;:25;;3390:6;;3371:25;:::i;:::-;;;;-1:-1:-1;;;;;;;3542:13:10;;;;;;;:9;:13;;;;;;;:23;;;;;;3591:26;3542:13;;3591:26;;;;;;;3559:6;1391:25:23;;1379:2;1364:18;;1245:177;3591:26:10;;;;;;;;-1:-1:-1;3635:4:10;;3054:592;-1:-1:-1;;;;3054:592:10:o;5324:177::-;5381:7;5424:16;5407:13;:33;:87;;5470:24;:22;:24::i;:::-;5400:94;;5324:177;:::o;5407:87::-;-1:-1:-1;5443:24:10;;5324:177::o;256:85:22:-;317:17;323:2;327:6;317:5;:17::i;:::-;256:85;;:::o;1053:20:10:-;;;;;;;:::i;2675:373::-;2771:10;2745:4;2761:21;;;:9;:21;;;;;:31;;2786:6;;2761:21;2745:4;;2761:31;;2786:6;;2761:31;:::i;:::-;;;;-1:-1:-1;;;;;;;2938:13:10;;;;;;:9;:13;;;;;;;:23;;;;;;2987:32;2996:10;;2987:32;;;;2955:6;1391:25:23;;1379:2;1364:18;;1245:177;3835:1483:10;4054:15;4042:8;:27;;4034:63;;;;-1:-1:-1;;;4034:63:10;;4134:2:23;4034:63:10;;;4116:21:23;4173:2;4153:18;;;4146:30;4212:25;4192:18;;;4185:53;4255:18;;4034:63:10;;;;;;;;;4262:24;4289:805;4425:18;:16;:18::i;:::-;-1:-1:-1;;;;;4870:13:10;;;;;;;:6;:13;;;;;;;;;:15;;;;;;;;4508:449;;4552:165;4508:449;;;4571:25:23;4650:18;;;4643:43;;;;4722:15;;;4702:18;;;4695:43;4754:18;;;4747:34;;;4797:19;;;4790:35;;;;4841:19;;;;4834:35;;;4508:449:10;;;;;;;;;;4543:19:23;;;4508:449:10;;;4469:514;;;;;;;;-1:-1:-1;;;4347:658:10;;;5138:27:23;5181:11;;;5174:27;;;;5217:12;;;5210:28;;;;5254:12;;4347:658:10;;;-1:-1:-1;;4347:658:10;;;;;;;;;4316:707;;4347:658;4316:707;;;;4289:805;;;;;;;;;5504:25:23;5577:4;5565:17;;5545:18;;;5538:45;5599:18;;;5592:34;;;5642:18;;;5635:34;;;5476:19;;4289:805:10;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;4289:805:10;;-1:-1:-1;;4289:805:10;;;-1:-1:-1;;;;;;;5117:30:10;;;;;;:59;;;5171:5;-1:-1:-1;;;;;5151:25:10;:16;-1:-1:-1;;;;;5151:25:10;;5117:59;5109:86;;;;-1:-1:-1;;;5109:86:10;;5882:2:23;5109:86:10;;;5864:21:23;5921:2;5901:18;;;5894:30;-1:-1:-1;;;5940:18:23;;;5933:44;5994:18;;5109:86:10;5680:338:23;5109:86:10;-1:-1:-1;;;;;5210:27:10;;;;;;;:9;:27;;;;;;;;:36;;;;;;;;;;;;;:44;;;5280:31;1391:25:23;;;5210:36:10;;5280:31;;;;;1364:18:23;5280:31:10;;;;;;;3835:1483;;;;;;;:::o;5507:446::-;5572:7;5669:95;5802:4;5786:22;;;;;;:::i;:::-;;;;;;;;;;5637:295;;;7520:25:23;;;;7561:18;;7554:34;;;;5830:14:10;7604:18:23;;;7597:34;5866:13:10;7647:18:23;;;7640:34;5909:4:10;7690:19:23;;;7683:61;7492:19;;5637:295:10;;;;;;;;;;;;5610:336;;;;;;5591:355;;5507:446;:::o;6147:325::-;6232:6;6217:11;;:21;;;;;;;:::i;:::-;;;;-1:-1:-1;;;;;;;6384:13:10;;;;;;:9;:13;;;;;;;;:23;;;;;;6433:32;1391:25:23;;;6433:32:10;;1364:18:23;6433:32:10;;;;;;;6147:325;;:::o;14:597:23:-;126:4;155:2;184;173:9;166:21;216:6;210:13;259:6;254:2;243:9;239:18;232:34;284:1;294:140;308:6;305:1;302:13;294:140;;;403:14;;;399:23;;393:30;369:17;;;388:2;365:26;358:66;323:10;;294:140;;;452:6;449:1;446:13;443:91;;;522:1;517:2;508:6;497:9;493:22;489:31;482:42;443:91;-1:-1:-1;595:2:23;574:15;-1:-1:-1;;570:29:23;555:45;;;;602:2;551:54;;14:597;-1:-1:-1;;;14:597:23:o;616:173::-;684:20;;-1:-1:-1;;;;;733:31:23;;723:42;;713:70;;779:1;776;769:12;713:70;616:173;;;:::o;794:254::-;862:6;870;923:2;911:9;902:7;898:23;894:32;891:52;;;939:1;936;929:12;891:52;962:29;981:9;962:29;:::i;:::-;952:39;1038:2;1023:18;;;;1010:32;;-1:-1:-1;;;794:254:23:o;1427:328::-;1504:6;1512;1520;1573:2;1561:9;1552:7;1548:23;1544:32;1541:52;;;1589:1;1586;1579:12;1541:52;1612:29;1631:9;1612:29;:::i;:::-;1602:39;;1660:38;1694:2;1683:9;1679:18;1660:38;:::i;:::-;1650:48;;1745:2;1734:9;1730:18;1717:32;1707:42;;1427:328;;;;;:::o;2131:186::-;2190:6;2243:2;2231:9;2222:7;2218:23;2214:32;2211:52;;;2259:1;2256;2249:12;2211:52;2282:29;2301:9;2282:29;:::i;:::-;2272:39;2131:186;-1:-1:-1;;;2131:186:23:o;2322:693::-;2433:6;2441;2449;2457;2465;2473;2481;2534:3;2522:9;2513:7;2509:23;2505:33;2502:53;;;2551:1;2548;2541:12;2502:53;2574:29;2593:9;2574:29;:::i;:::-;2564:39;;2622:38;2656:2;2645:9;2641:18;2622:38;:::i;:::-;2612:48;;2707:2;2696:9;2692:18;2679:32;2669:42;;2758:2;2747:9;2743:18;2730:32;2720:42;;2812:3;2801:9;2797:19;2784:33;2857:4;2850:5;2846:16;2839:5;2836:27;2826:55;;2877:1;2874;2867:12;2826:55;2322:693;;;;-1:-1:-1;2322:693:23;;;;2900:5;2952:3;2937:19;;2924:33;;-1:-1:-1;3004:3:23;2989:19;;;2976:33;;2322:693;-1:-1:-1;;2322:693:23:o;3020:260::-;3088:6;3096;3149:2;3137:9;3128:7;3124:23;3120:32;3117:52;;;3165:1;3162;3155:12;3117:52;3188:29;3207:9;3188:29;:::i;:::-;3178:39;;3236:38;3270:2;3259:9;3255:18;3236:38;:::i;:::-;3226:48;;3020:260;;;;;:::o;3285:380::-;3364:1;3360:12;;;;3407;;;3428:61;;3482:4;3474:6;3470:17;3460:27;;3428:61;3535:2;3527:6;3524:14;3504:18;3501:38;3498:161;;;3581:10;3576:3;3572:20;3569:1;3562:31;3616:4;3613:1;3606:15;3644:4;3641:1;3634:15;3498:161;;3285:380;;;:::o;3670:127::-;3731:10;3726:3;3722:20;3719:1;3712:31;3762:4;3759:1;3752:15;3786:4;3783:1;3776:15;3802:125;3842:4;3870:1;3867;3864:8;3861:34;;;3875:18;;:::i;:::-;-1:-1:-1;3912:9:23;;3802:125::o;6152:1104::-;6282:3;6311:1;6344:6;6338:13;6374:3;6396:1;6424:9;6420:2;6416:18;6406:28;;6484:2;6473:9;6469:18;6506;6496:61;;6550:4;6542:6;6538:17;6528:27;;6496:61;6576:2;6624;6616:6;6613:14;6593:18;6590:38;6587:165;;;-1:-1:-1;;;6651:33:23;;6707:4;6704:1;6697:15;6737:4;6658:3;6725:17;6587:165;6768:18;6795:104;;;;6913:1;6908:323;;;;6761:470;;6795:104;-1:-1:-1;;6828:24:23;;6816:37;;6873:16;;;;-1:-1:-1;6795:104:23;;6908:323;6099:1;6092:14;;;6136:4;6123:18;;7006:1;7020:165;7034:6;7031:1;7028:13;7020:165;;;7112:14;;7099:11;;;7092:35;7155:16;;;;7049:10;;7020:165;;;7024:3;;7214:6;7209:3;7205:16;7198:23;;6761:470;-1:-1:-1;7247:3:23;;6152:1104;-1:-1:-1;;;;;;;;6152:1104:23:o;7755:128::-;7795:3;7826:1;7822:6;7819:1;7816:13;7813:39;;;7832:18;;:::i;:::-;-1:-1:-1;7868:9:23;;7755:128::o","linkReferences":{},"immutableReferences":{"3499":[{"start":324,"length":32}],"3513":[{"start":1054,"length":32}],"3515":[{"start":1107,"length":32}]}},"methodIdentifiers":{"DOMAIN_SEPARATOR()":"3644e515","allowance(address,address)":"dd62ed3e","approve(address,uint256)":"095ea7b3","balanceOf(address)":"70a08231","decimals()":"313ce567","mint(address,uint256)":"40c10f19","name()":"06fdde03","nonces(address)":"7ecebe00","permit(address,address,uint256,uint256,uint8,bytes32,bytes32)":"d505accf","symbol()":"95d89b41","totalSupply()":"18160ddd","transfer(address,uint256)":"a9059cbb","transferFrom(address,address,uint256)":"23b872dd"}} \ No newline at end of file diff --git a/crates/cast/tests/fixtures/interface.json b/crates/cast/tests/fixtures/interface.json index 73e561886..26163abee 100644 --- a/crates/cast/tests/fixtures/interface.json +++ b/crates/cast/tests/fixtures/interface.json @@ -1,141 +1 @@ -[ - { - "type": "constructor", - "inputs": [ - { - "name": "_integrationManager", - "type": "address", - "internalType": "address" - }, - { - "name": "_addressListRegistry", - "type": "address", - "internalType": "address" - }, - { - "name": "_aTokenListId", - "type": "uint256", - "internalType": "uint256" - }, - { - "name": "_pool", - "type": "address", - "internalType": "address" - }, - { - "name": "_referralCode", - "type": "uint16", - "internalType": "uint16" - } - ], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "getIntegrationManager", - "inputs": [], - "outputs": [ - { - "name": "integrationManager_", - "type": "address", - "internalType": "address" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "lend", - "inputs": [ - { - "name": "_vaultProxy", - "type": "address", - "internalType": "address" - }, - { - "name": "", - "type": "bytes", - "internalType": "bytes" - }, - { - "name": "_assetData", - "type": "bytes", - "internalType": "bytes" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "parseAssetsForAction", - "inputs": [ - { - "name": "", - "type": "address", - "internalType": "address" - }, - { - "name": "_selector", - "type": "bytes4", - "internalType": "bytes4" - }, - { - "name": "_actionData", - "type": "bytes", - "internalType": "bytes" - } - ], - "outputs": [ - { - "name": "spendAssetsHandleType_", - "type": "uint8", - "internalType": "enum IIntegrationManager.SpendAssetsHandleType" - }, - { - "name": "spendAssets_", - "type": "address[]", - "internalType": "address[]" - }, - { - "name": "spendAssetAmounts_", - "type": "uint256[]", - "internalType": "uint256[]" - }, - { - "name": "incomingAssets_", - "type": "address[]", - "internalType": "address[]" - }, - { - "name": "minIncomingAssetAmounts_", - "type": "uint256[]", - "internalType": "uint256[]" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "redeem", - "inputs": [ - { - "name": "_vaultProxy", - "type": "address", - "internalType": "address" - }, - { - "name": "", - "type": "bytes", - "internalType": "bytes" - }, - { - "name": "_assetData", - "type": "bytes", - "internalType": "bytes" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - } -] \ No newline at end of file +[{"type":"constructor","inputs":[{"name":"_integrationManager","type":"address","internalType":"address"},{"name":"_addressListRegistry","type":"address","internalType":"address"},{"name":"_aTokenListId","type":"uint256","internalType":"uint256"},{"name":"_pool","type":"address","internalType":"address"},{"name":"_referralCode","type":"uint16","internalType":"uint16"}],"stateMutability":"nonpayable"},{"type":"function","name":"getIntegrationManager","inputs":[],"outputs":[{"name":"integrationManager_","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"lend","inputs":[{"name":"_vaultProxy","type":"address","internalType":"address"},{"name":"","type":"bytes","internalType":"bytes"},{"name":"_assetData","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"parseAssetsForAction","inputs":[{"name":"","type":"address","internalType":"address"},{"name":"_selector","type":"bytes4","internalType":"bytes4"},{"name":"_actionData","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"spendAssetsHandleType_","type":"uint8","internalType":"enum IIntegrationManager.SpendAssetsHandleType"},{"name":"spendAssets_","type":"address[]","internalType":"address[]"},{"name":"spendAssetAmounts_","type":"uint256[]","internalType":"uint256[]"},{"name":"incomingAssets_","type":"address[]","internalType":"address[]"},{"name":"minIncomingAssetAmounts_","type":"uint256[]","internalType":"uint256[]"}],"stateMutability":"view"},{"type":"function","name":"redeem","inputs":[{"name":"_vaultProxy","type":"address","internalType":"address"},{"name":"","type":"bytes","internalType":"bytes"},{"name":"_assetData","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"}] \ No newline at end of file diff --git a/crates/cast/tests/fixtures/sign_typed_data.json b/crates/cast/tests/fixtures/sign_typed_data.json index a6002810a..8dc45f2e7 100644 --- a/crates/cast/tests/fixtures/sign_typed_data.json +++ b/crates/cast/tests/fixtures/sign_typed_data.json @@ -1,38 +1 @@ -{ - "types": { - "EIP712Domain": [ - { - "name": "name", - "type": "string" - }, - { - "name": "version", - "type": "string" - }, - { - "name": "chainId", - "type": "uint256" - }, - { - "name": "verifyingContract", - "type": "address" - } - ], - "Message": [ - { - "name": "data", - "type": "string" - } - ] - }, - "primaryType": "Message", - "domain": { - "name": "example.metamask.io", - "version": "1", - "chainId": "1", - "verifyingContract": "0x0000000000000000000000000000000000000000" - }, - "message": { - "data": "Hello!" - } -} \ No newline at end of file +{"types":{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}],"Message":[{"name":"data","type":"string"}]},"primaryType":"Message","domain":{"name":"example.metamask.io","version":"1","chainId":"1","verifyingContract":"0x0000000000000000000000000000000000000000"},"message":{"data":"Hello!"}} \ No newline at end of file diff --git a/crates/evm/core/src/abi/HardhatConsole.json b/crates/evm/core/src/abi/HardhatConsole.json index c1b1b46cf..4013d8753 100644 --- a/crates/evm/core/src/abi/HardhatConsole.json +++ b/crates/evm/core/src/abi/HardhatConsole.json @@ -1 +1 @@ -[{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"}],"name":"logAddress","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"}],"name":"logBool","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"p0","type":"bytes"}],"name":"logBytes","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes1","name":"p0","type":"bytes1"}],"name":"logBytes1","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes10","name":"p0","type":"bytes10"}],"name":"logBytes10","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes11","name":"p0","type":"bytes11"}],"name":"logBytes11","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes12","name":"p0","type":"bytes12"}],"name":"logBytes12","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes13","name":"p0","type":"bytes13"}],"name":"logBytes13","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes14","name":"p0","type":"bytes14"}],"name":"logBytes14","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes15","name":"p0","type":"bytes15"}],"name":"logBytes15","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes16","name":"p0","type":"bytes16"}],"name":"logBytes16","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes17","name":"p0","type":"bytes17"}],"name":"logBytes17","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes18","name":"p0","type":"bytes18"}],"name":"logBytes18","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes19","name":"p0","type":"bytes19"}],"name":"logBytes19","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes2","name":"p0","type":"bytes2"}],"name":"logBytes2","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes20","name":"p0","type":"bytes20"}],"name":"logBytes20","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes21","name":"p0","type":"bytes21"}],"name":"logBytes21","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes22","name":"p0","type":"bytes22"}],"name":"logBytes22","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes23","name":"p0","type":"bytes23"}],"name":"logBytes23","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes24","name":"p0","type":"bytes24"}],"name":"logBytes24","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes25","name":"p0","type":"bytes25"}],"name":"logBytes25","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes26","name":"p0","type":"bytes26"}],"name":"logBytes26","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes27","name":"p0","type":"bytes27"}],"name":"logBytes27","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes28","name":"p0","type":"bytes28"}],"name":"logBytes28","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes29","name":"p0","type":"bytes29"}],"name":"logBytes29","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes3","name":"p0","type":"bytes3"}],"name":"logBytes3","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes30","name":"p0","type":"bytes30"}],"name":"logBytes30","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes31","name":"p0","type":"bytes31"}],"name":"logBytes31","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"p0","type":"bytes32"}],"name":"logBytes32","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"p0","type":"bytes4"}],"name":"logBytes4","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes5","name":"p0","type":"bytes5"}],"name":"logBytes5","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes6","name":"p0","type":"bytes6"}],"name":"logBytes6","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes7","name":"p0","type":"bytes7"}],"name":"logBytes7","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes8","name":"p0","type":"bytes8"}],"name":"logBytes8","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes9","name":"p0","type":"bytes9"}],"name":"logBytes9","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"p0","type":"int256"}],"name":"logInt","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"}],"name":"logString","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"}],"name":"logUint","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"p0","type":"int256"}],"outputs":[],"stateMutability":"view","type":"function","name":"log"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"int256","name":"p1","type":"int256"}],"outputs":[],"stateMutability":"view","type":"function","name":"log"}] +[{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"bool","name":"p2","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"uint256","name":"p3","type":"uint256"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"bool","name":"p1","type":"bool"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"string","name":"p1","type":"string"},{"internalType":"string","name":"p2","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"bool","name":"p3","type":"bool"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"address","name":"p2","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"},{"internalType":"address","name":"p1","type":"address"},{"internalType":"uint256","name":"p2","type":"uint256"},{"internalType":"string","name":"p3","type":"string"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"},{"internalType":"uint256","name":"p1","type":"uint256"},{"internalType":"string","name":"p2","type":"string"},{"internalType":"address","name":"p3","type":"address"}],"name":"log","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"p0","type":"address"}],"name":"logAddress","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"p0","type":"bool"}],"name":"logBool","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"p0","type":"bytes"}],"name":"logBytes","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes1","name":"p0","type":"bytes1"}],"name":"logBytes1","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes10","name":"p0","type":"bytes10"}],"name":"logBytes10","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes11","name":"p0","type":"bytes11"}],"name":"logBytes11","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes12","name":"p0","type":"bytes12"}],"name":"logBytes12","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes13","name":"p0","type":"bytes13"}],"name":"logBytes13","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes14","name":"p0","type":"bytes14"}],"name":"logBytes14","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes15","name":"p0","type":"bytes15"}],"name":"logBytes15","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes16","name":"p0","type":"bytes16"}],"name":"logBytes16","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes17","name":"p0","type":"bytes17"}],"name":"logBytes17","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes18","name":"p0","type":"bytes18"}],"name":"logBytes18","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes19","name":"p0","type":"bytes19"}],"name":"logBytes19","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes2","name":"p0","type":"bytes2"}],"name":"logBytes2","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes20","name":"p0","type":"bytes20"}],"name":"logBytes20","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes21","name":"p0","type":"bytes21"}],"name":"logBytes21","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes22","name":"p0","type":"bytes22"}],"name":"logBytes22","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes23","name":"p0","type":"bytes23"}],"name":"logBytes23","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes24","name":"p0","type":"bytes24"}],"name":"logBytes24","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes25","name":"p0","type":"bytes25"}],"name":"logBytes25","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes26","name":"p0","type":"bytes26"}],"name":"logBytes26","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes27","name":"p0","type":"bytes27"}],"name":"logBytes27","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes28","name":"p0","type":"bytes28"}],"name":"logBytes28","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes29","name":"p0","type":"bytes29"}],"name":"logBytes29","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes3","name":"p0","type":"bytes3"}],"name":"logBytes3","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes30","name":"p0","type":"bytes30"}],"name":"logBytes30","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes31","name":"p0","type":"bytes31"}],"name":"logBytes31","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"p0","type":"bytes32"}],"name":"logBytes32","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"p0","type":"bytes4"}],"name":"logBytes4","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes5","name":"p0","type":"bytes5"}],"name":"logBytes5","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes6","name":"p0","type":"bytes6"}],"name":"logBytes6","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes7","name":"p0","type":"bytes7"}],"name":"logBytes7","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes8","name":"p0","type":"bytes8"}],"name":"logBytes8","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes9","name":"p0","type":"bytes9"}],"name":"logBytes9","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"p0","type":"int256"}],"name":"logInt","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"p0","type":"string"}],"name":"logString","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"p0","type":"uint256"}],"name":"logUint","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"p0","type":"int256"}],"outputs":[],"stateMutability":"view","type":"function","name":"log"},{"inputs":[{"internalType":"string","name":"p0","type":"string"},{"internalType":"int256","name":"p1","type":"int256"}],"outputs":[],"stateMutability":"view","type":"function","name":"log"}] \ No newline at end of file diff --git a/crates/evm/core/test-data/storage.json b/crates/evm/core/test-data/storage.json index 4f2562591..6a602f7a6 100644 --- a/crates/evm/core/test-data/storage.json +++ b/crates/evm/core/test-data/storage.json @@ -1 +1 @@ -{"meta":{"cfg_env":{"chain_id":1,"spec_id":"LATEST","perf_all_precompiles_have_balance":false,"memory_limit":4294967295, "perf_analyse_created_bytecodes":"Analyse", "limit_contract_code_size": 24576, "disable_coinbase_tip": false},"block_env":{"number":"0xdc42b8","coinbase":"0x0000000000000000000000000000000000000000","timestamp":"0x1","difficulty":"0x0","basefee":"0x0","gas_limit":"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},"hosts":["mainnet.infura.io"]},"accounts":{"0x63091244180ae240c87d1f528f5f269134cb07b3":{"balance":"0x0","code_hash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","code":null,"nonce":0}},"storage":{"0x63091244180ae240c87d1f528f5f269134cb07b3":{"0x0":"0x0","0x1":"0x0","0x2":"0x0","0x3":"0x0","0x4":"0x0","0x5":"0x0","0x6":"0x0","0x7":"0x0","0x8":"0x0","0x9":"0x0"}},"block_hashes":{}} \ No newline at end of file +{"meta":{"cfg_env":{"chain_id":1,"spec_id":"LATEST","perf_all_precompiles_have_balance":false,"memory_limit":4294967295,"perf_analyse_created_bytecodes":"Analyse","limit_contract_code_size":24576,"disable_coinbase_tip":false},"block_env":{"number":"0xdc42b8","coinbase":"0x0000000000000000000000000000000000000000","timestamp":"0x1","difficulty":"0x0","basefee":"0x0","gas_limit":"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"},"hosts":["mainnet.infura.io"]},"accounts":{"0x63091244180ae240c87d1f528f5f269134cb07b3":{"balance":"0x0","code_hash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","code":null,"nonce":0}},"storage":{"0x63091244180ae240c87d1f528f5f269134cb07b3":{"0x0":"0x0","0x1":"0x0","0x2":"0x0","0x3":"0x0","0x4":"0x0","0x5":"0x0","0x6":"0x0","0x7":"0x0","0x8":"0x0","0x9":"0x0"}},"block_hashes":{}} \ No newline at end of file diff --git a/testdata/default/script/broadcast/deploy.sol/31337/run-latest.json b/testdata/default/script/broadcast/deploy.sol/31337/run-latest.json index 9a05e1cb7..1a8bccb39 100644 --- a/testdata/default/script/broadcast/deploy.sol/31337/run-latest.json +++ b/testdata/default/script/broadcast/deploy.sol/31337/run-latest.json @@ -1,63 +1 @@ -{ - "transactions": [ - { - "hash": null, - "type": "CREATE", - "contractName": null, - "contractAddress": "0x731a10897d267e19b34503ad902d0a29173ba4b1", - "function": null, - "arguments": null, - "transaction": { - "type": "0x02", - "from": "0x00a329c0648769a73afac7f9381e08fb43dbea72", - "gas": "0x54653", - "value": "0x0", - "data": "0x608060405234801561001057600080fd5b506103d9806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063d5dcf1271461003b578063f8194e4814610050575b600080fd5b61004e6100493660046100e9565b600155565b005b61006361005e366004610118565b610079565b60405161007091906101f9565b60405180910390f35b6060600061008783826102b5565b5060008260405160200161009b9190610375565b60405160208183030381529060405290507fefdeaaf566f7751d16a12c7fa8909eb74120f42cba334d07dd5246c48f1fba81816040516100db91906101f9565b60405180910390a192915050565b6000602082840312156100fb57600080fd5b5035919050565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561012a57600080fd5b813567ffffffffffffffff8082111561014257600080fd5b818401915084601f83011261015657600080fd5b81358181111561016857610168610102565b604051601f8201601f19908116603f0116810190838211818310171561019057610190610102565b816040528281528760208487010111156101a957600080fd5b826020860160208301376000928101602001929092525095945050505050565b60005b838110156101e45781810151838201526020016101cc565b838111156101f3576000848401525b50505050565b60208152600082518060208401526102188160408501602087016101c9565b601f01601f19169190910160400192915050565b600181811c9082168061024057607f821691505b60208210810361026057634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156102b057600081815260208120601f850160051c8101602086101561028d5750805b601f850160051c820191505b818110156102ac57828155600101610299565b5050505b505050565b815167ffffffffffffffff8111156102cf576102cf610102565b6102e3816102dd845461022c565b84610266565b602080601f83116001811461031857600084156103005750858301515b600019600386901b1c1916600185901b1785556102ac565b600085815260208120601f198616915b8281101561034757888601518255948401946001909101908401610328565b50858210156103655787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6502432b63637960d51b8152600082516103968160068501602087016101c9565b919091016006019291505056fea2646970667358221220a380cb042b6ca762a5a0f97e497c4cffa21c45dc21e2dab4107e5415921a704a64736f6c634300080f0033", - "nonce": "0x0", - "accessList": [] - } - }, - { - "hash": null, - "type": "CALL", - "contractName": null, - "contractAddress": "0x731a10897d267e19b34503ad902d0a29173ba4b1", - "function": null, - "arguments": null, - "transaction": { - "type": "0x02", - "from": "0x00a329c0648769a73afac7f9381e08fb43dbea72", - "to": "0x731a10897d267e19b34503ad902d0a29173ba4b1", - "gas": "0xef15", - "value": "0x0", - "data": "0xf8194e48000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000046a6f686e00000000000000000000000000000000000000000000000000000000", - "nonce": "0x1", - "accessList": [] - } - }, - { - "hash": null, - "type": "CALL", - "contractName": null, - "contractAddress": "0x731a10897d267e19b34503ad902d0a29173ba4b1", - "function": null, - "arguments": null, - "transaction": { - "type": "0x02", - "from": "0x00a329c0648769a73afac7f9381e08fb43dbea72", - "to": "0x731a10897d267e19b34503ad902d0a29173ba4b1", - "gas": "0xdcde", - "value": "0x0", - "data": "0xd5dcf127000000000000000000000000000000000000000000000000000000000000007b", - "nonce": "0x2", - "accessList": [] - } - } - ], - "receipts": [], - "libraries": [], - "pending": [], - "path": "broadcast/deploy.sol/31337/run-latest.json", - "returns": {}, - "timestamp": 1658913881 -} \ No newline at end of file +{"transactions":[{"hash":null,"type":"CREATE","contractName":null,"contractAddress":"0x731a10897d267e19b34503ad902d0a29173ba4b1","function":null,"arguments":null,"transaction":{"type":"0x02","from":"0x00a329c0648769a73afac7f9381e08fb43dbea72","gas":"0x54653","value":"0x0","data":"0x608060405234801561001057600080fd5b506103d9806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063d5dcf1271461003b578063f8194e4814610050575b600080fd5b61004e6100493660046100e9565b600155565b005b61006361005e366004610118565b610079565b60405161007091906101f9565b60405180910390f35b6060600061008783826102b5565b5060008260405160200161009b9190610375565b60405160208183030381529060405290507fefdeaaf566f7751d16a12c7fa8909eb74120f42cba334d07dd5246c48f1fba81816040516100db91906101f9565b60405180910390a192915050565b6000602082840312156100fb57600080fd5b5035919050565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561012a57600080fd5b813567ffffffffffffffff8082111561014257600080fd5b818401915084601f83011261015657600080fd5b81358181111561016857610168610102565b604051601f8201601f19908116603f0116810190838211818310171561019057610190610102565b816040528281528760208487010111156101a957600080fd5b826020860160208301376000928101602001929092525095945050505050565b60005b838110156101e45781810151838201526020016101cc565b838111156101f3576000848401525b50505050565b60208152600082518060208401526102188160408501602087016101c9565b601f01601f19169190910160400192915050565b600181811c9082168061024057607f821691505b60208210810361026057634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156102b057600081815260208120601f850160051c8101602086101561028d5750805b601f850160051c820191505b818110156102ac57828155600101610299565b5050505b505050565b815167ffffffffffffffff8111156102cf576102cf610102565b6102e3816102dd845461022c565b84610266565b602080601f83116001811461031857600084156103005750858301515b600019600386901b1c1916600185901b1785556102ac565b600085815260208120601f198616915b8281101561034757888601518255948401946001909101908401610328565b50858210156103655787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6502432b63637960d51b8152600082516103968160068501602087016101c9565b919091016006019291505056fea2646970667358221220a380cb042b6ca762a5a0f97e497c4cffa21c45dc21e2dab4107e5415921a704a64736f6c634300080f0033","nonce":"0x0","accessList":[]}},{"hash":null,"type":"CALL","contractName":null,"contractAddress":"0x731a10897d267e19b34503ad902d0a29173ba4b1","function":null,"arguments":null,"transaction":{"type":"0x02","from":"0x00a329c0648769a73afac7f9381e08fb43dbea72","to":"0x731a10897d267e19b34503ad902d0a29173ba4b1","gas":"0xef15","value":"0x0","data":"0xf8194e48000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000046a6f686e00000000000000000000000000000000000000000000000000000000","nonce":"0x1","accessList":[]}},{"hash":null,"type":"CALL","contractName":null,"contractAddress":"0x731a10897d267e19b34503ad902d0a29173ba4b1","function":null,"arguments":null,"transaction":{"type":"0x02","from":"0x00a329c0648769a73afac7f9381e08fb43dbea72","to":"0x731a10897d267e19b34503ad902d0a29173ba4b1","gas":"0xdcde","value":"0x0","data":"0xd5dcf127000000000000000000000000000000000000000000000000000000000000007b","nonce":"0x2","accessList":[]}}],"receipts":[],"libraries":[],"pending":[],"path":"broadcast/deploy.sol/31337/run-latest.json","returns":{},"timestamp":1658913881} \ No newline at end of file diff --git a/testdata/etherscan/0x044b75f554b886A065b9567891e45c79542d7357/creation_data.json b/testdata/etherscan/0x044b75f554b886A065b9567891e45c79542d7357/creation_data.json index d709187b8..899a44c15 100644 --- a/testdata/etherscan/0x044b75f554b886A065b9567891e45c79542d7357/creation_data.json +++ b/testdata/etherscan/0x044b75f554b886A065b9567891e45c79542d7357/creation_data.json @@ -1,5 +1 @@ -{ - "contractAddress": "0x044b75f554b886a065b9567891e45c79542d7357", - "contractCreator": "0xf87bc5535602077d340806d71f805ea9907a843d", - "txHash": "0x9a89d2f5528bf07661e92f3f78a3311396f11f15da19e3ec4d880be1ad1a4bec" -} \ No newline at end of file +{"contractAddress":"0x044b75f554b886a065b9567891e45c79542d7357","contractCreator":"0xf87bc5535602077d340806d71f805ea9907a843d","txHash":"0x9a89d2f5528bf07661e92f3f78a3311396f11f15da19e3ec4d880be1ad1a4bec"} \ No newline at end of file diff --git a/testdata/etherscan/0x044b75f554b886A065b9567891e45c79542d7357/metadata.json b/testdata/etherscan/0x044b75f554b886A065b9567891e45c79542d7357/metadata.json index 12f7f6a3b..54aec7d6a 100644 --- a/testdata/etherscan/0x044b75f554b886A065b9567891e45c79542d7357/metadata.json +++ b/testdata/etherscan/0x044b75f554b886A065b9567891e45c79542d7357/metadata.json @@ -1,78 +1 @@ -[ - { - "SourceCode": { - "language": "Solidity", - "sources": { - "contracts/InputStream.sol": { - "content": "// SPDX-License-Identifier: UNLICENSED\n\npragma solidity 0.8.10;\n\nlibrary InputStream {\n function createStream(bytes memory data) internal pure returns (uint256 stream) {\n assembly {\n stream := mload(0x40)\n mstore(0x40, add(stream, 64))\n mstore(stream, data)\n let length := mload(data)\n mstore(add(stream, 32), add(data, length))\n }\n }\n\n function isNotEmpty(uint256 stream) internal pure returns (bool) {\n uint256 pos;\n uint256 finish;\n assembly {\n pos := mload(stream)\n finish := mload(add(stream, 32))\n }\n return pos < finish;\n }\n\n function readUint8(uint256 stream) internal pure returns (uint8 res) {\n assembly {\n let pos := mload(stream)\n pos := add(pos, 1)\n res := mload(pos)\n mstore(stream, pos)\n }\n }\n\n function readUint16(uint256 stream) internal pure returns (uint16 res) {\n assembly {\n let pos := mload(stream)\n pos := add(pos, 2)\n res := mload(pos)\n mstore(stream, pos)\n }\n }\n\n function readUint32(uint256 stream) internal pure returns (uint32 res) {\n assembly {\n let pos := mload(stream)\n pos := add(pos, 4)\n res := mload(pos)\n mstore(stream, pos)\n }\n }\n\n function readUint(uint256 stream) internal pure returns (uint256 res) {\n assembly {\n let pos := mload(stream)\n pos := add(pos, 32)\n res := mload(pos)\n mstore(stream, pos)\n }\n }\n\n function readAddress(uint256 stream) internal pure returns (address res) {\n assembly {\n let pos := mload(stream)\n pos := add(pos, 20)\n res := mload(pos)\n mstore(stream, pos)\n }\n }\n\n function readBytes(uint256 stream) internal pure returns (bytes memory res) {\n assembly {\n let pos := mload(stream)\n res := add(pos, 32)\n let length := mload(res)\n mstore(stream, add(res, length))\n }\n }\n}\n" - }, - "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { - "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../extensions/draft-IERC20Permit.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n function safePermit(\n IERC20Permit token,\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal {\n uint256 nonceBefore = token.nonces(owner);\n token.permit(owner, spender, value, deadline, v, r, s);\n uint256 nonceAfter = token.nonces(owner);\n require(nonceAfter == nonceBefore + 1, \"SafeERC20: permit did not succeed\");\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" - }, - "interfaces/IBentoBoxMinimal.sol": { - "content": "// SPDX-License-Identifier: UNLICENSED\n\npragma solidity >=0.8.0;\n\nstruct Rebase {\n uint128 elastic;\n uint128 base;\n}\n\nstruct StrategyData {\n uint64 strategyStartDate;\n uint64 targetPercentage;\n uint128 balance; // the balance of the strategy that BentoBox thinks is in there\n}\n\n/// @notice A rebasing library\nlibrary RebaseLibrary {\n /// @notice Calculates the base value in relationship to `elastic` and `total`.\n function toBase(Rebase memory total, uint256 elastic) internal pure returns (uint256 base) {\n if (total.elastic == 0) {\n base = elastic;\n } else {\n base = (elastic * total.base) / total.elastic;\n }\n }\n\n /// @notice Calculates the elastic value in relationship to `base` and `total`.\n function toElastic(Rebase memory total, uint256 base) internal pure returns (uint256 elastic) {\n if (total.base == 0) {\n elastic = base;\n } else {\n elastic = (base * total.elastic) / total.base;\n }\n }\n}\n\n/// @notice Minimal BentoBox vault interface.\n/// @dev `token` is aliased as `address` from `IERC20` for simplicity.\ninterface IBentoBoxMinimal {\n /// @notice Balance per ERC-20 token per account in shares.\n function balanceOf(address, address) external view returns (uint256);\n\n /// @dev Helper function to represent an `amount` of `token` in shares.\n /// @param token The ERC-20 token.\n /// @param amount The `token` amount.\n /// @param roundUp If the result `share` should be rounded up.\n /// @return share The token amount represented in shares.\n function toShare(\n address token,\n uint256 amount,\n bool roundUp\n ) external view returns (uint256 share);\n\n /// @dev Helper function to represent shares back into the `token` amount.\n /// @param token The ERC-20 token.\n /// @param share The amount of shares.\n /// @param roundUp If the result should be rounded up.\n /// @return amount The share amount back into native representation.\n function toAmount(\n address token,\n uint256 share,\n bool roundUp\n ) external view returns (uint256 amount);\n\n /// @notice Registers this contract so that users can approve it for BentoBox.\n function registerProtocol() external;\n\n /// @notice Deposit an amount of `token` represented in either `amount` or `share`.\n /// @param token The ERC-20 token to deposit.\n /// @param from which account to pull the tokens.\n /// @param to which account to push the tokens.\n /// @param amount Token amount in native representation to deposit.\n /// @param share Token amount represented in shares to deposit. Takes precedence over `amount`.\n /// @return amountOut The amount deposited.\n /// @return shareOut The deposited amount represented in shares.\n function deposit(\n address token,\n address from,\n address to,\n uint256 amount,\n uint256 share\n ) external payable returns (uint256 amountOut, uint256 shareOut);\n\n /// @notice Withdraws an amount of `token` from a user account.\n /// @param token_ The ERC-20 token to withdraw.\n /// @param from which user to pull the tokens.\n /// @param to which user to push the tokens.\n /// @param amount of tokens. Either one of `amount` or `share` needs to be supplied.\n /// @param share Like above, but `share` takes precedence over `amount`.\n function withdraw(\n address token_,\n address from,\n address to,\n uint256 amount,\n uint256 share\n ) external returns (uint256 amountOut, uint256 shareOut);\n\n /// @notice Transfer shares from a user account to another one.\n /// @param token The ERC-20 token to transfer.\n /// @param from which user to pull the tokens.\n /// @param to which user to push the tokens.\n /// @param share The amount of `token` in shares.\n function transfer(\n address token,\n address from,\n address to,\n uint256 share\n ) external;\n\n /// @dev Reads the Rebase `totals`from storage for a given token\n function totals(address token) external view returns (Rebase memory total);\n\n function strategyData(address token) external view returns (StrategyData memory total);\n\n /// @dev Approves users' BentoBox assets to a \"master\" contract.\n function setMasterContractApproval(\n address user,\n address masterContract,\n bool approved,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n function harvest(\n address token,\n bool balance,\n uint256 maxChangeAmount\n ) external;\n}\n" - }, - "@openzeppelin/contracts/utils/Address.sol": { - "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\n * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\n *\n * _Available since v4.8._\n */\n function verifyCallResultFromTarget(\n address target,\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n if (success) {\n if (returndata.length == 0) {\n // only check isContract if the call was successful and the return data is empty\n // otherwise we already know that it was a contract\n require(isContract(target), \"Address: call to non-contract\");\n }\n return returndata;\n } else {\n _revert(returndata, errorMessage);\n }\n }\n\n /**\n * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason or using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n _revert(returndata, errorMessage);\n }\n }\n\n function _revert(bytes memory returndata, string memory errorMessage) private pure {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n /// @solidity memory-safe-assembly\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n}\n" - }, - "interfaces/IPool.sol": { - "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n\npragma solidity >=0.5.0;\npragma experimental ABIEncoderV2;\n\n/// @notice Trident pool interface.\ninterface IPool {\n /// @notice Executes a swap from one token to another.\n /// @dev The input tokens must've already been sent to the pool.\n /// @param data ABI-encoded params that the pool requires.\n /// @return finalAmountOut The amount of output tokens that were sent to the user.\n function swap(bytes calldata data) external returns (uint256 finalAmountOut);\n\n /// @notice Executes a swap from one token to another with a callback.\n /// @dev This function allows borrowing the output tokens and sending the input tokens in the callback.\n /// @param data ABI-encoded params that the pool requires.\n /// @return finalAmountOut The amount of output tokens that were sent to the user.\n function flashSwap(bytes calldata data) external returns (uint256 finalAmountOut);\n\n /// @notice Mints liquidity tokens.\n /// @param data ABI-encoded params that the pool requires.\n /// @return liquidity The amount of liquidity tokens that were minted for the user.\n function mint(bytes calldata data) external returns (uint256 liquidity);\n\n /// @notice Burns liquidity tokens.\n /// @dev The input LP tokens must've already been sent to the pool.\n /// @param data ABI-encoded params that the pool requires.\n /// @return withdrawnAmounts The amount of various output tokens that were sent to the user.\n function burn(bytes calldata data) external returns (TokenAmount[] memory withdrawnAmounts);\n\n /// @notice Burns liquidity tokens for a single output token.\n /// @dev The input LP tokens must've already been sent to the pool.\n /// @param data ABI-encoded params that the pool requires.\n /// @return amountOut The amount of output tokens that were sent to the user.\n function burnSingle(bytes calldata data) external returns (uint256 amountOut);\n\n /// @return A unique identifier for the pool type.\n function poolIdentifier() external pure returns (bytes32);\n\n /// @return An array of tokens supported by the pool.\n function getAssets() external view returns (address[] memory);\n\n /// @notice Simulates a trade and returns the expected output.\n /// @dev The pool does not need to include a trade simulator directly in itself - it can use a library.\n /// @param data ABI-encoded params that the pool requires.\n /// @return finalAmountOut The amount of output tokens that will be sent to the user if the trade is executed.\n function getAmountOut(bytes calldata data) external view returns (uint256 finalAmountOut);\n\n /// @notice Simulates a trade and returns the expected output.\n /// @dev The pool does not need to include a trade simulator directly in itself - it can use a library.\n /// @param data ABI-encoded params that the pool requires.\n /// @return finalAmountIn The amount of input tokens that are required from the user if the trade is executed.\n function getAmountIn(bytes calldata data) external view returns (uint256 finalAmountIn);\n\n /// @dev This event must be emitted on all swaps.\n event Swap(address indexed recipient, address indexed tokenIn, address indexed tokenOut, uint256 amountIn, uint256 amountOut);\n\n /// @dev This struct frames output tokens for burns.\n struct TokenAmount {\n address token;\n uint256 amount;\n }\n}\n" - }, - "@openzeppelin/contracts/token/ERC20/IERC20.sol": { - "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n}\n" - }, - "contracts/RouteProcessor2.sol": { - "content": "// SPDX-License-Identifier: UNLICENSED\n\npragma solidity 0.8.10;\n\nimport '../interfaces/IUniswapV2Pair.sol';\nimport '../interfaces/IUniswapV3Pool.sol';\nimport '../interfaces/ITridentCLPool.sol';\nimport '../interfaces/IBentoBoxMinimal.sol';\nimport '../interfaces/IPool.sol';\nimport '../interfaces/IWETH.sol';\nimport './InputStream.sol';\nimport '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';\n\naddress constant NATIVE_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\naddress constant IMPOSSIBLE_POOL_ADDRESS = 0x0000000000000000000000000000000000000001;\n\n/// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)\nuint160 constant MIN_SQRT_RATIO = 4295128739;\n/// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)\nuint160 constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;\n\n/// @title A route processor for the Sushi Aggregator\n/// @author Ilya Lyalin\ncontract RouteProcessor2 {\n using SafeERC20 for IERC20;\n using InputStream for uint256;\n\n IBentoBoxMinimal public immutable bentoBox;\n address private lastCalledPool;\n\n uint private unlocked = 1;\n modifier lock() {\n require(unlocked == 1, 'RouteProcessor is locked');\n unlocked = 2;\n _;\n unlocked = 1;\n }\n\n constructor(address _bentoBox) {\n bentoBox = IBentoBoxMinimal(_bentoBox);\n lastCalledPool = IMPOSSIBLE_POOL_ADDRESS;\n }\n\n /// @notice For native unwrapping\n receive() external payable {}\n\n /// @notice Processes the route generated off-chain. Has a lock\n /// @param tokenIn Address of the input token\n /// @param amountIn Amount of the input token\n /// @param tokenOut Address of the output token\n /// @param amountOutMin Minimum amount of the output token\n /// @return amountOut Actual amount of the output token\n function processRoute(\n address tokenIn,\n uint256 amountIn,\n address tokenOut,\n uint256 amountOutMin,\n address to,\n bytes memory route\n ) external payable lock returns (uint256 amountOut) {\n return processRouteInternal(tokenIn, amountIn, tokenOut, amountOutMin, to, route);\n }\n\n /// @notice Transfers some value to and then processes the route\n /// @param transferValueTo Address where the value should be transferred\n /// @param amountValueTransfer How much value to transfer\n /// @param tokenIn Address of the input token\n /// @param amountIn Amount of the input token\n /// @param tokenOut Address of the output token\n /// @param amountOutMin Minimum amount of the output token\n /// @return amountOut Actual amount of the output token\n function transferValueAndprocessRoute(\n address payable transferValueTo,\n uint256 amountValueTransfer,\n address tokenIn,\n uint256 amountIn,\n address tokenOut,\n uint256 amountOutMin,\n address to,\n bytes memory route\n ) external payable lock returns (uint256 amountOut) {\n (bool success, bytes memory returnBytes) = transferValueTo.call{value: amountValueTransfer}('');\n require(success, string(abi.encodePacked(returnBytes)));\n return processRouteInternal(tokenIn, amountIn, tokenOut, amountOutMin, to, route);\n }\n\n /// @notice Processes the route generated off-chain\n /// @param tokenIn Address of the input token\n /// @param amountIn Amount of the input token\n /// @param tokenOut Address of the output token\n /// @param amountOutMin Minimum amount of the output token\n /// @return amountOut Actual amount of the output token\n function processRouteInternal(\n address tokenIn,\n uint256 amountIn,\n address tokenOut,\n uint256 amountOutMin,\n address to,\n bytes memory route\n ) private returns (uint256 amountOut) {\n uint256 balanceInInitial = tokenIn == NATIVE_ADDRESS ? address(this).balance : IERC20(tokenIn).balanceOf(msg.sender);\n uint256 balanceOutInitial = tokenOut == NATIVE_ADDRESS ? address(to).balance : IERC20(tokenOut).balanceOf(to);\n\n uint256 stream = InputStream.createStream(route);\n while (stream.isNotEmpty()) {\n uint8 commandCode = stream.readUint8();\n if (commandCode == 1) processMyERC20(stream);\n else if (commandCode == 2) processUserERC20(stream, amountIn);\n else if (commandCode == 3) processNative(stream);\n else if (commandCode == 4) processOnePool(stream);\n else if (commandCode == 5) processInsideBento(stream);\n else revert('RouteProcessor: Unknown command code');\n }\n\n uint256 balanceInFinal = tokenIn == NATIVE_ADDRESS ? address(this).balance : IERC20(tokenIn).balanceOf(msg.sender);\n require(balanceInFinal + amountIn >= balanceInInitial, 'RouteProcessor: Minimal imput balance violation');\n\n uint256 balanceOutFinal = tokenOut == NATIVE_ADDRESS ? address(to).balance : IERC20(tokenOut).balanceOf(to);\n require(balanceOutFinal >= balanceOutInitial + amountOutMin, 'RouteProcessor: Minimal ouput balance violation');\n\n amountOut = balanceOutFinal - balanceOutInitial;\n }\n\n /// @notice Processes native coin: call swap for all pools that swap from native coin\n /// @param stream Streamed process program\n function processNative(uint256 stream) private {\n uint256 amountTotal = address(this).balance;\n distributeAndSwap(stream, address(this), NATIVE_ADDRESS, amountTotal);\n }\n\n /// @notice Processes ERC20 token from this contract balance:\n /// @notice Call swap for all pools that swap from this token\n /// @param stream Streamed process program\n function processMyERC20(uint256 stream) private {\n address token = stream.readAddress();\n uint256 amountTotal = IERC20(token).balanceOf(address(this));\n unchecked {\n if (amountTotal > 0) amountTotal -= 1; // slot undrain protection\n }\n distributeAndSwap(stream, address(this), token, amountTotal);\n }\n \n /// @notice Processes ERC20 token from msg.sender balance:\n /// @notice Call swap for all pools that swap from this token\n /// @param stream Streamed process program\n /// @param amountTotal Amount of tokens to take from msg.sender\n function processUserERC20(uint256 stream, uint256 amountTotal) private {\n address token = stream.readAddress();\n distributeAndSwap(stream, msg.sender, token, amountTotal);\n }\n\n /// @notice Distributes amountTotal to several pools according to their shares and calls swap for each pool\n /// @param stream Streamed process program\n /// @param from Where to take liquidity for swap\n /// @param tokenIn Input token\n /// @param amountTotal Total amount of tokenIn for swaps \n function distributeAndSwap(uint256 stream, address from, address tokenIn, uint256 amountTotal) private {\n uint8 num = stream.readUint8();\n unchecked {\n for (uint256 i = 0; i < num; ++i) {\n uint16 share = stream.readUint16();\n uint256 amount = (amountTotal * share) / 65535;\n amountTotal -= amount;\n swap(stream, from, tokenIn, amount);\n }\n }\n }\n\n /// @notice Processes ERC20 token for cases when the token has only one output pool\n /// @notice In this case liquidity is already at pool balance. This is an optimization\n /// @notice Call swap for all pools that swap from this token\n /// @param stream Streamed process program\n function processOnePool(uint256 stream) private {\n address token = stream.readAddress();\n swap(stream, address(this), token, 0);\n }\n\n /// @notice Processes Bento tokens \n /// @notice Call swap for all pools that swap from this token\n /// @param stream Streamed process program\n function processInsideBento(uint256 stream) private {\n address token = stream.readAddress();\n uint8 num = stream.readUint8();\n\n uint256 amountTotal = bentoBox.balanceOf(token, address(this));\n unchecked {\n if (amountTotal > 0) amountTotal -= 1; // slot undrain protection\n for (uint256 i = 0; i < num; ++i) {\n uint16 share = stream.readUint16();\n uint256 amount = (amountTotal * share) / 65535;\n amountTotal -= amount;\n swap(stream, address(this), token, amount);\n }\n }\n }\n\n /// @notice Makes swap\n /// @param stream Streamed process program\n /// @param from Where to take liquidity for swap\n /// @param tokenIn Input token\n /// @param amountIn Amount of tokenIn to take for swap\n function swap(uint256 stream, address from, address tokenIn, uint256 amountIn) private {\n uint8 poolType = stream.readUint8();\n if (poolType == 0) swapUniV2(stream, from, tokenIn, amountIn);\n else if (poolType == 1) swapUniV3(stream, from, tokenIn, amountIn);\n else if (poolType == 2) wrapNative(stream, from, tokenIn, amountIn);\n else if (poolType == 3) bentoBridge(stream, from, tokenIn, amountIn);\n else if (poolType == 4) swapTrident(stream, from, tokenIn, amountIn);\n else if (poolType == 5) swapTridentCL(stream, from, tokenIn, amountIn);\n else revert('RouteProcessor: Unknown pool type');\n }\n\n /// @notice Wraps/unwraps native token\n /// @param stream [direction & fake, recipient, wrapToken?]\n /// @param from Where to take liquidity for swap\n /// @param tokenIn Input token\n /// @param amountIn Amount of tokenIn to take for swap\n function wrapNative(uint256 stream, address from, address tokenIn, uint256 amountIn) private {\n uint8 directionAndFake = stream.readUint8();\n address to = stream.readAddress();\n\n if (directionAndFake & 1 == 1) { // wrap native\n address wrapToken = stream.readAddress();\n if (directionAndFake & 2 == 0) IWETH(wrapToken).deposit{value: amountIn}();\n if (to != address(this)) IERC20(wrapToken).safeTransfer(to, amountIn);\n } else { // unwrap native\n if (directionAndFake & 2 == 0) {\n if (from != address(this)) IERC20(tokenIn).safeTransferFrom(from, address(this), amountIn);\n IWETH(tokenIn).withdraw(amountIn);\n }\n payable(to).transfer(address(this).balance);\n }\n }\n\n /// @notice Bridge/unbridge tokens to/from Bento\n /// @param stream [direction, recipient]\n /// @param from Where to take liquidity for swap\n /// @param tokenIn Input token\n /// @param amountIn Amount of tokenIn to take for swap\n function bentoBridge(uint256 stream, address from, address tokenIn, uint256 amountIn) private {\n uint8 direction = stream.readUint8();\n address to = stream.readAddress();\n\n if (direction > 0) { // outside to Bento\n // deposit to arbitrary recipient is possible only from address(bentoBox)\n if (amountIn != 0) {\n if (from == address(this)) IERC20(tokenIn).safeTransfer(address(bentoBox), amountIn);\n else IERC20(tokenIn).safeTransferFrom(from, address(bentoBox), amountIn);\n } else {\n // tokens already are at address(bentoBox)\n amountIn = IERC20(tokenIn).balanceOf(address(bentoBox)) +\n bentoBox.strategyData(tokenIn).balance -\n bentoBox.totals(tokenIn).elastic;\n }\n bentoBox.deposit(tokenIn, address(bentoBox), to, amountIn, 0);\n } else { // Bento to outside\n if (amountIn > 0) {\n bentoBox.transfer(tokenIn, from, address(this), amountIn);\n } else amountIn = bentoBox.balanceOf(tokenIn, address(this));\n bentoBox.withdraw(tokenIn, address(this), to, 0, amountIn);\n }\n }\n\n /// @notice UniswapV2 pool swap\n /// @param stream [pool, direction, recipient]\n /// @param from Where to take liquidity for swap\n /// @param tokenIn Input token\n /// @param amountIn Amount of tokenIn to take for swap\n function swapUniV2(uint256 stream, address from, address tokenIn, uint256 amountIn) private {\n address pool = stream.readAddress();\n uint8 direction = stream.readUint8();\n address to = stream.readAddress();\n\n (uint256 r0, uint256 r1, ) = IUniswapV2Pair(pool).getReserves();\n require(r0 > 0 && r1 > 0, 'Wrong pool reserves');\n (uint256 reserveIn, uint256 reserveOut) = direction == 1 ? (r0, r1) : (r1, r0);\n\n if (amountIn != 0) {\n if (from == address(this)) IERC20(tokenIn).safeTransfer(pool, amountIn);\n else IERC20(tokenIn).safeTransferFrom(from, pool, amountIn);\n } else amountIn = IERC20(tokenIn).balanceOf(pool) - reserveIn; // tokens already were transferred\n\n uint256 amountInWithFee = amountIn * 997;\n uint256 amountOut = (amountInWithFee * reserveOut) / (reserveIn * 1000 + amountInWithFee);\n (uint256 amount0Out, uint256 amount1Out) = direction == 1 ? (uint256(0), amountOut) : (amountOut, uint256(0));\n IUniswapV2Pair(pool).swap(amount0Out, amount1Out, to, new bytes(0));\n }\n\n /// @notice Trident pool swap\n /// @param stream [pool, swapData]\n /// @param from Where to take liquidity for swap\n /// @param tokenIn Input token\n /// @param amountIn Amount of tokenIn to take for swap\n function swapTrident(uint256 stream, address from, address tokenIn, uint256 amountIn) private {\n address pool = stream.readAddress();\n bytes memory swapData = stream.readBytes();\n\n if (amountIn != 0) {\n bentoBox.transfer(tokenIn, from, pool, amountIn);\n }\n \n IPool(pool).swap(swapData);\n }\n\n /// @notice UniswapV3 pool swap\n /// @param stream [pool, direction, recipient]\n /// @param from Where to take liquidity for swap\n /// @param tokenIn Input token\n /// @param amountIn Amount of tokenIn to take for swap\n function swapUniV3(uint256 stream, address from, address tokenIn, uint256 amountIn) private {\n address pool = stream.readAddress();\n bool zeroForOne = stream.readUint8() > 0;\n address recipient = stream.readAddress();\n\n lastCalledPool = pool;\n IUniswapV3Pool(pool).swap(\n recipient,\n zeroForOne,\n int256(amountIn),\n zeroForOne ? MIN_SQRT_RATIO + 1 : MAX_SQRT_RATIO - 1,\n abi.encode(tokenIn, from)\n );\n require(lastCalledPool == IMPOSSIBLE_POOL_ADDRESS, 'RouteProcessor.swapUniV3: unexpected'); // Just to be sure\n }\n\n /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.\n /// @dev In the implementation you must pay the pool tokens owed for the swap.\n /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.\n /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.\n /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by\n /// the end of the swap. If positive, the callback must send that amount of token0 to the pool.\n /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by\n /// the end of the swap. If positive, the callback must send that amount of token1 to the pool.\n /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call\n function uniswapV3SwapCallback(\n int256 amount0Delta,\n int256 amount1Delta,\n bytes calldata data\n ) external {\n require(msg.sender == lastCalledPool, 'RouteProcessor.uniswapV3SwapCallback: call from unknown source');\n lastCalledPool = IMPOSSIBLE_POOL_ADDRESS;\n (address tokenIn, address from) = abi.decode(data, (address, address));\n int256 amount = amount0Delta > 0 ? amount0Delta : amount1Delta;\n require(amount > 0, 'RouteProcessor.uniswapV3SwapCallback: not positive amount');\n\n if (from == address(this)) IERC20(tokenIn).safeTransfer(msg.sender, uint256(amount));\n else IERC20(tokenIn).safeTransferFrom(from, msg.sender, uint256(amount));\n }\n\n /// @notice TridentCL pool swap\n /// @param stream [pool, direction, recipient]\n /// @param from Where to take liquidity for swap\n /// @param tokenIn Input token\n /// @param amountIn Amount of tokenIn to take for swap\n function swapTridentCL(uint256 stream, address from, address tokenIn, uint256 amountIn) private {\n address pool = stream.readAddress();\n bool zeroForOne = stream.readUint8() > 0;\n address recipient = stream.readAddress();\n\n lastCalledPool = pool;\n ITridentCLPool(pool).swap(\n recipient,\n zeroForOne,\n int256(amountIn),\n zeroForOne ? MIN_SQRT_RATIO + 1 : MAX_SQRT_RATIO - 1,\n false,\n abi.encode(tokenIn, from)\n );\n require(lastCalledPool == IMPOSSIBLE_POOL_ADDRESS, 'RouteProcessor.swapTridentCL: unexpected'); // Just to be sure\n }\n\n /// @notice Called to `msg.sender` after executing a swap via ITridentCLPool#swap.\n /// @dev In the implementation you must pay the pool tokens owed for the swap.\n /// The caller of this method must be checked to be a TridentCLPool deployed by the canonical TridentCLFactory.\n /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.\n /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by\n /// the end of the swap. If positive, the callback must send that amount of token0 to the pool.\n /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by\n /// the end of the swap. If positive, the callback must send that amount of token1 to the pool.\n /// @param data Any data passed through by the caller via the ITridentCLPoolActions#swap call\n function tridentCLSwapCallback(\n int256 amount0Delta,\n int256 amount1Delta,\n bytes calldata data\n ) external {\n require(msg.sender == lastCalledPool, 'RouteProcessor.TridentCLSwapCallback: call from unknown source');\n lastCalledPool = IMPOSSIBLE_POOL_ADDRESS;\n (address tokenIn, address from) = abi.decode(data, (address, address));\n int256 amount = amount0Delta > 0 ? amount0Delta : amount1Delta;\n require(amount > 0, 'RouteProcessor.TridentCLSwapCallback: not positive amount');\n\n if (from == address(this)) IERC20(tokenIn).safeTransfer(msg.sender, uint256(amount));\n else IERC20(tokenIn).safeTransferFrom(from, msg.sender, uint256(amount));\n }\n}\n" - }, - "interfaces/ITridentCLPool.sol": { - "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n\npragma solidity 0.8.10;\n\ninterface ITridentCLPool {\n function token0() external returns (address);\n function token1() external returns (address);\n\n function swap(\n address recipient,\n bool zeroForOne,\n int256 amountSpecified,\n uint160 sqrtPriceLimitX96,\n bool unwrapBento,\n bytes calldata data\n ) external returns (int256 amount0, int256 amount1);\n}\n" - }, - "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol": { - "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n */\ninterface IERC20Permit {\n /**\n * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\n * given ``owner``'s signed approval.\n *\n * IMPORTANT: The same issues {IERC20-approve} has related to transaction\n * ordering also apply here.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `deadline` must be a timestamp in the future.\n * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\n * over the EIP712-formatted function arguments.\n * - the signature must use ``owner``'s current nonce (see {nonces}).\n *\n * For more information on the signature format, see the\n * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\n * section].\n */\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n /**\n * @dev Returns the current nonce for `owner`. This value must be\n * included whenever a signature is generated for {permit}.\n *\n * Every successful call to {permit} increases ``owner``'s nonce by one. This\n * prevents a signature from being used multiple times.\n */\n function nonces(address owner) external view returns (uint256);\n\n /**\n * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\n */\n // solhint-disable-next-line func-name-mixedcase\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n" - }, - "interfaces/IUniswapV2Pair.sol": { - "content": "// SPDX-License-Identifier: GPL-3.0\n\npragma solidity >=0.5.0;\n\ninterface IUniswapV2Pair {\n event Approval(address indexed owner, address indexed spender, uint value);\n event Transfer(address indexed from, address indexed to, uint value);\n\n function name() external pure returns (string memory);\n function symbol() external pure returns (string memory);\n function decimals() external pure returns (uint8);\n function totalSupply() external view returns (uint);\n function balanceOf(address owner) external view returns (uint);\n function allowance(address owner, address spender) external view returns (uint);\n\n function approve(address spender, uint value) external returns (bool);\n function transfer(address to, uint value) external returns (bool);\n function transferFrom(address from, address to, uint value) external returns (bool);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n function PERMIT_TYPEHASH() external pure returns (bytes32);\n function nonces(address owner) external view returns (uint);\n\n function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;\n\n event Mint(address indexed sender, uint amount0, uint amount1);\n event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);\n event Swap(\n address indexed sender,\n uint amount0In,\n uint amount1In,\n uint amount0Out,\n uint amount1Out,\n address indexed to\n );\n event Sync(uint112 reserve0, uint112 reserve1);\n\n function MINIMUM_LIQUIDITY() external pure returns (uint);\n function factory() external view returns (address);\n function token0() external view returns (address);\n function token1() external view returns (address);\n function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);\n function price0CumulativeLast() external view returns (uint);\n function price1CumulativeLast() external view returns (uint);\n function kLast() external view returns (uint);\n\n function mint(address to) external returns (uint liquidity);\n function burn(address to) external returns (uint amount0, uint amount1);\n function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;\n function skim(address to) external;\n function sync() external;\n\n function initialize(address, address) external;\n}" - }, - "interfaces/IUniswapV3Pool.sol": { - "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n\npragma solidity 0.8.10;\n\ninterface IUniswapV3Pool {\n function token0() external returns (address);\n function token1() external returns (address);\n\n function swap(\n address recipient,\n bool zeroForOne,\n int256 amountSpecified,\n uint160 sqrtPriceLimitX96,\n bytes calldata data\n ) external returns (int256 amount0, int256 amount1);\n}\n" - }, - "interfaces/IWETH.sol": { - "content": "// SPDX-License-Identifier: GPL-3.0-or-later\n\npragma solidity 0.8.10;\n\ninterface IWETH {\n function deposit() external payable;\n\n function transfer(address to, uint256 value) external returns (bool);\n\n function withdraw(uint256) external;\n}\n" - } - }, - "settings": { - "optimizer": { - "enabled": true, - "runs": 10000000 - }, - "outputSelection": { - "*": { - "*": [ - "evm.bytecode", - "evm.deployedBytecode", - "devdoc", - "userdoc", - "metadata", - "abi" - ] - } - }, - "metadata": { - "useLiteralContent": true - }, - "libraries": {} - } - }, - "ABI": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_bentoBox\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"bentoBox\",\"outputs\":[{\"internalType\":\"contract IBentoBoxMinimal\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"tokenIn\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amountIn\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"tokenOut\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amountOutMin\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"route\",\"type\":\"bytes\"}],\"name\":\"processRoute\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"amountOut\",\"type\":\"uint256\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address payable\",\"name\":\"transferValueTo\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amountValueTransfer\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"tokenIn\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amountIn\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"tokenOut\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amountOutMin\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"route\",\"type\":\"bytes\"}],\"name\":\"transferValueAndprocessRoute\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"amountOut\",\"type\":\"uint256\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"amount0Delta\",\"type\":\"int256\"},{\"internalType\":\"int256\",\"name\":\"amount1Delta\",\"type\":\"int256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"tridentCLSwapCallback\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"amount0Delta\",\"type\":\"int256\"},{\"internalType\":\"int256\",\"name\":\"amount1Delta\",\"type\":\"int256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"uniswapV3SwapCallback\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]", - "ContractName": "RouteProcessor2", - "CompilerVersion": "v0.8.10+commit.fc410830", - "OptimizationUsed": 1, - "Runs": 10000000, - "ConstructorArguments": "0x000000000000000000000000f5bce5077908a1b7370b9ae04adc565ebd643966", - "EVMVersion": "Default", - "Library": "", - "LicenseType": "", - "Proxy": 0, - "SwarmSource": "" - } -] \ No newline at end of file +[{"SourceCode":{"language":"Solidity","sources":{"contracts/InputStream.sol":{"content":"// SPDX-License-Identifier: UNLICENSED\n\npragma solidity 0.8.10;\n\nlibrary InputStream {\n function createStream(bytes memory data) internal pure returns (uint256 stream) {\n assembly {\n stream := mload(0x40)\n mstore(0x40, add(stream, 64))\n mstore(stream, data)\n let length := mload(data)\n mstore(add(stream, 32), add(data, length))\n }\n }\n\n function isNotEmpty(uint256 stream) internal pure returns (bool) {\n uint256 pos;\n uint256 finish;\n assembly {\n pos := mload(stream)\n finish := mload(add(stream, 32))\n }\n return pos < finish;\n }\n\n function readUint8(uint256 stream) internal pure returns (uint8 res) {\n assembly {\n let pos := mload(stream)\n pos := add(pos, 1)\n res := mload(pos)\n mstore(stream, pos)\n }\n }\n\n function readUint16(uint256 stream) internal pure returns (uint16 res) {\n assembly {\n let pos := mload(stream)\n pos := add(pos, 2)\n res := mload(pos)\n mstore(stream, pos)\n }\n }\n\n function readUint32(uint256 stream) internal pure returns (uint32 res) {\n assembly {\n let pos := mload(stream)\n pos := add(pos, 4)\n res := mload(pos)\n mstore(stream, pos)\n }\n }\n\n function readUint(uint256 stream) internal pure returns (uint256 res) {\n assembly {\n let pos := mload(stream)\n pos := add(pos, 32)\n res := mload(pos)\n mstore(stream, pos)\n }\n }\n\n function readAddress(uint256 stream) internal pure returns (address res) {\n assembly {\n let pos := mload(stream)\n pos := add(pos, 20)\n res := mload(pos)\n mstore(stream, pos)\n }\n }\n\n function readBytes(uint256 stream) internal pure returns (bytes memory res) {\n assembly {\n let pos := mload(stream)\n res := add(pos, 32)\n let length := mload(res)\n mstore(stream, add(res, length))\n }\n }\n}\n"},"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../extensions/draft-IERC20Permit.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n function safePermit(\n IERC20Permit token,\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal {\n uint256 nonceBefore = token.nonces(owner);\n token.permit(owner, spender, value, deadline, v, r, s);\n uint256 nonceAfter = token.nonces(owner);\n require(nonceAfter == nonceBefore + 1, \"SafeERC20: permit did not succeed\");\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n"},"interfaces/IBentoBoxMinimal.sol":{"content":"// SPDX-License-Identifier: UNLICENSED\n\npragma solidity >=0.8.0;\n\nstruct Rebase {\n uint128 elastic;\n uint128 base;\n}\n\nstruct StrategyData {\n uint64 strategyStartDate;\n uint64 targetPercentage;\n uint128 balance; // the balance of the strategy that BentoBox thinks is in there\n}\n\n/// @notice A rebasing library\nlibrary RebaseLibrary {\n /// @notice Calculates the base value in relationship to `elastic` and `total`.\n function toBase(Rebase memory total, uint256 elastic) internal pure returns (uint256 base) {\n if (total.elastic == 0) {\n base = elastic;\n } else {\n base = (elastic * total.base) / total.elastic;\n }\n }\n\n /// @notice Calculates the elastic value in relationship to `base` and `total`.\n function toElastic(Rebase memory total, uint256 base) internal pure returns (uint256 elastic) {\n if (total.base == 0) {\n elastic = base;\n } else {\n elastic = (base * total.elastic) / total.base;\n }\n }\n}\n\n/// @notice Minimal BentoBox vault interface.\n/// @dev `token` is aliased as `address` from `IERC20` for simplicity.\ninterface IBentoBoxMinimal {\n /// @notice Balance per ERC-20 token per account in shares.\n function balanceOf(address, address) external view returns (uint256);\n\n /// @dev Helper function to represent an `amount` of `token` in shares.\n /// @param token The ERC-20 token.\n /// @param amount The `token` amount.\n /// @param roundUp If the result `share` should be rounded up.\n /// @return share The token amount represented in shares.\n function toShare(\n address token,\n uint256 amount,\n bool roundUp\n ) external view returns (uint256 share);\n\n /// @dev Helper function to represent shares back into the `token` amount.\n /// @param token The ERC-20 token.\n /// @param share The amount of shares.\n /// @param roundUp If the result should be rounded up.\n /// @return amount The share amount back into native representation.\n function toAmount(\n address token,\n uint256 share,\n bool roundUp\n ) external view returns (uint256 amount);\n\n /// @notice Registers this contract so that users can approve it for BentoBox.\n function registerProtocol() external;\n\n /// @notice Deposit an amount of `token` represented in either `amount` or `share`.\n /// @param token The ERC-20 token to deposit.\n /// @param from which account to pull the tokens.\n /// @param to which account to push the tokens.\n /// @param amount Token amount in native representation to deposit.\n /// @param share Token amount represented in shares to deposit. Takes precedence over `amount`.\n /// @return amountOut The amount deposited.\n /// @return shareOut The deposited amount represented in shares.\n function deposit(\n address token,\n address from,\n address to,\n uint256 amount,\n uint256 share\n ) external payable returns (uint256 amountOut, uint256 shareOut);\n\n /// @notice Withdraws an amount of `token` from a user account.\n /// @param token_ The ERC-20 token to withdraw.\n /// @param from which user to pull the tokens.\n /// @param to which user to push the tokens.\n /// @param amount of tokens. Either one of `amount` or `share` needs to be supplied.\n /// @param share Like above, but `share` takes precedence over `amount`.\n function withdraw(\n address token_,\n address from,\n address to,\n uint256 amount,\n uint256 share\n ) external returns (uint256 amountOut, uint256 shareOut);\n\n /// @notice Transfer shares from a user account to another one.\n /// @param token The ERC-20 token to transfer.\n /// @param from which user to pull the tokens.\n /// @param to which user to push the tokens.\n /// @param share The amount of `token` in shares.\n function transfer(\n address token,\n address from,\n address to,\n uint256 share\n ) external;\n\n /// @dev Reads the Rebase `totals`from storage for a given token\n function totals(address token) external view returns (Rebase memory total);\n\n function strategyData(address token) external view returns (StrategyData memory total);\n\n /// @dev Approves users' BentoBox assets to a \"master\" contract.\n function setMasterContractApproval(\n address user,\n address masterContract,\n bool approved,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n function harvest(\n address token,\n bool balance,\n uint256 maxChangeAmount\n ) external;\n}\n"},"@openzeppelin/contracts/utils/Address.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\n * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\n *\n * _Available since v4.8._\n */\n function verifyCallResultFromTarget(\n address target,\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n if (success) {\n if (returndata.length == 0) {\n // only check isContract if the call was successful and the return data is empty\n // otherwise we already know that it was a contract\n require(isContract(target), \"Address: call to non-contract\");\n }\n return returndata;\n } else {\n _revert(returndata, errorMessage);\n }\n }\n\n /**\n * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason or using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n _revert(returndata, errorMessage);\n }\n }\n\n function _revert(bytes memory returndata, string memory errorMessage) private pure {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n /// @solidity memory-safe-assembly\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n}\n"},"interfaces/IPool.sol":{"content":"// SPDX-License-Identifier: GPL-3.0-or-later\n\npragma solidity >=0.5.0;\npragma experimental ABIEncoderV2;\n\n/// @notice Trident pool interface.\ninterface IPool {\n /// @notice Executes a swap from one token to another.\n /// @dev The input tokens must've already been sent to the pool.\n /// @param data ABI-encoded params that the pool requires.\n /// @return finalAmountOut The amount of output tokens that were sent to the user.\n function swap(bytes calldata data) external returns (uint256 finalAmountOut);\n\n /// @notice Executes a swap from one token to another with a callback.\n /// @dev This function allows borrowing the output tokens and sending the input tokens in the callback.\n /// @param data ABI-encoded params that the pool requires.\n /// @return finalAmountOut The amount of output tokens that were sent to the user.\n function flashSwap(bytes calldata data) external returns (uint256 finalAmountOut);\n\n /// @notice Mints liquidity tokens.\n /// @param data ABI-encoded params that the pool requires.\n /// @return liquidity The amount of liquidity tokens that were minted for the user.\n function mint(bytes calldata data) external returns (uint256 liquidity);\n\n /// @notice Burns liquidity tokens.\n /// @dev The input LP tokens must've already been sent to the pool.\n /// @param data ABI-encoded params that the pool requires.\n /// @return withdrawnAmounts The amount of various output tokens that were sent to the user.\n function burn(bytes calldata data) external returns (TokenAmount[] memory withdrawnAmounts);\n\n /// @notice Burns liquidity tokens for a single output token.\n /// @dev The input LP tokens must've already been sent to the pool.\n /// @param data ABI-encoded params that the pool requires.\n /// @return amountOut The amount of output tokens that were sent to the user.\n function burnSingle(bytes calldata data) external returns (uint256 amountOut);\n\n /// @return A unique identifier for the pool type.\n function poolIdentifier() external pure returns (bytes32);\n\n /// @return An array of tokens supported by the pool.\n function getAssets() external view returns (address[] memory);\n\n /// @notice Simulates a trade and returns the expected output.\n /// @dev The pool does not need to include a trade simulator directly in itself - it can use a library.\n /// @param data ABI-encoded params that the pool requires.\n /// @return finalAmountOut The amount of output tokens that will be sent to the user if the trade is executed.\n function getAmountOut(bytes calldata data) external view returns (uint256 finalAmountOut);\n\n /// @notice Simulates a trade and returns the expected output.\n /// @dev The pool does not need to include a trade simulator directly in itself - it can use a library.\n /// @param data ABI-encoded params that the pool requires.\n /// @return finalAmountIn The amount of input tokens that are required from the user if the trade is executed.\n function getAmountIn(bytes calldata data) external view returns (uint256 finalAmountIn);\n\n /// @dev This event must be emitted on all swaps.\n event Swap(address indexed recipient, address indexed tokenIn, address indexed tokenOut, uint256 amountIn, uint256 amountOut);\n\n /// @dev This struct frames output tokens for burns.\n struct TokenAmount {\n address token;\n uint256 amount;\n }\n}\n"},"@openzeppelin/contracts/token/ERC20/IERC20.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n}\n"},"contracts/RouteProcessor2.sol":{"content":"// SPDX-License-Identifier: UNLICENSED\n\npragma solidity 0.8.10;\n\nimport '../interfaces/IUniswapV2Pair.sol';\nimport '../interfaces/IUniswapV3Pool.sol';\nimport '../interfaces/ITridentCLPool.sol';\nimport '../interfaces/IBentoBoxMinimal.sol';\nimport '../interfaces/IPool.sol';\nimport '../interfaces/IWETH.sol';\nimport './InputStream.sol';\nimport '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';\n\naddress constant NATIVE_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\naddress constant IMPOSSIBLE_POOL_ADDRESS = 0x0000000000000000000000000000000000000001;\n\n/// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)\nuint160 constant MIN_SQRT_RATIO = 4295128739;\n/// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)\nuint160 constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;\n\n/// @title A route processor for the Sushi Aggregator\n/// @author Ilya Lyalin\ncontract RouteProcessor2 {\n using SafeERC20 for IERC20;\n using InputStream for uint256;\n\n IBentoBoxMinimal public immutable bentoBox;\n address private lastCalledPool;\n\n uint private unlocked = 1;\n modifier lock() {\n require(unlocked == 1, 'RouteProcessor is locked');\n unlocked = 2;\n _;\n unlocked = 1;\n }\n\n constructor(address _bentoBox) {\n bentoBox = IBentoBoxMinimal(_bentoBox);\n lastCalledPool = IMPOSSIBLE_POOL_ADDRESS;\n }\n\n /// @notice For native unwrapping\n receive() external payable {}\n\n /// @notice Processes the route generated off-chain. Has a lock\n /// @param tokenIn Address of the input token\n /// @param amountIn Amount of the input token\n /// @param tokenOut Address of the output token\n /// @param amountOutMin Minimum amount of the output token\n /// @return amountOut Actual amount of the output token\n function processRoute(\n address tokenIn,\n uint256 amountIn,\n address tokenOut,\n uint256 amountOutMin,\n address to,\n bytes memory route\n ) external payable lock returns (uint256 amountOut) {\n return processRouteInternal(tokenIn, amountIn, tokenOut, amountOutMin, to, route);\n }\n\n /// @notice Transfers some value to and then processes the route\n /// @param transferValueTo Address where the value should be transferred\n /// @param amountValueTransfer How much value to transfer\n /// @param tokenIn Address of the input token\n /// @param amountIn Amount of the input token\n /// @param tokenOut Address of the output token\n /// @param amountOutMin Minimum amount of the output token\n /// @return amountOut Actual amount of the output token\n function transferValueAndprocessRoute(\n address payable transferValueTo,\n uint256 amountValueTransfer,\n address tokenIn,\n uint256 amountIn,\n address tokenOut,\n uint256 amountOutMin,\n address to,\n bytes memory route\n ) external payable lock returns (uint256 amountOut) {\n (bool success, bytes memory returnBytes) = transferValueTo.call{value: amountValueTransfer}('');\n require(success, string(abi.encodePacked(returnBytes)));\n return processRouteInternal(tokenIn, amountIn, tokenOut, amountOutMin, to, route);\n }\n\n /// @notice Processes the route generated off-chain\n /// @param tokenIn Address of the input token\n /// @param amountIn Amount of the input token\n /// @param tokenOut Address of the output token\n /// @param amountOutMin Minimum amount of the output token\n /// @return amountOut Actual amount of the output token\n function processRouteInternal(\n address tokenIn,\n uint256 amountIn,\n address tokenOut,\n uint256 amountOutMin,\n address to,\n bytes memory route\n ) private returns (uint256 amountOut) {\n uint256 balanceInInitial = tokenIn == NATIVE_ADDRESS ? address(this).balance : IERC20(tokenIn).balanceOf(msg.sender);\n uint256 balanceOutInitial = tokenOut == NATIVE_ADDRESS ? address(to).balance : IERC20(tokenOut).balanceOf(to);\n\n uint256 stream = InputStream.createStream(route);\n while (stream.isNotEmpty()) {\n uint8 commandCode = stream.readUint8();\n if (commandCode == 1) processMyERC20(stream);\n else if (commandCode == 2) processUserERC20(stream, amountIn);\n else if (commandCode == 3) processNative(stream);\n else if (commandCode == 4) processOnePool(stream);\n else if (commandCode == 5) processInsideBento(stream);\n else revert('RouteProcessor: Unknown command code');\n }\n\n uint256 balanceInFinal = tokenIn == NATIVE_ADDRESS ? address(this).balance : IERC20(tokenIn).balanceOf(msg.sender);\n require(balanceInFinal + amountIn >= balanceInInitial, 'RouteProcessor: Minimal imput balance violation');\n\n uint256 balanceOutFinal = tokenOut == NATIVE_ADDRESS ? address(to).balance : IERC20(tokenOut).balanceOf(to);\n require(balanceOutFinal >= balanceOutInitial + amountOutMin, 'RouteProcessor: Minimal ouput balance violation');\n\n amountOut = balanceOutFinal - balanceOutInitial;\n }\n\n /// @notice Processes native coin: call swap for all pools that swap from native coin\n /// @param stream Streamed process program\n function processNative(uint256 stream) private {\n uint256 amountTotal = address(this).balance;\n distributeAndSwap(stream, address(this), NATIVE_ADDRESS, amountTotal);\n }\n\n /// @notice Processes ERC20 token from this contract balance:\n /// @notice Call swap for all pools that swap from this token\n /// @param stream Streamed process program\n function processMyERC20(uint256 stream) private {\n address token = stream.readAddress();\n uint256 amountTotal = IERC20(token).balanceOf(address(this));\n unchecked {\n if (amountTotal > 0) amountTotal -= 1; // slot undrain protection\n }\n distributeAndSwap(stream, address(this), token, amountTotal);\n }\n \n /// @notice Processes ERC20 token from msg.sender balance:\n /// @notice Call swap for all pools that swap from this token\n /// @param stream Streamed process program\n /// @param amountTotal Amount of tokens to take from msg.sender\n function processUserERC20(uint256 stream, uint256 amountTotal) private {\n address token = stream.readAddress();\n distributeAndSwap(stream, msg.sender, token, amountTotal);\n }\n\n /// @notice Distributes amountTotal to several pools according to their shares and calls swap for each pool\n /// @param stream Streamed process program\n /// @param from Where to take liquidity for swap\n /// @param tokenIn Input token\n /// @param amountTotal Total amount of tokenIn for swaps \n function distributeAndSwap(uint256 stream, address from, address tokenIn, uint256 amountTotal) private {\n uint8 num = stream.readUint8();\n unchecked {\n for (uint256 i = 0; i < num; ++i) {\n uint16 share = stream.readUint16();\n uint256 amount = (amountTotal * share) / 65535;\n amountTotal -= amount;\n swap(stream, from, tokenIn, amount);\n }\n }\n }\n\n /// @notice Processes ERC20 token for cases when the token has only one output pool\n /// @notice In this case liquidity is already at pool balance. This is an optimization\n /// @notice Call swap for all pools that swap from this token\n /// @param stream Streamed process program\n function processOnePool(uint256 stream) private {\n address token = stream.readAddress();\n swap(stream, address(this), token, 0);\n }\n\n /// @notice Processes Bento tokens \n /// @notice Call swap for all pools that swap from this token\n /// @param stream Streamed process program\n function processInsideBento(uint256 stream) private {\n address token = stream.readAddress();\n uint8 num = stream.readUint8();\n\n uint256 amountTotal = bentoBox.balanceOf(token, address(this));\n unchecked {\n if (amountTotal > 0) amountTotal -= 1; // slot undrain protection\n for (uint256 i = 0; i < num; ++i) {\n uint16 share = stream.readUint16();\n uint256 amount = (amountTotal * share) / 65535;\n amountTotal -= amount;\n swap(stream, address(this), token, amount);\n }\n }\n }\n\n /// @notice Makes swap\n /// @param stream Streamed process program\n /// @param from Where to take liquidity for swap\n /// @param tokenIn Input token\n /// @param amountIn Amount of tokenIn to take for swap\n function swap(uint256 stream, address from, address tokenIn, uint256 amountIn) private {\n uint8 poolType = stream.readUint8();\n if (poolType == 0) swapUniV2(stream, from, tokenIn, amountIn);\n else if (poolType == 1) swapUniV3(stream, from, tokenIn, amountIn);\n else if (poolType == 2) wrapNative(stream, from, tokenIn, amountIn);\n else if (poolType == 3) bentoBridge(stream, from, tokenIn, amountIn);\n else if (poolType == 4) swapTrident(stream, from, tokenIn, amountIn);\n else if (poolType == 5) swapTridentCL(stream, from, tokenIn, amountIn);\n else revert('RouteProcessor: Unknown pool type');\n }\n\n /// @notice Wraps/unwraps native token\n /// @param stream [direction & fake, recipient, wrapToken?]\n /// @param from Where to take liquidity for swap\n /// @param tokenIn Input token\n /// @param amountIn Amount of tokenIn to take for swap\n function wrapNative(uint256 stream, address from, address tokenIn, uint256 amountIn) private {\n uint8 directionAndFake = stream.readUint8();\n address to = stream.readAddress();\n\n if (directionAndFake & 1 == 1) { // wrap native\n address wrapToken = stream.readAddress();\n if (directionAndFake & 2 == 0) IWETH(wrapToken).deposit{value: amountIn}();\n if (to != address(this)) IERC20(wrapToken).safeTransfer(to, amountIn);\n } else { // unwrap native\n if (directionAndFake & 2 == 0) {\n if (from != address(this)) IERC20(tokenIn).safeTransferFrom(from, address(this), amountIn);\n IWETH(tokenIn).withdraw(amountIn);\n }\n payable(to).transfer(address(this).balance);\n }\n }\n\n /// @notice Bridge/unbridge tokens to/from Bento\n /// @param stream [direction, recipient]\n /// @param from Where to take liquidity for swap\n /// @param tokenIn Input token\n /// @param amountIn Amount of tokenIn to take for swap\n function bentoBridge(uint256 stream, address from, address tokenIn, uint256 amountIn) private {\n uint8 direction = stream.readUint8();\n address to = stream.readAddress();\n\n if (direction > 0) { // outside to Bento\n // deposit to arbitrary recipient is possible only from address(bentoBox)\n if (amountIn != 0) {\n if (from == address(this)) IERC20(tokenIn).safeTransfer(address(bentoBox), amountIn);\n else IERC20(tokenIn).safeTransferFrom(from, address(bentoBox), amountIn);\n } else {\n // tokens already are at address(bentoBox)\n amountIn = IERC20(tokenIn).balanceOf(address(bentoBox)) +\n bentoBox.strategyData(tokenIn).balance -\n bentoBox.totals(tokenIn).elastic;\n }\n bentoBox.deposit(tokenIn, address(bentoBox), to, amountIn, 0);\n } else { // Bento to outside\n if (amountIn > 0) {\n bentoBox.transfer(tokenIn, from, address(this), amountIn);\n } else amountIn = bentoBox.balanceOf(tokenIn, address(this));\n bentoBox.withdraw(tokenIn, address(this), to, 0, amountIn);\n }\n }\n\n /// @notice UniswapV2 pool swap\n /// @param stream [pool, direction, recipient]\n /// @param from Where to take liquidity for swap\n /// @param tokenIn Input token\n /// @param amountIn Amount of tokenIn to take for swap\n function swapUniV2(uint256 stream, address from, address tokenIn, uint256 amountIn) private {\n address pool = stream.readAddress();\n uint8 direction = stream.readUint8();\n address to = stream.readAddress();\n\n (uint256 r0, uint256 r1, ) = IUniswapV2Pair(pool).getReserves();\n require(r0 > 0 && r1 > 0, 'Wrong pool reserves');\n (uint256 reserveIn, uint256 reserveOut) = direction == 1 ? (r0, r1) : (r1, r0);\n\n if (amountIn != 0) {\n if (from == address(this)) IERC20(tokenIn).safeTransfer(pool, amountIn);\n else IERC20(tokenIn).safeTransferFrom(from, pool, amountIn);\n } else amountIn = IERC20(tokenIn).balanceOf(pool) - reserveIn; // tokens already were transferred\n\n uint256 amountInWithFee = amountIn * 997;\n uint256 amountOut = (amountInWithFee * reserveOut) / (reserveIn * 1000 + amountInWithFee);\n (uint256 amount0Out, uint256 amount1Out) = direction == 1 ? (uint256(0), amountOut) : (amountOut, uint256(0));\n IUniswapV2Pair(pool).swap(amount0Out, amount1Out, to, new bytes(0));\n }\n\n /// @notice Trident pool swap\n /// @param stream [pool, swapData]\n /// @param from Where to take liquidity for swap\n /// @param tokenIn Input token\n /// @param amountIn Amount of tokenIn to take for swap\n function swapTrident(uint256 stream, address from, address tokenIn, uint256 amountIn) private {\n address pool = stream.readAddress();\n bytes memory swapData = stream.readBytes();\n\n if (amountIn != 0) {\n bentoBox.transfer(tokenIn, from, pool, amountIn);\n }\n \n IPool(pool).swap(swapData);\n }\n\n /// @notice UniswapV3 pool swap\n /// @param stream [pool, direction, recipient]\n /// @param from Where to take liquidity for swap\n /// @param tokenIn Input token\n /// @param amountIn Amount of tokenIn to take for swap\n function swapUniV3(uint256 stream, address from, address tokenIn, uint256 amountIn) private {\n address pool = stream.readAddress();\n bool zeroForOne = stream.readUint8() > 0;\n address recipient = stream.readAddress();\n\n lastCalledPool = pool;\n IUniswapV3Pool(pool).swap(\n recipient,\n zeroForOne,\n int256(amountIn),\n zeroForOne ? MIN_SQRT_RATIO + 1 : MAX_SQRT_RATIO - 1,\n abi.encode(tokenIn, from)\n );\n require(lastCalledPool == IMPOSSIBLE_POOL_ADDRESS, 'RouteProcessor.swapUniV3: unexpected'); // Just to be sure\n }\n\n /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.\n /// @dev In the implementation you must pay the pool tokens owed for the swap.\n /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.\n /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.\n /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by\n /// the end of the swap. If positive, the callback must send that amount of token0 to the pool.\n /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by\n /// the end of the swap. If positive, the callback must send that amount of token1 to the pool.\n /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call\n function uniswapV3SwapCallback(\n int256 amount0Delta,\n int256 amount1Delta,\n bytes calldata data\n ) external {\n require(msg.sender == lastCalledPool, 'RouteProcessor.uniswapV3SwapCallback: call from unknown source');\n lastCalledPool = IMPOSSIBLE_POOL_ADDRESS;\n (address tokenIn, address from) = abi.decode(data, (address, address));\n int256 amount = amount0Delta > 0 ? amount0Delta : amount1Delta;\n require(amount > 0, 'RouteProcessor.uniswapV3SwapCallback: not positive amount');\n\n if (from == address(this)) IERC20(tokenIn).safeTransfer(msg.sender, uint256(amount));\n else IERC20(tokenIn).safeTransferFrom(from, msg.sender, uint256(amount));\n }\n\n /// @notice TridentCL pool swap\n /// @param stream [pool, direction, recipient]\n /// @param from Where to take liquidity for swap\n /// @param tokenIn Input token\n /// @param amountIn Amount of tokenIn to take for swap\n function swapTridentCL(uint256 stream, address from, address tokenIn, uint256 amountIn) private {\n address pool = stream.readAddress();\n bool zeroForOne = stream.readUint8() > 0;\n address recipient = stream.readAddress();\n\n lastCalledPool = pool;\n ITridentCLPool(pool).swap(\n recipient,\n zeroForOne,\n int256(amountIn),\n zeroForOne ? MIN_SQRT_RATIO + 1 : MAX_SQRT_RATIO - 1,\n false,\n abi.encode(tokenIn, from)\n );\n require(lastCalledPool == IMPOSSIBLE_POOL_ADDRESS, 'RouteProcessor.swapTridentCL: unexpected'); // Just to be sure\n }\n\n /// @notice Called to `msg.sender` after executing a swap via ITridentCLPool#swap.\n /// @dev In the implementation you must pay the pool tokens owed for the swap.\n /// The caller of this method must be checked to be a TridentCLPool deployed by the canonical TridentCLFactory.\n /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.\n /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by\n /// the end of the swap. If positive, the callback must send that amount of token0 to the pool.\n /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by\n /// the end of the swap. If positive, the callback must send that amount of token1 to the pool.\n /// @param data Any data passed through by the caller via the ITridentCLPoolActions#swap call\n function tridentCLSwapCallback(\n int256 amount0Delta,\n int256 amount1Delta,\n bytes calldata data\n ) external {\n require(msg.sender == lastCalledPool, 'RouteProcessor.TridentCLSwapCallback: call from unknown source');\n lastCalledPool = IMPOSSIBLE_POOL_ADDRESS;\n (address tokenIn, address from) = abi.decode(data, (address, address));\n int256 amount = amount0Delta > 0 ? amount0Delta : amount1Delta;\n require(amount > 0, 'RouteProcessor.TridentCLSwapCallback: not positive amount');\n\n if (from == address(this)) IERC20(tokenIn).safeTransfer(msg.sender, uint256(amount));\n else IERC20(tokenIn).safeTransferFrom(from, msg.sender, uint256(amount));\n }\n}\n"},"interfaces/ITridentCLPool.sol":{"content":"// SPDX-License-Identifier: GPL-3.0-or-later\n\npragma solidity 0.8.10;\n\ninterface ITridentCLPool {\n function token0() external returns (address);\n function token1() external returns (address);\n\n function swap(\n address recipient,\n bool zeroForOne,\n int256 amountSpecified,\n uint160 sqrtPriceLimitX96,\n bool unwrapBento,\n bytes calldata data\n ) external returns (int256 amount0, int256 amount1);\n}\n"},"@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n */\ninterface IERC20Permit {\n /**\n * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\n * given ``owner``'s signed approval.\n *\n * IMPORTANT: The same issues {IERC20-approve} has related to transaction\n * ordering also apply here.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `deadline` must be a timestamp in the future.\n * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\n * over the EIP712-formatted function arguments.\n * - the signature must use ``owner``'s current nonce (see {nonces}).\n *\n * For more information on the signature format, see the\n * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\n * section].\n */\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n /**\n * @dev Returns the current nonce for `owner`. This value must be\n * included whenever a signature is generated for {permit}.\n *\n * Every successful call to {permit} increases ``owner``'s nonce by one. This\n * prevents a signature from being used multiple times.\n */\n function nonces(address owner) external view returns (uint256);\n\n /**\n * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\n */\n // solhint-disable-next-line func-name-mixedcase\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n"},"interfaces/IUniswapV2Pair.sol":{"content":"// SPDX-License-Identifier: GPL-3.0\n\npragma solidity >=0.5.0;\n\ninterface IUniswapV2Pair {\n event Approval(address indexed owner, address indexed spender, uint value);\n event Transfer(address indexed from, address indexed to, uint value);\n\n function name() external pure returns (string memory);\n function symbol() external pure returns (string memory);\n function decimals() external pure returns (uint8);\n function totalSupply() external view returns (uint);\n function balanceOf(address owner) external view returns (uint);\n function allowance(address owner, address spender) external view returns (uint);\n\n function approve(address spender, uint value) external returns (bool);\n function transfer(address to, uint value) external returns (bool);\n function transferFrom(address from, address to, uint value) external returns (bool);\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n function PERMIT_TYPEHASH() external pure returns (bytes32);\n function nonces(address owner) external view returns (uint);\n\n function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;\n\n event Mint(address indexed sender, uint amount0, uint amount1);\n event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);\n event Swap(\n address indexed sender,\n uint amount0In,\n uint amount1In,\n uint amount0Out,\n uint amount1Out,\n address indexed to\n );\n event Sync(uint112 reserve0, uint112 reserve1);\n\n function MINIMUM_LIQUIDITY() external pure returns (uint);\n function factory() external view returns (address);\n function token0() external view returns (address);\n function token1() external view returns (address);\n function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);\n function price0CumulativeLast() external view returns (uint);\n function price1CumulativeLast() external view returns (uint);\n function kLast() external view returns (uint);\n\n function mint(address to) external returns (uint liquidity);\n function burn(address to) external returns (uint amount0, uint amount1);\n function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;\n function skim(address to) external;\n function sync() external;\n\n function initialize(address, address) external;\n}"},"interfaces/IUniswapV3Pool.sol":{"content":"// SPDX-License-Identifier: GPL-3.0-or-later\n\npragma solidity 0.8.10;\n\ninterface IUniswapV3Pool {\n function token0() external returns (address);\n function token1() external returns (address);\n\n function swap(\n address recipient,\n bool zeroForOne,\n int256 amountSpecified,\n uint160 sqrtPriceLimitX96,\n bytes calldata data\n ) external returns (int256 amount0, int256 amount1);\n}\n"},"interfaces/IWETH.sol":{"content":"// SPDX-License-Identifier: GPL-3.0-or-later\n\npragma solidity 0.8.10;\n\ninterface IWETH {\n function deposit() external payable;\n\n function transfer(address to, uint256 value) external returns (bool);\n\n function withdraw(uint256) external;\n}\n"}},"settings":{"optimizer":{"enabled":true,"runs":10000000},"outputSelection":{"*":{"*":["evm.bytecode","evm.deployedBytecode","devdoc","userdoc","metadata","abi"]}},"metadata":{"useLiteralContent":true},"libraries":{}}},"ABI":"[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_bentoBox\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"bentoBox\",\"outputs\":[{\"internalType\":\"contract IBentoBoxMinimal\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"tokenIn\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amountIn\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"tokenOut\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amountOutMin\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"route\",\"type\":\"bytes\"}],\"name\":\"processRoute\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"amountOut\",\"type\":\"uint256\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address payable\",\"name\":\"transferValueTo\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amountValueTransfer\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"tokenIn\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amountIn\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"tokenOut\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amountOutMin\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"route\",\"type\":\"bytes\"}],\"name\":\"transferValueAndprocessRoute\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"amountOut\",\"type\":\"uint256\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"amount0Delta\",\"type\":\"int256\"},{\"internalType\":\"int256\",\"name\":\"amount1Delta\",\"type\":\"int256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"tridentCLSwapCallback\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"amount0Delta\",\"type\":\"int256\"},{\"internalType\":\"int256\",\"name\":\"amount1Delta\",\"type\":\"int256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"uniswapV3SwapCallback\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]","ContractName":"RouteProcessor2","CompilerVersion":"v0.8.10+commit.fc410830","OptimizationUsed":1,"Runs":10000000,"ConstructorArguments":"0x000000000000000000000000f5bce5077908a1b7370b9ae04adc565ebd643966","EVMVersion":"Default","Library":"","LicenseType":"","Proxy":0,"SwarmSource":""}] \ No newline at end of file diff --git a/testdata/etherscan/0x35Fb958109b70799a8f9Bc2a8b1Ee4cC62034193/creation_data.json b/testdata/etherscan/0x35Fb958109b70799a8f9Bc2a8b1Ee4cC62034193/creation_data.json index 2c3135353..e3433ef20 100644 --- a/testdata/etherscan/0x35Fb958109b70799a8f9Bc2a8b1Ee4cC62034193/creation_data.json +++ b/testdata/etherscan/0x35Fb958109b70799a8f9Bc2a8b1Ee4cC62034193/creation_data.json @@ -1,5 +1 @@ -{ - "contractAddress": "0x35fb958109b70799a8f9bc2a8b1ee4cc62034193", - "contractCreator": "0x3e32324277e96b69750bc6f7c4ba27e122413e07", - "txHash": "0x41e3517f8262b55e1eb1707ba0760b603a70e89ea4a86eff56072fcc80c3d0a1" -} \ No newline at end of file +{"contractAddress":"0x35fb958109b70799a8f9bc2a8b1ee4cc62034193","contractCreator":"0x3e32324277e96b69750bc6f7c4ba27e122413e07","txHash":"0x41e3517f8262b55e1eb1707ba0760b603a70e89ea4a86eff56072fcc80c3d0a1"} \ No newline at end of file diff --git a/testdata/etherscan/0x35Fb958109b70799a8f9Bc2a8b1Ee4cC62034193/metadata.json b/testdata/etherscan/0x35Fb958109b70799a8f9Bc2a8b1Ee4cC62034193/metadata.json index 7601e4d6e..bd48a2efb 100644 --- a/testdata/etherscan/0x35Fb958109b70799a8f9Bc2a8b1Ee4cC62034193/metadata.json +++ b/testdata/etherscan/0x35Fb958109b70799a8f9Bc2a8b1Ee4cC62034193/metadata.json @@ -1,16 +1 @@ -[ - { - "SourceCode": "/**\r\n *Submitted for verification at Etherscan.io on 2022-02-19\r\n*/\r\n\r\n/**\r\n *Submitted for verification at Etherscan.io on 2022-02-18\r\n*/\r\n\r\n/**\r\n *Submitted for verification at Etherscan.io on 2022-02-14\r\n*/\r\n\r\n/**\r\n *Submitted for verification at Etherscan.io on 2022-02-10\r\n*/\r\n\r\n/**\r\n *Submitted for verification at Etherscan.io on 2022-02-09\r\n*/\r\n\r\n/**\r\n *Submitted for verification at Etherscan.io on 2022-02-08\r\n*/\r\n\r\n/**\r\n *Submitted for verification at Etherscan.io on 2022-02-05\r\n*/\r\n\r\n/**\r\n *Submitted for verification at Etherscan.io on 2022-01-22\r\n*/\r\n\r\n// SPDX-License-Identifier: UNLICENSED\r\n/*\r\nmade by cty0312\r\n2022.01.22\r\n**/\r\n\r\npragma solidity >=0.7.0 <0.9.0;\r\n\r\nlibrary StringsUpgradeable {\r\n bytes16 private constant _HEX_SYMBOLS = \"0123456789abcdef\";\r\n\r\n /**\r\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\r\n */\r\n function toString(uint256 value) internal pure returns (string memory) {\r\n // Inspired by OraclizeAPI's implementation - MIT licence\r\n // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol\r\n\r\n if (value == 0) {\r\n return \"0\";\r\n }\r\n uint256 temp = value;\r\n uint256 digits;\r\n while (temp != 0) {\r\n digits++;\r\n temp /= 10;\r\n }\r\n bytes memory buffer = new bytes(digits);\r\n while (value != 0) {\r\n digits -= 1;\r\n buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));\r\n value /= 10;\r\n }\r\n return string(buffer);\r\n }\r\n\r\n /**\r\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\r\n */\r\n function toHexString(uint256 value) internal pure returns (string memory) {\r\n if (value == 0) {\r\n return \"0x00\";\r\n }\r\n uint256 temp = value;\r\n uint256 length = 0;\r\n while (temp != 0) {\r\n length++;\r\n temp >>= 8;\r\n }\r\n return toHexString(value, length);\r\n }\r\n\r\n /**\r\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\r\n */\r\n function toHexString(uint256 value, uint256 length)\r\n internal\r\n pure\r\n returns (string memory)\r\n {\r\n bytes memory buffer = new bytes(2 * length + 2);\r\n buffer[0] = \"0\";\r\n buffer[1] = \"x\";\r\n for (uint256 i = 2 * length + 1; i > 1; --i) {\r\n buffer[i] = _HEX_SYMBOLS[value & 0xf];\r\n value >>= 4;\r\n }\r\n require(value == 0, \"Strings: hex length insufficient\");\r\n return string(buffer);\r\n }\r\n}\r\n\r\nlibrary AddressUpgradeable {\r\n /**\r\n * @dev Returns true if `account` is a contract.\r\n *\r\n * [IMPORTANT]\r\n * ====\r\n * It is unsafe to assume that an address for which this function returns\r\n * false is an externally-owned account (EOA) and not a contract.\r\n *\r\n * Among others, `isContract` will return false for the following\r\n * types of addresses:\r\n *\r\n * - an externally-owned account\r\n * - a contract in construction\r\n * - an address where a contract will be created\r\n * - an address where a contract lived, but was destroyed\r\n * ====\r\n */\r\n function isContract(address account) internal view returns (bool) {\r\n // This method relies on extcodesize, which returns 0 for contracts in\r\n // construction, since the code is only stored at the end of the\r\n // constructor execution.\r\n\r\n uint256 size;\r\n assembly {\r\n size := extcodesize(account)\r\n }\r\n return size > 0;\r\n }\r\n\r\n /**\r\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\r\n * `recipient`, forwarding all available gas and reverting on errors.\r\n *\r\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\r\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\r\n * imposed by `transfer`, making them unable to receive funds via\r\n * `transfer`. {sendValue} removes this limitation.\r\n *\r\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\r\n *\r\n * IMPORTANT: because control is transferred to `recipient`, care must be\r\n * taken to not create reentrancy vulnerabilities. Consider using\r\n * {ReentrancyGuard} or the\r\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\r\n */\r\n function sendValue(address payable recipient, uint256 amount) internal {\r\n require(\r\n address(this).balance >= amount,\r\n \"Address: insufficient balance\"\r\n );\r\n\r\n (bool success, ) = recipient.call{value: amount}(\"\");\r\n require(\r\n success,\r\n \"Address: unable to send value, recipient may have reverted\"\r\n );\r\n }\r\n\r\n /**\r\n * @dev Performs a Solidity function call using a low level `call`. A\r\n * plain `call` is an unsafe replacement for a function call: use this\r\n * function instead.\r\n *\r\n * If `target` reverts with a revert reason, it is bubbled up by this\r\n * function (like regular Solidity function calls).\r\n *\r\n * Returns the raw returned data. To convert to the expected return value,\r\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\r\n *\r\n * Requirements:\r\n *\r\n * - `target` must be a contract.\r\n * - calling `target` with `data` must not revert.\r\n *\r\n * _Available since v3.1._\r\n */\r\n function functionCall(address target, bytes memory data)\r\n internal\r\n returns (bytes memory)\r\n {\r\n return functionCall(target, data, \"Address: low-level call failed\");\r\n }\r\n\r\n /**\r\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\r\n * `errorMessage` as a fallback revert reason when `target` reverts.\r\n *\r\n * _Available since v3.1._\r\n */\r\n function functionCall(\r\n address target,\r\n bytes memory data,\r\n string memory errorMessage\r\n ) internal returns (bytes memory) {\r\n return functionCallWithValue(target, data, 0, errorMessage);\r\n }\r\n\r\n /**\r\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\r\n * but also transferring `value` wei to `target`.\r\n *\r\n * Requirements:\r\n *\r\n * - the calling contract must have an ETH balance of at least `value`.\r\n * - the called Solidity function must be `payable`.\r\n *\r\n * _Available since v3.1._\r\n */\r\n function functionCallWithValue(\r\n address target,\r\n bytes memory data,\r\n uint256 value\r\n ) internal returns (bytes memory) {\r\n return\r\n functionCallWithValue(\r\n target,\r\n data,\r\n value,\r\n \"Address: low-level call with value failed\"\r\n );\r\n }\r\n\r\n /**\r\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\r\n * with `errorMessage` as a fallback revert reason when `target` reverts.\r\n *\r\n * _Available since v3.1._\r\n */\r\n function functionCallWithValue(\r\n address target,\r\n bytes memory data,\r\n uint256 value,\r\n string memory errorMessage\r\n ) internal returns (bytes memory) {\r\n require(\r\n address(this).balance >= value,\r\n \"Address: insufficient balance for call\"\r\n );\r\n require(isContract(target), \"Address: call to non-contract\");\r\n\r\n (bool success, bytes memory returndata) = target.call{value: value}(\r\n data\r\n );\r\n return verifyCallResult(success, returndata, errorMessage);\r\n }\r\n\r\n /**\r\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\r\n * but performing a static call.\r\n *\r\n * _Available since v3.3._\r\n */\r\n function functionStaticCall(address target, bytes memory data)\r\n internal\r\n view\r\n returns (bytes memory)\r\n {\r\n return\r\n functionStaticCall(\r\n target,\r\n data,\r\n \"Address: low-level static call failed\"\r\n );\r\n }\r\n\r\n /**\r\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\r\n * but performing a static call.\r\n *\r\n * _Available since v3.3._\r\n */\r\n function functionStaticCall(\r\n address target,\r\n bytes memory data,\r\n string memory errorMessage\r\n ) internal view returns (bytes memory) {\r\n require(isContract(target), \"Address: static call to non-contract\");\r\n\r\n (bool success, bytes memory returndata) = target.staticcall(data);\r\n return verifyCallResult(success, returndata, errorMessage);\r\n }\r\n\r\n /**\r\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\r\n * revert reason using the provided one.\r\n *\r\n * _Available since v4.3._\r\n */\r\n function verifyCallResult(\r\n bool success,\r\n bytes memory returndata,\r\n string memory errorMessage\r\n ) internal pure returns (bytes memory) {\r\n if (success) {\r\n return returndata;\r\n } else {\r\n // Look for revert reason and bubble it up if present\r\n if (returndata.length > 0) {\r\n // The easiest way to bubble the revert reason is using memory via assembly\r\n\r\n assembly {\r\n let returndata_size := mload(returndata)\r\n revert(add(32, returndata), returndata_size)\r\n }\r\n } else {\r\n revert(errorMessage);\r\n }\r\n }\r\n }\r\n}\r\n\r\nlibrary SafeMathUpgradeable {\r\n /**\r\n * @dev Returns the addition of two unsigned integers, reverting on\r\n * overflow.\r\n *\r\n * Counterpart to Solidity's `+` operator.\r\n *\r\n * Requirements:\r\n *\r\n * - Addition cannot overflow.\r\n */\r\n function add(uint256 a, uint256 b) internal pure returns (uint256) {\r\n uint256 c = a + b;\r\n require(c >= a, \"SafeMath: addition overflow\");\r\n\r\n return c;\r\n }\r\n\r\n /**\r\n * @dev Returns the subtraction of two unsigned integers, reverting on\r\n * overflow (when the result is negative).\r\n *\r\n * Counterpart to Solidity's `-` operator.\r\n *\r\n * Requirements:\r\n *\r\n * - Subtraction cannot overflow.\r\n */\r\n function sub(uint256 a, uint256 b) internal pure returns (uint256) {\r\n return sub(a, b, \"SafeMath: subtraction overflow\");\r\n }\r\n\r\n /**\r\n * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\r\n * overflow (when the result is negative).\r\n *\r\n * Counterpart to Solidity's `-` operator.\r\n *\r\n * Requirements:\r\n *\r\n * - Subtraction cannot overflow.\r\n */\r\n function sub(\r\n uint256 a,\r\n uint256 b,\r\n string memory errorMessage\r\n ) internal pure returns (uint256) {\r\n require(b <= a, errorMessage);\r\n uint256 c = a - b;\r\n\r\n return c;\r\n }\r\n\r\n /**\r\n * @dev Returns the multiplication of two unsigned integers, reverting on\r\n * overflow.\r\n *\r\n * Counterpart to Solidity's `*` operator.\r\n *\r\n * Requirements:\r\n *\r\n * - Multiplication cannot overflow.\r\n */\r\n function mul(uint256 a, uint256 b) internal pure returns (uint256) {\r\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\r\n // benefit is lost if 'b' is also tested.\r\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\r\n if (a == 0) {\r\n return 0;\r\n }\r\n\r\n uint256 c = a * b;\r\n require(c / a == b, \"SafeMath: multiplication overflow\");\r\n\r\n return c;\r\n }\r\n\r\n /**\r\n * @dev Returns the integer division of two unsigned integers. Reverts on\r\n * division by zero. The result is rounded towards zero.\r\n *\r\n * Counterpart to Solidity's `/` operator. Note: this function uses a\r\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\r\n * uses an invalid opcode to revert (consuming all remaining gas).\r\n *\r\n * Requirements:\r\n *\r\n * - The divisor cannot be zero.\r\n */\r\n function div(uint256 a, uint256 b) internal pure returns (uint256) {\r\n return div(a, b, \"SafeMath: division by zero\");\r\n }\r\n\r\n /**\r\n * @dev Returns the integer division of two unsigned integers. Reverts with custom message on\r\n * division by zero. The result is rounded towards zero.\r\n *\r\n * Counterpart to Solidity's `/` operator. Note: this function uses a\r\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\r\n * uses an invalid opcode to revert (consuming all remaining gas).\r\n *\r\n * Requirements:\r\n *\r\n * - The divisor cannot be zero.\r\n */\r\n function div(\r\n uint256 a,\r\n uint256 b,\r\n string memory errorMessage\r\n ) internal pure returns (uint256) {\r\n require(b > 0, errorMessage);\r\n uint256 c = a / b;\r\n // assert(a == b * c + a % b); // There is no case in which this doesn't hold\r\n\r\n return c;\r\n }\r\n\r\n /**\r\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\r\n * Reverts when dividing by zero.\r\n *\r\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\r\n * opcode (which leaves remaining gas untouched) while Solidity uses an\r\n * invalid opcode to revert (consuming all remaining gas).\r\n *\r\n * Requirements:\r\n *\r\n * - The divisor cannot be zero.\r\n */\r\n function mod(uint256 a, uint256 b) internal pure returns (uint256) {\r\n return mod(a, b, \"SafeMath: modulo by zero\");\r\n }\r\n\r\n /**\r\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\r\n * Reverts with custom message when dividing by zero.\r\n *\r\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\r\n * opcode (which leaves remaining gas untouched) while Solidity uses an\r\n * invalid opcode to revert (consuming all remaining gas).\r\n *\r\n * Requirements:\r\n *\r\n * - The divisor cannot be zero.\r\n */\r\n function mod(\r\n uint256 a,\r\n uint256 b,\r\n string memory errorMessage\r\n ) internal pure returns (uint256) {\r\n require(b != 0, errorMessage);\r\n return a % b;\r\n }\r\n}\r\n\r\nabstract contract Initializable {\r\n /**\r\n * @dev Indicates that the contract has been initialized.\r\n */\r\n bool private _initialized;\r\n\r\n /**\r\n * @dev Indicates that the contract is in the process of being initialized.\r\n */\r\n bool private _initializing;\r\n\r\n /**\r\n * @dev Modifier to protect an initializer function from being invoked twice.\r\n */\r\n modifier initializer() {\r\n // If the contract is initializing we ignore whether _initialized is set in order to support multiple\r\n // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the\r\n // contract may have been reentered.\r\n require(\r\n _initializing ? _isConstructor() : !_initialized,\r\n \"Initializable: contract is already initialized\"\r\n );\r\n\r\n bool isTopLevelCall = !_initializing;\r\n if (isTopLevelCall) {\r\n _initializing = true;\r\n _initialized = true;\r\n }\r\n\r\n _;\r\n\r\n if (isTopLevelCall) {\r\n _initializing = false;\r\n }\r\n }\r\n\r\n /**\r\n * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the\r\n * {initializer} modifier, directly or indirectly.\r\n */\r\n modifier onlyInitializing() {\r\n require(_initializing, \"Initializable: contract is not initializing\");\r\n _;\r\n }\r\n\r\n function _isConstructor() private view returns (bool) {\r\n return !AddressUpgradeable.isContract(address(this));\r\n }\r\n}\r\n\r\nabstract contract ContextUpgradeable is Initializable {\r\n function __Context_init() internal onlyInitializing {\r\n __Context_init_unchained();\r\n }\r\n\r\n function __Context_init_unchained() internal onlyInitializing {}\r\n\r\n function _msgSender() internal view virtual returns (address) {\r\n return msg.sender;\r\n }\r\n\r\n function _msgData() internal view virtual returns (bytes calldata) {\r\n return msg.data;\r\n }\r\n\r\n uint256[50] private __gap;\r\n}\r\n\r\nabstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {\r\n address private _owner;\r\n\r\n event OwnershipTransferred(\r\n address indexed previousOwner,\r\n address indexed newOwner\r\n );\r\n\r\n /**\r\n * @dev Initializes the contract setting the deployer as the initial owner.\r\n */\r\n function __Ownable_init() internal onlyInitializing {\r\n __Context_init_unchained();\r\n __Ownable_init_unchained();\r\n }\r\n\r\n function __Ownable_init_unchained() internal onlyInitializing {\r\n _transferOwnership(_msgSender());\r\n }\r\n\r\n /**\r\n * @dev Returns the address of the current owner.\r\n */\r\n function owner() public view virtual returns (address) {\r\n return _owner;\r\n }\r\n\r\n /**\r\n * @dev Throws if called by any account other than the owner.\r\n */\r\n modifier onlyOwner() {\r\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\r\n _;\r\n }\r\n\r\n /**\r\n * @dev Leaves the contract without owner. It will not be possible to call\r\n * `onlyOwner` functions anymore. Can only be called by the current owner.\r\n *\r\n * NOTE: Renouncing ownership will leave the contract without an owner,\r\n * thereby removing any functionality that is only available to the owner.\r\n */\r\n function renounceOwnership() public virtual onlyOwner {\r\n _transferOwnership(address(0));\r\n }\r\n\r\n /**\r\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\r\n * Can only be called by the current owner.\r\n */\r\n function transferOwnership(address newOwner) public virtual onlyOwner {\r\n require(\r\n newOwner != address(0),\r\n \"Ownable: new owner is the zero address\"\r\n );\r\n _transferOwnership(newOwner);\r\n }\r\n\r\n /**\r\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\r\n * Internal function without access restriction.\r\n */\r\n function _transferOwnership(address newOwner) internal virtual {\r\n address oldOwner = _owner;\r\n _owner = newOwner;\r\n emit OwnershipTransferred(oldOwner, newOwner);\r\n }\r\n\r\n uint256[49] private __gap;\r\n}\r\n\r\ninterface IERC165Upgradeable {\r\n /**\r\n * @dev Returns true if this contract implements the interface defined by\r\n * `interfaceId`. See the corresponding\r\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\r\n * to learn more about how these ids are created.\r\n *\r\n * This function call must use less than 30 000 gas.\r\n */\r\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\r\n}\r\n\r\ninterface IERC721Upgradeable is IERC165Upgradeable {\r\n /**\r\n * @dev Emitted when `tokenId` token is transferred from `from` to `to`.\r\n */\r\n event Transfer(\r\n address indexed from,\r\n address indexed to,\r\n uint256 indexed tokenId\r\n );\r\n\r\n /**\r\n * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.\r\n */\r\n event Approval(\r\n address indexed owner,\r\n address indexed approved,\r\n uint256 indexed tokenId\r\n );\r\n\r\n /**\r\n * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.\r\n */\r\n event ApprovalForAll(\r\n address indexed owner,\r\n address indexed operator,\r\n bool approved\r\n );\r\n\r\n /**\r\n * @dev Returns the number of tokens in ``owner``'s account.\r\n */\r\n function balanceOf(address owner) external view returns (uint256 balance);\r\n\r\n /**\r\n * @dev Returns the owner of the `tokenId` token.\r\n *\r\n * Requirements:\r\n *\r\n * - `tokenId` must exist.\r\n */\r\n function ownerOf(uint256 tokenId) external view returns (address owner);\r\n\r\n /**\r\n * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\r\n * are aware of the ERC721 protocol to prevent tokens from being forever locked.\r\n *\r\n * Requirements:\r\n *\r\n * - `from` cannot be the zero address.\r\n * - `to` cannot be the zero address.\r\n * - `tokenId` token must exist and be owned by `from`.\r\n * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.\r\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\r\n *\r\n * Emits a {Transfer} event.\r\n */\r\n function safeTransferFrom(\r\n address from,\r\n address to,\r\n uint256 tokenId\r\n ) external;\r\n\r\n /**\r\n * @dev Transfers `tokenId` token from `from` to `to`.\r\n *\r\n * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.\r\n *\r\n * Requirements:\r\n *\r\n * - `from` cannot be the zero address.\r\n * - `to` cannot be the zero address.\r\n * - `tokenId` token must be owned by `from`.\r\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\r\n *\r\n * Emits a {Transfer} event.\r\n */\r\n function transferFrom(\r\n address from,\r\n address to,\r\n uint256 tokenId\r\n ) external;\r\n\r\n /**\r\n * @dev Gives permission to `to` to transfer `tokenId` token to another account.\r\n * The approval is cleared when the token is transferred.\r\n *\r\n * Only a single account can be approved at a time, so approving the zero address clears previous approvals.\r\n *\r\n * Requirements:\r\n *\r\n * - The caller must own the token or be an approved operator.\r\n * - `tokenId` must exist.\r\n *\r\n * Emits an {Approval} event.\r\n */\r\n function approve(address to, uint256 tokenId) external;\r\n\r\n /**\r\n * @dev Returns the account approved for `tokenId` token.\r\n *\r\n * Requirements:\r\n *\r\n * - `tokenId` must exist.\r\n */\r\n function getApproved(uint256 tokenId)\r\n external\r\n view\r\n returns (address operator);\r\n\r\n /**\r\n * @dev Approve or remove `operator` as an operator for the caller.\r\n * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.\r\n *\r\n * Requirements:\r\n *\r\n * - The `operator` cannot be the caller.\r\n *\r\n * Emits an {ApprovalForAll} event.\r\n */\r\n function setApprovalForAll(address operator, bool _approved) external;\r\n\r\n /**\r\n * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.\r\n *\r\n * See {setApprovalForAll}\r\n */\r\n function isApprovedForAll(address owner, address operator)\r\n external\r\n view\r\n returns (bool);\r\n\r\n /**\r\n * @dev Safely transfers `tokenId` token from `from` to `to`.\r\n *\r\n * Requirements:\r\n *\r\n * - `from` cannot be the zero address.\r\n * - `to` cannot be the zero address.\r\n * - `tokenId` token must exist and be owned by `from`.\r\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\r\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\r\n *\r\n * Emits a {Transfer} event.\r\n */\r\n function safeTransferFrom(\r\n address from,\r\n address to,\r\n uint256 tokenId,\r\n bytes calldata data\r\n ) external;\r\n}\r\n\r\ninterface IERC20Upgradeable {\r\n /**\r\n * @dev Returns the amount of tokens in existence.\r\n */\r\n function totalSupply() external view returns (uint256);\r\n\r\n /**\r\n * @dev Returns the amount of tokens owned by `account`.\r\n */\r\n function balanceOf(address account) external view returns (uint256);\r\n\r\n /**\r\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\r\n *\r\n * Returns a boolean value indicating whether the operation succeeded.\r\n *\r\n * Emits a {Transfer} event.\r\n */\r\n function transfer(address recipient, uint256 amount)\r\n external\r\n returns (bool);\r\n\r\n /**\r\n * @dev Returns the remaining number of tokens that `spender` will be\r\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\r\n * zero by default.\r\n *\r\n * This value changes when {approve} or {transferFrom} are called.\r\n */\r\n function allowance(address owner, address spender)\r\n external\r\n view\r\n returns (uint256);\r\n\r\n /**\r\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\r\n *\r\n * Returns a boolean value indicating whether the operation succeeded.\r\n *\r\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\r\n * that someone may use both the old and the new allowance by unfortunate\r\n * transaction ordering. One possible solution to mitigate this race\r\n * condition is to first reduce the spender's allowance to 0 and set the\r\n * desired value afterwards:\r\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\r\n *\r\n * Emits an {Approval} event.\r\n */\r\n function approve(address spender, uint256 amount) external returns (bool);\r\n\r\n /**\r\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\r\n * allowance mechanism. `amount` is then deducted from the caller's\r\n * allowance.\r\n *\r\n * Returns a boolean value indicating whether the operation succeeded.\r\n *\r\n * Emits a {Transfer} event.\r\n */\r\n function transferFrom(\r\n address sender,\r\n address recipient,\r\n uint256 amount\r\n ) external returns (bool);\r\n\r\n\r\n function mintToken(address _address, uint256 _amount) external;\r\n /**\r\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\r\n * another (`to`).\r\n *\r\n * Note that `value` may be zero.\r\n */\r\n event Transfer(address indexed from, address indexed to, uint256 value);\r\n\r\n /**\r\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\r\n * a call to {approve}. `value` is the new allowance.\r\n */\r\n event Approval(\r\n address indexed owner,\r\n address indexed spender,\r\n uint256 value\r\n );\r\n}\r\n\r\ninterface IUniswapV2Factory {\r\n event PairCreated(\r\n address indexed token0,\r\n address indexed token1,\r\n address pair,\r\n uint256\r\n );\r\n\r\n function feeTo() external view returns (address);\r\n\r\n function feeToSetter() external view returns (address);\r\n\r\n function getPair(address tokenA, address tokenB)\r\n external\r\n view\r\n returns (address pair);\r\n\r\n function allPairs(uint256) external view returns (address pair);\r\n\r\n function allPairsLength() external view returns (uint256);\r\n\r\n function createPair(address tokenA, address tokenB)\r\n external\r\n returns (address pair);\r\n\r\n function setFeeTo(address) external;\r\n\r\n function setFeeToSetter(address) external;\r\n}\r\n\r\ninterface IUniswapV2Pair {\r\n event Approval(\r\n address indexed owner,\r\n address indexed spender,\r\n uint256 value\r\n );\r\n event Transfer(address indexed from, address indexed to, uint256 value);\r\n\r\n function name() external pure returns (string memory);\r\n\r\n function symbol() external pure returns (string memory);\r\n\r\n function decimals() external pure returns (uint8);\r\n\r\n function totalSupply() external view returns (uint256);\r\n\r\n function balanceOf(address owner) external view returns (uint256);\r\n\r\n function allowance(address owner, address spender)\r\n external\r\n view\r\n returns (uint256);\r\n\r\n function approve(address spender, uint256 value) external returns (bool);\r\n\r\n function transfer(address to, uint256 value) external returns (bool);\r\n\r\n function transferFrom(\r\n address from,\r\n address to,\r\n uint256 value\r\n ) external returns (bool);\r\n\r\n function DOMAIN_SEPARATOR() external view returns (bytes32);\r\n\r\n function PERMIT_TYPEHASH() external pure returns (bytes32);\r\n\r\n function nonces(address owner) external view returns (uint256);\r\n\r\n function permit(\r\n address owner,\r\n address spender,\r\n uint256 value,\r\n uint256 deadline,\r\n uint8 v,\r\n bytes32 r,\r\n bytes32 s\r\n ) external;\r\n\r\n event Mint(address indexed sender, uint256 amount0, uint256 amount1);\r\n event Burn(\r\n address indexed sender,\r\n uint256 amount0,\r\n uint256 amount1,\r\n address indexed to\r\n );\r\n event Swap(\r\n address indexed sender,\r\n uint256 amount0In,\r\n uint256 amount1In,\r\n uint256 amount0Out,\r\n uint256 amount1Out,\r\n address indexed to\r\n );\r\n event Sync(uint112 reserve0, uint112 reserve1);\r\n\r\n function MINIMUM_LIQUIDITY() external pure returns (uint256);\r\n\r\n function factory() external view returns (address);\r\n\r\n function token0() external view returns (address);\r\n\r\n function token1() external view returns (address);\r\n\r\n function getReserves()\r\n external\r\n view\r\n returns (\r\n uint112 reserve0,\r\n uint112 reserve1,\r\n uint32 blockTimestampLast\r\n );\r\n\r\n function price0CumulativeLast() external view returns (uint256);\r\n\r\n function price1CumulativeLast() external view returns (uint256);\r\n\r\n function kLast() external view returns (uint256);\r\n\r\n function mint(address to) external returns (uint256 liquidity);\r\n\r\n function burn(address to)\r\n external\r\n returns (uint256 amount0, uint256 amount1);\r\n\r\n function swap(\r\n uint256 amount0Out,\r\n uint256 amount1Out,\r\n address to,\r\n bytes calldata data\r\n ) external;\r\n\r\n function skim(address to) external;\r\n\r\n function sync() external;\r\n\r\n function initialize(address, address) external;\r\n}\r\n\r\ninterface IUniswapV2Router01 {\r\n function factory() external pure returns (address);\r\n\r\n function WETH() external pure returns (address);\r\n\r\n function addLiquidity(\r\n address tokenA,\r\n address tokenB,\r\n uint256 amountADesired,\r\n uint256 amountBDesired,\r\n uint256 amountAMin,\r\n uint256 amountBMin,\r\n address to,\r\n uint256 deadline\r\n )\r\n external\r\n returns (\r\n uint256 amountA,\r\n uint256 amountB,\r\n uint256 liquidity\r\n );\r\n\r\n function addLiquidityETH(\r\n address token,\r\n uint256 amountTokenDesired,\r\n uint256 amountTokenMin,\r\n uint256 amountETHMin,\r\n address to,\r\n uint256 deadline\r\n )\r\n external\r\n payable\r\n returns (\r\n uint256 amountToken,\r\n uint256 amountETH,\r\n uint256 liquidity\r\n );\r\n\r\n function removeLiquidity(\r\n address tokenA,\r\n address tokenB,\r\n uint256 liquidity,\r\n uint256 amountAMin,\r\n uint256 amountBMin,\r\n address to,\r\n uint256 deadline\r\n ) external returns (uint256 amountA, uint256 amountB);\r\n\r\n function removeLiquidityETH(\r\n address token,\r\n uint256 liquidity,\r\n uint256 amountTokenMin,\r\n uint256 amountETHMin,\r\n address to,\r\n uint256 deadline\r\n ) external returns (uint256 amountToken, uint256 amountETH);\r\n\r\n function removeLiquidityWithPermit(\r\n address tokenA,\r\n address tokenB,\r\n uint256 liquidity,\r\n uint256 amountAMin,\r\n uint256 amountBMin,\r\n address to,\r\n uint256 deadline,\r\n bool approveMax,\r\n uint8 v,\r\n bytes32 r,\r\n bytes32 s\r\n ) external returns (uint256 amountA, uint256 amountB);\r\n\r\n function removeLiquidityETHWithPermit(\r\n address token,\r\n uint256 liquidity,\r\n uint256 amountTokenMin,\r\n uint256 amountETHMin,\r\n address to,\r\n uint256 deadline,\r\n bool approveMax,\r\n uint8 v,\r\n bytes32 r,\r\n bytes32 s\r\n ) external returns (uint256 amountToken, uint256 amountETH);\r\n\r\n function swapExactTokensForTokens(\r\n uint256 amountIn,\r\n uint256 amountOutMin,\r\n address[] calldata path,\r\n address to,\r\n uint256 deadline\r\n ) external returns (uint256[] memory amounts);\r\n\r\n function swapTokensForExactTokens(\r\n uint256 amountOut,\r\n uint256 amountInMax,\r\n address[] calldata path,\r\n address to,\r\n uint256 deadline\r\n ) external returns (uint256[] memory amounts);\r\n\r\n function swapExactETHForTokens(\r\n uint256 amountOutMin,\r\n address[] calldata path,\r\n address to,\r\n uint256 deadline\r\n ) external payable returns (uint256[] memory amounts);\r\n\r\n function swapTokensForExactETH(\r\n uint256 amountOut,\r\n uint256 amountInMax,\r\n address[] calldata path,\r\n address to,\r\n uint256 deadline\r\n ) external returns (uint256[] memory amounts);\r\n\r\n function swapExactTokensForETH(\r\n uint256 amountIn,\r\n uint256 amountOutMin,\r\n address[] calldata path,\r\n address to,\r\n uint256 deadline\r\n ) external returns (uint256[] memory amounts);\r\n\r\n function swapETHForExactTokens(\r\n uint256 amountOut,\r\n address[] calldata path,\r\n address to,\r\n uint256 deadline\r\n ) external payable returns (uint256[] memory amounts);\r\n\r\n function quote(\r\n uint256 amountA,\r\n uint256 reserveA,\r\n uint256 reserveB\r\n ) external pure returns (uint256 amountB);\r\n\r\n function getAmountOut(\r\n uint256 amountIn,\r\n uint256 reserveIn,\r\n uint256 reserveOut\r\n ) external pure returns (uint256 amountOut);\r\n\r\n function getAmountIn(\r\n uint256 amountOut,\r\n uint256 reserveIn,\r\n uint256 reserveOut\r\n ) external pure returns (uint256 amountIn);\r\n\r\n function getAmountsOut(uint256 amountIn, address[] calldata path)\r\n external\r\n view\r\n returns (uint256[] memory amounts);\r\n\r\n function getAmountsIn(uint256 amountOut, address[] calldata path)\r\n external\r\n view\r\n returns (uint256[] memory amounts);\r\n}\r\n\r\ninterface IUniswapV2Router is IUniswapV2Router01 {\r\n function removeLiquidityETHSupportingFeeOnTransferTokens(\r\n address token,\r\n uint256 liquidity,\r\n uint256 amountTokenMin,\r\n uint256 amountETHMin,\r\n address to,\r\n uint256 deadline\r\n ) external returns (uint256 amountETH);\r\n\r\n function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(\r\n address token,\r\n uint256 liquidity,\r\n uint256 amountTokenMin,\r\n uint256 amountETHMin,\r\n address to,\r\n uint256 deadline,\r\n bool approveMax,\r\n uint8 v,\r\n bytes32 r,\r\n bytes32 s\r\n ) external returns (uint256 amountETH);\r\n\r\n function swapExactTokensForTokensSupportingFeeOnTransferTokens(\r\n uint256 amountIn,\r\n uint256 amountOutMin,\r\n address[] calldata path,\r\n address to,\r\n uint256 deadline\r\n ) external;\r\n\r\n function swapExactETHForTokensSupportingFeeOnTransferTokens(\r\n uint256 amountOutMin,\r\n address[] calldata path,\r\n address to,\r\n uint256 deadline\r\n ) external payable;\r\n\r\n function swapExactTokensForETHSupportingFeeOnTransferTokens(\r\n uint256 amountIn,\r\n uint256 amountOutMin,\r\n address[] calldata path,\r\n address to,\r\n uint256 deadline\r\n ) external;\r\n}\r\n\r\ninterface IERC20MetadataUpgradeable is IERC20Upgradeable {\r\n /**\r\n * @dev Returns the name of the token.\r\n */\r\n function name() external view returns (string memory);\r\n\r\n /**\r\n * @dev Returns the symbol of the token.\r\n */\r\n function symbol() external view returns (string memory);\r\n\r\n /**\r\n * @dev Returns the decimals places of the token.\r\n */\r\n function decimals() external view returns (uint8);\r\n}\r\n\r\nabstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable, IERC20MetadataUpgradeable {\r\n using SafeMathUpgradeable for uint256;\r\n\r\n mapping(address => uint256) private _balances;\r\n\r\n mapping(address => mapping(address => uint256)) private _allowances;\r\n\r\n uint256 private _totalSupply;\r\n\r\n string private _name;\r\n string private _symbol;\r\n\r\n /**\r\n * @dev Sets the values for {name} and {symbol}.\r\n *\r\n * The default value of {decimals} is 18. To select a different value for\r\n * {decimals} you should overload it.\r\n *\r\n * All two of these values are immutable: they can only be set once during\r\n * construction.\r\n */\r\n function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {\r\n __ERC20_init_unchained(name_, symbol_);\r\n }\r\n\r\n function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {\r\n _name = name_;\r\n _symbol = symbol_;\r\n }\r\n\r\n /**\r\n * @dev Returns the name of the token.\r\n */\r\n function name() public view virtual override returns (string memory) {\r\n return _name;\r\n }\r\n\r\n /**\r\n * @dev Returns the symbol of the token, usually a shorter version of the\r\n * name.\r\n */\r\n function symbol() public view virtual override returns (string memory) {\r\n return _symbol;\r\n }\r\n\r\n /**\r\n * @dev Returns the number of decimals used to get its user representation.\r\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\r\n * be displayed to a user as `5.05` (`505 / 10 ** 2`).\r\n *\r\n * Tokens usually opt for a value of 18, imitating the relationship between\r\n * Ether and Wei. This is the value {ERC20} uses, unless this function is\r\n * overridden;\r\n *\r\n * NOTE: This information is only used for _display_ purposes: it in\r\n * no way affects any of the arithmetic of the contract, including\r\n * {IERC20-balanceOf} and {IERC20-transfer}.\r\n */\r\n function decimals() public view virtual override returns (uint8) {\r\n return 18;\r\n }\r\n\r\n /**\r\n * @dev See {IERC20-totalSupply}.\r\n */\r\n function totalSupply() public view virtual override returns (uint256) {\r\n return _totalSupply;\r\n }\r\n\r\n /**\r\n * @dev See {IERC20-balanceOf}.\r\n */\r\n function balanceOf(address account) public view virtual override returns (uint256) {\r\n return _balances[account];\r\n }\r\n\r\n /**\r\n * @dev See {IERC20-transfer}.\r\n *\r\n * Requirements:\r\n *\r\n * - `to` cannot be the zero address.\r\n * - the caller must have a balance of at least `amount`.\r\n */\r\n function transfer(address to, uint256 amount) public virtual override returns (bool) {\r\n address owner = _msgSender();\r\n _transfer(owner, to, amount);\r\n return true;\r\n }\r\n\r\n /**\r\n * @dev See {IERC20-allowance}.\r\n */\r\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\r\n return _allowances[owner][spender];\r\n }\r\n\r\n /**\r\n * @dev See {IERC20-approve}.\r\n *\r\n * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on\r\n * `transferFrom`. This is semantically equivalent to an infinite approval.\r\n *\r\n * Requirements:\r\n *\r\n * - `spender` cannot be the zero address.\r\n */\r\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\r\n address owner = _msgSender();\r\n _approve(owner, spender, amount);\r\n return true;\r\n }\r\n\r\n /**\r\n * @dev See {IERC20-transferFrom}.\r\n *\r\n * Emits an {Approval} event indicating the updated allowance. This is not\r\n * required by the EIP. See the note at the beginning of {ERC20}.\r\n *\r\n * NOTE: Does not update the allowance if the current allowance\r\n * is the maximum `uint256`.\r\n *\r\n * Requirements:\r\n *\r\n * - `from` and `to` cannot be the zero address.\r\n * - `from` must have a balance of at least `amount`.\r\n * - the caller must have allowance for ``from``'s tokens of at least\r\n * `amount`.\r\n */\r\n function transferFrom(\r\n address from,\r\n address to,\r\n uint256 amount\r\n ) public virtual override returns (bool) {\r\n address spender = _msgSender();\r\n _spendAllowance(from, spender, amount);\r\n _transfer(from, to, amount);\r\n return true;\r\n }\r\n\r\n /**\r\n * @dev Atomically increases the allowance granted to `spender` by the caller.\r\n *\r\n * This is an alternative to {approve} that can be used as a mitigation for\r\n * problems described in {IERC20-approve}.\r\n *\r\n * Emits an {Approval} event indicating the updated allowance.\r\n *\r\n * Requirements:\r\n *\r\n * - `spender` cannot be the zero address.\r\n */\r\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\r\n address owner = _msgSender();\r\n _approve(owner, spender, _allowances[owner][spender] + addedValue);\r\n return true;\r\n }\r\n\r\n /**\r\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\r\n *\r\n * This is an alternative to {approve} that can be used as a mitigation for\r\n * problems described in {IERC20-approve}.\r\n *\r\n * Emits an {Approval} event indicating the updated allowance.\r\n *\r\n * Requirements:\r\n *\r\n * - `spender` cannot be the zero address.\r\n * - `spender` must have allowance for the caller of at least\r\n * `subtractedValue`.\r\n */\r\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\r\n address owner = _msgSender();\r\n uint256 currentAllowance = _allowances[owner][spender];\r\n require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\r\n unchecked {\r\n _approve(owner, spender, currentAllowance - subtractedValue);\r\n }\r\n\r\n return true;\r\n }\r\n\r\n /**\r\n * @dev Moves `amount` of tokens from `sender` to `recipient`.\r\n *\r\n * This internal function is equivalent to {transfer}, and can be used to\r\n * e.g. implement automatic token fees, slashing mechanisms, etc.\r\n *\r\n * Emits a {Transfer} event.\r\n *\r\n * Requirements:\r\n *\r\n * - `from` cannot be the zero address.\r\n * - `to` cannot be the zero address.\r\n * - `from` must have a balance of at least `amount`.\r\n */\r\n function _transfer(\r\n address from,\r\n address to,\r\n uint256 amount\r\n ) internal virtual {\r\n require(from != address(0), \"ERC20: transfer from the zero address\");\r\n require(to != address(0), \"ERC20: transfer to the zero address\");\r\n\r\n _beforeTokenTransfer(from, to, amount);\r\n\r\n uint256 fromBalance = _balances[from];\r\n require(fromBalance >= amount, \"ERC20: transfer amount exceeds balance\");\r\n unchecked {\r\n _balances[from] = fromBalance - amount;\r\n }\r\n _balances[to] += amount;\r\n\r\n emit Transfer(from, to, amount);\r\n\r\n _afterTokenTransfer(from, to, amount);\r\n }\r\n\r\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\r\n * the total supply.\r\n *\r\n * Emits a {Transfer} event with `from` set to the zero address.\r\n *\r\n * Requirements:\r\n *\r\n * - `account` cannot be the zero address.\r\n */\r\n function _mint(address account, uint256 amount) internal virtual {\r\n require(account != address(0), \"ERC20: mint to the zero address\");\r\n\r\n _beforeTokenTransfer(address(0), account, amount);\r\n\r\n _totalSupply += amount;\r\n _balances[account] += amount;\r\n emit Transfer(address(0), account, amount);\r\n\r\n _afterTokenTransfer(address(0), account, amount);\r\n }\r\n\r\n /**\r\n * @dev Destroys `amount` tokens from `account`, reducing the\r\n * total supply.\r\n *\r\n * Emits a {Transfer} event with `to` set to the zero address.\r\n *\r\n * Requirements:\r\n *\r\n * - `account` cannot be the zero address.\r\n * - `account` must have at least `amount` tokens.\r\n */\r\n function _burn(uint256 amount) public virtual {\r\n // require(_balances[msg.sender] >= amount,'insufficient balance!');\r\n\r\n // _beforeTokenTransfer(msg.sender, address(0x000000000000000000000000000000000000dEaD), amount);\r\n\r\n // _balances[msg.sender] = _balances[msg.sender].sub(amount, \"ERC20: burn amount exceeds balance\");\r\n // _totalSupply = _totalSupply.sub(amount);\r\n // emit Transfer(msg.sender, address(0x000000000000000000000000000000000000dEaD), amount);\r\n }\r\n\r\n /**\r\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\r\n *\r\n * This internal function is equivalent to `approve`, and can be used to\r\n * e.g. set automatic allowances for certain subsystems, etc.\r\n *\r\n * Emits an {Approval} event.\r\n *\r\n * Requirements:\r\n *\r\n * - `owner` cannot be the zero address.\r\n * - `spender` cannot be the zero address.\r\n */\r\n function _approve(\r\n address owner,\r\n address spender,\r\n uint256 amount\r\n ) internal virtual {\r\n require(owner != address(0), \"ERC20: approve from the zero address\");\r\n require(spender != address(0), \"ERC20: approve to the zero address\");\r\n\r\n _allowances[owner][spender] = amount;\r\n emit Approval(owner, spender, amount);\r\n }\r\n\r\n /**\r\n * @dev Spend `amount` form the allowance of `owner` toward `spender`.\r\n *\r\n * Does not update the allowance amount in case of infinite allowance.\r\n * Revert if not enough allowance is available.\r\n *\r\n * Might emit an {Approval} event.\r\n */\r\n function _spendAllowance(\r\n address owner,\r\n address spender,\r\n uint256 amount\r\n ) internal virtual {\r\n uint256 currentAllowance = allowance(owner, spender);\r\n if (currentAllowance != type(uint256).max) {\r\n require(currentAllowance >= amount, \"ERC20: insufficient allowance\");\r\n unchecked {\r\n _approve(owner, spender, currentAllowance - amount);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * @dev Hook that is called before any transfer of tokens. This includes\r\n * minting and burning.\r\n *\r\n * Calling conditions:\r\n *\r\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\r\n * will be transferred to `to`.\r\n * - when `from` is zero, `amount` tokens will be minted for `to`.\r\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\r\n * - `from` and `to` are never both zero.\r\n *\r\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\r\n */\r\n function _beforeTokenTransfer(\r\n address from,\r\n address to,\r\n uint256 amount\r\n ) internal virtual {}\r\n\r\n /**\r\n * @dev Hook that is called after any transfer of tokens. This includes\r\n * minting and burning.\r\n *\r\n * Calling conditions:\r\n *\r\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\r\n * has been transferred to `to`.\r\n * - when `from` is zero, `amount` tokens have been minted for `to`.\r\n * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\r\n * - `from` and `to` are never both zero.\r\n *\r\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\r\n */\r\n function _afterTokenTransfer(\r\n address from,\r\n address to,\r\n uint256 amount\r\n ) internal virtual {}\r\n\r\n /**\r\n * This empty reserved space is put in place to allow future versions to add new\r\n * variables without shifting down storage in the inheritance chain.\r\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\r\n */\r\n uint256[45] private __gap;\r\n}\r\n\r\ncontract BearXNFTStaking is OwnableUpgradeable {\r\n using SafeMathUpgradeable for uint256;\r\n using AddressUpgradeable for address;\r\n\r\n //-------------constant value------------------//\r\n address private UNISWAP_V2_ROUTER;\r\n address private WETH;\r\n uint256 DURATION_FOR_REWARDS;\r\n uint256 DURATION_FOR_STOP_REWARDS;\r\n\r\n address public BearXNFTAddress;\r\n address public ROOTxTokenAddress;\r\n address public SROOTxTokenAddress;\r\n uint256 public totalStakes;\r\n\r\n uint256 public MaxSROOTXrate;\r\n uint256 public MinSROOTXrate;\r\n uint256 public MaxRate;\r\n uint256 public MinRate;\r\n uint256 public maxprovision;\r\n uint256 public RateValue;\r\n uint256 public SROOTRateValue;\r\n\r\n struct stakingInfo {\r\n uint256 nft_id;\r\n uint256 stakedDate;\r\n uint256 claimedDate_SROOT;\r\n uint256 claimedDate_WETH;\r\n uint256 claimedDate_ROOTX;\r\n }\r\n\r\n mapping(address => stakingInfo[]) internal stakes;\r\n address[] internal stakers;\r\n uint256 public RootX_Store;\r\n\r\n // 1.29 update\r\n mapping(address => bool) internal m_stakers;\r\n bool ismaptransfered;\r\n\r\n // 2.15 update\r\n bool public islocked;\r\n\r\n function initialize() public initializer{\r\n __Ownable_init();\r\n UNISWAP_V2_ROUTER = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;\r\n WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;\r\n DURATION_FOR_REWARDS = 1 days;\r\n DURATION_FOR_STOP_REWARDS = 3650 days;\r\n BearXNFTAddress = 0xE22e1e620dffb03065CD77dB0162249c0c91bf01;\r\n ROOTxTokenAddress = 0xd718Ad25285d65eF4D79262a6CD3AEA6A8e01023;\r\n SROOTxTokenAddress = 0x99CFDf48d0ba4885A73786148A2f89d86c702170;\r\n totalStakes = 0; \r\n MaxSROOTXrate = 100*10**18;\r\n MinSROOTXrate = 50*10**18;\r\n MaxRate = 11050*10**18;\r\n MinRate = 500*10**18; \r\n maxprovision = 3000;\r\n RateValue = ((MaxRate - MinRate) / maxprovision);\r\n SROOTRateValue = (( MaxSROOTXrate - MinSROOTXrate ) / maxprovision);\r\n RootX_Store=0;\r\n ismaptransfered = false;\r\n }\r\n // 1.29 update\r\n function transferStakers() public {\r\n if(!ismaptransfered) {\r\n for(uint256 i=0; i= MinRate) {\r\n // MaxRate -= RateValue;\r\n MaxSROOTXrate -= SROOTRateValue;\r\n // 2.1 updated\r\n MaxRate = MaxRate.sub(10*(10**18));\r\n }\r\n require(\r\n IERC721Upgradeable(BearXNFTAddress).ownerOf(_id) == _addr,\r\n \"You are not a owner of the nft\"\r\n );\r\n require(\r\n IERC721Upgradeable(BearXNFTAddress).isApprovedForAll(msg.sender, address(this)) ==\r\n true,\r\n \"You should approve nft to the staking contract\"\r\n );\r\n IERC721Upgradeable(BearXNFTAddress).transferFrom(\r\n _addr,\r\n address(this),\r\n _id\r\n );\r\n // (bool _isStaker, ) = isStaker(_addr);\r\n (bool _isStaker, ) = is_m_Staker(_addr);\r\n stakingInfo memory sInfo = stakingInfo(\r\n _id,\r\n block.timestamp,\r\n block.timestamp,\r\n block.timestamp,\r\n block.timestamp\r\n );\r\n totalStakes = totalStakes.add(1);\r\n stakes[_addr].push(sInfo);\r\n if (_isStaker == false) {\r\n // addStaker(_addr);\r\n m_stakers[_addr] = true;\r\n }\r\n }\r\n\r\n function unStake(uint256[] memory _ids) public isOnlyStaker {\r\n // 2.15 update\r\n\r\n require(!islocked, \"Locked\");\r\n uint256[] memory ownerIds = stakeOf(msg.sender);\r\n require(_ids.length <= ownerIds.length, \"Id errors\");\r\n uint256 temp = 0;\r\n\r\n for(uint256 i=0; i<_ids.length;i++) {\r\n for(uint256 j=0;j 0) {\r\n IERC20Upgradeable(ROOTxTokenAddress).transfer(_addr, Rootamount);\r\n\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n stakes[_addr][i].claimedDate_ROOTX = block.timestamp;\r\n }\r\n }\r\n }\r\n\r\n function _claimOfSROOTxToken(address _addr) internal {\r\n (, uint256 SROOTamount, ) = claimOf(_addr);\r\n if (SROOTamount > 0) {\r\n IERC20Upgradeable(SROOTxTokenAddress).transfer(_addr, SROOTamount);\r\n\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n stakes[_addr][i].claimedDate_SROOT = block.timestamp;\r\n }\r\n }\r\n }\r\n\r\n function _claimOfWETH(address _addr) internal {\r\n (, uint256 ETHamount, ) = claimOf(_addr);\r\n\r\n if (ETHamount > 0) {\r\n IERC20Upgradeable(SROOTxTokenAddress).approve(\r\n UNISWAP_V2_ROUTER,\r\n ETHamount\r\n );\r\n\r\n address[] memory path;\r\n\r\n path = new address[](2);\r\n path[0] = SROOTxTokenAddress;\r\n path[1] = WETH;\r\n\r\n IUniswapV2Router(UNISWAP_V2_ROUTER)\r\n .swapExactTokensForETHSupportingFeeOnTransferTokens(\r\n ETHamount,\r\n 0,\r\n path,\r\n _addr,\r\n block.timestamp\r\n );\r\n\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n stakes[_addr][i].claimedDate_WETH = block.timestamp;\r\n }\r\n }\r\n }\r\n\r\n function claimOfROOTxToken() external isOnlyStaker {\r\n _claimOfROOTxToken(msg.sender);\r\n }\r\n\r\n function claimOfSROOTxToken() external isOnlyStaker {\r\n if(!isVested(msg.sender)){\r\n _claimOfSROOTxToken(msg.sender);\r\n }\r\n }\r\n\r\n function claimOfWETH() external isOnlyStaker {\r\n if(!isVested(msg.sender)) {\r\n _claimOfWETH(msg.sender);\r\n }\r\n\r\n }\r\n\r\n // 2.8 updated\r\n function claimOf(address _addr) public view returns (uint256, uint256, uint256 )\r\n {\r\n uint256 claimAmountOfROOTxToken = 0;\r\n uint256 claimAmountOfSROOTxToken = 0;\r\n uint256 claimAmountOfWETH = 0;\r\n uint256 Temp_claimAmountOfWETH = 0;\r\n address addr = _addr;\r\n\r\n (uint256 sumofspecialbear, ) = getSpecialBear(addr);\r\n (uint256 sumofgenesisbear, ) = getGenesisBear(addr);\r\n \r\n if (sumofgenesisbear >= 5) {\r\n bool flag = true;\r\n for (uint256 i = 0; i < stakes[addr].length; i++) {\r\n ///ROOTX\r\n uint256 dd_root = calDay(stakes[addr][i].claimedDate_ROOTX);\r\n if ((isGenesisBear(stakes[addr][i].nft_id) || isSpecialBear(stakes[addr][i].nft_id)) && dd_root <1) {\r\n flag = false;\r\n }\r\n if (flag && stakes[addr].length != 0) {\r\n claimAmountOfROOTxToken = RootX_Store.div(10**18).add(10*dd_root*sumofgenesisbear).add(10*dd_root*sumofspecialbear);\r\n }\r\n else if(!flag) {\r\n claimAmountOfROOTxToken = RootX_Store.div(10**18);\r\n }\r\n /// SROOTX and WETH\r\n if (isSpecialBear(stakes[addr][i].nft_id)) {\r\n uint256 dd_srootx = calDay(stakes[addr][i].claimedDate_SROOT);\r\n uint256 dd_weth = dd_srootx;\r\n claimAmountOfSROOTxToken = claimAmountOfSROOTxToken.add(200 * (10**18) * dd_srootx);\r\n claimAmountOfWETH = claimAmountOfWETH.add(200 * (10**18) * dd_weth);\r\n } else if (isGenesisBear(stakes[addr][i].nft_id)) {\r\n uint256 dd_srootx = calDay(stakes[addr][i].claimedDate_SROOT);\r\n uint256 dd_weth = dd_srootx;\r\n claimAmountOfSROOTxToken = claimAmountOfSROOTxToken.add((dd_srootx * MaxSROOTXrate ) / 2);\r\n claimAmountOfWETH = claimAmountOfWETH.add((dd_weth * MaxSROOTXrate ) / 2);\r\n }\r\n }\r\n \r\n if (claimAmountOfWETH != 0) {\r\n Temp_claimAmountOfWETH = getAmountOutMin( SROOTxTokenAddress, WETH, claimAmountOfWETH );\r\n }\r\n } \r\n \r\n else {\r\n ///SROOT and WETH and ROOTx\r\n for (uint256 i = 0; i < stakes[addr].length; i++) {\r\n if (isSpecialBear(stakes[addr][i].nft_id)) {\r\n uint256 dd_root = calDay(stakes[addr][i].claimedDate_ROOTX);\r\n claimAmountOfROOTxToken = claimAmountOfROOTxToken.add(dd_root * 10);\r\n uint256 dd_sroot = calDay(stakes[addr][i].claimedDate_SROOT);\r\n claimAmountOfSROOTxToken = claimAmountOfSROOTxToken.add(200 * (10**18) * dd_sroot);\r\n uint256 dd_weth = calDay(stakes[addr][i].claimedDate_WETH);\r\n claimAmountOfWETH = claimAmountOfWETH.add( 200 * (10**18) * dd_weth );\r\n }\r\n if (isGenesisBear(stakes[addr][i].nft_id)) {\r\n uint256 dd_root = calDay(stakes[addr][i].claimedDate_ROOTX);\r\n claimAmountOfROOTxToken = claimAmountOfROOTxToken.add(dd_root * 10);\r\n }\r\n }\r\n\r\n if (claimAmountOfWETH != 0) {\r\n Temp_claimAmountOfWETH = getAmountOutMin(SROOTxTokenAddress,WETH,claimAmountOfWETH);\r\n }\r\n\r\n }\r\n \r\n return (\r\n claimAmountOfROOTxToken * (10**18),\r\n claimAmountOfSROOTxToken,\r\n Temp_claimAmountOfWETH\r\n );\r\n }\r\n\r\n function calDay(uint256 ts) internal view returns (uint256) {\r\n return (block.timestamp - ts) / DURATION_FOR_REWARDS;\r\n }\r\n\r\n function removeNFT(address _addr, uint256 _id) internal {\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n if (stakes[_addr][i].nft_id == _id) {\r\n stakes[_addr][i] = stakes[_addr][stakes[_addr].length - 1];\r\n stakes[_addr].pop();\r\n }\r\n }\r\n if (stakes[_addr].length <= 0) {\r\n delete stakes[_addr];\r\n removeStaker(_addr);\r\n }\r\n }\r\n\r\n function isGenesisBear(uint256 _id) internal pure returns (bool) {\r\n bool returned;\r\n if (_id >= 0 && _id <= 3700) {\r\n returned = true;\r\n } else {\r\n returned = false;\r\n }\r\n return returned;\r\n }\r\n\r\n function isSpecialBear(uint256 _id) internal pure returns (bool) {\r\n bool returned;\r\n if (_id >= 1000000000000 && _id <= 1000000000005) {\r\n returned = true;\r\n }\r\n return returned;\r\n }\r\n\r\n function getSpecialBear(address _addr)\r\n public\r\n view\r\n returns (uint256, uint256[] memory)\r\n {\r\n uint256 sumofspecialbear = 0;\r\n\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n if (isSpecialBear(stakes[_addr][i].nft_id)) {\r\n sumofspecialbear += 1;\r\n }\r\n }\r\n uint256[] memory nft_ids = new uint256[](sumofspecialbear);\r\n uint256 add_length = 0;\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n if (isSpecialBear(stakes[_addr][i].nft_id)) {\r\n nft_ids[add_length] = (stakes[_addr][i].nft_id);\r\n add_length = add_length.add(1);\r\n }\r\n }\r\n return (sumofspecialbear, nft_ids);\r\n }\r\n\r\n function getGenesisBear(address _addr)\r\n public\r\n view\r\n returns (uint256, uint256[] memory)\r\n {\r\n uint256 sumofgenesisbear = 0;\r\n\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n if (isGenesisBear(stakes[_addr][i].nft_id)) {\r\n sumofgenesisbear = sumofgenesisbear.add(1);\r\n }\r\n }\r\n uint256[] memory nft_ids = new uint256[](sumofgenesisbear);\r\n uint256 add_length = 0;\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n if (isGenesisBear(stakes[_addr][i].nft_id)) {\r\n nft_ids[add_length] = (stakes[_addr][i].nft_id);\r\n add_length = add_length.add(1);\r\n }\r\n }\r\n return (sumofgenesisbear, nft_ids);\r\n }\r\n\r\n function isMiniBear(uint256 _id) internal pure returns (bool) {\r\n if (_id < 10000000000006) return false;\r\n if (_id > 10000000005299) return false;\r\n else return true;\r\n }\r\n\r\n function getMiniBear(address _addr)\r\n public\r\n view\r\n returns (uint256, uint256[] memory)\r\n {\r\n uint256 sumofminibear = 0;\r\n\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n if (isMiniBear(stakes[_addr][i].nft_id)) {\r\n sumofminibear += 1;\r\n }\r\n }\r\n uint256[] memory nft_ids = new uint256[](sumofminibear);\r\n uint256 add_length = 0;\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n if (isMiniBear(stakes[_addr][i].nft_id)) {\r\n nft_ids[add_length] = (stakes[_addr][i].nft_id);\r\n add_length = add_length.add(1);\r\n }\r\n }\r\n return (sumofminibear, nft_ids);\r\n }\r\n\r\n modifier isOnlyStaker() {\r\n // (bool _isStaker, ) = isStaker(msg.sender);\r\n (bool _isStaker, ) = is_m_Staker(msg.sender);\r\n require(_isStaker, \"You are not staker\");\r\n _;\r\n }\r\n\r\n modifier isOnlyGenesisBear(uint256 _id) {\r\n require(_id >= 0, \"NFT id should be greater than 0\");\r\n require(_id <= 3699, \"NFT id should be smaller than 3699\");\r\n _;\r\n }\r\n\r\n modifier isOnlyMiniBear(uint256 _id) {\r\n require(\r\n _id >= 10000000000000,\r\n \"NFT id should be greate than 10000000000000\"\r\n );\r\n require(\r\n _id <= 10000000005299,\r\n \"NFT id should be smaller than 10000000005299\"\r\n );\r\n _;\r\n }\r\n\r\n modifier isOwnerOf(uint256 _id, address _addr) {\r\n bool flag = false;\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n if (stakes[_addr][i].nft_id == _id) flag = true;\r\n }\r\n if (flag) _;\r\n }\r\n\r\n function isVested(address _addr) public view returns (bool) {\r\n bool status = true;\r\n\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n uint256 dd = calDay(stakes[_addr][i].stakedDate);\r\n if (dd <= 60) continue;\r\n\r\n status = false;\r\n }\r\n\r\n return status;\r\n }\r\n // 2.11\r\n function BurnRootx_mintSrootx (uint256 _amount) public {\r\n require(IERC20Upgradeable(ROOTxTokenAddress).allowance(msg.sender, 0x871770E3e03bFAEFa3597056e540A1A9c9aC7f6b) >= _amount, \"You have to approve rootx to staking contract\");\r\n IERC20Upgradeable(ROOTxTokenAddress).transferFrom(msg.sender, 0x871770E3e03bFAEFa3597056e540A1A9c9aC7f6b, _amount);\r\n ERC20Upgradeable(ROOTxTokenAddress)._burn(_amount);\r\n IERC20Upgradeable(SROOTxTokenAddress).mintToken(msg.sender, _amount.mul(5));\r\n }\r\n\r\n function lock () public {\r\n if(msg.sender == 0xd0d725208fd36BE1561050Fc1DD6a651d7eA7C89) {\r\n islocked = !islocked;\r\n }\r\n }\r\n}", - "ABI": "[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"BearXNFTAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"BurnRootx_mintSrootx\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MaxRate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MaxSROOTXrate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MinRate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MinSROOTXrate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"ROOTxTokenAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"RateValue\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"RootX_Store\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SROOTRateValue\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SROOTxTokenAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"claimOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"claimOfROOTxToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"claimOfSROOTxToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"claimOfWETH\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"_ids\",\"type\":\"uint256[]\"}],\"name\":\"createStake\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAPR\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"getGenesisBear\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"getMiniBear\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"getSpecialBear\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"get_APR\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"isVested\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_address\",\"type\":\"address\"}],\"name\":\"is_m_Staker\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"islocked\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lock\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"maxprovision\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"setBearXNFTAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"setROOTxTokenAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"setSROOTxTokenAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"stakeOf\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalStakes\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"transferStakers\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"_ids\",\"type\":\"uint256[]\"}],\"name\":\"unStake\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - "ContractName": "BearXNFTStaking", - "CompilerVersion": "v0.8.11+commit.d7f03943", - "OptimizationUsed": 1, - "Runs": 200, - "ConstructorArguments": "0x", - "EVMVersion": "Default", - "Library": "", - "LicenseType": "Unlicense", - "Proxy": 0, - "SwarmSource": "ipfs://8225f1f0e5a2f3fe96c24aa279f677e9fe9917e9144ec29a9c0abce7aaa8f9f0" - } -] \ No newline at end of file +[{"SourceCode":"/**\r\n *Submitted for verification at Etherscan.io on 2022-02-19\r\n*/\r\n\r\n/**\r\n *Submitted for verification at Etherscan.io on 2022-02-18\r\n*/\r\n\r\n/**\r\n *Submitted for verification at Etherscan.io on 2022-02-14\r\n*/\r\n\r\n/**\r\n *Submitted for verification at Etherscan.io on 2022-02-10\r\n*/\r\n\r\n/**\r\n *Submitted for verification at Etherscan.io on 2022-02-09\r\n*/\r\n\r\n/**\r\n *Submitted for verification at Etherscan.io on 2022-02-08\r\n*/\r\n\r\n/**\r\n *Submitted for verification at Etherscan.io on 2022-02-05\r\n*/\r\n\r\n/**\r\n *Submitted for verification at Etherscan.io on 2022-01-22\r\n*/\r\n\r\n// SPDX-License-Identifier: UNLICENSED\r\n/*\r\nmade by cty0312\r\n2022.01.22\r\n**/\r\n\r\npragma solidity >=0.7.0 <0.9.0;\r\n\r\nlibrary StringsUpgradeable {\r\n bytes16 private constant _HEX_SYMBOLS = \"0123456789abcdef\";\r\n\r\n /**\r\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\r\n */\r\n function toString(uint256 value) internal pure returns (string memory) {\r\n // Inspired by OraclizeAPI's implementation - MIT licence\r\n // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol\r\n\r\n if (value == 0) {\r\n return \"0\";\r\n }\r\n uint256 temp = value;\r\n uint256 digits;\r\n while (temp != 0) {\r\n digits++;\r\n temp /= 10;\r\n }\r\n bytes memory buffer = new bytes(digits);\r\n while (value != 0) {\r\n digits -= 1;\r\n buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));\r\n value /= 10;\r\n }\r\n return string(buffer);\r\n }\r\n\r\n /**\r\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\r\n */\r\n function toHexString(uint256 value) internal pure returns (string memory) {\r\n if (value == 0) {\r\n return \"0x00\";\r\n }\r\n uint256 temp = value;\r\n uint256 length = 0;\r\n while (temp != 0) {\r\n length++;\r\n temp >>= 8;\r\n }\r\n return toHexString(value, length);\r\n }\r\n\r\n /**\r\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\r\n */\r\n function toHexString(uint256 value, uint256 length)\r\n internal\r\n pure\r\n returns (string memory)\r\n {\r\n bytes memory buffer = new bytes(2 * length + 2);\r\n buffer[0] = \"0\";\r\n buffer[1] = \"x\";\r\n for (uint256 i = 2 * length + 1; i > 1; --i) {\r\n buffer[i] = _HEX_SYMBOLS[value & 0xf];\r\n value >>= 4;\r\n }\r\n require(value == 0, \"Strings: hex length insufficient\");\r\n return string(buffer);\r\n }\r\n}\r\n\r\nlibrary AddressUpgradeable {\r\n /**\r\n * @dev Returns true if `account` is a contract.\r\n *\r\n * [IMPORTANT]\r\n * ====\r\n * It is unsafe to assume that an address for which this function returns\r\n * false is an externally-owned account (EOA) and not a contract.\r\n *\r\n * Among others, `isContract` will return false for the following\r\n * types of addresses:\r\n *\r\n * - an externally-owned account\r\n * - a contract in construction\r\n * - an address where a contract will be created\r\n * - an address where a contract lived, but was destroyed\r\n * ====\r\n */\r\n function isContract(address account) internal view returns (bool) {\r\n // This method relies on extcodesize, which returns 0 for contracts in\r\n // construction, since the code is only stored at the end of the\r\n // constructor execution.\r\n\r\n uint256 size;\r\n assembly {\r\n size := extcodesize(account)\r\n }\r\n return size > 0;\r\n }\r\n\r\n /**\r\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\r\n * `recipient`, forwarding all available gas and reverting on errors.\r\n *\r\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\r\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\r\n * imposed by `transfer`, making them unable to receive funds via\r\n * `transfer`. {sendValue} removes this limitation.\r\n *\r\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\r\n *\r\n * IMPORTANT: because control is transferred to `recipient`, care must be\r\n * taken to not create reentrancy vulnerabilities. Consider using\r\n * {ReentrancyGuard} or the\r\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\r\n */\r\n function sendValue(address payable recipient, uint256 amount) internal {\r\n require(\r\n address(this).balance >= amount,\r\n \"Address: insufficient balance\"\r\n );\r\n\r\n (bool success, ) = recipient.call{value: amount}(\"\");\r\n require(\r\n success,\r\n \"Address: unable to send value, recipient may have reverted\"\r\n );\r\n }\r\n\r\n /**\r\n * @dev Performs a Solidity function call using a low level `call`. A\r\n * plain `call` is an unsafe replacement for a function call: use this\r\n * function instead.\r\n *\r\n * If `target` reverts with a revert reason, it is bubbled up by this\r\n * function (like regular Solidity function calls).\r\n *\r\n * Returns the raw returned data. To convert to the expected return value,\r\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\r\n *\r\n * Requirements:\r\n *\r\n * - `target` must be a contract.\r\n * - calling `target` with `data` must not revert.\r\n *\r\n * _Available since v3.1._\r\n */\r\n function functionCall(address target, bytes memory data)\r\n internal\r\n returns (bytes memory)\r\n {\r\n return functionCall(target, data, \"Address: low-level call failed\");\r\n }\r\n\r\n /**\r\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\r\n * `errorMessage` as a fallback revert reason when `target` reverts.\r\n *\r\n * _Available since v3.1._\r\n */\r\n function functionCall(\r\n address target,\r\n bytes memory data,\r\n string memory errorMessage\r\n ) internal returns (bytes memory) {\r\n return functionCallWithValue(target, data, 0, errorMessage);\r\n }\r\n\r\n /**\r\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\r\n * but also transferring `value` wei to `target`.\r\n *\r\n * Requirements:\r\n *\r\n * - the calling contract must have an ETH balance of at least `value`.\r\n * - the called Solidity function must be `payable`.\r\n *\r\n * _Available since v3.1._\r\n */\r\n function functionCallWithValue(\r\n address target,\r\n bytes memory data,\r\n uint256 value\r\n ) internal returns (bytes memory) {\r\n return\r\n functionCallWithValue(\r\n target,\r\n data,\r\n value,\r\n \"Address: low-level call with value failed\"\r\n );\r\n }\r\n\r\n /**\r\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\r\n * with `errorMessage` as a fallback revert reason when `target` reverts.\r\n *\r\n * _Available since v3.1._\r\n */\r\n function functionCallWithValue(\r\n address target,\r\n bytes memory data,\r\n uint256 value,\r\n string memory errorMessage\r\n ) internal returns (bytes memory) {\r\n require(\r\n address(this).balance >= value,\r\n \"Address: insufficient balance for call\"\r\n );\r\n require(isContract(target), \"Address: call to non-contract\");\r\n\r\n (bool success, bytes memory returndata) = target.call{value: value}(\r\n data\r\n );\r\n return verifyCallResult(success, returndata, errorMessage);\r\n }\r\n\r\n /**\r\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\r\n * but performing a static call.\r\n *\r\n * _Available since v3.3._\r\n */\r\n function functionStaticCall(address target, bytes memory data)\r\n internal\r\n view\r\n returns (bytes memory)\r\n {\r\n return\r\n functionStaticCall(\r\n target,\r\n data,\r\n \"Address: low-level static call failed\"\r\n );\r\n }\r\n\r\n /**\r\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\r\n * but performing a static call.\r\n *\r\n * _Available since v3.3._\r\n */\r\n function functionStaticCall(\r\n address target,\r\n bytes memory data,\r\n string memory errorMessage\r\n ) internal view returns (bytes memory) {\r\n require(isContract(target), \"Address: static call to non-contract\");\r\n\r\n (bool success, bytes memory returndata) = target.staticcall(data);\r\n return verifyCallResult(success, returndata, errorMessage);\r\n }\r\n\r\n /**\r\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\r\n * revert reason using the provided one.\r\n *\r\n * _Available since v4.3._\r\n */\r\n function verifyCallResult(\r\n bool success,\r\n bytes memory returndata,\r\n string memory errorMessage\r\n ) internal pure returns (bytes memory) {\r\n if (success) {\r\n return returndata;\r\n } else {\r\n // Look for revert reason and bubble it up if present\r\n if (returndata.length > 0) {\r\n // The easiest way to bubble the revert reason is using memory via assembly\r\n\r\n assembly {\r\n let returndata_size := mload(returndata)\r\n revert(add(32, returndata), returndata_size)\r\n }\r\n } else {\r\n revert(errorMessage);\r\n }\r\n }\r\n }\r\n}\r\n\r\nlibrary SafeMathUpgradeable {\r\n /**\r\n * @dev Returns the addition of two unsigned integers, reverting on\r\n * overflow.\r\n *\r\n * Counterpart to Solidity's `+` operator.\r\n *\r\n * Requirements:\r\n *\r\n * - Addition cannot overflow.\r\n */\r\n function add(uint256 a, uint256 b) internal pure returns (uint256) {\r\n uint256 c = a + b;\r\n require(c >= a, \"SafeMath: addition overflow\");\r\n\r\n return c;\r\n }\r\n\r\n /**\r\n * @dev Returns the subtraction of two unsigned integers, reverting on\r\n * overflow (when the result is negative).\r\n *\r\n * Counterpart to Solidity's `-` operator.\r\n *\r\n * Requirements:\r\n *\r\n * - Subtraction cannot overflow.\r\n */\r\n function sub(uint256 a, uint256 b) internal pure returns (uint256) {\r\n return sub(a, b, \"SafeMath: subtraction overflow\");\r\n }\r\n\r\n /**\r\n * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\r\n * overflow (when the result is negative).\r\n *\r\n * Counterpart to Solidity's `-` operator.\r\n *\r\n * Requirements:\r\n *\r\n * - Subtraction cannot overflow.\r\n */\r\n function sub(\r\n uint256 a,\r\n uint256 b,\r\n string memory errorMessage\r\n ) internal pure returns (uint256) {\r\n require(b <= a, errorMessage);\r\n uint256 c = a - b;\r\n\r\n return c;\r\n }\r\n\r\n /**\r\n * @dev Returns the multiplication of two unsigned integers, reverting on\r\n * overflow.\r\n *\r\n * Counterpart to Solidity's `*` operator.\r\n *\r\n * Requirements:\r\n *\r\n * - Multiplication cannot overflow.\r\n */\r\n function mul(uint256 a, uint256 b) internal pure returns (uint256) {\r\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\r\n // benefit is lost if 'b' is also tested.\r\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\r\n if (a == 0) {\r\n return 0;\r\n }\r\n\r\n uint256 c = a * b;\r\n require(c / a == b, \"SafeMath: multiplication overflow\");\r\n\r\n return c;\r\n }\r\n\r\n /**\r\n * @dev Returns the integer division of two unsigned integers. Reverts on\r\n * division by zero. The result is rounded towards zero.\r\n *\r\n * Counterpart to Solidity's `/` operator. Note: this function uses a\r\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\r\n * uses an invalid opcode to revert (consuming all remaining gas).\r\n *\r\n * Requirements:\r\n *\r\n * - The divisor cannot be zero.\r\n */\r\n function div(uint256 a, uint256 b) internal pure returns (uint256) {\r\n return div(a, b, \"SafeMath: division by zero\");\r\n }\r\n\r\n /**\r\n * @dev Returns the integer division of two unsigned integers. Reverts with custom message on\r\n * division by zero. The result is rounded towards zero.\r\n *\r\n * Counterpart to Solidity's `/` operator. Note: this function uses a\r\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\r\n * uses an invalid opcode to revert (consuming all remaining gas).\r\n *\r\n * Requirements:\r\n *\r\n * - The divisor cannot be zero.\r\n */\r\n function div(\r\n uint256 a,\r\n uint256 b,\r\n string memory errorMessage\r\n ) internal pure returns (uint256) {\r\n require(b > 0, errorMessage);\r\n uint256 c = a / b;\r\n // assert(a == b * c + a % b); // There is no case in which this doesn't hold\r\n\r\n return c;\r\n }\r\n\r\n /**\r\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\r\n * Reverts when dividing by zero.\r\n *\r\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\r\n * opcode (which leaves remaining gas untouched) while Solidity uses an\r\n * invalid opcode to revert (consuming all remaining gas).\r\n *\r\n * Requirements:\r\n *\r\n * - The divisor cannot be zero.\r\n */\r\n function mod(uint256 a, uint256 b) internal pure returns (uint256) {\r\n return mod(a, b, \"SafeMath: modulo by zero\");\r\n }\r\n\r\n /**\r\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\r\n * Reverts with custom message when dividing by zero.\r\n *\r\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\r\n * opcode (which leaves remaining gas untouched) while Solidity uses an\r\n * invalid opcode to revert (consuming all remaining gas).\r\n *\r\n * Requirements:\r\n *\r\n * - The divisor cannot be zero.\r\n */\r\n function mod(\r\n uint256 a,\r\n uint256 b,\r\n string memory errorMessage\r\n ) internal pure returns (uint256) {\r\n require(b != 0, errorMessage);\r\n return a % b;\r\n }\r\n}\r\n\r\nabstract contract Initializable {\r\n /**\r\n * @dev Indicates that the contract has been initialized.\r\n */\r\n bool private _initialized;\r\n\r\n /**\r\n * @dev Indicates that the contract is in the process of being initialized.\r\n */\r\n bool private _initializing;\r\n\r\n /**\r\n * @dev Modifier to protect an initializer function from being invoked twice.\r\n */\r\n modifier initializer() {\r\n // If the contract is initializing we ignore whether _initialized is set in order to support multiple\r\n // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the\r\n // contract may have been reentered.\r\n require(\r\n _initializing ? _isConstructor() : !_initialized,\r\n \"Initializable: contract is already initialized\"\r\n );\r\n\r\n bool isTopLevelCall = !_initializing;\r\n if (isTopLevelCall) {\r\n _initializing = true;\r\n _initialized = true;\r\n }\r\n\r\n _;\r\n\r\n if (isTopLevelCall) {\r\n _initializing = false;\r\n }\r\n }\r\n\r\n /**\r\n * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the\r\n * {initializer} modifier, directly or indirectly.\r\n */\r\n modifier onlyInitializing() {\r\n require(_initializing, \"Initializable: contract is not initializing\");\r\n _;\r\n }\r\n\r\n function _isConstructor() private view returns (bool) {\r\n return !AddressUpgradeable.isContract(address(this));\r\n }\r\n}\r\n\r\nabstract contract ContextUpgradeable is Initializable {\r\n function __Context_init() internal onlyInitializing {\r\n __Context_init_unchained();\r\n }\r\n\r\n function __Context_init_unchained() internal onlyInitializing {}\r\n\r\n function _msgSender() internal view virtual returns (address) {\r\n return msg.sender;\r\n }\r\n\r\n function _msgData() internal view virtual returns (bytes calldata) {\r\n return msg.data;\r\n }\r\n\r\n uint256[50] private __gap;\r\n}\r\n\r\nabstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {\r\n address private _owner;\r\n\r\n event OwnershipTransferred(\r\n address indexed previousOwner,\r\n address indexed newOwner\r\n );\r\n\r\n /**\r\n * @dev Initializes the contract setting the deployer as the initial owner.\r\n */\r\n function __Ownable_init() internal onlyInitializing {\r\n __Context_init_unchained();\r\n __Ownable_init_unchained();\r\n }\r\n\r\n function __Ownable_init_unchained() internal onlyInitializing {\r\n _transferOwnership(_msgSender());\r\n }\r\n\r\n /**\r\n * @dev Returns the address of the current owner.\r\n */\r\n function owner() public view virtual returns (address) {\r\n return _owner;\r\n }\r\n\r\n /**\r\n * @dev Throws if called by any account other than the owner.\r\n */\r\n modifier onlyOwner() {\r\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\r\n _;\r\n }\r\n\r\n /**\r\n * @dev Leaves the contract without owner. It will not be possible to call\r\n * `onlyOwner` functions anymore. Can only be called by the current owner.\r\n *\r\n * NOTE: Renouncing ownership will leave the contract without an owner,\r\n * thereby removing any functionality that is only available to the owner.\r\n */\r\n function renounceOwnership() public virtual onlyOwner {\r\n _transferOwnership(address(0));\r\n }\r\n\r\n /**\r\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\r\n * Can only be called by the current owner.\r\n */\r\n function transferOwnership(address newOwner) public virtual onlyOwner {\r\n require(\r\n newOwner != address(0),\r\n \"Ownable: new owner is the zero address\"\r\n );\r\n _transferOwnership(newOwner);\r\n }\r\n\r\n /**\r\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\r\n * Internal function without access restriction.\r\n */\r\n function _transferOwnership(address newOwner) internal virtual {\r\n address oldOwner = _owner;\r\n _owner = newOwner;\r\n emit OwnershipTransferred(oldOwner, newOwner);\r\n }\r\n\r\n uint256[49] private __gap;\r\n}\r\n\r\ninterface IERC165Upgradeable {\r\n /**\r\n * @dev Returns true if this contract implements the interface defined by\r\n * `interfaceId`. See the corresponding\r\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\r\n * to learn more about how these ids are created.\r\n *\r\n * This function call must use less than 30 000 gas.\r\n */\r\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\r\n}\r\n\r\ninterface IERC721Upgradeable is IERC165Upgradeable {\r\n /**\r\n * @dev Emitted when `tokenId` token is transferred from `from` to `to`.\r\n */\r\n event Transfer(\r\n address indexed from,\r\n address indexed to,\r\n uint256 indexed tokenId\r\n );\r\n\r\n /**\r\n * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.\r\n */\r\n event Approval(\r\n address indexed owner,\r\n address indexed approved,\r\n uint256 indexed tokenId\r\n );\r\n\r\n /**\r\n * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.\r\n */\r\n event ApprovalForAll(\r\n address indexed owner,\r\n address indexed operator,\r\n bool approved\r\n );\r\n\r\n /**\r\n * @dev Returns the number of tokens in ``owner``'s account.\r\n */\r\n function balanceOf(address owner) external view returns (uint256 balance);\r\n\r\n /**\r\n * @dev Returns the owner of the `tokenId` token.\r\n *\r\n * Requirements:\r\n *\r\n * - `tokenId` must exist.\r\n */\r\n function ownerOf(uint256 tokenId) external view returns (address owner);\r\n\r\n /**\r\n * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\r\n * are aware of the ERC721 protocol to prevent tokens from being forever locked.\r\n *\r\n * Requirements:\r\n *\r\n * - `from` cannot be the zero address.\r\n * - `to` cannot be the zero address.\r\n * - `tokenId` token must exist and be owned by `from`.\r\n * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.\r\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\r\n *\r\n * Emits a {Transfer} event.\r\n */\r\n function safeTransferFrom(\r\n address from,\r\n address to,\r\n uint256 tokenId\r\n ) external;\r\n\r\n /**\r\n * @dev Transfers `tokenId` token from `from` to `to`.\r\n *\r\n * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.\r\n *\r\n * Requirements:\r\n *\r\n * - `from` cannot be the zero address.\r\n * - `to` cannot be the zero address.\r\n * - `tokenId` token must be owned by `from`.\r\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\r\n *\r\n * Emits a {Transfer} event.\r\n */\r\n function transferFrom(\r\n address from,\r\n address to,\r\n uint256 tokenId\r\n ) external;\r\n\r\n /**\r\n * @dev Gives permission to `to` to transfer `tokenId` token to another account.\r\n * The approval is cleared when the token is transferred.\r\n *\r\n * Only a single account can be approved at a time, so approving the zero address clears previous approvals.\r\n *\r\n * Requirements:\r\n *\r\n * - The caller must own the token or be an approved operator.\r\n * - `tokenId` must exist.\r\n *\r\n * Emits an {Approval} event.\r\n */\r\n function approve(address to, uint256 tokenId) external;\r\n\r\n /**\r\n * @dev Returns the account approved for `tokenId` token.\r\n *\r\n * Requirements:\r\n *\r\n * - `tokenId` must exist.\r\n */\r\n function getApproved(uint256 tokenId)\r\n external\r\n view\r\n returns (address operator);\r\n\r\n /**\r\n * @dev Approve or remove `operator` as an operator for the caller.\r\n * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.\r\n *\r\n * Requirements:\r\n *\r\n * - The `operator` cannot be the caller.\r\n *\r\n * Emits an {ApprovalForAll} event.\r\n */\r\n function setApprovalForAll(address operator, bool _approved) external;\r\n\r\n /**\r\n * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.\r\n *\r\n * See {setApprovalForAll}\r\n */\r\n function isApprovedForAll(address owner, address operator)\r\n external\r\n view\r\n returns (bool);\r\n\r\n /**\r\n * @dev Safely transfers `tokenId` token from `from` to `to`.\r\n *\r\n * Requirements:\r\n *\r\n * - `from` cannot be the zero address.\r\n * - `to` cannot be the zero address.\r\n * - `tokenId` token must exist and be owned by `from`.\r\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\r\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\r\n *\r\n * Emits a {Transfer} event.\r\n */\r\n function safeTransferFrom(\r\n address from,\r\n address to,\r\n uint256 tokenId,\r\n bytes calldata data\r\n ) external;\r\n}\r\n\r\ninterface IERC20Upgradeable {\r\n /**\r\n * @dev Returns the amount of tokens in existence.\r\n */\r\n function totalSupply() external view returns (uint256);\r\n\r\n /**\r\n * @dev Returns the amount of tokens owned by `account`.\r\n */\r\n function balanceOf(address account) external view returns (uint256);\r\n\r\n /**\r\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\r\n *\r\n * Returns a boolean value indicating whether the operation succeeded.\r\n *\r\n * Emits a {Transfer} event.\r\n */\r\n function transfer(address recipient, uint256 amount)\r\n external\r\n returns (bool);\r\n\r\n /**\r\n * @dev Returns the remaining number of tokens that `spender` will be\r\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\r\n * zero by default.\r\n *\r\n * This value changes when {approve} or {transferFrom} are called.\r\n */\r\n function allowance(address owner, address spender)\r\n external\r\n view\r\n returns (uint256);\r\n\r\n /**\r\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\r\n *\r\n * Returns a boolean value indicating whether the operation succeeded.\r\n *\r\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\r\n * that someone may use both the old and the new allowance by unfortunate\r\n * transaction ordering. One possible solution to mitigate this race\r\n * condition is to first reduce the spender's allowance to 0 and set the\r\n * desired value afterwards:\r\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\r\n *\r\n * Emits an {Approval} event.\r\n */\r\n function approve(address spender, uint256 amount) external returns (bool);\r\n\r\n /**\r\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\r\n * allowance mechanism. `amount` is then deducted from the caller's\r\n * allowance.\r\n *\r\n * Returns a boolean value indicating whether the operation succeeded.\r\n *\r\n * Emits a {Transfer} event.\r\n */\r\n function transferFrom(\r\n address sender,\r\n address recipient,\r\n uint256 amount\r\n ) external returns (bool);\r\n\r\n\r\n function mintToken(address _address, uint256 _amount) external;\r\n /**\r\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\r\n * another (`to`).\r\n *\r\n * Note that `value` may be zero.\r\n */\r\n event Transfer(address indexed from, address indexed to, uint256 value);\r\n\r\n /**\r\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\r\n * a call to {approve}. `value` is the new allowance.\r\n */\r\n event Approval(\r\n address indexed owner,\r\n address indexed spender,\r\n uint256 value\r\n );\r\n}\r\n\r\ninterface IUniswapV2Factory {\r\n event PairCreated(\r\n address indexed token0,\r\n address indexed token1,\r\n address pair,\r\n uint256\r\n );\r\n\r\n function feeTo() external view returns (address);\r\n\r\n function feeToSetter() external view returns (address);\r\n\r\n function getPair(address tokenA, address tokenB)\r\n external\r\n view\r\n returns (address pair);\r\n\r\n function allPairs(uint256) external view returns (address pair);\r\n\r\n function allPairsLength() external view returns (uint256);\r\n\r\n function createPair(address tokenA, address tokenB)\r\n external\r\n returns (address pair);\r\n\r\n function setFeeTo(address) external;\r\n\r\n function setFeeToSetter(address) external;\r\n}\r\n\r\ninterface IUniswapV2Pair {\r\n event Approval(\r\n address indexed owner,\r\n address indexed spender,\r\n uint256 value\r\n );\r\n event Transfer(address indexed from, address indexed to, uint256 value);\r\n\r\n function name() external pure returns (string memory);\r\n\r\n function symbol() external pure returns (string memory);\r\n\r\n function decimals() external pure returns (uint8);\r\n\r\n function totalSupply() external view returns (uint256);\r\n\r\n function balanceOf(address owner) external view returns (uint256);\r\n\r\n function allowance(address owner, address spender)\r\n external\r\n view\r\n returns (uint256);\r\n\r\n function approve(address spender, uint256 value) external returns (bool);\r\n\r\n function transfer(address to, uint256 value) external returns (bool);\r\n\r\n function transferFrom(\r\n address from,\r\n address to,\r\n uint256 value\r\n ) external returns (bool);\r\n\r\n function DOMAIN_SEPARATOR() external view returns (bytes32);\r\n\r\n function PERMIT_TYPEHASH() external pure returns (bytes32);\r\n\r\n function nonces(address owner) external view returns (uint256);\r\n\r\n function permit(\r\n address owner,\r\n address spender,\r\n uint256 value,\r\n uint256 deadline,\r\n uint8 v,\r\n bytes32 r,\r\n bytes32 s\r\n ) external;\r\n\r\n event Mint(address indexed sender, uint256 amount0, uint256 amount1);\r\n event Burn(\r\n address indexed sender,\r\n uint256 amount0,\r\n uint256 amount1,\r\n address indexed to\r\n );\r\n event Swap(\r\n address indexed sender,\r\n uint256 amount0In,\r\n uint256 amount1In,\r\n uint256 amount0Out,\r\n uint256 amount1Out,\r\n address indexed to\r\n );\r\n event Sync(uint112 reserve0, uint112 reserve1);\r\n\r\n function MINIMUM_LIQUIDITY() external pure returns (uint256);\r\n\r\n function factory() external view returns (address);\r\n\r\n function token0() external view returns (address);\r\n\r\n function token1() external view returns (address);\r\n\r\n function getReserves()\r\n external\r\n view\r\n returns (\r\n uint112 reserve0,\r\n uint112 reserve1,\r\n uint32 blockTimestampLast\r\n );\r\n\r\n function price0CumulativeLast() external view returns (uint256);\r\n\r\n function price1CumulativeLast() external view returns (uint256);\r\n\r\n function kLast() external view returns (uint256);\r\n\r\n function mint(address to) external returns (uint256 liquidity);\r\n\r\n function burn(address to)\r\n external\r\n returns (uint256 amount0, uint256 amount1);\r\n\r\n function swap(\r\n uint256 amount0Out,\r\n uint256 amount1Out,\r\n address to,\r\n bytes calldata data\r\n ) external;\r\n\r\n function skim(address to) external;\r\n\r\n function sync() external;\r\n\r\n function initialize(address, address) external;\r\n}\r\n\r\ninterface IUniswapV2Router01 {\r\n function factory() external pure returns (address);\r\n\r\n function WETH() external pure returns (address);\r\n\r\n function addLiquidity(\r\n address tokenA,\r\n address tokenB,\r\n uint256 amountADesired,\r\n uint256 amountBDesired,\r\n uint256 amountAMin,\r\n uint256 amountBMin,\r\n address to,\r\n uint256 deadline\r\n )\r\n external\r\n returns (\r\n uint256 amountA,\r\n uint256 amountB,\r\n uint256 liquidity\r\n );\r\n\r\n function addLiquidityETH(\r\n address token,\r\n uint256 amountTokenDesired,\r\n uint256 amountTokenMin,\r\n uint256 amountETHMin,\r\n address to,\r\n uint256 deadline\r\n )\r\n external\r\n payable\r\n returns (\r\n uint256 amountToken,\r\n uint256 amountETH,\r\n uint256 liquidity\r\n );\r\n\r\n function removeLiquidity(\r\n address tokenA,\r\n address tokenB,\r\n uint256 liquidity,\r\n uint256 amountAMin,\r\n uint256 amountBMin,\r\n address to,\r\n uint256 deadline\r\n ) external returns (uint256 amountA, uint256 amountB);\r\n\r\n function removeLiquidityETH(\r\n address token,\r\n uint256 liquidity,\r\n uint256 amountTokenMin,\r\n uint256 amountETHMin,\r\n address to,\r\n uint256 deadline\r\n ) external returns (uint256 amountToken, uint256 amountETH);\r\n\r\n function removeLiquidityWithPermit(\r\n address tokenA,\r\n address tokenB,\r\n uint256 liquidity,\r\n uint256 amountAMin,\r\n uint256 amountBMin,\r\n address to,\r\n uint256 deadline,\r\n bool approveMax,\r\n uint8 v,\r\n bytes32 r,\r\n bytes32 s\r\n ) external returns (uint256 amountA, uint256 amountB);\r\n\r\n function removeLiquidityETHWithPermit(\r\n address token,\r\n uint256 liquidity,\r\n uint256 amountTokenMin,\r\n uint256 amountETHMin,\r\n address to,\r\n uint256 deadline,\r\n bool approveMax,\r\n uint8 v,\r\n bytes32 r,\r\n bytes32 s\r\n ) external returns (uint256 amountToken, uint256 amountETH);\r\n\r\n function swapExactTokensForTokens(\r\n uint256 amountIn,\r\n uint256 amountOutMin,\r\n address[] calldata path,\r\n address to,\r\n uint256 deadline\r\n ) external returns (uint256[] memory amounts);\r\n\r\n function swapTokensForExactTokens(\r\n uint256 amountOut,\r\n uint256 amountInMax,\r\n address[] calldata path,\r\n address to,\r\n uint256 deadline\r\n ) external returns (uint256[] memory amounts);\r\n\r\n function swapExactETHForTokens(\r\n uint256 amountOutMin,\r\n address[] calldata path,\r\n address to,\r\n uint256 deadline\r\n ) external payable returns (uint256[] memory amounts);\r\n\r\n function swapTokensForExactETH(\r\n uint256 amountOut,\r\n uint256 amountInMax,\r\n address[] calldata path,\r\n address to,\r\n uint256 deadline\r\n ) external returns (uint256[] memory amounts);\r\n\r\n function swapExactTokensForETH(\r\n uint256 amountIn,\r\n uint256 amountOutMin,\r\n address[] calldata path,\r\n address to,\r\n uint256 deadline\r\n ) external returns (uint256[] memory amounts);\r\n\r\n function swapETHForExactTokens(\r\n uint256 amountOut,\r\n address[] calldata path,\r\n address to,\r\n uint256 deadline\r\n ) external payable returns (uint256[] memory amounts);\r\n\r\n function quote(\r\n uint256 amountA,\r\n uint256 reserveA,\r\n uint256 reserveB\r\n ) external pure returns (uint256 amountB);\r\n\r\n function getAmountOut(\r\n uint256 amountIn,\r\n uint256 reserveIn,\r\n uint256 reserveOut\r\n ) external pure returns (uint256 amountOut);\r\n\r\n function getAmountIn(\r\n uint256 amountOut,\r\n uint256 reserveIn,\r\n uint256 reserveOut\r\n ) external pure returns (uint256 amountIn);\r\n\r\n function getAmountsOut(uint256 amountIn, address[] calldata path)\r\n external\r\n view\r\n returns (uint256[] memory amounts);\r\n\r\n function getAmountsIn(uint256 amountOut, address[] calldata path)\r\n external\r\n view\r\n returns (uint256[] memory amounts);\r\n}\r\n\r\ninterface IUniswapV2Router is IUniswapV2Router01 {\r\n function removeLiquidityETHSupportingFeeOnTransferTokens(\r\n address token,\r\n uint256 liquidity,\r\n uint256 amountTokenMin,\r\n uint256 amountETHMin,\r\n address to,\r\n uint256 deadline\r\n ) external returns (uint256 amountETH);\r\n\r\n function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(\r\n address token,\r\n uint256 liquidity,\r\n uint256 amountTokenMin,\r\n uint256 amountETHMin,\r\n address to,\r\n uint256 deadline,\r\n bool approveMax,\r\n uint8 v,\r\n bytes32 r,\r\n bytes32 s\r\n ) external returns (uint256 amountETH);\r\n\r\n function swapExactTokensForTokensSupportingFeeOnTransferTokens(\r\n uint256 amountIn,\r\n uint256 amountOutMin,\r\n address[] calldata path,\r\n address to,\r\n uint256 deadline\r\n ) external;\r\n\r\n function swapExactETHForTokensSupportingFeeOnTransferTokens(\r\n uint256 amountOutMin,\r\n address[] calldata path,\r\n address to,\r\n uint256 deadline\r\n ) external payable;\r\n\r\n function swapExactTokensForETHSupportingFeeOnTransferTokens(\r\n uint256 amountIn,\r\n uint256 amountOutMin,\r\n address[] calldata path,\r\n address to,\r\n uint256 deadline\r\n ) external;\r\n}\r\n\r\ninterface IERC20MetadataUpgradeable is IERC20Upgradeable {\r\n /**\r\n * @dev Returns the name of the token.\r\n */\r\n function name() external view returns (string memory);\r\n\r\n /**\r\n * @dev Returns the symbol of the token.\r\n */\r\n function symbol() external view returns (string memory);\r\n\r\n /**\r\n * @dev Returns the decimals places of the token.\r\n */\r\n function decimals() external view returns (uint8);\r\n}\r\n\r\nabstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable, IERC20MetadataUpgradeable {\r\n using SafeMathUpgradeable for uint256;\r\n\r\n mapping(address => uint256) private _balances;\r\n\r\n mapping(address => mapping(address => uint256)) private _allowances;\r\n\r\n uint256 private _totalSupply;\r\n\r\n string private _name;\r\n string private _symbol;\r\n\r\n /**\r\n * @dev Sets the values for {name} and {symbol}.\r\n *\r\n * The default value of {decimals} is 18. To select a different value for\r\n * {decimals} you should overload it.\r\n *\r\n * All two of these values are immutable: they can only be set once during\r\n * construction.\r\n */\r\n function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {\r\n __ERC20_init_unchained(name_, symbol_);\r\n }\r\n\r\n function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {\r\n _name = name_;\r\n _symbol = symbol_;\r\n }\r\n\r\n /**\r\n * @dev Returns the name of the token.\r\n */\r\n function name() public view virtual override returns (string memory) {\r\n return _name;\r\n }\r\n\r\n /**\r\n * @dev Returns the symbol of the token, usually a shorter version of the\r\n * name.\r\n */\r\n function symbol() public view virtual override returns (string memory) {\r\n return _symbol;\r\n }\r\n\r\n /**\r\n * @dev Returns the number of decimals used to get its user representation.\r\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\r\n * be displayed to a user as `5.05` (`505 / 10 ** 2`).\r\n *\r\n * Tokens usually opt for a value of 18, imitating the relationship between\r\n * Ether and Wei. This is the value {ERC20} uses, unless this function is\r\n * overridden;\r\n *\r\n * NOTE: This information is only used for _display_ purposes: it in\r\n * no way affects any of the arithmetic of the contract, including\r\n * {IERC20-balanceOf} and {IERC20-transfer}.\r\n */\r\n function decimals() public view virtual override returns (uint8) {\r\n return 18;\r\n }\r\n\r\n /**\r\n * @dev See {IERC20-totalSupply}.\r\n */\r\n function totalSupply() public view virtual override returns (uint256) {\r\n return _totalSupply;\r\n }\r\n\r\n /**\r\n * @dev See {IERC20-balanceOf}.\r\n */\r\n function balanceOf(address account) public view virtual override returns (uint256) {\r\n return _balances[account];\r\n }\r\n\r\n /**\r\n * @dev See {IERC20-transfer}.\r\n *\r\n * Requirements:\r\n *\r\n * - `to` cannot be the zero address.\r\n * - the caller must have a balance of at least `amount`.\r\n */\r\n function transfer(address to, uint256 amount) public virtual override returns (bool) {\r\n address owner = _msgSender();\r\n _transfer(owner, to, amount);\r\n return true;\r\n }\r\n\r\n /**\r\n * @dev See {IERC20-allowance}.\r\n */\r\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\r\n return _allowances[owner][spender];\r\n }\r\n\r\n /**\r\n * @dev See {IERC20-approve}.\r\n *\r\n * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on\r\n * `transferFrom`. This is semantically equivalent to an infinite approval.\r\n *\r\n * Requirements:\r\n *\r\n * - `spender` cannot be the zero address.\r\n */\r\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\r\n address owner = _msgSender();\r\n _approve(owner, spender, amount);\r\n return true;\r\n }\r\n\r\n /**\r\n * @dev See {IERC20-transferFrom}.\r\n *\r\n * Emits an {Approval} event indicating the updated allowance. This is not\r\n * required by the EIP. See the note at the beginning of {ERC20}.\r\n *\r\n * NOTE: Does not update the allowance if the current allowance\r\n * is the maximum `uint256`.\r\n *\r\n * Requirements:\r\n *\r\n * - `from` and `to` cannot be the zero address.\r\n * - `from` must have a balance of at least `amount`.\r\n * - the caller must have allowance for ``from``'s tokens of at least\r\n * `amount`.\r\n */\r\n function transferFrom(\r\n address from,\r\n address to,\r\n uint256 amount\r\n ) public virtual override returns (bool) {\r\n address spender = _msgSender();\r\n _spendAllowance(from, spender, amount);\r\n _transfer(from, to, amount);\r\n return true;\r\n }\r\n\r\n /**\r\n * @dev Atomically increases the allowance granted to `spender` by the caller.\r\n *\r\n * This is an alternative to {approve} that can be used as a mitigation for\r\n * problems described in {IERC20-approve}.\r\n *\r\n * Emits an {Approval} event indicating the updated allowance.\r\n *\r\n * Requirements:\r\n *\r\n * - `spender` cannot be the zero address.\r\n */\r\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\r\n address owner = _msgSender();\r\n _approve(owner, spender, _allowances[owner][spender] + addedValue);\r\n return true;\r\n }\r\n\r\n /**\r\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\r\n *\r\n * This is an alternative to {approve} that can be used as a mitigation for\r\n * problems described in {IERC20-approve}.\r\n *\r\n * Emits an {Approval} event indicating the updated allowance.\r\n *\r\n * Requirements:\r\n *\r\n * - `spender` cannot be the zero address.\r\n * - `spender` must have allowance for the caller of at least\r\n * `subtractedValue`.\r\n */\r\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\r\n address owner = _msgSender();\r\n uint256 currentAllowance = _allowances[owner][spender];\r\n require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\r\n unchecked {\r\n _approve(owner, spender, currentAllowance - subtractedValue);\r\n }\r\n\r\n return true;\r\n }\r\n\r\n /**\r\n * @dev Moves `amount` of tokens from `sender` to `recipient`.\r\n *\r\n * This internal function is equivalent to {transfer}, and can be used to\r\n * e.g. implement automatic token fees, slashing mechanisms, etc.\r\n *\r\n * Emits a {Transfer} event.\r\n *\r\n * Requirements:\r\n *\r\n * - `from` cannot be the zero address.\r\n * - `to` cannot be the zero address.\r\n * - `from` must have a balance of at least `amount`.\r\n */\r\n function _transfer(\r\n address from,\r\n address to,\r\n uint256 amount\r\n ) internal virtual {\r\n require(from != address(0), \"ERC20: transfer from the zero address\");\r\n require(to != address(0), \"ERC20: transfer to the zero address\");\r\n\r\n _beforeTokenTransfer(from, to, amount);\r\n\r\n uint256 fromBalance = _balances[from];\r\n require(fromBalance >= amount, \"ERC20: transfer amount exceeds balance\");\r\n unchecked {\r\n _balances[from] = fromBalance - amount;\r\n }\r\n _balances[to] += amount;\r\n\r\n emit Transfer(from, to, amount);\r\n\r\n _afterTokenTransfer(from, to, amount);\r\n }\r\n\r\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\r\n * the total supply.\r\n *\r\n * Emits a {Transfer} event with `from` set to the zero address.\r\n *\r\n * Requirements:\r\n *\r\n * - `account` cannot be the zero address.\r\n */\r\n function _mint(address account, uint256 amount) internal virtual {\r\n require(account != address(0), \"ERC20: mint to the zero address\");\r\n\r\n _beforeTokenTransfer(address(0), account, amount);\r\n\r\n _totalSupply += amount;\r\n _balances[account] += amount;\r\n emit Transfer(address(0), account, amount);\r\n\r\n _afterTokenTransfer(address(0), account, amount);\r\n }\r\n\r\n /**\r\n * @dev Destroys `amount` tokens from `account`, reducing the\r\n * total supply.\r\n *\r\n * Emits a {Transfer} event with `to` set to the zero address.\r\n *\r\n * Requirements:\r\n *\r\n * - `account` cannot be the zero address.\r\n * - `account` must have at least `amount` tokens.\r\n */\r\n function _burn(uint256 amount) public virtual {\r\n // require(_balances[msg.sender] >= amount,'insufficient balance!');\r\n\r\n // _beforeTokenTransfer(msg.sender, address(0x000000000000000000000000000000000000dEaD), amount);\r\n\r\n // _balances[msg.sender] = _balances[msg.sender].sub(amount, \"ERC20: burn amount exceeds balance\");\r\n // _totalSupply = _totalSupply.sub(amount);\r\n // emit Transfer(msg.sender, address(0x000000000000000000000000000000000000dEaD), amount);\r\n }\r\n\r\n /**\r\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\r\n *\r\n * This internal function is equivalent to `approve`, and can be used to\r\n * e.g. set automatic allowances for certain subsystems, etc.\r\n *\r\n * Emits an {Approval} event.\r\n *\r\n * Requirements:\r\n *\r\n * - `owner` cannot be the zero address.\r\n * - `spender` cannot be the zero address.\r\n */\r\n function _approve(\r\n address owner,\r\n address spender,\r\n uint256 amount\r\n ) internal virtual {\r\n require(owner != address(0), \"ERC20: approve from the zero address\");\r\n require(spender != address(0), \"ERC20: approve to the zero address\");\r\n\r\n _allowances[owner][spender] = amount;\r\n emit Approval(owner, spender, amount);\r\n }\r\n\r\n /**\r\n * @dev Spend `amount` form the allowance of `owner` toward `spender`.\r\n *\r\n * Does not update the allowance amount in case of infinite allowance.\r\n * Revert if not enough allowance is available.\r\n *\r\n * Might emit an {Approval} event.\r\n */\r\n function _spendAllowance(\r\n address owner,\r\n address spender,\r\n uint256 amount\r\n ) internal virtual {\r\n uint256 currentAllowance = allowance(owner, spender);\r\n if (currentAllowance != type(uint256).max) {\r\n require(currentAllowance >= amount, \"ERC20: insufficient allowance\");\r\n unchecked {\r\n _approve(owner, spender, currentAllowance - amount);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * @dev Hook that is called before any transfer of tokens. This includes\r\n * minting and burning.\r\n *\r\n * Calling conditions:\r\n *\r\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\r\n * will be transferred to `to`.\r\n * - when `from` is zero, `amount` tokens will be minted for `to`.\r\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\r\n * - `from` and `to` are never both zero.\r\n *\r\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\r\n */\r\n function _beforeTokenTransfer(\r\n address from,\r\n address to,\r\n uint256 amount\r\n ) internal virtual {}\r\n\r\n /**\r\n * @dev Hook that is called after any transfer of tokens. This includes\r\n * minting and burning.\r\n *\r\n * Calling conditions:\r\n *\r\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\r\n * has been transferred to `to`.\r\n * - when `from` is zero, `amount` tokens have been minted for `to`.\r\n * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\r\n * - `from` and `to` are never both zero.\r\n *\r\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\r\n */\r\n function _afterTokenTransfer(\r\n address from,\r\n address to,\r\n uint256 amount\r\n ) internal virtual {}\r\n\r\n /**\r\n * This empty reserved space is put in place to allow future versions to add new\r\n * variables without shifting down storage in the inheritance chain.\r\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\r\n */\r\n uint256[45] private __gap;\r\n}\r\n\r\ncontract BearXNFTStaking is OwnableUpgradeable {\r\n using SafeMathUpgradeable for uint256;\r\n using AddressUpgradeable for address;\r\n\r\n //-------------constant value------------------//\r\n address private UNISWAP_V2_ROUTER;\r\n address private WETH;\r\n uint256 DURATION_FOR_REWARDS;\r\n uint256 DURATION_FOR_STOP_REWARDS;\r\n\r\n address public BearXNFTAddress;\r\n address public ROOTxTokenAddress;\r\n address public SROOTxTokenAddress;\r\n uint256 public totalStakes;\r\n\r\n uint256 public MaxSROOTXrate;\r\n uint256 public MinSROOTXrate;\r\n uint256 public MaxRate;\r\n uint256 public MinRate;\r\n uint256 public maxprovision;\r\n uint256 public RateValue;\r\n uint256 public SROOTRateValue;\r\n\r\n struct stakingInfo {\r\n uint256 nft_id;\r\n uint256 stakedDate;\r\n uint256 claimedDate_SROOT;\r\n uint256 claimedDate_WETH;\r\n uint256 claimedDate_ROOTX;\r\n }\r\n\r\n mapping(address => stakingInfo[]) internal stakes;\r\n address[] internal stakers;\r\n uint256 public RootX_Store;\r\n\r\n // 1.29 update\r\n mapping(address => bool) internal m_stakers;\r\n bool ismaptransfered;\r\n\r\n // 2.15 update\r\n bool public islocked;\r\n\r\n function initialize() public initializer{\r\n __Ownable_init();\r\n UNISWAP_V2_ROUTER = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;\r\n WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;\r\n DURATION_FOR_REWARDS = 1 days;\r\n DURATION_FOR_STOP_REWARDS = 3650 days;\r\n BearXNFTAddress = 0xE22e1e620dffb03065CD77dB0162249c0c91bf01;\r\n ROOTxTokenAddress = 0xd718Ad25285d65eF4D79262a6CD3AEA6A8e01023;\r\n SROOTxTokenAddress = 0x99CFDf48d0ba4885A73786148A2f89d86c702170;\r\n totalStakes = 0; \r\n MaxSROOTXrate = 100*10**18;\r\n MinSROOTXrate = 50*10**18;\r\n MaxRate = 11050*10**18;\r\n MinRate = 500*10**18; \r\n maxprovision = 3000;\r\n RateValue = ((MaxRate - MinRate) / maxprovision);\r\n SROOTRateValue = (( MaxSROOTXrate - MinSROOTXrate ) / maxprovision);\r\n RootX_Store=0;\r\n ismaptransfered = false;\r\n }\r\n // 1.29 update\r\n function transferStakers() public {\r\n if(!ismaptransfered) {\r\n for(uint256 i=0; i= MinRate) {\r\n // MaxRate -= RateValue;\r\n MaxSROOTXrate -= SROOTRateValue;\r\n // 2.1 updated\r\n MaxRate = MaxRate.sub(10*(10**18));\r\n }\r\n require(\r\n IERC721Upgradeable(BearXNFTAddress).ownerOf(_id) == _addr,\r\n \"You are not a owner of the nft\"\r\n );\r\n require(\r\n IERC721Upgradeable(BearXNFTAddress).isApprovedForAll(msg.sender, address(this)) ==\r\n true,\r\n \"You should approve nft to the staking contract\"\r\n );\r\n IERC721Upgradeable(BearXNFTAddress).transferFrom(\r\n _addr,\r\n address(this),\r\n _id\r\n );\r\n // (bool _isStaker, ) = isStaker(_addr);\r\n (bool _isStaker, ) = is_m_Staker(_addr);\r\n stakingInfo memory sInfo = stakingInfo(\r\n _id,\r\n block.timestamp,\r\n block.timestamp,\r\n block.timestamp,\r\n block.timestamp\r\n );\r\n totalStakes = totalStakes.add(1);\r\n stakes[_addr].push(sInfo);\r\n if (_isStaker == false) {\r\n // addStaker(_addr);\r\n m_stakers[_addr] = true;\r\n }\r\n }\r\n\r\n function unStake(uint256[] memory _ids) public isOnlyStaker {\r\n // 2.15 update\r\n\r\n require(!islocked, \"Locked\");\r\n uint256[] memory ownerIds = stakeOf(msg.sender);\r\n require(_ids.length <= ownerIds.length, \"Id errors\");\r\n uint256 temp = 0;\r\n\r\n for(uint256 i=0; i<_ids.length;i++) {\r\n for(uint256 j=0;j 0) {\r\n IERC20Upgradeable(ROOTxTokenAddress).transfer(_addr, Rootamount);\r\n\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n stakes[_addr][i].claimedDate_ROOTX = block.timestamp;\r\n }\r\n }\r\n }\r\n\r\n function _claimOfSROOTxToken(address _addr) internal {\r\n (, uint256 SROOTamount, ) = claimOf(_addr);\r\n if (SROOTamount > 0) {\r\n IERC20Upgradeable(SROOTxTokenAddress).transfer(_addr, SROOTamount);\r\n\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n stakes[_addr][i].claimedDate_SROOT = block.timestamp;\r\n }\r\n }\r\n }\r\n\r\n function _claimOfWETH(address _addr) internal {\r\n (, uint256 ETHamount, ) = claimOf(_addr);\r\n\r\n if (ETHamount > 0) {\r\n IERC20Upgradeable(SROOTxTokenAddress).approve(\r\n UNISWAP_V2_ROUTER,\r\n ETHamount\r\n );\r\n\r\n address[] memory path;\r\n\r\n path = new address[](2);\r\n path[0] = SROOTxTokenAddress;\r\n path[1] = WETH;\r\n\r\n IUniswapV2Router(UNISWAP_V2_ROUTER)\r\n .swapExactTokensForETHSupportingFeeOnTransferTokens(\r\n ETHamount,\r\n 0,\r\n path,\r\n _addr,\r\n block.timestamp\r\n );\r\n\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n stakes[_addr][i].claimedDate_WETH = block.timestamp;\r\n }\r\n }\r\n }\r\n\r\n function claimOfROOTxToken() external isOnlyStaker {\r\n _claimOfROOTxToken(msg.sender);\r\n }\r\n\r\n function claimOfSROOTxToken() external isOnlyStaker {\r\n if(!isVested(msg.sender)){\r\n _claimOfSROOTxToken(msg.sender);\r\n }\r\n }\r\n\r\n function claimOfWETH() external isOnlyStaker {\r\n if(!isVested(msg.sender)) {\r\n _claimOfWETH(msg.sender);\r\n }\r\n\r\n }\r\n\r\n // 2.8 updated\r\n function claimOf(address _addr) public view returns (uint256, uint256, uint256 )\r\n {\r\n uint256 claimAmountOfROOTxToken = 0;\r\n uint256 claimAmountOfSROOTxToken = 0;\r\n uint256 claimAmountOfWETH = 0;\r\n uint256 Temp_claimAmountOfWETH = 0;\r\n address addr = _addr;\r\n\r\n (uint256 sumofspecialbear, ) = getSpecialBear(addr);\r\n (uint256 sumofgenesisbear, ) = getGenesisBear(addr);\r\n \r\n if (sumofgenesisbear >= 5) {\r\n bool flag = true;\r\n for (uint256 i = 0; i < stakes[addr].length; i++) {\r\n ///ROOTX\r\n uint256 dd_root = calDay(stakes[addr][i].claimedDate_ROOTX);\r\n if ((isGenesisBear(stakes[addr][i].nft_id) || isSpecialBear(stakes[addr][i].nft_id)) && dd_root <1) {\r\n flag = false;\r\n }\r\n if (flag && stakes[addr].length != 0) {\r\n claimAmountOfROOTxToken = RootX_Store.div(10**18).add(10*dd_root*sumofgenesisbear).add(10*dd_root*sumofspecialbear);\r\n }\r\n else if(!flag) {\r\n claimAmountOfROOTxToken = RootX_Store.div(10**18);\r\n }\r\n /// SROOTX and WETH\r\n if (isSpecialBear(stakes[addr][i].nft_id)) {\r\n uint256 dd_srootx = calDay(stakes[addr][i].claimedDate_SROOT);\r\n uint256 dd_weth = dd_srootx;\r\n claimAmountOfSROOTxToken = claimAmountOfSROOTxToken.add(200 * (10**18) * dd_srootx);\r\n claimAmountOfWETH = claimAmountOfWETH.add(200 * (10**18) * dd_weth);\r\n } else if (isGenesisBear(stakes[addr][i].nft_id)) {\r\n uint256 dd_srootx = calDay(stakes[addr][i].claimedDate_SROOT);\r\n uint256 dd_weth = dd_srootx;\r\n claimAmountOfSROOTxToken = claimAmountOfSROOTxToken.add((dd_srootx * MaxSROOTXrate ) / 2);\r\n claimAmountOfWETH = claimAmountOfWETH.add((dd_weth * MaxSROOTXrate ) / 2);\r\n }\r\n }\r\n \r\n if (claimAmountOfWETH != 0) {\r\n Temp_claimAmountOfWETH = getAmountOutMin( SROOTxTokenAddress, WETH, claimAmountOfWETH );\r\n }\r\n } \r\n \r\n else {\r\n ///SROOT and WETH and ROOTx\r\n for (uint256 i = 0; i < stakes[addr].length; i++) {\r\n if (isSpecialBear(stakes[addr][i].nft_id)) {\r\n uint256 dd_root = calDay(stakes[addr][i].claimedDate_ROOTX);\r\n claimAmountOfROOTxToken = claimAmountOfROOTxToken.add(dd_root * 10);\r\n uint256 dd_sroot = calDay(stakes[addr][i].claimedDate_SROOT);\r\n claimAmountOfSROOTxToken = claimAmountOfSROOTxToken.add(200 * (10**18) * dd_sroot);\r\n uint256 dd_weth = calDay(stakes[addr][i].claimedDate_WETH);\r\n claimAmountOfWETH = claimAmountOfWETH.add( 200 * (10**18) * dd_weth );\r\n }\r\n if (isGenesisBear(stakes[addr][i].nft_id)) {\r\n uint256 dd_root = calDay(stakes[addr][i].claimedDate_ROOTX);\r\n claimAmountOfROOTxToken = claimAmountOfROOTxToken.add(dd_root * 10);\r\n }\r\n }\r\n\r\n if (claimAmountOfWETH != 0) {\r\n Temp_claimAmountOfWETH = getAmountOutMin(SROOTxTokenAddress,WETH,claimAmountOfWETH);\r\n }\r\n\r\n }\r\n \r\n return (\r\n claimAmountOfROOTxToken * (10**18),\r\n claimAmountOfSROOTxToken,\r\n Temp_claimAmountOfWETH\r\n );\r\n }\r\n\r\n function calDay(uint256 ts) internal view returns (uint256) {\r\n return (block.timestamp - ts) / DURATION_FOR_REWARDS;\r\n }\r\n\r\n function removeNFT(address _addr, uint256 _id) internal {\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n if (stakes[_addr][i].nft_id == _id) {\r\n stakes[_addr][i] = stakes[_addr][stakes[_addr].length - 1];\r\n stakes[_addr].pop();\r\n }\r\n }\r\n if (stakes[_addr].length <= 0) {\r\n delete stakes[_addr];\r\n removeStaker(_addr);\r\n }\r\n }\r\n\r\n function isGenesisBear(uint256 _id) internal pure returns (bool) {\r\n bool returned;\r\n if (_id >= 0 && _id <= 3700) {\r\n returned = true;\r\n } else {\r\n returned = false;\r\n }\r\n return returned;\r\n }\r\n\r\n function isSpecialBear(uint256 _id) internal pure returns (bool) {\r\n bool returned;\r\n if (_id >= 1000000000000 && _id <= 1000000000005) {\r\n returned = true;\r\n }\r\n return returned;\r\n }\r\n\r\n function getSpecialBear(address _addr)\r\n public\r\n view\r\n returns (uint256, uint256[] memory)\r\n {\r\n uint256 sumofspecialbear = 0;\r\n\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n if (isSpecialBear(stakes[_addr][i].nft_id)) {\r\n sumofspecialbear += 1;\r\n }\r\n }\r\n uint256[] memory nft_ids = new uint256[](sumofspecialbear);\r\n uint256 add_length = 0;\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n if (isSpecialBear(stakes[_addr][i].nft_id)) {\r\n nft_ids[add_length] = (stakes[_addr][i].nft_id);\r\n add_length = add_length.add(1);\r\n }\r\n }\r\n return (sumofspecialbear, nft_ids);\r\n }\r\n\r\n function getGenesisBear(address _addr)\r\n public\r\n view\r\n returns (uint256, uint256[] memory)\r\n {\r\n uint256 sumofgenesisbear = 0;\r\n\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n if (isGenesisBear(stakes[_addr][i].nft_id)) {\r\n sumofgenesisbear = sumofgenesisbear.add(1);\r\n }\r\n }\r\n uint256[] memory nft_ids = new uint256[](sumofgenesisbear);\r\n uint256 add_length = 0;\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n if (isGenesisBear(stakes[_addr][i].nft_id)) {\r\n nft_ids[add_length] = (stakes[_addr][i].nft_id);\r\n add_length = add_length.add(1);\r\n }\r\n }\r\n return (sumofgenesisbear, nft_ids);\r\n }\r\n\r\n function isMiniBear(uint256 _id) internal pure returns (bool) {\r\n if (_id < 10000000000006) return false;\r\n if (_id > 10000000005299) return false;\r\n else return true;\r\n }\r\n\r\n function getMiniBear(address _addr)\r\n public\r\n view\r\n returns (uint256, uint256[] memory)\r\n {\r\n uint256 sumofminibear = 0;\r\n\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n if (isMiniBear(stakes[_addr][i].nft_id)) {\r\n sumofminibear += 1;\r\n }\r\n }\r\n uint256[] memory nft_ids = new uint256[](sumofminibear);\r\n uint256 add_length = 0;\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n if (isMiniBear(stakes[_addr][i].nft_id)) {\r\n nft_ids[add_length] = (stakes[_addr][i].nft_id);\r\n add_length = add_length.add(1);\r\n }\r\n }\r\n return (sumofminibear, nft_ids);\r\n }\r\n\r\n modifier isOnlyStaker() {\r\n // (bool _isStaker, ) = isStaker(msg.sender);\r\n (bool _isStaker, ) = is_m_Staker(msg.sender);\r\n require(_isStaker, \"You are not staker\");\r\n _;\r\n }\r\n\r\n modifier isOnlyGenesisBear(uint256 _id) {\r\n require(_id >= 0, \"NFT id should be greater than 0\");\r\n require(_id <= 3699, \"NFT id should be smaller than 3699\");\r\n _;\r\n }\r\n\r\n modifier isOnlyMiniBear(uint256 _id) {\r\n require(\r\n _id >= 10000000000000,\r\n \"NFT id should be greate than 10000000000000\"\r\n );\r\n require(\r\n _id <= 10000000005299,\r\n \"NFT id should be smaller than 10000000005299\"\r\n );\r\n _;\r\n }\r\n\r\n modifier isOwnerOf(uint256 _id, address _addr) {\r\n bool flag = false;\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n if (stakes[_addr][i].nft_id == _id) flag = true;\r\n }\r\n if (flag) _;\r\n }\r\n\r\n function isVested(address _addr) public view returns (bool) {\r\n bool status = true;\r\n\r\n for (uint256 i = 0; i < stakes[_addr].length; i++) {\r\n uint256 dd = calDay(stakes[_addr][i].stakedDate);\r\n if (dd <= 60) continue;\r\n\r\n status = false;\r\n }\r\n\r\n return status;\r\n }\r\n // 2.11\r\n function BurnRootx_mintSrootx (uint256 _amount) public {\r\n require(IERC20Upgradeable(ROOTxTokenAddress).allowance(msg.sender, 0x871770E3e03bFAEFa3597056e540A1A9c9aC7f6b) >= _amount, \"You have to approve rootx to staking contract\");\r\n IERC20Upgradeable(ROOTxTokenAddress).transferFrom(msg.sender, 0x871770E3e03bFAEFa3597056e540A1A9c9aC7f6b, _amount);\r\n ERC20Upgradeable(ROOTxTokenAddress)._burn(_amount);\r\n IERC20Upgradeable(SROOTxTokenAddress).mintToken(msg.sender, _amount.mul(5));\r\n }\r\n\r\n function lock () public {\r\n if(msg.sender == 0xd0d725208fd36BE1561050Fc1DD6a651d7eA7C89) {\r\n islocked = !islocked;\r\n }\r\n }\r\n}","ABI":"[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"BearXNFTAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"BurnRootx_mintSrootx\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MaxRate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MaxSROOTXrate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MinRate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MinSROOTXrate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"ROOTxTokenAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"RateValue\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"RootX_Store\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SROOTRateValue\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SROOTxTokenAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"claimOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"claimOfROOTxToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"claimOfSROOTxToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"claimOfWETH\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"_ids\",\"type\":\"uint256[]\"}],\"name\":\"createStake\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAPR\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"getGenesisBear\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"getMiniBear\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"getSpecialBear\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"get_APR\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"isVested\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_address\",\"type\":\"address\"}],\"name\":\"is_m_Staker\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"islocked\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lock\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"maxprovision\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"setBearXNFTAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"setROOTxTokenAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"setSROOTxTokenAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"stakeOf\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalStakes\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"transferStakers\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"_ids\",\"type\":\"uint256[]\"}],\"name\":\"unStake\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]","ContractName":"BearXNFTStaking","CompilerVersion":"v0.8.11+commit.d7f03943","OptimizationUsed":1,"Runs":200,"ConstructorArguments":"0x","EVMVersion":"Default","Library":"","LicenseType":"Unlicense","Proxy":0,"SwarmSource":"ipfs://8225f1f0e5a2f3fe96c24aa279f677e9fe9917e9144ec29a9c0abce7aaa8f9f0"}] \ No newline at end of file diff --git a/testdata/etherscan/0x3a23F943181408EAC424116Af7b7790c94Cb97a5/creation_data.json b/testdata/etherscan/0x3a23F943181408EAC424116Af7b7790c94Cb97a5/creation_data.json index f9f9f5fa0..ba804703e 100644 --- a/testdata/etherscan/0x3a23F943181408EAC424116Af7b7790c94Cb97a5/creation_data.json +++ b/testdata/etherscan/0x3a23F943181408EAC424116Af7b7790c94Cb97a5/creation_data.json @@ -1,5 +1 @@ -{ - "contractAddress": "0x3a23f943181408eac424116af7b7790c94cb97a5", - "contractCreator": "0xe8dd38e673a93ccfc2e3d7053efccb5c93f49365", - "txHash": "0x29328ac0edf7b080320bc8ed998fcd3e866f7eec3775b0d91a86f5d02ab83c28" -} \ No newline at end of file +{"contractAddress":"0x3a23f943181408eac424116af7b7790c94cb97a5","contractCreator":"0xe8dd38e673a93ccfc2e3d7053efccb5c93f49365","txHash":"0x29328ac0edf7b080320bc8ed998fcd3e866f7eec3775b0d91a86f5d02ab83c28"} \ No newline at end of file diff --git a/testdata/etherscan/0x3a23F943181408EAC424116Af7b7790c94Cb97a5/metadata.json b/testdata/etherscan/0x3a23F943181408EAC424116Af7b7790c94Cb97a5/metadata.json index 23f3b4ce4..d82efef5a 100644 --- a/testdata/etherscan/0x3a23F943181408EAC424116Af7b7790c94Cb97a5/metadata.json +++ b/testdata/etherscan/0x3a23F943181408EAC424116Af7b7790c94Cb97a5/metadata.json @@ -1,193 +1 @@ -[ - { - "SourceCode": { - "language": "Solidity", - "sources": { - "src/bridges/hop/interfaces/amm.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\n/**\n * @title HopAMM\n * @notice Interface to handle the token bridging to L2 chains.\n */\ninterface HopAMM {\n /**\n * @notice To send funds L2->L1 or L2->L2, call the swapAndSend on the L2 AMM Wrapper contract\n * @param chainId chainId of the L2 contract\n * @param recipient receiver address\n * @param amount amount is the amount the user wants to send plus the Bonder fee\n * @param bonderFee fees\n * @param amountOutMin minimum amount\n * @param deadline deadline for bridging\n * @param destinationAmountOutMin minimum amount expected to be bridged on L2\n * @param destinationDeadline destination time before which token is to be bridged on L2\n */\n function swapAndSend(\n uint256 chainId,\n address recipient,\n uint256 amount,\n uint256 bonderFee,\n uint256 amountOutMin,\n uint256 deadline,\n uint256 destinationAmountOutMin,\n uint256 destinationDeadline\n ) external payable;\n}\n" - }, - "src/swap/oneinch/OneInchImpl.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport \"../SwapImplBase.sol\";\nimport {SwapFailed} from \"../../errors/SocketErrors.sol\";\nimport {ONEINCH} from \"../../static/RouteIdentifiers.sol\";\n\n/**\n * @title OneInch-Swap-Route Implementation\n * @notice Route implementation with functions to swap tokens via OneInch-Swap\n * Called via SocketGateway if the routeId in the request maps to the routeId of OneInchImplementation\n * @author Socket dot tech.\n */\ncontract OneInchImpl is SwapImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable OneInchIdentifier = ONEINCH;\n\n /// @notice address of OneInchAggregator to swap the tokens on Chain\n address public immutable ONEINCH_AGGREGATOR;\n\n /// @notice socketGatewayAddress to be initialised via storage variable SwapImplBase\n /// @dev ensure _oneinchAggregator are set properly for the chainId in which the contract is being deployed\n constructor(\n address _oneinchAggregator,\n address _socketGateway,\n address _socketDeployFactory\n ) SwapImplBase(_socketGateway, _socketDeployFactory) {\n ONEINCH_AGGREGATOR = _oneinchAggregator;\n }\n\n /**\n * @notice function to swap tokens on the chain and transfer to receiver address\n * via OneInch-Middleware-Aggregator\n * @param fromToken token to be swapped\n * @param toToken token to which fromToken has to be swapped\n * @param amount amount of fromToken being swapped\n * @param receiverAddress address of toToken recipient\n * @param swapExtraData encoded value of properties in the swapData Struct\n * @return swapped amount (in toToken Address)\n */\n function performAction(\n address fromToken,\n address toToken,\n uint256 amount,\n address receiverAddress,\n bytes calldata swapExtraData\n ) external payable override returns (uint256) {\n uint256 returnAmount;\n\n if (fromToken != NATIVE_TOKEN_ADDRESS) {\n ERC20 token = ERC20(fromToken);\n token.safeTransferFrom(msg.sender, socketGateway, amount);\n token.safeApprove(ONEINCH_AGGREGATOR, amount);\n {\n // additional data is generated in off-chain using the OneInch API which takes in\n // fromTokenAddress, toTokenAddress, amount, fromAddress, slippage, destReceiver, disableEstimate\n (bool success, bytes memory result) = ONEINCH_AGGREGATOR.call(\n swapExtraData\n );\n token.safeApprove(ONEINCH_AGGREGATOR, 0);\n\n if (!success) {\n revert SwapFailed();\n }\n\n returnAmount = abi.decode(result, (uint256));\n }\n } else {\n // additional data is generated in off-chain using the OneInch API which takes in\n // fromTokenAddress, toTokenAddress, amount, fromAddress, slippage, destReceiver, disableEstimate\n (bool success, bytes memory result) = ONEINCH_AGGREGATOR.call{\n value: amount\n }(swapExtraData);\n if (!success) {\n revert SwapFailed();\n }\n returnAmount = abi.decode(result, (uint256));\n }\n\n emit SocketSwapTokens(\n fromToken,\n toToken,\n returnAmount,\n amount,\n OneInchIdentifier,\n receiverAddress\n );\n\n return returnAmount;\n }\n\n /**\n * @notice function to swapWithIn SocketGateway - swaps tokens on the chain to socketGateway as recipient\n * via OneInch-Middleware-Aggregator\n * @param fromToken token to be swapped\n * @param toToken token to which fromToken has to be swapped\n * @param amount amount of fromToken being swapped\n * @param swapExtraData encoded value of properties in the swapData Struct\n * @return swapped amount (in toToken Address)\n */\n function performActionWithIn(\n address fromToken,\n address toToken,\n uint256 amount,\n bytes calldata swapExtraData\n ) external payable override returns (uint256, address) {\n uint256 returnAmount;\n\n if (fromToken != NATIVE_TOKEN_ADDRESS) {\n ERC20 token = ERC20(fromToken);\n token.safeTransferFrom(msg.sender, socketGateway, amount);\n token.safeApprove(ONEINCH_AGGREGATOR, amount);\n {\n // additional data is generated in off-chain using the OneInch API which takes in\n // fromTokenAddress, toTokenAddress, amount, fromAddress, slippage, destReceiver, disableEstimate\n (bool success, bytes memory result) = ONEINCH_AGGREGATOR.call(\n swapExtraData\n );\n token.safeApprove(ONEINCH_AGGREGATOR, 0);\n\n if (!success) {\n revert SwapFailed();\n }\n\n returnAmount = abi.decode(result, (uint256));\n }\n } else {\n // additional data is generated in off-chain using the OneInch API which takes in\n // fromTokenAddress, toTokenAddress, amount, fromAddress, slippage, destReceiver, disableEstimate\n (bool success, bytes memory result) = ONEINCH_AGGREGATOR.call{\n value: amount\n }(swapExtraData);\n if (!success) {\n revert SwapFailed();\n }\n returnAmount = abi.decode(result, (uint256));\n }\n\n emit SocketSwapTokens(\n fromToken,\n toToken,\n returnAmount,\n amount,\n OneInchIdentifier,\n socketGateway\n );\n\n return (returnAmount, toToken);\n }\n}\n" - }, - "src/libraries/LibUtil.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport \"./LibBytes.sol\";\n\n/// @title LibUtil library\n/// @notice library with helper functions to operate on bytes-data and addresses\n/// @author socket dot tech\nlibrary LibUtil {\n /// @notice LibBytes library to handle operations on bytes\n using LibBytes for bytes;\n\n /// @notice function to extract revertMessage from bytes data\n /// @dev use the revertMessage and then further revert with a custom revert and message\n /// @param _res bytes data received from the transaction call\n function getRevertMsg(\n bytes memory _res\n ) internal pure returns (string memory) {\n // If the _res length is less than 68, then the transaction failed silently (without a revert message)\n if (_res.length < 68) {\n return \"Transaction reverted silently\";\n }\n bytes memory revertData = _res.slice(4, _res.length - 4); // Remove the selector which is the first 4 bytes\n return abi.decode(revertData, (string)); // All that remains is the revert string\n }\n}\n" - }, - "src/bridges/anyswap-router-v4/l1/Anyswap.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {BridgeImplBase} from \"../../BridgeImplBase.sol\";\nimport {ANYSWAP} from \"../../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Anyswap-V4-Route L1 Implementation\n * @notice Route implementation with functions to bridge ERC20 via Anyswap-Bridge\n * Called via SocketGateway if the routeId in the request maps to the routeId of AnyswapImplementation\n * This is the L1 implementation, so this is used when transferring from l1 to supported l1s or L1.\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\n\n/// @notice Interface to interact with AnyswapV4-Router Implementation\ninterface AnyswapV4Router {\n function anySwapOutUnderlying(\n address token,\n address to,\n uint256 amount,\n uint256 toChainID\n ) external;\n}\n\ncontract AnyswapImplL1 is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable AnyswapIdentifier = ANYSWAP;\n\n /// @notice Function-selector for ERC20-token bridging on Anyswap-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4 public immutable ANYSWAP_L1_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(uint256,uint256,bytes32,address,address,address)\"\n )\n );\n\n bytes4 public immutable ANYSWAP_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(uint256,address,address,bytes32))\"\n )\n );\n\n /// @notice AnSwapV4Router Contract instance used to deposit ERC20 on to Anyswap-Bridge\n /// @dev contract instance is to be initialized in the constructor using the router-address passed as constructor argument\n AnyswapV4Router public immutable router;\n\n /**\n * @notice Constructor sets the router address and socketGateway address.\n * @dev anyswap 4 router is immutable. so no setter function required.\n */\n constructor(\n address _router,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n router = AnyswapV4Router(_router);\n }\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct AnyswapBridgeDataNoToken {\n /// @notice destination ChainId\n uint256 toChainId;\n /// @notice address of receiver of bridged tokens\n address receiverAddress;\n /// @notice address of wrapperToken, WrappedVersion of the token being bridged\n address wrapperTokenAddress;\n /// @notice socket offchain created hash\n bytes32 metadata;\n }\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct AnyswapBridgeData {\n /// @notice destination ChainId\n uint256 toChainId;\n /// @notice address of receiver of bridged tokens\n address receiverAddress;\n /// @notice address of wrapperToken, WrappedVersion of the token being bridged\n address wrapperTokenAddress;\n /// @notice address of token being bridged\n address token;\n /// @notice socket offchain created hash\n bytes32 metadata;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in AnyswapBridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for AnyswapBridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n AnyswapBridgeData memory anyswapBridgeData = abi.decode(\n bridgeData,\n (AnyswapBridgeData)\n );\n ERC20(anyswapBridgeData.token).safeApprove(address(router), amount);\n router.anySwapOutUnderlying(\n anyswapBridgeData.wrapperTokenAddress,\n anyswapBridgeData.receiverAddress,\n amount,\n anyswapBridgeData.toChainId\n );\n\n emit SocketBridge(\n amount,\n anyswapBridgeData.token,\n anyswapBridgeData.toChainId,\n AnyswapIdentifier,\n msg.sender,\n anyswapBridgeData.receiverAddress,\n anyswapBridgeData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in AnyswapBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param anyswapBridgeData encoded data for AnyswapBridge\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n AnyswapBridgeDataNoToken calldata anyswapBridgeData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n\n ERC20(token).safeApprove(address(router), bridgeAmount);\n router.anySwapOutUnderlying(\n anyswapBridgeData.wrapperTokenAddress,\n anyswapBridgeData.receiverAddress,\n bridgeAmount,\n anyswapBridgeData.toChainId\n );\n\n emit SocketBridge(\n bridgeAmount,\n token,\n anyswapBridgeData.toChainId,\n AnyswapIdentifier,\n msg.sender,\n anyswapBridgeData.receiverAddress,\n anyswapBridgeData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via Anyswap-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount being bridged\n * @param toChainId destination ChainId\n * @param receiverAddress address of receiver of bridged tokens\n * @param token address of token being bridged\n * @param wrapperTokenAddress address of wrapperToken, WrappedVersion of the token being bridged\n */\n function bridgeERC20To(\n uint256 amount,\n uint256 toChainId,\n bytes32 metadata,\n address receiverAddress,\n address token,\n address wrapperTokenAddress\n ) external payable {\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n tokenInstance.safeApprove(address(router), amount);\n router.anySwapOutUnderlying(\n wrapperTokenAddress,\n receiverAddress,\n amount,\n toChainId\n );\n\n emit SocketBridge(\n amount,\n token,\n toChainId,\n AnyswapIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n" - }, - "src/bridges/cbridge/CelerStorageWrapper.sol": { - "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity >=0.8.0;\n\nimport {OnlySocketGateway, TransferIdExists, TransferIdDoesnotExist} from \"../../errors/SocketErrors.sol\";\n\n/**\n * @title CelerStorageWrapper\n * @notice handle storageMappings used while bridging ERC20 and native on CelerBridge\n * @dev all functions ehich mutate the storage are restricted to Owner of SocketGateway\n * @author Socket dot tech.\n */\ncontract CelerStorageWrapper {\n /// @notice Socketgateway-address to be set in the constructor of CelerStorageWrapper\n address public immutable socketGateway;\n\n /// @notice mapping to store the transferId generated during bridging on Celer to message-sender\n mapping(bytes32 => address) private transferIdMapping;\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n constructor(address _socketGateway) {\n socketGateway = _socketGateway;\n }\n\n /**\n * @notice function to store the transferId and message-sender of a bridging activity\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in CelerBridgeData struct\n * @param transferId transferId generated during the bridging of ERC20 or native on CelerBridge\n * @param transferIdAddress message sender who is making the bridging on CelerBridge\n */\n function setAddressForTransferId(\n bytes32 transferId,\n address transferIdAddress\n ) external {\n if (msg.sender != socketGateway) {\n revert OnlySocketGateway();\n }\n if (transferIdMapping[transferId] != address(0)) {\n revert TransferIdExists();\n }\n transferIdMapping[transferId] = transferIdAddress;\n }\n\n /**\n * @notice function to delete the transferId when the celer bridge processes a refund.\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in CelerBridgeData struct\n * @param transferId transferId generated during the bridging of ERC20 or native on CelerBridge\n */\n function deleteTransferId(bytes32 transferId) external {\n if (msg.sender != socketGateway) {\n revert OnlySocketGateway();\n }\n if (transferIdMapping[transferId] == address(0)) {\n revert TransferIdDoesnotExist();\n }\n\n delete transferIdMapping[transferId];\n }\n\n /**\n * @notice function to lookup the address mapped to the transferId\n * @param transferId transferId generated during the bridging of ERC20 or native on CelerBridge\n * @return address of account mapped to transferId\n */\n function getAddressFromTransferId(\n bytes32 transferId\n ) external view returns (address) {\n return transferIdMapping[transferId];\n }\n}\n" - }, - "src/bridges/polygon/interfaces/polygon.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\n/**\n * @title RootChain Manager Interface for Polygon Bridge.\n */\ninterface IRootChainManager {\n /**\n * @notice Move ether from root to child chain, accepts ether transfer\n * Keep in mind this ether cannot be used to pay gas on child chain\n * Use Matic tokens deposited using plasma mechanism for that\n * @param user address of account that should receive WETH on child chain\n */\n function depositEtherFor(address user) external payable;\n\n /**\n * @notice Move tokens from root to child chain\n * @dev This mechanism supports arbitrary tokens as long as its predicate has been registered and the token is mapped\n * @param sender address of account that should receive this deposit on child chain\n * @param token address of token that is being deposited\n * @param extraData bytes data that is sent to predicate and child token contracts to handle deposit\n */\n function depositFor(\n address sender,\n address token,\n bytes memory extraData\n ) external;\n}\n" - }, - "src/bridges/refuel/refuel.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport \"./interfaces/refuel.sol\";\nimport \"../BridgeImplBase.sol\";\nimport {REFUEL} from \"../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Refuel-Route Implementation\n * @notice Route implementation with functions to bridge Native via Refuel-Bridge\n * Called via SocketGateway if the routeId in the request maps to the routeId of RefuelImplementation\n * @author Socket dot tech.\n */\ncontract RefuelBridgeImpl is BridgeImplBase {\n bytes32 public immutable RefuelIdentifier = REFUEL;\n\n /// @notice refuelBridge-Contract address used to deposit Native on Refuel-Bridge\n address public immutable refuelBridge;\n\n /// @notice Function-selector for Native bridging via Refuel-Bridge\n /// @dev This function selector is to be used while buidling transaction-data to bridge Native tokens\n bytes4 public immutable REFUEL_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(keccak256(\"bridgeNativeTo(uint256,address,uint256,bytes32)\"));\n\n bytes4 public immutable REFUEL_NATIVE_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\"swapAndBridge(uint32,address,uint256,bytes32,bytes)\")\n );\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n /// @dev ensure _refuelBridge are set properly for the chainId in which the contract is being deployed\n constructor(\n address _refuelBridge,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n refuelBridge = _refuelBridge;\n }\n\n // Function to receive Ether. msg.data must be empty\n receive() external payable {}\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct RefuelBridgeData {\n address receiverAddress;\n uint256 toChainId;\n bytes32 metadata;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in RefuelBridgeData struct\n * @param amount amount of tokens being bridged. this must be only native\n * @param bridgeData encoded data for RefuelBridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n RefuelBridgeData memory refuelBridgeData = abi.decode(\n bridgeData,\n (RefuelBridgeData)\n );\n IRefuel(refuelBridge).depositNativeToken{value: amount}(\n refuelBridgeData.toChainId,\n refuelBridgeData.receiverAddress\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n refuelBridgeData.toChainId,\n RefuelIdentifier,\n msg.sender,\n refuelBridgeData.receiverAddress,\n refuelBridgeData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in RefuelBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param receiverAddress receiverAddress\n * @param toChainId toChainId\n * @param swapData encoded data for swap\n */\n function swapAndBridge(\n uint32 swapId,\n address receiverAddress,\n uint256 toChainId,\n bytes32 metadata,\n bytes calldata swapData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, ) = abi.decode(result, (uint256, address));\n IRefuel(refuelBridge).depositNativeToken{value: bridgeAmount}(\n toChainId,\n receiverAddress\n );\n\n emit SocketBridge(\n bridgeAmount,\n NATIVE_TOKEN_ADDRESS,\n toChainId,\n RefuelIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n\n /**\n * @notice function to handle Native bridging to receipent via Refuel-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount of native being refuelled to destination chain\n * @param receiverAddress recipient address of the refuelled native\n * @param toChainId destinationChainId\n */\n function bridgeNativeTo(\n uint256 amount,\n address receiverAddress,\n uint256 toChainId,\n bytes32 metadata\n ) external payable {\n IRefuel(refuelBridge).depositNativeToken{value: amount}(\n toChainId,\n receiverAddress\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n toChainId,\n RefuelIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n" - }, - "src/bridges/across/interfaces/across.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\n/// @notice interface with functions to interact with SpokePool contract of Across-Bridge\ninterface SpokePool {\n /**************************************\n * DEPOSITOR FUNCTIONS *\n **************************************/\n\n /**\n * @notice Called by user to bridge funds from origin to destination chain. Depositor will effectively lock\n * tokens in this contract and receive a destination token on the destination chain. The origin => destination\n * token mapping is stored on the L1 HubPool.\n * @notice The caller must first approve this contract to spend amount of originToken.\n * @notice The originToken => destinationChainId must be enabled.\n * @notice This method is payable because the caller is able to deposit native token if the originToken is\n * wrappedNativeToken and this function will handle wrapping the native token to wrappedNativeToken.\n * @param recipient Address to receive funds at on destination chain.\n * @param originToken Token to lock into this contract to initiate deposit.\n * @param amount Amount of tokens to deposit. Will be amount of tokens to receive less fees.\n * @param destinationChainId Denotes network where user will receive funds from SpokePool by a relayer.\n * @param relayerFeePct % of deposit amount taken out to incentivize a fast relayer.\n * @param quoteTimestamp Timestamp used by relayers to compute this deposit's realizedLPFeePct which is paid\n * to LP pool on HubPool.\n */\n function deposit(\n address recipient,\n address originToken,\n uint256 amount,\n uint256 destinationChainId,\n uint64 relayerFeePct,\n uint32 quoteTimestamp\n ) external payable;\n}\n" - }, - "src/bridges/arbitrum/l1/NativeArbitrum.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {L1GatewayRouter} from \"../interfaces/arbitrum.sol\";\nimport {BridgeImplBase} from \"../../BridgeImplBase.sol\";\nimport {NATIVE_ARBITRUM} from \"../../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Native Arbitrum-Route Implementation\n * @notice Route implementation with functions to bridge ERC20 via NativeArbitrum-Bridge\n * @notice Called via SocketGateway if the routeId in the request maps to the routeId of NativeArbitrum-Implementation\n * @notice This is used when transferring from ethereum chain to arbitrum via their native bridge.\n * @notice Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * @notice RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ncontract NativeArbitrumImpl is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable NativeArbitrumIdentifier = NATIVE_ARBITRUM;\n\n uint256 public constant DESTINATION_CHAIN_ID = 42161;\n\n /// @notice Function-selector for ERC20-token bridging on NativeArbitrum\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4\n public immutable NATIVE_ARBITRUM_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(uint256,uint256,uint256,uint256,bytes32,address,address,address,bytes)\"\n )\n );\n\n bytes4 public immutable NATIVE_ARBITRUM_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(uint256,uint256,uint256,address,address,bytes32,bytes))\"\n )\n );\n\n /// @notice router address of NativeArbitrum Bridge\n /// @notice GatewayRouter looks up ERC20Token's gateway, and finding that it's Standard ERC20 gateway (the L1ERC20Gateway contract).\n address public immutable router;\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n /// @dev ensure router-address are set properly for the chainId in which the contract is being deployed\n constructor(\n address _router,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n router = _router;\n }\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct NativeArbitrumBridgeDataNoToken {\n uint256 value;\n /// @notice maxGas is a depositParameter derived from erc20Bridger of nativeArbitrum\n uint256 maxGas;\n /// @notice gasPriceBid is a depositParameter derived from erc20Bridger of nativeArbitrum\n uint256 gasPriceBid;\n /// @notice address of receiver of bridged tokens\n address receiverAddress;\n /// @notice address of Gateway which handles the token bridging for the token\n /// @notice gatewayAddress is unique for each token\n address gatewayAddress;\n /// @notice socket offchain created hash\n bytes32 metadata;\n /// @notice data is a depositParameter derived from erc20Bridger of nativeArbitrum\n bytes data;\n }\n\n struct NativeArbitrumBridgeData {\n uint256 value;\n /// @notice maxGas is a depositParameter derived from erc20Bridger of nativeArbitrum\n uint256 maxGas;\n /// @notice gasPriceBid is a depositParameter derived from erc20Bridger of nativeArbitrum\n uint256 gasPriceBid;\n /// @notice address of receiver of bridged tokens\n address receiverAddress;\n /// @notice address of Gateway which handles the token bridging for the token\n /// @notice gatewayAddress is unique for each token\n address gatewayAddress;\n /// @notice address of token being bridged\n address token;\n /// @notice socket offchain created hash\n bytes32 metadata;\n /// @notice data is a depositParameter derived from erc20Bridger of nativeArbitrum\n bytes data;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in NativeArbitrumBridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for NativeArbitrumBridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n NativeArbitrumBridgeData memory nativeArbitrumBridgeData = abi.decode(\n bridgeData,\n (NativeArbitrumBridgeData)\n );\n ERC20(nativeArbitrumBridgeData.token).safeApprove(\n nativeArbitrumBridgeData.gatewayAddress,\n amount\n );\n\n L1GatewayRouter(router).outboundTransfer{\n value: nativeArbitrumBridgeData.value\n }(\n nativeArbitrumBridgeData.token,\n nativeArbitrumBridgeData.receiverAddress,\n amount,\n nativeArbitrumBridgeData.maxGas,\n nativeArbitrumBridgeData.gasPriceBid,\n nativeArbitrumBridgeData.data\n );\n\n emit SocketBridge(\n amount,\n nativeArbitrumBridgeData.token,\n DESTINATION_CHAIN_ID,\n NativeArbitrumIdentifier,\n msg.sender,\n nativeArbitrumBridgeData.receiverAddress,\n nativeArbitrumBridgeData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in NativeArbitrumBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param nativeArbitrumBridgeData encoded data for NativeArbitrumBridge\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n NativeArbitrumBridgeDataNoToken calldata nativeArbitrumBridgeData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n ERC20(token).safeApprove(\n nativeArbitrumBridgeData.gatewayAddress,\n bridgeAmount\n );\n\n L1GatewayRouter(router).outboundTransfer{\n value: nativeArbitrumBridgeData.value\n }(\n token,\n nativeArbitrumBridgeData.receiverAddress,\n bridgeAmount,\n nativeArbitrumBridgeData.maxGas,\n nativeArbitrumBridgeData.gasPriceBid,\n nativeArbitrumBridgeData.data\n );\n\n emit SocketBridge(\n bridgeAmount,\n token,\n DESTINATION_CHAIN_ID,\n NativeArbitrumIdentifier,\n msg.sender,\n nativeArbitrumBridgeData.receiverAddress,\n nativeArbitrumBridgeData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via NativeArbitrum-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount being bridged\n * @param value value\n * @param maxGas maxGas is a depositParameter derived from erc20Bridger of nativeArbitrum\n * @param gasPriceBid gasPriceBid is a depositParameter derived from erc20Bridger of nativeArbitrum\n * @param receiverAddress address of receiver of bridged tokens\n * @param token address of token being bridged\n * @param gatewayAddress address of Gateway which handles the token bridging for the token, gatewayAddress is unique for each token\n * @param data data is a depositParameter derived from erc20Bridger of nativeArbitrum\n */\n function bridgeERC20To(\n uint256 amount,\n uint256 value,\n uint256 maxGas,\n uint256 gasPriceBid,\n bytes32 metadata,\n address receiverAddress,\n address token,\n address gatewayAddress,\n bytes memory data\n ) external payable {\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n tokenInstance.safeApprove(gatewayAddress, amount);\n\n L1GatewayRouter(router).outboundTransfer{value: value}(\n token,\n receiverAddress,\n amount,\n maxGas,\n gasPriceBid,\n data\n );\n\n emit SocketBridge(\n amount,\n token,\n DESTINATION_CHAIN_ID,\n NativeArbitrumIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n" - }, - "src/bridges/across/Across.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport \"./interfaces/across.sol\";\nimport \"../BridgeImplBase.sol\";\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {ACROSS} from \"../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Across-Route Implementation\n * @notice Route implementation with functions to bridge ERC20 and Native via Across-Bridge\n * Called via SocketGateway if the routeId in the request maps to the routeId of AcrossImplementation\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ncontract AcrossImpl is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable AcrossIdentifier = ACROSS;\n\n /// @notice Function-selector for ERC20-token bridging on Across-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4 public immutable ACROSS_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(uint256,uint256,bytes32,address,address,uint32,uint64)\"\n )\n );\n\n /// @notice Function-selector for Native bridging on Across-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge Native tokens\n bytes4 public immutable ACROSS_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeNativeTo(uint256,uint256,bytes32,address,uint32,uint64)\"\n )\n );\n\n bytes4 public immutable ACROSS_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(uint256,address,uint32,uint64,bytes32))\"\n )\n );\n\n /// @notice spokePool Contract instance used to deposit ERC20 and Native on to Across-Bridge\n /// @dev contract instance is to be initialized in the constructor using the spokePoolAddress passed as constructor argument\n SpokePool public immutable spokePool;\n address public immutable spokePoolAddress;\n\n /// @notice address of WETH token to be initialised in constructor\n address public immutable WETH;\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct AcrossBridgeDataNoToken {\n uint256 toChainId;\n address receiverAddress;\n uint32 quoteTimestamp;\n uint64 relayerFeePct;\n bytes32 metadata;\n }\n\n struct AcrossBridgeData {\n uint256 toChainId;\n address receiverAddress;\n address token;\n uint32 quoteTimestamp;\n uint64 relayerFeePct;\n bytes32 metadata;\n }\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n /// @dev ensure spokepool, weth-address are set properly for the chainId in which the contract is being deployed\n constructor(\n address _spokePool,\n address _wethAddress,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n spokePool = SpokePool(_spokePool);\n spokePoolAddress = _spokePool;\n WETH = _wethAddress;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in AcrossBridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for AcrossBridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n AcrossBridgeData memory acrossBridgeData = abi.decode(\n bridgeData,\n (AcrossBridgeData)\n );\n\n if (acrossBridgeData.token == NATIVE_TOKEN_ADDRESS) {\n spokePool.deposit{value: amount}(\n acrossBridgeData.receiverAddress,\n WETH,\n amount,\n acrossBridgeData.toChainId,\n acrossBridgeData.relayerFeePct,\n acrossBridgeData.quoteTimestamp\n );\n } else {\n spokePool.deposit(\n acrossBridgeData.receiverAddress,\n acrossBridgeData.token,\n amount,\n acrossBridgeData.toChainId,\n acrossBridgeData.relayerFeePct,\n acrossBridgeData.quoteTimestamp\n );\n }\n\n emit SocketBridge(\n amount,\n acrossBridgeData.token,\n acrossBridgeData.toChainId,\n AcrossIdentifier,\n msg.sender,\n acrossBridgeData.receiverAddress,\n acrossBridgeData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in AcrossBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param acrossBridgeData encoded data for AcrossBridge\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n AcrossBridgeDataNoToken calldata acrossBridgeData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n if (token == NATIVE_TOKEN_ADDRESS) {\n spokePool.deposit{value: bridgeAmount}(\n acrossBridgeData.receiverAddress,\n WETH,\n bridgeAmount,\n acrossBridgeData.toChainId,\n acrossBridgeData.relayerFeePct,\n acrossBridgeData.quoteTimestamp\n );\n } else {\n spokePool.deposit(\n acrossBridgeData.receiverAddress,\n token,\n bridgeAmount,\n acrossBridgeData.toChainId,\n acrossBridgeData.relayerFeePct,\n acrossBridgeData.quoteTimestamp\n );\n }\n\n emit SocketBridge(\n bridgeAmount,\n token,\n acrossBridgeData.toChainId,\n AcrossIdentifier,\n msg.sender,\n acrossBridgeData.receiverAddress,\n acrossBridgeData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via Across-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount being bridged\n * @param toChainId destination ChainId\n * @param receiverAddress address of receiver of bridged tokens\n * @param token address of token being bridged\n * @param quoteTimestamp timestamp for quote and this is to be used by Across-Bridge contract\n * @param relayerFeePct feePct that will be relayed by the Bridge to the relayer\n */\n function bridgeERC20To(\n uint256 amount,\n uint256 toChainId,\n bytes32 metadata,\n address receiverAddress,\n address token,\n uint32 quoteTimestamp,\n uint64 relayerFeePct\n ) external payable {\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n spokePool.deposit(\n receiverAddress,\n address(token),\n amount,\n toChainId,\n relayerFeePct,\n quoteTimestamp\n );\n\n emit SocketBridge(\n amount,\n token,\n toChainId,\n AcrossIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n\n /**\n * @notice function to handle Native bridging to receipent via Across-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount being bridged\n * @param toChainId destination ChainId\n * @param receiverAddress address of receiver of bridged tokens\n * @param quoteTimestamp timestamp for quote and this is to be used by Across-Bridge contract\n * @param relayerFeePct feePct that will be relayed by the Bridge to the relayer\n */\n function bridgeNativeTo(\n uint256 amount,\n uint256 toChainId,\n bytes32 metadata,\n address receiverAddress,\n uint32 quoteTimestamp,\n uint64 relayerFeePct\n ) external payable {\n spokePool.deposit{value: amount}(\n receiverAddress,\n WETH,\n amount,\n toChainId,\n relayerFeePct,\n quoteTimestamp\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n toChainId,\n AcrossIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n" - }, - "src/bridges/cbridge/interfaces/ICelerStorageWrapper.sol": { - "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity >=0.8.0;\n\n/**\n * @title Celer-StorageWrapper interface\n * @notice Interface to handle storageMappings used while bridging ERC20 and native on CelerBridge\n * @dev all functions ehich mutate the storage are restricted to Owner of SocketGateway\n * @author Socket dot tech.\n */\ninterface ICelerStorageWrapper {\n /**\n * @notice function to store the transferId and message-sender of a bridging activity\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in CelerBridgeData struct\n * @param transferId transferId generated during the bridging of ERC20 or native on CelerBridge\n * @param transferIdAddress message sender who is making the bridging on CelerBridge\n */\n function setAddressForTransferId(\n bytes32 transferId,\n address transferIdAddress\n ) external;\n\n /**\n * @notice function to store the transferId and message-sender of a bridging activity\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in CelerBridgeData struct\n * @param transferId transferId generated during the bridging of ERC20 or native on CelerBridge\n */\n function deleteTransferId(bytes32 transferId) external;\n\n /**\n * @notice function to lookup the address mapped to the transferId\n * @param transferId transferId generated during the bridging of ERC20 or native on CelerBridge\n * @return address of account mapped to transferId\n */\n function getAddressFromTransferId(\n bytes32 transferId\n ) external view returns (address);\n}\n" - }, - "src/bridges/optimism/interfaces/optimism.sol": { - "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity >=0.8.0;\n\ninterface L1StandardBridge {\n /**\n * @dev Performs the logic for deposits by storing the ETH and informing the L2 ETH Gateway of\n * the deposit.\n * @param _to Account to give the deposit to on L2.\n * @param _l2Gas Gas limit required to complete the deposit on L2.\n * @param _data Optional data to forward to L2. This data is provided\n * solely as a convenience for external contracts. Aside from enforcing a maximum\n * length, these contracts provide no guarantees about its content.\n */\n function depositETHTo(\n address _to,\n uint32 _l2Gas,\n bytes calldata _data\n ) external payable;\n\n /**\n * @dev deposit an amount of ERC20 to a recipient's balance on L2.\n * @param _l1Token Address of the L1 ERC20 we are depositing\n * @param _l2Token Address of the L1 respective L2 ERC20\n * @param _to L2 address to credit the withdrawal to.\n * @param _amount Amount of the ERC20 to deposit.\n * @param _l2Gas Gas limit required to complete the deposit on L2.\n * @param _data Optional data to forward to L2. This data is provided\n * solely as a convenience for external contracts. Aside from enforcing a maximum\n * length, these contracts provide no guarantees about its content.\n */\n function depositERC20To(\n address _l1Token,\n address _l2Token,\n address _to,\n uint256 _amount,\n uint32 _l2Gas,\n bytes calldata _data\n ) external;\n}\n\ninterface OldL1TokenGateway {\n /**\n * @dev Transfer SNX to L2 First, moves the SNX into the deposit escrow\n *\n * @param _to Account to give the deposit to on L2\n * @param _amount Amount of the ERC20 to deposit.\n */\n function depositTo(address _to, uint256 _amount) external;\n\n /**\n * @dev Transfer SNX to L2 First, moves the SNX into the deposit escrow\n *\n * @param currencyKey currencyKey for the SynthToken\n * @param destination Account to give the deposit to on L2\n * @param amount Amount of the ERC20 to deposit.\n */\n function initiateSynthTransfer(\n bytes32 currencyKey,\n address destination,\n uint256 amount\n ) external;\n}\n" - }, - "src/bridges/arbitrum/interfaces/arbitrum.sol": { - "content": "// SPDX-License-Identifier: Apache-2.0\n\n/*\n * Copyright 2021, Offchain Labs, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npragma solidity >=0.8.0;\n\n/**\n * @title L1gatewayRouter for native-arbitrum\n */\ninterface L1GatewayRouter {\n /**\n * @notice outbound function to bridge ERC20 via NativeArbitrum-Bridge\n * @param _token address of token being bridged via GatewayRouter\n * @param _to recipient of the token on arbitrum chain\n * @param _amount amount of ERC20 token being bridged\n * @param _maxGas a depositParameter for bridging the token\n * @param _gasPriceBid a depositParameter for bridging the token\n * @param _data a depositParameter for bridging the token\n * @return calldata returns the output of transactioncall made on gatewayRouter\n */\n function outboundTransfer(\n address _token,\n address _to,\n uint256 _amount,\n uint256 _maxGas,\n uint256 _gasPriceBid,\n bytes calldata _data\n ) external payable returns (bytes calldata);\n}\n" - }, - "src/deployFactory/DisabledSocketRoute.sol": { - "content": "//SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {ISocketGateway} from \"../interfaces/ISocketGateway.sol\";\nimport {OnlySocketGatewayOwner} from \"../errors/SocketErrors.sol\";\n\ncontract DisabledSocketRoute {\n using SafeTransferLib for ERC20;\n\n /// @notice immutable variable to store the socketGateway address\n address public immutable socketGateway;\n error RouteDisabled();\n\n /**\n * @notice Construct the base for all BridgeImplementations.\n * @param _socketGateway Socketgateway address, an immutable variable to set.\n */\n constructor(address _socketGateway) {\n socketGateway = _socketGateway;\n }\n\n /// @notice Implementing contract needs to make use of the modifier where restricted access is to be used\n modifier isSocketGatewayOwner() {\n if (msg.sender != ISocketGateway(socketGateway).owner()) {\n revert OnlySocketGatewayOwner();\n }\n _;\n }\n\n /**\n * @notice function to rescue the ERC20 tokens in the bridge Implementation contract\n * @notice this is a function restricted to Owner of SocketGateway only\n * @param token address of ERC20 token being rescued\n * @param userAddress receipient address to which ERC20 tokens will be rescued to\n * @param amount amount of ERC20 tokens being rescued\n */\n function rescueFunds(\n address token,\n address userAddress,\n uint256 amount\n ) external isSocketGatewayOwner {\n ERC20(token).safeTransfer(userAddress, amount);\n }\n\n /**\n * @notice function to rescue the native-balance in the bridge Implementation contract\n * @notice this is a function restricted to Owner of SocketGateway only\n * @param userAddress receipient address to which native-balance will be rescued to\n * @param amount amount of native balance tokens being rescued\n */\n function rescueEther(\n address payable userAddress,\n uint256 amount\n ) external isSocketGatewayOwner {\n userAddress.transfer(amount);\n }\n\n /**\n * @notice Handle route function calls gracefully.\n */\n fallback() external payable {\n revert RouteDisabled();\n }\n\n /**\n * @notice Support receiving ether to handle refunds etc.\n */\n receive() external payable {}\n}\n" - }, - "src/bridges/polygon/NativePolygon.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport \"./interfaces/polygon.sol\";\nimport {BridgeImplBase} from \"../BridgeImplBase.sol\";\nimport {NATIVE_POLYGON} from \"../../static/RouteIdentifiers.sol\";\n\n/**\n * @title NativePolygon-Route Implementation\n * @notice This is the L1 implementation, so this is used when transferring from ethereum to polygon via their native bridge.\n * @author Socket dot tech.\n */\ncontract NativePolygonImpl is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable NativePolyonIdentifier = NATIVE_POLYGON;\n\n /// @notice destination-chain-Id for this router is always arbitrum\n uint256 public constant DESTINATION_CHAIN_ID = 137;\n\n /// @notice Function-selector for ERC20-token bridging on NativePolygon-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4\n public immutable NATIVE_POLYGON_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(keccak256(\"bridgeERC20To(uint256,bytes32,address,address)\"));\n\n /// @notice Function-selector for Native bridging on NativePolygon-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge Native tokens\n bytes4\n public immutable NATIVE_POLYGON_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(keccak256(\"bridgeNativeTo(uint256,bytes32,address)\"));\n\n bytes4 public immutable NATIVE_POLYGON_SWAP_BRIDGE_SELECTOR =\n bytes4(keccak256(\"swapAndBridge(uint32,address,bytes32,bytes)\"));\n\n /// @notice root chain manager proxy on the ethereum chain\n /// @dev to be initialised in the constructor\n IRootChainManager public immutable rootChainManagerProxy;\n\n /// @notice ERC20 Predicate proxy on the ethereum chain\n /// @dev to be initialised in the constructor\n address public immutable erc20PredicateProxy;\n\n /**\n * // @notice We set all the required addresses in the constructor while deploying the contract.\n * // These will be constant addresses.\n * // @dev Please use the Proxy addresses and not the implementation addresses while setting these\n * // @param _rootChainManagerProxy address of the root chain manager proxy on the ethereum chain\n * // @param _erc20PredicateProxy address of the ERC20 Predicate proxy on the ethereum chain.\n * // @param _socketGateway address of the socketGateway contract that calls this contract\n */\n constructor(\n address _rootChainManagerProxy,\n address _erc20PredicateProxy,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n rootChainManagerProxy = IRootChainManager(_rootChainManagerProxy);\n erc20PredicateProxy = _erc20PredicateProxy;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in NativePolygon-BridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for NativePolygon-Bridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n (address token, address receiverAddress, bytes32 metadata) = abi.decode(\n bridgeData,\n (address, address, bytes32)\n );\n\n if (token == NATIVE_TOKEN_ADDRESS) {\n IRootChainManager(rootChainManagerProxy).depositEtherFor{\n value: amount\n }(receiverAddress);\n } else {\n ERC20(token).safeApprove(erc20PredicateProxy, amount);\n\n // deposit into rootchain manager\n IRootChainManager(rootChainManagerProxy).depositFor(\n receiverAddress,\n token,\n abi.encodePacked(amount)\n );\n }\n\n emit SocketBridge(\n amount,\n token,\n DESTINATION_CHAIN_ID,\n NativePolyonIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in NativePolygon-BridgeData struct\n * @param swapId routeId for the swapImpl\n * @param receiverAddress address of the receiver\n * @param swapData encoded data for swap\n */\n function swapAndBridge(\n uint32 swapId,\n address receiverAddress,\n bytes32 metadata,\n bytes calldata swapData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n\n if (token == NATIVE_TOKEN_ADDRESS) {\n IRootChainManager(rootChainManagerProxy).depositEtherFor{\n value: bridgeAmount\n }(receiverAddress);\n } else {\n ERC20(token).safeApprove(erc20PredicateProxy, bridgeAmount);\n\n // deposit into rootchain manager\n IRootChainManager(rootChainManagerProxy).depositFor(\n receiverAddress,\n token,\n abi.encodePacked(bridgeAmount)\n );\n }\n\n emit SocketBridge(\n bridgeAmount,\n token,\n DESTINATION_CHAIN_ID,\n NativePolyonIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via NativePolygon-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount of tokens being bridged\n * @param receiverAddress recipient address\n * @param token address of token being bridged\n */\n function bridgeERC20To(\n uint256 amount,\n bytes32 metadata,\n address receiverAddress,\n address token\n ) external payable {\n ERC20 tokenInstance = ERC20(token);\n\n // set allowance for erc20 predicate\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n tokenInstance.safeApprove(erc20PredicateProxy, amount);\n\n // deposit into rootchain manager\n rootChainManagerProxy.depositFor(\n receiverAddress,\n token,\n abi.encodePacked(amount)\n );\n\n emit SocketBridge(\n amount,\n token,\n DESTINATION_CHAIN_ID,\n NativePolyonIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n\n /**\n * @notice function to handle Native bridging to receipent via NativePolygon-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount of tokens being bridged\n * @param receiverAddress recipient address\n */\n function bridgeNativeTo(\n uint256 amount,\n bytes32 metadata,\n address receiverAddress\n ) external payable {\n rootChainManagerProxy.depositEtherFor{value: amount}(receiverAddress);\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n DESTINATION_CHAIN_ID,\n NativePolyonIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n" - }, - "src/bridges/stargate/l1/Stargate.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport \"../interfaces/stargate.sol\";\nimport {BridgeImplBase} from \"../../BridgeImplBase.sol\";\nimport {STARGATE} from \"../../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Stargate-L1-Route Implementation\n * @notice Route implementation with functions to bridge ERC20 and Native via Stargate-L1-Bridge\n * Called via SocketGateway if the routeId in the request maps to the routeId of Stargate-L1-Implementation\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ncontract StargateImplL1 is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable StargateIdentifier = STARGATE;\n\n /// @notice Function-selector for ERC20-token bridging on Stargate-L1-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4\n public immutable STARGATE_L1_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(address,address,address,uint256,uint256,(uint256,uint256,uint256,uint256,bytes32,bytes,uint16))\"\n )\n );\n\n /// @notice Function-selector for Native bridging on Stargate-L1-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge Native tokens\n bytes4\n public immutable STARGATE_L1_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeNativeTo(address,address,uint16,uint256,uint256,uint256,bytes32)\"\n )\n );\n\n bytes4 public immutable STARGATE_L1_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(address,address,uint16,uint256,uint256,uint256,uint256,uint256,uint256,bytes32,bytes))\"\n )\n );\n\n /// @notice Stargate Router to bridge ERC20 tokens\n IBridgeStargate public immutable router;\n\n /// @notice Stargate Router to bridge native tokens\n IBridgeStargate public immutable routerETH;\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n /// @dev ensure router, routerEth are set properly for the chainId in which the contract is being deployed\n constructor(\n address _router,\n address _routerEth,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n router = IBridgeStargate(_router);\n routerETH = IBridgeStargate(_routerEth);\n }\n\n struct StargateBridgeExtraData {\n uint256 srcPoolId;\n uint256 dstPoolId;\n uint256 destinationGasLimit;\n uint256 minReceivedAmt;\n bytes32 metadata;\n bytes destinationPayload;\n uint16 stargateDstChainId; // stargate defines chain id in its way\n }\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct StargateBridgeDataNoToken {\n address receiverAddress;\n address senderAddress;\n uint16 stargateDstChainId; // stargate defines chain id in its way\n uint256 value;\n // a unique identifier that is uses to dedup transfers\n // this value is the a timestamp sent from frontend, but in theory can be any unique number\n uint256 srcPoolId;\n uint256 dstPoolId;\n uint256 minReceivedAmt; // defines the slippage, the min qty you would accept on the destination\n uint256 optionalValue;\n uint256 destinationGasLimit;\n bytes32 metadata;\n bytes destinationPayload;\n }\n\n struct StargateBridgeData {\n address token;\n address receiverAddress;\n address senderAddress;\n uint16 stargateDstChainId; // stargate defines chain id in its way\n uint256 value;\n // a unique identifier that is uses to dedup transfers\n // this value is the a timestamp sent from frontend, but in theory can be any unique number\n uint256 srcPoolId;\n uint256 dstPoolId;\n uint256 minReceivedAmt; // defines the slippage, the min qty you would accept on the destination\n uint256 optionalValue;\n uint256 destinationGasLimit;\n bytes32 metadata;\n bytes destinationPayload;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in Stargate-BridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for Stargate-L1-Bridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n StargateBridgeData memory stargateBridgeData = abi.decode(\n bridgeData,\n (StargateBridgeData)\n );\n\n if (stargateBridgeData.token == NATIVE_TOKEN_ADDRESS) {\n // perform bridging\n routerETH.swapETH{value: amount + stargateBridgeData.optionalValue}(\n stargateBridgeData.stargateDstChainId,\n payable(stargateBridgeData.senderAddress),\n abi.encodePacked(stargateBridgeData.receiverAddress),\n amount,\n stargateBridgeData.minReceivedAmt\n );\n } else {\n ERC20(stargateBridgeData.token).safeApprove(\n address(router),\n amount\n );\n {\n router.swap{value: stargateBridgeData.value}(\n stargateBridgeData.stargateDstChainId,\n stargateBridgeData.srcPoolId,\n stargateBridgeData.dstPoolId,\n payable(stargateBridgeData.senderAddress), // default to refund to main contract\n amount,\n stargateBridgeData.minReceivedAmt,\n IBridgeStargate.lzTxObj(\n stargateBridgeData.destinationGasLimit,\n 0, // zero amount since this is a ERC20 bridging\n \"0x\" //empty data since this is for only ERC20\n ),\n abi.encodePacked(stargateBridgeData.receiverAddress),\n stargateBridgeData.destinationPayload\n );\n }\n }\n\n emit SocketBridge(\n amount,\n stargateBridgeData.token,\n stargateBridgeData.stargateDstChainId,\n StargateIdentifier,\n msg.sender,\n stargateBridgeData.receiverAddress,\n stargateBridgeData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in Stargate-BridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param stargateBridgeData encoded data for StargateBridgeData\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n StargateBridgeDataNoToken calldata stargateBridgeData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n\n if (token == NATIVE_TOKEN_ADDRESS) {\n // perform bridging\n routerETH.swapETH{\n value: bridgeAmount + stargateBridgeData.optionalValue\n }(\n stargateBridgeData.stargateDstChainId,\n payable(stargateBridgeData.senderAddress),\n abi.encodePacked(stargateBridgeData.receiverAddress),\n bridgeAmount,\n stargateBridgeData.minReceivedAmt\n );\n } else {\n ERC20(token).safeApprove(address(router), bridgeAmount);\n {\n router.swap{value: stargateBridgeData.value}(\n stargateBridgeData.stargateDstChainId,\n stargateBridgeData.srcPoolId,\n stargateBridgeData.dstPoolId,\n payable(stargateBridgeData.senderAddress), // default to refund to main contract\n bridgeAmount,\n stargateBridgeData.minReceivedAmt,\n IBridgeStargate.lzTxObj(\n stargateBridgeData.destinationGasLimit,\n 0, // zero amount since this is a ERC20 bridging\n \"0x\" //empty data since this is for only ERC20\n ),\n abi.encodePacked(stargateBridgeData.receiverAddress),\n stargateBridgeData.destinationPayload\n );\n }\n }\n\n emit SocketBridge(\n bridgeAmount,\n token,\n stargateBridgeData.stargateDstChainId,\n StargateIdentifier,\n msg.sender,\n stargateBridgeData.receiverAddress,\n stargateBridgeData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via Stargate-L1-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param token address of token being bridged\n * @param senderAddress address of sender\n * @param receiverAddress address of recipient\n * @param amount amount of token being bridge\n * @param value value\n * @param stargateBridgeExtraData stargate bridge extradata\n */\n function bridgeERC20To(\n address token,\n address senderAddress,\n address receiverAddress,\n uint256 amount,\n uint256 value,\n StargateBridgeExtraData calldata stargateBridgeExtraData\n ) external payable {\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n tokenInstance.safeApprove(address(router), amount);\n {\n router.swap{value: value}(\n stargateBridgeExtraData.stargateDstChainId,\n stargateBridgeExtraData.srcPoolId,\n stargateBridgeExtraData.dstPoolId,\n payable(senderAddress), // default to refund to main contract\n amount,\n stargateBridgeExtraData.minReceivedAmt,\n IBridgeStargate.lzTxObj(\n stargateBridgeExtraData.destinationGasLimit,\n 0, // zero amount since this is a ERC20 bridging\n \"0x\" //empty data since this is for only ERC20\n ),\n abi.encodePacked(receiverAddress),\n stargateBridgeExtraData.destinationPayload\n );\n }\n\n emit SocketBridge(\n amount,\n token,\n stargateBridgeExtraData.stargateDstChainId,\n StargateIdentifier,\n msg.sender,\n receiverAddress,\n stargateBridgeExtraData.metadata\n );\n }\n\n /**\n * @notice function to handle Native bridging to receipent via Stargate-L1-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param receiverAddress address of receipient\n * @param senderAddress address of sender\n * @param stargateDstChainId stargate defines chain id in its way\n * @param amount amount of token being bridge\n * @param minReceivedAmt defines the slippage, the min qty you would accept on the destination\n * @param optionalValue optionalValue Native amount\n */\n function bridgeNativeTo(\n address receiverAddress,\n address senderAddress,\n uint16 stargateDstChainId,\n uint256 amount,\n uint256 minReceivedAmt,\n uint256 optionalValue,\n bytes32 metadata\n ) external payable {\n // perform bridging\n routerETH.swapETH{value: amount + optionalValue}(\n stargateDstChainId,\n payable(senderAddress),\n abi.encodePacked(receiverAddress),\n amount,\n minReceivedAmt\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n stargateDstChainId,\n StargateIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n" - }, - "src/bridges/hop/l2/HopImplL2.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport \"../interfaces/amm.sol\";\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {BridgeImplBase} from \"../../BridgeImplBase.sol\";\nimport {HOP} from \"../../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Hop-L2 Route Implementation\n * @notice This is the L2 implementation, so this is used when transferring from l2 to supported l2s\n * Called via SocketGateway if the routeId in the request maps to the routeId of HopL2-Implementation\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ncontract HopImplL2 is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable HopIdentifier = HOP;\n\n /// @notice Function-selector for ERC20-token bridging on Hop-L2-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4 public immutable HOP_L2_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(address,address,address,uint256,uint256,(uint256,uint256,uint256,uint256,uint256,bytes32))\"\n )\n );\n\n /// @notice Function-selector for Native bridging on Hop-L2-Route\n /// @dev This function selector is to be used while building transaction-data to bridge Native tokens\n bytes4 public immutable HOP_L2_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeNativeTo(address,address,uint256,uint256,uint256,uint256,uint256,uint256,uint256,bytes32)\"\n )\n );\n\n bytes4 public immutable HOP_L2_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes32))\"\n )\n );\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n constructor(\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {}\n\n /// @notice Struct to be used as a input parameter for Bridging tokens via Hop-L2-route\n /// @dev while building transactionData,values should be set in this sequence of properties in this struct\n struct HopBridgeRequestData {\n // fees passed to relayer\n uint256 bonderFee;\n // The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.\n uint256 amountOutMin;\n // The deadline for swapping in the destination AMM market. 0 if no swap is intended.\n uint256 deadline;\n // Minimum amount expected to be received or bridged to destination\n uint256 amountOutMinDestination;\n // deadline for bridging to destination\n uint256 deadlineDestination;\n // socket offchain created hash\n bytes32 metadata;\n }\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct HopBridgeDataNoToken {\n // The address receiving funds at the destination\n address receiverAddress;\n // AMM address of Hop on L2\n address hopAMM;\n // The chainId of the destination chain\n uint256 toChainId;\n // fees passed to relayer\n uint256 bonderFee;\n // The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.\n uint256 amountOutMin;\n // The deadline for swapping in the destination AMM market. 0 if no swap is intended.\n uint256 deadline;\n // Minimum amount expected to be received or bridged to destination\n uint256 amountOutMinDestination;\n // deadline for bridging to destination\n uint256 deadlineDestination;\n // socket offchain created hash\n bytes32 metadata;\n }\n\n struct HopBridgeData {\n /// @notice address of token being bridged\n address token;\n // The address receiving funds at the destination\n address receiverAddress;\n // AMM address of Hop on L2\n address hopAMM;\n // The chainId of the destination chain\n uint256 toChainId;\n // fees passed to relayer\n uint256 bonderFee;\n // The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.\n uint256 amountOutMin;\n // The deadline for swapping in the destination AMM market. 0 if no swap is intended.\n uint256 deadline;\n // Minimum amount expected to be received or bridged to destination\n uint256 amountOutMinDestination;\n // deadline for bridging to destination\n uint256 deadlineDestination;\n // socket offchain created hash\n bytes32 metadata;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in HopBridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for Hop-L2-Bridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n HopBridgeData memory hopData = abi.decode(bridgeData, (HopBridgeData));\n\n if (hopData.token == NATIVE_TOKEN_ADDRESS) {\n HopAMM(hopData.hopAMM).swapAndSend{value: amount}(\n hopData.toChainId,\n hopData.receiverAddress,\n amount,\n hopData.bonderFee,\n hopData.amountOutMin,\n hopData.deadline,\n hopData.amountOutMinDestination,\n hopData.deadlineDestination\n );\n } else {\n // perform bridging\n HopAMM(hopData.hopAMM).swapAndSend(\n hopData.toChainId,\n hopData.receiverAddress,\n amount,\n hopData.bonderFee,\n hopData.amountOutMin,\n hopData.deadline,\n hopData.amountOutMinDestination,\n hopData.deadlineDestination\n );\n }\n\n emit SocketBridge(\n amount,\n hopData.token,\n hopData.toChainId,\n HopIdentifier,\n msg.sender,\n hopData.receiverAddress,\n hopData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in HopBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param hopData encoded data for HopData\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n HopBridgeDataNoToken calldata hopData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n\n if (token == NATIVE_TOKEN_ADDRESS) {\n HopAMM(hopData.hopAMM).swapAndSend{value: bridgeAmount}(\n hopData.toChainId,\n hopData.receiverAddress,\n bridgeAmount,\n hopData.bonderFee,\n hopData.amountOutMin,\n hopData.deadline,\n hopData.amountOutMinDestination,\n hopData.deadlineDestination\n );\n } else {\n // perform bridging\n HopAMM(hopData.hopAMM).swapAndSend(\n hopData.toChainId,\n hopData.receiverAddress,\n bridgeAmount,\n hopData.bonderFee,\n hopData.amountOutMin,\n hopData.deadline,\n hopData.amountOutMinDestination,\n hopData.deadlineDestination\n );\n }\n\n emit SocketBridge(\n bridgeAmount,\n token,\n hopData.toChainId,\n HopIdentifier,\n msg.sender,\n hopData.receiverAddress,\n hopData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via Hop-L2-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param receiverAddress The address receiving funds at the destination\n * @param token token being bridged\n * @param hopAMM AMM address of Hop on L2\n * @param amount The amount being bridged\n * @param toChainId The chainId of the destination chain\n * @param hopBridgeRequestData extraData for Bridging across Hop-L2\n */\n function bridgeERC20To(\n address receiverAddress,\n address token,\n address hopAMM,\n uint256 amount,\n uint256 toChainId,\n HopBridgeRequestData calldata hopBridgeRequestData\n ) external payable {\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n\n HopAMM(hopAMM).swapAndSend(\n toChainId,\n receiverAddress,\n amount,\n hopBridgeRequestData.bonderFee,\n hopBridgeRequestData.amountOutMin,\n hopBridgeRequestData.deadline,\n hopBridgeRequestData.amountOutMinDestination,\n hopBridgeRequestData.deadlineDestination\n );\n\n emit SocketBridge(\n amount,\n token,\n toChainId,\n HopIdentifier,\n msg.sender,\n receiverAddress,\n hopBridgeRequestData.metadata\n );\n }\n\n /**\n * @notice function to handle Native bridging to receipent via Hop-L2-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param receiverAddress The address receiving funds at the destination\n * @param hopAMM AMM address of Hop on L2\n * @param amount The amount being bridged\n * @param toChainId The chainId of the destination chain\n * @param bonderFee fees passed to relayer\n * @param amountOutMin The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.\n * @param deadline The deadline for swapping in the destination AMM market. 0 if no swap is intended.\n * @param amountOutMinDestination Minimum amount expected to be received or bridged to destination\n * @param deadlineDestination deadline for bridging to destination\n */\n function bridgeNativeTo(\n address receiverAddress,\n address hopAMM,\n uint256 amount,\n uint256 toChainId,\n uint256 bonderFee,\n uint256 amountOutMin,\n uint256 deadline,\n uint256 amountOutMinDestination,\n uint256 deadlineDestination,\n bytes32 metadata\n ) external payable {\n // token address might not be indication thats why passed through extraData\n // perform bridging\n HopAMM(hopAMM).swapAndSend{value: amount}(\n toChainId,\n receiverAddress,\n amount,\n bonderFee,\n amountOutMin,\n deadline,\n amountOutMinDestination,\n deadlineDestination\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n toChainId,\n HopIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n" - }, - "src/bridges/anyswap-router-v4/l2/Anyswap.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {BridgeImplBase} from \"../../BridgeImplBase.sol\";\nimport {ANYSWAP} from \"../../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Anyswap-V4-Route L1 Implementation\n * @notice Route implementation with functions to bridge ERC20 via Anyswap-Bridge\n * Called via SocketGateway if the routeId in the request maps to the routeId of AnyswapImplementation\n * This is the L2 implementation, so this is used when transferring from l2.\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ninterface AnyswapV4Router {\n function anySwapOutUnderlying(\n address token,\n address to,\n uint256 amount,\n uint256 toChainID\n ) external;\n}\n\ncontract AnyswapL2Impl is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable AnyswapIdentifier = ANYSWAP;\n\n /// @notice Function-selector for ERC20-token bridging on Anyswap-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4 public immutable ANYSWAP_L2_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(uint256,uint256,bytes32,address,address,address)\"\n )\n );\n\n bytes4 public immutable ANYSWAP_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(uint256,address,address,bytes32))\"\n )\n );\n\n // polygon router multichain router v4\n AnyswapV4Router public immutable router;\n\n /**\n * @notice Constructor sets the router address and socketGateway address.\n * @dev anyswap v4 router is immutable. so no setter function required.\n */\n constructor(\n address _router,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n router = AnyswapV4Router(_router);\n }\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct AnyswapBridgeDataNoToken {\n /// @notice destination ChainId\n uint256 toChainId;\n /// @notice address of receiver of bridged tokens\n address receiverAddress;\n /// @notice address of wrapperToken, WrappedVersion of the token being bridged\n address wrapperTokenAddress;\n /// @notice socket offchain created hash\n bytes32 metadata;\n }\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct AnyswapBridgeData {\n /// @notice destination ChainId\n uint256 toChainId;\n /// @notice address of receiver of bridged tokens\n address receiverAddress;\n /// @notice address of wrapperToken, WrappedVersion of the token being bridged\n address wrapperTokenAddress;\n /// @notice address of token being bridged\n address token;\n /// @notice socket offchain created hash\n bytes32 metadata;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in AnyswapBridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for AnyswapBridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n AnyswapBridgeData memory anyswapBridgeData = abi.decode(\n bridgeData,\n (AnyswapBridgeData)\n );\n ERC20(anyswapBridgeData.token).safeApprove(address(router), amount);\n router.anySwapOutUnderlying(\n anyswapBridgeData.wrapperTokenAddress,\n anyswapBridgeData.receiverAddress,\n amount,\n anyswapBridgeData.toChainId\n );\n\n emit SocketBridge(\n amount,\n anyswapBridgeData.token,\n anyswapBridgeData.toChainId,\n AnyswapIdentifier,\n msg.sender,\n anyswapBridgeData.receiverAddress,\n anyswapBridgeData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in AnyswapBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param anyswapBridgeData encoded data for AnyswapBridge\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n AnyswapBridgeDataNoToken calldata anyswapBridgeData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n\n ERC20(token).safeApprove(address(router), bridgeAmount);\n router.anySwapOutUnderlying(\n anyswapBridgeData.wrapperTokenAddress,\n anyswapBridgeData.receiverAddress,\n bridgeAmount,\n anyswapBridgeData.toChainId\n );\n\n emit SocketBridge(\n bridgeAmount,\n token,\n anyswapBridgeData.toChainId,\n AnyswapIdentifier,\n msg.sender,\n anyswapBridgeData.receiverAddress,\n anyswapBridgeData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via Anyswap-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount being bridged\n * @param toChainId destination ChainId\n * @param receiverAddress address of receiver of bridged tokens\n * @param token address of token being bridged\n * @param wrapperTokenAddress address of wrapperToken, WrappedVersion of the token being bridged\n */\n function bridgeERC20To(\n uint256 amount,\n uint256 toChainId,\n bytes32 metadata,\n address receiverAddress,\n address token,\n address wrapperTokenAddress\n ) external payable {\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n tokenInstance.safeApprove(address(router), amount);\n router.anySwapOutUnderlying(\n wrapperTokenAddress,\n receiverAddress,\n amount,\n toChainId\n );\n\n emit SocketBridge(\n amount,\n token,\n toChainId,\n AnyswapIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n" - }, - "src/bridges/hyphen/Hyphen.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport \"./interfaces/hyphen.sol\";\nimport \"../BridgeImplBase.sol\";\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {HYPHEN} from \"../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Hyphen-Route Implementation\n * @notice Route implementation with functions to bridge ERC20 and Native via Hyphen-Bridge\n * Called via SocketGateway if the routeId in the request maps to the routeId of HyphenImplementation\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ncontract HyphenImpl is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable HyphenIdentifier = HYPHEN;\n\n /// @notice Function-selector for ERC20-token bridging on Hyphen-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4 public immutable HYPHEN_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\"bridgeERC20To(uint256,bytes32,address,address,uint256)\")\n );\n\n /// @notice Function-selector for Native bridging on Hyphen-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge Native tokens\n bytes4 public immutable HYPHEN_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(keccak256(\"bridgeNativeTo(uint256,bytes32,address,uint256)\"));\n\n bytes4 public immutable HYPHEN_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\"swapAndBridge(uint32,bytes,(address,uint256,bytes32))\")\n );\n\n /// @notice liquidityPoolManager - liquidityPool Manager of Hyphen used to bridge ERC20 and native\n /// @dev this is to be initialized in constructor with a valid deployed address of hyphen-liquidityPoolManager\n HyphenLiquidityPoolManager public immutable liquidityPoolManager;\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n /// @dev ensure liquidityPoolManager-address are set properly for the chainId in which the contract is being deployed\n constructor(\n address _liquidityPoolManager,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n liquidityPoolManager = HyphenLiquidityPoolManager(\n _liquidityPoolManager\n );\n }\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct HyphenData {\n /// @notice address of token being bridged\n address token;\n /// @notice address of receiver\n address receiverAddress;\n /// @notice chainId of destination\n uint256 toChainId;\n /// @notice socket offchain created hash\n bytes32 metadata;\n }\n\n struct HyphenDataNoToken {\n /// @notice address of receiver\n address receiverAddress;\n /// @notice chainId of destination\n uint256 toChainId;\n /// @notice chainId of destination\n bytes32 metadata;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in HyphenBridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for HyphenBridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n HyphenData memory hyphenData = abi.decode(bridgeData, (HyphenData));\n\n if (hyphenData.token == NATIVE_TOKEN_ADDRESS) {\n liquidityPoolManager.depositNative{value: amount}(\n hyphenData.receiverAddress,\n hyphenData.toChainId,\n \"SOCKET\"\n );\n } else {\n ERC20(hyphenData.token).safeApprove(\n address(liquidityPoolManager),\n amount\n );\n liquidityPoolManager.depositErc20(\n hyphenData.toChainId,\n hyphenData.token,\n hyphenData.receiverAddress,\n amount,\n \"SOCKET\"\n );\n }\n\n emit SocketBridge(\n amount,\n hyphenData.token,\n hyphenData.toChainId,\n HyphenIdentifier,\n msg.sender,\n hyphenData.receiverAddress,\n hyphenData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in HyphenBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param hyphenData encoded data for hyphenData\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n HyphenDataNoToken calldata hyphenData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n if (token == NATIVE_TOKEN_ADDRESS) {\n liquidityPoolManager.depositNative{value: bridgeAmount}(\n hyphenData.receiverAddress,\n hyphenData.toChainId,\n \"SOCKET\"\n );\n } else {\n ERC20(token).safeApprove(\n address(liquidityPoolManager),\n bridgeAmount\n );\n liquidityPoolManager.depositErc20(\n hyphenData.toChainId,\n token,\n hyphenData.receiverAddress,\n bridgeAmount,\n \"SOCKET\"\n );\n }\n\n emit SocketBridge(\n bridgeAmount,\n token,\n hyphenData.toChainId,\n HyphenIdentifier,\n msg.sender,\n hyphenData.receiverAddress,\n hyphenData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via Hyphen-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount to be sent\n * @param receiverAddress address of the token to bridged to the destination chain.\n * @param token address of token being bridged\n * @param toChainId chainId of destination\n */\n function bridgeERC20To(\n uint256 amount,\n bytes32 metadata,\n address receiverAddress,\n address token,\n uint256 toChainId\n ) external payable {\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n tokenInstance.safeApprove(address(liquidityPoolManager), amount);\n liquidityPoolManager.depositErc20(\n toChainId,\n token,\n receiverAddress,\n amount,\n \"SOCKET\"\n );\n\n emit SocketBridge(\n amount,\n token,\n toChainId,\n HyphenIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n\n /**\n * @notice function to handle Native bridging to receipent via Hyphen-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount to be sent\n * @param receiverAddress address of the token to bridged to the destination chain.\n * @param toChainId chainId of destination\n */\n function bridgeNativeTo(\n uint256 amount,\n bytes32 metadata,\n address receiverAddress,\n uint256 toChainId\n ) external payable {\n liquidityPoolManager.depositNative{value: amount}(\n receiverAddress,\n toChainId,\n \"SOCKET\"\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n toChainId,\n HyphenIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n" - }, - "src/bridges/optimism/l1/NativeOptimism.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport \"../interfaces/optimism.sol\";\nimport {BridgeImplBase} from \"../../BridgeImplBase.sol\";\nimport {UnsupportedInterfaceId} from \"../../../errors/SocketErrors.sol\";\nimport {NATIVE_OPTIMISM} from \"../../../static/RouteIdentifiers.sol\";\n\n/**\n * @title NativeOptimism-Route Implementation\n * @notice Route implementation with functions to bridge ERC20 and Native via NativeOptimism-Bridge\n * Tokens are bridged from Ethereum to Optimism Chain.\n * Called via SocketGateway if the routeId in the request maps to the routeId of NativeOptimism-Implementation\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ncontract NativeOptimismImpl is BridgeImplBase {\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable NativeOptimismIdentifier = NATIVE_OPTIMISM;\n\n uint256 public constant DESTINATION_CHAIN_ID = 10;\n\n /// @notice Function-selector for ERC20-token bridging on Native-Optimism-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4\n public immutable NATIVE_OPTIMISM_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(address,address,address,uint32,(bytes32,bytes32),uint256,uint256,address,bytes)\"\n )\n );\n\n /// @notice Function-selector for Native bridging on Native-Optimism-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge Native balance\n bytes4\n public immutable NATIVE_OPTIMISM_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeNativeTo(address,address,uint32,uint256,bytes32,bytes)\"\n )\n );\n\n bytes4 public immutable NATIVE_OPTIMISM_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(uint256,bytes32,bytes32,address,address,uint32,address,bytes))\"\n )\n );\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n constructor(\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {}\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct OptimismBridgeDataNoToken {\n // interfaceId to be set offchain which is used to select one of the 3 kinds of bridging (standard bridge / old standard / synthetic)\n uint256 interfaceId;\n // currencyKey of the token beingBridged\n bytes32 currencyKey;\n // socket offchain created hash\n bytes32 metadata;\n // address of receiver of bridged tokens\n address receiverAddress;\n /**\n * OptimismBridge that Performs the logic for deposits by informing the L2 Deposited Token\n * contract of the deposit and calling a handler to lock the L1 funds. (e.g. transferFrom)\n */\n address customBridgeAddress;\n // Gas limit required to complete the deposit on L2.\n uint32 l2Gas;\n // Address of the L1 respective L2 ERC20\n address l2Token;\n // additional data , for ll contracts this will be 0x data or empty data\n bytes data;\n }\n\n struct OptimismBridgeData {\n // interfaceId to be set offchain which is used to select one of the 3 kinds of bridging (standard bridge / old standard / synthetic)\n uint256 interfaceId;\n // currencyKey of the token beingBridged\n bytes32 currencyKey;\n // socket offchain created hash\n bytes32 metadata;\n // address of receiver of bridged tokens\n address receiverAddress;\n /**\n * OptimismBridge that Performs the logic for deposits by informing the L2 Deposited Token\n * contract of the deposit and calling a handler to lock the L1 funds. (e.g. transferFrom)\n */\n address customBridgeAddress;\n /// @notice address of token being bridged\n address token;\n // Gas limit required to complete the deposit on L2.\n uint32 l2Gas;\n // Address of the L1 respective L2 ERC20\n address l2Token;\n // additional data , for ll contracts this will be 0x data or empty data\n bytes data;\n }\n\n struct OptimismERC20Data {\n bytes32 currencyKey;\n bytes32 metadata;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in OptimismBridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for Optimism-Bridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n OptimismBridgeData memory optimismBridgeData = abi.decode(\n bridgeData,\n (OptimismBridgeData)\n );\n\n emit SocketBridge(\n amount,\n optimismBridgeData.token,\n DESTINATION_CHAIN_ID,\n NativeOptimismIdentifier,\n msg.sender,\n optimismBridgeData.receiverAddress,\n optimismBridgeData.metadata\n );\n if (optimismBridgeData.token == NATIVE_TOKEN_ADDRESS) {\n L1StandardBridge(optimismBridgeData.customBridgeAddress)\n .depositETHTo{value: amount}(\n optimismBridgeData.receiverAddress,\n optimismBridgeData.l2Gas,\n optimismBridgeData.data\n );\n } else {\n if (optimismBridgeData.interfaceId == 0) {\n revert UnsupportedInterfaceId();\n }\n\n ERC20(optimismBridgeData.token).safeApprove(\n optimismBridgeData.customBridgeAddress,\n amount\n );\n\n if (optimismBridgeData.interfaceId == 1) {\n // deposit into standard bridge\n L1StandardBridge(optimismBridgeData.customBridgeAddress)\n .depositERC20To(\n optimismBridgeData.token,\n optimismBridgeData.l2Token,\n optimismBridgeData.receiverAddress,\n amount,\n optimismBridgeData.l2Gas,\n optimismBridgeData.data\n );\n return;\n }\n\n // Deposit Using Old Standard - iOVM_L1TokenGateway(Example - SNX Token)\n if (optimismBridgeData.interfaceId == 2) {\n OldL1TokenGateway(optimismBridgeData.customBridgeAddress)\n .depositTo(optimismBridgeData.receiverAddress, amount);\n return;\n }\n\n if (optimismBridgeData.interfaceId == 3) {\n OldL1TokenGateway(optimismBridgeData.customBridgeAddress)\n .initiateSynthTransfer(\n optimismBridgeData.currencyKey,\n optimismBridgeData.receiverAddress,\n amount\n );\n return;\n }\n }\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in OptimismBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param optimismBridgeData encoded data for OptimismBridgeData\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n OptimismBridgeDataNoToken calldata optimismBridgeData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n\n emit SocketBridge(\n bridgeAmount,\n token,\n DESTINATION_CHAIN_ID,\n NativeOptimismIdentifier,\n msg.sender,\n optimismBridgeData.receiverAddress,\n optimismBridgeData.metadata\n );\n if (token == NATIVE_TOKEN_ADDRESS) {\n L1StandardBridge(optimismBridgeData.customBridgeAddress)\n .depositETHTo{value: bridgeAmount}(\n optimismBridgeData.receiverAddress,\n optimismBridgeData.l2Gas,\n optimismBridgeData.data\n );\n } else {\n if (optimismBridgeData.interfaceId == 0) {\n revert UnsupportedInterfaceId();\n }\n\n ERC20(token).safeApprove(\n optimismBridgeData.customBridgeAddress,\n bridgeAmount\n );\n\n if (optimismBridgeData.interfaceId == 1) {\n // deposit into standard bridge\n L1StandardBridge(optimismBridgeData.customBridgeAddress)\n .depositERC20To(\n token,\n optimismBridgeData.l2Token,\n optimismBridgeData.receiverAddress,\n bridgeAmount,\n optimismBridgeData.l2Gas,\n optimismBridgeData.data\n );\n return;\n }\n\n // Deposit Using Old Standard - iOVM_L1TokenGateway(Example - SNX Token)\n if (optimismBridgeData.interfaceId == 2) {\n OldL1TokenGateway(optimismBridgeData.customBridgeAddress)\n .depositTo(\n optimismBridgeData.receiverAddress,\n bridgeAmount\n );\n return;\n }\n\n if (optimismBridgeData.interfaceId == 3) {\n OldL1TokenGateway(optimismBridgeData.customBridgeAddress)\n .initiateSynthTransfer(\n optimismBridgeData.currencyKey,\n optimismBridgeData.receiverAddress,\n bridgeAmount\n );\n return;\n }\n }\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via NativeOptimism-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param token address of token being bridged\n * @param receiverAddress address of receiver of bridged tokens\n * @param customBridgeAddress OptimismBridge that Performs the logic for deposits by informing the L2 Deposited Token\n * contract of the deposit and calling a handler to lock the L1 funds. (e.g. transferFrom)\n * @param l2Gas Gas limit required to complete the deposit on L2.\n * @param optimismData extra data needed for optimism bridge\n * @param amount amount being bridged\n * @param interfaceId interfaceId to be set offchain which is used to select one of the 3 kinds of bridging (standard bridge / old standard / synthetic)\n * @param l2Token Address of the L1 respective L2 ERC20\n * @param data additional data , for ll contracts this will be 0x data or empty data\n */\n function bridgeERC20To(\n address token,\n address receiverAddress,\n address customBridgeAddress,\n uint32 l2Gas,\n OptimismERC20Data calldata optimismData,\n uint256 amount,\n uint256 interfaceId,\n address l2Token,\n bytes calldata data\n ) external payable {\n if (interfaceId == 0) {\n revert UnsupportedInterfaceId();\n }\n\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n tokenInstance.safeApprove(customBridgeAddress, amount);\n\n emit SocketBridge(\n amount,\n token,\n DESTINATION_CHAIN_ID,\n NativeOptimismIdentifier,\n msg.sender,\n receiverAddress,\n optimismData.metadata\n );\n if (interfaceId == 1) {\n // deposit into standard bridge\n L1StandardBridge(customBridgeAddress).depositERC20To(\n token,\n l2Token,\n receiverAddress,\n amount,\n l2Gas,\n data\n );\n return;\n }\n\n // Deposit Using Old Standard - iOVM_L1TokenGateway(Example - SNX Token)\n if (interfaceId == 2) {\n OldL1TokenGateway(customBridgeAddress).depositTo(\n receiverAddress,\n amount\n );\n return;\n }\n\n if (interfaceId == 3) {\n OldL1TokenGateway(customBridgeAddress).initiateSynthTransfer(\n optimismData.currencyKey,\n receiverAddress,\n amount\n );\n return;\n }\n }\n\n /**\n * @notice function to handle native balance bridging to receipent via NativeOptimism-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param receiverAddress address of receiver of bridged tokens\n * @param customBridgeAddress OptimismBridge that Performs the logic for deposits by informing the L2 Deposited Token\n * contract of the deposit and calling a handler to lock the L1 funds. (e.g. transferFrom)\n * @param l2Gas Gas limit required to complete the deposit on L2.\n * @param amount amount being bridged\n * @param data additional data , for ll contracts this will be 0x data or empty data\n */\n function bridgeNativeTo(\n address receiverAddress,\n address customBridgeAddress,\n uint32 l2Gas,\n uint256 amount,\n bytes32 metadata,\n bytes calldata data\n ) external payable {\n L1StandardBridge(customBridgeAddress).depositETHTo{value: amount}(\n receiverAddress,\n l2Gas,\n data\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n DESTINATION_CHAIN_ID,\n NativeOptimismIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n" - }, - "src/deployFactory/SocketDeployFactory.sol": { - "content": "//SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport \"../utils/Ownable.sol\";\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {ISocketBridgeBase} from \"../interfaces/ISocketBridgeBase.sol\";\n\n/**\n * @dev In the constructor, set up the initialization code for socket\n * contracts as well as the keccak256 hash of the given initialization code.\n * that will be used to deploy any transient contracts, which will deploy any\n * socket contracts that require the use of a constructor.\n *\n * Socket contract initialization code (29 bytes):\n *\n * 0x5860208158601c335a63aaf10f428752fa158151803b80938091923cf3\n *\n * Description:\n *\n * pc|op|name | [stack] | \n *\n * ** set the first stack item to zero - used later **\n * 00 58 getpc [0] <>\n *\n * ** set second stack item to 32, length of word returned from staticcall **\n * 01 60 push1\n * 02 20 outsize [0, 32] <>\n *\n * ** set third stack item to 0, position of word returned from staticcall **\n * 03 81 dup2 [0, 32, 0] <>\n *\n * ** set fourth stack item to 4, length of selector given to staticcall **\n * 04 58 getpc [0, 32, 0, 4] <>\n *\n * ** set fifth stack item to 28, position of selector given to staticcall **\n * 05 60 push1\n * 06 1c inpos [0, 32, 0, 4, 28] <>\n *\n * ** set the sixth stack item to msg.sender, target address for staticcall **\n * 07 33 caller [0, 32, 0, 4, 28, caller] <>\n *\n * ** set the seventh stack item to msg.gas, gas to forward for staticcall **\n * 08 5a gas [0, 32, 0, 4, 28, caller, gas] <>\n *\n * ** set the eighth stack item to selector, \"what\" to store via mstore **\n * 09 63 push4\n * 10 aaf10f42 selector [0, 32, 0, 4, 28, caller, gas, 0xaaf10f42] <>\n *\n * ** set the ninth stack item to 0, \"where\" to store via mstore ***\n * 11 87 dup8 [0, 32, 0, 4, 28, caller, gas, 0xaaf10f42, 0] <>\n *\n * ** call mstore, consume 8 and 9 from the stack, place selector in memory **\n * 12 52 mstore [0, 32, 0, 4, 0, caller, gas] <0xaaf10f42>\n *\n * ** call staticcall, consume items 2 through 7, place address in memory **\n * 13 fa staticcall [0, 1 (if successful)]
\n *\n * ** flip success bit in second stack item to set to 0 **\n * 14 15 iszero [0, 0]
\n *\n * ** push a third 0 to the stack, position of address in memory **\n * 15 81 dup2 [0, 0, 0]
\n *\n * ** place address from position in memory onto third stack item **\n * 16 51 mload [0, 0, address] <>\n *\n * ** place address to fourth stack item for extcodesize to consume **\n * 17 80 dup1 [0, 0, address, address] <>\n *\n * ** get extcodesize on fourth stack item for extcodecopy **\n * 18 3b extcodesize [0, 0, address, size] <>\n *\n * ** dup and swap size for use by return at end of init code **\n * 19 80 dup1 [0, 0, address, size, size] <>\n * 20 93 swap4 [size, 0, address, size, 0] <>\n *\n * ** push code position 0 to stack and reorder stack items for extcodecopy **\n * 21 80 dup1 [size, 0, address, size, 0, 0] <>\n * 22 91 swap2 [size, 0, address, 0, 0, size] <>\n * 23 92 swap3 [size, 0, size, 0, 0, address] <>\n *\n * ** call extcodecopy, consume four items, clone runtime code to memory **\n * 24 3c extcodecopy [size, 0] \n *\n * ** return to deploy final code in memory **\n * 25 f3 return [] *deployed!*\n */\ncontract SocketDeployFactory is Ownable {\n using SafeTransferLib for ERC20;\n address public immutable disabledRouteAddress;\n\n mapping(address => address) _implementations;\n mapping(uint256 => bool) isDisabled;\n mapping(uint256 => bool) isRouteDeployed;\n mapping(address => bool) canDisableRoute;\n\n event Deployed(address _addr);\n event DisabledRoute(address _addr);\n event Destroyed(address _addr);\n error ContractAlreadyDeployed();\n error NothingToDestroy();\n error AlreadyDisabled();\n error CannotBeDisabled();\n error OnlyDisabler();\n\n constructor(address _owner, address disabledRoute) Ownable(_owner) {\n disabledRouteAddress = disabledRoute;\n canDisableRoute[_owner] = true;\n }\n\n modifier onlyDisabler() {\n if (!canDisableRoute[msg.sender]) {\n revert OnlyDisabler();\n }\n _;\n }\n\n function addDisablerAddress(address disabler) external onlyOwner {\n canDisableRoute[disabler] = true;\n }\n\n function removeDisablerAddress(address disabler) external onlyOwner {\n canDisableRoute[disabler] = false;\n }\n\n /**\n * @notice Deploys a route contract at predetermined location\n * @notice Caller must first deploy the route contract at another location and pass its address as implementation.\n * @param routeId route identifier\n * @param implementationContract address of deployed route contract. Its byte code will be copied to predetermined location.\n */\n function deploy(\n uint256 routeId,\n address implementationContract\n ) external onlyOwner returns (address) {\n // assign the initialization code for the socket contract.\n\n bytes memory initCode = (\n hex\"5860208158601c335a63aaf10f428752fa158151803b80938091923cf3\"\n );\n\n // determine the address of the socket contract.\n address routeContractAddress = _getContractAddress(routeId);\n\n if (isRouteDeployed[routeId]) {\n revert ContractAlreadyDeployed();\n }\n\n isRouteDeployed[routeId] = true;\n\n //first we deploy the code we want to deploy on a separate address\n // store the implementation to be retrieved by the socket contract.\n _implementations[routeContractAddress] = implementationContract;\n address addr;\n assembly {\n let encoded_data := add(0x20, initCode) // load initialization code.\n let encoded_size := mload(initCode) // load init code's length.\n addr := create2(0, encoded_data, encoded_size, routeId) // routeId is used as salt\n }\n require(\n addr == routeContractAddress,\n \"Failed to deploy the new socket contract.\"\n );\n emit Deployed(addr);\n return addr;\n }\n\n /**\n * @notice Destroy the route deployed at a location.\n * @param routeId route identifier to be destroyed.\n */\n function destroy(uint256 routeId) external onlyDisabler {\n // determine the address of the socket contract.\n _destroy(routeId);\n }\n\n /**\n * @notice Deploy a disabled contract at destroyed route to handle it gracefully.\n * @param routeId route identifier to be disabled.\n */\n function disableRoute(\n uint256 routeId\n ) external onlyDisabler returns (address) {\n return _disableRoute(routeId);\n }\n\n /**\n * @notice Destroy a list of routeIds\n * @param routeIds array of routeIds to be destroyed.\n */\n function multiDestroy(uint256[] calldata routeIds) external onlyDisabler {\n for (uint32 index = 0; index < routeIds.length; ) {\n _destroy(routeIds[index]);\n unchecked {\n ++index;\n }\n }\n }\n\n /**\n * @notice Deploy a disabled contract at list of routeIds.\n * @param routeIds array of routeIds to be disabled.\n */\n function multiDisableRoute(\n uint256[] calldata routeIds\n ) external onlyDisabler {\n for (uint32 index = 0; index < routeIds.length; ) {\n _disableRoute(routeIds[index]);\n unchecked {\n ++index;\n }\n }\n }\n\n /**\n * @dev External view function for calculating a socket contract address\n * given a particular routeId.\n */\n function getContractAddress(\n uint256 routeId\n ) external view returns (address) {\n // determine the address of the socket contract.\n return _getContractAddress(routeId);\n }\n\n //those two functions are getting called by the socket Contract\n function getImplementation()\n external\n view\n returns (address implementation)\n {\n return _implementations[msg.sender];\n }\n\n function _disableRoute(uint256 routeId) internal returns (address) {\n // assign the initialization code for the socket contract.\n bytes memory initCode = (\n hex\"5860208158601c335a63aaf10f428752fa158151803b80938091923cf3\"\n );\n\n // determine the address of the socket contract.\n address routeContractAddress = _getContractAddress(routeId);\n\n if (!isRouteDeployed[routeId]) {\n revert CannotBeDisabled();\n }\n\n if (isDisabled[routeId]) {\n revert AlreadyDisabled();\n }\n\n isDisabled[routeId] = true;\n\n //first we deploy the code we want to deploy on a separate address\n // store the implementation to be retrieved by the socket contract.\n _implementations[routeContractAddress] = disabledRouteAddress;\n address addr;\n assembly {\n let encoded_data := add(0x20, initCode) // load initialization code.\n let encoded_size := mload(initCode) // load init code's length.\n addr := create2(0, encoded_data, encoded_size, routeId) // routeId is used as salt.\n }\n require(\n addr == routeContractAddress,\n \"Failed to deploy the new socket contract.\"\n );\n emit Deployed(addr);\n return addr;\n }\n\n function _destroy(uint256 routeId) internal {\n // determine the address of the socket contract.\n address routeContractAddress = _getContractAddress(routeId);\n\n if (!isRouteDeployed[routeId]) {\n revert NothingToDestroy();\n }\n ISocketBridgeBase(routeContractAddress).killme();\n emit Destroyed(routeContractAddress);\n }\n\n /**\n * @dev Internal view function for calculating a socket contract address\n * given a particular routeId.\n */\n function _getContractAddress(\n uint256 routeId\n ) internal view returns (address) {\n // determine the address of the socket contract.\n\n bytes memory initCode = (\n hex\"5860208158601c335a63aaf10f428752fa158151803b80938091923cf3\"\n );\n return\n address(\n uint160( // downcast to match the address type.\n uint256( // convert to uint to truncate upper digits.\n keccak256( // compute the CREATE2 hash using 4 inputs.\n abi.encodePacked( // pack all inputs to the hash together.\n hex\"ff\", // start with 0xff to distinguish from RLP.\n address(this), // this contract will be the caller.\n routeId, // the routeId is used as salt.\n keccak256(abi.encodePacked(initCode)) // the init code hash.\n )\n )\n )\n )\n );\n }\n\n /**\n * @notice Rescues the ERC20 token to an address\n this is a restricted function to be called by only socketGatewayOwner\n * @dev as this is a restricted to socketGatewayOwner, ensure the userAddress is a known address\n * @param token address of the ERC20 token being rescued\n * @param userAddress address to which ERC20 is to be rescued\n * @param amount amount of ERC20 tokens being rescued\n */\n function rescueFunds(\n address token,\n address userAddress,\n uint256 amount\n ) external onlyOwner {\n ERC20(token).safeTransfer(userAddress, amount);\n }\n\n /**\n * @notice Rescues the native balance to an address\n this is a restricted function to be called by only socketGatewayOwner\n * @dev as this is a restricted to socketGatewayOwner, ensure the userAddress is a known address\n * @param userAddress address to which native-balance is to be rescued\n * @param amount amount of native-balance being rescued\n */\n function rescueEther(\n address payable userAddress,\n uint256 amount\n ) external onlyOwner {\n userAddress.transfer(amount);\n }\n}\n" - }, - "src/interfaces/ISocketController.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/**\n * @title ISocketController\n * @notice Interface for SocketController functions.\n * @dev functions can be added here for invocation from external contracts or off-chain\n * only restriction is that this should have functions to manage controllers\n * @author Socket dot tech.\n */\ninterface ISocketController {\n /**\n * @notice Add controller to the socketGateway\n This is a restricted function to be called by only socketGatewayOwner\n * @dev ensure controllerAddress is a verified controller implementation address\n * @param _controllerAddress The address of controller implementation contract deployed\n * @return Id of the controller added to the controllers-mapping in socketGateway storage\n */\n function addController(\n address _controllerAddress\n ) external returns (uint32);\n\n /**\n * @notice disable controller by setting ZeroAddress to the entry in controllers-mapping\n identified by controllerId as key.\n This is a restricted function to be called by only socketGatewayOwner\n * @param _controllerId The Id of controller-implementation in the controllers mapping\n */\n function disableController(uint32 _controllerId) external;\n\n /**\n * @notice Get controllerImplementation address mapped to the controllerId\n * @param _controllerId controllerId is the key in the mapping for controllers\n * @return controller-implementation address\n */\n function getController(uint32 _controllerId) external returns (address);\n}\n" - }, - "lib/solmate/src/utils/SafeTransferLib.sol": { - "content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\nimport {ERC20} from \"../tokens/ERC20.sol\";\n\n/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.\n/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)\n/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.\n/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.\nlibrary SafeTransferLib {\n /*//////////////////////////////////////////////////////////////\n ETH OPERATIONS\n //////////////////////////////////////////////////////////////*/\n\n function safeTransferETH(address to, uint256 amount) internal {\n bool success;\n\n /// @solidity memory-safe-assembly\n assembly {\n // Transfer the ETH and store if it succeeded or not.\n success := call(gas(), to, amount, 0, 0, 0, 0)\n }\n\n require(success, \"ETH_TRANSFER_FAILED\");\n }\n\n /*//////////////////////////////////////////////////////////////\n ERC20 OPERATIONS\n //////////////////////////////////////////////////////////////*/\n\n function safeTransferFrom(\n ERC20 token,\n address from,\n address to,\n uint256 amount\n ) internal {\n bool success;\n\n /// @solidity memory-safe-assembly\n assembly {\n // Get a pointer to some free memory.\n let freeMemoryPointer := mload(0x40)\n\n // Write the abi-encoded calldata into memory, beginning with the function selector.\n mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)\n mstore(add(freeMemoryPointer, 4), from) // Append the \"from\" argument.\n mstore(add(freeMemoryPointer, 36), to) // Append the \"to\" argument.\n mstore(add(freeMemoryPointer, 68), amount) // Append the \"amount\" argument.\n\n success := and(\n // Set success to whether the call reverted, if not we check it either\n // returned exactly 1 (can't just be non-zero data), or had no return data.\n or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),\n // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.\n // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.\n // Counterintuitively, this call must be positioned second to the or() call in the\n // surrounding and() call or else returndatasize() will be zero during the computation.\n call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)\n )\n }\n\n require(success, \"TRANSFER_FROM_FAILED\");\n }\n\n function safeTransfer(\n ERC20 token,\n address to,\n uint256 amount\n ) internal {\n bool success;\n\n /// @solidity memory-safe-assembly\n assembly {\n // Get a pointer to some free memory.\n let freeMemoryPointer := mload(0x40)\n\n // Write the abi-encoded calldata into memory, beginning with the function selector.\n mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)\n mstore(add(freeMemoryPointer, 4), to) // Append the \"to\" argument.\n mstore(add(freeMemoryPointer, 36), amount) // Append the \"amount\" argument.\n\n success := and(\n // Set success to whether the call reverted, if not we check it either\n // returned exactly 1 (can't just be non-zero data), or had no return data.\n or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),\n // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.\n // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.\n // Counterintuitively, this call must be positioned second to the or() call in the\n // surrounding and() call or else returndatasize() will be zero during the computation.\n call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)\n )\n }\n\n require(success, \"TRANSFER_FAILED\");\n }\n\n function safeApprove(\n ERC20 token,\n address to,\n uint256 amount\n ) internal {\n bool success;\n\n /// @solidity memory-safe-assembly\n assembly {\n // Get a pointer to some free memory.\n let freeMemoryPointer := mload(0x40)\n\n // Write the abi-encoded calldata into memory, beginning with the function selector.\n mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)\n mstore(add(freeMemoryPointer, 4), to) // Append the \"to\" argument.\n mstore(add(freeMemoryPointer, 36), amount) // Append the \"amount\" argument.\n\n success := and(\n // Set success to whether the call reverted, if not we check it either\n // returned exactly 1 (can't just be non-zero data), or had no return data.\n or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),\n // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.\n // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.\n // Counterintuitively, this call must be positioned second to the or() call in the\n // surrounding and() call or else returndatasize() will be zero during the computation.\n call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)\n )\n }\n\n require(success, \"APPROVE_FAILED\");\n }\n}\n" - }, - "src/controllers/BaseController.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {ISocketRequest} from \"../interfaces/ISocketRequest.sol\";\nimport {ISocketRoute} from \"../interfaces/ISocketRoute.sol\";\n\n/// @title BaseController Controller\n/// @notice Base contract for all controller contracts\nabstract contract BaseController {\n /// @notice Address used to identify if it is a native token transfer or not\n address public immutable NATIVE_TOKEN_ADDRESS =\n address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);\n\n /// @notice Address used to identify if it is a Zero address\n address public immutable NULL_ADDRESS = address(0);\n\n /// @notice FunctionSelector used to delegatecall from swap to the function of bridge router implementation\n bytes4 public immutable BRIDGE_AFTER_SWAP_SELECTOR =\n bytes4(keccak256(\"bridgeAfterSwap(uint256,bytes)\"));\n\n /// @notice immutable variable to store the socketGateway address\n address public immutable socketGatewayAddress;\n\n /// @notice immutable variable with instance of SocketRoute to access route functions\n ISocketRoute public immutable socketRoute;\n\n /**\n * @notice Construct the base for all controllers.\n * @param _socketGatewayAddress Socketgateway address, an immutable variable to set.\n * @notice initialize the immutable variables of SocketRoute, SocketGateway\n */\n constructor(address _socketGatewayAddress) {\n socketGatewayAddress = _socketGatewayAddress;\n socketRoute = ISocketRoute(_socketGatewayAddress);\n }\n\n /**\n * @notice Construct the base for all BridgeImplementations.\n * @param routeId routeId mapped to the routrImplementation\n * @param data transactionData generated with arguments of bridgeRequest (offchain or by caller)\n * @return returns the bytes response of the route execution (bridging, refuel or swap executions)\n */\n function _executeRoute(\n uint32 routeId,\n bytes memory data\n ) internal returns (bytes memory) {\n (bool success, bytes memory result) = socketRoute\n .getRoute(routeId)\n .delegatecall(data);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n return result;\n }\n}\n" - }, - "src/interfaces/ISocketRoute.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/**\n * @title ISocketRoute\n * @notice Interface for routeManagement functions in SocketGateway.\n * @author Socket dot tech.\n */\ninterface ISocketRoute {\n /**\n * @notice Add route to the socketGateway\n This is a restricted function to be called by only socketGatewayOwner\n * @dev ensure routeAddress is a verified bridge or middleware implementation address\n * @param routeAddress The address of bridge or middleware implementation contract deployed\n * @return Id of the route added to the routes-mapping in socketGateway storage\n */\n function addRoute(address routeAddress) external returns (uint256);\n\n /**\n * @notice disable a route by setting ZeroAddress to the entry in routes-mapping\n identified by routeId as key.\n This is a restricted function to be called by only socketGatewayOwner\n * @param routeId The Id of route-implementation in the routes mapping\n */\n function disableRoute(uint32 routeId) external;\n\n /**\n * @notice Get routeImplementation address mapped to the routeId\n * @param routeId routeId is the key in the mapping for routes\n * @return route-implementation address\n */\n function getRoute(uint32 routeId) external view returns (address);\n}\n" - }, - "src/SocketGatewayDeployment.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\npragma experimental ABIEncoderV2;\n\nimport \"./utils/Ownable.sol\";\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {LibUtil} from \"./libraries/LibUtil.sol\";\nimport \"./libraries/LibBytes.sol\";\nimport {ISocketRoute} from \"./interfaces/ISocketRoute.sol\";\nimport {ISocketRequest} from \"./interfaces/ISocketRequest.sol\";\nimport {ISocketGateway} from \"./interfaces/ISocketGateway.sol\";\nimport {IncorrectBridgeRatios, ZeroAddressNotAllowed, ArrayLengthMismatch} from \"./errors/SocketErrors.sol\";\n\n/// @title SocketGatewayContract\n/// @notice Socketgateway is a contract with entrypoint functions for all interactions with socket liquidity layer\n/// @author Socket Team\ncontract SocketGateway is Ownable {\n using LibBytes for bytes;\n using LibBytes for bytes4;\n using SafeTransferLib for ERC20;\n\n /// @notice FunctionSelector used to delegatecall from swap to the function of bridge router implementation\n bytes4 public immutable BRIDGE_AFTER_SWAP_SELECTOR =\n bytes4(keccak256(\"bridgeAfterSwap(uint256,bytes)\"));\n\n /// @notice storage variable to keep track of total number of routes registered in socketgateway\n uint32 public routesCount = 385;\n\n /// @notice storage variable to keep track of total number of controllers registered in socketgateway\n uint32 public controllerCount;\n\n address public immutable disabledRouteAddress;\n\n uint256 public constant CENT_PERCENT = 100e18;\n\n /// @notice storage mapping for route implementation addresses\n mapping(uint32 => address) public routes;\n\n /// storage mapping for controller implemenation addresses\n mapping(uint32 => address) public controllers;\n\n // Events ------------------------------------------------------------------------------------------------------->\n\n /// @notice Event emitted when a router is added to socketgateway\n event NewRouteAdded(uint32 indexed routeId, address indexed route);\n\n /// @notice Event emitted when a route is disabled\n event RouteDisabled(uint32 indexed routeId);\n\n /// @notice Event emitted when ownership transfer is requested by socket-gateway-owner\n event OwnershipTransferRequested(\n address indexed _from,\n address indexed _to\n );\n\n /// @notice Event emitted when a controller is added to socketgateway\n event ControllerAdded(\n uint32 indexed controllerId,\n address indexed controllerAddress\n );\n\n /// @notice Event emitted when a controller is disabled\n event ControllerDisabled(uint32 indexed controllerId);\n\n constructor(address _owner, address _disabledRoute) Ownable(_owner) {\n disabledRouteAddress = _disabledRoute;\n }\n\n // Able to receive ether\n // solhint-disable-next-line no-empty-blocks\n receive() external payable {}\n\n /*******************************************\n * EXTERNAL AND PUBLIC FUNCTIONS *\n *******************************************/\n\n /**\n * @notice executes functions in the routes identified using routeId and functionSelectorData\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped\n * @dev ensure the data in routeData to be built using the function-selector defined as a\n * constant in the route implementation contract\n * @param routeId route identifier\n * @param routeData functionSelectorData generated using the function-selector defined in the route Implementation\n */\n function executeRoute(\n uint32 routeId,\n bytes calldata routeData\n ) external payable returns (bytes memory) {\n (bool success, bytes memory result) = addressAt(routeId).delegatecall(\n routeData\n );\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n return result;\n }\n\n /**\n * @notice swaps a token on sourceChain and split it across multiple bridge-recipients\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being swapped\n * @dev ensure the swap-data and bridge-data is generated using the function-selector defined as a constant in the implementation address\n * @param swapMultiBridgeRequest request\n */\n function swapAndMultiBridge(\n ISocketRequest.SwapMultiBridgeRequest calldata swapMultiBridgeRequest\n ) external payable {\n uint256 requestLength = swapMultiBridgeRequest.bridgeRouteIds.length;\n\n if (\n requestLength != swapMultiBridgeRequest.bridgeImplDataItems.length\n ) {\n revert ArrayLengthMismatch();\n }\n uint256 ratioAggregate;\n for (uint256 index = 0; index < requestLength; ) {\n ratioAggregate += swapMultiBridgeRequest.bridgeRatios[index];\n }\n\n if (ratioAggregate != CENT_PERCENT) {\n revert IncorrectBridgeRatios();\n }\n\n (bool swapSuccess, bytes memory swapResult) = addressAt(\n swapMultiBridgeRequest.swapRouteId\n ).delegatecall(swapMultiBridgeRequest.swapImplData);\n\n if (!swapSuccess) {\n assembly {\n revert(add(swapResult, 32), mload(swapResult))\n }\n }\n\n uint256 amountReceivedFromSwap = abi.decode(swapResult, (uint256));\n\n uint256 bridgedAmount;\n\n for (uint256 index = 0; index < requestLength; ) {\n uint256 bridgingAmount;\n\n // if it is the last bridge request, bridge the remaining amount\n if (index == requestLength - 1) {\n bridgingAmount = amountReceivedFromSwap - bridgedAmount;\n } else {\n // bridging amount is the multiplication of bridgeRatio and amountReceivedFromSwap\n bridgingAmount =\n (amountReceivedFromSwap *\n swapMultiBridgeRequest.bridgeRatios[index]) /\n (CENT_PERCENT);\n }\n\n // update the bridged amount, this would be used for computation for last bridgeRequest\n bridgedAmount += bridgingAmount;\n\n bytes memory bridgeImpldata = abi.encodeWithSelector(\n BRIDGE_AFTER_SWAP_SELECTOR,\n bridgingAmount,\n swapMultiBridgeRequest.bridgeImplDataItems[index]\n );\n\n (bool bridgeSuccess, bytes memory bridgeResult) = addressAt(\n swapMultiBridgeRequest.bridgeRouteIds[index]\n ).delegatecall(bridgeImpldata);\n\n if (!bridgeSuccess) {\n assembly {\n revert(add(bridgeResult, 32), mload(bridgeResult))\n }\n }\n\n unchecked {\n ++index;\n }\n }\n }\n\n /**\n * @notice sequentially executes functions in the routes identified using routeId and functionSelectorData\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped\n * @dev ensure the data in each dataItem to be built using the function-selector defined as a\n * constant in the route implementation contract\n * @param routeIds a list of route identifiers\n * @param dataItems a list of functionSelectorData generated using the function-selector defined in the route Implementation\n */\n function executeRoutes(\n uint32[] calldata routeIds,\n bytes[] calldata dataItems\n ) external payable {\n uint256 routeIdslength = routeIds.length;\n if (routeIdslength != dataItems.length) revert ArrayLengthMismatch();\n for (uint256 index = 0; index < routeIdslength; ) {\n (bool success, bytes memory result) = addressAt(routeIds[index])\n .delegatecall(dataItems[index]);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n unchecked {\n ++index;\n }\n }\n }\n\n /**\n * @notice execute a controller function identified using the controllerId in the request\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped\n * @dev ensure the data in request to be built using the function-selector defined as a\n * constant in the controller implementation contract\n * @param socketControllerRequest socketControllerRequest with controllerId to identify the\n * controllerAddress and byteData constructed using functionSelector\n * of the function being invoked\n * @return bytes data received from the call delegated to controller\n */\n function executeController(\n ISocketGateway.SocketControllerRequest calldata socketControllerRequest\n ) external payable returns (bytes memory) {\n (bool success, bytes memory result) = controllers[\n socketControllerRequest.controllerId\n ].delegatecall(socketControllerRequest.data);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n return result;\n }\n\n /**\n * @notice sequentially executes all controller requests\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped\n * @dev ensure the data in each controller-request to be built using the function-selector defined as a\n * constant in the controller implementation contract\n * @param controllerRequests a list of socketControllerRequest\n * Each controllerRequest contains controllerId to identify the controllerAddress and\n * byteData constructed using functionSelector of the function being invoked\n */\n function executeControllers(\n ISocketGateway.SocketControllerRequest[] calldata controllerRequests\n ) external payable {\n for (uint32 index = 0; index < controllerRequests.length; ) {\n (bool success, bytes memory result) = controllers[\n controllerRequests[index].controllerId\n ].delegatecall(controllerRequests[index].data);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n unchecked {\n ++index;\n }\n }\n }\n\n /**************************************\n * ADMIN FUNCTIONS *\n **************************************/\n\n /**\n * @notice Add route to the socketGateway\n This is a restricted function to be called by only socketGatewayOwner\n * @dev ensure routeAddress is a verified bridge or middleware implementation address\n * @param routeAddress The address of bridge or middleware implementation contract deployed\n * @return Id of the route added to the routes-mapping in socketGateway storage\n */\n function addRoute(\n address routeAddress\n ) external onlyOwner returns (uint32) {\n uint32 routeId = routesCount;\n routes[routeId] = routeAddress;\n\n routesCount += 1;\n\n emit NewRouteAdded(routeId, routeAddress);\n\n return routeId;\n }\n\n /**\n * @notice Give Infinite or 0 approval to bridgeRoute for the tokenAddress\n This is a restricted function to be called by only socketGatewayOwner\n */\n\n function setApprovalForRouters(\n address[] memory routeAddresses,\n address[] memory tokenAddresses,\n bool isMax\n ) external onlyOwner {\n for (uint32 index = 0; index < routeAddresses.length; ) {\n ERC20(tokenAddresses[index]).approve(\n routeAddresses[index],\n isMax ? type(uint256).max : 0\n );\n unchecked {\n ++index;\n }\n }\n }\n\n /**\n * @notice Add controller to the socketGateway\n This is a restricted function to be called by only socketGatewayOwner\n * @dev ensure controllerAddress is a verified controller implementation address\n * @param controllerAddress The address of controller implementation contract deployed\n * @return Id of the controller added to the controllers-mapping in socketGateway storage\n */\n function addController(\n address controllerAddress\n ) external onlyOwner returns (uint32) {\n uint32 controllerId = controllerCount;\n\n controllers[controllerId] = controllerAddress;\n\n controllerCount += 1;\n\n emit ControllerAdded(controllerId, controllerAddress);\n\n return controllerId;\n }\n\n /**\n * @notice disable controller by setting ZeroAddress to the entry in controllers-mapping\n identified by controllerId as key.\n This is a restricted function to be called by only socketGatewayOwner\n * @param controllerId The Id of controller-implementation in the controllers mapping\n */\n function disableController(uint32 controllerId) public onlyOwner {\n controllers[controllerId] = disabledRouteAddress;\n emit ControllerDisabled(controllerId);\n }\n\n /**\n * @notice disable a route by setting ZeroAddress to the entry in routes-mapping\n identified by routeId as key.\n This is a restricted function to be called by only socketGatewayOwner\n * @param routeId The Id of route-implementation in the routes mapping\n */\n function disableRoute(uint32 routeId) external onlyOwner {\n routes[routeId] = disabledRouteAddress;\n emit RouteDisabled(routeId);\n }\n\n /*******************************************\n * RESTRICTED RESCUE FUNCTIONS *\n *******************************************/\n\n /**\n * @notice Rescues the ERC20 token to an address\n this is a restricted function to be called by only socketGatewayOwner\n * @dev as this is a restricted to socketGatewayOwner, ensure the userAddress is a known address\n * @param token address of the ERC20 token being rescued\n * @param userAddress address to which ERC20 is to be rescued\n * @param amount amount of ERC20 tokens being rescued\n */\n function rescueFunds(\n address token,\n address userAddress,\n uint256 amount\n ) external onlyOwner {\n ERC20(token).safeTransfer(userAddress, amount);\n }\n\n /**\n * @notice Rescues the native balance to an address\n this is a restricted function to be called by only socketGatewayOwner\n * @dev as this is a restricted to socketGatewayOwner, ensure the userAddress is a known address\n * @param userAddress address to which native-balance is to be rescued\n * @param amount amount of native-balance being rescued\n */\n function rescueEther(\n address payable userAddress,\n uint256 amount\n ) external onlyOwner {\n userAddress.transfer(amount);\n }\n\n /*******************************************\n * VIEW FUNCTIONS *\n *******************************************/\n\n /**\n * @notice Get routeImplementation address mapped to the routeId\n * @param routeId routeId is the key in the mapping for routes\n * @return route-implementation address\n */\n function getRoute(uint32 routeId) public view returns (address) {\n return addressAt(routeId);\n }\n\n /**\n * @notice Get controllerImplementation address mapped to the controllerId\n * @param controllerId controllerId is the key in the mapping for controllers\n * @return controller-implementation address\n */\n function getController(uint32 controllerId) public view returns (address) {\n return controllers[controllerId];\n }\n\n function addressAt(uint32 routeId) public view returns (address) {\n if (routeId < 385) {\n if (routeId < 257) {\n if (routeId < 129) {\n if (routeId < 65) {\n if (routeId < 33) {\n if (routeId < 17) {\n if (routeId < 9) {\n if (routeId < 5) {\n if (routeId < 3) {\n if (routeId == 1) {\n return\n 0x8cd6BaCDAe46B449E2e5B34e348A4eD459c84D50;\n } else {\n return\n 0x31524750Cd865fF6A3540f232754Fb974c18585C;\n }\n } else {\n if (routeId == 3) {\n return\n 0xEd9b37342BeC8f3a2D7b000732ec87498aA6EC6a;\n } else {\n return\n 0xE8704Ef6211F8988Ccbb11badC89841808d66890;\n }\n }\n } else {\n if (routeId < 7) {\n if (routeId == 5) {\n return\n 0x9aFF58C460a461578C433e11C4108D1c4cF77761;\n } else {\n return\n 0x2D1733886cFd465B0B99F1492F40847495f334C5;\n }\n } else {\n if (routeId == 7) {\n return\n 0x715497Be4D130F04B8442F0A1F7a9312D4e54FC4;\n } else {\n return\n 0x90C8a40c38E633B5B0e0d0585b9F7FA05462CaaF;\n }\n }\n }\n } else {\n if (routeId < 13) {\n if (routeId < 11) {\n if (routeId == 9) {\n return\n 0xa402b70FCfF3F4a8422B93Ef58E895021eAdE4F6;\n } else {\n return\n 0xc1B718522E15CD42C4Ac385a929fc2B51f5B892e;\n }\n } else {\n if (routeId == 11) {\n return\n 0xa97bf2f7c26C43c010c349F52f5eA5dC49B2DD38;\n } else {\n return\n 0x969423d71b62C81d2f28d707364c9Dc4a0764c53;\n }\n }\n } else {\n if (routeId < 15) {\n if (routeId == 13) {\n return\n 0xF86729934C083fbEc8C796068A1fC60701Ea1207;\n } else {\n return\n 0xD7cC2571F5823caCA26A42690D2BE7803DD5393f;\n }\n } else {\n if (routeId == 15) {\n return\n 0x7c8837a279bbbf7d8B93413763176de9F65d5bB9;\n } else {\n return\n 0x13b81C27B588C07D04458ed7dDbdbD26D1e39bcc;\n }\n }\n }\n }\n } else {\n if (routeId < 25) {\n if (routeId < 21) {\n if (routeId < 19) {\n if (routeId == 17) {\n return\n 0x52560Ac678aFA1345D15474287d16Dc1eA3F78aE;\n } else {\n return\n 0x1E31e376551459667cd7643440c1b21CE69065A0;\n }\n } else {\n if (routeId == 19) {\n return\n 0xc57D822CB3288e7b97EF8f8af0EcdcD1B783529B;\n } else {\n return\n 0x2197A1D9Af24b4d6a64Bff95B4c29Fcd3Ff28C30;\n }\n }\n } else {\n if (routeId < 23) {\n if (routeId == 21) {\n return\n 0xE3700feAa5100041Bf6b7AdBA1f72f647809Fd00;\n } else {\n return\n 0xc02E8a0Fdabf0EeFCEA025163d90B5621E2b9948;\n }\n } else {\n if (routeId == 23) {\n return\n 0xF5144235E2926cAb3c69b30113254Fa632f72d62;\n } else {\n return\n 0xBa3F92313B00A1f7Bc53b2c24EB195c8b2F57682;\n }\n }\n }\n } else {\n if (routeId < 29) {\n if (routeId < 27) {\n if (routeId == 25) {\n return\n 0x77a6856fe1fFA5bEB55A1d2ED86E27C7c482CB76;\n } else {\n return\n 0x4826Ff4e01E44b1FCEFBfb38cd96687Eb7786b44;\n }\n } else {\n if (routeId == 27) {\n return\n 0x55FF3f5493cf5e80E76DEA7E327b9Cd8440Af646;\n } else {\n return\n 0xF430Db544bE9770503BE4aa51997aA19bBd5BA4f;\n }\n }\n } else {\n if (routeId < 31) {\n if (routeId == 29) {\n return\n 0x0f166446ce1484EE3B0663E7E67DF10F5D240115;\n } else {\n return\n 0x6365095D92537f242Db5EdFDd572745E72aC33d9;\n }\n } else {\n if (routeId == 31) {\n return\n 0x5c7BC93f06ce3eAe75ADf55E10e23d2c1dE5Bc65;\n } else {\n return\n 0xe46383bAD90d7A08197ccF08972e9DCdccCE9BA4;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 49) {\n if (routeId < 41) {\n if (routeId < 37) {\n if (routeId < 35) {\n if (routeId == 33) {\n return\n 0xf0f21710c071E3B728bdc4654c3c0b873aAaa308;\n } else {\n return\n 0x63Bc9ed3AcAAeB0332531C9fB03b0a2352E9Ff25;\n }\n } else {\n if (routeId == 35) {\n return\n 0xd1CE808625CB4007a1708824AE82CdB0ece57De9;\n } else {\n return\n 0x57BbB148112f4ba224841c3FE018884171004661;\n }\n }\n } else {\n if (routeId < 39) {\n if (routeId == 37) {\n return\n 0x037f7d6933036F34DFabd40Ff8e4D789069f92e3;\n } else {\n return\n 0xeF978c280915CfF3Dca4EDfa8932469e40ADA1e1;\n }\n } else {\n if (routeId == 39) {\n return\n 0x92ee9e071B13f7ecFD62B7DED404A16CBc223CD3;\n } else {\n return\n 0x94Ae539c186e41ed762271338Edf140414D1E442;\n }\n }\n }\n } else {\n if (routeId < 45) {\n if (routeId < 43) {\n if (routeId == 41) {\n return\n 0x30A64BBe4DdBD43dA2368EFd1eB2d80C10d84DAb;\n } else {\n return\n 0x3aEABf81c1Dc4c1b73d5B2a95410f126426FB596;\n }\n } else {\n if (routeId == 43) {\n return\n 0x25b08aB3D0C8ea4cC9d967b79688C6D98f3f563a;\n } else {\n return\n 0xea40cB15C9A3BBd27af6474483886F7c0c9AE406;\n }\n }\n } else {\n if (routeId < 47) {\n if (routeId == 45) {\n return\n 0x9580113Cc04e5a0a03359686304EF3A80b936Dd3;\n } else {\n return\n 0xD211c826d568957F3b66a3F4d9c5f68cCc66E619;\n }\n } else {\n if (routeId == 47) {\n return\n 0xCEE24D0635c4C56315d133b031984d4A6f509476;\n } else {\n return\n 0x3922e6B987983229798e7A20095EC372744d4D4c;\n }\n }\n }\n }\n } else {\n if (routeId < 57) {\n if (routeId < 53) {\n if (routeId < 51) {\n if (routeId == 49) {\n return\n 0x2d92D03413d296e1F31450479349757187F2a2b7;\n } else {\n return\n 0x0fe5308eE90FC78F45c89dB6053eA859097860CA;\n }\n } else {\n if (routeId == 51) {\n return\n 0x08Ba68e067C0505bAF0C1311E0cFB2B1B59b969c;\n } else {\n return\n 0x9bee5DdDF75C24897374f92A534B7A6f24e97f4a;\n }\n }\n } else {\n if (routeId < 55) {\n if (routeId == 53) {\n return\n 0x1FC5A90B232208704B930c1edf82FFC6ACc02734;\n } else {\n return\n 0x5b1B0417cb44c761C2a23ee435d011F0214b3C85;\n }\n } else {\n if (routeId == 55) {\n return\n 0x9d70cDaCA12A738C283020760f449D7816D592ec;\n } else {\n return\n 0x95a23b9CB830EcCFDDD5dF56A4ec665e3381Fa12;\n }\n }\n }\n } else {\n if (routeId < 61) {\n if (routeId < 59) {\n if (routeId == 57) {\n return\n 0x483a957Cf1251c20e096C35c8399721D1200A3Fc;\n } else {\n return\n 0xb4AD39Cb293b0Ec7FEDa743442769A7FF04987CD;\n }\n } else {\n if (routeId == 59) {\n return\n 0x4C543AD78c1590D81BAe09Fc5B6Df4132A2461d0;\n } else {\n return\n 0x471d5E5195c563902781734cfe1FF3981F8B6c86;\n }\n }\n } else {\n if (routeId < 63) {\n if (routeId == 61) {\n return\n 0x1B12a54B5E606D95B8B8D123c9Cb09221Ee37584;\n } else {\n return\n 0xE4127cC550baC433646a7D998775a84daC16c7f3;\n }\n } else {\n if (routeId == 63) {\n return\n 0xecb1b55AB12E7dd788D585c6C5cD61B5F87be836;\n } else {\n return\n 0xf91ef487C5A1579f70601b6D347e19756092eEBf;\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 97) {\n if (routeId < 81) {\n if (routeId < 73) {\n if (routeId < 69) {\n if (routeId < 67) {\n if (routeId == 65) {\n return\n 0x34a16a7e9BADEEFD4f056310cbE0b1423Fa1b760;\n } else {\n return\n 0x60E10E80c7680f429dBbC232830BEcd3D623c4CF;\n }\n } else {\n if (routeId == 67) {\n return\n 0x66465285B8D65362A1d86CE00fE2bE949Fd6debF;\n } else {\n return\n 0x5aB231B7e1A3A74a48f67Ab7bde5Cdd4267022E0;\n }\n }\n } else {\n if (routeId < 71) {\n if (routeId == 69) {\n return\n 0x3A1C3633eE79d43366F5c67802a746aFD6b162Ba;\n } else {\n return\n 0x0C4BfCbA8dC3C811437521a80E81e41DAF479039;\n }\n } else {\n if (routeId == 71) {\n return\n 0x6caf25d2e139C5431a1FA526EAf8d73ff2e6252C;\n } else {\n return\n 0x74ad21e09FDa68638CE14A3009A79B6D16574257;\n }\n }\n }\n } else {\n if (routeId < 77) {\n if (routeId < 75) {\n if (routeId == 73) {\n return\n 0xD4923A61008894b99cc1CD3407eF9524f02aA0Ca;\n } else {\n return\n 0x6F159b5EB823BD415886b9271aA2A723a00a1987;\n }\n } else {\n if (routeId == 75) {\n return\n 0x742a8aA42E7bfB4554dE30f4Fb07FFb6f2068863;\n } else {\n return\n 0x4AE9702d3360400E47B446e76DE063ACAb930101;\n }\n }\n } else {\n if (routeId < 79) {\n if (routeId == 77) {\n return\n 0x0E19a0a44ddA7dAD854ec5Cc867d16869c4E80F4;\n } else {\n return\n 0xE021A51968f25148F726E326C88d2556c5647557;\n }\n } else {\n if (routeId == 79) {\n return\n 0x64287BDDDaeF4d94E4599a3D882bed29E6Ada4B6;\n } else {\n return\n 0xcBB57Fd2e19cc7e9D444d5b4325A2F1047d0C73f;\n }\n }\n }\n }\n } else {\n if (routeId < 89) {\n if (routeId < 85) {\n if (routeId < 83) {\n if (routeId == 81) {\n return\n 0x373DE80DF7D82cFF6D76F29581b360C56331e957;\n } else {\n return\n 0x0466356E131AD61596a51F86BAd1C03A328960D8;\n }\n } else {\n if (routeId == 83) {\n return\n 0x01726B960992f1b74311b248E2a922fC707d43A6;\n } else {\n return\n 0x2E21bdf9A4509b89795BCE7E132f248a75814CEc;\n }\n }\n } else {\n if (routeId < 87) {\n if (routeId == 85) {\n return\n 0x769512b23aEfF842379091d3B6E4B5456F631D42;\n } else {\n return\n 0xe7eD9be946a74Ec19325D39C6EEb57887ccB2B0D;\n }\n } else {\n if (routeId == 87) {\n return\n 0xc4D01Ec357c2b511d10c15e6b6974380F0E62e67;\n } else {\n return\n 0x5bC49CC9dD77bECF2fd3A3C55611e84E69AFa3AE;\n }\n }\n }\n } else {\n if (routeId < 93) {\n if (routeId < 91) {\n if (routeId == 89) {\n return\n 0x48bcD879954fA14e7DbdAeb56F79C1e9DDcb69ec;\n } else {\n return\n 0xE929bDde21b462572FcAA4de6F49B9D3246688D0;\n }\n } else {\n if (routeId == 91) {\n return\n 0x85Aae300438222f0e3A9Bc870267a5633A9438bd;\n } else {\n return\n 0x51f72E1096a81C55cd142d66d39B688C657f9Be8;\n }\n }\n } else {\n if (routeId < 95) {\n if (routeId == 93) {\n return\n 0x3A8a05BF68ac54B01E6C0f492abF97465F3d15f9;\n } else {\n return\n 0x145aA67133F0c2C36b9771e92e0B7655f0D59040;\n }\n } else {\n if (routeId == 95) {\n return\n 0xa030315d7DB11F9892758C9e7092D841e0ADC618;\n } else {\n return\n 0xdF1f8d81a3734bdDdEfaC6Ca1596E081e57c3044;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 113) {\n if (routeId < 105) {\n if (routeId < 101) {\n if (routeId < 99) {\n if (routeId == 97) {\n return\n 0xFF2833123B58aa05d04D7fb99f5FB768B2b435F8;\n } else {\n return\n 0xc8f09c1fD751C570233765f71b0e280d74e6e743;\n }\n } else {\n if (routeId == 99) {\n return\n 0x3026DA6Ceca2E5A57A05153653D9212FFAaA49d8;\n } else {\n return\n 0xdE68Ee703dE0D11f67B0cE5891cB4a903de6D160;\n }\n }\n } else {\n if (routeId < 103) {\n if (routeId == 101) {\n return\n 0xE23a7730e81FB4E87A6D0bd9f63EE77ac86C3DA4;\n } else {\n return\n 0x8b1DBe04aD76a7d8bC079cACd3ED4D99B897F4a0;\n }\n } else {\n if (routeId == 103) {\n return\n 0xBB227240FA459b69C6889B2b8cb1BE76F118061f;\n } else {\n return\n 0xC062b9b3f0dB28BB8afAfcD4d075729344114ffe;\n }\n }\n }\n } else {\n if (routeId < 109) {\n if (routeId < 107) {\n if (routeId == 105) {\n return\n 0x553188Aa45f5FDB83EC4Ca485982F8fC082480D1;\n } else {\n return\n 0x0109d83D746EaCb6d4014953D9E12d6ca85e330b;\n }\n } else {\n if (routeId == 107) {\n return\n 0x45B1bEd29812F5bf6711074ACD180B2aeB783AD9;\n } else {\n return\n 0xdA06eC8c19aea31D77F60299678Cba40E743e1aD;\n }\n }\n } else {\n if (routeId < 111) {\n if (routeId == 109) {\n return\n 0x3cC5235c97d975a9b4FD4501B3446c981ea3D855;\n } else {\n return\n 0xa1827267d6Bd989Ff38580aE3d9deff6Acf19163;\n }\n } else {\n if (routeId == 111) {\n return\n 0x3663CAA0433A3D4171b3581Cf2410702840A735A;\n } else {\n return\n 0x7575D0a7614F655BA77C74a72a43bbd4fA6246a3;\n }\n }\n }\n }\n } else {\n if (routeId < 121) {\n if (routeId < 117) {\n if (routeId < 115) {\n if (routeId == 113) {\n return\n 0x2516Defc18bc07089c5dAFf5eafD7B0EF64611E2;\n } else {\n return\n 0xfec5FF08E20fbc107a97Af2D38BD0025b84ee233;\n }\n } else {\n if (routeId == 115) {\n return\n 0x0FB5763a87242B25243e23D73f55945fE787523A;\n } else {\n return\n 0xe4C00db89678dBf8391f430C578Ca857Dd98aDE1;\n }\n }\n } else {\n if (routeId < 119) {\n if (routeId == 117) {\n return\n 0x8F2A22061F9F35E64f14523dC1A5f8159e6a21B7;\n } else {\n return\n 0x18e4b838ae966917E20E9c9c5Ad359cDD38303bB;\n }\n } else {\n if (routeId == 119) {\n return\n 0x61ACb1d3Dcb3e3429832A164Cc0fC9849fb75A4a;\n } else {\n return\n 0x7681e3c8e7A41DCA55C257cc0d1Ae757f5530E65;\n }\n }\n }\n } else {\n if (routeId < 125) {\n if (routeId < 123) {\n if (routeId == 121) {\n return\n 0x806a2AB9748C3D1DB976550890E3f528B7E8Faec;\n } else {\n return\n 0xBDb8A5DD52C2c239fbC31E9d43B763B0197028FF;\n }\n } else {\n if (routeId == 123) {\n return\n 0x474EC9203706010B9978D6bD0b105D36755e4848;\n } else {\n return\n 0x8dfd0D829b303F2239212E591a0F92a32880f36E;\n }\n }\n } else {\n if (routeId < 127) {\n if (routeId == 125) {\n return\n 0xad4BcE9745860B1adD6F1Bd34a916f050E4c82C2;\n } else {\n return\n 0xBC701115b9fe14bC8CC5934cdC92517173e308C4;\n }\n } else {\n if (routeId == 127) {\n return\n 0x0D1918d786Db8546a11aDeD475C98370E06f255E;\n } else {\n return\n 0xee44f57cD6936DB55B99163f3Df367B01EdA785a;\n }\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 193) {\n if (routeId < 161) {\n if (routeId < 145) {\n if (routeId < 137) {\n if (routeId < 133) {\n if (routeId < 131) {\n if (routeId == 129) {\n return\n 0x63044521fe5a1e488D7eD419cD0e35b7C24F2aa7;\n } else {\n return\n 0x410085E73BD85e90d97b84A68C125aDB9F91f85b;\n }\n } else {\n if (routeId == 131) {\n return\n 0x7913fe97E07C7A397Ec274Ab1d4E2622C88EC5D1;\n } else {\n return\n 0x977f9fE93c064DCf54157406DaABC3a722e8184C;\n }\n }\n } else {\n if (routeId < 135) {\n if (routeId == 133) {\n return\n 0xCD2236468722057cFbbABad2db3DEA9c20d5B01B;\n } else {\n return\n 0x17c7287A491cf5Ff81E2678cF2BfAE4333F6108c;\n }\n } else {\n if (routeId == 135) {\n return\n 0x354D9a5Dbf96c71B79a265F03B595C6Fdc04dadd;\n } else {\n return\n 0xb4e409EB8e775eeFEb0344f9eee884cc7ed21c69;\n }\n }\n }\n } else {\n if (routeId < 141) {\n if (routeId < 139) {\n if (routeId == 137) {\n return\n 0xa1a3c4670Ad69D9be4ab2D39D1231FEC2a63b519;\n } else {\n return\n 0x4589A22199870729C1be5CD62EE93BeD858113E6;\n }\n } else {\n if (routeId == 139) {\n return\n 0x8E7b864dB26Bd6C798C38d4Ba36EbA0d6602cF11;\n } else {\n return\n 0xA2D17C7260a4CB7b9854e89Fc367E80E87872a2d;\n }\n }\n } else {\n if (routeId < 143) {\n if (routeId == 141) {\n return\n 0xC7F0EDf0A1288627b0432304918A75e9084CBD46;\n } else {\n return\n 0xE4B4EF1f9A4aBFEdB371fA7a6143993B15d4df25;\n }\n } else {\n if (routeId == 143) {\n return\n 0xfe3D84A2Ef306FEBb5452441C9BDBb6521666F6A;\n } else {\n return\n 0x8A12B6C64121920110aE58F7cd67DfEc21c6a4C3;\n }\n }\n }\n }\n } else {\n if (routeId < 153) {\n if (routeId < 149) {\n if (routeId < 147) {\n if (routeId == 145) {\n return\n 0x76c4d9aFC4717a2BAac4e5f26CccF02351f7a3DA;\n } else {\n return\n 0xd4719BA550E397aeAcca1Ad2201c1ba69024FAAf;\n }\n } else {\n if (routeId == 147) {\n return\n 0x9646126Ce025224d1682C227d915a386efc0A1Fb;\n } else {\n return\n 0x4DD8Af2E3F2044842f0247920Bc4BABb636915ea;\n }\n }\n } else {\n if (routeId < 151) {\n if (routeId == 149) {\n return\n 0x8e8a327183Af0cf8C2ece9F0ed547C42A160D409;\n } else {\n return\n 0x9D49614CaE1C685C71678CA6d8CDF7584bfd0740;\n }\n } else {\n if (routeId == 151) {\n return\n 0x5a00ef257394cbc31828d48655E3d39e9c11c93d;\n } else {\n return\n 0xC9a2751b38d3dDD161A41Ca0135C5C6c09EC1d56;\n }\n }\n }\n } else {\n if (routeId < 157) {\n if (routeId < 155) {\n if (routeId == 153) {\n return\n 0x7e1c261640a525C94Ca4f8c25b48CF754DD83590;\n } else {\n return\n 0x409Fe24ba6F6BD5aF31C1aAf8059b986A3158233;\n }\n } else {\n if (routeId == 155) {\n return\n 0x704Cf5BFDADc0f55fDBb53B6ed8B582E018A72A2;\n } else {\n return\n 0x3982bF65d7d6E77E3b6661cd6F6468c247512737;\n }\n }\n } else {\n if (routeId < 159) {\n if (routeId == 157) {\n return\n 0x3982b9f26FFD67a13Ee371e2C0a9Da338BA70E7f;\n } else {\n return\n 0x6D834AB385900c1f49055D098e90264077FbC4f2;\n }\n } else {\n if (routeId == 159) {\n return\n 0x11FE5F70779A094B7166B391e1Fb73d422eF4e4d;\n } else {\n return\n 0xD347e4E47280d21F13B73D89c6d16f867D50DD13;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 177) {\n if (routeId < 169) {\n if (routeId < 165) {\n if (routeId < 163) {\n if (routeId == 161) {\n return\n 0xb6035eDD53DDA28d8B69b4ae9836E40C80306CD7;\n } else {\n return\n 0x54c884e6f5C7CcfeCA990396c520C858c922b6CA;\n }\n } else {\n if (routeId == 163) {\n return\n 0x5eA93E240b083d686558Ed607BC013d88057cE46;\n } else {\n return\n 0x4C7131eE812De685cBe4e2cCb033d46ecD46612E;\n }\n }\n } else {\n if (routeId < 167) {\n if (routeId == 165) {\n return\n 0xc1a5Be9F0c33D8483801D702111068669f81fF91;\n } else {\n return\n 0x9E5fAb91455Be5E5b2C05967E73F456c8118B1Fc;\n }\n } else {\n if (routeId == 167) {\n return\n 0x3d9A05927223E0DC2F382831770405885e22F0d8;\n } else {\n return\n 0x6303A011fB6063f5B1681cb5a9938EA278dc6128;\n }\n }\n }\n } else {\n if (routeId < 173) {\n if (routeId < 171) {\n if (routeId == 169) {\n return\n 0xe9c60795c90C66797e4c8E97511eA07CdAda32bE;\n } else {\n return\n 0xD56cC98e69A1e13815818b466a8aA6163d84234A;\n }\n } else {\n if (routeId == 171) {\n return\n 0x47EbB9D36a6e40895316cD894E4860D774E2c531;\n } else {\n return\n 0xA5EB293629410065d14a7B1663A67829b0618292;\n }\n }\n } else {\n if (routeId < 175) {\n if (routeId == 173) {\n return\n 0x1b3B4C8146F939cE00899db8B3ddeF0062b7E023;\n } else {\n return\n 0x257Bbc11653625EbfB6A8587eF4f4FBe49828EB3;\n }\n } else {\n if (routeId == 175) {\n return\n 0x44cc979C01b5bB1eAC21301E73C37200dFD06F59;\n } else {\n return\n 0x2972fDF43352225D82754C0174Ff853819D1ef2A;\n }\n }\n }\n }\n } else {\n if (routeId < 185) {\n if (routeId < 181) {\n if (routeId < 179) {\n if (routeId == 177) {\n return\n 0x3e54144f032648A04D62d79f7B4b93FF3aC2333b;\n } else {\n return\n 0x444016102dB8adbE73C3B6703a1ea7F2f75A510D;\n }\n } else {\n if (routeId == 179) {\n return\n 0xac079143f98a6eb744Fde34541ebF243DF5B5dED;\n } else {\n return\n 0xAe9010767Fb112d29d35CEdfba2b372Ad7A308d3;\n }\n }\n } else {\n if (routeId < 183) {\n if (routeId == 181) {\n return\n 0xfE0BCcF9cCC2265D5fB3450743f17DfE57aE1e56;\n } else {\n return\n 0x04ED8C0545716119437a45386B1d691C63234C7D;\n }\n } else {\n if (routeId == 183) {\n return\n 0x636c14013e531A286Bc4C848da34585f0bB73d59;\n } else {\n return\n 0x2Fa67fc7ECC5cAA01C653d3BFeA98ecc5db9C42A;\n }\n }\n }\n } else {\n if (routeId < 189) {\n if (routeId < 187) {\n if (routeId == 185) {\n return\n 0x23e9a0FC180818aA872D2079a985217017E97bd9;\n } else {\n return\n 0x79A95c3Ef81b3ae64ee03A9D5f73e570495F164E;\n }\n } else {\n if (routeId == 187) {\n return\n 0xa7EA0E88F04a84ba0ad1E396cb07Fa3fDAD7dF6D;\n } else {\n return\n 0xd23cA1278a2B01a3C0Ca1a00d104b11c1Ebe6f42;\n }\n }\n } else {\n if (routeId < 191) {\n if (routeId == 189) {\n return\n 0x707bc4a9FA2E349AED5df4e9f5440C15aA9D14Bd;\n } else {\n return\n 0x7E290F2dd539Ac6CE58d8B4C2B944931a1fD3612;\n }\n } else {\n if (routeId == 191) {\n return\n 0x707AA5503088Ce06Ba450B6470A506122eA5c8eF;\n } else {\n return\n 0xFbB3f7BF680deeb149f4E7BC30eA3DDfa68F3C3f;\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 225) {\n if (routeId < 209) {\n if (routeId < 201) {\n if (routeId < 197) {\n if (routeId < 195) {\n if (routeId == 193) {\n return\n 0xDE74aD8cCC3dbF14992f49Cf24f36855912f4934;\n } else {\n return\n 0x409BA83df7777F070b2B50a10a41DE2468d2a3B3;\n }\n } else {\n if (routeId == 195) {\n return\n 0x5CB7Be90A5DD7CfDa54e87626e254FE8C18255B4;\n } else {\n return\n 0x0A684fE12BC64fb72B59d0771a566F49BC090356;\n }\n }\n } else {\n if (routeId < 199) {\n if (routeId == 197) {\n return\n 0xDf30048d91F8FA2bCfC54952B92bFA8e161D3360;\n } else {\n return\n 0x050825Fff032a547C47061CF0696FDB0f65AEa5D;\n }\n } else {\n if (routeId == 199) {\n return\n 0xd55e671dAC1f03d366d8535073ada5DB2Aab1Ea2;\n } else {\n return\n 0x9470C704A9616c8Cd41c595Fcd2181B6fe2183C2;\n }\n }\n }\n } else {\n if (routeId < 205) {\n if (routeId < 203) {\n if (routeId == 201) {\n return\n 0x2D9ffD275181F5865d5e11CbB4ced1521C4dF9f1;\n } else {\n return\n 0x816d28Dec10ec95DF5334f884dE85cA6215918d8;\n }\n } else {\n if (routeId == 203) {\n return\n 0xd1f87267c4A43835E666dd69Df077e578A3b6299;\n } else {\n return\n 0x39E89Bde9DACbe5468C025dE371FbDa12bDeBAB1;\n }\n }\n } else {\n if (routeId < 207) {\n if (routeId == 205) {\n return\n 0x7b40A3207956ecad6686E61EfcaC48912FcD0658;\n } else {\n return\n 0x090cF10D793B1Efba9c7D76115878814B663859A;\n }\n } else {\n if (routeId == 207) {\n return\n 0x312A59c06E41327878F2063eD0e9c282C1DA3AfC;\n } else {\n return\n 0x4F1188f46236DD6B5de11Ebf2a9fF08716E7DeB6;\n }\n }\n }\n }\n } else {\n if (routeId < 217) {\n if (routeId < 213) {\n if (routeId < 211) {\n if (routeId == 209) {\n return\n 0x0A6F9a3f4fA49909bBfb4339cbE12B42F53BbBeD;\n } else {\n return\n 0x01d13d7aCaCbB955B81935c80ffF31e14BdFa71f;\n }\n } else {\n if (routeId == 211) {\n return\n 0x691a14Fa6C7360422EC56dF5876f84d4eDD7f00A;\n } else {\n return\n 0x97Aad18d886d181a9c726B3B6aE15a0A69F5aF73;\n }\n }\n } else {\n if (routeId < 215) {\n if (routeId == 213) {\n return\n 0x2917241371D2099049Fa29432DC46735baEC33b4;\n } else {\n return\n 0x5F20F20F7890c2e383E29D4147C9695A371165f5;\n }\n } else {\n if (routeId == 215) {\n return\n 0xeC0a60e639958335662C5219A320cCEbb56C6077;\n } else {\n return\n 0x96d63CF5062975C09845d17ec672E10255866053;\n }\n }\n }\n } else {\n if (routeId < 221) {\n if (routeId < 219) {\n if (routeId == 217) {\n return\n 0xFF57429e57D383939CAB50f09ABBfB63C0e6c9AD;\n } else {\n return\n 0x18E393A7c8578fb1e235C242076E50013cDdD0d7;\n }\n } else {\n if (routeId == 219) {\n return\n 0xE7E5238AF5d61f52E9B4ACC025F713d1C0216507;\n } else {\n return\n 0x428401D4d0F25A2EE1DA4d5366cB96Ded425D9bD;\n }\n }\n } else {\n if (routeId < 223) {\n if (routeId == 221) {\n return\n 0x42E5733551ff1Ee5B48Aa9fc2B61Af9b58C812E6;\n } else {\n return\n 0x64Df9c7A0551B056d860Bc2419Ca4c1EF75320bE;\n }\n } else {\n if (routeId == 223) {\n return\n 0x46006925506145611bBf0263243D8627dAf26B0F;\n } else {\n return\n 0x8D64BE884314662804eAaB884531f5C50F4d500c;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 241) {\n if (routeId < 233) {\n if (routeId < 229) {\n if (routeId < 227) {\n if (routeId == 225) {\n return\n 0x157a62D92D07B5ce221A5429645a03bBaCE85373;\n } else {\n return\n 0xaF037D33e1F1F2F87309B425fe8a9d895Ef3722B;\n }\n } else {\n if (routeId == 227) {\n return\n 0x921D1154E494A2f7218a37ad7B17701f94b4B40e;\n } else {\n return\n 0xF282b4555186d8Dea51B8b3F947E1E0568d09bc4;\n }\n }\n } else {\n if (routeId < 231) {\n if (routeId == 229) {\n return\n 0xa794E2E1869765a4600b3DFd8a4ebcF16350f6B6;\n } else {\n return\n 0xFEFb048e20c5652F7940A49B1980E0125Ec4D358;\n }\n } else {\n if (routeId == 231) {\n return\n 0x220104b641971e9b25612a8F001bf48AbB23f1cF;\n } else {\n return\n 0xcB9D373Bb54A501B35dd3be5bF4Ba43cA31F7035;\n }\n }\n }\n } else {\n if (routeId < 237) {\n if (routeId < 235) {\n if (routeId == 233) {\n return\n 0x37D627F56e3FF36aC316372109ea82E03ac97DAc;\n } else {\n return\n 0x4E81355FfB4A271B4EA59ff78da2b61c7833161f;\n }\n } else {\n if (routeId == 235) {\n return\n 0xADd8D65cAF6Cc9ad73127B49E16eA7ac29d91e87;\n } else {\n return\n 0x630F9b95626487dfEAe3C97A44DB6C59cF35d996;\n }\n }\n } else {\n if (routeId < 239) {\n if (routeId == 237) {\n return\n 0x78CE2BC8238B679680A67FCB98C5A60E4ec17b2D;\n } else {\n return\n 0xA38D776028eD1310b9A6b086f67F788201762E21;\n }\n } else {\n if (routeId == 239) {\n return\n 0x7Bb5178827B76B86753Ed62a0d662c72cEcb1bD3;\n } else {\n return\n 0x4faC26f61C76eC5c3D43b43eDfAFF0736Ae0e3da;\n }\n }\n }\n }\n } else {\n if (routeId < 249) {\n if (routeId < 245) {\n if (routeId < 243) {\n if (routeId == 241) {\n return\n 0x791Bb49bfFA7129D6889FDB27744422Ac4571A85;\n } else {\n return\n 0x26766fFEbb5fa564777913A6f101dF019AB32afa;\n }\n } else {\n if (routeId == 243) {\n return\n 0x05e98E5e95b4ECBbbAf3258c3999Cc81ed8048Be;\n } else {\n return\n 0xC5c4621e52f1D6A1825A5ed4F95855401a3D9C6b;\n }\n }\n } else {\n if (routeId < 247) {\n if (routeId == 245) {\n return\n 0xfcb15f909BA7FC7Ea083503Fb4c1020203c107EB;\n } else {\n return\n 0xbD27603279d969c74f2486ad14E71080829DFd38;\n }\n } else {\n if (routeId == 247) {\n return\n 0xff2f756BcEcC1A55BFc09a30cc5F64720458cFCB;\n } else {\n return\n 0x3bfB968FEbC12F4e8420B2d016EfcE1E615f7246;\n }\n }\n }\n } else {\n if (routeId < 253) {\n if (routeId < 251) {\n if (routeId == 249) {\n return\n 0x982EE9Ffe23051A2ec945ed676D864fa8345222b;\n } else {\n return\n 0xe101899100785E74767d454FFF0131277BaD48d9;\n }\n } else {\n if (routeId == 251) {\n return\n 0x4F730C0c6b3B5B7d06ca511379f4Aa5BfB2E9525;\n } else {\n return\n 0x5499c36b365795e4e0Ef671aF6C2ce26D7c78265;\n }\n }\n } else {\n if (routeId < 255) {\n if (routeId == 253) {\n return\n 0x8AF51F7237Fc8fB2fc3E700488a94a0aC6Ad8b5a;\n } else {\n return\n 0xda8716df61213c0b143F2849785FB85928084857;\n }\n } else {\n if (routeId == 255) {\n return\n 0xF040Cf9b1ebD11Bf28e04e80740DF3DDe717e4f5;\n } else {\n return\n 0xB87ba32f759D14023C7520366B844dF7f0F036C2;\n }\n }\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 321) {\n if (routeId < 289) {\n if (routeId < 273) {\n if (routeId < 265) {\n if (routeId < 261) {\n if (routeId < 259) {\n if (routeId == 257) {\n return\n 0x0Edde681b8478F0c3194f468EdD2dB5e75c65CDD;\n } else {\n return\n 0x59C70900Fca06eE2aCE1BDd5A8D0Af0cc3BBA720;\n }\n } else {\n if (routeId == 259) {\n return\n 0x8041F0f180D17dD07087199632c45E17AeB0BAd5;\n } else {\n return\n 0x4fB4727064BA595995DD516b63b5921Df9B93aC6;\n }\n }\n } else {\n if (routeId < 263) {\n if (routeId == 261) {\n return\n 0x86e98b594565857eD098864F560915C0dAfd6Ea1;\n } else {\n return\n 0x70f8818E8B698EFfeCd86A513a4c87c0c380Bef6;\n }\n } else {\n if (routeId == 263) {\n return\n 0x78Ed227c8A897A21Da2875a752142dd80d865158;\n } else {\n return\n 0xd02A30BB5C3a8C51d2751A029a6fcfDE2Af9fbc6;\n }\n }\n }\n } else {\n if (routeId < 269) {\n if (routeId < 267) {\n if (routeId == 265) {\n return\n 0x0F00d5c5acb24e975e2a56730609f7F40aa763b8;\n } else {\n return\n 0xC3e2091edc2D3D9D98ba09269138b617B536834A;\n }\n } else {\n if (routeId == 267) {\n return\n 0xa6FbaF7F30867C9633908998ea8C3da28920E75C;\n } else {\n return\n 0xE6dDdcD41E2bBe8122AE32Ac29B8fbAB79CD21d9;\n }\n }\n } else {\n if (routeId < 271) {\n if (routeId == 269) {\n return\n 0x537aa8c1Ef6a8Eaf039dd6e1Eb67694a48195cE4;\n } else {\n return\n 0x96ABAC485fd2D0B03CF4a10df8BD58b8dED28300;\n }\n } else {\n if (routeId == 271) {\n return\n 0xda8e7D46d04Bd4F62705Cd80355BDB6d441DafFD;\n } else {\n return\n 0xbE50018E7a5c67E2e5f5414393e971CC96F293f2;\n }\n }\n }\n }\n } else {\n if (routeId < 281) {\n if (routeId < 277) {\n if (routeId < 275) {\n if (routeId == 273) {\n return\n 0xa1b3907D6CB542a4cbe2eE441EfFAA909FAb62C3;\n } else {\n return\n 0x6d08ee8511C0237a515013aC389e7B3968Cb1753;\n }\n } else {\n if (routeId == 275) {\n return\n 0x22faa5B5Fe43eAdbB52745e35a5cdA8bD5F96bbA;\n } else {\n return\n 0x7a673eB74D79e4868D689E7852abB5f93Ec2fD4b;\n }\n }\n } else {\n if (routeId < 279) {\n if (routeId == 277) {\n return\n 0x0b8531F8AFD4190b76F3e10deCaDb84c98b4d419;\n } else {\n return\n 0x78eABC743A93583DeE403D6b84795490e652216B;\n }\n } else {\n if (routeId == 279) {\n return\n 0x3A95D907b2a7a8604B59BccA08585F58Afe0Aa64;\n } else {\n return\n 0xf4271f0C8c9Af0F06A80b8832fa820ccE64FAda8;\n }\n }\n }\n } else {\n if (routeId < 285) {\n if (routeId < 283) {\n if (routeId == 281) {\n return\n 0x74b2DF841245C3748c0d31542e1335659a25C33b;\n } else {\n return\n 0xdFC99Fd0Ad7D16f30f295a5EEFcE029E04d0fa65;\n }\n } else {\n if (routeId == 283) {\n return\n 0xE992416b6aC1144eD8148a9632973257839027F6;\n } else {\n return\n 0x54ce55ba954E981BB1fd9399054B35Ce1f2C0816;\n }\n }\n } else {\n if (routeId < 287) {\n if (routeId == 285) {\n return\n 0xD4AB52f9e7E5B315Bd7471920baD04F405Ab1c38;\n } else {\n return\n 0x3670C990994d12837e95eE127fE2f06FD3E2104B;\n }\n } else {\n if (routeId == 287) {\n return\n 0xDcf190B09C47E4f551E30BBb79969c3FdEA1e992;\n } else {\n return\n 0xa65057B967B59677237e57Ab815B209744b9bc40;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 305) {\n if (routeId < 297) {\n if (routeId < 293) {\n if (routeId < 291) {\n if (routeId == 289) {\n return\n 0x6Efc86B40573e4C7F28659B13327D55ae955C483;\n } else {\n return\n 0x06BcC25CF8e0E72316F53631b3aA7134E9f73Ae0;\n }\n } else {\n if (routeId == 291) {\n return\n 0x710b6414E1D53882b1FCD3A168aD5Ccd435fc6D0;\n } else {\n return\n 0x5Ebb2C3d78c4e9818074559e7BaE7FCc99781DC1;\n }\n }\n } else {\n if (routeId < 295) {\n if (routeId == 293) {\n return\n 0xAf0a409c3AEe0bD08015cfb29D89E90b6e89A88F;\n } else {\n return\n 0x522559d8b99773C693B80cE06DF559036295Ce44;\n }\n } else {\n if (routeId == 295) {\n return\n 0xB65290A5Bae838aaa7825c9ECEC68041841a1B64;\n } else {\n return\n 0x801b8F2068edd5Bcb659E6BDa0c425909043C420;\n }\n }\n }\n } else {\n if (routeId < 301) {\n if (routeId < 299) {\n if (routeId == 297) {\n return\n 0x29b5F00515d093627E0B7bd0b5c8E84F6b4cDb87;\n } else {\n return\n 0x652839Ae74683cbF9f1293F1019D938F87464D3E;\n }\n } else {\n if (routeId == 299) {\n return\n 0x5Bc95dCebDDE9B79F2b6DC76121BC7936eF8D666;\n } else {\n return\n 0x90db359CEA62E53051158Ab5F99811C0a07Fe686;\n }\n }\n } else {\n if (routeId < 303) {\n if (routeId == 301) {\n return\n 0x2c3625EedadbDcDbB5330eb0d17b3C39ff269807;\n } else {\n return\n 0xC3f0324471b5c9d415acD625b8d8694a4e48e001;\n }\n } else {\n if (routeId == 303) {\n return\n 0x8C60e7E05fa0FfB6F720233736f245134685799d;\n } else {\n return\n 0x98fAF2c09aa4EBb995ad0B56152993E7291a500e;\n }\n }\n }\n }\n } else {\n if (routeId < 313) {\n if (routeId < 309) {\n if (routeId < 307) {\n if (routeId == 305) {\n return\n 0x802c1063a861414dFAEc16bacb81429FC0d40D6e;\n } else {\n return\n 0x11C4AeFCC0dC156f64195f6513CB1Fb3Be0Ae056;\n }\n } else {\n if (routeId == 307) {\n return\n 0xEff1F3258214E31B6B4F640b4389d55715C3Be2B;\n } else {\n return\n 0x47e379Abe8DDFEA4289aBa01235EFF7E93758fd7;\n }\n }\n } else {\n if (routeId < 311) {\n if (routeId == 309) {\n return\n 0x3CC26384c3eA31dDc8D9789e8872CeA6F20cD3ff;\n } else {\n return\n 0xEdd9EFa6c69108FAA4611097d643E20Ba0Ed1634;\n }\n } else {\n if (routeId == 311) {\n return\n 0xCb93525CA5f3D371F74F3D112bC19526740717B8;\n } else {\n return\n 0x7071E0124EB4438137e60dF1b8DD8Af1BfB362cF;\n }\n }\n }\n } else {\n if (routeId < 317) {\n if (routeId < 315) {\n if (routeId == 313) {\n return\n 0x4691096EB0b78C8F4b4A8091E5B66b18e1835c10;\n } else {\n return\n 0x8d953c9b2d1C2137CF95992079f3A77fCd793272;\n }\n } else {\n if (routeId == 315) {\n return\n 0xbdCc2A3Bf6e3Ba49ff86595e6b2b8D70d8368c92;\n } else {\n return\n 0x95E6948aB38c61b2D294E8Bd896BCc4cCC0713cf;\n }\n }\n } else {\n if (routeId < 319) {\n if (routeId == 317) {\n return\n 0x607b27C881fFEE4Cb95B1c5862FaE7224ccd0b4A;\n } else {\n return\n 0x09D28aFA166e566A2Ee1cB834ea8e78C7E627eD2;\n }\n } else {\n if (routeId == 319) {\n return\n 0x9c01449b38bDF0B263818401044Fb1401B29fDfA;\n } else {\n return\n 0x1F7723599bbB658c051F8A39bE2688388d22ceD6;\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 353) {\n if (routeId < 337) {\n if (routeId < 329) {\n if (routeId < 325) {\n if (routeId < 323) {\n if (routeId == 321) {\n return\n 0x52B71603f7b8A5d15B4482e965a0619aa3210194;\n } else {\n return\n 0x01c0f072CB210406653752FecFA70B42dA9173a2;\n }\n } else {\n if (routeId == 323) {\n return\n 0x3021142f021E943e57fc1886cAF58D06147D09A6;\n } else {\n return\n 0xe6f2AF38e76AB09Db59225d97d3E770942D3D842;\n }\n }\n } else {\n if (routeId < 327) {\n if (routeId == 325) {\n return\n 0x06a25554e5135F08b9e2eD1DEC1fc3CEd52e0B48;\n } else {\n return\n 0x71d75e670EE3511C8290C705E0620126B710BF8D;\n }\n } else {\n if (routeId == 327) {\n return\n 0x8b9cE142b80FeA7c932952EC533694b1DF9B3c54;\n } else {\n return\n 0xd7Be24f32f39231116B3fDc483C2A12E1521f73B;\n }\n }\n }\n } else {\n if (routeId < 333) {\n if (routeId < 331) {\n if (routeId == 329) {\n return\n 0xb40cafBC4797d4Ff64087E087F6D2e661f954CbE;\n } else {\n return\n 0xBdDCe7771EfEe81893e838f62204A4c76D72757e;\n }\n } else {\n if (routeId == 331) {\n return\n 0x5d3D299EA7Fd4F39AcDb336E26631Dfee41F9287;\n } else {\n return\n 0x6BfEE09E1Fc0684e0826A9A0dC1352a14B136FAC;\n }\n }\n } else {\n if (routeId < 335) {\n if (routeId == 333) {\n return\n 0xd0001bB8E2Cb661436093f96458a4358B5156E3c;\n } else {\n return\n 0x1867c6485CfD1eD448988368A22bfB17a7747293;\n }\n } else {\n if (routeId == 335) {\n return\n 0x8997EF9F95dF24aB67703AB6C262aABfeEBE33bD;\n } else {\n return\n 0x1e39E9E601922deD91BCFc8F78836302133465e2;\n }\n }\n }\n }\n } else {\n if (routeId < 345) {\n if (routeId < 341) {\n if (routeId < 339) {\n if (routeId == 337) {\n return\n 0x8A8ec6CeacFf502a782216774E5AF3421562C6ff;\n } else {\n return\n 0x3B8FC561df5415c8DC01e97Ee6E38435A8F9C40A;\n }\n } else {\n if (routeId == 339) {\n return\n 0xD5d5f5B37E67c43ceA663aEDADFFc3a93a2065B0;\n } else {\n return\n 0xCC8F55EC43B4f25013CE1946FBB740c43Be5B96D;\n }\n }\n } else {\n if (routeId < 343) {\n if (routeId == 341) {\n return\n 0x18f586E816eEeDbb57B8011239150367561B58Fb;\n } else {\n return\n 0xd0CD802B19c1a52501cb2f07d656e3Cd7B0Ce124;\n }\n } else {\n if (routeId == 343) {\n return\n 0xe0AeD899b39C6e4f2d83e4913a1e9e0cf6368abE;\n } else {\n return\n 0x0606e1b6c0f1A398C38825DCcc4678a7Cbc2737c;\n }\n }\n }\n } else {\n if (routeId < 349) {\n if (routeId < 347) {\n if (routeId == 345) {\n return\n 0x2d188e85b27d18EF80f16686EA1593ABF7Ed2A63;\n } else {\n return\n 0x64412292fA4A135a3300E24366E99ff59Db2eAc1;\n }\n } else {\n if (routeId == 347) {\n return\n 0x38b74c173f3733E8b90aAEf0e98B89791266149F;\n } else {\n return\n 0x36DAA49A79aaEF4E7a217A11530D3cCD84414124;\n }\n }\n } else {\n if (routeId < 351) {\n if (routeId == 349) {\n return\n 0x10f088FE2C88F90270E4449c46c8B1b232511d58;\n } else {\n return\n 0x4FeDbd25B58586838ABD17D10272697dF1dC3087;\n }\n } else {\n if (routeId == 351) {\n return\n 0x685278209248CB058E5cEe93e37f274A80Faf6eb;\n } else {\n return\n 0xDd9F8F1eeC3955f78168e2Fb2d1e808fa8A8f15b;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 369) {\n if (routeId < 361) {\n if (routeId < 357) {\n if (routeId < 355) {\n if (routeId == 353) {\n return\n 0x7392aEeFD5825aaC28817031dEEBbFaAA20983D9;\n } else {\n return\n 0x0Cc182555E00767D6FB8AD161A10d0C04C476d91;\n }\n } else {\n if (routeId == 355) {\n return\n 0x90E52837d56715c79FD592E8D58bFD20365798b2;\n } else {\n return\n 0x6F4451DE14049B6770ad5BF4013118529e68A40C;\n }\n }\n } else {\n if (routeId < 359) {\n if (routeId == 357) {\n return\n 0x89B97ef2aFAb9ed9c7f0FDb095d02E6840b52d9c;\n } else {\n return\n 0x92A5cC5C42d94d3e23aeB1214fFf43Db2B97759E;\n }\n } else {\n if (routeId == 359) {\n return\n 0x63ddc52F135A1dcBA831EAaC11C63849F018b739;\n } else {\n return\n 0x692A691533B571C2c54C1D7F8043A204b3d8120E;\n }\n }\n }\n } else {\n if (routeId < 365) {\n if (routeId < 363) {\n if (routeId == 361) {\n return\n 0x97c7492CF083969F61C6f302d45c8270391b921c;\n } else {\n return\n 0xDeFD2B8643553dAd19548eB14fd94A57F4B9e543;\n }\n } else {\n if (routeId == 363) {\n return\n 0x30645C04205cA3f670B67b02F971B088930ACB8C;\n } else {\n return\n 0xA6f80ed2d607Cd67aEB4109B64A0BEcc4D7d03CF;\n }\n }\n } else {\n if (routeId < 367) {\n if (routeId == 365) {\n return\n 0xBbbbC6c276eB3F7E674f2D39301509236001c42f;\n } else {\n return\n 0xC20E77d349FB40CE88eB01824e2873ad9f681f3C;\n }\n } else {\n if (routeId == 367) {\n return\n 0x5fCfD9a962De19294467C358C1FA55082285960b;\n } else {\n return\n 0x4D87BD6a0E4E5cc6332923cb3E85fC71b287F58A;\n }\n }\n }\n }\n } else {\n if (routeId < 377) {\n if (routeId < 373) {\n if (routeId < 371) {\n if (routeId == 369) {\n return\n 0x3AA5B757cd6Dde98214E56D57Dde7fcF0F7aB04E;\n } else {\n return\n 0xe28eFCE7192e11a2297f44059113C1fD6967b2d4;\n }\n } else {\n if (routeId == 371) {\n return\n 0x3251cAE10a1Cf246e0808D76ACC26F7B5edA0eE5;\n } else {\n return\n 0xbA2091cc9357Cf4c4F25D64F30d1b4Ba3A5a174B;\n }\n }\n } else {\n if (routeId < 375) {\n if (routeId == 373) {\n return\n 0x49c8e1Da9693692096F63C82D11b52d738566d55;\n } else {\n return\n 0xA0731615aB5FFF451031E9551367A4F7dB27b39c;\n }\n } else {\n if (routeId == 375) {\n return\n 0xFb214541888671AE1403CecC1D59763a12fc1609;\n } else {\n return\n 0x1D6bCB17642E2336405df73dF22F07688cAec020;\n }\n }\n }\n } else {\n if (routeId < 381) {\n if (routeId < 379) {\n if (routeId == 377) {\n return\n 0xfC9c0C7bfe187120fF7f4E21446161794A617a9e;\n } else {\n return\n 0xBa5bF37678EeE2dAB17AEf9D898153258252250E;\n }\n } else {\n if (routeId == 379) {\n return\n 0x7c55690bd2C9961576A32c02f8EB29ed36415Ec7;\n } else {\n return\n 0xcA40073E868E8Bc611aEc8Fe741D17E68Fe422f6;\n }\n }\n } else {\n if (routeId < 383) {\n if (routeId == 381) {\n return\n 0x31641bAFb87E9A58f78835050a7BE56921986339;\n } else {\n return\n 0xA54766424f6dA74b45EbCc5Bf0Bd1D74D2CCcaAB;\n }\n } else {\n if (routeId == 383) {\n return\n 0xc7bBa57F8C179EDDBaa62117ddA360e28f3F8252;\n } else {\n return\n 0x5e663ED97ea77d393B8858C90d0683bF180E0ffd;\n }\n }\n }\n }\n }\n }\n }\n }\n }\n\n if (routes[routeId] == address(0)) revert ZeroAddressNotAllowed();\n return routes[routeId];\n }\n\n /// @notice fallback function to handle swap, bridge execution\n /// @dev ensure routeId is converted to bytes4 and sent as msg.sig in the transaction\n fallback() external payable {\n address routeAddress = addressAt(uint32(msg.sig));\n\n bytes memory result;\n\n assembly {\n // copy function selector and any arguments\n calldatacopy(0, 4, sub(calldatasize(), 4))\n // execute function call using the facet\n result := delegatecall(\n gas(),\n routeAddress,\n 0,\n sub(calldatasize(), 4),\n 0,\n 0\n )\n // get any return value\n returndatacopy(0, 0, returndatasize())\n // return any return value or error back to the caller\n switch result\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n}\n" - }, - "src/bridges/hop/interfaces/IHopL1Bridge.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/**\n * @title L1Bridge Hop Interface\n * @notice L1 Hop Bridge, Used to transfer from L1 to L2s.\n */\ninterface IHopL1Bridge {\n /**\n * @notice `amountOutMin` and `deadline` should be 0 when no swap is intended at the destination.\n * @notice `amount` is the total amount the user wants to send including the relayer fee\n * @dev Send tokens to a supported layer-2 to mint hToken and optionally swap the hToken in the\n * AMM at the destination.\n * @param chainId The chainId of the destination chain\n * @param recipient The address receiving funds at the destination\n * @param amount The amount being sent\n * @param amountOutMin The minimum amount received after attempting to swap in the destination\n * AMM market. 0 if no swap is intended.\n * @param deadline The deadline for swapping in the destination AMM market. 0 if no\n * swap is intended.\n * @param relayer The address of the relayer at the destination.\n * @param relayerFee The amount distributed to the relayer at the destination. This is subtracted from the `amount`.\n */\n function sendToL2(\n uint256 chainId,\n address recipient,\n uint256 amount,\n uint256 amountOutMin,\n uint256 deadline,\n address relayer,\n uint256 relayerFee\n ) external payable;\n}\n" - }, - "src/bridges/refuel/interfaces/refuel.sol": { - "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity >=0.8.0;\n\n/// @notice interface with functions to interact with Refuel contract\ninterface IRefuel {\n /**\n * @notice function to deposit nativeToken to Destination-address on destinationChain\n * @param destinationChainId chainId of the Destination chain\n * @param _to recipient address\n */\n function depositNativeToken(\n uint256 destinationChainId,\n address _to\n ) external payable;\n}\n" - }, - "src/bridges/stargate/interfaces/stargate.sol": { - "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\n/**\n * @title IBridgeStargate Interface Contract.\n * @notice Interface used by Stargate-L1 and L2 Router implementations\n * @dev router and routerETH addresses will be distinct for L1 and L2\n */\ninterface IBridgeStargate {\n // @notice Struct to hold the additional-data for bridging ERC20 token\n struct lzTxObj {\n // gas limit to bridge the token in Stargate to destinationChain\n uint256 dstGasForCall;\n // destination nativeAmount, this is always set as 0\n uint256 dstNativeAmount;\n // destination nativeAddress, this is always set as 0x\n bytes dstNativeAddr;\n }\n\n /// @notice function in stargate bridge which is used to bridge ERC20 tokens to recipient on destinationChain\n function swap(\n uint16 _dstChainId,\n uint256 _srcPoolId,\n uint256 _dstPoolId,\n address payable _refundAddress,\n uint256 _amountLD,\n uint256 _minAmountLD,\n lzTxObj memory _lzTxParams,\n bytes calldata _to,\n bytes calldata _payload\n ) external payable;\n\n /// @notice function in stargate bridge which is used to bridge native tokens to recipient on destinationChain\n function swapETH(\n uint16 _dstChainId, // destination Stargate chainId\n address payable _refundAddress, // refund additional messageFee to this address\n bytes calldata _toAddress, // the receiver of the destination ETH\n uint256 _amountLD, // the amount, in Local Decimals, to be swapped\n uint256 _minAmountLD // the minimum amount accepted out on destination\n ) external payable;\n}\n" - }, - "src/controllers/FeesTakerController.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {BaseController} from \"./BaseController.sol\";\nimport {ISocketRequest} from \"../interfaces/ISocketRequest.sol\";\n\n/**\n * @title FeesTaker-Controller Implementation\n * @notice Controller with composed actions to deduct-fees followed by Refuel, Swap and Bridge\n * to be executed Sequentially and this is atomic\n * @author Socket dot tech.\n */\ncontract FeesTakerController is BaseController {\n using SafeTransferLib for ERC20;\n\n /// @notice event emitted upon fee-deduction to fees-taker address\n event SocketFeesDeducted(\n uint256 fees,\n address feesToken,\n address feesTaker\n );\n\n /// @notice Function-selector to invoke deduct-fees and swap token\n /// @dev This function selector is to be used while building transaction-data\n bytes4 public immutable FEES_TAKER_SWAP_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\"takeFeesAndSwap((address,address,uint256,uint32,bytes))\")\n );\n\n /// @notice Function-selector to invoke deduct-fees and bridge token\n /// @dev This function selector is to be used while building transaction-data\n bytes4 public immutable FEES_TAKER_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"takeFeesAndBridge((address,address,uint256,uint32,bytes))\"\n )\n );\n\n /// @notice Function-selector to invoke deduct-fees and bridge multiple tokens\n /// @dev This function selector is to be used while building transaction-data\n bytes4 public immutable FEES_TAKER_MULTI_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"takeFeesAndMultiBridge((address,address,uint256,uint32[],bytes[]))\"\n )\n );\n\n /// @notice Function-selector to invoke deduct-fees followed by swapping of a token and bridging the swapped bridge\n /// @dev This function selector is to be used while building transaction-data\n bytes4 public immutable FEES_TAKER_SWAP_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"takeFeeAndSwapAndBridge((address,address,uint256,uint32,bytes,uint32,bytes))\"\n )\n );\n\n /// @notice Function-selector to invoke deduct-fees refuel\n /// @notice followed by swapping of a token and bridging the swapped bridge\n /// @dev This function selector is to be used while building transaction-data\n bytes4 public immutable FEES_TAKER_REFUEL_SWAP_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"takeFeeAndRefuelAndSwapAndBridge((address,address,uint256,uint32,bytes,uint32,bytes,uint32,bytes))\"\n )\n );\n\n /// @notice socketGatewayAddress to be initialised via storage variable BaseController\n constructor(\n address _socketGatewayAddress\n ) BaseController(_socketGatewayAddress) {}\n\n /**\n * @notice function to deduct-fees to fees-taker address on source-chain and swap token\n * @dev ensure correct function selector is used to generate transaction-data for bridgeRequest\n * @param ftsRequest feesTakerSwapRequest object generated either off-chain or the calling contract using\n * the function-selector FEES_TAKER_SWAP_FUNCTION_SELECTOR\n * @return output bytes from the swap operation (last operation in the composed actions)\n */\n function takeFeesAndSwap(\n ISocketRequest.FeesTakerSwapRequest calldata ftsRequest\n ) external payable returns (bytes memory) {\n if (ftsRequest.feesToken == NATIVE_TOKEN_ADDRESS) {\n //transfer the native amount to the feeTakerAddress\n payable(ftsRequest.feesTakerAddress).transfer(\n ftsRequest.feesAmount\n );\n } else {\n //transfer feesAmount to feesTakerAddress\n ERC20(ftsRequest.feesToken).safeTransferFrom(\n msg.sender,\n ftsRequest.feesTakerAddress,\n ftsRequest.feesAmount\n );\n }\n\n emit SocketFeesDeducted(\n ftsRequest.feesAmount,\n ftsRequest.feesTakerAddress,\n ftsRequest.feesToken\n );\n\n //call bridge function (executeRoute for the swapRequestData)\n return _executeRoute(ftsRequest.routeId, ftsRequest.swapRequestData);\n }\n\n /**\n * @notice function to deduct-fees to fees-taker address on source-chain and bridge amount to destinationChain\n * @dev ensure correct function selector is used to generate transaction-data for bridgeRequest\n * @param ftbRequest feesTakerBridgeRequest object generated either off-chain or the calling contract using\n * the function-selector FEES_TAKER_BRIDGE_FUNCTION_SELECTOR\n * @return output bytes from the bridge operation (last operation in the composed actions)\n */\n function takeFeesAndBridge(\n ISocketRequest.FeesTakerBridgeRequest calldata ftbRequest\n ) external payable returns (bytes memory) {\n if (ftbRequest.feesToken == NATIVE_TOKEN_ADDRESS) {\n //transfer the native amount to the feeTakerAddress\n payable(ftbRequest.feesTakerAddress).transfer(\n ftbRequest.feesAmount\n );\n } else {\n //transfer feesAmount to feesTakerAddress\n ERC20(ftbRequest.feesToken).safeTransferFrom(\n msg.sender,\n ftbRequest.feesTakerAddress,\n ftbRequest.feesAmount\n );\n }\n\n emit SocketFeesDeducted(\n ftbRequest.feesAmount,\n ftbRequest.feesTakerAddress,\n ftbRequest.feesToken\n );\n\n //call bridge function (executeRoute for the bridgeData)\n return _executeRoute(ftbRequest.routeId, ftbRequest.bridgeRequestData);\n }\n\n /**\n * @notice function to deduct-fees to fees-taker address on source-chain and bridge amount to destinationChain\n * @notice multiple bridge-requests are to be generated and sequence and number of routeIds should match with the bridgeData array\n * @dev ensure correct function selector is used to generate transaction-data for bridgeRequest\n * @param ftmbRequest feesTakerMultiBridgeRequest object generated either off-chain or the calling contract using\n * the function-selector FEES_TAKER_MULTI_BRIDGE_FUNCTION_SELECTOR\n */\n function takeFeesAndMultiBridge(\n ISocketRequest.FeesTakerMultiBridgeRequest calldata ftmbRequest\n ) external payable {\n if (ftmbRequest.feesToken == NATIVE_TOKEN_ADDRESS) {\n //transfer the native amount to the feeTakerAddress\n payable(ftmbRequest.feesTakerAddress).transfer(\n ftmbRequest.feesAmount\n );\n } else {\n //transfer feesAmount to feesTakerAddress\n ERC20(ftmbRequest.feesToken).safeTransferFrom(\n msg.sender,\n ftmbRequest.feesTakerAddress,\n ftmbRequest.feesAmount\n );\n }\n\n emit SocketFeesDeducted(\n ftmbRequest.feesAmount,\n ftmbRequest.feesTakerAddress,\n ftmbRequest.feesToken\n );\n\n // multiple bridge-requests are to be generated and sequence and number of routeIds should match with the bridgeData array\n for (\n uint256 index = 0;\n index < ftmbRequest.bridgeRouteIds.length;\n ++index\n ) {\n //call bridge function (executeRoute for the bridgeData)\n _executeRoute(\n ftmbRequest.bridgeRouteIds[index],\n ftmbRequest.bridgeRequestDataItems[index]\n );\n }\n }\n\n /**\n * @notice function to deduct-fees to fees-taker address on source-chain followed by swap the amount on sourceChain followed by\n * bridging the swapped amount to destinationChain\n * @dev while generating implData for swap and bridgeRequests, ensure correct function selector is used\n * bridge action corresponds to the bridgeAfterSwap function of the bridgeImplementation\n * @param fsbRequest feesTakerSwapBridgeRequest object generated either off-chain or the calling contract using\n * the function-selector FEES_TAKER_SWAP_BRIDGE_FUNCTION_SELECTOR\n */\n function takeFeeAndSwapAndBridge(\n ISocketRequest.FeesTakerSwapBridgeRequest calldata fsbRequest\n ) external payable returns (bytes memory) {\n if (fsbRequest.feesToken == NATIVE_TOKEN_ADDRESS) {\n //transfer the native amount to the feeTakerAddress\n payable(fsbRequest.feesTakerAddress).transfer(\n fsbRequest.feesAmount\n );\n } else {\n //transfer feesAmount to feesTakerAddress\n ERC20(fsbRequest.feesToken).safeTransferFrom(\n msg.sender,\n fsbRequest.feesTakerAddress,\n fsbRequest.feesAmount\n );\n }\n\n emit SocketFeesDeducted(\n fsbRequest.feesAmount,\n fsbRequest.feesTakerAddress,\n fsbRequest.feesToken\n );\n\n // execute swap operation\n bytes memory swapResponseData = _executeRoute(\n fsbRequest.swapRouteId,\n fsbRequest.swapData\n );\n\n uint256 swapAmount = abi.decode(swapResponseData, (uint256));\n\n // swapped amount is to be bridged to the recipient on destinationChain\n bytes memory bridgeImpldata = abi.encodeWithSelector(\n BRIDGE_AFTER_SWAP_SELECTOR,\n swapAmount,\n fsbRequest.bridgeData\n );\n\n // execute bridge operation and return the byte-data from response of bridge operation\n return _executeRoute(fsbRequest.bridgeRouteId, bridgeImpldata);\n }\n\n /**\n * @notice function to deduct-fees to fees-taker address on source-chain followed by refuel followed by\n * swap the amount on sourceChain followed by bridging the swapped amount to destinationChain\n * @dev while generating implData for refuel, swap and bridge Requests, ensure correct function selector is used\n * bridge action corresponds to the bridgeAfterSwap function of the bridgeImplementation\n * @param frsbRequest feesTakerRefuelSwapBridgeRequest object generated either off-chain or the calling contract using\n * the function-selector FEES_TAKER_REFUEL_SWAP_BRIDGE_FUNCTION_SELECTOR\n */\n function takeFeeAndRefuelAndSwapAndBridge(\n ISocketRequest.FeesTakerRefuelSwapBridgeRequest calldata frsbRequest\n ) external payable returns (bytes memory) {\n if (frsbRequest.feesToken == NATIVE_TOKEN_ADDRESS) {\n //transfer the native amount to the feeTakerAddress\n payable(frsbRequest.feesTakerAddress).transfer(\n frsbRequest.feesAmount\n );\n } else {\n //transfer feesAmount to feesTakerAddress\n ERC20(frsbRequest.feesToken).safeTransferFrom(\n msg.sender,\n frsbRequest.feesTakerAddress,\n frsbRequest.feesAmount\n );\n }\n\n emit SocketFeesDeducted(\n frsbRequest.feesAmount,\n frsbRequest.feesTakerAddress,\n frsbRequest.feesToken\n );\n\n // refuel is also done via bridge execution via refuelRouteImplementation identified by refuelRouteId\n _executeRoute(frsbRequest.refuelRouteId, frsbRequest.refuelData);\n\n // execute swap operation\n bytes memory swapResponseData = _executeRoute(\n frsbRequest.swapRouteId,\n frsbRequest.swapData\n );\n\n uint256 swapAmount = abi.decode(swapResponseData, (uint256));\n\n // swapped amount is to be bridged to the recipient on destinationChain\n bytes memory bridgeImpldata = abi.encodeWithSelector(\n BRIDGE_AFTER_SWAP_SELECTOR,\n swapAmount,\n frsbRequest.bridgeData\n );\n\n // execute bridge operation and return the byte-data from response of bridge operation\n return _executeRoute(frsbRequest.bridgeRouteId, bridgeImpldata);\n }\n}\n" - }, - "src/errors/SocketErrors.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nerror CelerRefundNotReady();\nerror OnlySocketDeployer();\nerror OnlySocketGatewayOwner();\nerror OnlySocketGateway();\nerror OnlyOwner();\nerror OnlyNominee();\nerror TransferIdExists();\nerror TransferIdDoesnotExist();\nerror Address0Provided();\nerror SwapFailed();\nerror UnsupportedInterfaceId();\nerror InvalidCelerRefund();\nerror CelerAlreadyRefunded();\nerror IncorrectBridgeRatios();\nerror ZeroAddressNotAllowed();\nerror ArrayLengthMismatch();\n" - }, - "src/bridges/hop/l1/HopImplL1.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\nimport \"../interfaces/IHopL1Bridge.sol\";\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {BridgeImplBase} from \"../../BridgeImplBase.sol\";\nimport {HOP} from \"../../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Hop-L1 Route Implementation\n * @notice Route implementation with functions to bridge ERC20 and Native via Hop-Bridge from L1 to Supported L2s\n * Called via SocketGateway if the routeId in the request maps to the routeId of HopImplementation\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ncontract HopImplL1 is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable HopIdentifier = HOP;\n\n /// @notice Function-selector for ERC20-token bridging on Hop-L1-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4 public immutable HOP_L1_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(address,address,address,address,uint256,uint256,uint256,uint256,(uint256,bytes32))\"\n )\n );\n\n /// @notice Function-selector for Native bridging on Hop-L1-Route\n /// @dev This function selector is to be used while building transaction-data to bridge Native tokens\n bytes4 public immutable HOP_L1_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeNativeTo(address,address,address,uint256,uint256,uint256,uint256,uint256,bytes32)\"\n )\n );\n\n bytes4 public immutable HOP_L1_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(address,address,address,uint256,uint256,uint256,uint256,bytes32))\"\n )\n );\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n constructor(\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {}\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct HopDataNoToken {\n // The address receiving funds at the destination\n address receiverAddress;\n // address of the Hop-L1-Bridge to handle bridging the tokens\n address l1bridgeAddr;\n // relayerFee The amount distributed to the relayer at the destination. This is subtracted from the `_amount`.\n address relayer;\n // The chainId of the destination chain\n uint256 toChainId;\n // The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.\n uint256 amountOutMin;\n // The amount distributed to the relayer at the destination. This is subtracted from the `amount`.\n uint256 relayerFee;\n // The deadline for swapping in the destination AMM market. 0 if no swap is intended.\n uint256 deadline;\n // socket offchain created hash\n bytes32 metadata;\n }\n\n struct HopData {\n /// @notice address of token being bridged\n address token;\n // The address receiving funds at the destination\n address receiverAddress;\n // address of the Hop-L1-Bridge to handle bridging the tokens\n address l1bridgeAddr;\n // relayerFee The amount distributed to the relayer at the destination. This is subtracted from the `_amount`.\n address relayer;\n // The chainId of the destination chain\n uint256 toChainId;\n // The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.\n uint256 amountOutMin;\n // The amount distributed to the relayer at the destination. This is subtracted from the `amount`.\n uint256 relayerFee;\n // The deadline for swapping in the destination AMM market. 0 if no swap is intended.\n uint256 deadline;\n // socket offchain created hash\n bytes32 metadata;\n }\n\n struct HopERC20Data {\n uint256 deadline;\n bytes32 metadata;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in HopBridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for Hop-L1-Bridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n HopData memory hopData = abi.decode(bridgeData, (HopData));\n\n if (hopData.token == NATIVE_TOKEN_ADDRESS) {\n IHopL1Bridge(hopData.l1bridgeAddr).sendToL2{value: amount}(\n hopData.toChainId,\n hopData.receiverAddress,\n amount,\n hopData.amountOutMin,\n hopData.deadline,\n hopData.relayer,\n hopData.relayerFee\n );\n } else {\n ERC20(hopData.token).safeApprove(hopData.l1bridgeAddr, amount);\n\n // perform bridging\n IHopL1Bridge(hopData.l1bridgeAddr).sendToL2(\n hopData.toChainId,\n hopData.receiverAddress,\n amount,\n hopData.amountOutMin,\n hopData.deadline,\n hopData.relayer,\n hopData.relayerFee\n );\n }\n\n emit SocketBridge(\n amount,\n hopData.token,\n hopData.toChainId,\n HopIdentifier,\n msg.sender,\n hopData.receiverAddress,\n hopData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in HopBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param hopData encoded data for HopData\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n HopDataNoToken calldata hopData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n\n if (token == NATIVE_TOKEN_ADDRESS) {\n IHopL1Bridge(hopData.l1bridgeAddr).sendToL2{value: bridgeAmount}(\n hopData.toChainId,\n hopData.receiverAddress,\n bridgeAmount,\n hopData.amountOutMin,\n hopData.deadline,\n hopData.relayer,\n hopData.relayerFee\n );\n } else {\n ERC20(token).safeApprove(hopData.l1bridgeAddr, bridgeAmount);\n\n // perform bridging\n IHopL1Bridge(hopData.l1bridgeAddr).sendToL2(\n hopData.toChainId,\n hopData.receiverAddress,\n bridgeAmount,\n hopData.amountOutMin,\n hopData.deadline,\n hopData.relayer,\n hopData.relayerFee\n );\n }\n\n emit SocketBridge(\n bridgeAmount,\n token,\n hopData.toChainId,\n HopIdentifier,\n msg.sender,\n hopData.receiverAddress,\n hopData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via Hop-L1-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param receiverAddress The address receiving funds at the destination\n * @param token token being bridged\n * @param l1bridgeAddr address of the Hop-L1-Bridge to handle bridging the tokens\n * @param relayer The amount distributed to the relayer at the destination. This is subtracted from the `_amount`.\n * @param toChainId The chainId of the destination chain\n * @param amount The amount being sent\n * @param amountOutMin The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.\n * @param relayerFee The amount distributed to the relayer at the destination. This is subtracted from the `amount`.\n * @param hopData extra data needed to build the tx\n */\n function bridgeERC20To(\n address receiverAddress,\n address token,\n address l1bridgeAddr,\n address relayer,\n uint256 toChainId,\n uint256 amount,\n uint256 amountOutMin,\n uint256 relayerFee,\n HopERC20Data calldata hopData\n ) external payable {\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n tokenInstance.safeApprove(l1bridgeAddr, amount);\n\n // perform bridging\n IHopL1Bridge(l1bridgeAddr).sendToL2(\n toChainId,\n receiverAddress,\n amount,\n amountOutMin,\n hopData.deadline,\n relayer,\n relayerFee\n );\n\n emit SocketBridge(\n amount,\n token,\n toChainId,\n HopIdentifier,\n msg.sender,\n receiverAddress,\n hopData.metadata\n );\n }\n\n /**\n * @notice function to handle Native bridging to receipent via Hop-L1-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param receiverAddress The address receiving funds at the destination\n * @param l1bridgeAddr address of the Hop-L1-Bridge to handle bridging the tokens\n * @param relayer The amount distributed to the relayer at the destination. This is subtracted from the `_amount`.\n * @param toChainId The chainId of the destination chain\n * @param amount The amount being sent\n * @param amountOutMin The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.\n * @param relayerFee The amount distributed to the relayer at the destination. This is subtracted from the `amount`.\n * @param deadline The deadline for swapping in the destination AMM market. 0 if no swap is intended.\n */\n function bridgeNativeTo(\n address receiverAddress,\n address l1bridgeAddr,\n address relayer,\n uint256 toChainId,\n uint256 amount,\n uint256 amountOutMin,\n uint256 relayerFee,\n uint256 deadline,\n bytes32 metadata\n ) external payable {\n IHopL1Bridge(l1bridgeAddr).sendToL2{value: amount}(\n toChainId,\n receiverAddress,\n amount,\n amountOutMin,\n deadline,\n relayer,\n relayerFee\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n toChainId,\n HopIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n" - }, - "src/static/RouteIdentifiers.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\nbytes32 constant ACROSS = keccak256(\"Across\");\n\nbytes32 constant ANYSWAP = keccak256(\"Anyswap\");\n\nbytes32 constant CBRIDGE = keccak256(\"CBridge\");\n\nbytes32 constant HOP = keccak256(\"Hop\");\n\nbytes32 constant HYPHEN = keccak256(\"Hyphen\");\n\nbytes32 constant NATIVE_OPTIMISM = keccak256(\"NativeOptimism\");\n\nbytes32 constant NATIVE_ARBITRUM = keccak256(\"NativeArbitrum\");\n\nbytes32 constant NATIVE_POLYGON = keccak256(\"NativePolygon\");\n\nbytes32 constant REFUEL = keccak256(\"Refuel\");\n\nbytes32 constant STARGATE = keccak256(\"Stargate\");\n\nbytes32 constant ONEINCH = keccak256(\"OneInch\");\n\nbytes32 constant ZEROX = keccak256(\"Zerox\");\n\nbytes32 constant RAINBOW = keccak256(\"Rainbow\");\n" - }, - "src/SocketGateway.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\npragma experimental ABIEncoderV2;\n\nimport \"./utils/Ownable.sol\";\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {LibUtil} from \"./libraries/LibUtil.sol\";\nimport \"./libraries/LibBytes.sol\";\nimport {ISocketRoute} from \"./interfaces/ISocketRoute.sol\";\nimport {ISocketRequest} from \"./interfaces/ISocketRequest.sol\";\nimport {ISocketGateway} from \"./interfaces/ISocketGateway.sol\";\nimport {IncorrectBridgeRatios, ZeroAddressNotAllowed, ArrayLengthMismatch} from \"./errors/SocketErrors.sol\";\n\n/// @title SocketGatewayContract\n/// @notice Socketgateway is a contract with entrypoint functions for all interactions with socket liquidity layer\n/// @author Socket Team\ncontract SocketGatewayTemplate is Ownable {\n using LibBytes for bytes;\n using LibBytes for bytes4;\n using SafeTransferLib for ERC20;\n\n /// @notice FunctionSelector used to delegatecall from swap to the function of bridge router implementation\n bytes4 public immutable BRIDGE_AFTER_SWAP_SELECTOR =\n bytes4(keccak256(\"bridgeAfterSwap(uint256,bytes)\"));\n\n /// @notice storage variable to keep track of total number of routes registered in socketgateway\n uint32 public routesCount = 385;\n\n /// @notice storage variable to keep track of total number of controllers registered in socketgateway\n uint32 public controllerCount;\n\n address public immutable disabledRouteAddress;\n\n uint256 public constant CENT_PERCENT = 100e18;\n\n /// @notice storage mapping for route implementation addresses\n mapping(uint32 => address) public routes;\n\n /// storage mapping for controller implemenation addresses\n mapping(uint32 => address) public controllers;\n\n // Events ------------------------------------------------------------------------------------------------------->\n\n /// @notice Event emitted when a router is added to socketgateway\n event NewRouteAdded(uint32 indexed routeId, address indexed route);\n\n /// @notice Event emitted when a route is disabled\n event RouteDisabled(uint32 indexed routeId);\n\n /// @notice Event emitted when ownership transfer is requested by socket-gateway-owner\n event OwnershipTransferRequested(\n address indexed _from,\n address indexed _to\n );\n\n /// @notice Event emitted when a controller is added to socketgateway\n event ControllerAdded(\n uint32 indexed controllerId,\n address indexed controllerAddress\n );\n\n /// @notice Event emitted when a controller is disabled\n event ControllerDisabled(uint32 indexed controllerId);\n\n constructor(address _owner, address _disabledRoute) Ownable(_owner) {\n disabledRouteAddress = _disabledRoute;\n }\n\n // Able to receive ether\n // solhint-disable-next-line no-empty-blocks\n receive() external payable {}\n\n /*******************************************\n * EXTERNAL AND PUBLIC FUNCTIONS *\n *******************************************/\n\n /**\n * @notice executes functions in the routes identified using routeId and functionSelectorData\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped\n * @dev ensure the data in routeData to be built using the function-selector defined as a\n * constant in the route implementation contract\n * @param routeId route identifier\n * @param routeData functionSelectorData generated using the function-selector defined in the route Implementation\n */\n function executeRoute(\n uint32 routeId,\n bytes calldata routeData\n ) external payable returns (bytes memory) {\n (bool success, bytes memory result) = addressAt(routeId).delegatecall(\n routeData\n );\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n return result;\n }\n\n /**\n * @notice swaps a token on sourceChain and split it across multiple bridge-recipients\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being swapped\n * @dev ensure the swap-data and bridge-data is generated using the function-selector defined as a constant in the implementation address\n * @param swapMultiBridgeRequest request\n */\n function swapAndMultiBridge(\n ISocketRequest.SwapMultiBridgeRequest calldata swapMultiBridgeRequest\n ) external payable {\n uint256 requestLength = swapMultiBridgeRequest.bridgeRouteIds.length;\n\n if (\n requestLength != swapMultiBridgeRequest.bridgeImplDataItems.length\n ) {\n revert ArrayLengthMismatch();\n }\n uint256 ratioAggregate;\n for (uint256 index = 0; index < requestLength; ) {\n ratioAggregate += swapMultiBridgeRequest.bridgeRatios[index];\n }\n\n if (ratioAggregate != CENT_PERCENT) {\n revert IncorrectBridgeRatios();\n }\n\n (bool swapSuccess, bytes memory swapResult) = addressAt(\n swapMultiBridgeRequest.swapRouteId\n ).delegatecall(swapMultiBridgeRequest.swapImplData);\n\n if (!swapSuccess) {\n assembly {\n revert(add(swapResult, 32), mload(swapResult))\n }\n }\n\n uint256 amountReceivedFromSwap = abi.decode(swapResult, (uint256));\n\n uint256 bridgedAmount;\n\n for (uint256 index = 0; index < requestLength; ) {\n uint256 bridgingAmount;\n\n // if it is the last bridge request, bridge the remaining amount\n if (index == requestLength - 1) {\n bridgingAmount = amountReceivedFromSwap - bridgedAmount;\n } else {\n // bridging amount is the multiplication of bridgeRatio and amountReceivedFromSwap\n bridgingAmount =\n (amountReceivedFromSwap *\n swapMultiBridgeRequest.bridgeRatios[index]) /\n (CENT_PERCENT);\n }\n\n // update the bridged amount, this would be used for computation for last bridgeRequest\n bridgedAmount += bridgingAmount;\n\n bytes memory bridgeImpldata = abi.encodeWithSelector(\n BRIDGE_AFTER_SWAP_SELECTOR,\n bridgingAmount,\n swapMultiBridgeRequest.bridgeImplDataItems[index]\n );\n\n (bool bridgeSuccess, bytes memory bridgeResult) = addressAt(\n swapMultiBridgeRequest.bridgeRouteIds[index]\n ).delegatecall(bridgeImpldata);\n\n if (!bridgeSuccess) {\n assembly {\n revert(add(bridgeResult, 32), mload(bridgeResult))\n }\n }\n\n unchecked {\n ++index;\n }\n }\n }\n\n /**\n * @notice sequentially executes functions in the routes identified using routeId and functionSelectorData\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped\n * @dev ensure the data in each dataItem to be built using the function-selector defined as a\n * constant in the route implementation contract\n * @param routeIds a list of route identifiers\n * @param dataItems a list of functionSelectorData generated using the function-selector defined in the route Implementation\n */\n function executeRoutes(\n uint32[] calldata routeIds,\n bytes[] calldata dataItems\n ) external payable {\n uint256 routeIdslength = routeIds.length;\n if (routeIdslength != dataItems.length) revert ArrayLengthMismatch();\n for (uint256 index = 0; index < routeIdslength; ) {\n (bool success, bytes memory result) = addressAt(routeIds[index])\n .delegatecall(dataItems[index]);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n unchecked {\n ++index;\n }\n }\n }\n\n /**\n * @notice execute a controller function identified using the controllerId in the request\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped\n * @dev ensure the data in request to be built using the function-selector defined as a\n * constant in the controller implementation contract\n * @param socketControllerRequest socketControllerRequest with controllerId to identify the\n * controllerAddress and byteData constructed using functionSelector\n * of the function being invoked\n * @return bytes data received from the call delegated to controller\n */\n function executeController(\n ISocketGateway.SocketControllerRequest calldata socketControllerRequest\n ) external payable returns (bytes memory) {\n (bool success, bytes memory result) = controllers[\n socketControllerRequest.controllerId\n ].delegatecall(socketControllerRequest.data);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n return result;\n }\n\n /**\n * @notice sequentially executes all controller requests\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped\n * @dev ensure the data in each controller-request to be built using the function-selector defined as a\n * constant in the controller implementation contract\n * @param controllerRequests a list of socketControllerRequest\n * Each controllerRequest contains controllerId to identify the controllerAddress and\n * byteData constructed using functionSelector of the function being invoked\n */\n function executeControllers(\n ISocketGateway.SocketControllerRequest[] calldata controllerRequests\n ) external payable {\n for (uint32 index = 0; index < controllerRequests.length; ) {\n (bool success, bytes memory result) = controllers[\n controllerRequests[index].controllerId\n ].delegatecall(controllerRequests[index].data);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n unchecked {\n ++index;\n }\n }\n }\n\n /**************************************\n * ADMIN FUNCTIONS *\n **************************************/\n\n /**\n * @notice Add route to the socketGateway\n This is a restricted function to be called by only socketGatewayOwner\n * @dev ensure routeAddress is a verified bridge or middleware implementation address\n * @param routeAddress The address of bridge or middleware implementation contract deployed\n * @return Id of the route added to the routes-mapping in socketGateway storage\n */\n function addRoute(\n address routeAddress\n ) external onlyOwner returns (uint32) {\n uint32 routeId = routesCount;\n routes[routeId] = routeAddress;\n\n routesCount += 1;\n\n emit NewRouteAdded(routeId, routeAddress);\n\n return routeId;\n }\n\n /**\n * @notice Give Infinite or 0 approval to bridgeRoute for the tokenAddress\n This is a restricted function to be called by only socketGatewayOwner\n */\n\n function setApprovalForRouters(\n address[] memory routeAddresses,\n address[] memory tokenAddresses,\n bool isMax\n ) external onlyOwner {\n for (uint32 index = 0; index < routeAddresses.length; ) {\n ERC20(tokenAddresses[index]).approve(\n routeAddresses[index],\n isMax ? type(uint256).max : 0\n );\n unchecked {\n ++index;\n }\n }\n }\n\n /**\n * @notice Add controller to the socketGateway\n This is a restricted function to be called by only socketGatewayOwner\n * @dev ensure controllerAddress is a verified controller implementation address\n * @param controllerAddress The address of controller implementation contract deployed\n * @return Id of the controller added to the controllers-mapping in socketGateway storage\n */\n function addController(\n address controllerAddress\n ) external onlyOwner returns (uint32) {\n uint32 controllerId = controllerCount;\n\n controllers[controllerId] = controllerAddress;\n\n controllerCount += 1;\n\n emit ControllerAdded(controllerId, controllerAddress);\n\n return controllerId;\n }\n\n /**\n * @notice disable controller by setting ZeroAddress to the entry in controllers-mapping\n identified by controllerId as key.\n This is a restricted function to be called by only socketGatewayOwner\n * @param controllerId The Id of controller-implementation in the controllers mapping\n */\n function disableController(uint32 controllerId) public onlyOwner {\n controllers[controllerId] = disabledRouteAddress;\n emit ControllerDisabled(controllerId);\n }\n\n /**\n * @notice disable a route by setting ZeroAddress to the entry in routes-mapping\n identified by routeId as key.\n This is a restricted function to be called by only socketGatewayOwner\n * @param routeId The Id of route-implementation in the routes mapping\n */\n function disableRoute(uint32 routeId) external onlyOwner {\n routes[routeId] = disabledRouteAddress;\n emit RouteDisabled(routeId);\n }\n\n /*******************************************\n * RESTRICTED RESCUE FUNCTIONS *\n *******************************************/\n\n /**\n * @notice Rescues the ERC20 token to an address\n this is a restricted function to be called by only socketGatewayOwner\n * @dev as this is a restricted to socketGatewayOwner, ensure the userAddress is a known address\n * @param token address of the ERC20 token being rescued\n * @param userAddress address to which ERC20 is to be rescued\n * @param amount amount of ERC20 tokens being rescued\n */\n function rescueFunds(\n address token,\n address userAddress,\n uint256 amount\n ) external onlyOwner {\n ERC20(token).safeTransfer(userAddress, amount);\n }\n\n /**\n * @notice Rescues the native balance to an address\n this is a restricted function to be called by only socketGatewayOwner\n * @dev as this is a restricted to socketGatewayOwner, ensure the userAddress is a known address\n * @param userAddress address to which native-balance is to be rescued\n * @param amount amount of native-balance being rescued\n */\n function rescueEther(\n address payable userAddress,\n uint256 amount\n ) external onlyOwner {\n userAddress.transfer(amount);\n }\n\n /*******************************************\n * VIEW FUNCTIONS *\n *******************************************/\n\n /**\n * @notice Get routeImplementation address mapped to the routeId\n * @param routeId routeId is the key in the mapping for routes\n * @return route-implementation address\n */\n function getRoute(uint32 routeId) public view returns (address) {\n return addressAt(routeId);\n }\n\n /**\n * @notice Get controllerImplementation address mapped to the controllerId\n * @param controllerId controllerId is the key in the mapping for controllers\n * @return controller-implementation address\n */\n function getController(uint32 controllerId) public view returns (address) {\n return controllers[controllerId];\n }\n\n function addressAt(uint32 routeId) public view returns (address) {\n if (routeId < 385) {\n if (routeId < 257) {\n if (routeId < 129) {\n if (routeId < 65) {\n if (routeId < 33) {\n if (routeId < 17) {\n if (routeId < 9) {\n if (routeId < 5) {\n if (routeId < 3) {\n if (routeId == 1) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 3) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 7) {\n if (routeId == 5) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 7) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 13) {\n if (routeId < 11) {\n if (routeId == 9) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 11) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 15) {\n if (routeId == 13) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 15) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 25) {\n if (routeId < 21) {\n if (routeId < 19) {\n if (routeId == 17) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 19) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 23) {\n if (routeId == 21) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 23) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 29) {\n if (routeId < 27) {\n if (routeId == 25) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 27) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 31) {\n if (routeId == 29) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 31) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 49) {\n if (routeId < 41) {\n if (routeId < 37) {\n if (routeId < 35) {\n if (routeId == 33) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 35) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 39) {\n if (routeId == 37) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 39) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 45) {\n if (routeId < 43) {\n if (routeId == 41) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 43) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 47) {\n if (routeId == 45) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 47) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 57) {\n if (routeId < 53) {\n if (routeId < 51) {\n if (routeId == 49) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 51) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 55) {\n if (routeId == 53) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 55) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 61) {\n if (routeId < 59) {\n if (routeId == 57) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 59) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 63) {\n if (routeId == 61) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 63) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 97) {\n if (routeId < 81) {\n if (routeId < 73) {\n if (routeId < 69) {\n if (routeId < 67) {\n if (routeId == 65) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 67) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 71) {\n if (routeId == 69) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 71) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 77) {\n if (routeId < 75) {\n if (routeId == 73) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 75) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 79) {\n if (routeId == 77) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 79) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 89) {\n if (routeId < 85) {\n if (routeId < 83) {\n if (routeId == 81) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 83) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 87) {\n if (routeId == 85) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 87) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 93) {\n if (routeId < 91) {\n if (routeId == 89) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 91) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 95) {\n if (routeId == 93) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 95) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 113) {\n if (routeId < 105) {\n if (routeId < 101) {\n if (routeId < 99) {\n if (routeId == 97) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 99) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 103) {\n if (routeId == 101) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 103) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 109) {\n if (routeId < 107) {\n if (routeId == 105) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 107) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 111) {\n if (routeId == 109) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 111) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 121) {\n if (routeId < 117) {\n if (routeId < 115) {\n if (routeId == 113) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 115) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 119) {\n if (routeId == 117) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 119) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 125) {\n if (routeId < 123) {\n if (routeId == 121) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 123) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 127) {\n if (routeId == 125) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 127) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 193) {\n if (routeId < 161) {\n if (routeId < 145) {\n if (routeId < 137) {\n if (routeId < 133) {\n if (routeId < 131) {\n if (routeId == 129) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 131) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 135) {\n if (routeId == 133) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 135) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 141) {\n if (routeId < 139) {\n if (routeId == 137) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 139) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 143) {\n if (routeId == 141) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 143) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 153) {\n if (routeId < 149) {\n if (routeId < 147) {\n if (routeId == 145) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 147) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 151) {\n if (routeId == 149) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 151) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 157) {\n if (routeId < 155) {\n if (routeId == 153) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 155) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 159) {\n if (routeId == 157) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 159) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 177) {\n if (routeId < 169) {\n if (routeId < 165) {\n if (routeId < 163) {\n if (routeId == 161) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 163) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 167) {\n if (routeId == 165) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 167) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 173) {\n if (routeId < 171) {\n if (routeId == 169) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 171) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 175) {\n if (routeId == 173) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 175) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 185) {\n if (routeId < 181) {\n if (routeId < 179) {\n if (routeId == 177) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 179) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 183) {\n if (routeId == 181) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 183) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 189) {\n if (routeId < 187) {\n if (routeId == 185) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 187) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 191) {\n if (routeId == 189) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 191) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 225) {\n if (routeId < 209) {\n if (routeId < 201) {\n if (routeId < 197) {\n if (routeId < 195) {\n if (routeId == 193) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 195) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 199) {\n if (routeId == 197) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 199) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 205) {\n if (routeId < 203) {\n if (routeId == 201) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 203) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 207) {\n if (routeId == 205) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 207) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 217) {\n if (routeId < 213) {\n if (routeId < 211) {\n if (routeId == 209) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 211) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 215) {\n if (routeId == 213) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 215) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 221) {\n if (routeId < 219) {\n if (routeId == 217) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 219) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 223) {\n if (routeId == 221) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 223) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 241) {\n if (routeId < 233) {\n if (routeId < 229) {\n if (routeId < 227) {\n if (routeId == 225) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 227) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 231) {\n if (routeId == 229) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 231) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 237) {\n if (routeId < 235) {\n if (routeId == 233) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 235) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 239) {\n if (routeId == 237) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 239) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 249) {\n if (routeId < 245) {\n if (routeId < 243) {\n if (routeId == 241) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 243) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 247) {\n if (routeId == 245) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 247) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 253) {\n if (routeId < 251) {\n if (routeId == 249) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 251) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 255) {\n if (routeId == 253) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 255) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 321) {\n if (routeId < 289) {\n if (routeId < 273) {\n if (routeId < 265) {\n if (routeId < 261) {\n if (routeId < 259) {\n if (routeId == 257) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 259) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 263) {\n if (routeId == 261) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 263) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 269) {\n if (routeId < 267) {\n if (routeId == 265) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 267) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 271) {\n if (routeId == 269) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 271) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 281) {\n if (routeId < 277) {\n if (routeId < 275) {\n if (routeId == 273) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 275) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 279) {\n if (routeId == 277) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 279) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 285) {\n if (routeId < 283) {\n if (routeId == 281) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 283) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 287) {\n if (routeId == 285) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 287) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 305) {\n if (routeId < 297) {\n if (routeId < 293) {\n if (routeId < 291) {\n if (routeId == 289) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 291) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 295) {\n if (routeId == 293) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 295) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 301) {\n if (routeId < 299) {\n if (routeId == 297) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 299) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 303) {\n if (routeId == 301) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 303) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 313) {\n if (routeId < 309) {\n if (routeId < 307) {\n if (routeId == 305) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 307) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 311) {\n if (routeId == 309) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 311) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 317) {\n if (routeId < 315) {\n if (routeId == 313) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 315) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 319) {\n if (routeId == 317) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 319) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 353) {\n if (routeId < 337) {\n if (routeId < 329) {\n if (routeId < 325) {\n if (routeId < 323) {\n if (routeId == 321) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 323) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 327) {\n if (routeId == 325) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 327) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 333) {\n if (routeId < 331) {\n if (routeId == 329) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 331) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 335) {\n if (routeId == 333) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 335) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 345) {\n if (routeId < 341) {\n if (routeId < 339) {\n if (routeId == 337) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 339) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 343) {\n if (routeId == 341) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 343) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 349) {\n if (routeId < 347) {\n if (routeId == 345) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 347) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 351) {\n if (routeId == 349) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 351) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 369) {\n if (routeId < 361) {\n if (routeId < 357) {\n if (routeId < 355) {\n if (routeId == 353) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 355) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 359) {\n if (routeId == 357) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 359) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 365) {\n if (routeId < 363) {\n if (routeId == 361) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 363) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 367) {\n if (routeId == 365) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 367) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 377) {\n if (routeId < 373) {\n if (routeId < 371) {\n if (routeId == 369) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 371) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 375) {\n if (routeId == 373) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 375) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 381) {\n if (routeId < 379) {\n if (routeId == 377) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 379) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 383) {\n if (routeId == 381) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 383) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n }\n }\n }\n }\n\n if (routes[routeId] == address(0)) revert ZeroAddressNotAllowed();\n return routes[routeId];\n }\n\n /// @notice fallback function to handle swap, bridge execution\n /// @dev ensure routeId is converted to bytes4 and sent as msg.sig in the transaction\n fallback() external payable {\n address routeAddress = addressAt(uint32(msg.sig));\n\n bytes memory result;\n\n assembly {\n // copy function selector and any arguments\n calldatacopy(0, 4, sub(calldatasize(), 4))\n // execute function call using the facet\n result := delegatecall(\n gas(),\n routeAddress,\n 0,\n sub(calldatasize(), 4),\n 0,\n 0\n )\n // get any return value\n returndatacopy(0, 0, returndatasize())\n // return any return value or error back to the caller\n switch result\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n}\n" - }, - "src/swap/rainbow/Rainbow.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport \"../SwapImplBase.sol\";\nimport {Address0Provided, SwapFailed} from \"../../errors/SocketErrors.sol\";\nimport {RAINBOW} from \"../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Rainbow-Swap-Route Implementation\n * @notice Route implementation with functions to swap tokens via Rainbow-Swap\n * Called via SocketGateway if the routeId in the request maps to the routeId of RainbowImplementation\n * @author Socket dot tech.\n */\ncontract RainbowSwapImpl is SwapImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable RainbowIdentifier = RAINBOW;\n\n /// @notice unique name to identify the router, used to emit event upon successful bridging\n bytes32 public immutable NAME = keccak256(\"Rainbow-Router\");\n\n /// @notice address of rainbow-swap-aggregator to swap the tokens on Chain\n address payable public immutable rainbowSwapAggregator;\n\n /// @notice socketGatewayAddress to be initialised via storage variable SwapImplBase\n /// @notice rainbow swap aggregator contract is payable to allow ethereum swaps\n /// @dev ensure _rainbowSwapAggregator are set properly for the chainId in which the contract is being deployed\n constructor(\n address _rainbowSwapAggregator,\n address _socketGateway,\n address _socketDeployFactory\n ) SwapImplBase(_socketGateway, _socketDeployFactory) {\n rainbowSwapAggregator = payable(_rainbowSwapAggregator);\n }\n\n receive() external payable {}\n\n fallback() external payable {}\n\n /**\n * @notice function to swap tokens on the chain and transfer to receiver address\n * @notice This method is payable because the caller is doing token transfer and swap operation\n * @param fromToken address of token being Swapped\n * @param toToken address of token that recipient will receive after swap\n * @param amount amount of fromToken being swapped\n * @param receiverAddress recipient-address\n * @param swapExtraData additional Data to perform Swap via Rainbow-Aggregator\n * @return swapped amount (in toToken Address)\n */\n function performAction(\n address fromToken,\n address toToken,\n uint256 amount,\n address receiverAddress,\n bytes calldata swapExtraData\n ) external payable override returns (uint256) {\n if (fromToken == address(0)) {\n revert Address0Provided();\n }\n\n bytes memory swapCallData = abi.decode(swapExtraData, (bytes));\n\n uint256 _initialBalanceTokenOut;\n uint256 _finalBalanceTokenOut;\n\n ERC20 toTokenERC20 = ERC20(toToken);\n if (toToken != NATIVE_TOKEN_ADDRESS) {\n _initialBalanceTokenOut = toTokenERC20.balanceOf(socketGateway);\n } else {\n _initialBalanceTokenOut = address(this).balance;\n }\n\n if (fromToken != NATIVE_TOKEN_ADDRESS) {\n ERC20 token = ERC20(fromToken);\n token.safeTransferFrom(msg.sender, socketGateway, amount);\n token.safeApprove(rainbowSwapAggregator, amount);\n\n // solhint-disable-next-line\n (bool success, ) = rainbowSwapAggregator.call(swapCallData);\n\n if (!success) {\n revert SwapFailed();\n }\n\n token.safeApprove(rainbowSwapAggregator, 0);\n } else {\n (bool success, ) = rainbowSwapAggregator.call{value: amount}(\n swapCallData\n );\n if (!success) {\n revert SwapFailed();\n }\n }\n\n if (toToken != NATIVE_TOKEN_ADDRESS) {\n _finalBalanceTokenOut = toTokenERC20.balanceOf(socketGateway);\n } else {\n _finalBalanceTokenOut = address(this).balance;\n }\n\n uint256 returnAmount = _finalBalanceTokenOut - _initialBalanceTokenOut;\n\n if (toToken == NATIVE_TOKEN_ADDRESS) {\n payable(receiverAddress).transfer(returnAmount);\n } else {\n toTokenERC20.transfer(receiverAddress, returnAmount);\n }\n\n emit SocketSwapTokens(\n fromToken,\n toToken,\n returnAmount,\n amount,\n RainbowIdentifier,\n receiverAddress\n );\n\n return returnAmount;\n }\n\n /**\n * @notice function to swapWithIn SocketGateway - swaps tokens on the chain to socketGateway as recipient\n * @param fromToken token to be swapped\n * @param toToken token to which fromToken has to be swapped\n * @param amount amount of fromToken being swapped\n * @param swapExtraData encoded value of properties in the swapData Struct\n * @return swapped amount (in toToken Address)\n */\n function performActionWithIn(\n address fromToken,\n address toToken,\n uint256 amount,\n bytes calldata swapExtraData\n ) external payable override returns (uint256, address) {\n if (fromToken == address(0)) {\n revert Address0Provided();\n }\n\n bytes memory swapCallData = abi.decode(swapExtraData, (bytes));\n\n uint256 _initialBalanceTokenOut;\n uint256 _finalBalanceTokenOut;\n\n ERC20 toTokenERC20 = ERC20(toToken);\n if (toToken != NATIVE_TOKEN_ADDRESS) {\n _initialBalanceTokenOut = toTokenERC20.balanceOf(socketGateway);\n } else {\n _initialBalanceTokenOut = address(this).balance;\n }\n\n if (fromToken != NATIVE_TOKEN_ADDRESS) {\n ERC20 token = ERC20(fromToken);\n token.safeTransferFrom(msg.sender, socketGateway, amount);\n token.safeApprove(rainbowSwapAggregator, amount);\n\n // solhint-disable-next-line\n (bool success, ) = rainbowSwapAggregator.call(swapCallData);\n\n if (!success) {\n revert SwapFailed();\n }\n\n token.safeApprove(rainbowSwapAggregator, 0);\n } else {\n (bool success, ) = rainbowSwapAggregator.call{value: amount}(\n swapCallData\n );\n if (!success) {\n revert SwapFailed();\n }\n }\n\n if (toToken != NATIVE_TOKEN_ADDRESS) {\n _finalBalanceTokenOut = toTokenERC20.balanceOf(socketGateway);\n } else {\n _finalBalanceTokenOut = address(this).balance;\n }\n\n uint256 returnAmount = _finalBalanceTokenOut - _initialBalanceTokenOut;\n\n emit SocketSwapTokens(\n fromToken,\n toToken,\n returnAmount,\n amount,\n RainbowIdentifier,\n socketGateway\n );\n\n return (returnAmount, toToken);\n }\n}\n" - }, - "src/interfaces/ISocketBridgeBase.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\ninterface ISocketBridgeBase {\n function killme() external;\n}\n" - }, - "src/interfaces/ISocketRequest.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/**\n * @title ISocketRoute\n * @notice Interface with Request DataStructures to invoke controller functions.\n * @author Socket dot tech.\n */\ninterface ISocketRequest {\n struct SwapMultiBridgeRequest {\n uint32 swapRouteId;\n bytes swapImplData;\n uint32[] bridgeRouteIds;\n bytes[] bridgeImplDataItems;\n uint256[] bridgeRatios;\n bytes[] eventDataItems;\n }\n\n // Datastructure for Refuel-Swap-Bridge function\n struct RefuelSwapBridgeRequest {\n uint32 refuelRouteId;\n bytes refuelData;\n uint32 swapRouteId;\n bytes swapData;\n uint32 bridgeRouteId;\n bytes bridgeData;\n }\n\n // Datastructure for DeductFees-Swap function\n struct FeesTakerSwapRequest {\n address feesTakerAddress;\n address feesToken;\n uint256 feesAmount;\n uint32 routeId;\n bytes swapRequestData;\n }\n\n // Datastructure for DeductFees-Bridge function\n struct FeesTakerBridgeRequest {\n address feesTakerAddress;\n address feesToken;\n uint256 feesAmount;\n uint32 routeId;\n bytes bridgeRequestData;\n }\n\n // Datastructure for DeductFees-MultiBridge function\n struct FeesTakerMultiBridgeRequest {\n address feesTakerAddress;\n address feesToken;\n uint256 feesAmount;\n uint32[] bridgeRouteIds;\n bytes[] bridgeRequestDataItems;\n }\n\n // Datastructure for DeductFees-Swap-Bridge function\n struct FeesTakerSwapBridgeRequest {\n address feesTakerAddress;\n address feesToken;\n uint256 feesAmount;\n uint32 swapRouteId;\n bytes swapData;\n uint32 bridgeRouteId;\n bytes bridgeData;\n }\n\n // Datastructure for DeductFees-Refuel-Swap-Bridge function\n struct FeesTakerRefuelSwapBridgeRequest {\n address feesTakerAddress;\n address feesToken;\n uint256 feesAmount;\n uint32 refuelRouteId;\n bytes refuelData;\n uint32 swapRouteId;\n bytes swapData;\n uint32 bridgeRouteId;\n bytes bridgeData;\n }\n}\n" - }, - "src/utils/Ownable.sol": { - "content": "// SPDX-License-Identifier: GPL-3.0-only\npragma solidity ^0.8.4;\n\nimport {OnlyOwner, OnlyNominee} from \"../errors/SocketErrors.sol\";\n\nabstract contract Ownable {\n address private _owner;\n address private _nominee;\n\n event OwnerNominated(address indexed nominee);\n event OwnerClaimed(address indexed claimer);\n\n constructor(address owner_) {\n _claimOwner(owner_);\n }\n\n modifier onlyOwner() {\n if (msg.sender != _owner) {\n revert OnlyOwner();\n }\n _;\n }\n\n function owner() public view returns (address) {\n return _owner;\n }\n\n function nominee() public view returns (address) {\n return _nominee;\n }\n\n function nominateOwner(address nominee_) external {\n if (msg.sender != _owner) {\n revert OnlyOwner();\n }\n _nominee = nominee_;\n emit OwnerNominated(_nominee);\n }\n\n function claimOwner() external {\n if (msg.sender != _nominee) {\n revert OnlyNominee();\n }\n _claimOwner(msg.sender);\n }\n\n function _claimOwner(address claimer_) internal {\n _owner = claimer_;\n _nominee = address(0);\n emit OwnerClaimed(claimer_);\n }\n}\n" - }, - "lib/solmate/src/tokens/ERC20.sol": { - "content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\n/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.\n/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)\n/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)\n/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.\nabstract contract ERC20 {\n /*//////////////////////////////////////////////////////////////\n EVENTS\n //////////////////////////////////////////////////////////////*/\n\n event Transfer(address indexed from, address indexed to, uint256 amount);\n\n event Approval(address indexed owner, address indexed spender, uint256 amount);\n\n /*//////////////////////////////////////////////////////////////\n METADATA STORAGE\n //////////////////////////////////////////////////////////////*/\n\n string public name;\n\n string public symbol;\n\n uint8 public immutable decimals;\n\n /*//////////////////////////////////////////////////////////////\n ERC20 STORAGE\n //////////////////////////////////////////////////////////////*/\n\n uint256 public totalSupply;\n\n mapping(address => uint256) public balanceOf;\n\n mapping(address => mapping(address => uint256)) public allowance;\n\n /*//////////////////////////////////////////////////////////////\n EIP-2612 STORAGE\n //////////////////////////////////////////////////////////////*/\n\n uint256 internal immutable INITIAL_CHAIN_ID;\n\n bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;\n\n mapping(address => uint256) public nonces;\n\n /*//////////////////////////////////////////////////////////////\n CONSTRUCTOR\n //////////////////////////////////////////////////////////////*/\n\n constructor(\n string memory _name,\n string memory _symbol,\n uint8 _decimals\n ) {\n name = _name;\n symbol = _symbol;\n decimals = _decimals;\n\n INITIAL_CHAIN_ID = block.chainid;\n INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();\n }\n\n /*//////////////////////////////////////////////////////////////\n ERC20 LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function approve(address spender, uint256 amount) public virtual returns (bool) {\n allowance[msg.sender][spender] = amount;\n\n emit Approval(msg.sender, spender, amount);\n\n return true;\n }\n\n function transfer(address to, uint256 amount) public virtual returns (bool) {\n balanceOf[msg.sender] -= amount;\n\n // Cannot overflow because the sum of all user\n // balances can't exceed the max uint256 value.\n unchecked {\n balanceOf[to] += amount;\n }\n\n emit Transfer(msg.sender, to, amount);\n\n return true;\n }\n\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) public virtual returns (bool) {\n uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.\n\n if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;\n\n balanceOf[from] -= amount;\n\n // Cannot overflow because the sum of all user\n // balances can't exceed the max uint256 value.\n unchecked {\n balanceOf[to] += amount;\n }\n\n emit Transfer(from, to, amount);\n\n return true;\n }\n\n /*//////////////////////////////////////////////////////////////\n EIP-2612 LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) public virtual {\n require(deadline >= block.timestamp, \"PERMIT_DEADLINE_EXPIRED\");\n\n // Unchecked because the only math done is incrementing\n // the owner's nonce which cannot realistically overflow.\n unchecked {\n address recoveredAddress = ecrecover(\n keccak256(\n abi.encodePacked(\n \"\\x19\\x01\",\n DOMAIN_SEPARATOR(),\n keccak256(\n abi.encode(\n keccak256(\n \"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)\"\n ),\n owner,\n spender,\n value,\n nonces[owner]++,\n deadline\n )\n )\n )\n ),\n v,\n r,\n s\n );\n\n require(recoveredAddress != address(0) && recoveredAddress == owner, \"INVALID_SIGNER\");\n\n allowance[recoveredAddress][spender] = value;\n }\n\n emit Approval(owner, spender, value);\n }\n\n function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {\n return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();\n }\n\n function computeDomainSeparator() internal view virtual returns (bytes32) {\n return\n keccak256(\n abi.encode(\n keccak256(\"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\"),\n keccak256(bytes(name)),\n keccak256(\"1\"),\n block.chainid,\n address(this)\n )\n );\n }\n\n /*//////////////////////////////////////////////////////////////\n INTERNAL MINT/BURN LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function _mint(address to, uint256 amount) internal virtual {\n totalSupply += amount;\n\n // Cannot overflow because the sum of all user\n // balances can't exceed the max uint256 value.\n unchecked {\n balanceOf[to] += amount;\n }\n\n emit Transfer(address(0), to, amount);\n }\n\n function _burn(address from, uint256 amount) internal virtual {\n balanceOf[from] -= amount;\n\n // Cannot underflow because a user's balance\n // will never be larger than the total supply.\n unchecked {\n totalSupply -= amount;\n }\n\n emit Transfer(from, address(0), amount);\n }\n}\n" - }, - "src/controllers/RefuelSwapAndBridgeController.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {ISocketRequest} from \"../interfaces/ISocketRequest.sol\";\nimport {ISocketRoute} from \"../interfaces/ISocketRoute.sol\";\nimport {BaseController} from \"./BaseController.sol\";\n\n/**\n * @title RefuelSwapAndBridge Controller Implementation\n * @notice Controller with composed actions for Refuel,Swap and Bridge to be executed Sequentially and this is atomic\n * @author Socket dot tech.\n */\ncontract RefuelSwapAndBridgeController is BaseController {\n /// @notice Function-selector to invoke refuel-swap-bridge function\n /// @dev This function selector is to be used while buidling transaction-data\n bytes4 public immutable REFUEL_SWAP_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"refuelAndSwapAndBridge((uint32,bytes,uint32,bytes,uint32,bytes))\"\n )\n );\n\n /// @notice socketGatewayAddress to be initialised via storage variable BaseController\n constructor(\n address _socketGatewayAddress\n ) BaseController(_socketGatewayAddress) {}\n\n /**\n * @notice function to handle refuel followed by Swap and Bridge actions\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param rsbRequest Request with data to execute refuel followed by swap and bridge\n * @return output data from bridging operation\n */\n function refuelAndSwapAndBridge(\n ISocketRequest.RefuelSwapBridgeRequest calldata rsbRequest\n ) public payable returns (bytes memory) {\n _executeRoute(rsbRequest.refuelRouteId, rsbRequest.refuelData);\n\n // refuel is also a bridging activity via refuel-route-implementation\n bytes memory swapResponseData = _executeRoute(\n rsbRequest.swapRouteId,\n rsbRequest.swapData\n );\n\n uint256 swapAmount = abi.decode(swapResponseData, (uint256));\n\n //sequence of arguments for implData: amount, token, data\n // Bridging the swapAmount received in the preceeding step\n bytes memory bridgeImpldata = abi.encodeWithSelector(\n BRIDGE_AFTER_SWAP_SELECTOR,\n swapAmount,\n rsbRequest.bridgeData\n );\n\n return _executeRoute(rsbRequest.bridgeRouteId, bridgeImpldata);\n }\n}\n" - }, - "src/interfaces/ISocketGateway.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/**\n * @title ISocketGateway\n * @notice Interface for SocketGateway functions.\n * @dev functions can be added here for invocation from external contracts or off-chain\n * @author Socket dot tech.\n */\ninterface ISocketGateway {\n /**\n * @notice Request-struct for controllerRequests\n * @dev ensure the value for data is generated using the function-selectors defined in the controllerImplementation contracts\n */\n struct SocketControllerRequest {\n // controllerId is the id mapped to the controllerAddress\n uint32 controllerId;\n // transactionImplData generated off-chain or by caller using function-selector of the controllerContract\n bytes data;\n }\n\n // @notice view to get owner-address\n function owner() external view returns (address);\n}\n" - }, - "src/libraries/Pb.sol": { - "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity ^0.8.4;\n\n// runtime proto sol library\nlibrary Pb {\n enum WireType {\n Varint,\n Fixed64,\n LengthDelim,\n StartGroup,\n EndGroup,\n Fixed32\n }\n\n struct Buffer {\n uint256 idx; // the start index of next read. when idx=b.length, we're done\n bytes b; // hold serialized proto msg, readonly\n }\n\n // create a new in-memory Buffer object from raw msg bytes\n function fromBytes(\n bytes memory raw\n ) internal pure returns (Buffer memory buf) {\n buf.b = raw;\n buf.idx = 0;\n }\n\n // whether there are unread bytes\n function hasMore(Buffer memory buf) internal pure returns (bool) {\n return buf.idx < buf.b.length;\n }\n\n // decode current field number and wiretype\n function decKey(\n Buffer memory buf\n ) internal pure returns (uint256 tag, WireType wiretype) {\n uint256 v = decVarint(buf);\n tag = v / 8;\n wiretype = WireType(v & 7);\n }\n\n // read varint from current buf idx, move buf.idx to next read, return the int value\n function decVarint(Buffer memory buf) internal pure returns (uint256 v) {\n bytes10 tmp; // proto int is at most 10 bytes (7 bits can be used per byte)\n bytes memory bb = buf.b; // get buf.b mem addr to use in assembly\n v = buf.idx; // use v to save one additional uint variable\n assembly {\n tmp := mload(add(add(bb, 32), v)) // load 10 bytes from buf.b[buf.idx] to tmp\n }\n uint256 b; // store current byte content\n v = 0; // reset to 0 for return value\n for (uint256 i = 0; i < 10; i++) {\n assembly {\n b := byte(i, tmp) // don't use tmp[i] because it does bound check and costs extra\n }\n v |= (b & 0x7F) << (i * 7);\n if (b & 0x80 == 0) {\n buf.idx += i + 1;\n return v;\n }\n }\n revert(); // i=10, invalid varint stream\n }\n\n // read length delimited field and return bytes\n function decBytes(\n Buffer memory buf\n ) internal pure returns (bytes memory b) {\n uint256 len = decVarint(buf);\n uint256 end = buf.idx + len;\n require(end <= buf.b.length); // avoid overflow\n b = new bytes(len);\n bytes memory bufB = buf.b; // get buf.b mem addr to use in assembly\n uint256 bStart;\n uint256 bufBStart = buf.idx;\n assembly {\n bStart := add(b, 32)\n bufBStart := add(add(bufB, 32), bufBStart)\n }\n for (uint256 i = 0; i < len; i += 32) {\n assembly {\n mstore(add(bStart, i), mload(add(bufBStart, i)))\n }\n }\n buf.idx = end;\n }\n\n // move idx pass current value field, to beginning of next tag or msg end\n function skipValue(Buffer memory buf, WireType wire) internal pure {\n if (wire == WireType.Varint) {\n decVarint(buf);\n } else if (wire == WireType.LengthDelim) {\n uint256 len = decVarint(buf);\n buf.idx += len; // skip len bytes value data\n require(buf.idx <= buf.b.length); // avoid overflow\n } else {\n revert();\n } // unsupported wiretype\n }\n\n function _uint256(bytes memory b) internal pure returns (uint256 v) {\n require(b.length <= 32); // b's length must be smaller than or equal to 32\n assembly {\n v := mload(add(b, 32))\n } // load all 32bytes to v\n v = v >> (8 * (32 - b.length)); // only first b.length is valid\n }\n\n function _address(bytes memory b) internal pure returns (address v) {\n v = _addressPayable(b);\n }\n\n function _addressPayable(\n bytes memory b\n ) internal pure returns (address payable v) {\n require(b.length == 20);\n //load 32bytes then shift right 12 bytes\n assembly {\n v := div(mload(add(b, 32)), 0x1000000000000000000000000)\n }\n }\n\n function _bytes32(bytes memory b) internal pure returns (bytes32 v) {\n require(b.length == 32);\n assembly {\n v := mload(add(b, 32))\n }\n }\n}\n" - }, - "src/bridges/BridgeImplBase.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {ISocketGateway} from \"../interfaces/ISocketGateway.sol\";\nimport {ISocketRoute} from \"../interfaces/ISocketRoute.sol\";\nimport {OnlySocketGatewayOwner, OnlySocketDeployer} from \"../errors/SocketErrors.sol\";\n\n/**\n * @title Abstract Implementation Contract.\n * @notice All Bridge Implementation will follow this interface.\n */\nabstract contract BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n /// @notice Address used to identify if it is a native token transfer or not\n address public immutable NATIVE_TOKEN_ADDRESS =\n address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);\n\n /// @notice immutable variable to store the socketGateway address\n address public immutable socketGateway;\n\n /// @notice immutable variable to store the socketGateway address\n address public immutable socketDeployFactory;\n\n /// @notice immutable variable with instance of SocketRoute to access route functions\n ISocketRoute public immutable socketRoute;\n\n /// @notice FunctionSelector used to delegatecall from swap to the function of bridge router implementation\n bytes4 public immutable BRIDGE_AFTER_SWAP_SELECTOR =\n bytes4(keccak256(\"bridgeAfterSwap(uint256,bytes)\"));\n\n /****************************************\n * EVENTS *\n ****************************************/\n\n event SocketBridge(\n uint256 amount,\n address token,\n uint256 toChainId,\n bytes32 bridgeName,\n address sender,\n address receiver,\n bytes32 metadata\n );\n\n /**\n * @notice Construct the base for all BridgeImplementations.\n * @param _socketGateway Socketgateway address, an immutable variable to set.\n * @param _socketDeployFactory Socket Deploy Factory address, an immutable variable to set.\n */\n constructor(address _socketGateway, address _socketDeployFactory) {\n socketGateway = _socketGateway;\n socketDeployFactory = _socketDeployFactory;\n socketRoute = ISocketRoute(_socketGateway);\n }\n\n /****************************************\n * MODIFIERS *\n ****************************************/\n\n /// @notice Implementing contract needs to make use of the modifier where restricted access is to be used\n modifier isSocketGatewayOwner() {\n if (msg.sender != ISocketGateway(socketGateway).owner()) {\n revert OnlySocketGatewayOwner();\n }\n _;\n }\n\n /// @notice Implementing contract needs to make use of the modifier where restricted access is to be used\n modifier isSocketDeployFactory() {\n if (msg.sender != socketDeployFactory) {\n revert OnlySocketDeployer();\n }\n _;\n }\n\n /****************************************\n * RESTRICTED FUNCTIONS *\n ****************************************/\n\n /**\n * @notice function to rescue the ERC20 tokens in the bridge Implementation contract\n * @notice this is a function restricted to Owner of SocketGateway only\n * @param token address of ERC20 token being rescued\n * @param userAddress receipient address to which ERC20 tokens will be rescued to\n * @param amount amount of ERC20 tokens being rescued\n */\n function rescueFunds(\n address token,\n address userAddress,\n uint256 amount\n ) external isSocketGatewayOwner {\n ERC20(token).safeTransfer(userAddress, amount);\n }\n\n /**\n * @notice function to rescue the native-balance in the bridge Implementation contract\n * @notice this is a function restricted to Owner of SocketGateway only\n * @param userAddress receipient address to which native-balance will be rescued to\n * @param amount amount of native balance tokens being rescued\n */\n function rescueEther(\n address payable userAddress,\n uint256 amount\n ) external isSocketGatewayOwner {\n userAddress.transfer(amount);\n }\n\n function killme() external isSocketDeployFactory {\n selfdestruct(payable(msg.sender));\n }\n\n /******************************\n * VIRTUAL FUNCTIONS *\n *****************************/\n\n /**\n * @notice function to bridge which is succeeding the swap function\n * @notice this function is to be used only when bridging as a succeeding step\n * @notice All bridge implementation contracts must implement this function\n * @notice bridge-implementations will have a bridge specific struct with properties used in bridging\n * @param bridgeData encoded value of properties in the bridgeData Struct\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable virtual;\n}\n" - }, - "src/bridges/cbridge/CelerImpl.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport \"../../libraries/Pb.sol\";\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport \"./interfaces/cbridge.sol\";\nimport \"./interfaces/ICelerStorageWrapper.sol\";\nimport {TransferIdExists, InvalidCelerRefund, CelerAlreadyRefunded, CelerRefundNotReady} from \"../../errors/SocketErrors.sol\";\nimport {BridgeImplBase} from \"../BridgeImplBase.sol\";\nimport {CBRIDGE} from \"../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Celer-Route Implementation\n * @notice Route implementation with functions to bridge ERC20 and Native via Celer-Bridge\n * Called via SocketGateway if the routeId in the request maps to the routeId of CelerImplementation\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ncontract CelerImpl is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable CBridgeIdentifier = CBRIDGE;\n\n /// @notice Utility to perform operation on Buffer\n using Pb for Pb.Buffer;\n\n /// @notice Function-selector for ERC20-token bridging on Celer-Route\n /// @dev This function selector is to be used while building transaction-data to bridge ERC20 tokens\n bytes4 public immutable CELER_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(address,address,uint256,bytes32,uint64,uint64,uint32)\"\n )\n );\n\n /// @notice Function-selector for Native bridging on Celer-Route\n /// @dev This function selector is to be used while building transaction-data to bridge Native tokens\n bytes4 public immutable CELER_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeNativeTo(address,uint256,bytes32,uint64,uint64,uint32)\"\n )\n );\n\n bytes4 public immutable CELER_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(address,uint64,uint32,uint64,bytes32))\"\n )\n );\n\n /// @notice router Contract instance used to deposit ERC20 and Native on to Celer-Bridge\n /// @dev contract instance is to be initialized in the constructor using the routerAddress passed as constructor argument\n ICBridge public immutable router;\n\n /// @notice celerStorageWrapper Contract instance used to store the transferId generated during ERC20 and Native bridge on to Celer-Bridge\n /// @dev contract instance is to be initialized in the constructor using the celerStorageWrapperAddress passed as constructor argument\n ICelerStorageWrapper public immutable celerStorageWrapper;\n\n /// @notice WETH token address\n address public immutable weth;\n\n /// @notice chainId used during generation of transferId generated while bridging ERC20 and Native on to Celer-Bridge\n /// @dev this is to be initialised in the constructor\n uint64 public immutable chainId;\n\n struct WithdrawMsg {\n uint64 chainid; // tag: 1\n uint64 seqnum; // tag: 2\n address receiver; // tag: 3\n address token; // tag: 4\n uint256 amount; // tag: 5\n bytes32 refid; // tag: 6\n }\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n /// @dev ensure routerAddress, weth-address, celerStorageWrapperAddress are set properly for the chainId in which the contract is being deployed\n constructor(\n address _routerAddress,\n address _weth,\n address _celerStorageWrapperAddress,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n router = ICBridge(_routerAddress);\n celerStorageWrapper = ICelerStorageWrapper(_celerStorageWrapperAddress);\n weth = _weth;\n chainId = uint64(block.chainid);\n }\n\n // Function to receive Ether. msg.data must be empty\n receive() external payable {}\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct CelerBridgeDataNoToken {\n address receiverAddress;\n uint64 toChainId;\n uint32 maxSlippage;\n uint64 nonce;\n bytes32 metadata;\n }\n\n struct CelerBridgeData {\n address token;\n address receiverAddress;\n uint64 toChainId;\n uint32 maxSlippage;\n uint64 nonce;\n bytes32 metadata;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in CelerBridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for CelerBridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n CelerBridgeData memory celerBridgeData = abi.decode(\n bridgeData,\n (CelerBridgeData)\n );\n\n if (celerBridgeData.token == NATIVE_TOKEN_ADDRESS) {\n // transferId is generated using the request-params and nonce of the account\n // transferId should be unique for each request and this is used while handling refund from celerBridge\n bytes32 transferId = keccak256(\n abi.encodePacked(\n address(this),\n celerBridgeData.receiverAddress,\n weth,\n amount,\n celerBridgeData.toChainId,\n celerBridgeData.nonce,\n chainId\n )\n );\n\n // transferId is stored in CelerStorageWrapper with in a mapping where key is transferId and value is the msg-sender\n celerStorageWrapper.setAddressForTransferId(transferId, msg.sender);\n\n router.sendNative{value: amount}(\n celerBridgeData.receiverAddress,\n amount,\n celerBridgeData.toChainId,\n celerBridgeData.nonce,\n celerBridgeData.maxSlippage\n );\n } else {\n // transferId is generated using the request-params and nonce of the account\n // transferId should be unique for each request and this is used while handling refund from celerBridge\n bytes32 transferId = keccak256(\n abi.encodePacked(\n address(this),\n celerBridgeData.receiverAddress,\n celerBridgeData.token,\n amount,\n celerBridgeData.toChainId,\n celerBridgeData.nonce,\n chainId\n )\n );\n\n // transferId is stored in CelerStorageWrapper with in a mapping where key is transferId and value is the msg-sender\n celerStorageWrapper.setAddressForTransferId(transferId, msg.sender);\n router.send(\n celerBridgeData.receiverAddress,\n celerBridgeData.token,\n amount,\n celerBridgeData.toChainId,\n celerBridgeData.nonce,\n celerBridgeData.maxSlippage\n );\n }\n\n emit SocketBridge(\n amount,\n celerBridgeData.token,\n celerBridgeData.toChainId,\n CBridgeIdentifier,\n msg.sender,\n celerBridgeData.receiverAddress,\n celerBridgeData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in CelerBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param celerBridgeData encoded data for CelerBridgeData\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n CelerBridgeDataNoToken calldata celerBridgeData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n\n if (token == NATIVE_TOKEN_ADDRESS) {\n // transferId is generated using the request-params and nonce of the account\n // transferId should be unique for each request and this is used while handling refund from celerBridge\n bytes32 transferId = keccak256(\n abi.encodePacked(\n address(this),\n celerBridgeData.receiverAddress,\n weth,\n bridgeAmount,\n celerBridgeData.toChainId,\n celerBridgeData.nonce,\n chainId\n )\n );\n\n // transferId is stored in CelerStorageWrapper with in a mapping where key is transferId and value is the msg-sender\n celerStorageWrapper.setAddressForTransferId(transferId, msg.sender);\n\n router.sendNative{value: bridgeAmount}(\n celerBridgeData.receiverAddress,\n bridgeAmount,\n celerBridgeData.toChainId,\n celerBridgeData.nonce,\n celerBridgeData.maxSlippage\n );\n } else {\n // transferId is generated using the request-params and nonce of the account\n // transferId should be unique for each request and this is used while handling refund from celerBridge\n bytes32 transferId = keccak256(\n abi.encodePacked(\n address(this),\n celerBridgeData.receiverAddress,\n token,\n bridgeAmount,\n celerBridgeData.toChainId,\n celerBridgeData.nonce,\n chainId\n )\n );\n\n // transferId is stored in CelerStorageWrapper with in a mapping where key is transferId and value is the msg-sender\n celerStorageWrapper.setAddressForTransferId(transferId, msg.sender);\n router.send(\n celerBridgeData.receiverAddress,\n token,\n bridgeAmount,\n celerBridgeData.toChainId,\n celerBridgeData.nonce,\n celerBridgeData.maxSlippage\n );\n }\n\n emit SocketBridge(\n bridgeAmount,\n token,\n celerBridgeData.toChainId,\n CBridgeIdentifier,\n msg.sender,\n celerBridgeData.receiverAddress,\n celerBridgeData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via Celer-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param receiverAddress address of recipient\n * @param token address of token being bridged\n * @param amount amount of token for bridging\n * @param toChainId destination ChainId\n * @param nonce nonce of the sender-account address\n * @param maxSlippage maximum Slippage for the bridging\n */\n function bridgeERC20To(\n address receiverAddress,\n address token,\n uint256 amount,\n bytes32 metadata,\n uint64 toChainId,\n uint64 nonce,\n uint32 maxSlippage\n ) external payable {\n /// @notice transferId is generated using the request-params and nonce of the account\n /// @notice transferId should be unique for each request and this is used while handling refund from celerBridge\n bytes32 transferId = keccak256(\n abi.encodePacked(\n address(this),\n receiverAddress,\n token,\n amount,\n toChainId,\n nonce,\n chainId\n )\n );\n\n /// @notice stored in the CelerStorageWrapper contract\n celerStorageWrapper.setAddressForTransferId(transferId, msg.sender);\n\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n router.send(\n receiverAddress,\n token,\n amount,\n toChainId,\n nonce,\n maxSlippage\n );\n\n emit SocketBridge(\n amount,\n token,\n toChainId,\n CBridgeIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n\n /**\n * @notice function to handle Native bridging to receipent via Celer-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param receiverAddress address of recipient\n * @param amount amount of token for bridging\n * @param toChainId destination ChainId\n * @param nonce nonce of the sender-account address\n * @param maxSlippage maximum Slippage for the bridging\n */\n function bridgeNativeTo(\n address receiverAddress,\n uint256 amount,\n bytes32 metadata,\n uint64 toChainId,\n uint64 nonce,\n uint32 maxSlippage\n ) external payable {\n bytes32 transferId = keccak256(\n abi.encodePacked(\n address(this),\n receiverAddress,\n weth,\n amount,\n toChainId,\n nonce,\n chainId\n )\n );\n\n celerStorageWrapper.setAddressForTransferId(transferId, msg.sender);\n\n router.sendNative{value: amount}(\n receiverAddress,\n amount,\n toChainId,\n nonce,\n maxSlippage\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n toChainId,\n CBridgeIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n\n /**\n * @notice function to handle refund from CelerBridge-Router\n * @param _request request data generated offchain using the celer-SDK\n * @param _sigs generated offchain using the celer-SDK\n * @param _signers generated offchain using the celer-SDK\n * @param _powers generated offchain using the celer-SDK\n */\n function refundCelerUser(\n bytes calldata _request,\n bytes[] calldata _sigs,\n address[] calldata _signers,\n uint256[] calldata _powers\n ) external payable {\n WithdrawMsg memory request = decWithdrawMsg(_request);\n bytes32 transferId = keccak256(\n abi.encodePacked(\n request.chainid,\n request.seqnum,\n request.receiver,\n request.token,\n request.amount\n )\n );\n uint256 _initialNativeBalance = address(this).balance;\n uint256 _initialTokenBalance = ERC20(request.token).balanceOf(\n address(this)\n );\n if (!router.withdraws(transferId)) {\n router.withdraw(_request, _sigs, _signers, _powers);\n }\n\n if (request.receiver != socketGateway) {\n revert InvalidCelerRefund();\n }\n\n address _receiver = celerStorageWrapper.getAddressFromTransferId(\n request.refid\n );\n celerStorageWrapper.deleteTransferId(request.refid);\n\n if (_receiver == address(0)) {\n revert CelerAlreadyRefunded();\n }\n\n uint256 _nativeBalanceAfter = address(this).balance;\n uint256 _tokenBalanceAfter = ERC20(request.token).balanceOf(\n address(this)\n );\n if (_nativeBalanceAfter > _initialNativeBalance) {\n if ((_nativeBalanceAfter - _initialNativeBalance) != request.amount)\n revert CelerRefundNotReady();\n payable(_receiver).transfer(request.amount);\n return;\n }\n\n if (_tokenBalanceAfter > _initialTokenBalance) {\n if ((_tokenBalanceAfter - _initialTokenBalance) != request.amount)\n revert CelerRefundNotReady();\n ERC20(request.token).safeTransfer(_receiver, request.amount);\n return;\n }\n\n revert CelerRefundNotReady();\n }\n\n function decWithdrawMsg(\n bytes memory raw\n ) internal pure returns (WithdrawMsg memory m) {\n Pb.Buffer memory buf = Pb.fromBytes(raw);\n\n uint256 tag;\n Pb.WireType wire;\n while (buf.hasMore()) {\n (tag, wire) = buf.decKey();\n if (false) {}\n // solidity has no switch/case\n else if (tag == 1) {\n m.chainid = uint64(buf.decVarint());\n } else if (tag == 2) {\n m.seqnum = uint64(buf.decVarint());\n } else if (tag == 3) {\n m.receiver = Pb._address(buf.decBytes());\n } else if (tag == 4) {\n m.token = Pb._address(buf.decBytes());\n } else if (tag == 5) {\n m.amount = Pb._uint256(buf.decBytes());\n } else if (tag == 6) {\n m.refid = Pb._bytes32(buf.decBytes());\n } else {\n buf.skipValue(wire);\n } // skip value of unknown tag\n }\n } // end decoder WithdrawMsg\n}\n" - }, - "src/libraries/LibBytes.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n// Functions taken out from https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol\nlibrary LibBytes {\n // solhint-disable no-inline-assembly\n\n // LibBytes specific errors\n error SliceOverflow();\n error SliceOutOfBounds();\n error AddressOutOfBounds();\n error UintOutOfBounds();\n\n // -------------------------\n\n function concat(\n bytes memory _preBytes,\n bytes memory _postBytes\n ) internal pure returns (bytes memory) {\n bytes memory tempBytes;\n\n assembly {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // Store the length of the first bytes array at the beginning of\n // the memory for tempBytes.\n let length := mload(_preBytes)\n mstore(tempBytes, length)\n\n // Maintain a memory counter for the current write location in the\n // temp bytes array by adding the 32 bytes for the array length to\n // the starting location.\n let mc := add(tempBytes, 0x20)\n // Stop copying when the memory counter reaches the length of the\n // first bytes array.\n let end := add(mc, length)\n\n for {\n // Initialize a copy counter to the start of the _preBytes data,\n // 32 bytes into its memory.\n let cc := add(_preBytes, 0x20)\n } lt(mc, end) {\n // Increase both counters by 32 bytes each iteration.\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n // Write the _preBytes data into the tempBytes memory 32 bytes\n // at a time.\n mstore(mc, mload(cc))\n }\n\n // Add the length of _postBytes to the current length of tempBytes\n // and store it as the new length in the first 32 bytes of the\n // tempBytes memory.\n length := mload(_postBytes)\n mstore(tempBytes, add(length, mload(tempBytes)))\n\n // Move the memory counter back from a multiple of 0x20 to the\n // actual end of the _preBytes data.\n mc := end\n // Stop copying when the memory counter reaches the new combined\n // length of the arrays.\n end := add(mc, length)\n\n for {\n let cc := add(_postBytes, 0x20)\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n // Update the free-memory pointer by padding our last write location\n // to 32 bytes: add 31 bytes to the end of tempBytes to move to the\n // next 32 byte block, then round down to the nearest multiple of\n // 32. If the sum of the length of the two arrays is zero then add\n // one before rounding down to leave a blank 32 bytes (the length block with 0).\n mstore(\n 0x40,\n and(\n add(add(end, iszero(add(length, mload(_preBytes)))), 31),\n not(31) // Round down to the nearest 32 bytes.\n )\n )\n }\n\n return tempBytes;\n }\n\n function slice(\n bytes memory _bytes,\n uint256 _start,\n uint256 _length\n ) internal pure returns (bytes memory) {\n if (_length + 31 < _length) {\n revert SliceOverflow();\n }\n if (_bytes.length < _start + _length) {\n revert SliceOutOfBounds();\n }\n\n bytes memory tempBytes;\n\n assembly {\n switch iszero(_length)\n case 0 {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // The first word of the slice result is potentially a partial\n // word read from the original array. To read it, we calculate\n // the length of that partial word and start copying that many\n // bytes into the array. The first word we copy will start with\n // data we don't care about, but the last `lengthmod` bytes will\n // land at the beginning of the contents of the new array. When\n // we're done copying, we overwrite the full first word with\n // the actual length of the slice.\n let lengthmod := and(_length, 31)\n\n // The multiplication in the next line is necessary\n // because when slicing multiples of 32 bytes (lengthmod == 0)\n // the following copy loop was copying the origin's length\n // and then ending prematurely not copying everything it should.\n let mc := add(\n add(tempBytes, lengthmod),\n mul(0x20, iszero(lengthmod))\n )\n let end := add(mc, _length)\n\n for {\n // The multiplication in the next line has the same exact purpose\n // as the one above.\n let cc := add(\n add(\n add(_bytes, lengthmod),\n mul(0x20, iszero(lengthmod))\n ),\n _start\n )\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n mstore(tempBytes, _length)\n\n //update free-memory pointer\n //allocating the array padded to 32 bytes like the compiler does now\n mstore(0x40, and(add(mc, 31), not(31)))\n }\n //if we want a zero-length slice let's just return a zero-length array\n default {\n tempBytes := mload(0x40)\n //zero out the 32 bytes slice we are about to return\n //we need to do it because Solidity does not garbage collect\n mstore(tempBytes, 0)\n\n mstore(0x40, add(tempBytes, 0x20))\n }\n }\n\n return tempBytes;\n }\n}\n" - }, - "src/bridges/hyphen/interfaces/hyphen.sol": { - "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity >=0.8.0;\n\n/**\n * @title HyphenLiquidityPoolManager\n * @notice interface with functions to bridge ERC20 and Native via Hyphen-Bridge\n * @author Socket dot tech.\n */\ninterface HyphenLiquidityPoolManager {\n /**\n * @dev Function used to deposit tokens into pool to initiate a cross chain token transfer.\n * @param toChainId Chain id where funds needs to be transfered\n * @param tokenAddress ERC20 Token address that needs to be transfered\n * @param receiver Address on toChainId where tokens needs to be transfered\n * @param amount Amount of token being transfered\n */\n function depositErc20(\n uint256 toChainId,\n address tokenAddress,\n address receiver,\n uint256 amount,\n string calldata tag\n ) external;\n\n /**\n * @dev Function used to deposit native token into pool to initiate a cross chain token transfer.\n * @param receiver Address on toChainId where tokens needs to be transfered\n * @param toChainId Chain id where funds needs to be transfered\n */\n function depositNative(\n address receiver,\n uint256 toChainId,\n string calldata tag\n ) external payable;\n}\n" - }, - "src/swap/SwapImplBase.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {ISocketGateway} from \"../interfaces/ISocketGateway.sol\";\nimport {OnlySocketGatewayOwner, OnlySocketDeployer} from \"../errors/SocketErrors.sol\";\n\n/**\n * @title Abstract Implementation Contract.\n * @notice All Swap Implementation will follow this interface.\n * @author Socket dot tech.\n */\nabstract contract SwapImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n /// @notice Address used to identify if it is a native token transfer or not\n address public immutable NATIVE_TOKEN_ADDRESS =\n address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);\n\n /// @notice immutable variable to store the socketGateway address\n address public immutable socketGateway;\n\n /// @notice immutable variable to store the socketGateway address\n address public immutable socketDeployFactory;\n\n /// @notice FunctionSelector used to delegatecall to the performAction function of swap-router-implementation\n bytes4 public immutable SWAP_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\"performAction(address,address,uint256,address,bytes)\")\n );\n\n /// @notice FunctionSelector used to delegatecall to the performActionWithIn function of swap-router-implementation\n bytes4 public immutable SWAP_WITHIN_FUNCTION_SELECTOR =\n bytes4(keccak256(\"performActionWithIn(address,address,uint256,bytes)\"));\n\n /****************************************\n * EVENTS *\n ****************************************/\n\n event SocketSwapTokens(\n address fromToken,\n address toToken,\n uint256 buyAmount,\n uint256 sellAmount,\n bytes32 routeName,\n address receiver\n );\n\n /**\n * @notice Construct the base for all SwapImplementations.\n * @param _socketGateway Socketgateway address, an immutable variable to set.\n */\n constructor(address _socketGateway, address _socketDeployFactory) {\n socketGateway = _socketGateway;\n socketDeployFactory = _socketDeployFactory;\n }\n\n /****************************************\n * MODIFIERS *\n ****************************************/\n\n /// @notice Implementing contract needs to make use of the modifier where restricted access is to be used\n modifier isSocketGatewayOwner() {\n if (msg.sender != ISocketGateway(socketGateway).owner()) {\n revert OnlySocketGatewayOwner();\n }\n _;\n }\n\n /// @notice Implementing contract needs to make use of the modifier where restricted access is to be used\n modifier isSocketDeployFactory() {\n if (msg.sender != socketDeployFactory) {\n revert OnlySocketDeployer();\n }\n _;\n }\n\n /****************************************\n * RESTRICTED FUNCTIONS *\n ****************************************/\n\n /**\n * @notice function to rescue the ERC20 tokens in the Swap-Implementation contract\n * @notice this is a function restricted to Owner of SocketGateway only\n * @param token address of ERC20 token being rescued\n * @param userAddress receipient address to which ERC20 tokens will be rescued to\n * @param amount amount of ERC20 tokens being rescued\n */\n function rescueFunds(\n address token,\n address userAddress,\n uint256 amount\n ) external isSocketGatewayOwner {\n ERC20(token).safeTransfer(userAddress, amount);\n }\n\n /**\n * @notice function to rescue the native-balance in the Swap-Implementation contract\n * @notice this is a function restricted to Owner of SocketGateway only\n * @param userAddress receipient address to which native-balance will be rescued to\n * @param amount amount of native balance tokens being rescued\n */\n function rescueEther(\n address payable userAddress,\n uint256 amount\n ) external isSocketGatewayOwner {\n userAddress.transfer(amount);\n }\n\n function killme() external isSocketDeployFactory {\n selfdestruct(payable(msg.sender));\n }\n\n /******************************\n * VIRTUAL FUNCTIONS *\n *****************************/\n\n /**\n * @notice function to swap tokens on the chain\n * All swap implementation contracts must implement this function\n * @param fromToken token to be swapped\n * @param toToken token to which fromToken has to be swapped\n * @param amount amount of fromToken being swapped\n * @param receiverAddress recipient address of toToken\n * @param data encoded value of properties in the swapData Struct\n */\n function performAction(\n address fromToken,\n address toToken,\n uint256 amount,\n address receiverAddress,\n bytes memory data\n ) external payable virtual returns (uint256);\n\n /**\n * @notice function to swapWith - swaps tokens on the chain to socketGateway as recipient\n * All swap implementation contracts must implement this function\n * @param fromToken token to be swapped\n * @param toToken token to which fromToken has to be swapped\n * @param amount amount of fromToken being swapped\n * @param swapExtraData encoded value of properties in the swapData Struct\n */\n function performActionWithIn(\n address fromToken,\n address toToken,\n uint256 amount,\n bytes memory swapExtraData\n ) external payable virtual returns (uint256, address);\n}\n" - }, - "src/swap/zerox/ZeroXSwapImpl.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport \"../SwapImplBase.sol\";\nimport {Address0Provided, SwapFailed} from \"../../errors/SocketErrors.sol\";\nimport {ZEROX} from \"../../static/RouteIdentifiers.sol\";\n\n/**\n * @title ZeroX-Swap-Route Implementation\n * @notice Route implementation with functions to swap tokens via ZeroX-Swap\n * Called via SocketGateway if the routeId in the request maps to the routeId of ZeroX-Swap-Implementation\n * @author Socket dot tech.\n */\ncontract ZeroXSwapImpl is SwapImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable ZeroXIdentifier = ZEROX;\n\n /// @notice unique name to identify the router, used to emit event upon successful bridging\n bytes32 public immutable NAME = keccak256(\"Zerox-Router\");\n\n /// @notice address of ZeroX-Exchange-Proxy to swap the tokens on Chain\n address payable public immutable zeroXExchangeProxy;\n\n /// @notice socketGatewayAddress to be initialised via storage variable SwapImplBase\n /// @notice ZeroXExchangeProxy contract is payable to allow ethereum swaps\n /// @dev ensure _zeroXExchangeProxy are set properly for the chainId in which the contract is being deployed\n constructor(\n address _zeroXExchangeProxy,\n address _socketGateway,\n address _socketDeployFactory\n ) SwapImplBase(_socketGateway, _socketDeployFactory) {\n zeroXExchangeProxy = payable(_zeroXExchangeProxy);\n }\n\n receive() external payable {}\n\n fallback() external payable {}\n\n /**\n * @notice function to swap tokens on the chain and transfer to receiver address\n * @dev This is called only when there is a request for a swap.\n * @param fromToken token to be swapped\n * @param toToken token to which fromToken is to be swapped\n * @param amount amount to be swapped\n * @param receiverAddress address of toToken recipient\n * @param swapExtraData data required for zeroX Exchange to get the swap done\n */\n function performAction(\n address fromToken,\n address toToken,\n uint256 amount,\n address receiverAddress,\n bytes calldata swapExtraData\n ) external payable override returns (uint256) {\n if (fromToken == address(0)) {\n revert Address0Provided();\n }\n\n bytes memory swapCallData = abi.decode(swapExtraData, (bytes));\n\n uint256 _initialBalanceTokenOut;\n uint256 _finalBalanceTokenOut;\n\n ERC20 erc20ToToken = ERC20(toToken);\n if (toToken != NATIVE_TOKEN_ADDRESS) {\n _initialBalanceTokenOut = erc20ToToken.balanceOf(address(this));\n } else {\n _initialBalanceTokenOut = address(this).balance;\n }\n\n if (fromToken != NATIVE_TOKEN_ADDRESS) {\n ERC20 token = ERC20(fromToken);\n token.safeTransferFrom(msg.sender, address(this), amount);\n token.safeApprove(zeroXExchangeProxy, amount);\n\n // solhint-disable-next-line\n (bool success, ) = zeroXExchangeProxy.call(swapCallData);\n\n if (!success) {\n revert SwapFailed();\n }\n\n token.safeApprove(zeroXExchangeProxy, 0);\n } else {\n (bool success, ) = zeroXExchangeProxy.call{value: amount}(\n swapCallData\n );\n if (!success) {\n revert SwapFailed();\n }\n }\n\n if (toToken != NATIVE_TOKEN_ADDRESS) {\n _finalBalanceTokenOut = erc20ToToken.balanceOf(address(this));\n } else {\n _finalBalanceTokenOut = address(this).balance;\n }\n\n uint256 returnAmount = _finalBalanceTokenOut - _initialBalanceTokenOut;\n\n if (toToken == NATIVE_TOKEN_ADDRESS) {\n payable(receiverAddress).transfer(returnAmount);\n } else {\n erc20ToToken.transfer(receiverAddress, returnAmount);\n }\n\n emit SocketSwapTokens(\n fromToken,\n toToken,\n returnAmount,\n amount,\n ZeroXIdentifier,\n receiverAddress\n );\n\n return returnAmount;\n }\n\n /**\n * @notice function to swapWithIn SocketGateway - swaps tokens on the chain to socketGateway as recipient\n * @param fromToken token to be swapped\n * @param toToken token to which fromToken has to be swapped\n * @param amount amount of fromToken being swapped\n * @param swapExtraData encoded value of properties in the swapData Struct\n * @return swapped amount (in toToken Address)\n */\n function performActionWithIn(\n address fromToken,\n address toToken,\n uint256 amount,\n bytes calldata swapExtraData\n ) external payable override returns (uint256, address) {\n if (fromToken == address(0)) {\n revert Address0Provided();\n }\n\n bytes memory swapCallData = abi.decode(swapExtraData, (bytes));\n\n uint256 _initialBalanceTokenOut;\n uint256 _finalBalanceTokenOut;\n\n ERC20 erc20ToToken = ERC20(toToken);\n if (toToken != NATIVE_TOKEN_ADDRESS) {\n _initialBalanceTokenOut = erc20ToToken.balanceOf(address(this));\n } else {\n _initialBalanceTokenOut = address(this).balance;\n }\n\n if (fromToken != NATIVE_TOKEN_ADDRESS) {\n ERC20 token = ERC20(fromToken);\n token.safeTransferFrom(msg.sender, address(this), amount);\n token.safeApprove(zeroXExchangeProxy, amount);\n\n // solhint-disable-next-line\n (bool success, ) = zeroXExchangeProxy.call(swapCallData);\n\n if (!success) {\n revert SwapFailed();\n }\n\n token.safeApprove(zeroXExchangeProxy, 0);\n } else {\n (bool success, ) = zeroXExchangeProxy.call{value: amount}(\n swapCallData\n );\n if (!success) {\n revert SwapFailed();\n }\n }\n\n if (toToken != NATIVE_TOKEN_ADDRESS) {\n _finalBalanceTokenOut = erc20ToToken.balanceOf(address(this));\n } else {\n _finalBalanceTokenOut = address(this).balance;\n }\n\n uint256 returnAmount = _finalBalanceTokenOut - _initialBalanceTokenOut;\n\n emit SocketSwapTokens(\n fromToken,\n toToken,\n returnAmount,\n amount,\n ZeroXIdentifier,\n socketGateway\n );\n\n return (returnAmount, toToken);\n }\n}\n" - }, - "src/bridges/cbridge/interfaces/cbridge.sol": { - "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity >=0.8.0;\n\ninterface ICBridge {\n function send(\n address _receiver,\n address _token,\n uint256 _amount,\n uint64 _dstChinId,\n uint64 _nonce,\n uint32 _maxSlippage\n ) external;\n\n function sendNative(\n address _receiver,\n uint256 _amount,\n uint64 _dstChinId,\n uint64 _nonce,\n uint32 _maxSlippage\n ) external payable;\n\n function withdraws(bytes32 withdrawId) external view returns (bool);\n\n function withdraw(\n bytes calldata _wdmsg,\n bytes[] calldata _sigs,\n address[] calldata _signers,\n uint256[] calldata _powers\n ) external;\n}\n" - }, - "src/bridges/stargate/l2/Stargate.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport \"../interfaces/stargate.sol\";\nimport \"../../../errors/SocketErrors.sol\";\nimport {BridgeImplBase} from \"../../BridgeImplBase.sol\";\nimport {STARGATE} from \"../../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Stargate-L2-Route Implementation\n * @notice Route implementation with functions to bridge ERC20 and Native via Stargate-L2-Bridge\n * Called via SocketGateway if the routeId in the request maps to the routeId of Stargate-L2-Implementation\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ncontract StargateImplL2 is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable StargateIdentifier = STARGATE;\n\n /// @notice Function-selector for ERC20-token bridging on Stargate-L2-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4\n public immutable STARGATE_L2_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(address,address,address,uint256,uint256,uint256,(uint256,uint256,uint256,uint256,bytes32,bytes,uint16))\"\n )\n );\n\n bytes4 public immutable STARGATE_L1_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(address,address,uint16,uint256,uint256,uint256,uint256,uint256,uint256,bytes32,bytes))\"\n )\n );\n\n /// @notice Function-selector for Native bridging on Stargate-L2-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge Native tokens\n bytes4\n public immutable STARGATE_L2_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeNativeTo(address,address,uint16,uint256,uint256,uint256,bytes32)\"\n )\n );\n\n /// @notice Stargate Router to bridge ERC20 tokens\n IBridgeStargate public immutable router;\n\n /// @notice Stargate Router to bridge native tokens\n IBridgeStargate public immutable routerETH;\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n /// @dev ensure router, routerEth are set properly for the chainId in which the contract is being deployed\n constructor(\n address _router,\n address _routerEth,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n router = IBridgeStargate(_router);\n routerETH = IBridgeStargate(_routerEth);\n }\n\n /// @notice Struct to be used as a input parameter for Bridging tokens via Stargate-L2-route\n /// @dev while building transactionData,values should be set in this sequence of properties in this struct\n struct StargateBridgeExtraData {\n uint256 srcPoolId;\n uint256 dstPoolId;\n uint256 destinationGasLimit;\n uint256 minReceivedAmt;\n bytes32 metadata;\n bytes destinationPayload;\n uint16 stargateDstChainId; // stargate defines chain id in its way\n }\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct StargateBridgeDataNoToken {\n address receiverAddress;\n address senderAddress;\n uint16 stargateDstChainId; // stargate defines chain id in its way\n uint256 value;\n // a unique identifier that is uses to dedup transfers\n // this value is the a timestamp sent from frontend, but in theory can be any unique number\n uint256 srcPoolId;\n uint256 dstPoolId;\n uint256 minReceivedAmt; // defines the slippage, the min qty you would accept on the destination\n uint256 optionalValue;\n uint256 destinationGasLimit;\n bytes32 metadata;\n bytes destinationPayload;\n }\n\n struct StargateBridgeData {\n address token;\n address receiverAddress;\n address senderAddress;\n uint16 stargateDstChainId; // stargate defines chain id in its way\n uint256 value;\n // a unique identifier that is uses to dedup transfers\n // this value is the a timestamp sent from frontend, but in theory can be any unique number\n uint256 srcPoolId;\n uint256 dstPoolId;\n uint256 minReceivedAmt; // defines the slippage, the min qty you would accept on the destination\n uint256 optionalValue;\n uint256 destinationGasLimit;\n bytes32 metadata;\n bytes destinationPayload;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in Stargate-BridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for Stargate-L1-Bridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n StargateBridgeData memory stargateBridgeData = abi.decode(\n bridgeData,\n (StargateBridgeData)\n );\n\n if (stargateBridgeData.token == NATIVE_TOKEN_ADDRESS) {\n // perform bridging\n routerETH.swapETH{value: amount + stargateBridgeData.optionalValue}(\n stargateBridgeData.stargateDstChainId,\n payable(stargateBridgeData.senderAddress),\n abi.encodePacked(stargateBridgeData.receiverAddress),\n amount,\n stargateBridgeData.minReceivedAmt\n );\n } else {\n ERC20(stargateBridgeData.token).safeApprove(\n address(router),\n amount\n );\n {\n router.swap{value: stargateBridgeData.value}(\n stargateBridgeData.stargateDstChainId,\n stargateBridgeData.srcPoolId,\n stargateBridgeData.dstPoolId,\n payable(stargateBridgeData.senderAddress), // default to refund to main contract\n amount,\n stargateBridgeData.minReceivedAmt,\n IBridgeStargate.lzTxObj(\n stargateBridgeData.destinationGasLimit,\n 0, // zero amount since this is a ERC20 bridging\n \"0x\" //empty data since this is for only ERC20\n ),\n abi.encodePacked(stargateBridgeData.receiverAddress),\n stargateBridgeData.destinationPayload\n );\n }\n }\n\n emit SocketBridge(\n amount,\n stargateBridgeData.token,\n stargateBridgeData.stargateDstChainId,\n StargateIdentifier,\n msg.sender,\n stargateBridgeData.receiverAddress,\n stargateBridgeData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swapping.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in Stargate-BridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param stargateBridgeData encoded data for StargateBridgeData\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n StargateBridgeDataNoToken calldata stargateBridgeData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n\n if (token == NATIVE_TOKEN_ADDRESS) {\n routerETH.swapETH{\n value: bridgeAmount + stargateBridgeData.optionalValue\n }(\n stargateBridgeData.stargateDstChainId,\n payable(stargateBridgeData.senderAddress),\n abi.encodePacked(stargateBridgeData.receiverAddress),\n bridgeAmount,\n stargateBridgeData.minReceivedAmt\n );\n } else {\n ERC20(token).safeApprove(address(router), bridgeAmount);\n {\n router.swap{value: stargateBridgeData.value}(\n stargateBridgeData.stargateDstChainId,\n stargateBridgeData.srcPoolId,\n stargateBridgeData.dstPoolId,\n payable(stargateBridgeData.senderAddress), // default to refund to main contract\n bridgeAmount,\n stargateBridgeData.minReceivedAmt,\n IBridgeStargate.lzTxObj(\n stargateBridgeData.destinationGasLimit,\n 0,\n \"0x\"\n ),\n abi.encodePacked(stargateBridgeData.receiverAddress),\n stargateBridgeData.destinationPayload\n );\n }\n }\n\n emit SocketBridge(\n bridgeAmount,\n token,\n stargateBridgeData.stargateDstChainId,\n StargateIdentifier,\n msg.sender,\n stargateBridgeData.receiverAddress,\n stargateBridgeData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via Stargate-L1-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param token address of token being bridged\n * @param senderAddress address of sender\n * @param receiverAddress address of recipient\n * @param amount amount of token being bridge\n * @param value value\n * @param optionalValue optionalValue\n * @param stargateBridgeExtraData stargate bridge extradata\n */\n function bridgeERC20To(\n address token,\n address senderAddress,\n address receiverAddress,\n uint256 amount,\n uint256 value,\n uint256 optionalValue,\n StargateBridgeExtraData calldata stargateBridgeExtraData\n ) external payable {\n // token address might not be indication thats why passed through extraData\n if (token == NATIVE_TOKEN_ADDRESS) {\n // perform bridging\n routerETH.swapETH{value: amount + optionalValue}(\n stargateBridgeExtraData.stargateDstChainId,\n payable(senderAddress),\n abi.encodePacked(receiverAddress),\n amount,\n stargateBridgeExtraData.minReceivedAmt\n );\n } else {\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n tokenInstance.safeApprove(address(router), amount);\n {\n router.swap{value: value}(\n stargateBridgeExtraData.stargateDstChainId,\n stargateBridgeExtraData.srcPoolId,\n stargateBridgeExtraData.dstPoolId,\n payable(senderAddress), // default to refund to main contract\n amount,\n stargateBridgeExtraData.minReceivedAmt,\n IBridgeStargate.lzTxObj(\n stargateBridgeExtraData.destinationGasLimit,\n 0, // zero amount since this is a ERC20 bridging\n \"0x\" //empty data since this is for only ERC20\n ),\n abi.encodePacked(receiverAddress),\n stargateBridgeExtraData.destinationPayload\n );\n }\n }\n\n emit SocketBridge(\n amount,\n token,\n stargateBridgeExtraData.stargateDstChainId,\n StargateIdentifier,\n msg.sender,\n receiverAddress,\n stargateBridgeExtraData.metadata\n );\n }\n\n function bridgeNativeTo(\n address receiverAddress,\n address senderAddress,\n uint16 stargateDstChainId,\n uint256 amount,\n uint256 minReceivedAmt,\n uint256 optionalValue,\n bytes32 metadata\n ) external payable {\n // perform bridging\n routerETH.swapETH{value: amount + optionalValue}(\n stargateDstChainId,\n payable(senderAddress),\n abi.encodePacked(receiverAddress),\n amount,\n minReceivedAmt\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n stargateDstChainId,\n StargateIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n" - } - }, - "settings": { - "optimizer": { - "enabled": true, - "runs": 1000000 - }, - "outputSelection": { - "*": { - "*": [ - "evm.bytecode", - "evm.deployedBytecode", - "devdoc", - "userdoc", - "metadata", - "abi" - ] - } - }, - "metadata": { - "useLiteralContent": true - }, - "libraries": {} - } - }, - "ABI": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_disabledRoute\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"ArrayLengthMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectBridgeRatios\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyNominee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"controllerId\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"controllerAddress\",\"type\":\"address\"}],\"name\":\"ControllerAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"controllerId\",\"type\":\"uint32\"}],\"name\":\"ControllerDisabled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"routeId\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"route\",\"type\":\"address\"}],\"name\":\"NewRouteAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"claimer\",\"type\":\"address\"}],\"name\":\"OwnerClaimed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"nominee\",\"type\":\"address\"}],\"name\":\"OwnerNominated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"routeId\",\"type\":\"uint32\"}],\"name\":\"RouteDisabled\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"BRIDGE_AFTER_SWAP_SELECTOR\",\"outputs\":[{\"internalType\":\"bytes4\",\"name\":\"\",\"type\":\"bytes4\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"CENT_PERCENT\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"controllerAddress\",\"type\":\"address\"}],\"name\":\"addController\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"routeAddress\",\"type\":\"address\"}],\"name\":\"addRoute\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"routeId\",\"type\":\"uint32\"}],\"name\":\"addressAt\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"claimOwner\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"controllerCount\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"name\":\"controllers\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"controllerId\",\"type\":\"uint32\"}],\"name\":\"disableController\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"routeId\",\"type\":\"uint32\"}],\"name\":\"disableRoute\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"disabledRouteAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"controllerId\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"struct ISocketGateway.SocketControllerRequest\",\"name\":\"socketControllerRequest\",\"type\":\"tuple\"}],\"name\":\"executeController\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"controllerId\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"struct ISocketGateway.SocketControllerRequest[]\",\"name\":\"controllerRequests\",\"type\":\"tuple[]\"}],\"name\":\"executeControllers\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"routeId\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"routeData\",\"type\":\"bytes\"}],\"name\":\"executeRoute\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32[]\",\"name\":\"routeIds\",\"type\":\"uint32[]\"},{\"internalType\":\"bytes[]\",\"name\":\"dataItems\",\"type\":\"bytes[]\"}],\"name\":\"executeRoutes\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"controllerId\",\"type\":\"uint32\"}],\"name\":\"getController\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"routeId\",\"type\":\"uint32\"}],\"name\":\"getRoute\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"nominee_\",\"type\":\"address\"}],\"name\":\"nominateOwner\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"nominee\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address payable\",\"name\":\"userAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"rescueEther\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"userAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"rescueFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"name\":\"routes\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"routesCount\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"routeAddresses\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"tokenAddresses\",\"type\":\"address[]\"},{\"internalType\":\"bool\",\"name\":\"isMax\",\"type\":\"bool\"}],\"name\":\"setApprovalForRouters\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"swapRouteId\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"swapImplData\",\"type\":\"bytes\"},{\"internalType\":\"uint32[]\",\"name\":\"bridgeRouteIds\",\"type\":\"uint32[]\"},{\"internalType\":\"bytes[]\",\"name\":\"bridgeImplDataItems\",\"type\":\"bytes[]\"},{\"internalType\":\"uint256[]\",\"name\":\"bridgeRatios\",\"type\":\"uint256[]\"},{\"internalType\":\"bytes[]\",\"name\":\"eventDataItems\",\"type\":\"bytes[]\"}],\"internalType\":\"struct ISocketRequest.SwapMultiBridgeRequest\",\"name\":\"swapMultiBridgeRequest\",\"type\":\"tuple\"}],\"name\":\"swapAndMultiBridge\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]", - "ContractName": "SocketGateway", - "CompilerVersion": "v0.8.7+commit.e28d00a7", - "OptimizationUsed": 1, - "Runs": 1000000, - "ConstructorArguments": "0x000000000000000000000000e8dd38e673a93ccfc2e3d7053efccb5c93f493650000000000000000000000000f34a522ff82151c90679b73211955068fd854f1", - "EVMVersion": "Default", - "Library": "", - "LicenseType": "", - "Proxy": 1, - "Implementation": "0xa3c4e32af0da5efaddb20cc9fb26159f55c8c42f", - "SwarmSource": "" - } -] \ No newline at end of file +[{"SourceCode":{"language":"Solidity","sources":{"src/bridges/hop/interfaces/amm.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\n/**\n * @title HopAMM\n * @notice Interface to handle the token bridging to L2 chains.\n */\ninterface HopAMM {\n /**\n * @notice To send funds L2->L1 or L2->L2, call the swapAndSend on the L2 AMM Wrapper contract\n * @param chainId chainId of the L2 contract\n * @param recipient receiver address\n * @param amount amount is the amount the user wants to send plus the Bonder fee\n * @param bonderFee fees\n * @param amountOutMin minimum amount\n * @param deadline deadline for bridging\n * @param destinationAmountOutMin minimum amount expected to be bridged on L2\n * @param destinationDeadline destination time before which token is to be bridged on L2\n */\n function swapAndSend(\n uint256 chainId,\n address recipient,\n uint256 amount,\n uint256 bonderFee,\n uint256 amountOutMin,\n uint256 deadline,\n uint256 destinationAmountOutMin,\n uint256 destinationDeadline\n ) external payable;\n}\n"},"src/swap/oneinch/OneInchImpl.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport \"../SwapImplBase.sol\";\nimport {SwapFailed} from \"../../errors/SocketErrors.sol\";\nimport {ONEINCH} from \"../../static/RouteIdentifiers.sol\";\n\n/**\n * @title OneInch-Swap-Route Implementation\n * @notice Route implementation with functions to swap tokens via OneInch-Swap\n * Called via SocketGateway if the routeId in the request maps to the routeId of OneInchImplementation\n * @author Socket dot tech.\n */\ncontract OneInchImpl is SwapImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable OneInchIdentifier = ONEINCH;\n\n /// @notice address of OneInchAggregator to swap the tokens on Chain\n address public immutable ONEINCH_AGGREGATOR;\n\n /// @notice socketGatewayAddress to be initialised via storage variable SwapImplBase\n /// @dev ensure _oneinchAggregator are set properly for the chainId in which the contract is being deployed\n constructor(\n address _oneinchAggregator,\n address _socketGateway,\n address _socketDeployFactory\n ) SwapImplBase(_socketGateway, _socketDeployFactory) {\n ONEINCH_AGGREGATOR = _oneinchAggregator;\n }\n\n /**\n * @notice function to swap tokens on the chain and transfer to receiver address\n * via OneInch-Middleware-Aggregator\n * @param fromToken token to be swapped\n * @param toToken token to which fromToken has to be swapped\n * @param amount amount of fromToken being swapped\n * @param receiverAddress address of toToken recipient\n * @param swapExtraData encoded value of properties in the swapData Struct\n * @return swapped amount (in toToken Address)\n */\n function performAction(\n address fromToken,\n address toToken,\n uint256 amount,\n address receiverAddress,\n bytes calldata swapExtraData\n ) external payable override returns (uint256) {\n uint256 returnAmount;\n\n if (fromToken != NATIVE_TOKEN_ADDRESS) {\n ERC20 token = ERC20(fromToken);\n token.safeTransferFrom(msg.sender, socketGateway, amount);\n token.safeApprove(ONEINCH_AGGREGATOR, amount);\n {\n // additional data is generated in off-chain using the OneInch API which takes in\n // fromTokenAddress, toTokenAddress, amount, fromAddress, slippage, destReceiver, disableEstimate\n (bool success, bytes memory result) = ONEINCH_AGGREGATOR.call(\n swapExtraData\n );\n token.safeApprove(ONEINCH_AGGREGATOR, 0);\n\n if (!success) {\n revert SwapFailed();\n }\n\n returnAmount = abi.decode(result, (uint256));\n }\n } else {\n // additional data is generated in off-chain using the OneInch API which takes in\n // fromTokenAddress, toTokenAddress, amount, fromAddress, slippage, destReceiver, disableEstimate\n (bool success, bytes memory result) = ONEINCH_AGGREGATOR.call{\n value: amount\n }(swapExtraData);\n if (!success) {\n revert SwapFailed();\n }\n returnAmount = abi.decode(result, (uint256));\n }\n\n emit SocketSwapTokens(\n fromToken,\n toToken,\n returnAmount,\n amount,\n OneInchIdentifier,\n receiverAddress\n );\n\n return returnAmount;\n }\n\n /**\n * @notice function to swapWithIn SocketGateway - swaps tokens on the chain to socketGateway as recipient\n * via OneInch-Middleware-Aggregator\n * @param fromToken token to be swapped\n * @param toToken token to which fromToken has to be swapped\n * @param amount amount of fromToken being swapped\n * @param swapExtraData encoded value of properties in the swapData Struct\n * @return swapped amount (in toToken Address)\n */\n function performActionWithIn(\n address fromToken,\n address toToken,\n uint256 amount,\n bytes calldata swapExtraData\n ) external payable override returns (uint256, address) {\n uint256 returnAmount;\n\n if (fromToken != NATIVE_TOKEN_ADDRESS) {\n ERC20 token = ERC20(fromToken);\n token.safeTransferFrom(msg.sender, socketGateway, amount);\n token.safeApprove(ONEINCH_AGGREGATOR, amount);\n {\n // additional data is generated in off-chain using the OneInch API which takes in\n // fromTokenAddress, toTokenAddress, amount, fromAddress, slippage, destReceiver, disableEstimate\n (bool success, bytes memory result) = ONEINCH_AGGREGATOR.call(\n swapExtraData\n );\n token.safeApprove(ONEINCH_AGGREGATOR, 0);\n\n if (!success) {\n revert SwapFailed();\n }\n\n returnAmount = abi.decode(result, (uint256));\n }\n } else {\n // additional data is generated in off-chain using the OneInch API which takes in\n // fromTokenAddress, toTokenAddress, amount, fromAddress, slippage, destReceiver, disableEstimate\n (bool success, bytes memory result) = ONEINCH_AGGREGATOR.call{\n value: amount\n }(swapExtraData);\n if (!success) {\n revert SwapFailed();\n }\n returnAmount = abi.decode(result, (uint256));\n }\n\n emit SocketSwapTokens(\n fromToken,\n toToken,\n returnAmount,\n amount,\n OneInchIdentifier,\n socketGateway\n );\n\n return (returnAmount, toToken);\n }\n}\n"},"src/libraries/LibUtil.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport \"./LibBytes.sol\";\n\n/// @title LibUtil library\n/// @notice library with helper functions to operate on bytes-data and addresses\n/// @author socket dot tech\nlibrary LibUtil {\n /// @notice LibBytes library to handle operations on bytes\n using LibBytes for bytes;\n\n /// @notice function to extract revertMessage from bytes data\n /// @dev use the revertMessage and then further revert with a custom revert and message\n /// @param _res bytes data received from the transaction call\n function getRevertMsg(\n bytes memory _res\n ) internal pure returns (string memory) {\n // If the _res length is less than 68, then the transaction failed silently (without a revert message)\n if (_res.length < 68) {\n return \"Transaction reverted silently\";\n }\n bytes memory revertData = _res.slice(4, _res.length - 4); // Remove the selector which is the first 4 bytes\n return abi.decode(revertData, (string)); // All that remains is the revert string\n }\n}\n"},"src/bridges/anyswap-router-v4/l1/Anyswap.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {BridgeImplBase} from \"../../BridgeImplBase.sol\";\nimport {ANYSWAP} from \"../../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Anyswap-V4-Route L1 Implementation\n * @notice Route implementation with functions to bridge ERC20 via Anyswap-Bridge\n * Called via SocketGateway if the routeId in the request maps to the routeId of AnyswapImplementation\n * This is the L1 implementation, so this is used when transferring from l1 to supported l1s or L1.\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\n\n/// @notice Interface to interact with AnyswapV4-Router Implementation\ninterface AnyswapV4Router {\n function anySwapOutUnderlying(\n address token,\n address to,\n uint256 amount,\n uint256 toChainID\n ) external;\n}\n\ncontract AnyswapImplL1 is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable AnyswapIdentifier = ANYSWAP;\n\n /// @notice Function-selector for ERC20-token bridging on Anyswap-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4 public immutable ANYSWAP_L1_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(uint256,uint256,bytes32,address,address,address)\"\n )\n );\n\n bytes4 public immutable ANYSWAP_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(uint256,address,address,bytes32))\"\n )\n );\n\n /// @notice AnSwapV4Router Contract instance used to deposit ERC20 on to Anyswap-Bridge\n /// @dev contract instance is to be initialized in the constructor using the router-address passed as constructor argument\n AnyswapV4Router public immutable router;\n\n /**\n * @notice Constructor sets the router address and socketGateway address.\n * @dev anyswap 4 router is immutable. so no setter function required.\n */\n constructor(\n address _router,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n router = AnyswapV4Router(_router);\n }\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct AnyswapBridgeDataNoToken {\n /// @notice destination ChainId\n uint256 toChainId;\n /// @notice address of receiver of bridged tokens\n address receiverAddress;\n /// @notice address of wrapperToken, WrappedVersion of the token being bridged\n address wrapperTokenAddress;\n /// @notice socket offchain created hash\n bytes32 metadata;\n }\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct AnyswapBridgeData {\n /// @notice destination ChainId\n uint256 toChainId;\n /// @notice address of receiver of bridged tokens\n address receiverAddress;\n /// @notice address of wrapperToken, WrappedVersion of the token being bridged\n address wrapperTokenAddress;\n /// @notice address of token being bridged\n address token;\n /// @notice socket offchain created hash\n bytes32 metadata;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in AnyswapBridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for AnyswapBridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n AnyswapBridgeData memory anyswapBridgeData = abi.decode(\n bridgeData,\n (AnyswapBridgeData)\n );\n ERC20(anyswapBridgeData.token).safeApprove(address(router), amount);\n router.anySwapOutUnderlying(\n anyswapBridgeData.wrapperTokenAddress,\n anyswapBridgeData.receiverAddress,\n amount,\n anyswapBridgeData.toChainId\n );\n\n emit SocketBridge(\n amount,\n anyswapBridgeData.token,\n anyswapBridgeData.toChainId,\n AnyswapIdentifier,\n msg.sender,\n anyswapBridgeData.receiverAddress,\n anyswapBridgeData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in AnyswapBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param anyswapBridgeData encoded data for AnyswapBridge\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n AnyswapBridgeDataNoToken calldata anyswapBridgeData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n\n ERC20(token).safeApprove(address(router), bridgeAmount);\n router.anySwapOutUnderlying(\n anyswapBridgeData.wrapperTokenAddress,\n anyswapBridgeData.receiverAddress,\n bridgeAmount,\n anyswapBridgeData.toChainId\n );\n\n emit SocketBridge(\n bridgeAmount,\n token,\n anyswapBridgeData.toChainId,\n AnyswapIdentifier,\n msg.sender,\n anyswapBridgeData.receiverAddress,\n anyswapBridgeData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via Anyswap-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount being bridged\n * @param toChainId destination ChainId\n * @param receiverAddress address of receiver of bridged tokens\n * @param token address of token being bridged\n * @param wrapperTokenAddress address of wrapperToken, WrappedVersion of the token being bridged\n */\n function bridgeERC20To(\n uint256 amount,\n uint256 toChainId,\n bytes32 metadata,\n address receiverAddress,\n address token,\n address wrapperTokenAddress\n ) external payable {\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n tokenInstance.safeApprove(address(router), amount);\n router.anySwapOutUnderlying(\n wrapperTokenAddress,\n receiverAddress,\n amount,\n toChainId\n );\n\n emit SocketBridge(\n amount,\n token,\n toChainId,\n AnyswapIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n"},"src/bridges/cbridge/CelerStorageWrapper.sol":{"content":"// SPDX-License-Identifier: Apache-2.0\npragma solidity >=0.8.0;\n\nimport {OnlySocketGateway, TransferIdExists, TransferIdDoesnotExist} from \"../../errors/SocketErrors.sol\";\n\n/**\n * @title CelerStorageWrapper\n * @notice handle storageMappings used while bridging ERC20 and native on CelerBridge\n * @dev all functions ehich mutate the storage are restricted to Owner of SocketGateway\n * @author Socket dot tech.\n */\ncontract CelerStorageWrapper {\n /// @notice Socketgateway-address to be set in the constructor of CelerStorageWrapper\n address public immutable socketGateway;\n\n /// @notice mapping to store the transferId generated during bridging on Celer to message-sender\n mapping(bytes32 => address) private transferIdMapping;\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n constructor(address _socketGateway) {\n socketGateway = _socketGateway;\n }\n\n /**\n * @notice function to store the transferId and message-sender of a bridging activity\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in CelerBridgeData struct\n * @param transferId transferId generated during the bridging of ERC20 or native on CelerBridge\n * @param transferIdAddress message sender who is making the bridging on CelerBridge\n */\n function setAddressForTransferId(\n bytes32 transferId,\n address transferIdAddress\n ) external {\n if (msg.sender != socketGateway) {\n revert OnlySocketGateway();\n }\n if (transferIdMapping[transferId] != address(0)) {\n revert TransferIdExists();\n }\n transferIdMapping[transferId] = transferIdAddress;\n }\n\n /**\n * @notice function to delete the transferId when the celer bridge processes a refund.\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in CelerBridgeData struct\n * @param transferId transferId generated during the bridging of ERC20 or native on CelerBridge\n */\n function deleteTransferId(bytes32 transferId) external {\n if (msg.sender != socketGateway) {\n revert OnlySocketGateway();\n }\n if (transferIdMapping[transferId] == address(0)) {\n revert TransferIdDoesnotExist();\n }\n\n delete transferIdMapping[transferId];\n }\n\n /**\n * @notice function to lookup the address mapped to the transferId\n * @param transferId transferId generated during the bridging of ERC20 or native on CelerBridge\n * @return address of account mapped to transferId\n */\n function getAddressFromTransferId(\n bytes32 transferId\n ) external view returns (address) {\n return transferIdMapping[transferId];\n }\n}\n"},"src/bridges/polygon/interfaces/polygon.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\n/**\n * @title RootChain Manager Interface for Polygon Bridge.\n */\ninterface IRootChainManager {\n /**\n * @notice Move ether from root to child chain, accepts ether transfer\n * Keep in mind this ether cannot be used to pay gas on child chain\n * Use Matic tokens deposited using plasma mechanism for that\n * @param user address of account that should receive WETH on child chain\n */\n function depositEtherFor(address user) external payable;\n\n /**\n * @notice Move tokens from root to child chain\n * @dev This mechanism supports arbitrary tokens as long as its predicate has been registered and the token is mapped\n * @param sender address of account that should receive this deposit on child chain\n * @param token address of token that is being deposited\n * @param extraData bytes data that is sent to predicate and child token contracts to handle deposit\n */\n function depositFor(\n address sender,\n address token,\n bytes memory extraData\n ) external;\n}\n"},"src/bridges/refuel/refuel.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport \"./interfaces/refuel.sol\";\nimport \"../BridgeImplBase.sol\";\nimport {REFUEL} from \"../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Refuel-Route Implementation\n * @notice Route implementation with functions to bridge Native via Refuel-Bridge\n * Called via SocketGateway if the routeId in the request maps to the routeId of RefuelImplementation\n * @author Socket dot tech.\n */\ncontract RefuelBridgeImpl is BridgeImplBase {\n bytes32 public immutable RefuelIdentifier = REFUEL;\n\n /// @notice refuelBridge-Contract address used to deposit Native on Refuel-Bridge\n address public immutable refuelBridge;\n\n /// @notice Function-selector for Native bridging via Refuel-Bridge\n /// @dev This function selector is to be used while buidling transaction-data to bridge Native tokens\n bytes4 public immutable REFUEL_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(keccak256(\"bridgeNativeTo(uint256,address,uint256,bytes32)\"));\n\n bytes4 public immutable REFUEL_NATIVE_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\"swapAndBridge(uint32,address,uint256,bytes32,bytes)\")\n );\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n /// @dev ensure _refuelBridge are set properly for the chainId in which the contract is being deployed\n constructor(\n address _refuelBridge,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n refuelBridge = _refuelBridge;\n }\n\n // Function to receive Ether. msg.data must be empty\n receive() external payable {}\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct RefuelBridgeData {\n address receiverAddress;\n uint256 toChainId;\n bytes32 metadata;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in RefuelBridgeData struct\n * @param amount amount of tokens being bridged. this must be only native\n * @param bridgeData encoded data for RefuelBridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n RefuelBridgeData memory refuelBridgeData = abi.decode(\n bridgeData,\n (RefuelBridgeData)\n );\n IRefuel(refuelBridge).depositNativeToken{value: amount}(\n refuelBridgeData.toChainId,\n refuelBridgeData.receiverAddress\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n refuelBridgeData.toChainId,\n RefuelIdentifier,\n msg.sender,\n refuelBridgeData.receiverAddress,\n refuelBridgeData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in RefuelBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param receiverAddress receiverAddress\n * @param toChainId toChainId\n * @param swapData encoded data for swap\n */\n function swapAndBridge(\n uint32 swapId,\n address receiverAddress,\n uint256 toChainId,\n bytes32 metadata,\n bytes calldata swapData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, ) = abi.decode(result, (uint256, address));\n IRefuel(refuelBridge).depositNativeToken{value: bridgeAmount}(\n toChainId,\n receiverAddress\n );\n\n emit SocketBridge(\n bridgeAmount,\n NATIVE_TOKEN_ADDRESS,\n toChainId,\n RefuelIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n\n /**\n * @notice function to handle Native bridging to receipent via Refuel-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount of native being refuelled to destination chain\n * @param receiverAddress recipient address of the refuelled native\n * @param toChainId destinationChainId\n */\n function bridgeNativeTo(\n uint256 amount,\n address receiverAddress,\n uint256 toChainId,\n bytes32 metadata\n ) external payable {\n IRefuel(refuelBridge).depositNativeToken{value: amount}(\n toChainId,\n receiverAddress\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n toChainId,\n RefuelIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n"},"src/bridges/across/interfaces/across.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\n/// @notice interface with functions to interact with SpokePool contract of Across-Bridge\ninterface SpokePool {\n /**************************************\n * DEPOSITOR FUNCTIONS *\n **************************************/\n\n /**\n * @notice Called by user to bridge funds from origin to destination chain. Depositor will effectively lock\n * tokens in this contract and receive a destination token on the destination chain. The origin => destination\n * token mapping is stored on the L1 HubPool.\n * @notice The caller must first approve this contract to spend amount of originToken.\n * @notice The originToken => destinationChainId must be enabled.\n * @notice This method is payable because the caller is able to deposit native token if the originToken is\n * wrappedNativeToken and this function will handle wrapping the native token to wrappedNativeToken.\n * @param recipient Address to receive funds at on destination chain.\n * @param originToken Token to lock into this contract to initiate deposit.\n * @param amount Amount of tokens to deposit. Will be amount of tokens to receive less fees.\n * @param destinationChainId Denotes network where user will receive funds from SpokePool by a relayer.\n * @param relayerFeePct % of deposit amount taken out to incentivize a fast relayer.\n * @param quoteTimestamp Timestamp used by relayers to compute this deposit's realizedLPFeePct which is paid\n * to LP pool on HubPool.\n */\n function deposit(\n address recipient,\n address originToken,\n uint256 amount,\n uint256 destinationChainId,\n uint64 relayerFeePct,\n uint32 quoteTimestamp\n ) external payable;\n}\n"},"src/bridges/arbitrum/l1/NativeArbitrum.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {L1GatewayRouter} from \"../interfaces/arbitrum.sol\";\nimport {BridgeImplBase} from \"../../BridgeImplBase.sol\";\nimport {NATIVE_ARBITRUM} from \"../../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Native Arbitrum-Route Implementation\n * @notice Route implementation with functions to bridge ERC20 via NativeArbitrum-Bridge\n * @notice Called via SocketGateway if the routeId in the request maps to the routeId of NativeArbitrum-Implementation\n * @notice This is used when transferring from ethereum chain to arbitrum via their native bridge.\n * @notice Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * @notice RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ncontract NativeArbitrumImpl is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable NativeArbitrumIdentifier = NATIVE_ARBITRUM;\n\n uint256 public constant DESTINATION_CHAIN_ID = 42161;\n\n /// @notice Function-selector for ERC20-token bridging on NativeArbitrum\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4\n public immutable NATIVE_ARBITRUM_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(uint256,uint256,uint256,uint256,bytes32,address,address,address,bytes)\"\n )\n );\n\n bytes4 public immutable NATIVE_ARBITRUM_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(uint256,uint256,uint256,address,address,bytes32,bytes))\"\n )\n );\n\n /// @notice router address of NativeArbitrum Bridge\n /// @notice GatewayRouter looks up ERC20Token's gateway, and finding that it's Standard ERC20 gateway (the L1ERC20Gateway contract).\n address public immutable router;\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n /// @dev ensure router-address are set properly for the chainId in which the contract is being deployed\n constructor(\n address _router,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n router = _router;\n }\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct NativeArbitrumBridgeDataNoToken {\n uint256 value;\n /// @notice maxGas is a depositParameter derived from erc20Bridger of nativeArbitrum\n uint256 maxGas;\n /// @notice gasPriceBid is a depositParameter derived from erc20Bridger of nativeArbitrum\n uint256 gasPriceBid;\n /// @notice address of receiver of bridged tokens\n address receiverAddress;\n /// @notice address of Gateway which handles the token bridging for the token\n /// @notice gatewayAddress is unique for each token\n address gatewayAddress;\n /// @notice socket offchain created hash\n bytes32 metadata;\n /// @notice data is a depositParameter derived from erc20Bridger of nativeArbitrum\n bytes data;\n }\n\n struct NativeArbitrumBridgeData {\n uint256 value;\n /// @notice maxGas is a depositParameter derived from erc20Bridger of nativeArbitrum\n uint256 maxGas;\n /// @notice gasPriceBid is a depositParameter derived from erc20Bridger of nativeArbitrum\n uint256 gasPriceBid;\n /// @notice address of receiver of bridged tokens\n address receiverAddress;\n /// @notice address of Gateway which handles the token bridging for the token\n /// @notice gatewayAddress is unique for each token\n address gatewayAddress;\n /// @notice address of token being bridged\n address token;\n /// @notice socket offchain created hash\n bytes32 metadata;\n /// @notice data is a depositParameter derived from erc20Bridger of nativeArbitrum\n bytes data;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in NativeArbitrumBridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for NativeArbitrumBridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n NativeArbitrumBridgeData memory nativeArbitrumBridgeData = abi.decode(\n bridgeData,\n (NativeArbitrumBridgeData)\n );\n ERC20(nativeArbitrumBridgeData.token).safeApprove(\n nativeArbitrumBridgeData.gatewayAddress,\n amount\n );\n\n L1GatewayRouter(router).outboundTransfer{\n value: nativeArbitrumBridgeData.value\n }(\n nativeArbitrumBridgeData.token,\n nativeArbitrumBridgeData.receiverAddress,\n amount,\n nativeArbitrumBridgeData.maxGas,\n nativeArbitrumBridgeData.gasPriceBid,\n nativeArbitrumBridgeData.data\n );\n\n emit SocketBridge(\n amount,\n nativeArbitrumBridgeData.token,\n DESTINATION_CHAIN_ID,\n NativeArbitrumIdentifier,\n msg.sender,\n nativeArbitrumBridgeData.receiverAddress,\n nativeArbitrumBridgeData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in NativeArbitrumBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param nativeArbitrumBridgeData encoded data for NativeArbitrumBridge\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n NativeArbitrumBridgeDataNoToken calldata nativeArbitrumBridgeData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n ERC20(token).safeApprove(\n nativeArbitrumBridgeData.gatewayAddress,\n bridgeAmount\n );\n\n L1GatewayRouter(router).outboundTransfer{\n value: nativeArbitrumBridgeData.value\n }(\n token,\n nativeArbitrumBridgeData.receiverAddress,\n bridgeAmount,\n nativeArbitrumBridgeData.maxGas,\n nativeArbitrumBridgeData.gasPriceBid,\n nativeArbitrumBridgeData.data\n );\n\n emit SocketBridge(\n bridgeAmount,\n token,\n DESTINATION_CHAIN_ID,\n NativeArbitrumIdentifier,\n msg.sender,\n nativeArbitrumBridgeData.receiverAddress,\n nativeArbitrumBridgeData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via NativeArbitrum-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount being bridged\n * @param value value\n * @param maxGas maxGas is a depositParameter derived from erc20Bridger of nativeArbitrum\n * @param gasPriceBid gasPriceBid is a depositParameter derived from erc20Bridger of nativeArbitrum\n * @param receiverAddress address of receiver of bridged tokens\n * @param token address of token being bridged\n * @param gatewayAddress address of Gateway which handles the token bridging for the token, gatewayAddress is unique for each token\n * @param data data is a depositParameter derived from erc20Bridger of nativeArbitrum\n */\n function bridgeERC20To(\n uint256 amount,\n uint256 value,\n uint256 maxGas,\n uint256 gasPriceBid,\n bytes32 metadata,\n address receiverAddress,\n address token,\n address gatewayAddress,\n bytes memory data\n ) external payable {\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n tokenInstance.safeApprove(gatewayAddress, amount);\n\n L1GatewayRouter(router).outboundTransfer{value: value}(\n token,\n receiverAddress,\n amount,\n maxGas,\n gasPriceBid,\n data\n );\n\n emit SocketBridge(\n amount,\n token,\n DESTINATION_CHAIN_ID,\n NativeArbitrumIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n"},"src/bridges/across/Across.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport \"./interfaces/across.sol\";\nimport \"../BridgeImplBase.sol\";\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {ACROSS} from \"../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Across-Route Implementation\n * @notice Route implementation with functions to bridge ERC20 and Native via Across-Bridge\n * Called via SocketGateway if the routeId in the request maps to the routeId of AcrossImplementation\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ncontract AcrossImpl is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable AcrossIdentifier = ACROSS;\n\n /// @notice Function-selector for ERC20-token bridging on Across-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4 public immutable ACROSS_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(uint256,uint256,bytes32,address,address,uint32,uint64)\"\n )\n );\n\n /// @notice Function-selector for Native bridging on Across-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge Native tokens\n bytes4 public immutable ACROSS_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeNativeTo(uint256,uint256,bytes32,address,uint32,uint64)\"\n )\n );\n\n bytes4 public immutable ACROSS_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(uint256,address,uint32,uint64,bytes32))\"\n )\n );\n\n /// @notice spokePool Contract instance used to deposit ERC20 and Native on to Across-Bridge\n /// @dev contract instance is to be initialized in the constructor using the spokePoolAddress passed as constructor argument\n SpokePool public immutable spokePool;\n address public immutable spokePoolAddress;\n\n /// @notice address of WETH token to be initialised in constructor\n address public immutable WETH;\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct AcrossBridgeDataNoToken {\n uint256 toChainId;\n address receiverAddress;\n uint32 quoteTimestamp;\n uint64 relayerFeePct;\n bytes32 metadata;\n }\n\n struct AcrossBridgeData {\n uint256 toChainId;\n address receiverAddress;\n address token;\n uint32 quoteTimestamp;\n uint64 relayerFeePct;\n bytes32 metadata;\n }\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n /// @dev ensure spokepool, weth-address are set properly for the chainId in which the contract is being deployed\n constructor(\n address _spokePool,\n address _wethAddress,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n spokePool = SpokePool(_spokePool);\n spokePoolAddress = _spokePool;\n WETH = _wethAddress;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in AcrossBridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for AcrossBridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n AcrossBridgeData memory acrossBridgeData = abi.decode(\n bridgeData,\n (AcrossBridgeData)\n );\n\n if (acrossBridgeData.token == NATIVE_TOKEN_ADDRESS) {\n spokePool.deposit{value: amount}(\n acrossBridgeData.receiverAddress,\n WETH,\n amount,\n acrossBridgeData.toChainId,\n acrossBridgeData.relayerFeePct,\n acrossBridgeData.quoteTimestamp\n );\n } else {\n spokePool.deposit(\n acrossBridgeData.receiverAddress,\n acrossBridgeData.token,\n amount,\n acrossBridgeData.toChainId,\n acrossBridgeData.relayerFeePct,\n acrossBridgeData.quoteTimestamp\n );\n }\n\n emit SocketBridge(\n amount,\n acrossBridgeData.token,\n acrossBridgeData.toChainId,\n AcrossIdentifier,\n msg.sender,\n acrossBridgeData.receiverAddress,\n acrossBridgeData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in AcrossBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param acrossBridgeData encoded data for AcrossBridge\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n AcrossBridgeDataNoToken calldata acrossBridgeData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n if (token == NATIVE_TOKEN_ADDRESS) {\n spokePool.deposit{value: bridgeAmount}(\n acrossBridgeData.receiverAddress,\n WETH,\n bridgeAmount,\n acrossBridgeData.toChainId,\n acrossBridgeData.relayerFeePct,\n acrossBridgeData.quoteTimestamp\n );\n } else {\n spokePool.deposit(\n acrossBridgeData.receiverAddress,\n token,\n bridgeAmount,\n acrossBridgeData.toChainId,\n acrossBridgeData.relayerFeePct,\n acrossBridgeData.quoteTimestamp\n );\n }\n\n emit SocketBridge(\n bridgeAmount,\n token,\n acrossBridgeData.toChainId,\n AcrossIdentifier,\n msg.sender,\n acrossBridgeData.receiverAddress,\n acrossBridgeData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via Across-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount being bridged\n * @param toChainId destination ChainId\n * @param receiverAddress address of receiver of bridged tokens\n * @param token address of token being bridged\n * @param quoteTimestamp timestamp for quote and this is to be used by Across-Bridge contract\n * @param relayerFeePct feePct that will be relayed by the Bridge to the relayer\n */\n function bridgeERC20To(\n uint256 amount,\n uint256 toChainId,\n bytes32 metadata,\n address receiverAddress,\n address token,\n uint32 quoteTimestamp,\n uint64 relayerFeePct\n ) external payable {\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n spokePool.deposit(\n receiverAddress,\n address(token),\n amount,\n toChainId,\n relayerFeePct,\n quoteTimestamp\n );\n\n emit SocketBridge(\n amount,\n token,\n toChainId,\n AcrossIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n\n /**\n * @notice function to handle Native bridging to receipent via Across-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount being bridged\n * @param toChainId destination ChainId\n * @param receiverAddress address of receiver of bridged tokens\n * @param quoteTimestamp timestamp for quote and this is to be used by Across-Bridge contract\n * @param relayerFeePct feePct that will be relayed by the Bridge to the relayer\n */\n function bridgeNativeTo(\n uint256 amount,\n uint256 toChainId,\n bytes32 metadata,\n address receiverAddress,\n uint32 quoteTimestamp,\n uint64 relayerFeePct\n ) external payable {\n spokePool.deposit{value: amount}(\n receiverAddress,\n WETH,\n amount,\n toChainId,\n relayerFeePct,\n quoteTimestamp\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n toChainId,\n AcrossIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n"},"src/bridges/cbridge/interfaces/ICelerStorageWrapper.sol":{"content":"// SPDX-License-Identifier: Apache-2.0\npragma solidity >=0.8.0;\n\n/**\n * @title Celer-StorageWrapper interface\n * @notice Interface to handle storageMappings used while bridging ERC20 and native on CelerBridge\n * @dev all functions ehich mutate the storage are restricted to Owner of SocketGateway\n * @author Socket dot tech.\n */\ninterface ICelerStorageWrapper {\n /**\n * @notice function to store the transferId and message-sender of a bridging activity\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in CelerBridgeData struct\n * @param transferId transferId generated during the bridging of ERC20 or native on CelerBridge\n * @param transferIdAddress message sender who is making the bridging on CelerBridge\n */\n function setAddressForTransferId(\n bytes32 transferId,\n address transferIdAddress\n ) external;\n\n /**\n * @notice function to store the transferId and message-sender of a bridging activity\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in CelerBridgeData struct\n * @param transferId transferId generated during the bridging of ERC20 or native on CelerBridge\n */\n function deleteTransferId(bytes32 transferId) external;\n\n /**\n * @notice function to lookup the address mapped to the transferId\n * @param transferId transferId generated during the bridging of ERC20 or native on CelerBridge\n * @return address of account mapped to transferId\n */\n function getAddressFromTransferId(\n bytes32 transferId\n ) external view returns (address);\n}\n"},"src/bridges/optimism/interfaces/optimism.sol":{"content":"// SPDX-License-Identifier: Apache-2.0\npragma solidity >=0.8.0;\n\ninterface L1StandardBridge {\n /**\n * @dev Performs the logic for deposits by storing the ETH and informing the L2 ETH Gateway of\n * the deposit.\n * @param _to Account to give the deposit to on L2.\n * @param _l2Gas Gas limit required to complete the deposit on L2.\n * @param _data Optional data to forward to L2. This data is provided\n * solely as a convenience for external contracts. Aside from enforcing a maximum\n * length, these contracts provide no guarantees about its content.\n */\n function depositETHTo(\n address _to,\n uint32 _l2Gas,\n bytes calldata _data\n ) external payable;\n\n /**\n * @dev deposit an amount of ERC20 to a recipient's balance on L2.\n * @param _l1Token Address of the L1 ERC20 we are depositing\n * @param _l2Token Address of the L1 respective L2 ERC20\n * @param _to L2 address to credit the withdrawal to.\n * @param _amount Amount of the ERC20 to deposit.\n * @param _l2Gas Gas limit required to complete the deposit on L2.\n * @param _data Optional data to forward to L2. This data is provided\n * solely as a convenience for external contracts. Aside from enforcing a maximum\n * length, these contracts provide no guarantees about its content.\n */\n function depositERC20To(\n address _l1Token,\n address _l2Token,\n address _to,\n uint256 _amount,\n uint32 _l2Gas,\n bytes calldata _data\n ) external;\n}\n\ninterface OldL1TokenGateway {\n /**\n * @dev Transfer SNX to L2 First, moves the SNX into the deposit escrow\n *\n * @param _to Account to give the deposit to on L2\n * @param _amount Amount of the ERC20 to deposit.\n */\n function depositTo(address _to, uint256 _amount) external;\n\n /**\n * @dev Transfer SNX to L2 First, moves the SNX into the deposit escrow\n *\n * @param currencyKey currencyKey for the SynthToken\n * @param destination Account to give the deposit to on L2\n * @param amount Amount of the ERC20 to deposit.\n */\n function initiateSynthTransfer(\n bytes32 currencyKey,\n address destination,\n uint256 amount\n ) external;\n}\n"},"src/bridges/arbitrum/interfaces/arbitrum.sol":{"content":"// SPDX-License-Identifier: Apache-2.0\n\n/*\n * Copyright 2021, Offchain Labs, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npragma solidity >=0.8.0;\n\n/**\n * @title L1gatewayRouter for native-arbitrum\n */\ninterface L1GatewayRouter {\n /**\n * @notice outbound function to bridge ERC20 via NativeArbitrum-Bridge\n * @param _token address of token being bridged via GatewayRouter\n * @param _to recipient of the token on arbitrum chain\n * @param _amount amount of ERC20 token being bridged\n * @param _maxGas a depositParameter for bridging the token\n * @param _gasPriceBid a depositParameter for bridging the token\n * @param _data a depositParameter for bridging the token\n * @return calldata returns the output of transactioncall made on gatewayRouter\n */\n function outboundTransfer(\n address _token,\n address _to,\n uint256 _amount,\n uint256 _maxGas,\n uint256 _gasPriceBid,\n bytes calldata _data\n ) external payable returns (bytes calldata);\n}\n"},"src/deployFactory/DisabledSocketRoute.sol":{"content":"//SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {ISocketGateway} from \"../interfaces/ISocketGateway.sol\";\nimport {OnlySocketGatewayOwner} from \"../errors/SocketErrors.sol\";\n\ncontract DisabledSocketRoute {\n using SafeTransferLib for ERC20;\n\n /// @notice immutable variable to store the socketGateway address\n address public immutable socketGateway;\n error RouteDisabled();\n\n /**\n * @notice Construct the base for all BridgeImplementations.\n * @param _socketGateway Socketgateway address, an immutable variable to set.\n */\n constructor(address _socketGateway) {\n socketGateway = _socketGateway;\n }\n\n /// @notice Implementing contract needs to make use of the modifier where restricted access is to be used\n modifier isSocketGatewayOwner() {\n if (msg.sender != ISocketGateway(socketGateway).owner()) {\n revert OnlySocketGatewayOwner();\n }\n _;\n }\n\n /**\n * @notice function to rescue the ERC20 tokens in the bridge Implementation contract\n * @notice this is a function restricted to Owner of SocketGateway only\n * @param token address of ERC20 token being rescued\n * @param userAddress receipient address to which ERC20 tokens will be rescued to\n * @param amount amount of ERC20 tokens being rescued\n */\n function rescueFunds(\n address token,\n address userAddress,\n uint256 amount\n ) external isSocketGatewayOwner {\n ERC20(token).safeTransfer(userAddress, amount);\n }\n\n /**\n * @notice function to rescue the native-balance in the bridge Implementation contract\n * @notice this is a function restricted to Owner of SocketGateway only\n * @param userAddress receipient address to which native-balance will be rescued to\n * @param amount amount of native balance tokens being rescued\n */\n function rescueEther(\n address payable userAddress,\n uint256 amount\n ) external isSocketGatewayOwner {\n userAddress.transfer(amount);\n }\n\n /**\n * @notice Handle route function calls gracefully.\n */\n fallback() external payable {\n revert RouteDisabled();\n }\n\n /**\n * @notice Support receiving ether to handle refunds etc.\n */\n receive() external payable {}\n}\n"},"src/bridges/polygon/NativePolygon.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport \"./interfaces/polygon.sol\";\nimport {BridgeImplBase} from \"../BridgeImplBase.sol\";\nimport {NATIVE_POLYGON} from \"../../static/RouteIdentifiers.sol\";\n\n/**\n * @title NativePolygon-Route Implementation\n * @notice This is the L1 implementation, so this is used when transferring from ethereum to polygon via their native bridge.\n * @author Socket dot tech.\n */\ncontract NativePolygonImpl is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable NativePolyonIdentifier = NATIVE_POLYGON;\n\n /// @notice destination-chain-Id for this router is always arbitrum\n uint256 public constant DESTINATION_CHAIN_ID = 137;\n\n /// @notice Function-selector for ERC20-token bridging on NativePolygon-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4\n public immutable NATIVE_POLYGON_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(keccak256(\"bridgeERC20To(uint256,bytes32,address,address)\"));\n\n /// @notice Function-selector for Native bridging on NativePolygon-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge Native tokens\n bytes4\n public immutable NATIVE_POLYGON_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(keccak256(\"bridgeNativeTo(uint256,bytes32,address)\"));\n\n bytes4 public immutable NATIVE_POLYGON_SWAP_BRIDGE_SELECTOR =\n bytes4(keccak256(\"swapAndBridge(uint32,address,bytes32,bytes)\"));\n\n /// @notice root chain manager proxy on the ethereum chain\n /// @dev to be initialised in the constructor\n IRootChainManager public immutable rootChainManagerProxy;\n\n /// @notice ERC20 Predicate proxy on the ethereum chain\n /// @dev to be initialised in the constructor\n address public immutable erc20PredicateProxy;\n\n /**\n * // @notice We set all the required addresses in the constructor while deploying the contract.\n * // These will be constant addresses.\n * // @dev Please use the Proxy addresses and not the implementation addresses while setting these\n * // @param _rootChainManagerProxy address of the root chain manager proxy on the ethereum chain\n * // @param _erc20PredicateProxy address of the ERC20 Predicate proxy on the ethereum chain.\n * // @param _socketGateway address of the socketGateway contract that calls this contract\n */\n constructor(\n address _rootChainManagerProxy,\n address _erc20PredicateProxy,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n rootChainManagerProxy = IRootChainManager(_rootChainManagerProxy);\n erc20PredicateProxy = _erc20PredicateProxy;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in NativePolygon-BridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for NativePolygon-Bridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n (address token, address receiverAddress, bytes32 metadata) = abi.decode(\n bridgeData,\n (address, address, bytes32)\n );\n\n if (token == NATIVE_TOKEN_ADDRESS) {\n IRootChainManager(rootChainManagerProxy).depositEtherFor{\n value: amount\n }(receiverAddress);\n } else {\n ERC20(token).safeApprove(erc20PredicateProxy, amount);\n\n // deposit into rootchain manager\n IRootChainManager(rootChainManagerProxy).depositFor(\n receiverAddress,\n token,\n abi.encodePacked(amount)\n );\n }\n\n emit SocketBridge(\n amount,\n token,\n DESTINATION_CHAIN_ID,\n NativePolyonIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in NativePolygon-BridgeData struct\n * @param swapId routeId for the swapImpl\n * @param receiverAddress address of the receiver\n * @param swapData encoded data for swap\n */\n function swapAndBridge(\n uint32 swapId,\n address receiverAddress,\n bytes32 metadata,\n bytes calldata swapData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n\n if (token == NATIVE_TOKEN_ADDRESS) {\n IRootChainManager(rootChainManagerProxy).depositEtherFor{\n value: bridgeAmount\n }(receiverAddress);\n } else {\n ERC20(token).safeApprove(erc20PredicateProxy, bridgeAmount);\n\n // deposit into rootchain manager\n IRootChainManager(rootChainManagerProxy).depositFor(\n receiverAddress,\n token,\n abi.encodePacked(bridgeAmount)\n );\n }\n\n emit SocketBridge(\n bridgeAmount,\n token,\n DESTINATION_CHAIN_ID,\n NativePolyonIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via NativePolygon-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount of tokens being bridged\n * @param receiverAddress recipient address\n * @param token address of token being bridged\n */\n function bridgeERC20To(\n uint256 amount,\n bytes32 metadata,\n address receiverAddress,\n address token\n ) external payable {\n ERC20 tokenInstance = ERC20(token);\n\n // set allowance for erc20 predicate\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n tokenInstance.safeApprove(erc20PredicateProxy, amount);\n\n // deposit into rootchain manager\n rootChainManagerProxy.depositFor(\n receiverAddress,\n token,\n abi.encodePacked(amount)\n );\n\n emit SocketBridge(\n amount,\n token,\n DESTINATION_CHAIN_ID,\n NativePolyonIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n\n /**\n * @notice function to handle Native bridging to receipent via NativePolygon-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount of tokens being bridged\n * @param receiverAddress recipient address\n */\n function bridgeNativeTo(\n uint256 amount,\n bytes32 metadata,\n address receiverAddress\n ) external payable {\n rootChainManagerProxy.depositEtherFor{value: amount}(receiverAddress);\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n DESTINATION_CHAIN_ID,\n NativePolyonIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n"},"src/bridges/stargate/l1/Stargate.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport \"../interfaces/stargate.sol\";\nimport {BridgeImplBase} from \"../../BridgeImplBase.sol\";\nimport {STARGATE} from \"../../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Stargate-L1-Route Implementation\n * @notice Route implementation with functions to bridge ERC20 and Native via Stargate-L1-Bridge\n * Called via SocketGateway if the routeId in the request maps to the routeId of Stargate-L1-Implementation\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ncontract StargateImplL1 is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable StargateIdentifier = STARGATE;\n\n /// @notice Function-selector for ERC20-token bridging on Stargate-L1-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4\n public immutable STARGATE_L1_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(address,address,address,uint256,uint256,(uint256,uint256,uint256,uint256,bytes32,bytes,uint16))\"\n )\n );\n\n /// @notice Function-selector for Native bridging on Stargate-L1-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge Native tokens\n bytes4\n public immutable STARGATE_L1_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeNativeTo(address,address,uint16,uint256,uint256,uint256,bytes32)\"\n )\n );\n\n bytes4 public immutable STARGATE_L1_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(address,address,uint16,uint256,uint256,uint256,uint256,uint256,uint256,bytes32,bytes))\"\n )\n );\n\n /// @notice Stargate Router to bridge ERC20 tokens\n IBridgeStargate public immutable router;\n\n /// @notice Stargate Router to bridge native tokens\n IBridgeStargate public immutable routerETH;\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n /// @dev ensure router, routerEth are set properly for the chainId in which the contract is being deployed\n constructor(\n address _router,\n address _routerEth,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n router = IBridgeStargate(_router);\n routerETH = IBridgeStargate(_routerEth);\n }\n\n struct StargateBridgeExtraData {\n uint256 srcPoolId;\n uint256 dstPoolId;\n uint256 destinationGasLimit;\n uint256 minReceivedAmt;\n bytes32 metadata;\n bytes destinationPayload;\n uint16 stargateDstChainId; // stargate defines chain id in its way\n }\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct StargateBridgeDataNoToken {\n address receiverAddress;\n address senderAddress;\n uint16 stargateDstChainId; // stargate defines chain id in its way\n uint256 value;\n // a unique identifier that is uses to dedup transfers\n // this value is the a timestamp sent from frontend, but in theory can be any unique number\n uint256 srcPoolId;\n uint256 dstPoolId;\n uint256 minReceivedAmt; // defines the slippage, the min qty you would accept on the destination\n uint256 optionalValue;\n uint256 destinationGasLimit;\n bytes32 metadata;\n bytes destinationPayload;\n }\n\n struct StargateBridgeData {\n address token;\n address receiverAddress;\n address senderAddress;\n uint16 stargateDstChainId; // stargate defines chain id in its way\n uint256 value;\n // a unique identifier that is uses to dedup transfers\n // this value is the a timestamp sent from frontend, but in theory can be any unique number\n uint256 srcPoolId;\n uint256 dstPoolId;\n uint256 minReceivedAmt; // defines the slippage, the min qty you would accept on the destination\n uint256 optionalValue;\n uint256 destinationGasLimit;\n bytes32 metadata;\n bytes destinationPayload;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in Stargate-BridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for Stargate-L1-Bridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n StargateBridgeData memory stargateBridgeData = abi.decode(\n bridgeData,\n (StargateBridgeData)\n );\n\n if (stargateBridgeData.token == NATIVE_TOKEN_ADDRESS) {\n // perform bridging\n routerETH.swapETH{value: amount + stargateBridgeData.optionalValue}(\n stargateBridgeData.stargateDstChainId,\n payable(stargateBridgeData.senderAddress),\n abi.encodePacked(stargateBridgeData.receiverAddress),\n amount,\n stargateBridgeData.minReceivedAmt\n );\n } else {\n ERC20(stargateBridgeData.token).safeApprove(\n address(router),\n amount\n );\n {\n router.swap{value: stargateBridgeData.value}(\n stargateBridgeData.stargateDstChainId,\n stargateBridgeData.srcPoolId,\n stargateBridgeData.dstPoolId,\n payable(stargateBridgeData.senderAddress), // default to refund to main contract\n amount,\n stargateBridgeData.minReceivedAmt,\n IBridgeStargate.lzTxObj(\n stargateBridgeData.destinationGasLimit,\n 0, // zero amount since this is a ERC20 bridging\n \"0x\" //empty data since this is for only ERC20\n ),\n abi.encodePacked(stargateBridgeData.receiverAddress),\n stargateBridgeData.destinationPayload\n );\n }\n }\n\n emit SocketBridge(\n amount,\n stargateBridgeData.token,\n stargateBridgeData.stargateDstChainId,\n StargateIdentifier,\n msg.sender,\n stargateBridgeData.receiverAddress,\n stargateBridgeData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in Stargate-BridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param stargateBridgeData encoded data for StargateBridgeData\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n StargateBridgeDataNoToken calldata stargateBridgeData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n\n if (token == NATIVE_TOKEN_ADDRESS) {\n // perform bridging\n routerETH.swapETH{\n value: bridgeAmount + stargateBridgeData.optionalValue\n }(\n stargateBridgeData.stargateDstChainId,\n payable(stargateBridgeData.senderAddress),\n abi.encodePacked(stargateBridgeData.receiverAddress),\n bridgeAmount,\n stargateBridgeData.minReceivedAmt\n );\n } else {\n ERC20(token).safeApprove(address(router), bridgeAmount);\n {\n router.swap{value: stargateBridgeData.value}(\n stargateBridgeData.stargateDstChainId,\n stargateBridgeData.srcPoolId,\n stargateBridgeData.dstPoolId,\n payable(stargateBridgeData.senderAddress), // default to refund to main contract\n bridgeAmount,\n stargateBridgeData.minReceivedAmt,\n IBridgeStargate.lzTxObj(\n stargateBridgeData.destinationGasLimit,\n 0, // zero amount since this is a ERC20 bridging\n \"0x\" //empty data since this is for only ERC20\n ),\n abi.encodePacked(stargateBridgeData.receiverAddress),\n stargateBridgeData.destinationPayload\n );\n }\n }\n\n emit SocketBridge(\n bridgeAmount,\n token,\n stargateBridgeData.stargateDstChainId,\n StargateIdentifier,\n msg.sender,\n stargateBridgeData.receiverAddress,\n stargateBridgeData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via Stargate-L1-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param token address of token being bridged\n * @param senderAddress address of sender\n * @param receiverAddress address of recipient\n * @param amount amount of token being bridge\n * @param value value\n * @param stargateBridgeExtraData stargate bridge extradata\n */\n function bridgeERC20To(\n address token,\n address senderAddress,\n address receiverAddress,\n uint256 amount,\n uint256 value,\n StargateBridgeExtraData calldata stargateBridgeExtraData\n ) external payable {\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n tokenInstance.safeApprove(address(router), amount);\n {\n router.swap{value: value}(\n stargateBridgeExtraData.stargateDstChainId,\n stargateBridgeExtraData.srcPoolId,\n stargateBridgeExtraData.dstPoolId,\n payable(senderAddress), // default to refund to main contract\n amount,\n stargateBridgeExtraData.minReceivedAmt,\n IBridgeStargate.lzTxObj(\n stargateBridgeExtraData.destinationGasLimit,\n 0, // zero amount since this is a ERC20 bridging\n \"0x\" //empty data since this is for only ERC20\n ),\n abi.encodePacked(receiverAddress),\n stargateBridgeExtraData.destinationPayload\n );\n }\n\n emit SocketBridge(\n amount,\n token,\n stargateBridgeExtraData.stargateDstChainId,\n StargateIdentifier,\n msg.sender,\n receiverAddress,\n stargateBridgeExtraData.metadata\n );\n }\n\n /**\n * @notice function to handle Native bridging to receipent via Stargate-L1-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param receiverAddress address of receipient\n * @param senderAddress address of sender\n * @param stargateDstChainId stargate defines chain id in its way\n * @param amount amount of token being bridge\n * @param minReceivedAmt defines the slippage, the min qty you would accept on the destination\n * @param optionalValue optionalValue Native amount\n */\n function bridgeNativeTo(\n address receiverAddress,\n address senderAddress,\n uint16 stargateDstChainId,\n uint256 amount,\n uint256 minReceivedAmt,\n uint256 optionalValue,\n bytes32 metadata\n ) external payable {\n // perform bridging\n routerETH.swapETH{value: amount + optionalValue}(\n stargateDstChainId,\n payable(senderAddress),\n abi.encodePacked(receiverAddress),\n amount,\n minReceivedAmt\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n stargateDstChainId,\n StargateIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n"},"src/bridges/hop/l2/HopImplL2.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport \"../interfaces/amm.sol\";\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {BridgeImplBase} from \"../../BridgeImplBase.sol\";\nimport {HOP} from \"../../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Hop-L2 Route Implementation\n * @notice This is the L2 implementation, so this is used when transferring from l2 to supported l2s\n * Called via SocketGateway if the routeId in the request maps to the routeId of HopL2-Implementation\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ncontract HopImplL2 is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable HopIdentifier = HOP;\n\n /// @notice Function-selector for ERC20-token bridging on Hop-L2-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4 public immutable HOP_L2_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(address,address,address,uint256,uint256,(uint256,uint256,uint256,uint256,uint256,bytes32))\"\n )\n );\n\n /// @notice Function-selector for Native bridging on Hop-L2-Route\n /// @dev This function selector is to be used while building transaction-data to bridge Native tokens\n bytes4 public immutable HOP_L2_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeNativeTo(address,address,uint256,uint256,uint256,uint256,uint256,uint256,uint256,bytes32)\"\n )\n );\n\n bytes4 public immutable HOP_L2_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes32))\"\n )\n );\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n constructor(\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {}\n\n /// @notice Struct to be used as a input parameter for Bridging tokens via Hop-L2-route\n /// @dev while building transactionData,values should be set in this sequence of properties in this struct\n struct HopBridgeRequestData {\n // fees passed to relayer\n uint256 bonderFee;\n // The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.\n uint256 amountOutMin;\n // The deadline for swapping in the destination AMM market. 0 if no swap is intended.\n uint256 deadline;\n // Minimum amount expected to be received or bridged to destination\n uint256 amountOutMinDestination;\n // deadline for bridging to destination\n uint256 deadlineDestination;\n // socket offchain created hash\n bytes32 metadata;\n }\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct HopBridgeDataNoToken {\n // The address receiving funds at the destination\n address receiverAddress;\n // AMM address of Hop on L2\n address hopAMM;\n // The chainId of the destination chain\n uint256 toChainId;\n // fees passed to relayer\n uint256 bonderFee;\n // The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.\n uint256 amountOutMin;\n // The deadline for swapping in the destination AMM market. 0 if no swap is intended.\n uint256 deadline;\n // Minimum amount expected to be received or bridged to destination\n uint256 amountOutMinDestination;\n // deadline for bridging to destination\n uint256 deadlineDestination;\n // socket offchain created hash\n bytes32 metadata;\n }\n\n struct HopBridgeData {\n /// @notice address of token being bridged\n address token;\n // The address receiving funds at the destination\n address receiverAddress;\n // AMM address of Hop on L2\n address hopAMM;\n // The chainId of the destination chain\n uint256 toChainId;\n // fees passed to relayer\n uint256 bonderFee;\n // The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.\n uint256 amountOutMin;\n // The deadline for swapping in the destination AMM market. 0 if no swap is intended.\n uint256 deadline;\n // Minimum amount expected to be received or bridged to destination\n uint256 amountOutMinDestination;\n // deadline for bridging to destination\n uint256 deadlineDestination;\n // socket offchain created hash\n bytes32 metadata;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in HopBridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for Hop-L2-Bridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n HopBridgeData memory hopData = abi.decode(bridgeData, (HopBridgeData));\n\n if (hopData.token == NATIVE_TOKEN_ADDRESS) {\n HopAMM(hopData.hopAMM).swapAndSend{value: amount}(\n hopData.toChainId,\n hopData.receiverAddress,\n amount,\n hopData.bonderFee,\n hopData.amountOutMin,\n hopData.deadline,\n hopData.amountOutMinDestination,\n hopData.deadlineDestination\n );\n } else {\n // perform bridging\n HopAMM(hopData.hopAMM).swapAndSend(\n hopData.toChainId,\n hopData.receiverAddress,\n amount,\n hopData.bonderFee,\n hopData.amountOutMin,\n hopData.deadline,\n hopData.amountOutMinDestination,\n hopData.deadlineDestination\n );\n }\n\n emit SocketBridge(\n amount,\n hopData.token,\n hopData.toChainId,\n HopIdentifier,\n msg.sender,\n hopData.receiverAddress,\n hopData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in HopBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param hopData encoded data for HopData\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n HopBridgeDataNoToken calldata hopData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n\n if (token == NATIVE_TOKEN_ADDRESS) {\n HopAMM(hopData.hopAMM).swapAndSend{value: bridgeAmount}(\n hopData.toChainId,\n hopData.receiverAddress,\n bridgeAmount,\n hopData.bonderFee,\n hopData.amountOutMin,\n hopData.deadline,\n hopData.amountOutMinDestination,\n hopData.deadlineDestination\n );\n } else {\n // perform bridging\n HopAMM(hopData.hopAMM).swapAndSend(\n hopData.toChainId,\n hopData.receiverAddress,\n bridgeAmount,\n hopData.bonderFee,\n hopData.amountOutMin,\n hopData.deadline,\n hopData.amountOutMinDestination,\n hopData.deadlineDestination\n );\n }\n\n emit SocketBridge(\n bridgeAmount,\n token,\n hopData.toChainId,\n HopIdentifier,\n msg.sender,\n hopData.receiverAddress,\n hopData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via Hop-L2-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param receiverAddress The address receiving funds at the destination\n * @param token token being bridged\n * @param hopAMM AMM address of Hop on L2\n * @param amount The amount being bridged\n * @param toChainId The chainId of the destination chain\n * @param hopBridgeRequestData extraData for Bridging across Hop-L2\n */\n function bridgeERC20To(\n address receiverAddress,\n address token,\n address hopAMM,\n uint256 amount,\n uint256 toChainId,\n HopBridgeRequestData calldata hopBridgeRequestData\n ) external payable {\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n\n HopAMM(hopAMM).swapAndSend(\n toChainId,\n receiverAddress,\n amount,\n hopBridgeRequestData.bonderFee,\n hopBridgeRequestData.amountOutMin,\n hopBridgeRequestData.deadline,\n hopBridgeRequestData.amountOutMinDestination,\n hopBridgeRequestData.deadlineDestination\n );\n\n emit SocketBridge(\n amount,\n token,\n toChainId,\n HopIdentifier,\n msg.sender,\n receiverAddress,\n hopBridgeRequestData.metadata\n );\n }\n\n /**\n * @notice function to handle Native bridging to receipent via Hop-L2-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param receiverAddress The address receiving funds at the destination\n * @param hopAMM AMM address of Hop on L2\n * @param amount The amount being bridged\n * @param toChainId The chainId of the destination chain\n * @param bonderFee fees passed to relayer\n * @param amountOutMin The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.\n * @param deadline The deadline for swapping in the destination AMM market. 0 if no swap is intended.\n * @param amountOutMinDestination Minimum amount expected to be received or bridged to destination\n * @param deadlineDestination deadline for bridging to destination\n */\n function bridgeNativeTo(\n address receiverAddress,\n address hopAMM,\n uint256 amount,\n uint256 toChainId,\n uint256 bonderFee,\n uint256 amountOutMin,\n uint256 deadline,\n uint256 amountOutMinDestination,\n uint256 deadlineDestination,\n bytes32 metadata\n ) external payable {\n // token address might not be indication thats why passed through extraData\n // perform bridging\n HopAMM(hopAMM).swapAndSend{value: amount}(\n toChainId,\n receiverAddress,\n amount,\n bonderFee,\n amountOutMin,\n deadline,\n amountOutMinDestination,\n deadlineDestination\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n toChainId,\n HopIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n"},"src/bridges/anyswap-router-v4/l2/Anyswap.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {BridgeImplBase} from \"../../BridgeImplBase.sol\";\nimport {ANYSWAP} from \"../../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Anyswap-V4-Route L1 Implementation\n * @notice Route implementation with functions to bridge ERC20 via Anyswap-Bridge\n * Called via SocketGateway if the routeId in the request maps to the routeId of AnyswapImplementation\n * This is the L2 implementation, so this is used when transferring from l2.\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ninterface AnyswapV4Router {\n function anySwapOutUnderlying(\n address token,\n address to,\n uint256 amount,\n uint256 toChainID\n ) external;\n}\n\ncontract AnyswapL2Impl is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable AnyswapIdentifier = ANYSWAP;\n\n /// @notice Function-selector for ERC20-token bridging on Anyswap-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4 public immutable ANYSWAP_L2_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(uint256,uint256,bytes32,address,address,address)\"\n )\n );\n\n bytes4 public immutable ANYSWAP_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(uint256,address,address,bytes32))\"\n )\n );\n\n // polygon router multichain router v4\n AnyswapV4Router public immutable router;\n\n /**\n * @notice Constructor sets the router address and socketGateway address.\n * @dev anyswap v4 router is immutable. so no setter function required.\n */\n constructor(\n address _router,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n router = AnyswapV4Router(_router);\n }\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct AnyswapBridgeDataNoToken {\n /// @notice destination ChainId\n uint256 toChainId;\n /// @notice address of receiver of bridged tokens\n address receiverAddress;\n /// @notice address of wrapperToken, WrappedVersion of the token being bridged\n address wrapperTokenAddress;\n /// @notice socket offchain created hash\n bytes32 metadata;\n }\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct AnyswapBridgeData {\n /// @notice destination ChainId\n uint256 toChainId;\n /// @notice address of receiver of bridged tokens\n address receiverAddress;\n /// @notice address of wrapperToken, WrappedVersion of the token being bridged\n address wrapperTokenAddress;\n /// @notice address of token being bridged\n address token;\n /// @notice socket offchain created hash\n bytes32 metadata;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in AnyswapBridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for AnyswapBridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n AnyswapBridgeData memory anyswapBridgeData = abi.decode(\n bridgeData,\n (AnyswapBridgeData)\n );\n ERC20(anyswapBridgeData.token).safeApprove(address(router), amount);\n router.anySwapOutUnderlying(\n anyswapBridgeData.wrapperTokenAddress,\n anyswapBridgeData.receiverAddress,\n amount,\n anyswapBridgeData.toChainId\n );\n\n emit SocketBridge(\n amount,\n anyswapBridgeData.token,\n anyswapBridgeData.toChainId,\n AnyswapIdentifier,\n msg.sender,\n anyswapBridgeData.receiverAddress,\n anyswapBridgeData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in AnyswapBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param anyswapBridgeData encoded data for AnyswapBridge\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n AnyswapBridgeDataNoToken calldata anyswapBridgeData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n\n ERC20(token).safeApprove(address(router), bridgeAmount);\n router.anySwapOutUnderlying(\n anyswapBridgeData.wrapperTokenAddress,\n anyswapBridgeData.receiverAddress,\n bridgeAmount,\n anyswapBridgeData.toChainId\n );\n\n emit SocketBridge(\n bridgeAmount,\n token,\n anyswapBridgeData.toChainId,\n AnyswapIdentifier,\n msg.sender,\n anyswapBridgeData.receiverAddress,\n anyswapBridgeData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via Anyswap-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount being bridged\n * @param toChainId destination ChainId\n * @param receiverAddress address of receiver of bridged tokens\n * @param token address of token being bridged\n * @param wrapperTokenAddress address of wrapperToken, WrappedVersion of the token being bridged\n */\n function bridgeERC20To(\n uint256 amount,\n uint256 toChainId,\n bytes32 metadata,\n address receiverAddress,\n address token,\n address wrapperTokenAddress\n ) external payable {\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n tokenInstance.safeApprove(address(router), amount);\n router.anySwapOutUnderlying(\n wrapperTokenAddress,\n receiverAddress,\n amount,\n toChainId\n );\n\n emit SocketBridge(\n amount,\n token,\n toChainId,\n AnyswapIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n"},"src/bridges/hyphen/Hyphen.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport \"./interfaces/hyphen.sol\";\nimport \"../BridgeImplBase.sol\";\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {HYPHEN} from \"../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Hyphen-Route Implementation\n * @notice Route implementation with functions to bridge ERC20 and Native via Hyphen-Bridge\n * Called via SocketGateway if the routeId in the request maps to the routeId of HyphenImplementation\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ncontract HyphenImpl is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable HyphenIdentifier = HYPHEN;\n\n /// @notice Function-selector for ERC20-token bridging on Hyphen-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4 public immutable HYPHEN_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\"bridgeERC20To(uint256,bytes32,address,address,uint256)\")\n );\n\n /// @notice Function-selector for Native bridging on Hyphen-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge Native tokens\n bytes4 public immutable HYPHEN_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(keccak256(\"bridgeNativeTo(uint256,bytes32,address,uint256)\"));\n\n bytes4 public immutable HYPHEN_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\"swapAndBridge(uint32,bytes,(address,uint256,bytes32))\")\n );\n\n /// @notice liquidityPoolManager - liquidityPool Manager of Hyphen used to bridge ERC20 and native\n /// @dev this is to be initialized in constructor with a valid deployed address of hyphen-liquidityPoolManager\n HyphenLiquidityPoolManager public immutable liquidityPoolManager;\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n /// @dev ensure liquidityPoolManager-address are set properly for the chainId in which the contract is being deployed\n constructor(\n address _liquidityPoolManager,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n liquidityPoolManager = HyphenLiquidityPoolManager(\n _liquidityPoolManager\n );\n }\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct HyphenData {\n /// @notice address of token being bridged\n address token;\n /// @notice address of receiver\n address receiverAddress;\n /// @notice chainId of destination\n uint256 toChainId;\n /// @notice socket offchain created hash\n bytes32 metadata;\n }\n\n struct HyphenDataNoToken {\n /// @notice address of receiver\n address receiverAddress;\n /// @notice chainId of destination\n uint256 toChainId;\n /// @notice chainId of destination\n bytes32 metadata;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in HyphenBridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for HyphenBridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n HyphenData memory hyphenData = abi.decode(bridgeData, (HyphenData));\n\n if (hyphenData.token == NATIVE_TOKEN_ADDRESS) {\n liquidityPoolManager.depositNative{value: amount}(\n hyphenData.receiverAddress,\n hyphenData.toChainId,\n \"SOCKET\"\n );\n } else {\n ERC20(hyphenData.token).safeApprove(\n address(liquidityPoolManager),\n amount\n );\n liquidityPoolManager.depositErc20(\n hyphenData.toChainId,\n hyphenData.token,\n hyphenData.receiverAddress,\n amount,\n \"SOCKET\"\n );\n }\n\n emit SocketBridge(\n amount,\n hyphenData.token,\n hyphenData.toChainId,\n HyphenIdentifier,\n msg.sender,\n hyphenData.receiverAddress,\n hyphenData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in HyphenBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param hyphenData encoded data for hyphenData\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n HyphenDataNoToken calldata hyphenData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n if (token == NATIVE_TOKEN_ADDRESS) {\n liquidityPoolManager.depositNative{value: bridgeAmount}(\n hyphenData.receiverAddress,\n hyphenData.toChainId,\n \"SOCKET\"\n );\n } else {\n ERC20(token).safeApprove(\n address(liquidityPoolManager),\n bridgeAmount\n );\n liquidityPoolManager.depositErc20(\n hyphenData.toChainId,\n token,\n hyphenData.receiverAddress,\n bridgeAmount,\n \"SOCKET\"\n );\n }\n\n emit SocketBridge(\n bridgeAmount,\n token,\n hyphenData.toChainId,\n HyphenIdentifier,\n msg.sender,\n hyphenData.receiverAddress,\n hyphenData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via Hyphen-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount to be sent\n * @param receiverAddress address of the token to bridged to the destination chain.\n * @param token address of token being bridged\n * @param toChainId chainId of destination\n */\n function bridgeERC20To(\n uint256 amount,\n bytes32 metadata,\n address receiverAddress,\n address token,\n uint256 toChainId\n ) external payable {\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n tokenInstance.safeApprove(address(liquidityPoolManager), amount);\n liquidityPoolManager.depositErc20(\n toChainId,\n token,\n receiverAddress,\n amount,\n \"SOCKET\"\n );\n\n emit SocketBridge(\n amount,\n token,\n toChainId,\n HyphenIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n\n /**\n * @notice function to handle Native bridging to receipent via Hyphen-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param amount amount to be sent\n * @param receiverAddress address of the token to bridged to the destination chain.\n * @param toChainId chainId of destination\n */\n function bridgeNativeTo(\n uint256 amount,\n bytes32 metadata,\n address receiverAddress,\n uint256 toChainId\n ) external payable {\n liquidityPoolManager.depositNative{value: amount}(\n receiverAddress,\n toChainId,\n \"SOCKET\"\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n toChainId,\n HyphenIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n"},"src/bridges/optimism/l1/NativeOptimism.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport \"../interfaces/optimism.sol\";\nimport {BridgeImplBase} from \"../../BridgeImplBase.sol\";\nimport {UnsupportedInterfaceId} from \"../../../errors/SocketErrors.sol\";\nimport {NATIVE_OPTIMISM} from \"../../../static/RouteIdentifiers.sol\";\n\n/**\n * @title NativeOptimism-Route Implementation\n * @notice Route implementation with functions to bridge ERC20 and Native via NativeOptimism-Bridge\n * Tokens are bridged from Ethereum to Optimism Chain.\n * Called via SocketGateway if the routeId in the request maps to the routeId of NativeOptimism-Implementation\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ncontract NativeOptimismImpl is BridgeImplBase {\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable NativeOptimismIdentifier = NATIVE_OPTIMISM;\n\n uint256 public constant DESTINATION_CHAIN_ID = 10;\n\n /// @notice Function-selector for ERC20-token bridging on Native-Optimism-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4\n public immutable NATIVE_OPTIMISM_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(address,address,address,uint32,(bytes32,bytes32),uint256,uint256,address,bytes)\"\n )\n );\n\n /// @notice Function-selector for Native bridging on Native-Optimism-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge Native balance\n bytes4\n public immutable NATIVE_OPTIMISM_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeNativeTo(address,address,uint32,uint256,bytes32,bytes)\"\n )\n );\n\n bytes4 public immutable NATIVE_OPTIMISM_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(uint256,bytes32,bytes32,address,address,uint32,address,bytes))\"\n )\n );\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n constructor(\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {}\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct OptimismBridgeDataNoToken {\n // interfaceId to be set offchain which is used to select one of the 3 kinds of bridging (standard bridge / old standard / synthetic)\n uint256 interfaceId;\n // currencyKey of the token beingBridged\n bytes32 currencyKey;\n // socket offchain created hash\n bytes32 metadata;\n // address of receiver of bridged tokens\n address receiverAddress;\n /**\n * OptimismBridge that Performs the logic for deposits by informing the L2 Deposited Token\n * contract of the deposit and calling a handler to lock the L1 funds. (e.g. transferFrom)\n */\n address customBridgeAddress;\n // Gas limit required to complete the deposit on L2.\n uint32 l2Gas;\n // Address of the L1 respective L2 ERC20\n address l2Token;\n // additional data , for ll contracts this will be 0x data or empty data\n bytes data;\n }\n\n struct OptimismBridgeData {\n // interfaceId to be set offchain which is used to select one of the 3 kinds of bridging (standard bridge / old standard / synthetic)\n uint256 interfaceId;\n // currencyKey of the token beingBridged\n bytes32 currencyKey;\n // socket offchain created hash\n bytes32 metadata;\n // address of receiver of bridged tokens\n address receiverAddress;\n /**\n * OptimismBridge that Performs the logic for deposits by informing the L2 Deposited Token\n * contract of the deposit and calling a handler to lock the L1 funds. (e.g. transferFrom)\n */\n address customBridgeAddress;\n /// @notice address of token being bridged\n address token;\n // Gas limit required to complete the deposit on L2.\n uint32 l2Gas;\n // Address of the L1 respective L2 ERC20\n address l2Token;\n // additional data , for ll contracts this will be 0x data or empty data\n bytes data;\n }\n\n struct OptimismERC20Data {\n bytes32 currencyKey;\n bytes32 metadata;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in OptimismBridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for Optimism-Bridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n OptimismBridgeData memory optimismBridgeData = abi.decode(\n bridgeData,\n (OptimismBridgeData)\n );\n\n emit SocketBridge(\n amount,\n optimismBridgeData.token,\n DESTINATION_CHAIN_ID,\n NativeOptimismIdentifier,\n msg.sender,\n optimismBridgeData.receiverAddress,\n optimismBridgeData.metadata\n );\n if (optimismBridgeData.token == NATIVE_TOKEN_ADDRESS) {\n L1StandardBridge(optimismBridgeData.customBridgeAddress)\n .depositETHTo{value: amount}(\n optimismBridgeData.receiverAddress,\n optimismBridgeData.l2Gas,\n optimismBridgeData.data\n );\n } else {\n if (optimismBridgeData.interfaceId == 0) {\n revert UnsupportedInterfaceId();\n }\n\n ERC20(optimismBridgeData.token).safeApprove(\n optimismBridgeData.customBridgeAddress,\n amount\n );\n\n if (optimismBridgeData.interfaceId == 1) {\n // deposit into standard bridge\n L1StandardBridge(optimismBridgeData.customBridgeAddress)\n .depositERC20To(\n optimismBridgeData.token,\n optimismBridgeData.l2Token,\n optimismBridgeData.receiverAddress,\n amount,\n optimismBridgeData.l2Gas,\n optimismBridgeData.data\n );\n return;\n }\n\n // Deposit Using Old Standard - iOVM_L1TokenGateway(Example - SNX Token)\n if (optimismBridgeData.interfaceId == 2) {\n OldL1TokenGateway(optimismBridgeData.customBridgeAddress)\n .depositTo(optimismBridgeData.receiverAddress, amount);\n return;\n }\n\n if (optimismBridgeData.interfaceId == 3) {\n OldL1TokenGateway(optimismBridgeData.customBridgeAddress)\n .initiateSynthTransfer(\n optimismBridgeData.currencyKey,\n optimismBridgeData.receiverAddress,\n amount\n );\n return;\n }\n }\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in OptimismBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param optimismBridgeData encoded data for OptimismBridgeData\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n OptimismBridgeDataNoToken calldata optimismBridgeData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n\n emit SocketBridge(\n bridgeAmount,\n token,\n DESTINATION_CHAIN_ID,\n NativeOptimismIdentifier,\n msg.sender,\n optimismBridgeData.receiverAddress,\n optimismBridgeData.metadata\n );\n if (token == NATIVE_TOKEN_ADDRESS) {\n L1StandardBridge(optimismBridgeData.customBridgeAddress)\n .depositETHTo{value: bridgeAmount}(\n optimismBridgeData.receiverAddress,\n optimismBridgeData.l2Gas,\n optimismBridgeData.data\n );\n } else {\n if (optimismBridgeData.interfaceId == 0) {\n revert UnsupportedInterfaceId();\n }\n\n ERC20(token).safeApprove(\n optimismBridgeData.customBridgeAddress,\n bridgeAmount\n );\n\n if (optimismBridgeData.interfaceId == 1) {\n // deposit into standard bridge\n L1StandardBridge(optimismBridgeData.customBridgeAddress)\n .depositERC20To(\n token,\n optimismBridgeData.l2Token,\n optimismBridgeData.receiverAddress,\n bridgeAmount,\n optimismBridgeData.l2Gas,\n optimismBridgeData.data\n );\n return;\n }\n\n // Deposit Using Old Standard - iOVM_L1TokenGateway(Example - SNX Token)\n if (optimismBridgeData.interfaceId == 2) {\n OldL1TokenGateway(optimismBridgeData.customBridgeAddress)\n .depositTo(\n optimismBridgeData.receiverAddress,\n bridgeAmount\n );\n return;\n }\n\n if (optimismBridgeData.interfaceId == 3) {\n OldL1TokenGateway(optimismBridgeData.customBridgeAddress)\n .initiateSynthTransfer(\n optimismBridgeData.currencyKey,\n optimismBridgeData.receiverAddress,\n bridgeAmount\n );\n return;\n }\n }\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via NativeOptimism-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param token address of token being bridged\n * @param receiverAddress address of receiver of bridged tokens\n * @param customBridgeAddress OptimismBridge that Performs the logic for deposits by informing the L2 Deposited Token\n * contract of the deposit and calling a handler to lock the L1 funds. (e.g. transferFrom)\n * @param l2Gas Gas limit required to complete the deposit on L2.\n * @param optimismData extra data needed for optimism bridge\n * @param amount amount being bridged\n * @param interfaceId interfaceId to be set offchain which is used to select one of the 3 kinds of bridging (standard bridge / old standard / synthetic)\n * @param l2Token Address of the L1 respective L2 ERC20\n * @param data additional data , for ll contracts this will be 0x data or empty data\n */\n function bridgeERC20To(\n address token,\n address receiverAddress,\n address customBridgeAddress,\n uint32 l2Gas,\n OptimismERC20Data calldata optimismData,\n uint256 amount,\n uint256 interfaceId,\n address l2Token,\n bytes calldata data\n ) external payable {\n if (interfaceId == 0) {\n revert UnsupportedInterfaceId();\n }\n\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n tokenInstance.safeApprove(customBridgeAddress, amount);\n\n emit SocketBridge(\n amount,\n token,\n DESTINATION_CHAIN_ID,\n NativeOptimismIdentifier,\n msg.sender,\n receiverAddress,\n optimismData.metadata\n );\n if (interfaceId == 1) {\n // deposit into standard bridge\n L1StandardBridge(customBridgeAddress).depositERC20To(\n token,\n l2Token,\n receiverAddress,\n amount,\n l2Gas,\n data\n );\n return;\n }\n\n // Deposit Using Old Standard - iOVM_L1TokenGateway(Example - SNX Token)\n if (interfaceId == 2) {\n OldL1TokenGateway(customBridgeAddress).depositTo(\n receiverAddress,\n amount\n );\n return;\n }\n\n if (interfaceId == 3) {\n OldL1TokenGateway(customBridgeAddress).initiateSynthTransfer(\n optimismData.currencyKey,\n receiverAddress,\n amount\n );\n return;\n }\n }\n\n /**\n * @notice function to handle native balance bridging to receipent via NativeOptimism-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param receiverAddress address of receiver of bridged tokens\n * @param customBridgeAddress OptimismBridge that Performs the logic for deposits by informing the L2 Deposited Token\n * contract of the deposit and calling a handler to lock the L1 funds. (e.g. transferFrom)\n * @param l2Gas Gas limit required to complete the deposit on L2.\n * @param amount amount being bridged\n * @param data additional data , for ll contracts this will be 0x data or empty data\n */\n function bridgeNativeTo(\n address receiverAddress,\n address customBridgeAddress,\n uint32 l2Gas,\n uint256 amount,\n bytes32 metadata,\n bytes calldata data\n ) external payable {\n L1StandardBridge(customBridgeAddress).depositETHTo{value: amount}(\n receiverAddress,\n l2Gas,\n data\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n DESTINATION_CHAIN_ID,\n NativeOptimismIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n"},"src/deployFactory/SocketDeployFactory.sol":{"content":"//SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport \"../utils/Ownable.sol\";\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {ISocketBridgeBase} from \"../interfaces/ISocketBridgeBase.sol\";\n\n/**\n * @dev In the constructor, set up the initialization code for socket\n * contracts as well as the keccak256 hash of the given initialization code.\n * that will be used to deploy any transient contracts, which will deploy any\n * socket contracts that require the use of a constructor.\n *\n * Socket contract initialization code (29 bytes):\n *\n * 0x5860208158601c335a63aaf10f428752fa158151803b80938091923cf3\n *\n * Description:\n *\n * pc|op|name | [stack] | \n *\n * ** set the first stack item to zero - used later **\n * 00 58 getpc [0] <>\n *\n * ** set second stack item to 32, length of word returned from staticcall **\n * 01 60 push1\n * 02 20 outsize [0, 32] <>\n *\n * ** set third stack item to 0, position of word returned from staticcall **\n * 03 81 dup2 [0, 32, 0] <>\n *\n * ** set fourth stack item to 4, length of selector given to staticcall **\n * 04 58 getpc [0, 32, 0, 4] <>\n *\n * ** set fifth stack item to 28, position of selector given to staticcall **\n * 05 60 push1\n * 06 1c inpos [0, 32, 0, 4, 28] <>\n *\n * ** set the sixth stack item to msg.sender, target address for staticcall **\n * 07 33 caller [0, 32, 0, 4, 28, caller] <>\n *\n * ** set the seventh stack item to msg.gas, gas to forward for staticcall **\n * 08 5a gas [0, 32, 0, 4, 28, caller, gas] <>\n *\n * ** set the eighth stack item to selector, \"what\" to store via mstore **\n * 09 63 push4\n * 10 aaf10f42 selector [0, 32, 0, 4, 28, caller, gas, 0xaaf10f42] <>\n *\n * ** set the ninth stack item to 0, \"where\" to store via mstore ***\n * 11 87 dup8 [0, 32, 0, 4, 28, caller, gas, 0xaaf10f42, 0] <>\n *\n * ** call mstore, consume 8 and 9 from the stack, place selector in memory **\n * 12 52 mstore [0, 32, 0, 4, 0, caller, gas] <0xaaf10f42>\n *\n * ** call staticcall, consume items 2 through 7, place address in memory **\n * 13 fa staticcall [0, 1 (if successful)]
\n *\n * ** flip success bit in second stack item to set to 0 **\n * 14 15 iszero [0, 0]
\n *\n * ** push a third 0 to the stack, position of address in memory **\n * 15 81 dup2 [0, 0, 0]
\n *\n * ** place address from position in memory onto third stack item **\n * 16 51 mload [0, 0, address] <>\n *\n * ** place address to fourth stack item for extcodesize to consume **\n * 17 80 dup1 [0, 0, address, address] <>\n *\n * ** get extcodesize on fourth stack item for extcodecopy **\n * 18 3b extcodesize [0, 0, address, size] <>\n *\n * ** dup and swap size for use by return at end of init code **\n * 19 80 dup1 [0, 0, address, size, size] <>\n * 20 93 swap4 [size, 0, address, size, 0] <>\n *\n * ** push code position 0 to stack and reorder stack items for extcodecopy **\n * 21 80 dup1 [size, 0, address, size, 0, 0] <>\n * 22 91 swap2 [size, 0, address, 0, 0, size] <>\n * 23 92 swap3 [size, 0, size, 0, 0, address] <>\n *\n * ** call extcodecopy, consume four items, clone runtime code to memory **\n * 24 3c extcodecopy [size, 0] \n *\n * ** return to deploy final code in memory **\n * 25 f3 return [] *deployed!*\n */\ncontract SocketDeployFactory is Ownable {\n using SafeTransferLib for ERC20;\n address public immutable disabledRouteAddress;\n\n mapping(address => address) _implementations;\n mapping(uint256 => bool) isDisabled;\n mapping(uint256 => bool) isRouteDeployed;\n mapping(address => bool) canDisableRoute;\n\n event Deployed(address _addr);\n event DisabledRoute(address _addr);\n event Destroyed(address _addr);\n error ContractAlreadyDeployed();\n error NothingToDestroy();\n error AlreadyDisabled();\n error CannotBeDisabled();\n error OnlyDisabler();\n\n constructor(address _owner, address disabledRoute) Ownable(_owner) {\n disabledRouteAddress = disabledRoute;\n canDisableRoute[_owner] = true;\n }\n\n modifier onlyDisabler() {\n if (!canDisableRoute[msg.sender]) {\n revert OnlyDisabler();\n }\n _;\n }\n\n function addDisablerAddress(address disabler) external onlyOwner {\n canDisableRoute[disabler] = true;\n }\n\n function removeDisablerAddress(address disabler) external onlyOwner {\n canDisableRoute[disabler] = false;\n }\n\n /**\n * @notice Deploys a route contract at predetermined location\n * @notice Caller must first deploy the route contract at another location and pass its address as implementation.\n * @param routeId route identifier\n * @param implementationContract address of deployed route contract. Its byte code will be copied to predetermined location.\n */\n function deploy(\n uint256 routeId,\n address implementationContract\n ) external onlyOwner returns (address) {\n // assign the initialization code for the socket contract.\n\n bytes memory initCode = (\n hex\"5860208158601c335a63aaf10f428752fa158151803b80938091923cf3\"\n );\n\n // determine the address of the socket contract.\n address routeContractAddress = _getContractAddress(routeId);\n\n if (isRouteDeployed[routeId]) {\n revert ContractAlreadyDeployed();\n }\n\n isRouteDeployed[routeId] = true;\n\n //first we deploy the code we want to deploy on a separate address\n // store the implementation to be retrieved by the socket contract.\n _implementations[routeContractAddress] = implementationContract;\n address addr;\n assembly {\n let encoded_data := add(0x20, initCode) // load initialization code.\n let encoded_size := mload(initCode) // load init code's length.\n addr := create2(0, encoded_data, encoded_size, routeId) // routeId is used as salt\n }\n require(\n addr == routeContractAddress,\n \"Failed to deploy the new socket contract.\"\n );\n emit Deployed(addr);\n return addr;\n }\n\n /**\n * @notice Destroy the route deployed at a location.\n * @param routeId route identifier to be destroyed.\n */\n function destroy(uint256 routeId) external onlyDisabler {\n // determine the address of the socket contract.\n _destroy(routeId);\n }\n\n /**\n * @notice Deploy a disabled contract at destroyed route to handle it gracefully.\n * @param routeId route identifier to be disabled.\n */\n function disableRoute(\n uint256 routeId\n ) external onlyDisabler returns (address) {\n return _disableRoute(routeId);\n }\n\n /**\n * @notice Destroy a list of routeIds\n * @param routeIds array of routeIds to be destroyed.\n */\n function multiDestroy(uint256[] calldata routeIds) external onlyDisabler {\n for (uint32 index = 0; index < routeIds.length; ) {\n _destroy(routeIds[index]);\n unchecked {\n ++index;\n }\n }\n }\n\n /**\n * @notice Deploy a disabled contract at list of routeIds.\n * @param routeIds array of routeIds to be disabled.\n */\n function multiDisableRoute(\n uint256[] calldata routeIds\n ) external onlyDisabler {\n for (uint32 index = 0; index < routeIds.length; ) {\n _disableRoute(routeIds[index]);\n unchecked {\n ++index;\n }\n }\n }\n\n /**\n * @dev External view function for calculating a socket contract address\n * given a particular routeId.\n */\n function getContractAddress(\n uint256 routeId\n ) external view returns (address) {\n // determine the address of the socket contract.\n return _getContractAddress(routeId);\n }\n\n //those two functions are getting called by the socket Contract\n function getImplementation()\n external\n view\n returns (address implementation)\n {\n return _implementations[msg.sender];\n }\n\n function _disableRoute(uint256 routeId) internal returns (address) {\n // assign the initialization code for the socket contract.\n bytes memory initCode = (\n hex\"5860208158601c335a63aaf10f428752fa158151803b80938091923cf3\"\n );\n\n // determine the address of the socket contract.\n address routeContractAddress = _getContractAddress(routeId);\n\n if (!isRouteDeployed[routeId]) {\n revert CannotBeDisabled();\n }\n\n if (isDisabled[routeId]) {\n revert AlreadyDisabled();\n }\n\n isDisabled[routeId] = true;\n\n //first we deploy the code we want to deploy on a separate address\n // store the implementation to be retrieved by the socket contract.\n _implementations[routeContractAddress] = disabledRouteAddress;\n address addr;\n assembly {\n let encoded_data := add(0x20, initCode) // load initialization code.\n let encoded_size := mload(initCode) // load init code's length.\n addr := create2(0, encoded_data, encoded_size, routeId) // routeId is used as salt.\n }\n require(\n addr == routeContractAddress,\n \"Failed to deploy the new socket contract.\"\n );\n emit Deployed(addr);\n return addr;\n }\n\n function _destroy(uint256 routeId) internal {\n // determine the address of the socket contract.\n address routeContractAddress = _getContractAddress(routeId);\n\n if (!isRouteDeployed[routeId]) {\n revert NothingToDestroy();\n }\n ISocketBridgeBase(routeContractAddress).killme();\n emit Destroyed(routeContractAddress);\n }\n\n /**\n * @dev Internal view function for calculating a socket contract address\n * given a particular routeId.\n */\n function _getContractAddress(\n uint256 routeId\n ) internal view returns (address) {\n // determine the address of the socket contract.\n\n bytes memory initCode = (\n hex\"5860208158601c335a63aaf10f428752fa158151803b80938091923cf3\"\n );\n return\n address(\n uint160( // downcast to match the address type.\n uint256( // convert to uint to truncate upper digits.\n keccak256( // compute the CREATE2 hash using 4 inputs.\n abi.encodePacked( // pack all inputs to the hash together.\n hex\"ff\", // start with 0xff to distinguish from RLP.\n address(this), // this contract will be the caller.\n routeId, // the routeId is used as salt.\n keccak256(abi.encodePacked(initCode)) // the init code hash.\n )\n )\n )\n )\n );\n }\n\n /**\n * @notice Rescues the ERC20 token to an address\n this is a restricted function to be called by only socketGatewayOwner\n * @dev as this is a restricted to socketGatewayOwner, ensure the userAddress is a known address\n * @param token address of the ERC20 token being rescued\n * @param userAddress address to which ERC20 is to be rescued\n * @param amount amount of ERC20 tokens being rescued\n */\n function rescueFunds(\n address token,\n address userAddress,\n uint256 amount\n ) external onlyOwner {\n ERC20(token).safeTransfer(userAddress, amount);\n }\n\n /**\n * @notice Rescues the native balance to an address\n this is a restricted function to be called by only socketGatewayOwner\n * @dev as this is a restricted to socketGatewayOwner, ensure the userAddress is a known address\n * @param userAddress address to which native-balance is to be rescued\n * @param amount amount of native-balance being rescued\n */\n function rescueEther(\n address payable userAddress,\n uint256 amount\n ) external onlyOwner {\n userAddress.transfer(amount);\n }\n}\n"},"src/interfaces/ISocketController.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/**\n * @title ISocketController\n * @notice Interface for SocketController functions.\n * @dev functions can be added here for invocation from external contracts or off-chain\n * only restriction is that this should have functions to manage controllers\n * @author Socket dot tech.\n */\ninterface ISocketController {\n /**\n * @notice Add controller to the socketGateway\n This is a restricted function to be called by only socketGatewayOwner\n * @dev ensure controllerAddress is a verified controller implementation address\n * @param _controllerAddress The address of controller implementation contract deployed\n * @return Id of the controller added to the controllers-mapping in socketGateway storage\n */\n function addController(\n address _controllerAddress\n ) external returns (uint32);\n\n /**\n * @notice disable controller by setting ZeroAddress to the entry in controllers-mapping\n identified by controllerId as key.\n This is a restricted function to be called by only socketGatewayOwner\n * @param _controllerId The Id of controller-implementation in the controllers mapping\n */\n function disableController(uint32 _controllerId) external;\n\n /**\n * @notice Get controllerImplementation address mapped to the controllerId\n * @param _controllerId controllerId is the key in the mapping for controllers\n * @return controller-implementation address\n */\n function getController(uint32 _controllerId) external returns (address);\n}\n"},"lib/solmate/src/utils/SafeTransferLib.sol":{"content":"// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\nimport {ERC20} from \"../tokens/ERC20.sol\";\n\n/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.\n/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)\n/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.\n/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.\nlibrary SafeTransferLib {\n /*//////////////////////////////////////////////////////////////\n ETH OPERATIONS\n //////////////////////////////////////////////////////////////*/\n\n function safeTransferETH(address to, uint256 amount) internal {\n bool success;\n\n /// @solidity memory-safe-assembly\n assembly {\n // Transfer the ETH and store if it succeeded or not.\n success := call(gas(), to, amount, 0, 0, 0, 0)\n }\n\n require(success, \"ETH_TRANSFER_FAILED\");\n }\n\n /*//////////////////////////////////////////////////////////////\n ERC20 OPERATIONS\n //////////////////////////////////////////////////////////////*/\n\n function safeTransferFrom(\n ERC20 token,\n address from,\n address to,\n uint256 amount\n ) internal {\n bool success;\n\n /// @solidity memory-safe-assembly\n assembly {\n // Get a pointer to some free memory.\n let freeMemoryPointer := mload(0x40)\n\n // Write the abi-encoded calldata into memory, beginning with the function selector.\n mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)\n mstore(add(freeMemoryPointer, 4), from) // Append the \"from\" argument.\n mstore(add(freeMemoryPointer, 36), to) // Append the \"to\" argument.\n mstore(add(freeMemoryPointer, 68), amount) // Append the \"amount\" argument.\n\n success := and(\n // Set success to whether the call reverted, if not we check it either\n // returned exactly 1 (can't just be non-zero data), or had no return data.\n or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),\n // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.\n // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.\n // Counterintuitively, this call must be positioned second to the or() call in the\n // surrounding and() call or else returndatasize() will be zero during the computation.\n call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)\n )\n }\n\n require(success, \"TRANSFER_FROM_FAILED\");\n }\n\n function safeTransfer(\n ERC20 token,\n address to,\n uint256 amount\n ) internal {\n bool success;\n\n /// @solidity memory-safe-assembly\n assembly {\n // Get a pointer to some free memory.\n let freeMemoryPointer := mload(0x40)\n\n // Write the abi-encoded calldata into memory, beginning with the function selector.\n mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)\n mstore(add(freeMemoryPointer, 4), to) // Append the \"to\" argument.\n mstore(add(freeMemoryPointer, 36), amount) // Append the \"amount\" argument.\n\n success := and(\n // Set success to whether the call reverted, if not we check it either\n // returned exactly 1 (can't just be non-zero data), or had no return data.\n or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),\n // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.\n // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.\n // Counterintuitively, this call must be positioned second to the or() call in the\n // surrounding and() call or else returndatasize() will be zero during the computation.\n call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)\n )\n }\n\n require(success, \"TRANSFER_FAILED\");\n }\n\n function safeApprove(\n ERC20 token,\n address to,\n uint256 amount\n ) internal {\n bool success;\n\n /// @solidity memory-safe-assembly\n assembly {\n // Get a pointer to some free memory.\n let freeMemoryPointer := mload(0x40)\n\n // Write the abi-encoded calldata into memory, beginning with the function selector.\n mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)\n mstore(add(freeMemoryPointer, 4), to) // Append the \"to\" argument.\n mstore(add(freeMemoryPointer, 36), amount) // Append the \"amount\" argument.\n\n success := and(\n // Set success to whether the call reverted, if not we check it either\n // returned exactly 1 (can't just be non-zero data), or had no return data.\n or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),\n // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.\n // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.\n // Counterintuitively, this call must be positioned second to the or() call in the\n // surrounding and() call or else returndatasize() will be zero during the computation.\n call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)\n )\n }\n\n require(success, \"APPROVE_FAILED\");\n }\n}\n"},"src/controllers/BaseController.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {ISocketRequest} from \"../interfaces/ISocketRequest.sol\";\nimport {ISocketRoute} from \"../interfaces/ISocketRoute.sol\";\n\n/// @title BaseController Controller\n/// @notice Base contract for all controller contracts\nabstract contract BaseController {\n /// @notice Address used to identify if it is a native token transfer or not\n address public immutable NATIVE_TOKEN_ADDRESS =\n address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);\n\n /// @notice Address used to identify if it is a Zero address\n address public immutable NULL_ADDRESS = address(0);\n\n /// @notice FunctionSelector used to delegatecall from swap to the function of bridge router implementation\n bytes4 public immutable BRIDGE_AFTER_SWAP_SELECTOR =\n bytes4(keccak256(\"bridgeAfterSwap(uint256,bytes)\"));\n\n /// @notice immutable variable to store the socketGateway address\n address public immutable socketGatewayAddress;\n\n /// @notice immutable variable with instance of SocketRoute to access route functions\n ISocketRoute public immutable socketRoute;\n\n /**\n * @notice Construct the base for all controllers.\n * @param _socketGatewayAddress Socketgateway address, an immutable variable to set.\n * @notice initialize the immutable variables of SocketRoute, SocketGateway\n */\n constructor(address _socketGatewayAddress) {\n socketGatewayAddress = _socketGatewayAddress;\n socketRoute = ISocketRoute(_socketGatewayAddress);\n }\n\n /**\n * @notice Construct the base for all BridgeImplementations.\n * @param routeId routeId mapped to the routrImplementation\n * @param data transactionData generated with arguments of bridgeRequest (offchain or by caller)\n * @return returns the bytes response of the route execution (bridging, refuel or swap executions)\n */\n function _executeRoute(\n uint32 routeId,\n bytes memory data\n ) internal returns (bytes memory) {\n (bool success, bytes memory result) = socketRoute\n .getRoute(routeId)\n .delegatecall(data);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n return result;\n }\n}\n"},"src/interfaces/ISocketRoute.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/**\n * @title ISocketRoute\n * @notice Interface for routeManagement functions in SocketGateway.\n * @author Socket dot tech.\n */\ninterface ISocketRoute {\n /**\n * @notice Add route to the socketGateway\n This is a restricted function to be called by only socketGatewayOwner\n * @dev ensure routeAddress is a verified bridge or middleware implementation address\n * @param routeAddress The address of bridge or middleware implementation contract deployed\n * @return Id of the route added to the routes-mapping in socketGateway storage\n */\n function addRoute(address routeAddress) external returns (uint256);\n\n /**\n * @notice disable a route by setting ZeroAddress to the entry in routes-mapping\n identified by routeId as key.\n This is a restricted function to be called by only socketGatewayOwner\n * @param routeId The Id of route-implementation in the routes mapping\n */\n function disableRoute(uint32 routeId) external;\n\n /**\n * @notice Get routeImplementation address mapped to the routeId\n * @param routeId routeId is the key in the mapping for routes\n * @return route-implementation address\n */\n function getRoute(uint32 routeId) external view returns (address);\n}\n"},"src/SocketGatewayDeployment.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\npragma experimental ABIEncoderV2;\n\nimport \"./utils/Ownable.sol\";\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {LibUtil} from \"./libraries/LibUtil.sol\";\nimport \"./libraries/LibBytes.sol\";\nimport {ISocketRoute} from \"./interfaces/ISocketRoute.sol\";\nimport {ISocketRequest} from \"./interfaces/ISocketRequest.sol\";\nimport {ISocketGateway} from \"./interfaces/ISocketGateway.sol\";\nimport {IncorrectBridgeRatios, ZeroAddressNotAllowed, ArrayLengthMismatch} from \"./errors/SocketErrors.sol\";\n\n/// @title SocketGatewayContract\n/// @notice Socketgateway is a contract with entrypoint functions for all interactions with socket liquidity layer\n/// @author Socket Team\ncontract SocketGateway is Ownable {\n using LibBytes for bytes;\n using LibBytes for bytes4;\n using SafeTransferLib for ERC20;\n\n /// @notice FunctionSelector used to delegatecall from swap to the function of bridge router implementation\n bytes4 public immutable BRIDGE_AFTER_SWAP_SELECTOR =\n bytes4(keccak256(\"bridgeAfterSwap(uint256,bytes)\"));\n\n /// @notice storage variable to keep track of total number of routes registered in socketgateway\n uint32 public routesCount = 385;\n\n /// @notice storage variable to keep track of total number of controllers registered in socketgateway\n uint32 public controllerCount;\n\n address public immutable disabledRouteAddress;\n\n uint256 public constant CENT_PERCENT = 100e18;\n\n /// @notice storage mapping for route implementation addresses\n mapping(uint32 => address) public routes;\n\n /// storage mapping for controller implemenation addresses\n mapping(uint32 => address) public controllers;\n\n // Events ------------------------------------------------------------------------------------------------------->\n\n /// @notice Event emitted when a router is added to socketgateway\n event NewRouteAdded(uint32 indexed routeId, address indexed route);\n\n /// @notice Event emitted when a route is disabled\n event RouteDisabled(uint32 indexed routeId);\n\n /// @notice Event emitted when ownership transfer is requested by socket-gateway-owner\n event OwnershipTransferRequested(\n address indexed _from,\n address indexed _to\n );\n\n /// @notice Event emitted when a controller is added to socketgateway\n event ControllerAdded(\n uint32 indexed controllerId,\n address indexed controllerAddress\n );\n\n /// @notice Event emitted when a controller is disabled\n event ControllerDisabled(uint32 indexed controllerId);\n\n constructor(address _owner, address _disabledRoute) Ownable(_owner) {\n disabledRouteAddress = _disabledRoute;\n }\n\n // Able to receive ether\n // solhint-disable-next-line no-empty-blocks\n receive() external payable {}\n\n /*******************************************\n * EXTERNAL AND PUBLIC FUNCTIONS *\n *******************************************/\n\n /**\n * @notice executes functions in the routes identified using routeId and functionSelectorData\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped\n * @dev ensure the data in routeData to be built using the function-selector defined as a\n * constant in the route implementation contract\n * @param routeId route identifier\n * @param routeData functionSelectorData generated using the function-selector defined in the route Implementation\n */\n function executeRoute(\n uint32 routeId,\n bytes calldata routeData\n ) external payable returns (bytes memory) {\n (bool success, bytes memory result) = addressAt(routeId).delegatecall(\n routeData\n );\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n return result;\n }\n\n /**\n * @notice swaps a token on sourceChain and split it across multiple bridge-recipients\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being swapped\n * @dev ensure the swap-data and bridge-data is generated using the function-selector defined as a constant in the implementation address\n * @param swapMultiBridgeRequest request\n */\n function swapAndMultiBridge(\n ISocketRequest.SwapMultiBridgeRequest calldata swapMultiBridgeRequest\n ) external payable {\n uint256 requestLength = swapMultiBridgeRequest.bridgeRouteIds.length;\n\n if (\n requestLength != swapMultiBridgeRequest.bridgeImplDataItems.length\n ) {\n revert ArrayLengthMismatch();\n }\n uint256 ratioAggregate;\n for (uint256 index = 0; index < requestLength; ) {\n ratioAggregate += swapMultiBridgeRequest.bridgeRatios[index];\n }\n\n if (ratioAggregate != CENT_PERCENT) {\n revert IncorrectBridgeRatios();\n }\n\n (bool swapSuccess, bytes memory swapResult) = addressAt(\n swapMultiBridgeRequest.swapRouteId\n ).delegatecall(swapMultiBridgeRequest.swapImplData);\n\n if (!swapSuccess) {\n assembly {\n revert(add(swapResult, 32), mload(swapResult))\n }\n }\n\n uint256 amountReceivedFromSwap = abi.decode(swapResult, (uint256));\n\n uint256 bridgedAmount;\n\n for (uint256 index = 0; index < requestLength; ) {\n uint256 bridgingAmount;\n\n // if it is the last bridge request, bridge the remaining amount\n if (index == requestLength - 1) {\n bridgingAmount = amountReceivedFromSwap - bridgedAmount;\n } else {\n // bridging amount is the multiplication of bridgeRatio and amountReceivedFromSwap\n bridgingAmount =\n (amountReceivedFromSwap *\n swapMultiBridgeRequest.bridgeRatios[index]) /\n (CENT_PERCENT);\n }\n\n // update the bridged amount, this would be used for computation for last bridgeRequest\n bridgedAmount += bridgingAmount;\n\n bytes memory bridgeImpldata = abi.encodeWithSelector(\n BRIDGE_AFTER_SWAP_SELECTOR,\n bridgingAmount,\n swapMultiBridgeRequest.bridgeImplDataItems[index]\n );\n\n (bool bridgeSuccess, bytes memory bridgeResult) = addressAt(\n swapMultiBridgeRequest.bridgeRouteIds[index]\n ).delegatecall(bridgeImpldata);\n\n if (!bridgeSuccess) {\n assembly {\n revert(add(bridgeResult, 32), mload(bridgeResult))\n }\n }\n\n unchecked {\n ++index;\n }\n }\n }\n\n /**\n * @notice sequentially executes functions in the routes identified using routeId and functionSelectorData\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped\n * @dev ensure the data in each dataItem to be built using the function-selector defined as a\n * constant in the route implementation contract\n * @param routeIds a list of route identifiers\n * @param dataItems a list of functionSelectorData generated using the function-selector defined in the route Implementation\n */\n function executeRoutes(\n uint32[] calldata routeIds,\n bytes[] calldata dataItems\n ) external payable {\n uint256 routeIdslength = routeIds.length;\n if (routeIdslength != dataItems.length) revert ArrayLengthMismatch();\n for (uint256 index = 0; index < routeIdslength; ) {\n (bool success, bytes memory result) = addressAt(routeIds[index])\n .delegatecall(dataItems[index]);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n unchecked {\n ++index;\n }\n }\n }\n\n /**\n * @notice execute a controller function identified using the controllerId in the request\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped\n * @dev ensure the data in request to be built using the function-selector defined as a\n * constant in the controller implementation contract\n * @param socketControllerRequest socketControllerRequest with controllerId to identify the\n * controllerAddress and byteData constructed using functionSelector\n * of the function being invoked\n * @return bytes data received from the call delegated to controller\n */\n function executeController(\n ISocketGateway.SocketControllerRequest calldata socketControllerRequest\n ) external payable returns (bytes memory) {\n (bool success, bytes memory result) = controllers[\n socketControllerRequest.controllerId\n ].delegatecall(socketControllerRequest.data);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n return result;\n }\n\n /**\n * @notice sequentially executes all controller requests\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped\n * @dev ensure the data in each controller-request to be built using the function-selector defined as a\n * constant in the controller implementation contract\n * @param controllerRequests a list of socketControllerRequest\n * Each controllerRequest contains controllerId to identify the controllerAddress and\n * byteData constructed using functionSelector of the function being invoked\n */\n function executeControllers(\n ISocketGateway.SocketControllerRequest[] calldata controllerRequests\n ) external payable {\n for (uint32 index = 0; index < controllerRequests.length; ) {\n (bool success, bytes memory result) = controllers[\n controllerRequests[index].controllerId\n ].delegatecall(controllerRequests[index].data);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n unchecked {\n ++index;\n }\n }\n }\n\n /**************************************\n * ADMIN FUNCTIONS *\n **************************************/\n\n /**\n * @notice Add route to the socketGateway\n This is a restricted function to be called by only socketGatewayOwner\n * @dev ensure routeAddress is a verified bridge or middleware implementation address\n * @param routeAddress The address of bridge or middleware implementation contract deployed\n * @return Id of the route added to the routes-mapping in socketGateway storage\n */\n function addRoute(\n address routeAddress\n ) external onlyOwner returns (uint32) {\n uint32 routeId = routesCount;\n routes[routeId] = routeAddress;\n\n routesCount += 1;\n\n emit NewRouteAdded(routeId, routeAddress);\n\n return routeId;\n }\n\n /**\n * @notice Give Infinite or 0 approval to bridgeRoute for the tokenAddress\n This is a restricted function to be called by only socketGatewayOwner\n */\n\n function setApprovalForRouters(\n address[] memory routeAddresses,\n address[] memory tokenAddresses,\n bool isMax\n ) external onlyOwner {\n for (uint32 index = 0; index < routeAddresses.length; ) {\n ERC20(tokenAddresses[index]).approve(\n routeAddresses[index],\n isMax ? type(uint256).max : 0\n );\n unchecked {\n ++index;\n }\n }\n }\n\n /**\n * @notice Add controller to the socketGateway\n This is a restricted function to be called by only socketGatewayOwner\n * @dev ensure controllerAddress is a verified controller implementation address\n * @param controllerAddress The address of controller implementation contract deployed\n * @return Id of the controller added to the controllers-mapping in socketGateway storage\n */\n function addController(\n address controllerAddress\n ) external onlyOwner returns (uint32) {\n uint32 controllerId = controllerCount;\n\n controllers[controllerId] = controllerAddress;\n\n controllerCount += 1;\n\n emit ControllerAdded(controllerId, controllerAddress);\n\n return controllerId;\n }\n\n /**\n * @notice disable controller by setting ZeroAddress to the entry in controllers-mapping\n identified by controllerId as key.\n This is a restricted function to be called by only socketGatewayOwner\n * @param controllerId The Id of controller-implementation in the controllers mapping\n */\n function disableController(uint32 controllerId) public onlyOwner {\n controllers[controllerId] = disabledRouteAddress;\n emit ControllerDisabled(controllerId);\n }\n\n /**\n * @notice disable a route by setting ZeroAddress to the entry in routes-mapping\n identified by routeId as key.\n This is a restricted function to be called by only socketGatewayOwner\n * @param routeId The Id of route-implementation in the routes mapping\n */\n function disableRoute(uint32 routeId) external onlyOwner {\n routes[routeId] = disabledRouteAddress;\n emit RouteDisabled(routeId);\n }\n\n /*******************************************\n * RESTRICTED RESCUE FUNCTIONS *\n *******************************************/\n\n /**\n * @notice Rescues the ERC20 token to an address\n this is a restricted function to be called by only socketGatewayOwner\n * @dev as this is a restricted to socketGatewayOwner, ensure the userAddress is a known address\n * @param token address of the ERC20 token being rescued\n * @param userAddress address to which ERC20 is to be rescued\n * @param amount amount of ERC20 tokens being rescued\n */\n function rescueFunds(\n address token,\n address userAddress,\n uint256 amount\n ) external onlyOwner {\n ERC20(token).safeTransfer(userAddress, amount);\n }\n\n /**\n * @notice Rescues the native balance to an address\n this is a restricted function to be called by only socketGatewayOwner\n * @dev as this is a restricted to socketGatewayOwner, ensure the userAddress is a known address\n * @param userAddress address to which native-balance is to be rescued\n * @param amount amount of native-balance being rescued\n */\n function rescueEther(\n address payable userAddress,\n uint256 amount\n ) external onlyOwner {\n userAddress.transfer(amount);\n }\n\n /*******************************************\n * VIEW FUNCTIONS *\n *******************************************/\n\n /**\n * @notice Get routeImplementation address mapped to the routeId\n * @param routeId routeId is the key in the mapping for routes\n * @return route-implementation address\n */\n function getRoute(uint32 routeId) public view returns (address) {\n return addressAt(routeId);\n }\n\n /**\n * @notice Get controllerImplementation address mapped to the controllerId\n * @param controllerId controllerId is the key in the mapping for controllers\n * @return controller-implementation address\n */\n function getController(uint32 controllerId) public view returns (address) {\n return controllers[controllerId];\n }\n\n function addressAt(uint32 routeId) public view returns (address) {\n if (routeId < 385) {\n if (routeId < 257) {\n if (routeId < 129) {\n if (routeId < 65) {\n if (routeId < 33) {\n if (routeId < 17) {\n if (routeId < 9) {\n if (routeId < 5) {\n if (routeId < 3) {\n if (routeId == 1) {\n return\n 0x8cd6BaCDAe46B449E2e5B34e348A4eD459c84D50;\n } else {\n return\n 0x31524750Cd865fF6A3540f232754Fb974c18585C;\n }\n } else {\n if (routeId == 3) {\n return\n 0xEd9b37342BeC8f3a2D7b000732ec87498aA6EC6a;\n } else {\n return\n 0xE8704Ef6211F8988Ccbb11badC89841808d66890;\n }\n }\n } else {\n if (routeId < 7) {\n if (routeId == 5) {\n return\n 0x9aFF58C460a461578C433e11C4108D1c4cF77761;\n } else {\n return\n 0x2D1733886cFd465B0B99F1492F40847495f334C5;\n }\n } else {\n if (routeId == 7) {\n return\n 0x715497Be4D130F04B8442F0A1F7a9312D4e54FC4;\n } else {\n return\n 0x90C8a40c38E633B5B0e0d0585b9F7FA05462CaaF;\n }\n }\n }\n } else {\n if (routeId < 13) {\n if (routeId < 11) {\n if (routeId == 9) {\n return\n 0xa402b70FCfF3F4a8422B93Ef58E895021eAdE4F6;\n } else {\n return\n 0xc1B718522E15CD42C4Ac385a929fc2B51f5B892e;\n }\n } else {\n if (routeId == 11) {\n return\n 0xa97bf2f7c26C43c010c349F52f5eA5dC49B2DD38;\n } else {\n return\n 0x969423d71b62C81d2f28d707364c9Dc4a0764c53;\n }\n }\n } else {\n if (routeId < 15) {\n if (routeId == 13) {\n return\n 0xF86729934C083fbEc8C796068A1fC60701Ea1207;\n } else {\n return\n 0xD7cC2571F5823caCA26A42690D2BE7803DD5393f;\n }\n } else {\n if (routeId == 15) {\n return\n 0x7c8837a279bbbf7d8B93413763176de9F65d5bB9;\n } else {\n return\n 0x13b81C27B588C07D04458ed7dDbdbD26D1e39bcc;\n }\n }\n }\n }\n } else {\n if (routeId < 25) {\n if (routeId < 21) {\n if (routeId < 19) {\n if (routeId == 17) {\n return\n 0x52560Ac678aFA1345D15474287d16Dc1eA3F78aE;\n } else {\n return\n 0x1E31e376551459667cd7643440c1b21CE69065A0;\n }\n } else {\n if (routeId == 19) {\n return\n 0xc57D822CB3288e7b97EF8f8af0EcdcD1B783529B;\n } else {\n return\n 0x2197A1D9Af24b4d6a64Bff95B4c29Fcd3Ff28C30;\n }\n }\n } else {\n if (routeId < 23) {\n if (routeId == 21) {\n return\n 0xE3700feAa5100041Bf6b7AdBA1f72f647809Fd00;\n } else {\n return\n 0xc02E8a0Fdabf0EeFCEA025163d90B5621E2b9948;\n }\n } else {\n if (routeId == 23) {\n return\n 0xF5144235E2926cAb3c69b30113254Fa632f72d62;\n } else {\n return\n 0xBa3F92313B00A1f7Bc53b2c24EB195c8b2F57682;\n }\n }\n }\n } else {\n if (routeId < 29) {\n if (routeId < 27) {\n if (routeId == 25) {\n return\n 0x77a6856fe1fFA5bEB55A1d2ED86E27C7c482CB76;\n } else {\n return\n 0x4826Ff4e01E44b1FCEFBfb38cd96687Eb7786b44;\n }\n } else {\n if (routeId == 27) {\n return\n 0x55FF3f5493cf5e80E76DEA7E327b9Cd8440Af646;\n } else {\n return\n 0xF430Db544bE9770503BE4aa51997aA19bBd5BA4f;\n }\n }\n } else {\n if (routeId < 31) {\n if (routeId == 29) {\n return\n 0x0f166446ce1484EE3B0663E7E67DF10F5D240115;\n } else {\n return\n 0x6365095D92537f242Db5EdFDd572745E72aC33d9;\n }\n } else {\n if (routeId == 31) {\n return\n 0x5c7BC93f06ce3eAe75ADf55E10e23d2c1dE5Bc65;\n } else {\n return\n 0xe46383bAD90d7A08197ccF08972e9DCdccCE9BA4;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 49) {\n if (routeId < 41) {\n if (routeId < 37) {\n if (routeId < 35) {\n if (routeId == 33) {\n return\n 0xf0f21710c071E3B728bdc4654c3c0b873aAaa308;\n } else {\n return\n 0x63Bc9ed3AcAAeB0332531C9fB03b0a2352E9Ff25;\n }\n } else {\n if (routeId == 35) {\n return\n 0xd1CE808625CB4007a1708824AE82CdB0ece57De9;\n } else {\n return\n 0x57BbB148112f4ba224841c3FE018884171004661;\n }\n }\n } else {\n if (routeId < 39) {\n if (routeId == 37) {\n return\n 0x037f7d6933036F34DFabd40Ff8e4D789069f92e3;\n } else {\n return\n 0xeF978c280915CfF3Dca4EDfa8932469e40ADA1e1;\n }\n } else {\n if (routeId == 39) {\n return\n 0x92ee9e071B13f7ecFD62B7DED404A16CBc223CD3;\n } else {\n return\n 0x94Ae539c186e41ed762271338Edf140414D1E442;\n }\n }\n }\n } else {\n if (routeId < 45) {\n if (routeId < 43) {\n if (routeId == 41) {\n return\n 0x30A64BBe4DdBD43dA2368EFd1eB2d80C10d84DAb;\n } else {\n return\n 0x3aEABf81c1Dc4c1b73d5B2a95410f126426FB596;\n }\n } else {\n if (routeId == 43) {\n return\n 0x25b08aB3D0C8ea4cC9d967b79688C6D98f3f563a;\n } else {\n return\n 0xea40cB15C9A3BBd27af6474483886F7c0c9AE406;\n }\n }\n } else {\n if (routeId < 47) {\n if (routeId == 45) {\n return\n 0x9580113Cc04e5a0a03359686304EF3A80b936Dd3;\n } else {\n return\n 0xD211c826d568957F3b66a3F4d9c5f68cCc66E619;\n }\n } else {\n if (routeId == 47) {\n return\n 0xCEE24D0635c4C56315d133b031984d4A6f509476;\n } else {\n return\n 0x3922e6B987983229798e7A20095EC372744d4D4c;\n }\n }\n }\n }\n } else {\n if (routeId < 57) {\n if (routeId < 53) {\n if (routeId < 51) {\n if (routeId == 49) {\n return\n 0x2d92D03413d296e1F31450479349757187F2a2b7;\n } else {\n return\n 0x0fe5308eE90FC78F45c89dB6053eA859097860CA;\n }\n } else {\n if (routeId == 51) {\n return\n 0x08Ba68e067C0505bAF0C1311E0cFB2B1B59b969c;\n } else {\n return\n 0x9bee5DdDF75C24897374f92A534B7A6f24e97f4a;\n }\n }\n } else {\n if (routeId < 55) {\n if (routeId == 53) {\n return\n 0x1FC5A90B232208704B930c1edf82FFC6ACc02734;\n } else {\n return\n 0x5b1B0417cb44c761C2a23ee435d011F0214b3C85;\n }\n } else {\n if (routeId == 55) {\n return\n 0x9d70cDaCA12A738C283020760f449D7816D592ec;\n } else {\n return\n 0x95a23b9CB830EcCFDDD5dF56A4ec665e3381Fa12;\n }\n }\n }\n } else {\n if (routeId < 61) {\n if (routeId < 59) {\n if (routeId == 57) {\n return\n 0x483a957Cf1251c20e096C35c8399721D1200A3Fc;\n } else {\n return\n 0xb4AD39Cb293b0Ec7FEDa743442769A7FF04987CD;\n }\n } else {\n if (routeId == 59) {\n return\n 0x4C543AD78c1590D81BAe09Fc5B6Df4132A2461d0;\n } else {\n return\n 0x471d5E5195c563902781734cfe1FF3981F8B6c86;\n }\n }\n } else {\n if (routeId < 63) {\n if (routeId == 61) {\n return\n 0x1B12a54B5E606D95B8B8D123c9Cb09221Ee37584;\n } else {\n return\n 0xE4127cC550baC433646a7D998775a84daC16c7f3;\n }\n } else {\n if (routeId == 63) {\n return\n 0xecb1b55AB12E7dd788D585c6C5cD61B5F87be836;\n } else {\n return\n 0xf91ef487C5A1579f70601b6D347e19756092eEBf;\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 97) {\n if (routeId < 81) {\n if (routeId < 73) {\n if (routeId < 69) {\n if (routeId < 67) {\n if (routeId == 65) {\n return\n 0x34a16a7e9BADEEFD4f056310cbE0b1423Fa1b760;\n } else {\n return\n 0x60E10E80c7680f429dBbC232830BEcd3D623c4CF;\n }\n } else {\n if (routeId == 67) {\n return\n 0x66465285B8D65362A1d86CE00fE2bE949Fd6debF;\n } else {\n return\n 0x5aB231B7e1A3A74a48f67Ab7bde5Cdd4267022E0;\n }\n }\n } else {\n if (routeId < 71) {\n if (routeId == 69) {\n return\n 0x3A1C3633eE79d43366F5c67802a746aFD6b162Ba;\n } else {\n return\n 0x0C4BfCbA8dC3C811437521a80E81e41DAF479039;\n }\n } else {\n if (routeId == 71) {\n return\n 0x6caf25d2e139C5431a1FA526EAf8d73ff2e6252C;\n } else {\n return\n 0x74ad21e09FDa68638CE14A3009A79B6D16574257;\n }\n }\n }\n } else {\n if (routeId < 77) {\n if (routeId < 75) {\n if (routeId == 73) {\n return\n 0xD4923A61008894b99cc1CD3407eF9524f02aA0Ca;\n } else {\n return\n 0x6F159b5EB823BD415886b9271aA2A723a00a1987;\n }\n } else {\n if (routeId == 75) {\n return\n 0x742a8aA42E7bfB4554dE30f4Fb07FFb6f2068863;\n } else {\n return\n 0x4AE9702d3360400E47B446e76DE063ACAb930101;\n }\n }\n } else {\n if (routeId < 79) {\n if (routeId == 77) {\n return\n 0x0E19a0a44ddA7dAD854ec5Cc867d16869c4E80F4;\n } else {\n return\n 0xE021A51968f25148F726E326C88d2556c5647557;\n }\n } else {\n if (routeId == 79) {\n return\n 0x64287BDDDaeF4d94E4599a3D882bed29E6Ada4B6;\n } else {\n return\n 0xcBB57Fd2e19cc7e9D444d5b4325A2F1047d0C73f;\n }\n }\n }\n }\n } else {\n if (routeId < 89) {\n if (routeId < 85) {\n if (routeId < 83) {\n if (routeId == 81) {\n return\n 0x373DE80DF7D82cFF6D76F29581b360C56331e957;\n } else {\n return\n 0x0466356E131AD61596a51F86BAd1C03A328960D8;\n }\n } else {\n if (routeId == 83) {\n return\n 0x01726B960992f1b74311b248E2a922fC707d43A6;\n } else {\n return\n 0x2E21bdf9A4509b89795BCE7E132f248a75814CEc;\n }\n }\n } else {\n if (routeId < 87) {\n if (routeId == 85) {\n return\n 0x769512b23aEfF842379091d3B6E4B5456F631D42;\n } else {\n return\n 0xe7eD9be946a74Ec19325D39C6EEb57887ccB2B0D;\n }\n } else {\n if (routeId == 87) {\n return\n 0xc4D01Ec357c2b511d10c15e6b6974380F0E62e67;\n } else {\n return\n 0x5bC49CC9dD77bECF2fd3A3C55611e84E69AFa3AE;\n }\n }\n }\n } else {\n if (routeId < 93) {\n if (routeId < 91) {\n if (routeId == 89) {\n return\n 0x48bcD879954fA14e7DbdAeb56F79C1e9DDcb69ec;\n } else {\n return\n 0xE929bDde21b462572FcAA4de6F49B9D3246688D0;\n }\n } else {\n if (routeId == 91) {\n return\n 0x85Aae300438222f0e3A9Bc870267a5633A9438bd;\n } else {\n return\n 0x51f72E1096a81C55cd142d66d39B688C657f9Be8;\n }\n }\n } else {\n if (routeId < 95) {\n if (routeId == 93) {\n return\n 0x3A8a05BF68ac54B01E6C0f492abF97465F3d15f9;\n } else {\n return\n 0x145aA67133F0c2C36b9771e92e0B7655f0D59040;\n }\n } else {\n if (routeId == 95) {\n return\n 0xa030315d7DB11F9892758C9e7092D841e0ADC618;\n } else {\n return\n 0xdF1f8d81a3734bdDdEfaC6Ca1596E081e57c3044;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 113) {\n if (routeId < 105) {\n if (routeId < 101) {\n if (routeId < 99) {\n if (routeId == 97) {\n return\n 0xFF2833123B58aa05d04D7fb99f5FB768B2b435F8;\n } else {\n return\n 0xc8f09c1fD751C570233765f71b0e280d74e6e743;\n }\n } else {\n if (routeId == 99) {\n return\n 0x3026DA6Ceca2E5A57A05153653D9212FFAaA49d8;\n } else {\n return\n 0xdE68Ee703dE0D11f67B0cE5891cB4a903de6D160;\n }\n }\n } else {\n if (routeId < 103) {\n if (routeId == 101) {\n return\n 0xE23a7730e81FB4E87A6D0bd9f63EE77ac86C3DA4;\n } else {\n return\n 0x8b1DBe04aD76a7d8bC079cACd3ED4D99B897F4a0;\n }\n } else {\n if (routeId == 103) {\n return\n 0xBB227240FA459b69C6889B2b8cb1BE76F118061f;\n } else {\n return\n 0xC062b9b3f0dB28BB8afAfcD4d075729344114ffe;\n }\n }\n }\n } else {\n if (routeId < 109) {\n if (routeId < 107) {\n if (routeId == 105) {\n return\n 0x553188Aa45f5FDB83EC4Ca485982F8fC082480D1;\n } else {\n return\n 0x0109d83D746EaCb6d4014953D9E12d6ca85e330b;\n }\n } else {\n if (routeId == 107) {\n return\n 0x45B1bEd29812F5bf6711074ACD180B2aeB783AD9;\n } else {\n return\n 0xdA06eC8c19aea31D77F60299678Cba40E743e1aD;\n }\n }\n } else {\n if (routeId < 111) {\n if (routeId == 109) {\n return\n 0x3cC5235c97d975a9b4FD4501B3446c981ea3D855;\n } else {\n return\n 0xa1827267d6Bd989Ff38580aE3d9deff6Acf19163;\n }\n } else {\n if (routeId == 111) {\n return\n 0x3663CAA0433A3D4171b3581Cf2410702840A735A;\n } else {\n return\n 0x7575D0a7614F655BA77C74a72a43bbd4fA6246a3;\n }\n }\n }\n }\n } else {\n if (routeId < 121) {\n if (routeId < 117) {\n if (routeId < 115) {\n if (routeId == 113) {\n return\n 0x2516Defc18bc07089c5dAFf5eafD7B0EF64611E2;\n } else {\n return\n 0xfec5FF08E20fbc107a97Af2D38BD0025b84ee233;\n }\n } else {\n if (routeId == 115) {\n return\n 0x0FB5763a87242B25243e23D73f55945fE787523A;\n } else {\n return\n 0xe4C00db89678dBf8391f430C578Ca857Dd98aDE1;\n }\n }\n } else {\n if (routeId < 119) {\n if (routeId == 117) {\n return\n 0x8F2A22061F9F35E64f14523dC1A5f8159e6a21B7;\n } else {\n return\n 0x18e4b838ae966917E20E9c9c5Ad359cDD38303bB;\n }\n } else {\n if (routeId == 119) {\n return\n 0x61ACb1d3Dcb3e3429832A164Cc0fC9849fb75A4a;\n } else {\n return\n 0x7681e3c8e7A41DCA55C257cc0d1Ae757f5530E65;\n }\n }\n }\n } else {\n if (routeId < 125) {\n if (routeId < 123) {\n if (routeId == 121) {\n return\n 0x806a2AB9748C3D1DB976550890E3f528B7E8Faec;\n } else {\n return\n 0xBDb8A5DD52C2c239fbC31E9d43B763B0197028FF;\n }\n } else {\n if (routeId == 123) {\n return\n 0x474EC9203706010B9978D6bD0b105D36755e4848;\n } else {\n return\n 0x8dfd0D829b303F2239212E591a0F92a32880f36E;\n }\n }\n } else {\n if (routeId < 127) {\n if (routeId == 125) {\n return\n 0xad4BcE9745860B1adD6F1Bd34a916f050E4c82C2;\n } else {\n return\n 0xBC701115b9fe14bC8CC5934cdC92517173e308C4;\n }\n } else {\n if (routeId == 127) {\n return\n 0x0D1918d786Db8546a11aDeD475C98370E06f255E;\n } else {\n return\n 0xee44f57cD6936DB55B99163f3Df367B01EdA785a;\n }\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 193) {\n if (routeId < 161) {\n if (routeId < 145) {\n if (routeId < 137) {\n if (routeId < 133) {\n if (routeId < 131) {\n if (routeId == 129) {\n return\n 0x63044521fe5a1e488D7eD419cD0e35b7C24F2aa7;\n } else {\n return\n 0x410085E73BD85e90d97b84A68C125aDB9F91f85b;\n }\n } else {\n if (routeId == 131) {\n return\n 0x7913fe97E07C7A397Ec274Ab1d4E2622C88EC5D1;\n } else {\n return\n 0x977f9fE93c064DCf54157406DaABC3a722e8184C;\n }\n }\n } else {\n if (routeId < 135) {\n if (routeId == 133) {\n return\n 0xCD2236468722057cFbbABad2db3DEA9c20d5B01B;\n } else {\n return\n 0x17c7287A491cf5Ff81E2678cF2BfAE4333F6108c;\n }\n } else {\n if (routeId == 135) {\n return\n 0x354D9a5Dbf96c71B79a265F03B595C6Fdc04dadd;\n } else {\n return\n 0xb4e409EB8e775eeFEb0344f9eee884cc7ed21c69;\n }\n }\n }\n } else {\n if (routeId < 141) {\n if (routeId < 139) {\n if (routeId == 137) {\n return\n 0xa1a3c4670Ad69D9be4ab2D39D1231FEC2a63b519;\n } else {\n return\n 0x4589A22199870729C1be5CD62EE93BeD858113E6;\n }\n } else {\n if (routeId == 139) {\n return\n 0x8E7b864dB26Bd6C798C38d4Ba36EbA0d6602cF11;\n } else {\n return\n 0xA2D17C7260a4CB7b9854e89Fc367E80E87872a2d;\n }\n }\n } else {\n if (routeId < 143) {\n if (routeId == 141) {\n return\n 0xC7F0EDf0A1288627b0432304918A75e9084CBD46;\n } else {\n return\n 0xE4B4EF1f9A4aBFEdB371fA7a6143993B15d4df25;\n }\n } else {\n if (routeId == 143) {\n return\n 0xfe3D84A2Ef306FEBb5452441C9BDBb6521666F6A;\n } else {\n return\n 0x8A12B6C64121920110aE58F7cd67DfEc21c6a4C3;\n }\n }\n }\n }\n } else {\n if (routeId < 153) {\n if (routeId < 149) {\n if (routeId < 147) {\n if (routeId == 145) {\n return\n 0x76c4d9aFC4717a2BAac4e5f26CccF02351f7a3DA;\n } else {\n return\n 0xd4719BA550E397aeAcca1Ad2201c1ba69024FAAf;\n }\n } else {\n if (routeId == 147) {\n return\n 0x9646126Ce025224d1682C227d915a386efc0A1Fb;\n } else {\n return\n 0x4DD8Af2E3F2044842f0247920Bc4BABb636915ea;\n }\n }\n } else {\n if (routeId < 151) {\n if (routeId == 149) {\n return\n 0x8e8a327183Af0cf8C2ece9F0ed547C42A160D409;\n } else {\n return\n 0x9D49614CaE1C685C71678CA6d8CDF7584bfd0740;\n }\n } else {\n if (routeId == 151) {\n return\n 0x5a00ef257394cbc31828d48655E3d39e9c11c93d;\n } else {\n return\n 0xC9a2751b38d3dDD161A41Ca0135C5C6c09EC1d56;\n }\n }\n }\n } else {\n if (routeId < 157) {\n if (routeId < 155) {\n if (routeId == 153) {\n return\n 0x7e1c261640a525C94Ca4f8c25b48CF754DD83590;\n } else {\n return\n 0x409Fe24ba6F6BD5aF31C1aAf8059b986A3158233;\n }\n } else {\n if (routeId == 155) {\n return\n 0x704Cf5BFDADc0f55fDBb53B6ed8B582E018A72A2;\n } else {\n return\n 0x3982bF65d7d6E77E3b6661cd6F6468c247512737;\n }\n }\n } else {\n if (routeId < 159) {\n if (routeId == 157) {\n return\n 0x3982b9f26FFD67a13Ee371e2C0a9Da338BA70E7f;\n } else {\n return\n 0x6D834AB385900c1f49055D098e90264077FbC4f2;\n }\n } else {\n if (routeId == 159) {\n return\n 0x11FE5F70779A094B7166B391e1Fb73d422eF4e4d;\n } else {\n return\n 0xD347e4E47280d21F13B73D89c6d16f867D50DD13;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 177) {\n if (routeId < 169) {\n if (routeId < 165) {\n if (routeId < 163) {\n if (routeId == 161) {\n return\n 0xb6035eDD53DDA28d8B69b4ae9836E40C80306CD7;\n } else {\n return\n 0x54c884e6f5C7CcfeCA990396c520C858c922b6CA;\n }\n } else {\n if (routeId == 163) {\n return\n 0x5eA93E240b083d686558Ed607BC013d88057cE46;\n } else {\n return\n 0x4C7131eE812De685cBe4e2cCb033d46ecD46612E;\n }\n }\n } else {\n if (routeId < 167) {\n if (routeId == 165) {\n return\n 0xc1a5Be9F0c33D8483801D702111068669f81fF91;\n } else {\n return\n 0x9E5fAb91455Be5E5b2C05967E73F456c8118B1Fc;\n }\n } else {\n if (routeId == 167) {\n return\n 0x3d9A05927223E0DC2F382831770405885e22F0d8;\n } else {\n return\n 0x6303A011fB6063f5B1681cb5a9938EA278dc6128;\n }\n }\n }\n } else {\n if (routeId < 173) {\n if (routeId < 171) {\n if (routeId == 169) {\n return\n 0xe9c60795c90C66797e4c8E97511eA07CdAda32bE;\n } else {\n return\n 0xD56cC98e69A1e13815818b466a8aA6163d84234A;\n }\n } else {\n if (routeId == 171) {\n return\n 0x47EbB9D36a6e40895316cD894E4860D774E2c531;\n } else {\n return\n 0xA5EB293629410065d14a7B1663A67829b0618292;\n }\n }\n } else {\n if (routeId < 175) {\n if (routeId == 173) {\n return\n 0x1b3B4C8146F939cE00899db8B3ddeF0062b7E023;\n } else {\n return\n 0x257Bbc11653625EbfB6A8587eF4f4FBe49828EB3;\n }\n } else {\n if (routeId == 175) {\n return\n 0x44cc979C01b5bB1eAC21301E73C37200dFD06F59;\n } else {\n return\n 0x2972fDF43352225D82754C0174Ff853819D1ef2A;\n }\n }\n }\n }\n } else {\n if (routeId < 185) {\n if (routeId < 181) {\n if (routeId < 179) {\n if (routeId == 177) {\n return\n 0x3e54144f032648A04D62d79f7B4b93FF3aC2333b;\n } else {\n return\n 0x444016102dB8adbE73C3B6703a1ea7F2f75A510D;\n }\n } else {\n if (routeId == 179) {\n return\n 0xac079143f98a6eb744Fde34541ebF243DF5B5dED;\n } else {\n return\n 0xAe9010767Fb112d29d35CEdfba2b372Ad7A308d3;\n }\n }\n } else {\n if (routeId < 183) {\n if (routeId == 181) {\n return\n 0xfE0BCcF9cCC2265D5fB3450743f17DfE57aE1e56;\n } else {\n return\n 0x04ED8C0545716119437a45386B1d691C63234C7D;\n }\n } else {\n if (routeId == 183) {\n return\n 0x636c14013e531A286Bc4C848da34585f0bB73d59;\n } else {\n return\n 0x2Fa67fc7ECC5cAA01C653d3BFeA98ecc5db9C42A;\n }\n }\n }\n } else {\n if (routeId < 189) {\n if (routeId < 187) {\n if (routeId == 185) {\n return\n 0x23e9a0FC180818aA872D2079a985217017E97bd9;\n } else {\n return\n 0x79A95c3Ef81b3ae64ee03A9D5f73e570495F164E;\n }\n } else {\n if (routeId == 187) {\n return\n 0xa7EA0E88F04a84ba0ad1E396cb07Fa3fDAD7dF6D;\n } else {\n return\n 0xd23cA1278a2B01a3C0Ca1a00d104b11c1Ebe6f42;\n }\n }\n } else {\n if (routeId < 191) {\n if (routeId == 189) {\n return\n 0x707bc4a9FA2E349AED5df4e9f5440C15aA9D14Bd;\n } else {\n return\n 0x7E290F2dd539Ac6CE58d8B4C2B944931a1fD3612;\n }\n } else {\n if (routeId == 191) {\n return\n 0x707AA5503088Ce06Ba450B6470A506122eA5c8eF;\n } else {\n return\n 0xFbB3f7BF680deeb149f4E7BC30eA3DDfa68F3C3f;\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 225) {\n if (routeId < 209) {\n if (routeId < 201) {\n if (routeId < 197) {\n if (routeId < 195) {\n if (routeId == 193) {\n return\n 0xDE74aD8cCC3dbF14992f49Cf24f36855912f4934;\n } else {\n return\n 0x409BA83df7777F070b2B50a10a41DE2468d2a3B3;\n }\n } else {\n if (routeId == 195) {\n return\n 0x5CB7Be90A5DD7CfDa54e87626e254FE8C18255B4;\n } else {\n return\n 0x0A684fE12BC64fb72B59d0771a566F49BC090356;\n }\n }\n } else {\n if (routeId < 199) {\n if (routeId == 197) {\n return\n 0xDf30048d91F8FA2bCfC54952B92bFA8e161D3360;\n } else {\n return\n 0x050825Fff032a547C47061CF0696FDB0f65AEa5D;\n }\n } else {\n if (routeId == 199) {\n return\n 0xd55e671dAC1f03d366d8535073ada5DB2Aab1Ea2;\n } else {\n return\n 0x9470C704A9616c8Cd41c595Fcd2181B6fe2183C2;\n }\n }\n }\n } else {\n if (routeId < 205) {\n if (routeId < 203) {\n if (routeId == 201) {\n return\n 0x2D9ffD275181F5865d5e11CbB4ced1521C4dF9f1;\n } else {\n return\n 0x816d28Dec10ec95DF5334f884dE85cA6215918d8;\n }\n } else {\n if (routeId == 203) {\n return\n 0xd1f87267c4A43835E666dd69Df077e578A3b6299;\n } else {\n return\n 0x39E89Bde9DACbe5468C025dE371FbDa12bDeBAB1;\n }\n }\n } else {\n if (routeId < 207) {\n if (routeId == 205) {\n return\n 0x7b40A3207956ecad6686E61EfcaC48912FcD0658;\n } else {\n return\n 0x090cF10D793B1Efba9c7D76115878814B663859A;\n }\n } else {\n if (routeId == 207) {\n return\n 0x312A59c06E41327878F2063eD0e9c282C1DA3AfC;\n } else {\n return\n 0x4F1188f46236DD6B5de11Ebf2a9fF08716E7DeB6;\n }\n }\n }\n }\n } else {\n if (routeId < 217) {\n if (routeId < 213) {\n if (routeId < 211) {\n if (routeId == 209) {\n return\n 0x0A6F9a3f4fA49909bBfb4339cbE12B42F53BbBeD;\n } else {\n return\n 0x01d13d7aCaCbB955B81935c80ffF31e14BdFa71f;\n }\n } else {\n if (routeId == 211) {\n return\n 0x691a14Fa6C7360422EC56dF5876f84d4eDD7f00A;\n } else {\n return\n 0x97Aad18d886d181a9c726B3B6aE15a0A69F5aF73;\n }\n }\n } else {\n if (routeId < 215) {\n if (routeId == 213) {\n return\n 0x2917241371D2099049Fa29432DC46735baEC33b4;\n } else {\n return\n 0x5F20F20F7890c2e383E29D4147C9695A371165f5;\n }\n } else {\n if (routeId == 215) {\n return\n 0xeC0a60e639958335662C5219A320cCEbb56C6077;\n } else {\n return\n 0x96d63CF5062975C09845d17ec672E10255866053;\n }\n }\n }\n } else {\n if (routeId < 221) {\n if (routeId < 219) {\n if (routeId == 217) {\n return\n 0xFF57429e57D383939CAB50f09ABBfB63C0e6c9AD;\n } else {\n return\n 0x18E393A7c8578fb1e235C242076E50013cDdD0d7;\n }\n } else {\n if (routeId == 219) {\n return\n 0xE7E5238AF5d61f52E9B4ACC025F713d1C0216507;\n } else {\n return\n 0x428401D4d0F25A2EE1DA4d5366cB96Ded425D9bD;\n }\n }\n } else {\n if (routeId < 223) {\n if (routeId == 221) {\n return\n 0x42E5733551ff1Ee5B48Aa9fc2B61Af9b58C812E6;\n } else {\n return\n 0x64Df9c7A0551B056d860Bc2419Ca4c1EF75320bE;\n }\n } else {\n if (routeId == 223) {\n return\n 0x46006925506145611bBf0263243D8627dAf26B0F;\n } else {\n return\n 0x8D64BE884314662804eAaB884531f5C50F4d500c;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 241) {\n if (routeId < 233) {\n if (routeId < 229) {\n if (routeId < 227) {\n if (routeId == 225) {\n return\n 0x157a62D92D07B5ce221A5429645a03bBaCE85373;\n } else {\n return\n 0xaF037D33e1F1F2F87309B425fe8a9d895Ef3722B;\n }\n } else {\n if (routeId == 227) {\n return\n 0x921D1154E494A2f7218a37ad7B17701f94b4B40e;\n } else {\n return\n 0xF282b4555186d8Dea51B8b3F947E1E0568d09bc4;\n }\n }\n } else {\n if (routeId < 231) {\n if (routeId == 229) {\n return\n 0xa794E2E1869765a4600b3DFd8a4ebcF16350f6B6;\n } else {\n return\n 0xFEFb048e20c5652F7940A49B1980E0125Ec4D358;\n }\n } else {\n if (routeId == 231) {\n return\n 0x220104b641971e9b25612a8F001bf48AbB23f1cF;\n } else {\n return\n 0xcB9D373Bb54A501B35dd3be5bF4Ba43cA31F7035;\n }\n }\n }\n } else {\n if (routeId < 237) {\n if (routeId < 235) {\n if (routeId == 233) {\n return\n 0x37D627F56e3FF36aC316372109ea82E03ac97DAc;\n } else {\n return\n 0x4E81355FfB4A271B4EA59ff78da2b61c7833161f;\n }\n } else {\n if (routeId == 235) {\n return\n 0xADd8D65cAF6Cc9ad73127B49E16eA7ac29d91e87;\n } else {\n return\n 0x630F9b95626487dfEAe3C97A44DB6C59cF35d996;\n }\n }\n } else {\n if (routeId < 239) {\n if (routeId == 237) {\n return\n 0x78CE2BC8238B679680A67FCB98C5A60E4ec17b2D;\n } else {\n return\n 0xA38D776028eD1310b9A6b086f67F788201762E21;\n }\n } else {\n if (routeId == 239) {\n return\n 0x7Bb5178827B76B86753Ed62a0d662c72cEcb1bD3;\n } else {\n return\n 0x4faC26f61C76eC5c3D43b43eDfAFF0736Ae0e3da;\n }\n }\n }\n }\n } else {\n if (routeId < 249) {\n if (routeId < 245) {\n if (routeId < 243) {\n if (routeId == 241) {\n return\n 0x791Bb49bfFA7129D6889FDB27744422Ac4571A85;\n } else {\n return\n 0x26766fFEbb5fa564777913A6f101dF019AB32afa;\n }\n } else {\n if (routeId == 243) {\n return\n 0x05e98E5e95b4ECBbbAf3258c3999Cc81ed8048Be;\n } else {\n return\n 0xC5c4621e52f1D6A1825A5ed4F95855401a3D9C6b;\n }\n }\n } else {\n if (routeId < 247) {\n if (routeId == 245) {\n return\n 0xfcb15f909BA7FC7Ea083503Fb4c1020203c107EB;\n } else {\n return\n 0xbD27603279d969c74f2486ad14E71080829DFd38;\n }\n } else {\n if (routeId == 247) {\n return\n 0xff2f756BcEcC1A55BFc09a30cc5F64720458cFCB;\n } else {\n return\n 0x3bfB968FEbC12F4e8420B2d016EfcE1E615f7246;\n }\n }\n }\n } else {\n if (routeId < 253) {\n if (routeId < 251) {\n if (routeId == 249) {\n return\n 0x982EE9Ffe23051A2ec945ed676D864fa8345222b;\n } else {\n return\n 0xe101899100785E74767d454FFF0131277BaD48d9;\n }\n } else {\n if (routeId == 251) {\n return\n 0x4F730C0c6b3B5B7d06ca511379f4Aa5BfB2E9525;\n } else {\n return\n 0x5499c36b365795e4e0Ef671aF6C2ce26D7c78265;\n }\n }\n } else {\n if (routeId < 255) {\n if (routeId == 253) {\n return\n 0x8AF51F7237Fc8fB2fc3E700488a94a0aC6Ad8b5a;\n } else {\n return\n 0xda8716df61213c0b143F2849785FB85928084857;\n }\n } else {\n if (routeId == 255) {\n return\n 0xF040Cf9b1ebD11Bf28e04e80740DF3DDe717e4f5;\n } else {\n return\n 0xB87ba32f759D14023C7520366B844dF7f0F036C2;\n }\n }\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 321) {\n if (routeId < 289) {\n if (routeId < 273) {\n if (routeId < 265) {\n if (routeId < 261) {\n if (routeId < 259) {\n if (routeId == 257) {\n return\n 0x0Edde681b8478F0c3194f468EdD2dB5e75c65CDD;\n } else {\n return\n 0x59C70900Fca06eE2aCE1BDd5A8D0Af0cc3BBA720;\n }\n } else {\n if (routeId == 259) {\n return\n 0x8041F0f180D17dD07087199632c45E17AeB0BAd5;\n } else {\n return\n 0x4fB4727064BA595995DD516b63b5921Df9B93aC6;\n }\n }\n } else {\n if (routeId < 263) {\n if (routeId == 261) {\n return\n 0x86e98b594565857eD098864F560915C0dAfd6Ea1;\n } else {\n return\n 0x70f8818E8B698EFfeCd86A513a4c87c0c380Bef6;\n }\n } else {\n if (routeId == 263) {\n return\n 0x78Ed227c8A897A21Da2875a752142dd80d865158;\n } else {\n return\n 0xd02A30BB5C3a8C51d2751A029a6fcfDE2Af9fbc6;\n }\n }\n }\n } else {\n if (routeId < 269) {\n if (routeId < 267) {\n if (routeId == 265) {\n return\n 0x0F00d5c5acb24e975e2a56730609f7F40aa763b8;\n } else {\n return\n 0xC3e2091edc2D3D9D98ba09269138b617B536834A;\n }\n } else {\n if (routeId == 267) {\n return\n 0xa6FbaF7F30867C9633908998ea8C3da28920E75C;\n } else {\n return\n 0xE6dDdcD41E2bBe8122AE32Ac29B8fbAB79CD21d9;\n }\n }\n } else {\n if (routeId < 271) {\n if (routeId == 269) {\n return\n 0x537aa8c1Ef6a8Eaf039dd6e1Eb67694a48195cE4;\n } else {\n return\n 0x96ABAC485fd2D0B03CF4a10df8BD58b8dED28300;\n }\n } else {\n if (routeId == 271) {\n return\n 0xda8e7D46d04Bd4F62705Cd80355BDB6d441DafFD;\n } else {\n return\n 0xbE50018E7a5c67E2e5f5414393e971CC96F293f2;\n }\n }\n }\n }\n } else {\n if (routeId < 281) {\n if (routeId < 277) {\n if (routeId < 275) {\n if (routeId == 273) {\n return\n 0xa1b3907D6CB542a4cbe2eE441EfFAA909FAb62C3;\n } else {\n return\n 0x6d08ee8511C0237a515013aC389e7B3968Cb1753;\n }\n } else {\n if (routeId == 275) {\n return\n 0x22faa5B5Fe43eAdbB52745e35a5cdA8bD5F96bbA;\n } else {\n return\n 0x7a673eB74D79e4868D689E7852abB5f93Ec2fD4b;\n }\n }\n } else {\n if (routeId < 279) {\n if (routeId == 277) {\n return\n 0x0b8531F8AFD4190b76F3e10deCaDb84c98b4d419;\n } else {\n return\n 0x78eABC743A93583DeE403D6b84795490e652216B;\n }\n } else {\n if (routeId == 279) {\n return\n 0x3A95D907b2a7a8604B59BccA08585F58Afe0Aa64;\n } else {\n return\n 0xf4271f0C8c9Af0F06A80b8832fa820ccE64FAda8;\n }\n }\n }\n } else {\n if (routeId < 285) {\n if (routeId < 283) {\n if (routeId == 281) {\n return\n 0x74b2DF841245C3748c0d31542e1335659a25C33b;\n } else {\n return\n 0xdFC99Fd0Ad7D16f30f295a5EEFcE029E04d0fa65;\n }\n } else {\n if (routeId == 283) {\n return\n 0xE992416b6aC1144eD8148a9632973257839027F6;\n } else {\n return\n 0x54ce55ba954E981BB1fd9399054B35Ce1f2C0816;\n }\n }\n } else {\n if (routeId < 287) {\n if (routeId == 285) {\n return\n 0xD4AB52f9e7E5B315Bd7471920baD04F405Ab1c38;\n } else {\n return\n 0x3670C990994d12837e95eE127fE2f06FD3E2104B;\n }\n } else {\n if (routeId == 287) {\n return\n 0xDcf190B09C47E4f551E30BBb79969c3FdEA1e992;\n } else {\n return\n 0xa65057B967B59677237e57Ab815B209744b9bc40;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 305) {\n if (routeId < 297) {\n if (routeId < 293) {\n if (routeId < 291) {\n if (routeId == 289) {\n return\n 0x6Efc86B40573e4C7F28659B13327D55ae955C483;\n } else {\n return\n 0x06BcC25CF8e0E72316F53631b3aA7134E9f73Ae0;\n }\n } else {\n if (routeId == 291) {\n return\n 0x710b6414E1D53882b1FCD3A168aD5Ccd435fc6D0;\n } else {\n return\n 0x5Ebb2C3d78c4e9818074559e7BaE7FCc99781DC1;\n }\n }\n } else {\n if (routeId < 295) {\n if (routeId == 293) {\n return\n 0xAf0a409c3AEe0bD08015cfb29D89E90b6e89A88F;\n } else {\n return\n 0x522559d8b99773C693B80cE06DF559036295Ce44;\n }\n } else {\n if (routeId == 295) {\n return\n 0xB65290A5Bae838aaa7825c9ECEC68041841a1B64;\n } else {\n return\n 0x801b8F2068edd5Bcb659E6BDa0c425909043C420;\n }\n }\n }\n } else {\n if (routeId < 301) {\n if (routeId < 299) {\n if (routeId == 297) {\n return\n 0x29b5F00515d093627E0B7bd0b5c8E84F6b4cDb87;\n } else {\n return\n 0x652839Ae74683cbF9f1293F1019D938F87464D3E;\n }\n } else {\n if (routeId == 299) {\n return\n 0x5Bc95dCebDDE9B79F2b6DC76121BC7936eF8D666;\n } else {\n return\n 0x90db359CEA62E53051158Ab5F99811C0a07Fe686;\n }\n }\n } else {\n if (routeId < 303) {\n if (routeId == 301) {\n return\n 0x2c3625EedadbDcDbB5330eb0d17b3C39ff269807;\n } else {\n return\n 0xC3f0324471b5c9d415acD625b8d8694a4e48e001;\n }\n } else {\n if (routeId == 303) {\n return\n 0x8C60e7E05fa0FfB6F720233736f245134685799d;\n } else {\n return\n 0x98fAF2c09aa4EBb995ad0B56152993E7291a500e;\n }\n }\n }\n }\n } else {\n if (routeId < 313) {\n if (routeId < 309) {\n if (routeId < 307) {\n if (routeId == 305) {\n return\n 0x802c1063a861414dFAEc16bacb81429FC0d40D6e;\n } else {\n return\n 0x11C4AeFCC0dC156f64195f6513CB1Fb3Be0Ae056;\n }\n } else {\n if (routeId == 307) {\n return\n 0xEff1F3258214E31B6B4F640b4389d55715C3Be2B;\n } else {\n return\n 0x47e379Abe8DDFEA4289aBa01235EFF7E93758fd7;\n }\n }\n } else {\n if (routeId < 311) {\n if (routeId == 309) {\n return\n 0x3CC26384c3eA31dDc8D9789e8872CeA6F20cD3ff;\n } else {\n return\n 0xEdd9EFa6c69108FAA4611097d643E20Ba0Ed1634;\n }\n } else {\n if (routeId == 311) {\n return\n 0xCb93525CA5f3D371F74F3D112bC19526740717B8;\n } else {\n return\n 0x7071E0124EB4438137e60dF1b8DD8Af1BfB362cF;\n }\n }\n }\n } else {\n if (routeId < 317) {\n if (routeId < 315) {\n if (routeId == 313) {\n return\n 0x4691096EB0b78C8F4b4A8091E5B66b18e1835c10;\n } else {\n return\n 0x8d953c9b2d1C2137CF95992079f3A77fCd793272;\n }\n } else {\n if (routeId == 315) {\n return\n 0xbdCc2A3Bf6e3Ba49ff86595e6b2b8D70d8368c92;\n } else {\n return\n 0x95E6948aB38c61b2D294E8Bd896BCc4cCC0713cf;\n }\n }\n } else {\n if (routeId < 319) {\n if (routeId == 317) {\n return\n 0x607b27C881fFEE4Cb95B1c5862FaE7224ccd0b4A;\n } else {\n return\n 0x09D28aFA166e566A2Ee1cB834ea8e78C7E627eD2;\n }\n } else {\n if (routeId == 319) {\n return\n 0x9c01449b38bDF0B263818401044Fb1401B29fDfA;\n } else {\n return\n 0x1F7723599bbB658c051F8A39bE2688388d22ceD6;\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 353) {\n if (routeId < 337) {\n if (routeId < 329) {\n if (routeId < 325) {\n if (routeId < 323) {\n if (routeId == 321) {\n return\n 0x52B71603f7b8A5d15B4482e965a0619aa3210194;\n } else {\n return\n 0x01c0f072CB210406653752FecFA70B42dA9173a2;\n }\n } else {\n if (routeId == 323) {\n return\n 0x3021142f021E943e57fc1886cAF58D06147D09A6;\n } else {\n return\n 0xe6f2AF38e76AB09Db59225d97d3E770942D3D842;\n }\n }\n } else {\n if (routeId < 327) {\n if (routeId == 325) {\n return\n 0x06a25554e5135F08b9e2eD1DEC1fc3CEd52e0B48;\n } else {\n return\n 0x71d75e670EE3511C8290C705E0620126B710BF8D;\n }\n } else {\n if (routeId == 327) {\n return\n 0x8b9cE142b80FeA7c932952EC533694b1DF9B3c54;\n } else {\n return\n 0xd7Be24f32f39231116B3fDc483C2A12E1521f73B;\n }\n }\n }\n } else {\n if (routeId < 333) {\n if (routeId < 331) {\n if (routeId == 329) {\n return\n 0xb40cafBC4797d4Ff64087E087F6D2e661f954CbE;\n } else {\n return\n 0xBdDCe7771EfEe81893e838f62204A4c76D72757e;\n }\n } else {\n if (routeId == 331) {\n return\n 0x5d3D299EA7Fd4F39AcDb336E26631Dfee41F9287;\n } else {\n return\n 0x6BfEE09E1Fc0684e0826A9A0dC1352a14B136FAC;\n }\n }\n } else {\n if (routeId < 335) {\n if (routeId == 333) {\n return\n 0xd0001bB8E2Cb661436093f96458a4358B5156E3c;\n } else {\n return\n 0x1867c6485CfD1eD448988368A22bfB17a7747293;\n }\n } else {\n if (routeId == 335) {\n return\n 0x8997EF9F95dF24aB67703AB6C262aABfeEBE33bD;\n } else {\n return\n 0x1e39E9E601922deD91BCFc8F78836302133465e2;\n }\n }\n }\n }\n } else {\n if (routeId < 345) {\n if (routeId < 341) {\n if (routeId < 339) {\n if (routeId == 337) {\n return\n 0x8A8ec6CeacFf502a782216774E5AF3421562C6ff;\n } else {\n return\n 0x3B8FC561df5415c8DC01e97Ee6E38435A8F9C40A;\n }\n } else {\n if (routeId == 339) {\n return\n 0xD5d5f5B37E67c43ceA663aEDADFFc3a93a2065B0;\n } else {\n return\n 0xCC8F55EC43B4f25013CE1946FBB740c43Be5B96D;\n }\n }\n } else {\n if (routeId < 343) {\n if (routeId == 341) {\n return\n 0x18f586E816eEeDbb57B8011239150367561B58Fb;\n } else {\n return\n 0xd0CD802B19c1a52501cb2f07d656e3Cd7B0Ce124;\n }\n } else {\n if (routeId == 343) {\n return\n 0xe0AeD899b39C6e4f2d83e4913a1e9e0cf6368abE;\n } else {\n return\n 0x0606e1b6c0f1A398C38825DCcc4678a7Cbc2737c;\n }\n }\n }\n } else {\n if (routeId < 349) {\n if (routeId < 347) {\n if (routeId == 345) {\n return\n 0x2d188e85b27d18EF80f16686EA1593ABF7Ed2A63;\n } else {\n return\n 0x64412292fA4A135a3300E24366E99ff59Db2eAc1;\n }\n } else {\n if (routeId == 347) {\n return\n 0x38b74c173f3733E8b90aAEf0e98B89791266149F;\n } else {\n return\n 0x36DAA49A79aaEF4E7a217A11530D3cCD84414124;\n }\n }\n } else {\n if (routeId < 351) {\n if (routeId == 349) {\n return\n 0x10f088FE2C88F90270E4449c46c8B1b232511d58;\n } else {\n return\n 0x4FeDbd25B58586838ABD17D10272697dF1dC3087;\n }\n } else {\n if (routeId == 351) {\n return\n 0x685278209248CB058E5cEe93e37f274A80Faf6eb;\n } else {\n return\n 0xDd9F8F1eeC3955f78168e2Fb2d1e808fa8A8f15b;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 369) {\n if (routeId < 361) {\n if (routeId < 357) {\n if (routeId < 355) {\n if (routeId == 353) {\n return\n 0x7392aEeFD5825aaC28817031dEEBbFaAA20983D9;\n } else {\n return\n 0x0Cc182555E00767D6FB8AD161A10d0C04C476d91;\n }\n } else {\n if (routeId == 355) {\n return\n 0x90E52837d56715c79FD592E8D58bFD20365798b2;\n } else {\n return\n 0x6F4451DE14049B6770ad5BF4013118529e68A40C;\n }\n }\n } else {\n if (routeId < 359) {\n if (routeId == 357) {\n return\n 0x89B97ef2aFAb9ed9c7f0FDb095d02E6840b52d9c;\n } else {\n return\n 0x92A5cC5C42d94d3e23aeB1214fFf43Db2B97759E;\n }\n } else {\n if (routeId == 359) {\n return\n 0x63ddc52F135A1dcBA831EAaC11C63849F018b739;\n } else {\n return\n 0x692A691533B571C2c54C1D7F8043A204b3d8120E;\n }\n }\n }\n } else {\n if (routeId < 365) {\n if (routeId < 363) {\n if (routeId == 361) {\n return\n 0x97c7492CF083969F61C6f302d45c8270391b921c;\n } else {\n return\n 0xDeFD2B8643553dAd19548eB14fd94A57F4B9e543;\n }\n } else {\n if (routeId == 363) {\n return\n 0x30645C04205cA3f670B67b02F971B088930ACB8C;\n } else {\n return\n 0xA6f80ed2d607Cd67aEB4109B64A0BEcc4D7d03CF;\n }\n }\n } else {\n if (routeId < 367) {\n if (routeId == 365) {\n return\n 0xBbbbC6c276eB3F7E674f2D39301509236001c42f;\n } else {\n return\n 0xC20E77d349FB40CE88eB01824e2873ad9f681f3C;\n }\n } else {\n if (routeId == 367) {\n return\n 0x5fCfD9a962De19294467C358C1FA55082285960b;\n } else {\n return\n 0x4D87BD6a0E4E5cc6332923cb3E85fC71b287F58A;\n }\n }\n }\n }\n } else {\n if (routeId < 377) {\n if (routeId < 373) {\n if (routeId < 371) {\n if (routeId == 369) {\n return\n 0x3AA5B757cd6Dde98214E56D57Dde7fcF0F7aB04E;\n } else {\n return\n 0xe28eFCE7192e11a2297f44059113C1fD6967b2d4;\n }\n } else {\n if (routeId == 371) {\n return\n 0x3251cAE10a1Cf246e0808D76ACC26F7B5edA0eE5;\n } else {\n return\n 0xbA2091cc9357Cf4c4F25D64F30d1b4Ba3A5a174B;\n }\n }\n } else {\n if (routeId < 375) {\n if (routeId == 373) {\n return\n 0x49c8e1Da9693692096F63C82D11b52d738566d55;\n } else {\n return\n 0xA0731615aB5FFF451031E9551367A4F7dB27b39c;\n }\n } else {\n if (routeId == 375) {\n return\n 0xFb214541888671AE1403CecC1D59763a12fc1609;\n } else {\n return\n 0x1D6bCB17642E2336405df73dF22F07688cAec020;\n }\n }\n }\n } else {\n if (routeId < 381) {\n if (routeId < 379) {\n if (routeId == 377) {\n return\n 0xfC9c0C7bfe187120fF7f4E21446161794A617a9e;\n } else {\n return\n 0xBa5bF37678EeE2dAB17AEf9D898153258252250E;\n }\n } else {\n if (routeId == 379) {\n return\n 0x7c55690bd2C9961576A32c02f8EB29ed36415Ec7;\n } else {\n return\n 0xcA40073E868E8Bc611aEc8Fe741D17E68Fe422f6;\n }\n }\n } else {\n if (routeId < 383) {\n if (routeId == 381) {\n return\n 0x31641bAFb87E9A58f78835050a7BE56921986339;\n } else {\n return\n 0xA54766424f6dA74b45EbCc5Bf0Bd1D74D2CCcaAB;\n }\n } else {\n if (routeId == 383) {\n return\n 0xc7bBa57F8C179EDDBaa62117ddA360e28f3F8252;\n } else {\n return\n 0x5e663ED97ea77d393B8858C90d0683bF180E0ffd;\n }\n }\n }\n }\n }\n }\n }\n }\n }\n\n if (routes[routeId] == address(0)) revert ZeroAddressNotAllowed();\n return routes[routeId];\n }\n\n /// @notice fallback function to handle swap, bridge execution\n /// @dev ensure routeId is converted to bytes4 and sent as msg.sig in the transaction\n fallback() external payable {\n address routeAddress = addressAt(uint32(msg.sig));\n\n bytes memory result;\n\n assembly {\n // copy function selector and any arguments\n calldatacopy(0, 4, sub(calldatasize(), 4))\n // execute function call using the facet\n result := delegatecall(\n gas(),\n routeAddress,\n 0,\n sub(calldatasize(), 4),\n 0,\n 0\n )\n // get any return value\n returndatacopy(0, 0, returndatasize())\n // return any return value or error back to the caller\n switch result\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n}\n"},"src/bridges/hop/interfaces/IHopL1Bridge.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/**\n * @title L1Bridge Hop Interface\n * @notice L1 Hop Bridge, Used to transfer from L1 to L2s.\n */\ninterface IHopL1Bridge {\n /**\n * @notice `amountOutMin` and `deadline` should be 0 when no swap is intended at the destination.\n * @notice `amount` is the total amount the user wants to send including the relayer fee\n * @dev Send tokens to a supported layer-2 to mint hToken and optionally swap the hToken in the\n * AMM at the destination.\n * @param chainId The chainId of the destination chain\n * @param recipient The address receiving funds at the destination\n * @param amount The amount being sent\n * @param amountOutMin The minimum amount received after attempting to swap in the destination\n * AMM market. 0 if no swap is intended.\n * @param deadline The deadline for swapping in the destination AMM market. 0 if no\n * swap is intended.\n * @param relayer The address of the relayer at the destination.\n * @param relayerFee The amount distributed to the relayer at the destination. This is subtracted from the `amount`.\n */\n function sendToL2(\n uint256 chainId,\n address recipient,\n uint256 amount,\n uint256 amountOutMin,\n uint256 deadline,\n address relayer,\n uint256 relayerFee\n ) external payable;\n}\n"},"src/bridges/refuel/interfaces/refuel.sol":{"content":"// SPDX-License-Identifier: Apache-2.0\npragma solidity >=0.8.0;\n\n/// @notice interface with functions to interact with Refuel contract\ninterface IRefuel {\n /**\n * @notice function to deposit nativeToken to Destination-address on destinationChain\n * @param destinationChainId chainId of the Destination chain\n * @param _to recipient address\n */\n function depositNativeToken(\n uint256 destinationChainId,\n address _to\n ) external payable;\n}\n"},"src/bridges/stargate/interfaces/stargate.sol":{"content":"// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\n/**\n * @title IBridgeStargate Interface Contract.\n * @notice Interface used by Stargate-L1 and L2 Router implementations\n * @dev router and routerETH addresses will be distinct for L1 and L2\n */\ninterface IBridgeStargate {\n // @notice Struct to hold the additional-data for bridging ERC20 token\n struct lzTxObj {\n // gas limit to bridge the token in Stargate to destinationChain\n uint256 dstGasForCall;\n // destination nativeAmount, this is always set as 0\n uint256 dstNativeAmount;\n // destination nativeAddress, this is always set as 0x\n bytes dstNativeAddr;\n }\n\n /// @notice function in stargate bridge which is used to bridge ERC20 tokens to recipient on destinationChain\n function swap(\n uint16 _dstChainId,\n uint256 _srcPoolId,\n uint256 _dstPoolId,\n address payable _refundAddress,\n uint256 _amountLD,\n uint256 _minAmountLD,\n lzTxObj memory _lzTxParams,\n bytes calldata _to,\n bytes calldata _payload\n ) external payable;\n\n /// @notice function in stargate bridge which is used to bridge native tokens to recipient on destinationChain\n function swapETH(\n uint16 _dstChainId, // destination Stargate chainId\n address payable _refundAddress, // refund additional messageFee to this address\n bytes calldata _toAddress, // the receiver of the destination ETH\n uint256 _amountLD, // the amount, in Local Decimals, to be swapped\n uint256 _minAmountLD // the minimum amount accepted out on destination\n ) external payable;\n}\n"},"src/controllers/FeesTakerController.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {BaseController} from \"./BaseController.sol\";\nimport {ISocketRequest} from \"../interfaces/ISocketRequest.sol\";\n\n/**\n * @title FeesTaker-Controller Implementation\n * @notice Controller with composed actions to deduct-fees followed by Refuel, Swap and Bridge\n * to be executed Sequentially and this is atomic\n * @author Socket dot tech.\n */\ncontract FeesTakerController is BaseController {\n using SafeTransferLib for ERC20;\n\n /// @notice event emitted upon fee-deduction to fees-taker address\n event SocketFeesDeducted(\n uint256 fees,\n address feesToken,\n address feesTaker\n );\n\n /// @notice Function-selector to invoke deduct-fees and swap token\n /// @dev This function selector is to be used while building transaction-data\n bytes4 public immutable FEES_TAKER_SWAP_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\"takeFeesAndSwap((address,address,uint256,uint32,bytes))\")\n );\n\n /// @notice Function-selector to invoke deduct-fees and bridge token\n /// @dev This function selector is to be used while building transaction-data\n bytes4 public immutable FEES_TAKER_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"takeFeesAndBridge((address,address,uint256,uint32,bytes))\"\n )\n );\n\n /// @notice Function-selector to invoke deduct-fees and bridge multiple tokens\n /// @dev This function selector is to be used while building transaction-data\n bytes4 public immutable FEES_TAKER_MULTI_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"takeFeesAndMultiBridge((address,address,uint256,uint32[],bytes[]))\"\n )\n );\n\n /// @notice Function-selector to invoke deduct-fees followed by swapping of a token and bridging the swapped bridge\n /// @dev This function selector is to be used while building transaction-data\n bytes4 public immutable FEES_TAKER_SWAP_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"takeFeeAndSwapAndBridge((address,address,uint256,uint32,bytes,uint32,bytes))\"\n )\n );\n\n /// @notice Function-selector to invoke deduct-fees refuel\n /// @notice followed by swapping of a token and bridging the swapped bridge\n /// @dev This function selector is to be used while building transaction-data\n bytes4 public immutable FEES_TAKER_REFUEL_SWAP_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"takeFeeAndRefuelAndSwapAndBridge((address,address,uint256,uint32,bytes,uint32,bytes,uint32,bytes))\"\n )\n );\n\n /// @notice socketGatewayAddress to be initialised via storage variable BaseController\n constructor(\n address _socketGatewayAddress\n ) BaseController(_socketGatewayAddress) {}\n\n /**\n * @notice function to deduct-fees to fees-taker address on source-chain and swap token\n * @dev ensure correct function selector is used to generate transaction-data for bridgeRequest\n * @param ftsRequest feesTakerSwapRequest object generated either off-chain or the calling contract using\n * the function-selector FEES_TAKER_SWAP_FUNCTION_SELECTOR\n * @return output bytes from the swap operation (last operation in the composed actions)\n */\n function takeFeesAndSwap(\n ISocketRequest.FeesTakerSwapRequest calldata ftsRequest\n ) external payable returns (bytes memory) {\n if (ftsRequest.feesToken == NATIVE_TOKEN_ADDRESS) {\n //transfer the native amount to the feeTakerAddress\n payable(ftsRequest.feesTakerAddress).transfer(\n ftsRequest.feesAmount\n );\n } else {\n //transfer feesAmount to feesTakerAddress\n ERC20(ftsRequest.feesToken).safeTransferFrom(\n msg.sender,\n ftsRequest.feesTakerAddress,\n ftsRequest.feesAmount\n );\n }\n\n emit SocketFeesDeducted(\n ftsRequest.feesAmount,\n ftsRequest.feesTakerAddress,\n ftsRequest.feesToken\n );\n\n //call bridge function (executeRoute for the swapRequestData)\n return _executeRoute(ftsRequest.routeId, ftsRequest.swapRequestData);\n }\n\n /**\n * @notice function to deduct-fees to fees-taker address on source-chain and bridge amount to destinationChain\n * @dev ensure correct function selector is used to generate transaction-data for bridgeRequest\n * @param ftbRequest feesTakerBridgeRequest object generated either off-chain or the calling contract using\n * the function-selector FEES_TAKER_BRIDGE_FUNCTION_SELECTOR\n * @return output bytes from the bridge operation (last operation in the composed actions)\n */\n function takeFeesAndBridge(\n ISocketRequest.FeesTakerBridgeRequest calldata ftbRequest\n ) external payable returns (bytes memory) {\n if (ftbRequest.feesToken == NATIVE_TOKEN_ADDRESS) {\n //transfer the native amount to the feeTakerAddress\n payable(ftbRequest.feesTakerAddress).transfer(\n ftbRequest.feesAmount\n );\n } else {\n //transfer feesAmount to feesTakerAddress\n ERC20(ftbRequest.feesToken).safeTransferFrom(\n msg.sender,\n ftbRequest.feesTakerAddress,\n ftbRequest.feesAmount\n );\n }\n\n emit SocketFeesDeducted(\n ftbRequest.feesAmount,\n ftbRequest.feesTakerAddress,\n ftbRequest.feesToken\n );\n\n //call bridge function (executeRoute for the bridgeData)\n return _executeRoute(ftbRequest.routeId, ftbRequest.bridgeRequestData);\n }\n\n /**\n * @notice function to deduct-fees to fees-taker address on source-chain and bridge amount to destinationChain\n * @notice multiple bridge-requests are to be generated and sequence and number of routeIds should match with the bridgeData array\n * @dev ensure correct function selector is used to generate transaction-data for bridgeRequest\n * @param ftmbRequest feesTakerMultiBridgeRequest object generated either off-chain or the calling contract using\n * the function-selector FEES_TAKER_MULTI_BRIDGE_FUNCTION_SELECTOR\n */\n function takeFeesAndMultiBridge(\n ISocketRequest.FeesTakerMultiBridgeRequest calldata ftmbRequest\n ) external payable {\n if (ftmbRequest.feesToken == NATIVE_TOKEN_ADDRESS) {\n //transfer the native amount to the feeTakerAddress\n payable(ftmbRequest.feesTakerAddress).transfer(\n ftmbRequest.feesAmount\n );\n } else {\n //transfer feesAmount to feesTakerAddress\n ERC20(ftmbRequest.feesToken).safeTransferFrom(\n msg.sender,\n ftmbRequest.feesTakerAddress,\n ftmbRequest.feesAmount\n );\n }\n\n emit SocketFeesDeducted(\n ftmbRequest.feesAmount,\n ftmbRequest.feesTakerAddress,\n ftmbRequest.feesToken\n );\n\n // multiple bridge-requests are to be generated and sequence and number of routeIds should match with the bridgeData array\n for (\n uint256 index = 0;\n index < ftmbRequest.bridgeRouteIds.length;\n ++index\n ) {\n //call bridge function (executeRoute for the bridgeData)\n _executeRoute(\n ftmbRequest.bridgeRouteIds[index],\n ftmbRequest.bridgeRequestDataItems[index]\n );\n }\n }\n\n /**\n * @notice function to deduct-fees to fees-taker address on source-chain followed by swap the amount on sourceChain followed by\n * bridging the swapped amount to destinationChain\n * @dev while generating implData for swap and bridgeRequests, ensure correct function selector is used\n * bridge action corresponds to the bridgeAfterSwap function of the bridgeImplementation\n * @param fsbRequest feesTakerSwapBridgeRequest object generated either off-chain or the calling contract using\n * the function-selector FEES_TAKER_SWAP_BRIDGE_FUNCTION_SELECTOR\n */\n function takeFeeAndSwapAndBridge(\n ISocketRequest.FeesTakerSwapBridgeRequest calldata fsbRequest\n ) external payable returns (bytes memory) {\n if (fsbRequest.feesToken == NATIVE_TOKEN_ADDRESS) {\n //transfer the native amount to the feeTakerAddress\n payable(fsbRequest.feesTakerAddress).transfer(\n fsbRequest.feesAmount\n );\n } else {\n //transfer feesAmount to feesTakerAddress\n ERC20(fsbRequest.feesToken).safeTransferFrom(\n msg.sender,\n fsbRequest.feesTakerAddress,\n fsbRequest.feesAmount\n );\n }\n\n emit SocketFeesDeducted(\n fsbRequest.feesAmount,\n fsbRequest.feesTakerAddress,\n fsbRequest.feesToken\n );\n\n // execute swap operation\n bytes memory swapResponseData = _executeRoute(\n fsbRequest.swapRouteId,\n fsbRequest.swapData\n );\n\n uint256 swapAmount = abi.decode(swapResponseData, (uint256));\n\n // swapped amount is to be bridged to the recipient on destinationChain\n bytes memory bridgeImpldata = abi.encodeWithSelector(\n BRIDGE_AFTER_SWAP_SELECTOR,\n swapAmount,\n fsbRequest.bridgeData\n );\n\n // execute bridge operation and return the byte-data from response of bridge operation\n return _executeRoute(fsbRequest.bridgeRouteId, bridgeImpldata);\n }\n\n /**\n * @notice function to deduct-fees to fees-taker address on source-chain followed by refuel followed by\n * swap the amount on sourceChain followed by bridging the swapped amount to destinationChain\n * @dev while generating implData for refuel, swap and bridge Requests, ensure correct function selector is used\n * bridge action corresponds to the bridgeAfterSwap function of the bridgeImplementation\n * @param frsbRequest feesTakerRefuelSwapBridgeRequest object generated either off-chain or the calling contract using\n * the function-selector FEES_TAKER_REFUEL_SWAP_BRIDGE_FUNCTION_SELECTOR\n */\n function takeFeeAndRefuelAndSwapAndBridge(\n ISocketRequest.FeesTakerRefuelSwapBridgeRequest calldata frsbRequest\n ) external payable returns (bytes memory) {\n if (frsbRequest.feesToken == NATIVE_TOKEN_ADDRESS) {\n //transfer the native amount to the feeTakerAddress\n payable(frsbRequest.feesTakerAddress).transfer(\n frsbRequest.feesAmount\n );\n } else {\n //transfer feesAmount to feesTakerAddress\n ERC20(frsbRequest.feesToken).safeTransferFrom(\n msg.sender,\n frsbRequest.feesTakerAddress,\n frsbRequest.feesAmount\n );\n }\n\n emit SocketFeesDeducted(\n frsbRequest.feesAmount,\n frsbRequest.feesTakerAddress,\n frsbRequest.feesToken\n );\n\n // refuel is also done via bridge execution via refuelRouteImplementation identified by refuelRouteId\n _executeRoute(frsbRequest.refuelRouteId, frsbRequest.refuelData);\n\n // execute swap operation\n bytes memory swapResponseData = _executeRoute(\n frsbRequest.swapRouteId,\n frsbRequest.swapData\n );\n\n uint256 swapAmount = abi.decode(swapResponseData, (uint256));\n\n // swapped amount is to be bridged to the recipient on destinationChain\n bytes memory bridgeImpldata = abi.encodeWithSelector(\n BRIDGE_AFTER_SWAP_SELECTOR,\n swapAmount,\n frsbRequest.bridgeData\n );\n\n // execute bridge operation and return the byte-data from response of bridge operation\n return _executeRoute(frsbRequest.bridgeRouteId, bridgeImpldata);\n }\n}\n"},"src/errors/SocketErrors.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nerror CelerRefundNotReady();\nerror OnlySocketDeployer();\nerror OnlySocketGatewayOwner();\nerror OnlySocketGateway();\nerror OnlyOwner();\nerror OnlyNominee();\nerror TransferIdExists();\nerror TransferIdDoesnotExist();\nerror Address0Provided();\nerror SwapFailed();\nerror UnsupportedInterfaceId();\nerror InvalidCelerRefund();\nerror CelerAlreadyRefunded();\nerror IncorrectBridgeRatios();\nerror ZeroAddressNotAllowed();\nerror ArrayLengthMismatch();\n"},"src/bridges/hop/l1/HopImplL1.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\nimport \"../interfaces/IHopL1Bridge.sol\";\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {BridgeImplBase} from \"../../BridgeImplBase.sol\";\nimport {HOP} from \"../../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Hop-L1 Route Implementation\n * @notice Route implementation with functions to bridge ERC20 and Native via Hop-Bridge from L1 to Supported L2s\n * Called via SocketGateway if the routeId in the request maps to the routeId of HopImplementation\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ncontract HopImplL1 is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable HopIdentifier = HOP;\n\n /// @notice Function-selector for ERC20-token bridging on Hop-L1-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4 public immutable HOP_L1_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(address,address,address,address,uint256,uint256,uint256,uint256,(uint256,bytes32))\"\n )\n );\n\n /// @notice Function-selector for Native bridging on Hop-L1-Route\n /// @dev This function selector is to be used while building transaction-data to bridge Native tokens\n bytes4 public immutable HOP_L1_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeNativeTo(address,address,address,uint256,uint256,uint256,uint256,uint256,bytes32)\"\n )\n );\n\n bytes4 public immutable HOP_L1_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(address,address,address,uint256,uint256,uint256,uint256,bytes32))\"\n )\n );\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n constructor(\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {}\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct HopDataNoToken {\n // The address receiving funds at the destination\n address receiverAddress;\n // address of the Hop-L1-Bridge to handle bridging the tokens\n address l1bridgeAddr;\n // relayerFee The amount distributed to the relayer at the destination. This is subtracted from the `_amount`.\n address relayer;\n // The chainId of the destination chain\n uint256 toChainId;\n // The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.\n uint256 amountOutMin;\n // The amount distributed to the relayer at the destination. This is subtracted from the `amount`.\n uint256 relayerFee;\n // The deadline for swapping in the destination AMM market. 0 if no swap is intended.\n uint256 deadline;\n // socket offchain created hash\n bytes32 metadata;\n }\n\n struct HopData {\n /// @notice address of token being bridged\n address token;\n // The address receiving funds at the destination\n address receiverAddress;\n // address of the Hop-L1-Bridge to handle bridging the tokens\n address l1bridgeAddr;\n // relayerFee The amount distributed to the relayer at the destination. This is subtracted from the `_amount`.\n address relayer;\n // The chainId of the destination chain\n uint256 toChainId;\n // The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.\n uint256 amountOutMin;\n // The amount distributed to the relayer at the destination. This is subtracted from the `amount`.\n uint256 relayerFee;\n // The deadline for swapping in the destination AMM market. 0 if no swap is intended.\n uint256 deadline;\n // socket offchain created hash\n bytes32 metadata;\n }\n\n struct HopERC20Data {\n uint256 deadline;\n bytes32 metadata;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in HopBridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for Hop-L1-Bridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n HopData memory hopData = abi.decode(bridgeData, (HopData));\n\n if (hopData.token == NATIVE_TOKEN_ADDRESS) {\n IHopL1Bridge(hopData.l1bridgeAddr).sendToL2{value: amount}(\n hopData.toChainId,\n hopData.receiverAddress,\n amount,\n hopData.amountOutMin,\n hopData.deadline,\n hopData.relayer,\n hopData.relayerFee\n );\n } else {\n ERC20(hopData.token).safeApprove(hopData.l1bridgeAddr, amount);\n\n // perform bridging\n IHopL1Bridge(hopData.l1bridgeAddr).sendToL2(\n hopData.toChainId,\n hopData.receiverAddress,\n amount,\n hopData.amountOutMin,\n hopData.deadline,\n hopData.relayer,\n hopData.relayerFee\n );\n }\n\n emit SocketBridge(\n amount,\n hopData.token,\n hopData.toChainId,\n HopIdentifier,\n msg.sender,\n hopData.receiverAddress,\n hopData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in HopBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param hopData encoded data for HopData\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n HopDataNoToken calldata hopData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n\n if (token == NATIVE_TOKEN_ADDRESS) {\n IHopL1Bridge(hopData.l1bridgeAddr).sendToL2{value: bridgeAmount}(\n hopData.toChainId,\n hopData.receiverAddress,\n bridgeAmount,\n hopData.amountOutMin,\n hopData.deadline,\n hopData.relayer,\n hopData.relayerFee\n );\n } else {\n ERC20(token).safeApprove(hopData.l1bridgeAddr, bridgeAmount);\n\n // perform bridging\n IHopL1Bridge(hopData.l1bridgeAddr).sendToL2(\n hopData.toChainId,\n hopData.receiverAddress,\n bridgeAmount,\n hopData.amountOutMin,\n hopData.deadline,\n hopData.relayer,\n hopData.relayerFee\n );\n }\n\n emit SocketBridge(\n bridgeAmount,\n token,\n hopData.toChainId,\n HopIdentifier,\n msg.sender,\n hopData.receiverAddress,\n hopData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via Hop-L1-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param receiverAddress The address receiving funds at the destination\n * @param token token being bridged\n * @param l1bridgeAddr address of the Hop-L1-Bridge to handle bridging the tokens\n * @param relayer The amount distributed to the relayer at the destination. This is subtracted from the `_amount`.\n * @param toChainId The chainId of the destination chain\n * @param amount The amount being sent\n * @param amountOutMin The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.\n * @param relayerFee The amount distributed to the relayer at the destination. This is subtracted from the `amount`.\n * @param hopData extra data needed to build the tx\n */\n function bridgeERC20To(\n address receiverAddress,\n address token,\n address l1bridgeAddr,\n address relayer,\n uint256 toChainId,\n uint256 amount,\n uint256 amountOutMin,\n uint256 relayerFee,\n HopERC20Data calldata hopData\n ) external payable {\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n tokenInstance.safeApprove(l1bridgeAddr, amount);\n\n // perform bridging\n IHopL1Bridge(l1bridgeAddr).sendToL2(\n toChainId,\n receiverAddress,\n amount,\n amountOutMin,\n hopData.deadline,\n relayer,\n relayerFee\n );\n\n emit SocketBridge(\n amount,\n token,\n toChainId,\n HopIdentifier,\n msg.sender,\n receiverAddress,\n hopData.metadata\n );\n }\n\n /**\n * @notice function to handle Native bridging to receipent via Hop-L1-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param receiverAddress The address receiving funds at the destination\n * @param l1bridgeAddr address of the Hop-L1-Bridge to handle bridging the tokens\n * @param relayer The amount distributed to the relayer at the destination. This is subtracted from the `_amount`.\n * @param toChainId The chainId of the destination chain\n * @param amount The amount being sent\n * @param amountOutMin The minimum amount received after attempting to swap in the destination AMM market. 0 if no swap is intended.\n * @param relayerFee The amount distributed to the relayer at the destination. This is subtracted from the `amount`.\n * @param deadline The deadline for swapping in the destination AMM market. 0 if no swap is intended.\n */\n function bridgeNativeTo(\n address receiverAddress,\n address l1bridgeAddr,\n address relayer,\n uint256 toChainId,\n uint256 amount,\n uint256 amountOutMin,\n uint256 relayerFee,\n uint256 deadline,\n bytes32 metadata\n ) external payable {\n IHopL1Bridge(l1bridgeAddr).sendToL2{value: amount}(\n toChainId,\n receiverAddress,\n amount,\n amountOutMin,\n deadline,\n relayer,\n relayerFee\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n toChainId,\n HopIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n"},"src/static/RouteIdentifiers.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\nbytes32 constant ACROSS = keccak256(\"Across\");\n\nbytes32 constant ANYSWAP = keccak256(\"Anyswap\");\n\nbytes32 constant CBRIDGE = keccak256(\"CBridge\");\n\nbytes32 constant HOP = keccak256(\"Hop\");\n\nbytes32 constant HYPHEN = keccak256(\"Hyphen\");\n\nbytes32 constant NATIVE_OPTIMISM = keccak256(\"NativeOptimism\");\n\nbytes32 constant NATIVE_ARBITRUM = keccak256(\"NativeArbitrum\");\n\nbytes32 constant NATIVE_POLYGON = keccak256(\"NativePolygon\");\n\nbytes32 constant REFUEL = keccak256(\"Refuel\");\n\nbytes32 constant STARGATE = keccak256(\"Stargate\");\n\nbytes32 constant ONEINCH = keccak256(\"OneInch\");\n\nbytes32 constant ZEROX = keccak256(\"Zerox\");\n\nbytes32 constant RAINBOW = keccak256(\"Rainbow\");\n"},"src/SocketGateway.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\npragma experimental ABIEncoderV2;\n\nimport \"./utils/Ownable.sol\";\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {LibUtil} from \"./libraries/LibUtil.sol\";\nimport \"./libraries/LibBytes.sol\";\nimport {ISocketRoute} from \"./interfaces/ISocketRoute.sol\";\nimport {ISocketRequest} from \"./interfaces/ISocketRequest.sol\";\nimport {ISocketGateway} from \"./interfaces/ISocketGateway.sol\";\nimport {IncorrectBridgeRatios, ZeroAddressNotAllowed, ArrayLengthMismatch} from \"./errors/SocketErrors.sol\";\n\n/// @title SocketGatewayContract\n/// @notice Socketgateway is a contract with entrypoint functions for all interactions with socket liquidity layer\n/// @author Socket Team\ncontract SocketGatewayTemplate is Ownable {\n using LibBytes for bytes;\n using LibBytes for bytes4;\n using SafeTransferLib for ERC20;\n\n /// @notice FunctionSelector used to delegatecall from swap to the function of bridge router implementation\n bytes4 public immutable BRIDGE_AFTER_SWAP_SELECTOR =\n bytes4(keccak256(\"bridgeAfterSwap(uint256,bytes)\"));\n\n /// @notice storage variable to keep track of total number of routes registered in socketgateway\n uint32 public routesCount = 385;\n\n /// @notice storage variable to keep track of total number of controllers registered in socketgateway\n uint32 public controllerCount;\n\n address public immutable disabledRouteAddress;\n\n uint256 public constant CENT_PERCENT = 100e18;\n\n /// @notice storage mapping for route implementation addresses\n mapping(uint32 => address) public routes;\n\n /// storage mapping for controller implemenation addresses\n mapping(uint32 => address) public controllers;\n\n // Events ------------------------------------------------------------------------------------------------------->\n\n /// @notice Event emitted when a router is added to socketgateway\n event NewRouteAdded(uint32 indexed routeId, address indexed route);\n\n /// @notice Event emitted when a route is disabled\n event RouteDisabled(uint32 indexed routeId);\n\n /// @notice Event emitted when ownership transfer is requested by socket-gateway-owner\n event OwnershipTransferRequested(\n address indexed _from,\n address indexed _to\n );\n\n /// @notice Event emitted when a controller is added to socketgateway\n event ControllerAdded(\n uint32 indexed controllerId,\n address indexed controllerAddress\n );\n\n /// @notice Event emitted when a controller is disabled\n event ControllerDisabled(uint32 indexed controllerId);\n\n constructor(address _owner, address _disabledRoute) Ownable(_owner) {\n disabledRouteAddress = _disabledRoute;\n }\n\n // Able to receive ether\n // solhint-disable-next-line no-empty-blocks\n receive() external payable {}\n\n /*******************************************\n * EXTERNAL AND PUBLIC FUNCTIONS *\n *******************************************/\n\n /**\n * @notice executes functions in the routes identified using routeId and functionSelectorData\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped\n * @dev ensure the data in routeData to be built using the function-selector defined as a\n * constant in the route implementation contract\n * @param routeId route identifier\n * @param routeData functionSelectorData generated using the function-selector defined in the route Implementation\n */\n function executeRoute(\n uint32 routeId,\n bytes calldata routeData\n ) external payable returns (bytes memory) {\n (bool success, bytes memory result) = addressAt(routeId).delegatecall(\n routeData\n );\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n return result;\n }\n\n /**\n * @notice swaps a token on sourceChain and split it across multiple bridge-recipients\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being swapped\n * @dev ensure the swap-data and bridge-data is generated using the function-selector defined as a constant in the implementation address\n * @param swapMultiBridgeRequest request\n */\n function swapAndMultiBridge(\n ISocketRequest.SwapMultiBridgeRequest calldata swapMultiBridgeRequest\n ) external payable {\n uint256 requestLength = swapMultiBridgeRequest.bridgeRouteIds.length;\n\n if (\n requestLength != swapMultiBridgeRequest.bridgeImplDataItems.length\n ) {\n revert ArrayLengthMismatch();\n }\n uint256 ratioAggregate;\n for (uint256 index = 0; index < requestLength; ) {\n ratioAggregate += swapMultiBridgeRequest.bridgeRatios[index];\n }\n\n if (ratioAggregate != CENT_PERCENT) {\n revert IncorrectBridgeRatios();\n }\n\n (bool swapSuccess, bytes memory swapResult) = addressAt(\n swapMultiBridgeRequest.swapRouteId\n ).delegatecall(swapMultiBridgeRequest.swapImplData);\n\n if (!swapSuccess) {\n assembly {\n revert(add(swapResult, 32), mload(swapResult))\n }\n }\n\n uint256 amountReceivedFromSwap = abi.decode(swapResult, (uint256));\n\n uint256 bridgedAmount;\n\n for (uint256 index = 0; index < requestLength; ) {\n uint256 bridgingAmount;\n\n // if it is the last bridge request, bridge the remaining amount\n if (index == requestLength - 1) {\n bridgingAmount = amountReceivedFromSwap - bridgedAmount;\n } else {\n // bridging amount is the multiplication of bridgeRatio and amountReceivedFromSwap\n bridgingAmount =\n (amountReceivedFromSwap *\n swapMultiBridgeRequest.bridgeRatios[index]) /\n (CENT_PERCENT);\n }\n\n // update the bridged amount, this would be used for computation for last bridgeRequest\n bridgedAmount += bridgingAmount;\n\n bytes memory bridgeImpldata = abi.encodeWithSelector(\n BRIDGE_AFTER_SWAP_SELECTOR,\n bridgingAmount,\n swapMultiBridgeRequest.bridgeImplDataItems[index]\n );\n\n (bool bridgeSuccess, bytes memory bridgeResult) = addressAt(\n swapMultiBridgeRequest.bridgeRouteIds[index]\n ).delegatecall(bridgeImpldata);\n\n if (!bridgeSuccess) {\n assembly {\n revert(add(bridgeResult, 32), mload(bridgeResult))\n }\n }\n\n unchecked {\n ++index;\n }\n }\n }\n\n /**\n * @notice sequentially executes functions in the routes identified using routeId and functionSelectorData\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped\n * @dev ensure the data in each dataItem to be built using the function-selector defined as a\n * constant in the route implementation contract\n * @param routeIds a list of route identifiers\n * @param dataItems a list of functionSelectorData generated using the function-selector defined in the route Implementation\n */\n function executeRoutes(\n uint32[] calldata routeIds,\n bytes[] calldata dataItems\n ) external payable {\n uint256 routeIdslength = routeIds.length;\n if (routeIdslength != dataItems.length) revert ArrayLengthMismatch();\n for (uint256 index = 0; index < routeIdslength; ) {\n (bool success, bytes memory result) = addressAt(routeIds[index])\n .delegatecall(dataItems[index]);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n unchecked {\n ++index;\n }\n }\n }\n\n /**\n * @notice execute a controller function identified using the controllerId in the request\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped\n * @dev ensure the data in request to be built using the function-selector defined as a\n * constant in the controller implementation contract\n * @param socketControllerRequest socketControllerRequest with controllerId to identify the\n * controllerAddress and byteData constructed using functionSelector\n * of the function being invoked\n * @return bytes data received from the call delegated to controller\n */\n function executeController(\n ISocketGateway.SocketControllerRequest calldata socketControllerRequest\n ) external payable returns (bytes memory) {\n (bool success, bytes memory result) = controllers[\n socketControllerRequest.controllerId\n ].delegatecall(socketControllerRequest.data);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n return result;\n }\n\n /**\n * @notice sequentially executes all controller requests\n * @notice The caller must first approve this contract to spend amount of ERC20-Token being bridged/swapped\n * @dev ensure the data in each controller-request to be built using the function-selector defined as a\n * constant in the controller implementation contract\n * @param controllerRequests a list of socketControllerRequest\n * Each controllerRequest contains controllerId to identify the controllerAddress and\n * byteData constructed using functionSelector of the function being invoked\n */\n function executeControllers(\n ISocketGateway.SocketControllerRequest[] calldata controllerRequests\n ) external payable {\n for (uint32 index = 0; index < controllerRequests.length; ) {\n (bool success, bytes memory result) = controllers[\n controllerRequests[index].controllerId\n ].delegatecall(controllerRequests[index].data);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n unchecked {\n ++index;\n }\n }\n }\n\n /**************************************\n * ADMIN FUNCTIONS *\n **************************************/\n\n /**\n * @notice Add route to the socketGateway\n This is a restricted function to be called by only socketGatewayOwner\n * @dev ensure routeAddress is a verified bridge or middleware implementation address\n * @param routeAddress The address of bridge or middleware implementation contract deployed\n * @return Id of the route added to the routes-mapping in socketGateway storage\n */\n function addRoute(\n address routeAddress\n ) external onlyOwner returns (uint32) {\n uint32 routeId = routesCount;\n routes[routeId] = routeAddress;\n\n routesCount += 1;\n\n emit NewRouteAdded(routeId, routeAddress);\n\n return routeId;\n }\n\n /**\n * @notice Give Infinite or 0 approval to bridgeRoute for the tokenAddress\n This is a restricted function to be called by only socketGatewayOwner\n */\n\n function setApprovalForRouters(\n address[] memory routeAddresses,\n address[] memory tokenAddresses,\n bool isMax\n ) external onlyOwner {\n for (uint32 index = 0; index < routeAddresses.length; ) {\n ERC20(tokenAddresses[index]).approve(\n routeAddresses[index],\n isMax ? type(uint256).max : 0\n );\n unchecked {\n ++index;\n }\n }\n }\n\n /**\n * @notice Add controller to the socketGateway\n This is a restricted function to be called by only socketGatewayOwner\n * @dev ensure controllerAddress is a verified controller implementation address\n * @param controllerAddress The address of controller implementation contract deployed\n * @return Id of the controller added to the controllers-mapping in socketGateway storage\n */\n function addController(\n address controllerAddress\n ) external onlyOwner returns (uint32) {\n uint32 controllerId = controllerCount;\n\n controllers[controllerId] = controllerAddress;\n\n controllerCount += 1;\n\n emit ControllerAdded(controllerId, controllerAddress);\n\n return controllerId;\n }\n\n /**\n * @notice disable controller by setting ZeroAddress to the entry in controllers-mapping\n identified by controllerId as key.\n This is a restricted function to be called by only socketGatewayOwner\n * @param controllerId The Id of controller-implementation in the controllers mapping\n */\n function disableController(uint32 controllerId) public onlyOwner {\n controllers[controllerId] = disabledRouteAddress;\n emit ControllerDisabled(controllerId);\n }\n\n /**\n * @notice disable a route by setting ZeroAddress to the entry in routes-mapping\n identified by routeId as key.\n This is a restricted function to be called by only socketGatewayOwner\n * @param routeId The Id of route-implementation in the routes mapping\n */\n function disableRoute(uint32 routeId) external onlyOwner {\n routes[routeId] = disabledRouteAddress;\n emit RouteDisabled(routeId);\n }\n\n /*******************************************\n * RESTRICTED RESCUE FUNCTIONS *\n *******************************************/\n\n /**\n * @notice Rescues the ERC20 token to an address\n this is a restricted function to be called by only socketGatewayOwner\n * @dev as this is a restricted to socketGatewayOwner, ensure the userAddress is a known address\n * @param token address of the ERC20 token being rescued\n * @param userAddress address to which ERC20 is to be rescued\n * @param amount amount of ERC20 tokens being rescued\n */\n function rescueFunds(\n address token,\n address userAddress,\n uint256 amount\n ) external onlyOwner {\n ERC20(token).safeTransfer(userAddress, amount);\n }\n\n /**\n * @notice Rescues the native balance to an address\n this is a restricted function to be called by only socketGatewayOwner\n * @dev as this is a restricted to socketGatewayOwner, ensure the userAddress is a known address\n * @param userAddress address to which native-balance is to be rescued\n * @param amount amount of native-balance being rescued\n */\n function rescueEther(\n address payable userAddress,\n uint256 amount\n ) external onlyOwner {\n userAddress.transfer(amount);\n }\n\n /*******************************************\n * VIEW FUNCTIONS *\n *******************************************/\n\n /**\n * @notice Get routeImplementation address mapped to the routeId\n * @param routeId routeId is the key in the mapping for routes\n * @return route-implementation address\n */\n function getRoute(uint32 routeId) public view returns (address) {\n return addressAt(routeId);\n }\n\n /**\n * @notice Get controllerImplementation address mapped to the controllerId\n * @param controllerId controllerId is the key in the mapping for controllers\n * @return controller-implementation address\n */\n function getController(uint32 controllerId) public view returns (address) {\n return controllers[controllerId];\n }\n\n function addressAt(uint32 routeId) public view returns (address) {\n if (routeId < 385) {\n if (routeId < 257) {\n if (routeId < 129) {\n if (routeId < 65) {\n if (routeId < 33) {\n if (routeId < 17) {\n if (routeId < 9) {\n if (routeId < 5) {\n if (routeId < 3) {\n if (routeId == 1) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 3) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 7) {\n if (routeId == 5) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 7) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 13) {\n if (routeId < 11) {\n if (routeId == 9) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 11) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 15) {\n if (routeId == 13) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 15) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 25) {\n if (routeId < 21) {\n if (routeId < 19) {\n if (routeId == 17) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 19) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 23) {\n if (routeId == 21) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 23) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 29) {\n if (routeId < 27) {\n if (routeId == 25) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 27) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 31) {\n if (routeId == 29) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 31) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 49) {\n if (routeId < 41) {\n if (routeId < 37) {\n if (routeId < 35) {\n if (routeId == 33) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 35) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 39) {\n if (routeId == 37) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 39) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 45) {\n if (routeId < 43) {\n if (routeId == 41) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 43) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 47) {\n if (routeId == 45) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 47) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 57) {\n if (routeId < 53) {\n if (routeId < 51) {\n if (routeId == 49) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 51) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 55) {\n if (routeId == 53) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 55) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 61) {\n if (routeId < 59) {\n if (routeId == 57) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 59) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 63) {\n if (routeId == 61) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 63) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 97) {\n if (routeId < 81) {\n if (routeId < 73) {\n if (routeId < 69) {\n if (routeId < 67) {\n if (routeId == 65) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 67) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 71) {\n if (routeId == 69) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 71) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 77) {\n if (routeId < 75) {\n if (routeId == 73) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 75) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 79) {\n if (routeId == 77) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 79) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 89) {\n if (routeId < 85) {\n if (routeId < 83) {\n if (routeId == 81) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 83) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 87) {\n if (routeId == 85) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 87) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 93) {\n if (routeId < 91) {\n if (routeId == 89) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 91) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 95) {\n if (routeId == 93) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 95) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 113) {\n if (routeId < 105) {\n if (routeId < 101) {\n if (routeId < 99) {\n if (routeId == 97) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 99) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 103) {\n if (routeId == 101) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 103) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 109) {\n if (routeId < 107) {\n if (routeId == 105) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 107) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 111) {\n if (routeId == 109) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 111) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 121) {\n if (routeId < 117) {\n if (routeId < 115) {\n if (routeId == 113) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 115) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 119) {\n if (routeId == 117) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 119) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 125) {\n if (routeId < 123) {\n if (routeId == 121) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 123) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 127) {\n if (routeId == 125) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 127) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 193) {\n if (routeId < 161) {\n if (routeId < 145) {\n if (routeId < 137) {\n if (routeId < 133) {\n if (routeId < 131) {\n if (routeId == 129) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 131) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 135) {\n if (routeId == 133) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 135) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 141) {\n if (routeId < 139) {\n if (routeId == 137) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 139) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 143) {\n if (routeId == 141) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 143) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 153) {\n if (routeId < 149) {\n if (routeId < 147) {\n if (routeId == 145) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 147) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 151) {\n if (routeId == 149) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 151) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 157) {\n if (routeId < 155) {\n if (routeId == 153) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 155) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 159) {\n if (routeId == 157) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 159) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 177) {\n if (routeId < 169) {\n if (routeId < 165) {\n if (routeId < 163) {\n if (routeId == 161) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 163) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 167) {\n if (routeId == 165) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 167) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 173) {\n if (routeId < 171) {\n if (routeId == 169) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 171) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 175) {\n if (routeId == 173) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 175) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 185) {\n if (routeId < 181) {\n if (routeId < 179) {\n if (routeId == 177) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 179) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 183) {\n if (routeId == 181) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 183) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 189) {\n if (routeId < 187) {\n if (routeId == 185) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 187) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 191) {\n if (routeId == 189) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 191) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 225) {\n if (routeId < 209) {\n if (routeId < 201) {\n if (routeId < 197) {\n if (routeId < 195) {\n if (routeId == 193) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 195) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 199) {\n if (routeId == 197) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 199) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 205) {\n if (routeId < 203) {\n if (routeId == 201) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 203) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 207) {\n if (routeId == 205) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 207) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 217) {\n if (routeId < 213) {\n if (routeId < 211) {\n if (routeId == 209) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 211) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 215) {\n if (routeId == 213) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 215) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 221) {\n if (routeId < 219) {\n if (routeId == 217) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 219) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 223) {\n if (routeId == 221) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 223) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 241) {\n if (routeId < 233) {\n if (routeId < 229) {\n if (routeId < 227) {\n if (routeId == 225) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 227) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 231) {\n if (routeId == 229) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 231) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 237) {\n if (routeId < 235) {\n if (routeId == 233) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 235) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 239) {\n if (routeId == 237) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 239) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 249) {\n if (routeId < 245) {\n if (routeId < 243) {\n if (routeId == 241) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 243) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 247) {\n if (routeId == 245) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 247) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 253) {\n if (routeId < 251) {\n if (routeId == 249) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 251) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 255) {\n if (routeId == 253) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 255) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 321) {\n if (routeId < 289) {\n if (routeId < 273) {\n if (routeId < 265) {\n if (routeId < 261) {\n if (routeId < 259) {\n if (routeId == 257) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 259) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 263) {\n if (routeId == 261) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 263) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 269) {\n if (routeId < 267) {\n if (routeId == 265) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 267) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 271) {\n if (routeId == 269) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 271) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 281) {\n if (routeId < 277) {\n if (routeId < 275) {\n if (routeId == 273) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 275) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 279) {\n if (routeId == 277) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 279) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 285) {\n if (routeId < 283) {\n if (routeId == 281) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 283) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 287) {\n if (routeId == 285) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 287) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 305) {\n if (routeId < 297) {\n if (routeId < 293) {\n if (routeId < 291) {\n if (routeId == 289) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 291) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 295) {\n if (routeId == 293) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 295) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 301) {\n if (routeId < 299) {\n if (routeId == 297) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 299) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 303) {\n if (routeId == 301) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 303) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 313) {\n if (routeId < 309) {\n if (routeId < 307) {\n if (routeId == 305) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 307) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 311) {\n if (routeId == 309) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 311) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 317) {\n if (routeId < 315) {\n if (routeId == 313) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 315) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 319) {\n if (routeId == 317) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 319) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n }\n } else {\n if (routeId < 353) {\n if (routeId < 337) {\n if (routeId < 329) {\n if (routeId < 325) {\n if (routeId < 323) {\n if (routeId == 321) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 323) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 327) {\n if (routeId == 325) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 327) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 333) {\n if (routeId < 331) {\n if (routeId == 329) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 331) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 335) {\n if (routeId == 333) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 335) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 345) {\n if (routeId < 341) {\n if (routeId < 339) {\n if (routeId == 337) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 339) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 343) {\n if (routeId == 341) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 343) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 349) {\n if (routeId < 347) {\n if (routeId == 345) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 347) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 351) {\n if (routeId == 349) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 351) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n } else {\n if (routeId < 369) {\n if (routeId < 361) {\n if (routeId < 357) {\n if (routeId < 355) {\n if (routeId == 353) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 355) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 359) {\n if (routeId == 357) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 359) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 365) {\n if (routeId < 363) {\n if (routeId == 361) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 363) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 367) {\n if (routeId == 365) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 367) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n } else {\n if (routeId < 377) {\n if (routeId < 373) {\n if (routeId < 371) {\n if (routeId == 369) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 371) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 375) {\n if (routeId == 373) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 375) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n } else {\n if (routeId < 381) {\n if (routeId < 379) {\n if (routeId == 377) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 379) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n } else {\n if (routeId < 383) {\n if (routeId == 381) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n } else {\n if (routeId == 383) {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n } else {\n return\n 0x822D4B4e63499a576Ab1cc152B86D1CFFf794F4f;\n }\n }\n }\n }\n }\n }\n }\n }\n }\n\n if (routes[routeId] == address(0)) revert ZeroAddressNotAllowed();\n return routes[routeId];\n }\n\n /// @notice fallback function to handle swap, bridge execution\n /// @dev ensure routeId is converted to bytes4 and sent as msg.sig in the transaction\n fallback() external payable {\n address routeAddress = addressAt(uint32(msg.sig));\n\n bytes memory result;\n\n assembly {\n // copy function selector and any arguments\n calldatacopy(0, 4, sub(calldatasize(), 4))\n // execute function call using the facet\n result := delegatecall(\n gas(),\n routeAddress,\n 0,\n sub(calldatasize(), 4),\n 0,\n 0\n )\n // get any return value\n returndatacopy(0, 0, returndatasize())\n // return any return value or error back to the caller\n switch result\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n}\n"},"src/swap/rainbow/Rainbow.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport \"../SwapImplBase.sol\";\nimport {Address0Provided, SwapFailed} from \"../../errors/SocketErrors.sol\";\nimport {RAINBOW} from \"../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Rainbow-Swap-Route Implementation\n * @notice Route implementation with functions to swap tokens via Rainbow-Swap\n * Called via SocketGateway if the routeId in the request maps to the routeId of RainbowImplementation\n * @author Socket dot tech.\n */\ncontract RainbowSwapImpl is SwapImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable RainbowIdentifier = RAINBOW;\n\n /// @notice unique name to identify the router, used to emit event upon successful bridging\n bytes32 public immutable NAME = keccak256(\"Rainbow-Router\");\n\n /// @notice address of rainbow-swap-aggregator to swap the tokens on Chain\n address payable public immutable rainbowSwapAggregator;\n\n /// @notice socketGatewayAddress to be initialised via storage variable SwapImplBase\n /// @notice rainbow swap aggregator contract is payable to allow ethereum swaps\n /// @dev ensure _rainbowSwapAggregator are set properly for the chainId in which the contract is being deployed\n constructor(\n address _rainbowSwapAggregator,\n address _socketGateway,\n address _socketDeployFactory\n ) SwapImplBase(_socketGateway, _socketDeployFactory) {\n rainbowSwapAggregator = payable(_rainbowSwapAggregator);\n }\n\n receive() external payable {}\n\n fallback() external payable {}\n\n /**\n * @notice function to swap tokens on the chain and transfer to receiver address\n * @notice This method is payable because the caller is doing token transfer and swap operation\n * @param fromToken address of token being Swapped\n * @param toToken address of token that recipient will receive after swap\n * @param amount amount of fromToken being swapped\n * @param receiverAddress recipient-address\n * @param swapExtraData additional Data to perform Swap via Rainbow-Aggregator\n * @return swapped amount (in toToken Address)\n */\n function performAction(\n address fromToken,\n address toToken,\n uint256 amount,\n address receiverAddress,\n bytes calldata swapExtraData\n ) external payable override returns (uint256) {\n if (fromToken == address(0)) {\n revert Address0Provided();\n }\n\n bytes memory swapCallData = abi.decode(swapExtraData, (bytes));\n\n uint256 _initialBalanceTokenOut;\n uint256 _finalBalanceTokenOut;\n\n ERC20 toTokenERC20 = ERC20(toToken);\n if (toToken != NATIVE_TOKEN_ADDRESS) {\n _initialBalanceTokenOut = toTokenERC20.balanceOf(socketGateway);\n } else {\n _initialBalanceTokenOut = address(this).balance;\n }\n\n if (fromToken != NATIVE_TOKEN_ADDRESS) {\n ERC20 token = ERC20(fromToken);\n token.safeTransferFrom(msg.sender, socketGateway, amount);\n token.safeApprove(rainbowSwapAggregator, amount);\n\n // solhint-disable-next-line\n (bool success, ) = rainbowSwapAggregator.call(swapCallData);\n\n if (!success) {\n revert SwapFailed();\n }\n\n token.safeApprove(rainbowSwapAggregator, 0);\n } else {\n (bool success, ) = rainbowSwapAggregator.call{value: amount}(\n swapCallData\n );\n if (!success) {\n revert SwapFailed();\n }\n }\n\n if (toToken != NATIVE_TOKEN_ADDRESS) {\n _finalBalanceTokenOut = toTokenERC20.balanceOf(socketGateway);\n } else {\n _finalBalanceTokenOut = address(this).balance;\n }\n\n uint256 returnAmount = _finalBalanceTokenOut - _initialBalanceTokenOut;\n\n if (toToken == NATIVE_TOKEN_ADDRESS) {\n payable(receiverAddress).transfer(returnAmount);\n } else {\n toTokenERC20.transfer(receiverAddress, returnAmount);\n }\n\n emit SocketSwapTokens(\n fromToken,\n toToken,\n returnAmount,\n amount,\n RainbowIdentifier,\n receiverAddress\n );\n\n return returnAmount;\n }\n\n /**\n * @notice function to swapWithIn SocketGateway - swaps tokens on the chain to socketGateway as recipient\n * @param fromToken token to be swapped\n * @param toToken token to which fromToken has to be swapped\n * @param amount amount of fromToken being swapped\n * @param swapExtraData encoded value of properties in the swapData Struct\n * @return swapped amount (in toToken Address)\n */\n function performActionWithIn(\n address fromToken,\n address toToken,\n uint256 amount,\n bytes calldata swapExtraData\n ) external payable override returns (uint256, address) {\n if (fromToken == address(0)) {\n revert Address0Provided();\n }\n\n bytes memory swapCallData = abi.decode(swapExtraData, (bytes));\n\n uint256 _initialBalanceTokenOut;\n uint256 _finalBalanceTokenOut;\n\n ERC20 toTokenERC20 = ERC20(toToken);\n if (toToken != NATIVE_TOKEN_ADDRESS) {\n _initialBalanceTokenOut = toTokenERC20.balanceOf(socketGateway);\n } else {\n _initialBalanceTokenOut = address(this).balance;\n }\n\n if (fromToken != NATIVE_TOKEN_ADDRESS) {\n ERC20 token = ERC20(fromToken);\n token.safeTransferFrom(msg.sender, socketGateway, amount);\n token.safeApprove(rainbowSwapAggregator, amount);\n\n // solhint-disable-next-line\n (bool success, ) = rainbowSwapAggregator.call(swapCallData);\n\n if (!success) {\n revert SwapFailed();\n }\n\n token.safeApprove(rainbowSwapAggregator, 0);\n } else {\n (bool success, ) = rainbowSwapAggregator.call{value: amount}(\n swapCallData\n );\n if (!success) {\n revert SwapFailed();\n }\n }\n\n if (toToken != NATIVE_TOKEN_ADDRESS) {\n _finalBalanceTokenOut = toTokenERC20.balanceOf(socketGateway);\n } else {\n _finalBalanceTokenOut = address(this).balance;\n }\n\n uint256 returnAmount = _finalBalanceTokenOut - _initialBalanceTokenOut;\n\n emit SocketSwapTokens(\n fromToken,\n toToken,\n returnAmount,\n amount,\n RainbowIdentifier,\n socketGateway\n );\n\n return (returnAmount, toToken);\n }\n}\n"},"src/interfaces/ISocketBridgeBase.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\ninterface ISocketBridgeBase {\n function killme() external;\n}\n"},"src/interfaces/ISocketRequest.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/**\n * @title ISocketRoute\n * @notice Interface with Request DataStructures to invoke controller functions.\n * @author Socket dot tech.\n */\ninterface ISocketRequest {\n struct SwapMultiBridgeRequest {\n uint32 swapRouteId;\n bytes swapImplData;\n uint32[] bridgeRouteIds;\n bytes[] bridgeImplDataItems;\n uint256[] bridgeRatios;\n bytes[] eventDataItems;\n }\n\n // Datastructure for Refuel-Swap-Bridge function\n struct RefuelSwapBridgeRequest {\n uint32 refuelRouteId;\n bytes refuelData;\n uint32 swapRouteId;\n bytes swapData;\n uint32 bridgeRouteId;\n bytes bridgeData;\n }\n\n // Datastructure for DeductFees-Swap function\n struct FeesTakerSwapRequest {\n address feesTakerAddress;\n address feesToken;\n uint256 feesAmount;\n uint32 routeId;\n bytes swapRequestData;\n }\n\n // Datastructure for DeductFees-Bridge function\n struct FeesTakerBridgeRequest {\n address feesTakerAddress;\n address feesToken;\n uint256 feesAmount;\n uint32 routeId;\n bytes bridgeRequestData;\n }\n\n // Datastructure for DeductFees-MultiBridge function\n struct FeesTakerMultiBridgeRequest {\n address feesTakerAddress;\n address feesToken;\n uint256 feesAmount;\n uint32[] bridgeRouteIds;\n bytes[] bridgeRequestDataItems;\n }\n\n // Datastructure for DeductFees-Swap-Bridge function\n struct FeesTakerSwapBridgeRequest {\n address feesTakerAddress;\n address feesToken;\n uint256 feesAmount;\n uint32 swapRouteId;\n bytes swapData;\n uint32 bridgeRouteId;\n bytes bridgeData;\n }\n\n // Datastructure for DeductFees-Refuel-Swap-Bridge function\n struct FeesTakerRefuelSwapBridgeRequest {\n address feesTakerAddress;\n address feesToken;\n uint256 feesAmount;\n uint32 refuelRouteId;\n bytes refuelData;\n uint32 swapRouteId;\n bytes swapData;\n uint32 bridgeRouteId;\n bytes bridgeData;\n }\n}\n"},"src/utils/Ownable.sol":{"content":"// SPDX-License-Identifier: GPL-3.0-only\npragma solidity ^0.8.4;\n\nimport {OnlyOwner, OnlyNominee} from \"../errors/SocketErrors.sol\";\n\nabstract contract Ownable {\n address private _owner;\n address private _nominee;\n\n event OwnerNominated(address indexed nominee);\n event OwnerClaimed(address indexed claimer);\n\n constructor(address owner_) {\n _claimOwner(owner_);\n }\n\n modifier onlyOwner() {\n if (msg.sender != _owner) {\n revert OnlyOwner();\n }\n _;\n }\n\n function owner() public view returns (address) {\n return _owner;\n }\n\n function nominee() public view returns (address) {\n return _nominee;\n }\n\n function nominateOwner(address nominee_) external {\n if (msg.sender != _owner) {\n revert OnlyOwner();\n }\n _nominee = nominee_;\n emit OwnerNominated(_nominee);\n }\n\n function claimOwner() external {\n if (msg.sender != _nominee) {\n revert OnlyNominee();\n }\n _claimOwner(msg.sender);\n }\n\n function _claimOwner(address claimer_) internal {\n _owner = claimer_;\n _nominee = address(0);\n emit OwnerClaimed(claimer_);\n }\n}\n"},"lib/solmate/src/tokens/ERC20.sol":{"content":"// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\n/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.\n/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)\n/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)\n/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.\nabstract contract ERC20 {\n /*//////////////////////////////////////////////////////////////\n EVENTS\n //////////////////////////////////////////////////////////////*/\n\n event Transfer(address indexed from, address indexed to, uint256 amount);\n\n event Approval(address indexed owner, address indexed spender, uint256 amount);\n\n /*//////////////////////////////////////////////////////////////\n METADATA STORAGE\n //////////////////////////////////////////////////////////////*/\n\n string public name;\n\n string public symbol;\n\n uint8 public immutable decimals;\n\n /*//////////////////////////////////////////////////////////////\n ERC20 STORAGE\n //////////////////////////////////////////////////////////////*/\n\n uint256 public totalSupply;\n\n mapping(address => uint256) public balanceOf;\n\n mapping(address => mapping(address => uint256)) public allowance;\n\n /*//////////////////////////////////////////////////////////////\n EIP-2612 STORAGE\n //////////////////////////////////////////////////////////////*/\n\n uint256 internal immutable INITIAL_CHAIN_ID;\n\n bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;\n\n mapping(address => uint256) public nonces;\n\n /*//////////////////////////////////////////////////////////////\n CONSTRUCTOR\n //////////////////////////////////////////////////////////////*/\n\n constructor(\n string memory _name,\n string memory _symbol,\n uint8 _decimals\n ) {\n name = _name;\n symbol = _symbol;\n decimals = _decimals;\n\n INITIAL_CHAIN_ID = block.chainid;\n INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();\n }\n\n /*//////////////////////////////////////////////////////////////\n ERC20 LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function approve(address spender, uint256 amount) public virtual returns (bool) {\n allowance[msg.sender][spender] = amount;\n\n emit Approval(msg.sender, spender, amount);\n\n return true;\n }\n\n function transfer(address to, uint256 amount) public virtual returns (bool) {\n balanceOf[msg.sender] -= amount;\n\n // Cannot overflow because the sum of all user\n // balances can't exceed the max uint256 value.\n unchecked {\n balanceOf[to] += amount;\n }\n\n emit Transfer(msg.sender, to, amount);\n\n return true;\n }\n\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) public virtual returns (bool) {\n uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.\n\n if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;\n\n balanceOf[from] -= amount;\n\n // Cannot overflow because the sum of all user\n // balances can't exceed the max uint256 value.\n unchecked {\n balanceOf[to] += amount;\n }\n\n emit Transfer(from, to, amount);\n\n return true;\n }\n\n /*//////////////////////////////////////////////////////////////\n EIP-2612 LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) public virtual {\n require(deadline >= block.timestamp, \"PERMIT_DEADLINE_EXPIRED\");\n\n // Unchecked because the only math done is incrementing\n // the owner's nonce which cannot realistically overflow.\n unchecked {\n address recoveredAddress = ecrecover(\n keccak256(\n abi.encodePacked(\n \"\\x19\\x01\",\n DOMAIN_SEPARATOR(),\n keccak256(\n abi.encode(\n keccak256(\n \"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)\"\n ),\n owner,\n spender,\n value,\n nonces[owner]++,\n deadline\n )\n )\n )\n ),\n v,\n r,\n s\n );\n\n require(recoveredAddress != address(0) && recoveredAddress == owner, \"INVALID_SIGNER\");\n\n allowance[recoveredAddress][spender] = value;\n }\n\n emit Approval(owner, spender, value);\n }\n\n function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {\n return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();\n }\n\n function computeDomainSeparator() internal view virtual returns (bytes32) {\n return\n keccak256(\n abi.encode(\n keccak256(\"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\"),\n keccak256(bytes(name)),\n keccak256(\"1\"),\n block.chainid,\n address(this)\n )\n );\n }\n\n /*//////////////////////////////////////////////////////////////\n INTERNAL MINT/BURN LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function _mint(address to, uint256 amount) internal virtual {\n totalSupply += amount;\n\n // Cannot overflow because the sum of all user\n // balances can't exceed the max uint256 value.\n unchecked {\n balanceOf[to] += amount;\n }\n\n emit Transfer(address(0), to, amount);\n }\n\n function _burn(address from, uint256 amount) internal virtual {\n balanceOf[from] -= amount;\n\n // Cannot underflow because a user's balance\n // will never be larger than the total supply.\n unchecked {\n totalSupply -= amount;\n }\n\n emit Transfer(from, address(0), amount);\n }\n}\n"},"src/controllers/RefuelSwapAndBridgeController.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {ISocketRequest} from \"../interfaces/ISocketRequest.sol\";\nimport {ISocketRoute} from \"../interfaces/ISocketRoute.sol\";\nimport {BaseController} from \"./BaseController.sol\";\n\n/**\n * @title RefuelSwapAndBridge Controller Implementation\n * @notice Controller with composed actions for Refuel,Swap and Bridge to be executed Sequentially and this is atomic\n * @author Socket dot tech.\n */\ncontract RefuelSwapAndBridgeController is BaseController {\n /// @notice Function-selector to invoke refuel-swap-bridge function\n /// @dev This function selector is to be used while buidling transaction-data\n bytes4 public immutable REFUEL_SWAP_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"refuelAndSwapAndBridge((uint32,bytes,uint32,bytes,uint32,bytes))\"\n )\n );\n\n /// @notice socketGatewayAddress to be initialised via storage variable BaseController\n constructor(\n address _socketGatewayAddress\n ) BaseController(_socketGatewayAddress) {}\n\n /**\n * @notice function to handle refuel followed by Swap and Bridge actions\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param rsbRequest Request with data to execute refuel followed by swap and bridge\n * @return output data from bridging operation\n */\n function refuelAndSwapAndBridge(\n ISocketRequest.RefuelSwapBridgeRequest calldata rsbRequest\n ) public payable returns (bytes memory) {\n _executeRoute(rsbRequest.refuelRouteId, rsbRequest.refuelData);\n\n // refuel is also a bridging activity via refuel-route-implementation\n bytes memory swapResponseData = _executeRoute(\n rsbRequest.swapRouteId,\n rsbRequest.swapData\n );\n\n uint256 swapAmount = abi.decode(swapResponseData, (uint256));\n\n //sequence of arguments for implData: amount, token, data\n // Bridging the swapAmount received in the preceeding step\n bytes memory bridgeImpldata = abi.encodeWithSelector(\n BRIDGE_AFTER_SWAP_SELECTOR,\n swapAmount,\n rsbRequest.bridgeData\n );\n\n return _executeRoute(rsbRequest.bridgeRouteId, bridgeImpldata);\n }\n}\n"},"src/interfaces/ISocketGateway.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n/**\n * @title ISocketGateway\n * @notice Interface for SocketGateway functions.\n * @dev functions can be added here for invocation from external contracts or off-chain\n * @author Socket dot tech.\n */\ninterface ISocketGateway {\n /**\n * @notice Request-struct for controllerRequests\n * @dev ensure the value for data is generated using the function-selectors defined in the controllerImplementation contracts\n */\n struct SocketControllerRequest {\n // controllerId is the id mapped to the controllerAddress\n uint32 controllerId;\n // transactionImplData generated off-chain or by caller using function-selector of the controllerContract\n bytes data;\n }\n\n // @notice view to get owner-address\n function owner() external view returns (address);\n}\n"},"src/libraries/Pb.sol":{"content":"// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity ^0.8.4;\n\n// runtime proto sol library\nlibrary Pb {\n enum WireType {\n Varint,\n Fixed64,\n LengthDelim,\n StartGroup,\n EndGroup,\n Fixed32\n }\n\n struct Buffer {\n uint256 idx; // the start index of next read. when idx=b.length, we're done\n bytes b; // hold serialized proto msg, readonly\n }\n\n // create a new in-memory Buffer object from raw msg bytes\n function fromBytes(\n bytes memory raw\n ) internal pure returns (Buffer memory buf) {\n buf.b = raw;\n buf.idx = 0;\n }\n\n // whether there are unread bytes\n function hasMore(Buffer memory buf) internal pure returns (bool) {\n return buf.idx < buf.b.length;\n }\n\n // decode current field number and wiretype\n function decKey(\n Buffer memory buf\n ) internal pure returns (uint256 tag, WireType wiretype) {\n uint256 v = decVarint(buf);\n tag = v / 8;\n wiretype = WireType(v & 7);\n }\n\n // read varint from current buf idx, move buf.idx to next read, return the int value\n function decVarint(Buffer memory buf) internal pure returns (uint256 v) {\n bytes10 tmp; // proto int is at most 10 bytes (7 bits can be used per byte)\n bytes memory bb = buf.b; // get buf.b mem addr to use in assembly\n v = buf.idx; // use v to save one additional uint variable\n assembly {\n tmp := mload(add(add(bb, 32), v)) // load 10 bytes from buf.b[buf.idx] to tmp\n }\n uint256 b; // store current byte content\n v = 0; // reset to 0 for return value\n for (uint256 i = 0; i < 10; i++) {\n assembly {\n b := byte(i, tmp) // don't use tmp[i] because it does bound check and costs extra\n }\n v |= (b & 0x7F) << (i * 7);\n if (b & 0x80 == 0) {\n buf.idx += i + 1;\n return v;\n }\n }\n revert(); // i=10, invalid varint stream\n }\n\n // read length delimited field and return bytes\n function decBytes(\n Buffer memory buf\n ) internal pure returns (bytes memory b) {\n uint256 len = decVarint(buf);\n uint256 end = buf.idx + len;\n require(end <= buf.b.length); // avoid overflow\n b = new bytes(len);\n bytes memory bufB = buf.b; // get buf.b mem addr to use in assembly\n uint256 bStart;\n uint256 bufBStart = buf.idx;\n assembly {\n bStart := add(b, 32)\n bufBStart := add(add(bufB, 32), bufBStart)\n }\n for (uint256 i = 0; i < len; i += 32) {\n assembly {\n mstore(add(bStart, i), mload(add(bufBStart, i)))\n }\n }\n buf.idx = end;\n }\n\n // move idx pass current value field, to beginning of next tag or msg end\n function skipValue(Buffer memory buf, WireType wire) internal pure {\n if (wire == WireType.Varint) {\n decVarint(buf);\n } else if (wire == WireType.LengthDelim) {\n uint256 len = decVarint(buf);\n buf.idx += len; // skip len bytes value data\n require(buf.idx <= buf.b.length); // avoid overflow\n } else {\n revert();\n } // unsupported wiretype\n }\n\n function _uint256(bytes memory b) internal pure returns (uint256 v) {\n require(b.length <= 32); // b's length must be smaller than or equal to 32\n assembly {\n v := mload(add(b, 32))\n } // load all 32bytes to v\n v = v >> (8 * (32 - b.length)); // only first b.length is valid\n }\n\n function _address(bytes memory b) internal pure returns (address v) {\n v = _addressPayable(b);\n }\n\n function _addressPayable(\n bytes memory b\n ) internal pure returns (address payable v) {\n require(b.length == 20);\n //load 32bytes then shift right 12 bytes\n assembly {\n v := div(mload(add(b, 32)), 0x1000000000000000000000000)\n }\n }\n\n function _bytes32(bytes memory b) internal pure returns (bytes32 v) {\n require(b.length == 32);\n assembly {\n v := mload(add(b, 32))\n }\n }\n}\n"},"src/bridges/BridgeImplBase.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {ISocketGateway} from \"../interfaces/ISocketGateway.sol\";\nimport {ISocketRoute} from \"../interfaces/ISocketRoute.sol\";\nimport {OnlySocketGatewayOwner, OnlySocketDeployer} from \"../errors/SocketErrors.sol\";\n\n/**\n * @title Abstract Implementation Contract.\n * @notice All Bridge Implementation will follow this interface.\n */\nabstract contract BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n /// @notice Address used to identify if it is a native token transfer or not\n address public immutable NATIVE_TOKEN_ADDRESS =\n address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);\n\n /// @notice immutable variable to store the socketGateway address\n address public immutable socketGateway;\n\n /// @notice immutable variable to store the socketGateway address\n address public immutable socketDeployFactory;\n\n /// @notice immutable variable with instance of SocketRoute to access route functions\n ISocketRoute public immutable socketRoute;\n\n /// @notice FunctionSelector used to delegatecall from swap to the function of bridge router implementation\n bytes4 public immutable BRIDGE_AFTER_SWAP_SELECTOR =\n bytes4(keccak256(\"bridgeAfterSwap(uint256,bytes)\"));\n\n /****************************************\n * EVENTS *\n ****************************************/\n\n event SocketBridge(\n uint256 amount,\n address token,\n uint256 toChainId,\n bytes32 bridgeName,\n address sender,\n address receiver,\n bytes32 metadata\n );\n\n /**\n * @notice Construct the base for all BridgeImplementations.\n * @param _socketGateway Socketgateway address, an immutable variable to set.\n * @param _socketDeployFactory Socket Deploy Factory address, an immutable variable to set.\n */\n constructor(address _socketGateway, address _socketDeployFactory) {\n socketGateway = _socketGateway;\n socketDeployFactory = _socketDeployFactory;\n socketRoute = ISocketRoute(_socketGateway);\n }\n\n /****************************************\n * MODIFIERS *\n ****************************************/\n\n /// @notice Implementing contract needs to make use of the modifier where restricted access is to be used\n modifier isSocketGatewayOwner() {\n if (msg.sender != ISocketGateway(socketGateway).owner()) {\n revert OnlySocketGatewayOwner();\n }\n _;\n }\n\n /// @notice Implementing contract needs to make use of the modifier where restricted access is to be used\n modifier isSocketDeployFactory() {\n if (msg.sender != socketDeployFactory) {\n revert OnlySocketDeployer();\n }\n _;\n }\n\n /****************************************\n * RESTRICTED FUNCTIONS *\n ****************************************/\n\n /**\n * @notice function to rescue the ERC20 tokens in the bridge Implementation contract\n * @notice this is a function restricted to Owner of SocketGateway only\n * @param token address of ERC20 token being rescued\n * @param userAddress receipient address to which ERC20 tokens will be rescued to\n * @param amount amount of ERC20 tokens being rescued\n */\n function rescueFunds(\n address token,\n address userAddress,\n uint256 amount\n ) external isSocketGatewayOwner {\n ERC20(token).safeTransfer(userAddress, amount);\n }\n\n /**\n * @notice function to rescue the native-balance in the bridge Implementation contract\n * @notice this is a function restricted to Owner of SocketGateway only\n * @param userAddress receipient address to which native-balance will be rescued to\n * @param amount amount of native balance tokens being rescued\n */\n function rescueEther(\n address payable userAddress,\n uint256 amount\n ) external isSocketGatewayOwner {\n userAddress.transfer(amount);\n }\n\n function killme() external isSocketDeployFactory {\n selfdestruct(payable(msg.sender));\n }\n\n /******************************\n * VIRTUAL FUNCTIONS *\n *****************************/\n\n /**\n * @notice function to bridge which is succeeding the swap function\n * @notice this function is to be used only when bridging as a succeeding step\n * @notice All bridge implementation contracts must implement this function\n * @notice bridge-implementations will have a bridge specific struct with properties used in bridging\n * @param bridgeData encoded value of properties in the bridgeData Struct\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable virtual;\n}\n"},"src/bridges/cbridge/CelerImpl.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport \"../../libraries/Pb.sol\";\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport \"./interfaces/cbridge.sol\";\nimport \"./interfaces/ICelerStorageWrapper.sol\";\nimport {TransferIdExists, InvalidCelerRefund, CelerAlreadyRefunded, CelerRefundNotReady} from \"../../errors/SocketErrors.sol\";\nimport {BridgeImplBase} from \"../BridgeImplBase.sol\";\nimport {CBRIDGE} from \"../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Celer-Route Implementation\n * @notice Route implementation with functions to bridge ERC20 and Native via Celer-Bridge\n * Called via SocketGateway if the routeId in the request maps to the routeId of CelerImplementation\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ncontract CelerImpl is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable CBridgeIdentifier = CBRIDGE;\n\n /// @notice Utility to perform operation on Buffer\n using Pb for Pb.Buffer;\n\n /// @notice Function-selector for ERC20-token bridging on Celer-Route\n /// @dev This function selector is to be used while building transaction-data to bridge ERC20 tokens\n bytes4 public immutable CELER_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(address,address,uint256,bytes32,uint64,uint64,uint32)\"\n )\n );\n\n /// @notice Function-selector for Native bridging on Celer-Route\n /// @dev This function selector is to be used while building transaction-data to bridge Native tokens\n bytes4 public immutable CELER_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeNativeTo(address,uint256,bytes32,uint64,uint64,uint32)\"\n )\n );\n\n bytes4 public immutable CELER_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(address,uint64,uint32,uint64,bytes32))\"\n )\n );\n\n /// @notice router Contract instance used to deposit ERC20 and Native on to Celer-Bridge\n /// @dev contract instance is to be initialized in the constructor using the routerAddress passed as constructor argument\n ICBridge public immutable router;\n\n /// @notice celerStorageWrapper Contract instance used to store the transferId generated during ERC20 and Native bridge on to Celer-Bridge\n /// @dev contract instance is to be initialized in the constructor using the celerStorageWrapperAddress passed as constructor argument\n ICelerStorageWrapper public immutable celerStorageWrapper;\n\n /// @notice WETH token address\n address public immutable weth;\n\n /// @notice chainId used during generation of transferId generated while bridging ERC20 and Native on to Celer-Bridge\n /// @dev this is to be initialised in the constructor\n uint64 public immutable chainId;\n\n struct WithdrawMsg {\n uint64 chainid; // tag: 1\n uint64 seqnum; // tag: 2\n address receiver; // tag: 3\n address token; // tag: 4\n uint256 amount; // tag: 5\n bytes32 refid; // tag: 6\n }\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n /// @dev ensure routerAddress, weth-address, celerStorageWrapperAddress are set properly for the chainId in which the contract is being deployed\n constructor(\n address _routerAddress,\n address _weth,\n address _celerStorageWrapperAddress,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n router = ICBridge(_routerAddress);\n celerStorageWrapper = ICelerStorageWrapper(_celerStorageWrapperAddress);\n weth = _weth;\n chainId = uint64(block.chainid);\n }\n\n // Function to receive Ether. msg.data must be empty\n receive() external payable {}\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct CelerBridgeDataNoToken {\n address receiverAddress;\n uint64 toChainId;\n uint32 maxSlippage;\n uint64 nonce;\n bytes32 metadata;\n }\n\n struct CelerBridgeData {\n address token;\n address receiverAddress;\n uint64 toChainId;\n uint32 maxSlippage;\n uint64 nonce;\n bytes32 metadata;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in CelerBridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for CelerBridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n CelerBridgeData memory celerBridgeData = abi.decode(\n bridgeData,\n (CelerBridgeData)\n );\n\n if (celerBridgeData.token == NATIVE_TOKEN_ADDRESS) {\n // transferId is generated using the request-params and nonce of the account\n // transferId should be unique for each request and this is used while handling refund from celerBridge\n bytes32 transferId = keccak256(\n abi.encodePacked(\n address(this),\n celerBridgeData.receiverAddress,\n weth,\n amount,\n celerBridgeData.toChainId,\n celerBridgeData.nonce,\n chainId\n )\n );\n\n // transferId is stored in CelerStorageWrapper with in a mapping where key is transferId and value is the msg-sender\n celerStorageWrapper.setAddressForTransferId(transferId, msg.sender);\n\n router.sendNative{value: amount}(\n celerBridgeData.receiverAddress,\n amount,\n celerBridgeData.toChainId,\n celerBridgeData.nonce,\n celerBridgeData.maxSlippage\n );\n } else {\n // transferId is generated using the request-params and nonce of the account\n // transferId should be unique for each request and this is used while handling refund from celerBridge\n bytes32 transferId = keccak256(\n abi.encodePacked(\n address(this),\n celerBridgeData.receiverAddress,\n celerBridgeData.token,\n amount,\n celerBridgeData.toChainId,\n celerBridgeData.nonce,\n chainId\n )\n );\n\n // transferId is stored in CelerStorageWrapper with in a mapping where key is transferId and value is the msg-sender\n celerStorageWrapper.setAddressForTransferId(transferId, msg.sender);\n router.send(\n celerBridgeData.receiverAddress,\n celerBridgeData.token,\n amount,\n celerBridgeData.toChainId,\n celerBridgeData.nonce,\n celerBridgeData.maxSlippage\n );\n }\n\n emit SocketBridge(\n amount,\n celerBridgeData.token,\n celerBridgeData.toChainId,\n CBridgeIdentifier,\n msg.sender,\n celerBridgeData.receiverAddress,\n celerBridgeData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in CelerBridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param celerBridgeData encoded data for CelerBridgeData\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n CelerBridgeDataNoToken calldata celerBridgeData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n\n if (token == NATIVE_TOKEN_ADDRESS) {\n // transferId is generated using the request-params and nonce of the account\n // transferId should be unique for each request and this is used while handling refund from celerBridge\n bytes32 transferId = keccak256(\n abi.encodePacked(\n address(this),\n celerBridgeData.receiverAddress,\n weth,\n bridgeAmount,\n celerBridgeData.toChainId,\n celerBridgeData.nonce,\n chainId\n )\n );\n\n // transferId is stored in CelerStorageWrapper with in a mapping where key is transferId and value is the msg-sender\n celerStorageWrapper.setAddressForTransferId(transferId, msg.sender);\n\n router.sendNative{value: bridgeAmount}(\n celerBridgeData.receiverAddress,\n bridgeAmount,\n celerBridgeData.toChainId,\n celerBridgeData.nonce,\n celerBridgeData.maxSlippage\n );\n } else {\n // transferId is generated using the request-params and nonce of the account\n // transferId should be unique for each request and this is used while handling refund from celerBridge\n bytes32 transferId = keccak256(\n abi.encodePacked(\n address(this),\n celerBridgeData.receiverAddress,\n token,\n bridgeAmount,\n celerBridgeData.toChainId,\n celerBridgeData.nonce,\n chainId\n )\n );\n\n // transferId is stored in CelerStorageWrapper with in a mapping where key is transferId and value is the msg-sender\n celerStorageWrapper.setAddressForTransferId(transferId, msg.sender);\n router.send(\n celerBridgeData.receiverAddress,\n token,\n bridgeAmount,\n celerBridgeData.toChainId,\n celerBridgeData.nonce,\n celerBridgeData.maxSlippage\n );\n }\n\n emit SocketBridge(\n bridgeAmount,\n token,\n celerBridgeData.toChainId,\n CBridgeIdentifier,\n msg.sender,\n celerBridgeData.receiverAddress,\n celerBridgeData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via Celer-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param receiverAddress address of recipient\n * @param token address of token being bridged\n * @param amount amount of token for bridging\n * @param toChainId destination ChainId\n * @param nonce nonce of the sender-account address\n * @param maxSlippage maximum Slippage for the bridging\n */\n function bridgeERC20To(\n address receiverAddress,\n address token,\n uint256 amount,\n bytes32 metadata,\n uint64 toChainId,\n uint64 nonce,\n uint32 maxSlippage\n ) external payable {\n /// @notice transferId is generated using the request-params and nonce of the account\n /// @notice transferId should be unique for each request and this is used while handling refund from celerBridge\n bytes32 transferId = keccak256(\n abi.encodePacked(\n address(this),\n receiverAddress,\n token,\n amount,\n toChainId,\n nonce,\n chainId\n )\n );\n\n /// @notice stored in the CelerStorageWrapper contract\n celerStorageWrapper.setAddressForTransferId(transferId, msg.sender);\n\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n router.send(\n receiverAddress,\n token,\n amount,\n toChainId,\n nonce,\n maxSlippage\n );\n\n emit SocketBridge(\n amount,\n token,\n toChainId,\n CBridgeIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n\n /**\n * @notice function to handle Native bridging to receipent via Celer-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param receiverAddress address of recipient\n * @param amount amount of token for bridging\n * @param toChainId destination ChainId\n * @param nonce nonce of the sender-account address\n * @param maxSlippage maximum Slippage for the bridging\n */\n function bridgeNativeTo(\n address receiverAddress,\n uint256 amount,\n bytes32 metadata,\n uint64 toChainId,\n uint64 nonce,\n uint32 maxSlippage\n ) external payable {\n bytes32 transferId = keccak256(\n abi.encodePacked(\n address(this),\n receiverAddress,\n weth,\n amount,\n toChainId,\n nonce,\n chainId\n )\n );\n\n celerStorageWrapper.setAddressForTransferId(transferId, msg.sender);\n\n router.sendNative{value: amount}(\n receiverAddress,\n amount,\n toChainId,\n nonce,\n maxSlippage\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n toChainId,\n CBridgeIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n\n /**\n * @notice function to handle refund from CelerBridge-Router\n * @param _request request data generated offchain using the celer-SDK\n * @param _sigs generated offchain using the celer-SDK\n * @param _signers generated offchain using the celer-SDK\n * @param _powers generated offchain using the celer-SDK\n */\n function refundCelerUser(\n bytes calldata _request,\n bytes[] calldata _sigs,\n address[] calldata _signers,\n uint256[] calldata _powers\n ) external payable {\n WithdrawMsg memory request = decWithdrawMsg(_request);\n bytes32 transferId = keccak256(\n abi.encodePacked(\n request.chainid,\n request.seqnum,\n request.receiver,\n request.token,\n request.amount\n )\n );\n uint256 _initialNativeBalance = address(this).balance;\n uint256 _initialTokenBalance = ERC20(request.token).balanceOf(\n address(this)\n );\n if (!router.withdraws(transferId)) {\n router.withdraw(_request, _sigs, _signers, _powers);\n }\n\n if (request.receiver != socketGateway) {\n revert InvalidCelerRefund();\n }\n\n address _receiver = celerStorageWrapper.getAddressFromTransferId(\n request.refid\n );\n celerStorageWrapper.deleteTransferId(request.refid);\n\n if (_receiver == address(0)) {\n revert CelerAlreadyRefunded();\n }\n\n uint256 _nativeBalanceAfter = address(this).balance;\n uint256 _tokenBalanceAfter = ERC20(request.token).balanceOf(\n address(this)\n );\n if (_nativeBalanceAfter > _initialNativeBalance) {\n if ((_nativeBalanceAfter - _initialNativeBalance) != request.amount)\n revert CelerRefundNotReady();\n payable(_receiver).transfer(request.amount);\n return;\n }\n\n if (_tokenBalanceAfter > _initialTokenBalance) {\n if ((_tokenBalanceAfter - _initialTokenBalance) != request.amount)\n revert CelerRefundNotReady();\n ERC20(request.token).safeTransfer(_receiver, request.amount);\n return;\n }\n\n revert CelerRefundNotReady();\n }\n\n function decWithdrawMsg(\n bytes memory raw\n ) internal pure returns (WithdrawMsg memory m) {\n Pb.Buffer memory buf = Pb.fromBytes(raw);\n\n uint256 tag;\n Pb.WireType wire;\n while (buf.hasMore()) {\n (tag, wire) = buf.decKey();\n if (false) {}\n // solidity has no switch/case\n else if (tag == 1) {\n m.chainid = uint64(buf.decVarint());\n } else if (tag == 2) {\n m.seqnum = uint64(buf.decVarint());\n } else if (tag == 3) {\n m.receiver = Pb._address(buf.decBytes());\n } else if (tag == 4) {\n m.token = Pb._address(buf.decBytes());\n } else if (tag == 5) {\n m.amount = Pb._uint256(buf.decBytes());\n } else if (tag == 6) {\n m.refid = Pb._bytes32(buf.decBytes());\n } else {\n buf.skipValue(wire);\n } // skip value of unknown tag\n }\n } // end decoder WithdrawMsg\n}\n"},"src/libraries/LibBytes.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\n// Functions taken out from https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol\nlibrary LibBytes {\n // solhint-disable no-inline-assembly\n\n // LibBytes specific errors\n error SliceOverflow();\n error SliceOutOfBounds();\n error AddressOutOfBounds();\n error UintOutOfBounds();\n\n // -------------------------\n\n function concat(\n bytes memory _preBytes,\n bytes memory _postBytes\n ) internal pure returns (bytes memory) {\n bytes memory tempBytes;\n\n assembly {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // Store the length of the first bytes array at the beginning of\n // the memory for tempBytes.\n let length := mload(_preBytes)\n mstore(tempBytes, length)\n\n // Maintain a memory counter for the current write location in the\n // temp bytes array by adding the 32 bytes for the array length to\n // the starting location.\n let mc := add(tempBytes, 0x20)\n // Stop copying when the memory counter reaches the length of the\n // first bytes array.\n let end := add(mc, length)\n\n for {\n // Initialize a copy counter to the start of the _preBytes data,\n // 32 bytes into its memory.\n let cc := add(_preBytes, 0x20)\n } lt(mc, end) {\n // Increase both counters by 32 bytes each iteration.\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n // Write the _preBytes data into the tempBytes memory 32 bytes\n // at a time.\n mstore(mc, mload(cc))\n }\n\n // Add the length of _postBytes to the current length of tempBytes\n // and store it as the new length in the first 32 bytes of the\n // tempBytes memory.\n length := mload(_postBytes)\n mstore(tempBytes, add(length, mload(tempBytes)))\n\n // Move the memory counter back from a multiple of 0x20 to the\n // actual end of the _preBytes data.\n mc := end\n // Stop copying when the memory counter reaches the new combined\n // length of the arrays.\n end := add(mc, length)\n\n for {\n let cc := add(_postBytes, 0x20)\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n // Update the free-memory pointer by padding our last write location\n // to 32 bytes: add 31 bytes to the end of tempBytes to move to the\n // next 32 byte block, then round down to the nearest multiple of\n // 32. If the sum of the length of the two arrays is zero then add\n // one before rounding down to leave a blank 32 bytes (the length block with 0).\n mstore(\n 0x40,\n and(\n add(add(end, iszero(add(length, mload(_preBytes)))), 31),\n not(31) // Round down to the nearest 32 bytes.\n )\n )\n }\n\n return tempBytes;\n }\n\n function slice(\n bytes memory _bytes,\n uint256 _start,\n uint256 _length\n ) internal pure returns (bytes memory) {\n if (_length + 31 < _length) {\n revert SliceOverflow();\n }\n if (_bytes.length < _start + _length) {\n revert SliceOutOfBounds();\n }\n\n bytes memory tempBytes;\n\n assembly {\n switch iszero(_length)\n case 0 {\n // Get a location of some free memory and store it in tempBytes as\n // Solidity does for memory variables.\n tempBytes := mload(0x40)\n\n // The first word of the slice result is potentially a partial\n // word read from the original array. To read it, we calculate\n // the length of that partial word and start copying that many\n // bytes into the array. The first word we copy will start with\n // data we don't care about, but the last `lengthmod` bytes will\n // land at the beginning of the contents of the new array. When\n // we're done copying, we overwrite the full first word with\n // the actual length of the slice.\n let lengthmod := and(_length, 31)\n\n // The multiplication in the next line is necessary\n // because when slicing multiples of 32 bytes (lengthmod == 0)\n // the following copy loop was copying the origin's length\n // and then ending prematurely not copying everything it should.\n let mc := add(\n add(tempBytes, lengthmod),\n mul(0x20, iszero(lengthmod))\n )\n let end := add(mc, _length)\n\n for {\n // The multiplication in the next line has the same exact purpose\n // as the one above.\n let cc := add(\n add(\n add(_bytes, lengthmod),\n mul(0x20, iszero(lengthmod))\n ),\n _start\n )\n } lt(mc, end) {\n mc := add(mc, 0x20)\n cc := add(cc, 0x20)\n } {\n mstore(mc, mload(cc))\n }\n\n mstore(tempBytes, _length)\n\n //update free-memory pointer\n //allocating the array padded to 32 bytes like the compiler does now\n mstore(0x40, and(add(mc, 31), not(31)))\n }\n //if we want a zero-length slice let's just return a zero-length array\n default {\n tempBytes := mload(0x40)\n //zero out the 32 bytes slice we are about to return\n //we need to do it because Solidity does not garbage collect\n mstore(tempBytes, 0)\n\n mstore(0x40, add(tempBytes, 0x20))\n }\n }\n\n return tempBytes;\n }\n}\n"},"src/bridges/hyphen/interfaces/hyphen.sol":{"content":"// SPDX-License-Identifier: Apache-2.0\npragma solidity >=0.8.0;\n\n/**\n * @title HyphenLiquidityPoolManager\n * @notice interface with functions to bridge ERC20 and Native via Hyphen-Bridge\n * @author Socket dot tech.\n */\ninterface HyphenLiquidityPoolManager {\n /**\n * @dev Function used to deposit tokens into pool to initiate a cross chain token transfer.\n * @param toChainId Chain id where funds needs to be transfered\n * @param tokenAddress ERC20 Token address that needs to be transfered\n * @param receiver Address on toChainId where tokens needs to be transfered\n * @param amount Amount of token being transfered\n */\n function depositErc20(\n uint256 toChainId,\n address tokenAddress,\n address receiver,\n uint256 amount,\n string calldata tag\n ) external;\n\n /**\n * @dev Function used to deposit native token into pool to initiate a cross chain token transfer.\n * @param receiver Address on toChainId where tokens needs to be transfered\n * @param toChainId Chain id where funds needs to be transfered\n */\n function depositNative(\n address receiver,\n uint256 toChainId,\n string calldata tag\n ) external payable;\n}\n"},"src/swap/SwapImplBase.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport {ISocketGateway} from \"../interfaces/ISocketGateway.sol\";\nimport {OnlySocketGatewayOwner, OnlySocketDeployer} from \"../errors/SocketErrors.sol\";\n\n/**\n * @title Abstract Implementation Contract.\n * @notice All Swap Implementation will follow this interface.\n * @author Socket dot tech.\n */\nabstract contract SwapImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n /// @notice Address used to identify if it is a native token transfer or not\n address public immutable NATIVE_TOKEN_ADDRESS =\n address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);\n\n /// @notice immutable variable to store the socketGateway address\n address public immutable socketGateway;\n\n /// @notice immutable variable to store the socketGateway address\n address public immutable socketDeployFactory;\n\n /// @notice FunctionSelector used to delegatecall to the performAction function of swap-router-implementation\n bytes4 public immutable SWAP_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\"performAction(address,address,uint256,address,bytes)\")\n );\n\n /// @notice FunctionSelector used to delegatecall to the performActionWithIn function of swap-router-implementation\n bytes4 public immutable SWAP_WITHIN_FUNCTION_SELECTOR =\n bytes4(keccak256(\"performActionWithIn(address,address,uint256,bytes)\"));\n\n /****************************************\n * EVENTS *\n ****************************************/\n\n event SocketSwapTokens(\n address fromToken,\n address toToken,\n uint256 buyAmount,\n uint256 sellAmount,\n bytes32 routeName,\n address receiver\n );\n\n /**\n * @notice Construct the base for all SwapImplementations.\n * @param _socketGateway Socketgateway address, an immutable variable to set.\n */\n constructor(address _socketGateway, address _socketDeployFactory) {\n socketGateway = _socketGateway;\n socketDeployFactory = _socketDeployFactory;\n }\n\n /****************************************\n * MODIFIERS *\n ****************************************/\n\n /// @notice Implementing contract needs to make use of the modifier where restricted access is to be used\n modifier isSocketGatewayOwner() {\n if (msg.sender != ISocketGateway(socketGateway).owner()) {\n revert OnlySocketGatewayOwner();\n }\n _;\n }\n\n /// @notice Implementing contract needs to make use of the modifier where restricted access is to be used\n modifier isSocketDeployFactory() {\n if (msg.sender != socketDeployFactory) {\n revert OnlySocketDeployer();\n }\n _;\n }\n\n /****************************************\n * RESTRICTED FUNCTIONS *\n ****************************************/\n\n /**\n * @notice function to rescue the ERC20 tokens in the Swap-Implementation contract\n * @notice this is a function restricted to Owner of SocketGateway only\n * @param token address of ERC20 token being rescued\n * @param userAddress receipient address to which ERC20 tokens will be rescued to\n * @param amount amount of ERC20 tokens being rescued\n */\n function rescueFunds(\n address token,\n address userAddress,\n uint256 amount\n ) external isSocketGatewayOwner {\n ERC20(token).safeTransfer(userAddress, amount);\n }\n\n /**\n * @notice function to rescue the native-balance in the Swap-Implementation contract\n * @notice this is a function restricted to Owner of SocketGateway only\n * @param userAddress receipient address to which native-balance will be rescued to\n * @param amount amount of native balance tokens being rescued\n */\n function rescueEther(\n address payable userAddress,\n uint256 amount\n ) external isSocketGatewayOwner {\n userAddress.transfer(amount);\n }\n\n function killme() external isSocketDeployFactory {\n selfdestruct(payable(msg.sender));\n }\n\n /******************************\n * VIRTUAL FUNCTIONS *\n *****************************/\n\n /**\n * @notice function to swap tokens on the chain\n * All swap implementation contracts must implement this function\n * @param fromToken token to be swapped\n * @param toToken token to which fromToken has to be swapped\n * @param amount amount of fromToken being swapped\n * @param receiverAddress recipient address of toToken\n * @param data encoded value of properties in the swapData Struct\n */\n function performAction(\n address fromToken,\n address toToken,\n uint256 amount,\n address receiverAddress,\n bytes memory data\n ) external payable virtual returns (uint256);\n\n /**\n * @notice function to swapWith - swaps tokens on the chain to socketGateway as recipient\n * All swap implementation contracts must implement this function\n * @param fromToken token to be swapped\n * @param toToken token to which fromToken has to be swapped\n * @param amount amount of fromToken being swapped\n * @param swapExtraData encoded value of properties in the swapData Struct\n */\n function performActionWithIn(\n address fromToken,\n address toToken,\n uint256 amount,\n bytes memory swapExtraData\n ) external payable virtual returns (uint256, address);\n}\n"},"src/swap/zerox/ZeroXSwapImpl.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport \"../SwapImplBase.sol\";\nimport {Address0Provided, SwapFailed} from \"../../errors/SocketErrors.sol\";\nimport {ZEROX} from \"../../static/RouteIdentifiers.sol\";\n\n/**\n * @title ZeroX-Swap-Route Implementation\n * @notice Route implementation with functions to swap tokens via ZeroX-Swap\n * Called via SocketGateway if the routeId in the request maps to the routeId of ZeroX-Swap-Implementation\n * @author Socket dot tech.\n */\ncontract ZeroXSwapImpl is SwapImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable ZeroXIdentifier = ZEROX;\n\n /// @notice unique name to identify the router, used to emit event upon successful bridging\n bytes32 public immutable NAME = keccak256(\"Zerox-Router\");\n\n /// @notice address of ZeroX-Exchange-Proxy to swap the tokens on Chain\n address payable public immutable zeroXExchangeProxy;\n\n /// @notice socketGatewayAddress to be initialised via storage variable SwapImplBase\n /// @notice ZeroXExchangeProxy contract is payable to allow ethereum swaps\n /// @dev ensure _zeroXExchangeProxy are set properly for the chainId in which the contract is being deployed\n constructor(\n address _zeroXExchangeProxy,\n address _socketGateway,\n address _socketDeployFactory\n ) SwapImplBase(_socketGateway, _socketDeployFactory) {\n zeroXExchangeProxy = payable(_zeroXExchangeProxy);\n }\n\n receive() external payable {}\n\n fallback() external payable {}\n\n /**\n * @notice function to swap tokens on the chain and transfer to receiver address\n * @dev This is called only when there is a request for a swap.\n * @param fromToken token to be swapped\n * @param toToken token to which fromToken is to be swapped\n * @param amount amount to be swapped\n * @param receiverAddress address of toToken recipient\n * @param swapExtraData data required for zeroX Exchange to get the swap done\n */\n function performAction(\n address fromToken,\n address toToken,\n uint256 amount,\n address receiverAddress,\n bytes calldata swapExtraData\n ) external payable override returns (uint256) {\n if (fromToken == address(0)) {\n revert Address0Provided();\n }\n\n bytes memory swapCallData = abi.decode(swapExtraData, (bytes));\n\n uint256 _initialBalanceTokenOut;\n uint256 _finalBalanceTokenOut;\n\n ERC20 erc20ToToken = ERC20(toToken);\n if (toToken != NATIVE_TOKEN_ADDRESS) {\n _initialBalanceTokenOut = erc20ToToken.balanceOf(address(this));\n } else {\n _initialBalanceTokenOut = address(this).balance;\n }\n\n if (fromToken != NATIVE_TOKEN_ADDRESS) {\n ERC20 token = ERC20(fromToken);\n token.safeTransferFrom(msg.sender, address(this), amount);\n token.safeApprove(zeroXExchangeProxy, amount);\n\n // solhint-disable-next-line\n (bool success, ) = zeroXExchangeProxy.call(swapCallData);\n\n if (!success) {\n revert SwapFailed();\n }\n\n token.safeApprove(zeroXExchangeProxy, 0);\n } else {\n (bool success, ) = zeroXExchangeProxy.call{value: amount}(\n swapCallData\n );\n if (!success) {\n revert SwapFailed();\n }\n }\n\n if (toToken != NATIVE_TOKEN_ADDRESS) {\n _finalBalanceTokenOut = erc20ToToken.balanceOf(address(this));\n } else {\n _finalBalanceTokenOut = address(this).balance;\n }\n\n uint256 returnAmount = _finalBalanceTokenOut - _initialBalanceTokenOut;\n\n if (toToken == NATIVE_TOKEN_ADDRESS) {\n payable(receiverAddress).transfer(returnAmount);\n } else {\n erc20ToToken.transfer(receiverAddress, returnAmount);\n }\n\n emit SocketSwapTokens(\n fromToken,\n toToken,\n returnAmount,\n amount,\n ZeroXIdentifier,\n receiverAddress\n );\n\n return returnAmount;\n }\n\n /**\n * @notice function to swapWithIn SocketGateway - swaps tokens on the chain to socketGateway as recipient\n * @param fromToken token to be swapped\n * @param toToken token to which fromToken has to be swapped\n * @param amount amount of fromToken being swapped\n * @param swapExtraData encoded value of properties in the swapData Struct\n * @return swapped amount (in toToken Address)\n */\n function performActionWithIn(\n address fromToken,\n address toToken,\n uint256 amount,\n bytes calldata swapExtraData\n ) external payable override returns (uint256, address) {\n if (fromToken == address(0)) {\n revert Address0Provided();\n }\n\n bytes memory swapCallData = abi.decode(swapExtraData, (bytes));\n\n uint256 _initialBalanceTokenOut;\n uint256 _finalBalanceTokenOut;\n\n ERC20 erc20ToToken = ERC20(toToken);\n if (toToken != NATIVE_TOKEN_ADDRESS) {\n _initialBalanceTokenOut = erc20ToToken.balanceOf(address(this));\n } else {\n _initialBalanceTokenOut = address(this).balance;\n }\n\n if (fromToken != NATIVE_TOKEN_ADDRESS) {\n ERC20 token = ERC20(fromToken);\n token.safeTransferFrom(msg.sender, address(this), amount);\n token.safeApprove(zeroXExchangeProxy, amount);\n\n // solhint-disable-next-line\n (bool success, ) = zeroXExchangeProxy.call(swapCallData);\n\n if (!success) {\n revert SwapFailed();\n }\n\n token.safeApprove(zeroXExchangeProxy, 0);\n } else {\n (bool success, ) = zeroXExchangeProxy.call{value: amount}(\n swapCallData\n );\n if (!success) {\n revert SwapFailed();\n }\n }\n\n if (toToken != NATIVE_TOKEN_ADDRESS) {\n _finalBalanceTokenOut = erc20ToToken.balanceOf(address(this));\n } else {\n _finalBalanceTokenOut = address(this).balance;\n }\n\n uint256 returnAmount = _finalBalanceTokenOut - _initialBalanceTokenOut;\n\n emit SocketSwapTokens(\n fromToken,\n toToken,\n returnAmount,\n amount,\n ZeroXIdentifier,\n socketGateway\n );\n\n return (returnAmount, toToken);\n }\n}\n"},"src/bridges/cbridge/interfaces/cbridge.sol":{"content":"// SPDX-License-Identifier: Apache-2.0\npragma solidity >=0.8.0;\n\ninterface ICBridge {\n function send(\n address _receiver,\n address _token,\n uint256 _amount,\n uint64 _dstChinId,\n uint64 _nonce,\n uint32 _maxSlippage\n ) external;\n\n function sendNative(\n address _receiver,\n uint256 _amount,\n uint64 _dstChinId,\n uint64 _nonce,\n uint32 _maxSlippage\n ) external payable;\n\n function withdraws(bytes32 withdrawId) external view returns (bool);\n\n function withdraw(\n bytes calldata _wdmsg,\n bytes[] calldata _sigs,\n address[] calldata _signers,\n uint256[] calldata _powers\n ) external;\n}\n"},"src/bridges/stargate/l2/Stargate.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport {SafeTransferLib} from \"lib/solmate/src/utils/SafeTransferLib.sol\";\nimport {ERC20} from \"lib/solmate/src/tokens/ERC20.sol\";\nimport \"../interfaces/stargate.sol\";\nimport \"../../../errors/SocketErrors.sol\";\nimport {BridgeImplBase} from \"../../BridgeImplBase.sol\";\nimport {STARGATE} from \"../../../static/RouteIdentifiers.sol\";\n\n/**\n * @title Stargate-L2-Route Implementation\n * @notice Route implementation with functions to bridge ERC20 and Native via Stargate-L2-Bridge\n * Called via SocketGateway if the routeId in the request maps to the routeId of Stargate-L2-Implementation\n * Contains function to handle bridging as post-step i.e linked to a preceeding step for swap\n * RequestData is different to just bride and bridging chained with swap\n * @author Socket dot tech.\n */\ncontract StargateImplL2 is BridgeImplBase {\n /// @notice SafeTransferLib - library for safe and optimised operations on ERC20 tokens\n using SafeTransferLib for ERC20;\n\n bytes32 public immutable StargateIdentifier = STARGATE;\n\n /// @notice Function-selector for ERC20-token bridging on Stargate-L2-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge ERC20 tokens\n bytes4\n public immutable STARGATE_L2_ERC20_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeERC20To(address,address,address,uint256,uint256,uint256,(uint256,uint256,uint256,uint256,bytes32,bytes,uint16))\"\n )\n );\n\n bytes4 public immutable STARGATE_L1_SWAP_BRIDGE_SELECTOR =\n bytes4(\n keccak256(\n \"swapAndBridge(uint32,bytes,(address,address,uint16,uint256,uint256,uint256,uint256,uint256,uint256,bytes32,bytes))\"\n )\n );\n\n /// @notice Function-selector for Native bridging on Stargate-L2-Route\n /// @dev This function selector is to be used while buidling transaction-data to bridge Native tokens\n bytes4\n public immutable STARGATE_L2_NATIVE_EXTERNAL_BRIDGE_FUNCTION_SELECTOR =\n bytes4(\n keccak256(\n \"bridgeNativeTo(address,address,uint16,uint256,uint256,uint256,bytes32)\"\n )\n );\n\n /// @notice Stargate Router to bridge ERC20 tokens\n IBridgeStargate public immutable router;\n\n /// @notice Stargate Router to bridge native tokens\n IBridgeStargate public immutable routerETH;\n\n /// @notice socketGatewayAddress to be initialised via storage variable BridgeImplBase\n /// @dev ensure router, routerEth are set properly for the chainId in which the contract is being deployed\n constructor(\n address _router,\n address _routerEth,\n address _socketGateway,\n address _socketDeployFactory\n ) BridgeImplBase(_socketGateway, _socketDeployFactory) {\n router = IBridgeStargate(_router);\n routerETH = IBridgeStargate(_routerEth);\n }\n\n /// @notice Struct to be used as a input parameter for Bridging tokens via Stargate-L2-route\n /// @dev while building transactionData,values should be set in this sequence of properties in this struct\n struct StargateBridgeExtraData {\n uint256 srcPoolId;\n uint256 dstPoolId;\n uint256 destinationGasLimit;\n uint256 minReceivedAmt;\n bytes32 metadata;\n bytes destinationPayload;\n uint16 stargateDstChainId; // stargate defines chain id in its way\n }\n\n /// @notice Struct to be used in decode step from input parameter - a specific case of bridging after swap.\n /// @dev the data being encoded in offchain or by caller should have values set in this sequence of properties in this struct\n struct StargateBridgeDataNoToken {\n address receiverAddress;\n address senderAddress;\n uint16 stargateDstChainId; // stargate defines chain id in its way\n uint256 value;\n // a unique identifier that is uses to dedup transfers\n // this value is the a timestamp sent from frontend, but in theory can be any unique number\n uint256 srcPoolId;\n uint256 dstPoolId;\n uint256 minReceivedAmt; // defines the slippage, the min qty you would accept on the destination\n uint256 optionalValue;\n uint256 destinationGasLimit;\n bytes32 metadata;\n bytes destinationPayload;\n }\n\n struct StargateBridgeData {\n address token;\n address receiverAddress;\n address senderAddress;\n uint16 stargateDstChainId; // stargate defines chain id in its way\n uint256 value;\n // a unique identifier that is uses to dedup transfers\n // this value is the a timestamp sent from frontend, but in theory can be any unique number\n uint256 srcPoolId;\n uint256 dstPoolId;\n uint256 minReceivedAmt; // defines the slippage, the min qty you would accept on the destination\n uint256 optionalValue;\n uint256 destinationGasLimit;\n bytes32 metadata;\n bytes destinationPayload;\n }\n\n /**\n * @notice function to bridge tokens after swap.\n * @notice this is different from swapAndBridge, this function is called when the swap has already happened at a different place.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in Stargate-BridgeData struct\n * @param amount amount of tokens being bridged. this can be ERC20 or native\n * @param bridgeData encoded data for Stargate-L1-Bridge\n */\n function bridgeAfterSwap(\n uint256 amount,\n bytes calldata bridgeData\n ) external payable override {\n StargateBridgeData memory stargateBridgeData = abi.decode(\n bridgeData,\n (StargateBridgeData)\n );\n\n if (stargateBridgeData.token == NATIVE_TOKEN_ADDRESS) {\n // perform bridging\n routerETH.swapETH{value: amount + stargateBridgeData.optionalValue}(\n stargateBridgeData.stargateDstChainId,\n payable(stargateBridgeData.senderAddress),\n abi.encodePacked(stargateBridgeData.receiverAddress),\n amount,\n stargateBridgeData.minReceivedAmt\n );\n } else {\n ERC20(stargateBridgeData.token).safeApprove(\n address(router),\n amount\n );\n {\n router.swap{value: stargateBridgeData.value}(\n stargateBridgeData.stargateDstChainId,\n stargateBridgeData.srcPoolId,\n stargateBridgeData.dstPoolId,\n payable(stargateBridgeData.senderAddress), // default to refund to main contract\n amount,\n stargateBridgeData.minReceivedAmt,\n IBridgeStargate.lzTxObj(\n stargateBridgeData.destinationGasLimit,\n 0, // zero amount since this is a ERC20 bridging\n \"0x\" //empty data since this is for only ERC20\n ),\n abi.encodePacked(stargateBridgeData.receiverAddress),\n stargateBridgeData.destinationPayload\n );\n }\n }\n\n emit SocketBridge(\n amount,\n stargateBridgeData.token,\n stargateBridgeData.stargateDstChainId,\n StargateIdentifier,\n msg.sender,\n stargateBridgeData.receiverAddress,\n stargateBridgeData.metadata\n );\n }\n\n /**\n * @notice function to bridge tokens after swapping.\n * @notice this is different from bridgeAfterSwap since this function holds the logic for swapping tokens too.\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @dev for usage, refer to controller implementations\n * encodedData for bridge should follow the sequence of properties in Stargate-BridgeData struct\n * @param swapId routeId for the swapImpl\n * @param swapData encoded data for swap\n * @param stargateBridgeData encoded data for StargateBridgeData\n */\n function swapAndBridge(\n uint32 swapId,\n bytes calldata swapData,\n StargateBridgeDataNoToken calldata stargateBridgeData\n ) external payable {\n (bool success, bytes memory result) = socketRoute\n .getRoute(swapId)\n .delegatecall(swapData);\n\n if (!success) {\n assembly {\n revert(add(result, 32), mload(result))\n }\n }\n\n (uint256 bridgeAmount, address token) = abi.decode(\n result,\n (uint256, address)\n );\n\n if (token == NATIVE_TOKEN_ADDRESS) {\n routerETH.swapETH{\n value: bridgeAmount + stargateBridgeData.optionalValue\n }(\n stargateBridgeData.stargateDstChainId,\n payable(stargateBridgeData.senderAddress),\n abi.encodePacked(stargateBridgeData.receiverAddress),\n bridgeAmount,\n stargateBridgeData.minReceivedAmt\n );\n } else {\n ERC20(token).safeApprove(address(router), bridgeAmount);\n {\n router.swap{value: stargateBridgeData.value}(\n stargateBridgeData.stargateDstChainId,\n stargateBridgeData.srcPoolId,\n stargateBridgeData.dstPoolId,\n payable(stargateBridgeData.senderAddress), // default to refund to main contract\n bridgeAmount,\n stargateBridgeData.minReceivedAmt,\n IBridgeStargate.lzTxObj(\n stargateBridgeData.destinationGasLimit,\n 0,\n \"0x\"\n ),\n abi.encodePacked(stargateBridgeData.receiverAddress),\n stargateBridgeData.destinationPayload\n );\n }\n }\n\n emit SocketBridge(\n bridgeAmount,\n token,\n stargateBridgeData.stargateDstChainId,\n StargateIdentifier,\n msg.sender,\n stargateBridgeData.receiverAddress,\n stargateBridgeData.metadata\n );\n }\n\n /**\n * @notice function to handle ERC20 bridging to receipent via Stargate-L1-Bridge\n * @notice This method is payable because the caller is doing token transfer and briding operation\n * @param token address of token being bridged\n * @param senderAddress address of sender\n * @param receiverAddress address of recipient\n * @param amount amount of token being bridge\n * @param value value\n * @param optionalValue optionalValue\n * @param stargateBridgeExtraData stargate bridge extradata\n */\n function bridgeERC20To(\n address token,\n address senderAddress,\n address receiverAddress,\n uint256 amount,\n uint256 value,\n uint256 optionalValue,\n StargateBridgeExtraData calldata stargateBridgeExtraData\n ) external payable {\n // token address might not be indication thats why passed through extraData\n if (token == NATIVE_TOKEN_ADDRESS) {\n // perform bridging\n routerETH.swapETH{value: amount + optionalValue}(\n stargateBridgeExtraData.stargateDstChainId,\n payable(senderAddress),\n abi.encodePacked(receiverAddress),\n amount,\n stargateBridgeExtraData.minReceivedAmt\n );\n } else {\n ERC20 tokenInstance = ERC20(token);\n tokenInstance.safeTransferFrom(msg.sender, socketGateway, amount);\n tokenInstance.safeApprove(address(router), amount);\n {\n router.swap{value: value}(\n stargateBridgeExtraData.stargateDstChainId,\n stargateBridgeExtraData.srcPoolId,\n stargateBridgeExtraData.dstPoolId,\n payable(senderAddress), // default to refund to main contract\n amount,\n stargateBridgeExtraData.minReceivedAmt,\n IBridgeStargate.lzTxObj(\n stargateBridgeExtraData.destinationGasLimit,\n 0, // zero amount since this is a ERC20 bridging\n \"0x\" //empty data since this is for only ERC20\n ),\n abi.encodePacked(receiverAddress),\n stargateBridgeExtraData.destinationPayload\n );\n }\n }\n\n emit SocketBridge(\n amount,\n token,\n stargateBridgeExtraData.stargateDstChainId,\n StargateIdentifier,\n msg.sender,\n receiverAddress,\n stargateBridgeExtraData.metadata\n );\n }\n\n function bridgeNativeTo(\n address receiverAddress,\n address senderAddress,\n uint16 stargateDstChainId,\n uint256 amount,\n uint256 minReceivedAmt,\n uint256 optionalValue,\n bytes32 metadata\n ) external payable {\n // perform bridging\n routerETH.swapETH{value: amount + optionalValue}(\n stargateDstChainId,\n payable(senderAddress),\n abi.encodePacked(receiverAddress),\n amount,\n minReceivedAmt\n );\n\n emit SocketBridge(\n amount,\n NATIVE_TOKEN_ADDRESS,\n stargateDstChainId,\n StargateIdentifier,\n msg.sender,\n receiverAddress,\n metadata\n );\n }\n}\n"}},"settings":{"optimizer":{"enabled":true,"runs":1000000},"outputSelection":{"*":{"*":["evm.bytecode","evm.deployedBytecode","devdoc","userdoc","metadata","abi"]}},"metadata":{"useLiteralContent":true},"libraries":{}}},"ABI":"[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_disabledRoute\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"ArrayLengthMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectBridgeRatios\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyNominee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"controllerId\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"controllerAddress\",\"type\":\"address\"}],\"name\":\"ControllerAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"controllerId\",\"type\":\"uint32\"}],\"name\":\"ControllerDisabled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"routeId\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"route\",\"type\":\"address\"}],\"name\":\"NewRouteAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"claimer\",\"type\":\"address\"}],\"name\":\"OwnerClaimed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"nominee\",\"type\":\"address\"}],\"name\":\"OwnerNominated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"routeId\",\"type\":\"uint32\"}],\"name\":\"RouteDisabled\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"BRIDGE_AFTER_SWAP_SELECTOR\",\"outputs\":[{\"internalType\":\"bytes4\",\"name\":\"\",\"type\":\"bytes4\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"CENT_PERCENT\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"controllerAddress\",\"type\":\"address\"}],\"name\":\"addController\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"routeAddress\",\"type\":\"address\"}],\"name\":\"addRoute\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"routeId\",\"type\":\"uint32\"}],\"name\":\"addressAt\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"claimOwner\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"controllerCount\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"name\":\"controllers\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"controllerId\",\"type\":\"uint32\"}],\"name\":\"disableController\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"routeId\",\"type\":\"uint32\"}],\"name\":\"disableRoute\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"disabledRouteAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"controllerId\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"struct ISocketGateway.SocketControllerRequest\",\"name\":\"socketControllerRequest\",\"type\":\"tuple\"}],\"name\":\"executeController\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"controllerId\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"struct ISocketGateway.SocketControllerRequest[]\",\"name\":\"controllerRequests\",\"type\":\"tuple[]\"}],\"name\":\"executeControllers\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"routeId\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"routeData\",\"type\":\"bytes\"}],\"name\":\"executeRoute\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32[]\",\"name\":\"routeIds\",\"type\":\"uint32[]\"},{\"internalType\":\"bytes[]\",\"name\":\"dataItems\",\"type\":\"bytes[]\"}],\"name\":\"executeRoutes\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"controllerId\",\"type\":\"uint32\"}],\"name\":\"getController\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"routeId\",\"type\":\"uint32\"}],\"name\":\"getRoute\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"nominee_\",\"type\":\"address\"}],\"name\":\"nominateOwner\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"nominee\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address payable\",\"name\":\"userAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"rescueEther\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"userAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"rescueFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"name\":\"routes\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"routesCount\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"routeAddresses\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"tokenAddresses\",\"type\":\"address[]\"},{\"internalType\":\"bool\",\"name\":\"isMax\",\"type\":\"bool\"}],\"name\":\"setApprovalForRouters\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"swapRouteId\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"swapImplData\",\"type\":\"bytes\"},{\"internalType\":\"uint32[]\",\"name\":\"bridgeRouteIds\",\"type\":\"uint32[]\"},{\"internalType\":\"bytes[]\",\"name\":\"bridgeImplDataItems\",\"type\":\"bytes[]\"},{\"internalType\":\"uint256[]\",\"name\":\"bridgeRatios\",\"type\":\"uint256[]\"},{\"internalType\":\"bytes[]\",\"name\":\"eventDataItems\",\"type\":\"bytes[]\"}],\"internalType\":\"struct ISocketRequest.SwapMultiBridgeRequest\",\"name\":\"swapMultiBridgeRequest\",\"type\":\"tuple\"}],\"name\":\"swapAndMultiBridge\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]","ContractName":"SocketGateway","CompilerVersion":"v0.8.7+commit.e28d00a7","OptimizationUsed":1,"Runs":1000000,"ConstructorArguments":"0x000000000000000000000000e8dd38e673a93ccfc2e3d7053efccb5c93f493650000000000000000000000000f34a522ff82151c90679b73211955068fd854f1","EVMVersion":"Default","Library":"","LicenseType":"","Proxy":1,"Implementation":"0xa3c4e32af0da5efaddb20cc9fb26159f55c8c42f","SwarmSource":""}] \ No newline at end of file diff --git a/testdata/etherscan/0x71356E37e0368Bd10bFDbF41dC052fE5FA24cD05/creation_data.json b/testdata/etherscan/0x71356E37e0368Bd10bFDbF41dC052fE5FA24cD05/creation_data.json index b3976447f..08b838e2b 100644 --- a/testdata/etherscan/0x71356E37e0368Bd10bFDbF41dC052fE5FA24cD05/creation_data.json +++ b/testdata/etherscan/0x71356E37e0368Bd10bFDbF41dC052fE5FA24cD05/creation_data.json @@ -1,5 +1 @@ -{ - "contractAddress": "0x71356e37e0368bd10bfdbf41dc052fe5fa24cd05", - "contractCreator": "0xaa1d342354d755ec515f40e7d5e83cb4184bb9ee", - "txHash": "0x1c800c2c2d5230823602cae6896a8db1ab7d1341ca697c6c64d9f0edf11dabe2" -} \ No newline at end of file +{"contractAddress":"0x71356e37e0368bd10bfdbf41dc052fe5fa24cd05","contractCreator":"0xaa1d342354d755ec515f40e7d5e83cb4184bb9ee","txHash":"0x1c800c2c2d5230823602cae6896a8db1ab7d1341ca697c6c64d9f0edf11dabe2"} \ No newline at end of file diff --git a/testdata/etherscan/0x71356E37e0368Bd10bFDbF41dC052fE5FA24cD05/metadata.json b/testdata/etherscan/0x71356E37e0368Bd10bFDbF41dC052fE5FA24cD05/metadata.json index cc890a571..08318ba41 100644 --- a/testdata/etherscan/0x71356E37e0368Bd10bFDbF41dC052fE5FA24cD05/metadata.json +++ b/testdata/etherscan/0x71356E37e0368Bd10bFDbF41dC052fE5FA24cD05/metadata.json @@ -1,129 +1 @@ -[ - { - "SourceCode": { - "language": "Solidity", - "sources": { - "@openzeppelin/contracts/access/IAccessControl.sol": { - "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev External interface of AccessControl declared to support ERC165 detection.\n */\ninterface IAccessControl {\n /**\n * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n *\n * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n * {RoleAdminChanged} not being emitted signaling this.\n *\n * _Available since v3.1._\n */\n event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n /**\n * @dev Emitted when `account` is granted `role`.\n *\n * `sender` is the account that originated the contract call, an admin role\n * bearer except when using {AccessControl-_setupRole}.\n */\n event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Emitted when `account` is revoked `role`.\n *\n * `sender` is the account that originated the contract call:\n * - if using `revokeRole`, it is the admin role bearer\n * - if using `renounceRole`, it is the role bearer (i.e. `account`)\n */\n event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) external view returns (bool);\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been granted `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) external;\n}\n" - }, - "@openzeppelin/contracts/token/ERC721/IERC721.sol": { - "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../../utils/introspection/IERC165.sol\";\n\n/**\n * @dev Required interface of an ERC721 compliant contract.\n */\ninterface IERC721 is IERC165 {\n /**\n * @dev Emitted when `tokenId` token is transferred from `from` to `to`.\n */\n event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);\n\n /**\n * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.\n */\n event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);\n\n /**\n * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.\n */\n event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n\n /**\n * @dev Returns the number of tokens in ``owner``'s account.\n */\n function balanceOf(address owner) external view returns (uint256 balance);\n\n /**\n * @dev Returns the owner of the `tokenId` token.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function ownerOf(uint256 tokenId) external view returns (address owner);\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\n * are aware of the ERC721 protocol to prevent tokens from being forever locked.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 tokenId\n ) external;\n\n /**\n * @dev Transfers `tokenId` token from `from` to `to`.\n *\n * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must be owned by `from`.\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 tokenId\n ) external;\n\n /**\n * @dev Gives permission to `to` to transfer `tokenId` token to another account.\n * The approval is cleared when the token is transferred.\n *\n * Only a single account can be approved at a time, so approving the zero address clears previous approvals.\n *\n * Requirements:\n *\n * - The caller must own the token or be an approved operator.\n * - `tokenId` must exist.\n *\n * Emits an {Approval} event.\n */\n function approve(address to, uint256 tokenId) external;\n\n /**\n * @dev Returns the account approved for `tokenId` token.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function getApproved(uint256 tokenId) external view returns (address operator);\n\n /**\n * @dev Approve or remove `operator` as an operator for the caller.\n * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.\n *\n * Requirements:\n *\n * - The `operator` cannot be the caller.\n *\n * Emits an {ApprovalForAll} event.\n */\n function setApprovalForAll(address operator, bool _approved) external;\n\n /**\n * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.\n *\n * See {setApprovalForAll}\n */\n function isApprovedForAll(address owner, address operator) external view returns (bool);\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 tokenId,\n bytes calldata data\n ) external;\n}\n" - }, - "@openzeppelin/contracts/access/IAccessControlEnumerable.sol": { - "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\n\n/**\n * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.\n */\ninterface IAccessControlEnumerable is IAccessControl {\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) external view returns (address);\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) external view returns (uint256);\n}\n" - }, - "@openzeppelin/contracts/utils/StorageSlot.sol": { - "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/StorageSlot.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for reading and writing primitive types to specific storage slots.\n *\n * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.\n * This library helps with reading and writing to such slots without the need for inline assembly.\n *\n * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.\n *\n * Example usage to set ERC1967 implementation slot:\n * ```\n * contract ERC1967 {\n * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n *\n * function _getImplementation() internal view returns (address) {\n * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\n * }\n *\n * function _setImplementation(address newImplementation) internal {\n * require(Address.isContract(newImplementation), \"ERC1967: new implementation is not a contract\");\n * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\n * }\n * }\n * ```\n *\n * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._\n */\nlibrary StorageSlot {\n struct AddressSlot {\n address value;\n }\n\n struct BooleanSlot {\n bool value;\n }\n\n struct Bytes32Slot {\n bytes32 value;\n }\n\n struct Uint256Slot {\n uint256 value;\n }\n\n /**\n * @dev Returns an `AddressSlot` with member `value` located at `slot`.\n */\n function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {\n assembly {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns an `BooleanSlot` with member `value` located at `slot`.\n */\n function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {\n assembly {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.\n */\n function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {\n assembly {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns an `Uint256Slot` with member `value` located at `slot`.\n */\n function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {\n assembly {\n r.slot := slot\n }\n }\n}\n" - }, - "@openzeppelin/contracts/utils/cryptography/ECDSA.sol": { - "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/ECDSA.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../Strings.sol\";\n\n/**\n * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.\n *\n * These functions can be used to verify that a message was signed by the holder\n * of the private keys of a given address.\n */\nlibrary ECDSA {\n enum RecoverError {\n NoError,\n InvalidSignature,\n InvalidSignatureLength,\n InvalidSignatureS,\n InvalidSignatureV\n }\n\n function _throwError(RecoverError error) private pure {\n if (error == RecoverError.NoError) {\n return; // no error: do nothing\n } else if (error == RecoverError.InvalidSignature) {\n revert(\"ECDSA: invalid signature\");\n } else if (error == RecoverError.InvalidSignatureLength) {\n revert(\"ECDSA: invalid signature length\");\n } else if (error == RecoverError.InvalidSignatureS) {\n revert(\"ECDSA: invalid signature 's' value\");\n } else if (error == RecoverError.InvalidSignatureV) {\n revert(\"ECDSA: invalid signature 'v' value\");\n }\n }\n\n /**\n * @dev Returns the address that signed a hashed message (`hash`) with\n * `signature` or error string. This address can then be used for verification purposes.\n *\n * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:\n * this function rejects them by requiring the `s` value to be in the lower\n * half order, and the `v` value to be either 27 or 28.\n *\n * IMPORTANT: `hash` _must_ be the result of a hash operation for the\n * verification to be secure: it is possible to craft signatures that\n * recover to arbitrary addresses for non-hashed data. A safe way to ensure\n * this is by receiving a hash of the original message (which may otherwise\n * be too long), and then calling {toEthSignedMessageHash} on it.\n *\n * Documentation for signature generation:\n * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]\n * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]\n *\n * _Available since v4.3._\n */\n function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {\n // Check the signature length\n // - case 65: r,s,v signature (standard)\n // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._\n if (signature.length == 65) {\n bytes32 r;\n bytes32 s;\n uint8 v;\n // ecrecover takes the signature parameters, and the only way to get them\n // currently is to use assembly.\n assembly {\n r := mload(add(signature, 0x20))\n s := mload(add(signature, 0x40))\n v := byte(0, mload(add(signature, 0x60)))\n }\n return tryRecover(hash, v, r, s);\n } else if (signature.length == 64) {\n bytes32 r;\n bytes32 vs;\n // ecrecover takes the signature parameters, and the only way to get them\n // currently is to use assembly.\n assembly {\n r := mload(add(signature, 0x20))\n vs := mload(add(signature, 0x40))\n }\n return tryRecover(hash, r, vs);\n } else {\n return (address(0), RecoverError.InvalidSignatureLength);\n }\n }\n\n /**\n * @dev Returns the address that signed a hashed message (`hash`) with\n * `signature`. This address can then be used for verification purposes.\n *\n * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:\n * this function rejects them by requiring the `s` value to be in the lower\n * half order, and the `v` value to be either 27 or 28.\n *\n * IMPORTANT: `hash` _must_ be the result of a hash operation for the\n * verification to be secure: it is possible to craft signatures that\n * recover to arbitrary addresses for non-hashed data. A safe way to ensure\n * this is by receiving a hash of the original message (which may otherwise\n * be too long), and then calling {toEthSignedMessageHash} on it.\n */\n function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, signature);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.\n *\n * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]\n *\n * _Available since v4.3._\n */\n function tryRecover(\n bytes32 hash,\n bytes32 r,\n bytes32 vs\n ) internal pure returns (address, RecoverError) {\n bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);\n uint8 v = uint8((uint256(vs) >> 255) + 27);\n return tryRecover(hash, v, r, s);\n }\n\n /**\n * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.\n *\n * _Available since v4.2._\n */\n function recover(\n bytes32 hash,\n bytes32 r,\n bytes32 vs\n ) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, r, vs);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Overload of {ECDSA-tryRecover} that receives the `v`,\n * `r` and `s` signature fields separately.\n *\n * _Available since v4.3._\n */\n function tryRecover(\n bytes32 hash,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal pure returns (address, RecoverError) {\n // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature\n // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines\n // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most\n // signatures from current libraries generate a unique signature with an s-value in the lower half order.\n //\n // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value\n // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or\n // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept\n // these malleable signatures as well.\n if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {\n return (address(0), RecoverError.InvalidSignatureS);\n }\n if (v != 27 && v != 28) {\n return (address(0), RecoverError.InvalidSignatureV);\n }\n\n // If the signature is valid (and not malleable), return the signer address\n address signer = ecrecover(hash, v, r, s);\n if (signer == address(0)) {\n return (address(0), RecoverError.InvalidSignature);\n }\n\n return (signer, RecoverError.NoError);\n }\n\n /**\n * @dev Overload of {ECDSA-recover} that receives the `v`,\n * `r` and `s` signature fields separately.\n */\n function recover(\n bytes32 hash,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, v, r, s);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Returns an Ethereum Signed Message, created from a `hash`. This\n * produces hash corresponding to the one signed with the\n * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]\n * JSON-RPC method as part of EIP-191.\n *\n * See {recover}.\n */\n function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {\n // 32 is the length in bytes of hash,\n // enforced by the type signature above\n return keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n32\", hash));\n }\n\n /**\n * @dev Returns an Ethereum Signed Message, created from `s`. This\n * produces hash corresponding to the one signed with the\n * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]\n * JSON-RPC method as part of EIP-191.\n *\n * See {recover}.\n */\n function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {\n return keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n\", Strings.toString(s.length), s));\n }\n\n /**\n * @dev Returns an Ethereum Signed Typed Data, created from a\n * `domainSeparator` and a `structHash`. This produces hash corresponding\n * to the one signed with the\n * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]\n * JSON-RPC method as part of EIP-712.\n *\n * See {recover}.\n */\n function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {\n return keccak256(abi.encodePacked(\"\\x19\\x01\", domainSeparator, structHash));\n }\n}\n" - }, - "contracts/v0.8/extensions/GatewayV2.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/security/Pausable.sol\";\nimport \"../interfaces/IQuorum.sol\";\nimport \"../interfaces/IWeightedValidator.sol\";\nimport \"./HasProxyAdmin.sol\";\n\nabstract contract GatewayV2 is HasProxyAdmin, Pausable, IQuorum {\n /// @dev Emitted when the validator contract address is updated.\n event ValidatorContractUpdated(IWeightedValidator);\n\n uint256 internal _num;\n uint256 internal _denom;\n\n IWeightedValidator public validatorContract;\n uint256 public nonce;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n */\n uint256[50] private ______gap;\n\n /**\n * @dev See {IQuorum-getThreshold}.\n */\n function getThreshold() external view virtual returns (uint256, uint256) {\n return (_num, _denom);\n }\n\n /**\n * @dev See {IQuorum-checkThreshold}.\n */\n function checkThreshold(uint256 _voteWeight) external view virtual returns (bool) {\n return _voteWeight * _denom >= _num * validatorContract.totalWeights();\n }\n\n /**\n * @dev See {IQuorum-setThreshold}.\n */\n function setThreshold(uint256 _numerator, uint256 _denominator)\n external\n virtual\n onlyAdmin\n returns (uint256, uint256)\n {\n return _setThreshold(_numerator, _denominator);\n }\n\n /**\n * @dev Triggers paused state.\n */\n function pause() external onlyAdmin {\n _pause();\n }\n\n /**\n * @dev Triggers unpaused state.\n */\n function unpause() external onlyAdmin {\n _unpause();\n }\n\n /**\n * @dev Sets validator contract address.\n *\n * Requirements:\n * - The method caller is admin.\n *\n * Emits the `ValidatorContractUpdated` event.\n *\n */\n function setValidatorContract(IWeightedValidator _validatorContract) external virtual onlyAdmin {\n _setValidatorContract(_validatorContract);\n }\n\n /**\n * @dev See {IQuorum-minimumVoteWeight}.\n */\n function minimumVoteWeight() public view virtual returns (uint256) {\n return _minimumVoteWeight(validatorContract.totalWeights());\n }\n\n /**\n * @dev Sets validator contract address.\n *\n * Emits the `ValidatorContractUpdated` event.\n *\n */\n function _setValidatorContract(IWeightedValidator _validatorContract) internal virtual {\n validatorContract = _validatorContract;\n emit ValidatorContractUpdated(_validatorContract);\n }\n\n /**\n * @dev Sets threshold and returns the old one.\n *\n * Emits the `ThresholdUpdated` event.\n *\n */\n function _setThreshold(uint256 _numerator, uint256 _denominator)\n internal\n virtual\n returns (uint256 _previousNum, uint256 _previousDenom)\n {\n require(_numerator <= _denominator, \"GatewayV2: invalid threshold\");\n _previousNum = _num;\n _previousDenom = _denom;\n _num = _numerator;\n _denom = _denominator;\n emit ThresholdUpdated(nonce++, _numerator, _denominator, _previousNum, _previousDenom);\n }\n\n /**\n * @dev Returns minimum vote weight.\n */\n function _minimumVoteWeight(uint256 _totalWeight) internal view virtual returns (uint256) {\n return (_num * _totalWeight + _denom - 1) / _denom;\n }\n}\n" - }, - "@openzeppelin/contracts/proxy/utils/Initializable.sol": { - "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (proxy/utils/Initializable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../../utils/Address.sol\";\n\n/**\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\n * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\n *\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\n *\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\n *\n * [CAUTION]\n * ====\n * Avoid leaving a contract uninitialized.\n *\n * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation\n * contract, which may impact the proxy. To initialize the implementation contract, you can either invoke the\n * initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed:\n *\n * [.hljs-theme-light.nopadding]\n * ```\n * /// @custom:oz-upgrades-unsafe-allow constructor\n * constructor() initializer {}\n * ```\n * ====\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private _initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private _initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n // If the contract is initializing we ignore whether _initialized is set in order to support multiple\n // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the\n // contract may have been reentered.\n require(_initializing ? _isConstructor() : !_initialized, \"Initializable: contract is already initialized\");\n\n bool isTopLevelCall = !_initializing;\n if (isTopLevelCall) {\n _initializing = true;\n _initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n _initializing = false;\n }\n }\n\n /**\n * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the\n * {initializer} modifier, directly or indirectly.\n */\n modifier onlyInitializing() {\n require(_initializing, \"Initializable: contract is not initializing\");\n _;\n }\n\n function _isConstructor() private view returns (bool) {\n return !Address.isContract(address(this));\n }\n}\n" - }, - "contracts/v0.8/extensions/WithdrawalLimitation.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"./GatewayV2.sol\";\n\nabstract contract WithdrawalLimitation is GatewayV2 {\n /// @dev Emitted when the high-tier vote weight threshold is updated\n event HighTierVoteWeightThresholdUpdated(\n uint256 indexed nonce,\n uint256 indexed numerator,\n uint256 indexed denominator,\n uint256 previousNumerator,\n uint256 previousDenominator\n );\n /// @dev Emitted when the thresholds for high-tier withdrawals that requires high-tier vote weights are updated\n event HighTierThresholdsUpdated(address[] tokens, uint256[] thresholds);\n /// @dev Emitted when the thresholds for locked withdrawals are updated\n event LockedThresholdsUpdated(address[] tokens, uint256[] thresholds);\n /// @dev Emitted when the fee percentages to unlock withdraw are updated\n event UnlockFeePercentagesUpdated(address[] tokens, uint256[] percentages);\n /// @dev Emitted when the daily limit thresholds are updated\n event DailyWithdrawalLimitsUpdated(address[] tokens, uint256[] limits);\n\n uint256 public constant _MAX_PERCENTAGE = 1_000_000;\n\n uint256 internal _highTierVWNum;\n uint256 internal _highTierVWDenom;\n\n /// @dev Mapping from mainchain token => the amount thresholds for high-tier withdrawals that requires high-tier vote weights\n mapping(address => uint256) public highTierThreshold;\n /// @dev Mapping from mainchain token => the amount thresholds to lock withdrawal\n mapping(address => uint256) public lockedThreshold;\n /// @dev Mapping from mainchain token => unlock fee percentages for unlocker\n /// @notice Values 0-1,000,000 map to 0%-100%\n mapping(address => uint256) public unlockFeePercentages;\n /// @dev Mapping from mainchain token => daily limit amount for withdrawal\n mapping(address => uint256) public dailyWithdrawalLimit;\n /// @dev Mapping from token address => today withdrawal amount\n mapping(address => uint256) public lastSyncedWithdrawal;\n /// @dev Mapping from token address => last date synced to record the `lastSyncedWithdrawal`\n mapping(address => uint256) public lastDateSynced;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n */\n uint256[50] private ______gap;\n\n /**\n * @dev Override {GatewayV2-setThreshold}.\n *\n * Requirements:\n * - The high-tier vote weight threshold must equal to or larger than the normal threshold.\n *\n */\n function setThreshold(uint256 _numerator, uint256 _denominator)\n external\n virtual\n override\n onlyAdmin\n returns (uint256 _previousNum, uint256 _previousDenom)\n {\n (_previousNum, _previousDenom) = _setThreshold(_numerator, _denominator);\n _verifyThresholds();\n }\n\n /**\n * @dev Returns the high-tier vote weight threshold.\n */\n function getHighTierVoteWeightThreshold() external view virtual returns (uint256, uint256) {\n return (_highTierVWNum, _highTierVWDenom);\n }\n\n /**\n * @dev Checks whether the `_voteWeight` passes the high-tier vote weight threshold.\n */\n function checkHighTierVoteWeightThreshold(uint256 _voteWeight) external view virtual returns (bool) {\n return _voteWeight * _highTierVWDenom >= _highTierVWNum * validatorContract.totalWeights();\n }\n\n /**\n * @dev Sets high-tier vote weight threshold and returns the old one.\n *\n * Requirements:\n * - The method caller is admin.\n * - The high-tier vote weight threshold must equal to or larger than the normal threshold.\n *\n * Emits the `HighTierVoteWeightThresholdUpdated` event.\n *\n */\n function setHighTierVoteWeightThreshold(uint256 _numerator, uint256 _denominator)\n external\n virtual\n onlyAdmin\n returns (uint256 _previousNum, uint256 _previousDenom)\n {\n (_previousNum, _previousDenom) = _setHighTierVoteWeightThreshold(_numerator, _denominator);\n _verifyThresholds();\n }\n\n /**\n * @dev Sets the thresholds for high-tier withdrawals that requires high-tier vote weights.\n *\n * Requirements:\n * - The method caller is admin.\n * - The arrays have the same length and its length larger than 0.\n *\n * Emits the `HighTierThresholdsUpdated` event.\n *\n */\n function setHighTierThresholds(address[] calldata _tokens, uint256[] calldata _thresholds)\n external\n virtual\n onlyAdmin\n {\n require(_tokens.length > 0, \"WithdrawalLimitation: invalid array length\");\n _setHighTierThresholds(_tokens, _thresholds);\n }\n\n /**\n * @dev Sets the amount thresholds to lock withdrawal.\n *\n * Requirements:\n * - The method caller is admin.\n * - The arrays have the same length and its length larger than 0.\n *\n * Emits the `LockedThresholdsUpdated` event.\n *\n */\n function setLockedThresholds(address[] calldata _tokens, uint256[] calldata _thresholds) external virtual onlyAdmin {\n require(_tokens.length > 0, \"WithdrawalLimitation: invalid array length\");\n _setLockedThresholds(_tokens, _thresholds);\n }\n\n /**\n * @dev Sets fee percentages to unlock withdrawal.\n *\n * Requirements:\n * - The method caller is admin.\n * - The arrays have the same length and its length larger than 0.\n *\n * Emits the `UnlockFeePercentagesUpdated` event.\n *\n */\n function setUnlockFeePercentages(address[] calldata _tokens, uint256[] calldata _percentages)\n external\n virtual\n onlyAdmin\n {\n require(_tokens.length > 0, \"WithdrawalLimitation: invalid array length\");\n _setUnlockFeePercentages(_tokens, _percentages);\n }\n\n /**\n * @dev Sets daily limit amounts for the withdrawals.\n *\n * Requirements:\n * - The method caller is admin.\n * - The arrays have the same length and its length larger than 0.\n *\n * Emits the `DailyWithdrawalLimitsUpdated` event.\n *\n */\n function setDailyWithdrawalLimits(address[] calldata _tokens, uint256[] calldata _limits) external virtual onlyAdmin {\n require(_tokens.length > 0, \"WithdrawalLimitation: invalid array length\");\n _setDailyWithdrawalLimits(_tokens, _limits);\n }\n\n /**\n * @dev Checks whether the withdrawal reaches the limitation.\n */\n function reachedWithdrawalLimit(address _token, uint256 _quantity) external view virtual returns (bool) {\n return _reachedWithdrawalLimit(_token, _quantity);\n }\n\n /**\n * @dev Sets high-tier vote weight threshold and returns the old one.\n *\n * Emits the `HighTierVoteWeightThresholdUpdated` event.\n *\n */\n function _setHighTierVoteWeightThreshold(uint256 _numerator, uint256 _denominator)\n internal\n returns (uint256 _previousNum, uint256 _previousDenom)\n {\n require(_numerator <= _denominator, \"WithdrawalLimitation: invalid threshold\");\n _previousNum = _highTierVWNum;\n _previousDenom = _highTierVWDenom;\n _highTierVWNum = _numerator;\n _highTierVWDenom = _denominator;\n emit HighTierVoteWeightThresholdUpdated(nonce++, _numerator, _denominator, _previousNum, _previousDenom);\n }\n\n /**\n * @dev Sets the thresholds for high-tier withdrawals that requires high-tier vote weights.\n *\n * Requirements:\n * - The array lengths are equal.\n *\n * Emits the `HighTierThresholdsUpdated` event.\n *\n */\n function _setHighTierThresholds(address[] calldata _tokens, uint256[] calldata _thresholds) internal virtual {\n require(_tokens.length == _thresholds.length, \"WithdrawalLimitation: invalid array length\");\n for (uint256 _i; _i < _tokens.length; _i++) {\n highTierThreshold[_tokens[_i]] = _thresholds[_i];\n }\n emit HighTierThresholdsUpdated(_tokens, _thresholds);\n }\n\n /**\n * @dev Sets the amount thresholds to lock withdrawal.\n *\n * Requirements:\n * - The array lengths are equal.\n *\n * Emits the `LockedThresholdsUpdated` event.\n *\n */\n function _setLockedThresholds(address[] calldata _tokens, uint256[] calldata _thresholds) internal virtual {\n require(_tokens.length == _thresholds.length, \"WithdrawalLimitation: invalid array length\");\n for (uint256 _i; _i < _tokens.length; _i++) {\n lockedThreshold[_tokens[_i]] = _thresholds[_i];\n }\n emit LockedThresholdsUpdated(_tokens, _thresholds);\n }\n\n /**\n * @dev Sets fee percentages to unlock withdrawal.\n *\n * Requirements:\n * - The array lengths are equal.\n * - The percentage is equal to or less than 100_000.\n *\n * Emits the `UnlockFeePercentagesUpdated` event.\n *\n */\n function _setUnlockFeePercentages(address[] calldata _tokens, uint256[] calldata _percentages) internal virtual {\n require(_tokens.length == _percentages.length, \"WithdrawalLimitation: invalid array length\");\n for (uint256 _i; _i < _tokens.length; _i++) {\n require(_percentages[_i] <= _MAX_PERCENTAGE, \"WithdrawalLimitation: invalid percentage\");\n unlockFeePercentages[_tokens[_i]] = _percentages[_i];\n }\n emit UnlockFeePercentagesUpdated(_tokens, _percentages);\n }\n\n /**\n * @dev Sets daily limit amounts for the withdrawals.\n *\n * Requirements:\n * - The array lengths are equal.\n *\n * Emits the `DailyWithdrawalLimitsUpdated` event.\n *\n */\n function _setDailyWithdrawalLimits(address[] calldata _tokens, uint256[] calldata _limits) internal virtual {\n require(_tokens.length == _limits.length, \"WithdrawalLimitation: invalid array length\");\n for (uint256 _i; _i < _tokens.length; _i++) {\n dailyWithdrawalLimit[_tokens[_i]] = _limits[_i];\n }\n emit DailyWithdrawalLimitsUpdated(_tokens, _limits);\n }\n\n /**\n * @dev Checks whether the withdrawal reaches the daily limitation.\n *\n * Requirements:\n * - The daily withdrawal threshold should not apply for locked withdrawals.\n *\n */\n function _reachedWithdrawalLimit(address _token, uint256 _quantity) internal view virtual returns (bool) {\n if (_lockedWithdrawalRequest(_token, _quantity)) {\n return false;\n }\n\n uint256 _currentDate = block.timestamp / 1 days;\n if (_currentDate > lastDateSynced[_token]) {\n return dailyWithdrawalLimit[_token] <= _quantity;\n } else {\n return dailyWithdrawalLimit[_token] <= lastSyncedWithdrawal[_token] + _quantity;\n }\n }\n\n /**\n * @dev Record withdrawal token.\n */\n function _recordWithdrawal(address _token, uint256 _quantity) internal virtual {\n uint256 _currentDate = block.timestamp / 1 days;\n if (_currentDate > lastDateSynced[_token]) {\n lastDateSynced[_token] = _currentDate;\n lastSyncedWithdrawal[_token] = _quantity;\n } else {\n lastSyncedWithdrawal[_token] += _quantity;\n }\n }\n\n /**\n * @dev Returns whether the withdrawal request is locked or not.\n */\n function _lockedWithdrawalRequest(address _token, uint256 _quantity) internal view virtual returns (bool) {\n return lockedThreshold[_token] <= _quantity;\n }\n\n /**\n * @dev Computes fee percentage.\n */\n function _computeFeePercentage(uint256 _amount, uint256 _percentage) internal view virtual returns (uint256) {\n return (_amount * _percentage) / _MAX_PERCENTAGE;\n }\n\n /**\n * @dev Returns high-tier vote weight.\n */\n function _highTierVoteWeight(uint256 _totalWeight) internal view virtual returns (uint256) {\n return (_highTierVWNum * _totalWeight + _highTierVWDenom - 1) / _highTierVWDenom;\n }\n\n /**\n * @dev Validates whether the high-tier vote weight threshold is larger than the normal threshold.\n */\n function _verifyThresholds() internal view {\n require(_num * _highTierVWDenom <= _highTierVWNum * _denom, \"WithdrawalLimitation: invalid thresholds\");\n }\n}\n" - }, - "contracts/v0.8/interfaces/MappedTokenConsumer.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"../library/Token.sol\";\n\ninterface MappedTokenConsumer {\n struct MappedToken {\n Token.Standard erc;\n address tokenAddr;\n }\n}\n" - }, - "contracts/v0.8/library/Token.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC721/IERC721.sol\";\nimport \"@openzeppelin/contracts/utils/Strings.sol\";\nimport \"../interfaces/IWETH.sol\";\n\nlibrary Token {\n enum Standard {\n ERC20,\n ERC721\n }\n struct Info {\n Standard erc;\n // For ERC20: the id must be 0 and the quantity is larger than 0.\n // For ERC721: the quantity must be 0.\n uint256 id;\n uint256 quantity;\n }\n\n // keccak256(\"TokenInfo(uint8 erc,uint256 id,uint256 quantity)\");\n bytes32 public constant INFO_TYPE_HASH = 0x1e2b74b2a792d5c0f0b6e59b037fa9d43d84fbb759337f0112fcc15ca414fc8d;\n\n /**\n * @dev Returns token info struct hash.\n */\n function hash(Info memory _info) internal pure returns (bytes32) {\n return keccak256(abi.encode(INFO_TYPE_HASH, _info.erc, _info.id, _info.quantity));\n }\n\n /**\n * @dev Validates the token info.\n */\n function validate(Info memory _info) internal pure {\n require(\n (_info.erc == Standard.ERC20 && _info.quantity > 0 && _info.id == 0) ||\n (_info.erc == Standard.ERC721 && _info.quantity == 0),\n \"Token: invalid info\"\n );\n }\n\n /**\n * @dev Transfer asset from.\n *\n * Requirements:\n * - The `_from` address must approve for the contract using this library.\n *\n */\n function transferFrom(\n Info memory _info,\n address _from,\n address _to,\n address _token\n ) internal {\n bool _success;\n bytes memory _data;\n if (_info.erc == Standard.ERC20) {\n (_success, _data) = _token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, _from, _to, _info.quantity));\n _success = _success && (_data.length == 0 || abi.decode(_data, (bool)));\n } else if (_info.erc == Standard.ERC721) {\n // bytes4(keccak256(\"transferFrom(address,address,uint256)\"))\n (_success, ) = _token.call(abi.encodeWithSelector(0x23b872dd, _from, _to, _info.id));\n } else {\n revert(\"Token: unsupported token standard\");\n }\n\n if (!_success) {\n revert(\n string(\n abi.encodePacked(\n \"Token: could not transfer \",\n toString(_info),\n \" from \",\n Strings.toHexString(uint160(_from), 20),\n \" to \",\n Strings.toHexString(uint160(_to), 20),\n \" token \",\n Strings.toHexString(uint160(_token), 20)\n )\n )\n );\n }\n }\n\n /**\n * @dev Transfers ERC721 token and returns the result.\n */\n function tryTransferERC721(\n address _token,\n address _to,\n uint256 _id\n ) internal returns (bool _success) {\n (_success, ) = _token.call(abi.encodeWithSelector(IERC721.transferFrom.selector, address(this), _to, _id));\n }\n\n /**\n * @dev Transfers ERC20 token and returns the result.\n */\n function tryTransferERC20(\n address _token,\n address _to,\n uint256 _quantity\n ) internal returns (bool _success) {\n bytes memory _data;\n (_success, _data) = _token.call(abi.encodeWithSelector(IERC20.transfer.selector, _to, _quantity));\n _success = _success && (_data.length == 0 || abi.decode(_data, (bool)));\n }\n\n /**\n * @dev Transfer assets from current address to `_to` address.\n */\n function transfer(\n Info memory _info,\n address _to,\n address _token\n ) internal {\n bool _success;\n if (_info.erc == Standard.ERC20) {\n _success = tryTransferERC20(_token, _to, _info.quantity);\n } else if (_info.erc == Standard.ERC721) {\n _success = tryTransferERC721(_token, _to, _info.id);\n } else {\n revert(\"Token: unsupported token standard\");\n }\n\n if (!_success) {\n revert(\n string(\n abi.encodePacked(\n \"Token: could not transfer \",\n toString(_info),\n \" to \",\n Strings.toHexString(uint160(_to), 20),\n \" token \",\n Strings.toHexString(uint160(_token), 20)\n )\n )\n );\n }\n }\n\n /**\n * @dev Tries minting and transfering assets.\n *\n * @notice Prioritizes transfer native token if the token is wrapped.\n *\n */\n function handleAssetTransfer(\n Info memory _info,\n address payable _to,\n address _token,\n IWETH _wrappedNativeToken\n ) internal {\n bool _success;\n if (_token == address(_wrappedNativeToken)) {\n // Try sending the native token before transferring the wrapped token\n if (!_to.send(_info.quantity)) {\n _wrappedNativeToken.deposit{ value: _info.quantity }();\n transfer(_info, _to, _token);\n }\n } else if (_info.erc == Token.Standard.ERC20) {\n uint256 _balance = IERC20(_token).balanceOf(address(this));\n\n if (_balance < _info.quantity) {\n // bytes4(keccak256(\"mint(address,uint256)\"))\n (_success, ) = _token.call(abi.encodeWithSelector(0x40c10f19, address(this), _info.quantity - _balance));\n require(_success, \"Token: ERC20 minting failed\");\n }\n\n transfer(_info, _to, _token);\n } else if (_info.erc == Token.Standard.ERC721) {\n if (!tryTransferERC721(_token, _to, _info.id)) {\n // bytes4(keccak256(\"mint(address,uint256)\"))\n (_success, ) = _token.call(abi.encodeWithSelector(0x40c10f19, _to, _info.id));\n require(_success, \"Token: ERC721 minting failed\");\n }\n } else {\n revert(\"Token: unsupported token standard\");\n }\n }\n\n /**\n * @dev Returns readable string.\n */\n function toString(Info memory _info) internal pure returns (string memory) {\n return\n string(\n abi.encodePacked(\n \"TokenInfo(\",\n Strings.toHexString(uint160(_info.erc), 1),\n \",\",\n Strings.toHexString(_info.id),\n \",\",\n Strings.toHexString(_info.quantity),\n \")\"\n )\n );\n }\n\n struct Owner {\n address addr;\n address tokenAddr;\n uint256 chainId;\n }\n\n // keccak256(\"TokenOwner(address addr,address tokenAddr,uint256 chainId)\");\n bytes32 public constant OWNER_TYPE_HASH = 0x353bdd8d69b9e3185b3972e08b03845c0c14a21a390215302776a7a34b0e8764;\n\n /**\n * @dev Returns ownership struct hash.\n */\n function hash(Owner memory _owner) internal pure returns (bytes32) {\n return keccak256(abi.encode(OWNER_TYPE_HASH, _owner.addr, _owner.tokenAddr, _owner.chainId));\n }\n}\n" - }, - "contracts/v0.8/mainchain/IMainchainGatewayV2.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"../interfaces/IWETH.sol\";\nimport \"../library/Transfer.sol\";\nimport \"../interfaces/SignatureConsumer.sol\";\nimport \"../interfaces/MappedTokenConsumer.sol\";\n\ninterface IMainchainGatewayV2 is SignatureConsumer, MappedTokenConsumer {\n /// @dev Emitted when the deposit is requested\n event DepositRequested(bytes32 receiptHash, Transfer.Receipt receipt);\n /// @dev Emitted when the assets are withdrawn\n event Withdrew(bytes32 receiptHash, Transfer.Receipt receipt);\n /// @dev Emitted when the tokens are mapped\n event TokenMapped(address[] mainchainTokens, address[] roninTokens, Token.Standard[] standards);\n /// @dev Emitted when the wrapped native token contract is updated\n event WrappedNativeTokenContractUpdated(IWETH weth);\n /// @dev Emitted when the withdrawal is locked\n event WithdrawalLocked(bytes32 receiptHash, Transfer.Receipt receipt);\n /// @dev Emitted when the withdrawal is unlocked\n event WithdrawalUnlocked(bytes32 receiptHash, Transfer.Receipt receipt);\n\n /**\n * @dev Returns the domain seperator.\n */\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n /**\n * @dev Returns deposit count.\n */\n function depositCount() external view returns (uint256);\n\n /**\n * @dev Sets the wrapped native token contract.\n *\n * Requirements:\n * - The method caller is admin.\n *\n * Emits the `WrappedNativeTokenContractUpdated` event.\n *\n */\n function setWrappedNativeTokenContract(IWETH _wrappedToken) external;\n\n /**\n * @dev Returns whether the withdrawal is locked.\n */\n function withdrawalLocked(uint256 withdrawalId) external view returns (bool);\n\n /**\n * @dev Returns the withdrawal hash.\n */\n function withdrawalHash(uint256 withdrawalId) external view returns (bytes32);\n\n /**\n * @dev Locks the assets and request deposit.\n */\n function requestDepositFor(Transfer.Request calldata _request) external payable;\n\n /**\n * @dev Withdraws based on the receipt and the validator signatures.\n * Returns whether the withdrawal is locked.\n *\n * Emits the `Withdrew` once the assets are released.\n *\n */\n function submitWithdrawal(Transfer.Receipt memory _receipt, Signature[] memory _signatures)\n external\n returns (bool _locked);\n\n /**\n * @dev Approves a specific withdrawal.\n *\n * Requirements:\n * - The method caller is a validator.\n *\n * Emits the `Withdrew` once the assets are released.\n *\n */\n function unlockWithdrawal(Transfer.Receipt calldata _receipt) external;\n\n /**\n * @dev Maps mainchain tokens to Ronin network.\n *\n * Requirement:\n * - The method caller is admin.\n * - The arrays have the same length and its length larger than 0.\n *\n * Emits the `TokenMapped` event.\n *\n */\n function mapTokens(\n address[] calldata _mainchainTokens,\n address[] calldata _roninTokens,\n Token.Standard[] calldata _standards\n ) external;\n\n /**\n * @dev Maps mainchain tokens to Ronin network and sets thresholds.\n *\n * Requirement:\n * - The method caller is admin.\n * - The arrays have the same length and its length larger than 0.\n *\n * Emits the `TokenMapped` event.\n *\n */\n function mapTokensAndThresholds(\n address[] calldata _mainchainTokens,\n address[] calldata _roninTokens,\n Token.Standard[] calldata _standards,\n uint256[][4] calldata _thresholds\n ) external;\n\n /**\n * @dev Returns token address on Ronin network.\n * @notice Reverts for unsupported token.\n */\n function getRoninToken(address _mainchainToken) external view returns (MappedToken memory _token);\n}\n" - }, - "contracts/v0.8/mainchain/MainchainGatewayV2.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\nimport \"@openzeppelin/contracts/proxy/utils/Initializable.sol\";\nimport \"../extensions/GatewayV2.sol\";\nimport \"../extensions/WithdrawalLimitation.sol\";\nimport \"../library/Transfer.sol\";\nimport \"./IMainchainGatewayV2.sol\";\n\ncontract MainchainGatewayV2 is WithdrawalLimitation, Initializable, AccessControlEnumerable, IMainchainGatewayV2 {\n using Token for Token.Info;\n using Transfer for Transfer.Request;\n using Transfer for Transfer.Receipt;\n\n /// @dev Withdrawal unlocker role hash\n bytes32 public constant WITHDRAWAL_UNLOCKER_ROLE = keccak256(\"WITHDRAWAL_UNLOCKER_ROLE\");\n\n /// @dev Wrapped native token address\n IWETH public wrappedNativeToken;\n /// @dev Ronin network id\n uint256 public roninChainId;\n /// @dev Total deposit\n uint256 public depositCount;\n /// @dev Domain seperator\n bytes32 internal _domainSeparator;\n /// @dev Mapping from mainchain token => token address on Ronin network\n mapping(address => MappedToken) internal _roninToken;\n /// @dev Mapping from withdrawal id => withdrawal hash\n mapping(uint256 => bytes32) public withdrawalHash;\n /// @dev Mapping from withdrawal id => locked\n mapping(uint256 => bool) public withdrawalLocked;\n\n fallback() external payable {\n _fallback();\n }\n\n receive() external payable {\n _fallback();\n }\n\n /**\n * @dev Initializes contract storage.\n */\n function initialize(\n address _roleSetter,\n IWETH _wrappedToken,\n IWeightedValidator _validatorContract,\n uint256 _roninChainId,\n uint256 _numerator,\n uint256 _highTierVWNumerator,\n uint256 _denominator,\n // _addresses[0]: mainchainTokens\n // _addresses[1]: roninTokens\n // _addresses[2]: withdrawalUnlockers\n address[][3] calldata _addresses,\n // _thresholds[0]: highTierThreshold\n // _thresholds[1]: lockedThreshold\n // _thresholds[2]: unlockFeePercentages\n // _thresholds[3]: dailyWithdrawalLimit\n uint256[][4] calldata _thresholds,\n Token.Standard[] calldata _standards\n ) external payable virtual initializer {\n _setupRole(DEFAULT_ADMIN_ROLE, _roleSetter);\n roninChainId = _roninChainId;\n\n _setWrappedNativeTokenContract(_wrappedToken);\n _setValidatorContract(_validatorContract);\n _updateDomainSeparator();\n _setThreshold(_numerator, _denominator);\n _setHighTierVoteWeightThreshold(_highTierVWNumerator, _denominator);\n _verifyThresholds();\n\n if (_addresses[0].length > 0) {\n // Map mainchain tokens to ronin tokens\n _mapTokens(_addresses[0], _addresses[1], _standards);\n // Sets thresholds based on the mainchain tokens\n _setHighTierThresholds(_addresses[0], _thresholds[0]);\n _setLockedThresholds(_addresses[0], _thresholds[1]);\n _setUnlockFeePercentages(_addresses[0], _thresholds[2]);\n _setDailyWithdrawalLimits(_addresses[0], _thresholds[3]);\n }\n\n // Grant role for withdrawal unlocker\n for (uint256 _i; _i < _addresses[2].length; _i++) {\n _grantRole(WITHDRAWAL_UNLOCKER_ROLE, _addresses[2][_i]);\n }\n }\n\n /**\n * @dev Receives ether without doing anything. Use this function to topup native token.\n */\n function receiveEther() external payable {}\n\n /**\n * @dev See {IMainchainGatewayV2-DOMAIN_SEPARATOR}.\n */\n function DOMAIN_SEPARATOR() external view virtual returns (bytes32) {\n return _domainSeparator;\n }\n\n /**\n * @dev See {IMainchainGatewayV2-setWrappedNativeTokenContract}.\n */\n function setWrappedNativeTokenContract(IWETH _wrappedToken) external virtual onlyAdmin {\n _setWrappedNativeTokenContract(_wrappedToken);\n }\n\n /**\n * @dev See {IMainchainGatewayV2-requestDepositFor}.\n */\n function requestDepositFor(Transfer.Request calldata _request) external payable virtual whenNotPaused {\n _requestDepositFor(_request, msg.sender);\n }\n\n /**\n * @dev See {IMainchainGatewayV2-submitWithdrawal}.\n */\n function submitWithdrawal(Transfer.Receipt calldata _receipt, Signature[] calldata _signatures)\n external\n virtual\n whenNotPaused\n returns (bool _locked)\n {\n return _submitWithdrawal(_receipt, _signatures);\n }\n\n /**\n * @dev See {IMainchainGatewayV2-unlockWithdrawal}.\n */\n function unlockWithdrawal(Transfer.Receipt calldata _receipt) external onlyRole(WITHDRAWAL_UNLOCKER_ROLE) {\n bytes32 _receiptHash = _receipt.hash();\n require(withdrawalHash[_receipt.id] == _receipt.hash(), \"MainchainGatewayV2: invalid receipt\");\n require(withdrawalLocked[_receipt.id], \"MainchainGatewayV2: query for approved withdrawal\");\n delete withdrawalLocked[_receipt.id];\n emit WithdrawalUnlocked(_receiptHash, _receipt);\n\n address _token = _receipt.mainchain.tokenAddr;\n if (_receipt.info.erc == Token.Standard.ERC20) {\n Token.Info memory _feeInfo = _receipt.info;\n _feeInfo.quantity = _computeFeePercentage(_receipt.info.quantity, unlockFeePercentages[_token]);\n Token.Info memory _withdrawInfo = _receipt.info;\n _withdrawInfo.quantity = _receipt.info.quantity - _feeInfo.quantity;\n\n _feeInfo.handleAssetTransfer(payable(msg.sender), _token, wrappedNativeToken);\n _withdrawInfo.handleAssetTransfer(payable(_receipt.mainchain.addr), _token, wrappedNativeToken);\n } else {\n _receipt.info.handleAssetTransfer(payable(_receipt.mainchain.addr), _token, wrappedNativeToken);\n }\n\n emit Withdrew(_receiptHash, _receipt);\n }\n\n /**\n * @dev See {IMainchainGatewayV2-mapTokens}.\n */\n function mapTokens(\n address[] calldata _mainchainTokens,\n address[] calldata _roninTokens,\n Token.Standard[] calldata _standards\n ) external virtual onlyAdmin {\n require(_mainchainTokens.length > 0, \"MainchainGatewayV2: query for empty array\");\n _mapTokens(_mainchainTokens, _roninTokens, _standards);\n }\n\n /**\n * @dev See {IMainchainGatewayV2-mapTokensAndThresholds}.\n */\n function mapTokensAndThresholds(\n address[] calldata _mainchainTokens,\n address[] calldata _roninTokens,\n Token.Standard[] calldata _standards,\n // _thresholds[0]: highTierThreshold\n // _thresholds[1]: lockedThreshold\n // _thresholds[2]: unlockFeePercentages\n // _thresholds[3]: dailyWithdrawalLimit\n uint256[][4] calldata _thresholds\n ) external virtual onlyAdmin {\n require(_mainchainTokens.length > 0, \"MainchainGatewayV2: query for empty array\");\n _mapTokens(_mainchainTokens, _roninTokens, _standards);\n _setHighTierThresholds(_mainchainTokens, _thresholds[0]);\n _setLockedThresholds(_mainchainTokens, _thresholds[1]);\n _setUnlockFeePercentages(_mainchainTokens, _thresholds[2]);\n _setDailyWithdrawalLimits(_mainchainTokens, _thresholds[3]);\n }\n\n /**\n * @dev See {IMainchainGatewayV2-getRoninToken}.\n */\n function getRoninToken(address _mainchainToken) public view returns (MappedToken memory _token) {\n _token = _roninToken[_mainchainToken];\n require(_token.tokenAddr != address(0), \"MainchainGatewayV2: unsupported token\");\n }\n\n /**\n * @dev Maps mainchain tokens to Ronin network.\n *\n * Requirement:\n * - The arrays have the same length.\n *\n * Emits the `TokenMapped` event.\n *\n */\n function _mapTokens(\n address[] calldata _mainchainTokens,\n address[] calldata _roninTokens,\n Token.Standard[] calldata _standards\n ) internal virtual {\n require(\n _mainchainTokens.length == _roninTokens.length && _mainchainTokens.length == _standards.length,\n \"MainchainGatewayV2: invalid array length\"\n );\n\n for (uint256 _i; _i < _mainchainTokens.length; _i++) {\n _roninToken[_mainchainTokens[_i]].tokenAddr = _roninTokens[_i];\n _roninToken[_mainchainTokens[_i]].erc = _standards[_i];\n }\n\n emit TokenMapped(_mainchainTokens, _roninTokens, _standards);\n }\n\n /**\n * @dev Submits withdrawal receipt.\n *\n * Requirements:\n * - The receipt kind is withdrawal.\n * - The receipt is to withdraw on this chain.\n * - The receipt is not used to withdraw before.\n * - The withdrawal is not reached the limit threshold.\n * - The signer weight total is larger than or equal to the minimum threshold.\n * - The signature signers are in order.\n *\n * Emits the `Withdrew` once the assets are released.\n *\n */\n function _submitWithdrawal(Transfer.Receipt calldata _receipt, Signature[] memory _signatures)\n internal\n virtual\n returns (bool _locked)\n {\n uint256 _id = _receipt.id;\n uint256 _quantity = _receipt.info.quantity;\n address _tokenAddr = _receipt.mainchain.tokenAddr;\n\n _receipt.info.validate();\n require(_receipt.kind == Transfer.Kind.Withdrawal, \"MainchainGatewayV2: invalid receipt kind\");\n require(_receipt.mainchain.chainId == block.chainid, \"MainchainGatewayV2: invalid chain id\");\n MappedToken memory _token = getRoninToken(_receipt.mainchain.tokenAddr);\n require(\n _token.erc == _receipt.info.erc && _token.tokenAddr == _receipt.ronin.tokenAddr,\n \"MainchainGatewayV2: invalid receipt\"\n );\n require(withdrawalHash[_id] == bytes32(0), \"MainchainGatewayV2: query for processed withdrawal\");\n require(\n _receipt.info.erc == Token.Standard.ERC721 || !_reachedWithdrawalLimit(_tokenAddr, _quantity),\n \"MainchainGatewayV2: reached daily withdrawal limit\"\n );\n\n bytes32 _receiptHash = _receipt.hash();\n bytes32 _receiptDigest = Transfer.receiptDigest(_domainSeparator, _receiptHash);\n IWeightedValidator _validatorContract = validatorContract;\n\n uint256 _minimumVoteWeight;\n (_minimumVoteWeight, _locked) = _computeMinVoteWeight(_receipt.info.erc, _tokenAddr, _quantity, _validatorContract);\n\n {\n bool _passed;\n address _signer;\n address _lastSigner;\n Signature memory _sig;\n uint256 _weight;\n for (uint256 _i; _i < _signatures.length; _i++) {\n _sig = _signatures[_i];\n _signer = ecrecover(_receiptDigest, _sig.v, _sig.r, _sig.s);\n require(_lastSigner < _signer, \"MainchainGatewayV2: invalid order\");\n _lastSigner = _signer;\n\n _weight += _validatorContract.getValidatorWeight(_signer);\n if (_weight >= _minimumVoteWeight) {\n _passed = true;\n break;\n }\n }\n require(_passed, \"MainchainGatewayV2: query for insufficient vote weight\");\n withdrawalHash[_id] = _receiptHash;\n }\n\n if (_locked) {\n withdrawalLocked[_id] = true;\n emit WithdrawalLocked(_receiptHash, _receipt);\n return _locked;\n }\n\n _recordWithdrawal(_tokenAddr, _quantity);\n _receipt.info.handleAssetTransfer(payable(_receipt.mainchain.addr), _tokenAddr, wrappedNativeToken);\n emit Withdrew(_receiptHash, _receipt);\n }\n\n /**\n * @dev Requests deposit made by `_requester` address.\n *\n * Requirements:\n * - The token info is valid.\n * - The `msg.value` is 0 while depositing ERC20 token.\n * - The `msg.value` is equal to deposit quantity while depositing native token.\n *\n * Emits the `DepositRequested` event.\n *\n */\n function _requestDepositFor(Transfer.Request memory _request, address _requester) internal virtual {\n MappedToken memory _token;\n address _weth = address(wrappedNativeToken);\n\n _request.info.validate();\n if (_request.tokenAddr == address(0)) {\n require(_request.info.quantity == msg.value, \"MainchainGatewayV2: invalid request\");\n _token = getRoninToken(_weth);\n require(_token.erc == _request.info.erc, \"MainchainGatewayV2: invalid token standard\");\n _request.tokenAddr = _weth;\n } else {\n require(msg.value == 0, \"MainchainGatewayV2: invalid request\");\n _token = getRoninToken(_request.tokenAddr);\n require(_token.erc == _request.info.erc, \"MainchainGatewayV2: invalid token standard\");\n _request.info.transferFrom(_requester, address(this), _request.tokenAddr);\n // Withdraw if token is WETH\n if (_weth == _request.tokenAddr) {\n IWETH(_weth).withdraw(_request.info.quantity);\n }\n }\n\n uint256 _depositId = depositCount++;\n Transfer.Receipt memory _receipt = _request.into_deposit_receipt(\n _requester,\n _depositId,\n _token.tokenAddr,\n roninChainId\n );\n\n emit DepositRequested(_receipt.hash(), _receipt);\n }\n\n /**\n * @dev Returns the minimum vote weight for the token.\n */\n function _computeMinVoteWeight(\n Token.Standard _erc,\n address _token,\n uint256 _quantity,\n IWeightedValidator _validatorContract\n ) internal virtual returns (uint256 _weight, bool _locked) {\n uint256 _totalWeights = _validatorContract.totalWeights();\n _weight = _minimumVoteWeight(_totalWeights);\n if (_erc == Token.Standard.ERC20) {\n if (highTierThreshold[_token] <= _quantity) {\n _weight = _highTierVoteWeight(_totalWeights);\n }\n _locked = _lockedWithdrawalRequest(_token, _quantity);\n }\n }\n\n /**\n * @dev Update domain seperator.\n */\n function _updateDomainSeparator() internal {\n _domainSeparator = keccak256(\n abi.encode(\n keccak256(\"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\"),\n keccak256(\"MainchainGatewayV2\"),\n keccak256(\"2\"),\n block.chainid,\n address(this)\n )\n );\n }\n\n /**\n * @dev Sets the WETH contract.\n *\n * Emits the `WrappedNativeTokenContractUpdated` event.\n *\n */\n function _setWrappedNativeTokenContract(IWETH _wrapedToken) internal {\n wrappedNativeToken = _wrapedToken;\n emit WrappedNativeTokenContractUpdated(_wrapedToken);\n }\n\n /**\n * @dev Receives ETH from WETH or creates deposit request.\n */\n function _fallback() internal virtual whenNotPaused {\n if (msg.sender != address(wrappedNativeToken)) {\n Transfer.Request memory _request;\n _request.recipientAddr = msg.sender;\n _request.info.quantity = msg.value;\n _requestDepositFor(_request, _request.recipientAddr);\n }\n }\n}\n" - }, - "contracts/v0.8/interfaces/IWeightedValidator.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"./IQuorum.sol\";\n\ninterface IWeightedValidator is IQuorum {\n struct WeightedValidator {\n address validator;\n address governor;\n uint256 weight;\n }\n\n /// @dev Emitted when the validators are added\n event ValidatorsAdded(uint256 indexed nonce, WeightedValidator[] validators);\n /// @dev Emitted when the validators are updated\n event ValidatorsUpdated(uint256 indexed nonce, WeightedValidator[] validators);\n /// @dev Emitted when the validators are removed\n event ValidatorsRemoved(uint256 indexed nonce, address[] validators);\n\n /**\n * @dev Returns validator weight of the validator.\n */\n function getValidatorWeight(address _addr) external view returns (uint256);\n\n /**\n * @dev Returns governor weight of the governor.\n */\n function getGovernorWeight(address _addr) external view returns (uint256);\n\n /**\n * @dev Returns total validator weights of the address list.\n */\n function sumValidatorWeights(address[] calldata _addrList) external view returns (uint256 _weight);\n\n /**\n * @dev Returns total governor weights of the address list.\n */\n function sumGovernorWeights(address[] calldata _addrList) external view returns (uint256 _weight);\n\n /**\n * @dev Returns the validator list attached with governor address and weight.\n */\n function getValidatorInfo() external view returns (WeightedValidator[] memory _list);\n\n /**\n * @dev Returns the validator list.\n */\n function getValidators() external view returns (address[] memory _validators);\n\n /**\n * @dev Returns the validator at `_index` position.\n */\n function validators(uint256 _index) external view returns (WeightedValidator memory);\n\n /**\n * @dev Returns total of validators.\n */\n function totalValidators() external view returns (uint256);\n\n /**\n * @dev Returns total weights.\n */\n function totalWeights() external view returns (uint256);\n\n /**\n * @dev Adds validators.\n *\n * Requirements:\n * - The weights are larger than 0.\n * - The validators are not added.\n * - The method caller is admin.\n *\n * Emits the `ValidatorsAdded` event.\n *\n */\n function addValidators(WeightedValidator[] calldata _validators) external;\n\n /**\n * @dev Updates validators.\n *\n * Requirements:\n * - The weights are larger than 0.\n * - The validators are added.\n * - The method caller is admin.\n *\n * Emits the `ValidatorsUpdated` event.\n *\n */\n function updateValidators(WeightedValidator[] calldata _validators) external;\n\n /**\n * @dev Removes validators.\n *\n * Requirements:\n * - The validators are added.\n * - The method caller is admin.\n *\n * Emits the `ValidatorsRemoved` event.\n *\n */\n function removeValidators(address[] calldata _validators) external;\n}\n" - }, - "contracts/v0.8/extensions/HasProxyAdmin.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/utils/StorageSlot.sol\";\n\nabstract contract HasProxyAdmin {\n // bytes32(uint256(keccak256(\"eip1967.proxy.admin\")) - 1));\n bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;\n\n modifier onlyAdmin() {\n require(msg.sender == _getAdmin(), \"HasProxyAdmin: unauthorized sender\");\n _;\n }\n\n /**\n * @dev Returns proxy admin.\n */\n function _getAdmin() internal view returns (address) {\n return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;\n }\n}\n" - }, - "@openzeppelin/contracts/utils/introspection/IERC165.sol": { - "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n /**\n * @dev Returns true if this contract implements the interface defined by\n * `interfaceId`. See the corresponding\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n * to learn more about how these ids are created.\n *\n * This function call must use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n" - }, - "contracts/v0.8/interfaces/SignatureConsumer.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface SignatureConsumer {\n struct Signature {\n uint8 v;\n bytes32 r;\n bytes32 s;\n }\n}\n" - }, - "@openzeppelin/contracts/utils/Strings.sol": { - "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n bytes16 private constant _HEX_SYMBOLS = \"0123456789abcdef\";\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n */\n function toString(uint256 value) internal pure returns (string memory) {\n // Inspired by OraclizeAPI's implementation - MIT licence\n // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol\n\n if (value == 0) {\n return \"0\";\n }\n uint256 temp = value;\n uint256 digits;\n while (temp != 0) {\n digits++;\n temp /= 10;\n }\n bytes memory buffer = new bytes(digits);\n while (value != 0) {\n digits -= 1;\n buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));\n value /= 10;\n }\n return string(buffer);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n */\n function toHexString(uint256 value) internal pure returns (string memory) {\n if (value == 0) {\n return \"0x00\";\n }\n uint256 temp = value;\n uint256 length = 0;\n while (temp != 0) {\n length++;\n temp >>= 8;\n }\n return toHexString(value, length);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n */\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n bytes memory buffer = new bytes(2 * length + 2);\n buffer[0] = \"0\";\n buffer[1] = \"x\";\n for (uint256 i = 2 * length + 1; i > 1; --i) {\n buffer[i] = _HEX_SYMBOLS[value & 0xf];\n value >>= 4;\n }\n require(value == 0, \"Strings: hex length insufficient\");\n return string(buffer);\n }\n}\n" - }, - "@openzeppelin/contracts/utils/introspection/ERC165.sol": { - "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC165.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n *\n * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.\n */\nabstract contract ERC165 is IERC165 {\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IERC165).interfaceId;\n }\n}\n" - }, - "@openzeppelin/contracts/access/AccessControlEnumerable.sol": { - "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControlEnumerable.sol\";\nimport \"./AccessControl.sol\";\nimport \"../utils/structs/EnumerableSet.sol\";\n\n/**\n * @dev Extension of {AccessControl} that allows enumerating the members of each role.\n */\nabstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {\n using EnumerableSet for EnumerableSet.AddressSet;\n\n mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers;\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) {\n return _roleMembers[role].at(index);\n }\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) {\n return _roleMembers[role].length();\n }\n\n /**\n * @dev Overload {_grantRole} to track enumerable memberships\n */\n function _grantRole(bytes32 role, address account) internal virtual override {\n super._grantRole(role, account);\n _roleMembers[role].add(account);\n }\n\n /**\n * @dev Overload {_revokeRole} to track enumerable memberships\n */\n function _revokeRole(bytes32 role, address account) internal virtual override {\n super._revokeRole(role, account);\n _roleMembers[role].remove(account);\n }\n}\n" - }, - "contracts/v0.8/library/Transfer.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/utils/cryptography/ECDSA.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/utils/Strings.sol\";\nimport \"./Token.sol\";\n\nlibrary Transfer {\n using ECDSA for bytes32;\n\n enum Kind {\n Deposit,\n Withdrawal\n }\n\n struct Request {\n // For deposit request: Recipient address on Ronin network\n // For withdrawal request: Recipient address on mainchain network\n address recipientAddr;\n // Token address to deposit/withdraw\n // Value 0: native token\n address tokenAddr;\n Token.Info info;\n }\n\n /**\n * @dev Converts the transfer request into the deposit receipt.\n */\n function into_deposit_receipt(\n Request memory _request,\n address _requester,\n uint256 _id,\n address _roninTokenAddr,\n uint256 _roninChainId\n ) internal view returns (Receipt memory _receipt) {\n _receipt.id = _id;\n _receipt.kind = Kind.Deposit;\n _receipt.mainchain.addr = _requester;\n _receipt.mainchain.tokenAddr = _request.tokenAddr;\n _receipt.mainchain.chainId = block.chainid;\n _receipt.ronin.addr = _request.recipientAddr;\n _receipt.ronin.tokenAddr = _roninTokenAddr;\n _receipt.ronin.chainId = _roninChainId;\n _receipt.info = _request.info;\n }\n\n /**\n * @dev Converts the transfer request into the withdrawal receipt.\n */\n function into_withdrawal_receipt(\n Request memory _request,\n address _requester,\n uint256 _id,\n address _mainchainTokenAddr,\n uint256 _mainchainId\n ) internal view returns (Receipt memory _receipt) {\n _receipt.id = _id;\n _receipt.kind = Kind.Withdrawal;\n _receipt.ronin.addr = _requester;\n _receipt.ronin.tokenAddr = _request.tokenAddr;\n _receipt.ronin.chainId = block.chainid;\n _receipt.mainchain.addr = _request.recipientAddr;\n _receipt.mainchain.tokenAddr = _mainchainTokenAddr;\n _receipt.mainchain.chainId = _mainchainId;\n _receipt.info = _request.info;\n }\n\n struct Receipt {\n uint256 id;\n Kind kind;\n Token.Owner mainchain;\n Token.Owner ronin;\n Token.Info info;\n }\n\n // keccak256(\"Receipt(uint256 id,uint8 kind,TokenOwner mainchain,TokenOwner ronin,TokenInfo info)TokenInfo(uint8 erc,uint256 id,uint256 quantity)TokenOwner(address addr,address tokenAddr,uint256 chainId)\");\n bytes32 public constant TYPE_HASH = 0xb9d1fe7c9deeec5dc90a2f47ff1684239519f2545b2228d3d91fb27df3189eea;\n\n /**\n * @dev Returns token info struct hash.\n */\n function hash(Receipt memory _receipt) internal pure returns (bytes32) {\n return\n keccak256(\n abi.encode(\n TYPE_HASH,\n _receipt.id,\n _receipt.kind,\n Token.hash(_receipt.mainchain),\n Token.hash(_receipt.ronin),\n Token.hash(_receipt.info)\n )\n );\n }\n\n /**\n * @dev Returns the receipt digest.\n */\n function receiptDigest(bytes32 _domainSeparator, bytes32 _receiptHash) internal pure returns (bytes32) {\n return _domainSeparator.toTypedDataHash(_receiptHash);\n }\n}\n" - }, - "@openzeppelin/contracts/utils/Context.sol": { - "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n" - }, - "@openzeppelin/contracts/security/Pausable.sol": { - "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n" - }, - "@openzeppelin/contracts/utils/structs/EnumerableSet.sol": { - "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 => uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n" - }, - "contracts/v0.8/interfaces/IQuorum.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IQuorum {\n /// @dev Emitted when the threshold is updated\n event ThresholdUpdated(\n uint256 indexed nonce,\n uint256 indexed numerator,\n uint256 indexed denominator,\n uint256 previousNumerator,\n uint256 previousDenominator\n );\n\n /**\n * @dev Returns the threshold.\n */\n function getThreshold() external view returns (uint256 _num, uint256 _denom);\n\n /**\n * @dev Checks whether the `_voteWeight` passes the threshold.\n */\n function checkThreshold(uint256 _voteWeight) external view returns (bool);\n\n /**\n * @dev Returns the minimum vote weight to pass the threshold.\n */\n function minimumVoteWeight() external view returns (uint256);\n\n /**\n * @dev Sets the threshold.\n *\n * Requirements:\n * - The method caller is admin.\n *\n * Emits the `ThresholdUpdated` event.\n *\n */\n function setThreshold(uint256 _numerator, uint256 _denominator)\n external\n returns (uint256 _previousNum, uint256 _previousDenom);\n}\n" - }, - "contracts/v0.8/interfaces/IWETH.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH {\n function deposit() external payable;\n\n function withdraw(uint256 _wad) external;\n\n function balanceOf(address) external view returns (uint256);\n}\n" - }, - "@openzeppelin/contracts/access/AccessControl.sol": { - "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControl.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\nimport \"../utils/Context.sol\";\nimport \"../utils/Strings.sol\";\nimport \"../utils/introspection/ERC165.sol\";\n\n/**\n * @dev Contract module that allows children to implement role-based access\n * control mechanisms. This is a lightweight version that doesn't allow enumerating role\n * members except through off-chain means by accessing the contract event logs. Some\n * applications may benefit from on-chain enumerability, for those cases see\n * {AccessControlEnumerable}.\n *\n * Roles are referred to by their `bytes32` identifier. These should be exposed\n * in the external API and be unique. The best way to achieve this is by\n * using `public constant` hash digests:\n *\n * ```\n * bytes32 public constant MY_ROLE = keccak256(\"MY_ROLE\");\n * ```\n *\n * Roles can be used to represent a set of permissions. To restrict access to a\n * function call, use {hasRole}:\n *\n * ```\n * function foo() public {\n * require(hasRole(MY_ROLE, msg.sender));\n * ...\n * }\n * ```\n *\n * Roles can be granted and revoked dynamically via the {grantRole} and\n * {revokeRole} functions. Each role has an associated admin role, and only\n * accounts that have a role's admin role can call {grantRole} and {revokeRole}.\n *\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\n * that only accounts with this role will be able to grant or revoke other\n * roles. More complex role relationships can be created by using\n * {_setRoleAdmin}.\n *\n * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to\n * grant and revoke this role. Extra precautions should be taken to secure\n * accounts that have been granted it.\n */\nabstract contract AccessControl is Context, IAccessControl, ERC165 {\n struct RoleData {\n mapping(address => bool) members;\n bytes32 adminRole;\n }\n\n mapping(bytes32 => RoleData) private _roles;\n\n bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;\n\n /**\n * @dev Modifier that checks that an account has a specific role. Reverts\n * with a standardized message including the required role.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n *\n * _Available since v4.1._\n */\n modifier onlyRole(bytes32 role) {\n _checkRole(role, _msgSender());\n _;\n }\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) public view virtual override returns (bool) {\n return _roles[role].members[account];\n }\n\n /**\n * @dev Revert with a standard message if `account` is missing `role`.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n */\n function _checkRole(bytes32 role, address account) internal view virtual {\n if (!hasRole(role, account)) {\n revert(\n string(\n abi.encodePacked(\n \"AccessControl: account \",\n Strings.toHexString(uint160(account), 20),\n \" is missing role \",\n Strings.toHexString(uint256(role), 32)\n )\n )\n );\n }\n }\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {\n return _roles[role].adminRole;\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _grantRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _revokeRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been revoked `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) public virtual override {\n require(account == _msgSender(), \"AccessControl: can only renounce roles for self\");\n\n _revokeRole(role, account);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event. Note that unlike {grantRole}, this function doesn't perform any\n * checks on the calling account.\n *\n * [WARNING]\n * ====\n * This function should only be called from the constructor when setting\n * up the initial roles for the system.\n *\n * Using this function in any other way is effectively circumventing the admin\n * system imposed by {AccessControl}.\n * ====\n *\n * NOTE: This function is deprecated in favor of {_grantRole}.\n */\n function _setupRole(bytes32 role, address account) internal virtual {\n _grantRole(role, account);\n }\n\n /**\n * @dev Sets `adminRole` as ``role``'s admin role.\n *\n * Emits a {RoleAdminChanged} event.\n */\n function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\n bytes32 previousAdminRole = getRoleAdmin(role);\n _roles[role].adminRole = adminRole;\n emit RoleAdminChanged(role, previousAdminRole, adminRole);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * Internal function without access restriction.\n */\n function _grantRole(bytes32 role, address account) internal virtual {\n if (!hasRole(role, account)) {\n _roles[role].members[account] = true;\n emit RoleGranted(role, account, _msgSender());\n }\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * Internal function without access restriction.\n */\n function _revokeRole(bytes32 role, address account) internal virtual {\n if (hasRole(role, account)) {\n _roles[role].members[account] = false;\n emit RoleRevoked(role, account, _msgSender());\n }\n }\n}\n" - }, - "@openzeppelin/contracts/utils/Address.sol": { - "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" - }, - "@openzeppelin/contracts/token/ERC20/IERC20.sol": { - "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" - } - }, - "settings": { - "evmVersion": "london", - "libraries": {}, - "metadata": { - "bytecodeHash": "ipfs", - "useLiteralContent": true - }, - "optimizer": { - "enabled": true, - "runs": 1000 - }, - "remappings": [], - "outputSelection": { - "*": { - "*": [ - "evm.bytecode", - "evm.deployedBytecode", - "devdoc", - "userdoc", - "metadata", - "abi" - ] - } - } - } - }, - "ABI": "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"tokens\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"limits\",\"type\":\"uint256[]\"}],\"name\":\"DailyWithdrawalLimitsUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"receiptHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"enum Transfer.Kind\",\"name\":\"kind\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"mainchain\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"ronin\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"enum Token.Standard\",\"name\":\"erc\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Info\",\"name\":\"info\",\"type\":\"tuple\"}],\"indexed\":false,\"internalType\":\"struct Transfer.Receipt\",\"name\":\"receipt\",\"type\":\"tuple\"}],\"name\":\"DepositRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"tokens\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"thresholds\",\"type\":\"uint256[]\"}],\"name\":\"HighTierThresholdsUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"numerator\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"denominator\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"previousNumerator\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"previousDenominator\",\"type\":\"uint256\"}],\"name\":\"HighTierVoteWeightThresholdUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"tokens\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"thresholds\",\"type\":\"uint256[]\"}],\"name\":\"LockedThresholdsUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"previousAdminRole\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"newAdminRole\",\"type\":\"bytes32\"}],\"name\":\"RoleAdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleGranted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleRevoked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"numerator\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"denominator\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"previousNumerator\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"previousDenominator\",\"type\":\"uint256\"}],\"name\":\"ThresholdUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"mainchainTokens\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"roninTokens\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"enum Token.Standard[]\",\"name\":\"standards\",\"type\":\"uint8[]\"}],\"name\":\"TokenMapped\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"tokens\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"percentages\",\"type\":\"uint256[]\"}],\"name\":\"UnlockFeePercentagesUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IWeightedValidator\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"ValidatorContractUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"receiptHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"enum Transfer.Kind\",\"name\":\"kind\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"mainchain\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"ronin\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"enum Token.Standard\",\"name\":\"erc\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Info\",\"name\":\"info\",\"type\":\"tuple\"}],\"indexed\":false,\"internalType\":\"struct Transfer.Receipt\",\"name\":\"receipt\",\"type\":\"tuple\"}],\"name\":\"WithdrawalLocked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"receiptHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"enum Transfer.Kind\",\"name\":\"kind\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"mainchain\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"ronin\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"enum Token.Standard\",\"name\":\"erc\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Info\",\"name\":\"info\",\"type\":\"tuple\"}],\"indexed\":false,\"internalType\":\"struct Transfer.Receipt\",\"name\":\"receipt\",\"type\":\"tuple\"}],\"name\":\"WithdrawalUnlocked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"receiptHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"enum Transfer.Kind\",\"name\":\"kind\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"mainchain\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"ronin\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"enum Token.Standard\",\"name\":\"erc\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Info\",\"name\":\"info\",\"type\":\"tuple\"}],\"indexed\":false,\"internalType\":\"struct Transfer.Receipt\",\"name\":\"receipt\",\"type\":\"tuple\"}],\"name\":\"Withdrew\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IWETH\",\"name\":\"weth\",\"type\":\"address\"}],\"name\":\"WrappedNativeTokenContractUpdated\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"DEFAULT_ADMIN_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"DOMAIN_SEPARATOR\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"WITHDRAWAL_UNLOCKER_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"_MAX_PERCENTAGE\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_voteWeight\",\"type\":\"uint256\"}],\"name\":\"checkHighTierVoteWeightThreshold\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_voteWeight\",\"type\":\"uint256\"}],\"name\":\"checkThreshold\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"dailyWithdrawalLimit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"depositCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getHighTierVoteWeightThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"}],\"name\":\"getRoleAdmin\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"getRoleMember\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"}],\"name\":\"getRoleMemberCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_mainchainToken\",\"type\":\"address\"}],\"name\":\"getRoninToken\",\"outputs\":[{\"components\":[{\"internalType\":\"enum Token.Standard\",\"name\":\"erc\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"}],\"internalType\":\"struct MappedTokenConsumer.MappedToken\",\"name\":\"_token\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"grantRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"hasRole\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"highTierThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_roleSetter\",\"type\":\"address\"},{\"internalType\":\"contract IWETH\",\"name\":\"_wrappedToken\",\"type\":\"address\"},{\"internalType\":\"contract IWeightedValidator\",\"name\":\"_validatorContract\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_roninChainId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_numerator\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_highTierVWNumerator\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_denominator\",\"type\":\"uint256\"},{\"internalType\":\"address[][3]\",\"name\":\"_addresses\",\"type\":\"address[][3]\"},{\"internalType\":\"uint256[][4]\",\"name\":\"_thresholds\",\"type\":\"uint256[][4]\"},{\"internalType\":\"enum Token.Standard[]\",\"name\":\"_standards\",\"type\":\"uint8[]\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"lastDateSynced\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"lastSyncedWithdrawal\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"lockedThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_mainchainTokens\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"_roninTokens\",\"type\":\"address[]\"},{\"internalType\":\"enum Token.Standard[]\",\"name\":\"_standards\",\"type\":\"uint8[]\"}],\"name\":\"mapTokens\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_mainchainTokens\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"_roninTokens\",\"type\":\"address[]\"},{\"internalType\":\"enum Token.Standard[]\",\"name\":\"_standards\",\"type\":\"uint8[]\"},{\"internalType\":\"uint256[][4]\",\"name\":\"_thresholds\",\"type\":\"uint256[][4]\"}],\"name\":\"mapTokensAndThresholds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"minimumVoteWeight\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"nonce\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_quantity\",\"type\":\"uint256\"}],\"name\":\"reachedWithdrawalLimit\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"receiveEther\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"renounceRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"recipientAddr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"enum Token.Standard\",\"name\":\"erc\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Info\",\"name\":\"info\",\"type\":\"tuple\"}],\"internalType\":\"struct Transfer.Request\",\"name\":\"_request\",\"type\":\"tuple\"}],\"name\":\"requestDepositFor\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"revokeRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"roninChainId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_tokens\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"_limits\",\"type\":\"uint256[]\"}],\"name\":\"setDailyWithdrawalLimits\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_tokens\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"_thresholds\",\"type\":\"uint256[]\"}],\"name\":\"setHighTierThresholds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_numerator\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_denominator\",\"type\":\"uint256\"}],\"name\":\"setHighTierVoteWeightThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"_previousNum\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_previousDenom\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_tokens\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"_thresholds\",\"type\":\"uint256[]\"}],\"name\":\"setLockedThresholds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_numerator\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_denominator\",\"type\":\"uint256\"}],\"name\":\"setThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"_previousNum\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_previousDenom\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_tokens\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"_percentages\",\"type\":\"uint256[]\"}],\"name\":\"setUnlockFeePercentages\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IWeightedValidator\",\"name\":\"_validatorContract\",\"type\":\"address\"}],\"name\":\"setValidatorContract\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IWETH\",\"name\":\"_wrappedToken\",\"type\":\"address\"}],\"name\":\"setWrappedNativeTokenContract\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"enum Transfer.Kind\",\"name\":\"kind\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"mainchain\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"ronin\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"enum Token.Standard\",\"name\":\"erc\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Info\",\"name\":\"info\",\"type\":\"tuple\"}],\"internalType\":\"struct Transfer.Receipt\",\"name\":\"_receipt\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"internalType\":\"struct SignatureConsumer.Signature[]\",\"name\":\"_signatures\",\"type\":\"tuple[]\"}],\"name\":\"submitWithdrawal\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"_locked\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"unlockFeePercentages\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"enum Transfer.Kind\",\"name\":\"kind\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"mainchain\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"ronin\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"enum Token.Standard\",\"name\":\"erc\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Info\",\"name\":\"info\",\"type\":\"tuple\"}],\"internalType\":\"struct Transfer.Receipt\",\"name\":\"_receipt\",\"type\":\"tuple\"}],\"name\":\"unlockWithdrawal\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"validatorContract\",\"outputs\":[{\"internalType\":\"contract IWeightedValidator\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"withdrawalHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"withdrawalLocked\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"wrappedNativeToken\",\"outputs\":[{\"internalType\":\"contract IWETH\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]", - "ContractName": "MainchainGatewayV2", - "CompilerVersion": "v0.8.9+commit.e5eed63a", - "OptimizationUsed": 1, - "Runs": 1000, - "ConstructorArguments": "0x", - "EVMVersion": "Default", - "Library": "", - "LicenseType": "MIT", - "Proxy": 0, - "SwarmSource": "" - } -] \ No newline at end of file +[{"SourceCode":{"language":"Solidity","sources":{"@openzeppelin/contracts/access/IAccessControl.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev External interface of AccessControl declared to support ERC165 detection.\n */\ninterface IAccessControl {\n /**\n * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n *\n * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n * {RoleAdminChanged} not being emitted signaling this.\n *\n * _Available since v3.1._\n */\n event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n /**\n * @dev Emitted when `account` is granted `role`.\n *\n * `sender` is the account that originated the contract call, an admin role\n * bearer except when using {AccessControl-_setupRole}.\n */\n event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Emitted when `account` is revoked `role`.\n *\n * `sender` is the account that originated the contract call:\n * - if using `revokeRole`, it is the admin role bearer\n * - if using `renounceRole`, it is the role bearer (i.e. `account`)\n */\n event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) external view returns (bool);\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been granted `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) external;\n}\n"},"@openzeppelin/contracts/token/ERC721/IERC721.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../../utils/introspection/IERC165.sol\";\n\n/**\n * @dev Required interface of an ERC721 compliant contract.\n */\ninterface IERC721 is IERC165 {\n /**\n * @dev Emitted when `tokenId` token is transferred from `from` to `to`.\n */\n event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);\n\n /**\n * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.\n */\n event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);\n\n /**\n * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.\n */\n event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n\n /**\n * @dev Returns the number of tokens in ``owner``'s account.\n */\n function balanceOf(address owner) external view returns (uint256 balance);\n\n /**\n * @dev Returns the owner of the `tokenId` token.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function ownerOf(uint256 tokenId) external view returns (address owner);\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\n * are aware of the ERC721 protocol to prevent tokens from being forever locked.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 tokenId\n ) external;\n\n /**\n * @dev Transfers `tokenId` token from `from` to `to`.\n *\n * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must be owned by `from`.\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 tokenId\n ) external;\n\n /**\n * @dev Gives permission to `to` to transfer `tokenId` token to another account.\n * The approval is cleared when the token is transferred.\n *\n * Only a single account can be approved at a time, so approving the zero address clears previous approvals.\n *\n * Requirements:\n *\n * - The caller must own the token or be an approved operator.\n * - `tokenId` must exist.\n *\n * Emits an {Approval} event.\n */\n function approve(address to, uint256 tokenId) external;\n\n /**\n * @dev Returns the account approved for `tokenId` token.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function getApproved(uint256 tokenId) external view returns (address operator);\n\n /**\n * @dev Approve or remove `operator` as an operator for the caller.\n * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.\n *\n * Requirements:\n *\n * - The `operator` cannot be the caller.\n *\n * Emits an {ApprovalForAll} event.\n */\n function setApprovalForAll(address operator, bool _approved) external;\n\n /**\n * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.\n *\n * See {setApprovalForAll}\n */\n function isApprovedForAll(address owner, address operator) external view returns (bool);\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 tokenId,\n bytes calldata data\n ) external;\n}\n"},"@openzeppelin/contracts/access/IAccessControlEnumerable.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\n\n/**\n * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.\n */\ninterface IAccessControlEnumerable is IAccessControl {\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) external view returns (address);\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) external view returns (uint256);\n}\n"},"@openzeppelin/contracts/utils/StorageSlot.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/StorageSlot.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for reading and writing primitive types to specific storage slots.\n *\n * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.\n * This library helps with reading and writing to such slots without the need for inline assembly.\n *\n * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.\n *\n * Example usage to set ERC1967 implementation slot:\n * ```\n * contract ERC1967 {\n * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n *\n * function _getImplementation() internal view returns (address) {\n * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\n * }\n *\n * function _setImplementation(address newImplementation) internal {\n * require(Address.isContract(newImplementation), \"ERC1967: new implementation is not a contract\");\n * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\n * }\n * }\n * ```\n *\n * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._\n */\nlibrary StorageSlot {\n struct AddressSlot {\n address value;\n }\n\n struct BooleanSlot {\n bool value;\n }\n\n struct Bytes32Slot {\n bytes32 value;\n }\n\n struct Uint256Slot {\n uint256 value;\n }\n\n /**\n * @dev Returns an `AddressSlot` with member `value` located at `slot`.\n */\n function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {\n assembly {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns an `BooleanSlot` with member `value` located at `slot`.\n */\n function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {\n assembly {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.\n */\n function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {\n assembly {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns an `Uint256Slot` with member `value` located at `slot`.\n */\n function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {\n assembly {\n r.slot := slot\n }\n }\n}\n"},"@openzeppelin/contracts/utils/cryptography/ECDSA.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/ECDSA.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../Strings.sol\";\n\n/**\n * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.\n *\n * These functions can be used to verify that a message was signed by the holder\n * of the private keys of a given address.\n */\nlibrary ECDSA {\n enum RecoverError {\n NoError,\n InvalidSignature,\n InvalidSignatureLength,\n InvalidSignatureS,\n InvalidSignatureV\n }\n\n function _throwError(RecoverError error) private pure {\n if (error == RecoverError.NoError) {\n return; // no error: do nothing\n } else if (error == RecoverError.InvalidSignature) {\n revert(\"ECDSA: invalid signature\");\n } else if (error == RecoverError.InvalidSignatureLength) {\n revert(\"ECDSA: invalid signature length\");\n } else if (error == RecoverError.InvalidSignatureS) {\n revert(\"ECDSA: invalid signature 's' value\");\n } else if (error == RecoverError.InvalidSignatureV) {\n revert(\"ECDSA: invalid signature 'v' value\");\n }\n }\n\n /**\n * @dev Returns the address that signed a hashed message (`hash`) with\n * `signature` or error string. This address can then be used for verification purposes.\n *\n * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:\n * this function rejects them by requiring the `s` value to be in the lower\n * half order, and the `v` value to be either 27 or 28.\n *\n * IMPORTANT: `hash` _must_ be the result of a hash operation for the\n * verification to be secure: it is possible to craft signatures that\n * recover to arbitrary addresses for non-hashed data. A safe way to ensure\n * this is by receiving a hash of the original message (which may otherwise\n * be too long), and then calling {toEthSignedMessageHash} on it.\n *\n * Documentation for signature generation:\n * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]\n * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]\n *\n * _Available since v4.3._\n */\n function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {\n // Check the signature length\n // - case 65: r,s,v signature (standard)\n // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._\n if (signature.length == 65) {\n bytes32 r;\n bytes32 s;\n uint8 v;\n // ecrecover takes the signature parameters, and the only way to get them\n // currently is to use assembly.\n assembly {\n r := mload(add(signature, 0x20))\n s := mload(add(signature, 0x40))\n v := byte(0, mload(add(signature, 0x60)))\n }\n return tryRecover(hash, v, r, s);\n } else if (signature.length == 64) {\n bytes32 r;\n bytes32 vs;\n // ecrecover takes the signature parameters, and the only way to get them\n // currently is to use assembly.\n assembly {\n r := mload(add(signature, 0x20))\n vs := mload(add(signature, 0x40))\n }\n return tryRecover(hash, r, vs);\n } else {\n return (address(0), RecoverError.InvalidSignatureLength);\n }\n }\n\n /**\n * @dev Returns the address that signed a hashed message (`hash`) with\n * `signature`. This address can then be used for verification purposes.\n *\n * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:\n * this function rejects them by requiring the `s` value to be in the lower\n * half order, and the `v` value to be either 27 or 28.\n *\n * IMPORTANT: `hash` _must_ be the result of a hash operation for the\n * verification to be secure: it is possible to craft signatures that\n * recover to arbitrary addresses for non-hashed data. A safe way to ensure\n * this is by receiving a hash of the original message (which may otherwise\n * be too long), and then calling {toEthSignedMessageHash} on it.\n */\n function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, signature);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.\n *\n * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]\n *\n * _Available since v4.3._\n */\n function tryRecover(\n bytes32 hash,\n bytes32 r,\n bytes32 vs\n ) internal pure returns (address, RecoverError) {\n bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);\n uint8 v = uint8((uint256(vs) >> 255) + 27);\n return tryRecover(hash, v, r, s);\n }\n\n /**\n * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.\n *\n * _Available since v4.2._\n */\n function recover(\n bytes32 hash,\n bytes32 r,\n bytes32 vs\n ) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, r, vs);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Overload of {ECDSA-tryRecover} that receives the `v`,\n * `r` and `s` signature fields separately.\n *\n * _Available since v4.3._\n */\n function tryRecover(\n bytes32 hash,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal pure returns (address, RecoverError) {\n // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature\n // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines\n // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most\n // signatures from current libraries generate a unique signature with an s-value in the lower half order.\n //\n // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value\n // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or\n // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept\n // these malleable signatures as well.\n if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {\n return (address(0), RecoverError.InvalidSignatureS);\n }\n if (v != 27 && v != 28) {\n return (address(0), RecoverError.InvalidSignatureV);\n }\n\n // If the signature is valid (and not malleable), return the signer address\n address signer = ecrecover(hash, v, r, s);\n if (signer == address(0)) {\n return (address(0), RecoverError.InvalidSignature);\n }\n\n return (signer, RecoverError.NoError);\n }\n\n /**\n * @dev Overload of {ECDSA-recover} that receives the `v`,\n * `r` and `s` signature fields separately.\n */\n function recover(\n bytes32 hash,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, v, r, s);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Returns an Ethereum Signed Message, created from a `hash`. This\n * produces hash corresponding to the one signed with the\n * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]\n * JSON-RPC method as part of EIP-191.\n *\n * See {recover}.\n */\n function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {\n // 32 is the length in bytes of hash,\n // enforced by the type signature above\n return keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n32\", hash));\n }\n\n /**\n * @dev Returns an Ethereum Signed Message, created from `s`. This\n * produces hash corresponding to the one signed with the\n * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]\n * JSON-RPC method as part of EIP-191.\n *\n * See {recover}.\n */\n function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {\n return keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n\", Strings.toString(s.length), s));\n }\n\n /**\n * @dev Returns an Ethereum Signed Typed Data, created from a\n * `domainSeparator` and a `structHash`. This produces hash corresponding\n * to the one signed with the\n * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]\n * JSON-RPC method as part of EIP-712.\n *\n * See {recover}.\n */\n function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {\n return keccak256(abi.encodePacked(\"\\x19\\x01\", domainSeparator, structHash));\n }\n}\n"},"contracts/v0.8/extensions/GatewayV2.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/security/Pausable.sol\";\nimport \"../interfaces/IQuorum.sol\";\nimport \"../interfaces/IWeightedValidator.sol\";\nimport \"./HasProxyAdmin.sol\";\n\nabstract contract GatewayV2 is HasProxyAdmin, Pausable, IQuorum {\n /// @dev Emitted when the validator contract address is updated.\n event ValidatorContractUpdated(IWeightedValidator);\n\n uint256 internal _num;\n uint256 internal _denom;\n\n IWeightedValidator public validatorContract;\n uint256 public nonce;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n */\n uint256[50] private ______gap;\n\n /**\n * @dev See {IQuorum-getThreshold}.\n */\n function getThreshold() external view virtual returns (uint256, uint256) {\n return (_num, _denom);\n }\n\n /**\n * @dev See {IQuorum-checkThreshold}.\n */\n function checkThreshold(uint256 _voteWeight) external view virtual returns (bool) {\n return _voteWeight * _denom >= _num * validatorContract.totalWeights();\n }\n\n /**\n * @dev See {IQuorum-setThreshold}.\n */\n function setThreshold(uint256 _numerator, uint256 _denominator)\n external\n virtual\n onlyAdmin\n returns (uint256, uint256)\n {\n return _setThreshold(_numerator, _denominator);\n }\n\n /**\n * @dev Triggers paused state.\n */\n function pause() external onlyAdmin {\n _pause();\n }\n\n /**\n * @dev Triggers unpaused state.\n */\n function unpause() external onlyAdmin {\n _unpause();\n }\n\n /**\n * @dev Sets validator contract address.\n *\n * Requirements:\n * - The method caller is admin.\n *\n * Emits the `ValidatorContractUpdated` event.\n *\n */\n function setValidatorContract(IWeightedValidator _validatorContract) external virtual onlyAdmin {\n _setValidatorContract(_validatorContract);\n }\n\n /**\n * @dev See {IQuorum-minimumVoteWeight}.\n */\n function minimumVoteWeight() public view virtual returns (uint256) {\n return _minimumVoteWeight(validatorContract.totalWeights());\n }\n\n /**\n * @dev Sets validator contract address.\n *\n * Emits the `ValidatorContractUpdated` event.\n *\n */\n function _setValidatorContract(IWeightedValidator _validatorContract) internal virtual {\n validatorContract = _validatorContract;\n emit ValidatorContractUpdated(_validatorContract);\n }\n\n /**\n * @dev Sets threshold and returns the old one.\n *\n * Emits the `ThresholdUpdated` event.\n *\n */\n function _setThreshold(uint256 _numerator, uint256 _denominator)\n internal\n virtual\n returns (uint256 _previousNum, uint256 _previousDenom)\n {\n require(_numerator <= _denominator, \"GatewayV2: invalid threshold\");\n _previousNum = _num;\n _previousDenom = _denom;\n _num = _numerator;\n _denom = _denominator;\n emit ThresholdUpdated(nonce++, _numerator, _denominator, _previousNum, _previousDenom);\n }\n\n /**\n * @dev Returns minimum vote weight.\n */\n function _minimumVoteWeight(uint256 _totalWeight) internal view virtual returns (uint256) {\n return (_num * _totalWeight + _denom - 1) / _denom;\n }\n}\n"},"@openzeppelin/contracts/proxy/utils/Initializable.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (proxy/utils/Initializable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../../utils/Address.sol\";\n\n/**\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\n * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\n *\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\n *\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\n *\n * [CAUTION]\n * ====\n * Avoid leaving a contract uninitialized.\n *\n * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation\n * contract, which may impact the proxy. To initialize the implementation contract, you can either invoke the\n * initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed:\n *\n * [.hljs-theme-light.nopadding]\n * ```\n * /// @custom:oz-upgrades-unsafe-allow constructor\n * constructor() initializer {}\n * ```\n * ====\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private _initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private _initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n // If the contract is initializing we ignore whether _initialized is set in order to support multiple\n // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the\n // contract may have been reentered.\n require(_initializing ? _isConstructor() : !_initialized, \"Initializable: contract is already initialized\");\n\n bool isTopLevelCall = !_initializing;\n if (isTopLevelCall) {\n _initializing = true;\n _initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n _initializing = false;\n }\n }\n\n /**\n * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the\n * {initializer} modifier, directly or indirectly.\n */\n modifier onlyInitializing() {\n require(_initializing, \"Initializable: contract is not initializing\");\n _;\n }\n\n function _isConstructor() private view returns (bool) {\n return !Address.isContract(address(this));\n }\n}\n"},"contracts/v0.8/extensions/WithdrawalLimitation.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"./GatewayV2.sol\";\n\nabstract contract WithdrawalLimitation is GatewayV2 {\n /// @dev Emitted when the high-tier vote weight threshold is updated\n event HighTierVoteWeightThresholdUpdated(\n uint256 indexed nonce,\n uint256 indexed numerator,\n uint256 indexed denominator,\n uint256 previousNumerator,\n uint256 previousDenominator\n );\n /// @dev Emitted when the thresholds for high-tier withdrawals that requires high-tier vote weights are updated\n event HighTierThresholdsUpdated(address[] tokens, uint256[] thresholds);\n /// @dev Emitted when the thresholds for locked withdrawals are updated\n event LockedThresholdsUpdated(address[] tokens, uint256[] thresholds);\n /// @dev Emitted when the fee percentages to unlock withdraw are updated\n event UnlockFeePercentagesUpdated(address[] tokens, uint256[] percentages);\n /// @dev Emitted when the daily limit thresholds are updated\n event DailyWithdrawalLimitsUpdated(address[] tokens, uint256[] limits);\n\n uint256 public constant _MAX_PERCENTAGE = 1_000_000;\n\n uint256 internal _highTierVWNum;\n uint256 internal _highTierVWDenom;\n\n /// @dev Mapping from mainchain token => the amount thresholds for high-tier withdrawals that requires high-tier vote weights\n mapping(address => uint256) public highTierThreshold;\n /// @dev Mapping from mainchain token => the amount thresholds to lock withdrawal\n mapping(address => uint256) public lockedThreshold;\n /// @dev Mapping from mainchain token => unlock fee percentages for unlocker\n /// @notice Values 0-1,000,000 map to 0%-100%\n mapping(address => uint256) public unlockFeePercentages;\n /// @dev Mapping from mainchain token => daily limit amount for withdrawal\n mapping(address => uint256) public dailyWithdrawalLimit;\n /// @dev Mapping from token address => today withdrawal amount\n mapping(address => uint256) public lastSyncedWithdrawal;\n /// @dev Mapping from token address => last date synced to record the `lastSyncedWithdrawal`\n mapping(address => uint256) public lastDateSynced;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n */\n uint256[50] private ______gap;\n\n /**\n * @dev Override {GatewayV2-setThreshold}.\n *\n * Requirements:\n * - The high-tier vote weight threshold must equal to or larger than the normal threshold.\n *\n */\n function setThreshold(uint256 _numerator, uint256 _denominator)\n external\n virtual\n override\n onlyAdmin\n returns (uint256 _previousNum, uint256 _previousDenom)\n {\n (_previousNum, _previousDenom) = _setThreshold(_numerator, _denominator);\n _verifyThresholds();\n }\n\n /**\n * @dev Returns the high-tier vote weight threshold.\n */\n function getHighTierVoteWeightThreshold() external view virtual returns (uint256, uint256) {\n return (_highTierVWNum, _highTierVWDenom);\n }\n\n /**\n * @dev Checks whether the `_voteWeight` passes the high-tier vote weight threshold.\n */\n function checkHighTierVoteWeightThreshold(uint256 _voteWeight) external view virtual returns (bool) {\n return _voteWeight * _highTierVWDenom >= _highTierVWNum * validatorContract.totalWeights();\n }\n\n /**\n * @dev Sets high-tier vote weight threshold and returns the old one.\n *\n * Requirements:\n * - The method caller is admin.\n * - The high-tier vote weight threshold must equal to or larger than the normal threshold.\n *\n * Emits the `HighTierVoteWeightThresholdUpdated` event.\n *\n */\n function setHighTierVoteWeightThreshold(uint256 _numerator, uint256 _denominator)\n external\n virtual\n onlyAdmin\n returns (uint256 _previousNum, uint256 _previousDenom)\n {\n (_previousNum, _previousDenom) = _setHighTierVoteWeightThreshold(_numerator, _denominator);\n _verifyThresholds();\n }\n\n /**\n * @dev Sets the thresholds for high-tier withdrawals that requires high-tier vote weights.\n *\n * Requirements:\n * - The method caller is admin.\n * - The arrays have the same length and its length larger than 0.\n *\n * Emits the `HighTierThresholdsUpdated` event.\n *\n */\n function setHighTierThresholds(address[] calldata _tokens, uint256[] calldata _thresholds)\n external\n virtual\n onlyAdmin\n {\n require(_tokens.length > 0, \"WithdrawalLimitation: invalid array length\");\n _setHighTierThresholds(_tokens, _thresholds);\n }\n\n /**\n * @dev Sets the amount thresholds to lock withdrawal.\n *\n * Requirements:\n * - The method caller is admin.\n * - The arrays have the same length and its length larger than 0.\n *\n * Emits the `LockedThresholdsUpdated` event.\n *\n */\n function setLockedThresholds(address[] calldata _tokens, uint256[] calldata _thresholds) external virtual onlyAdmin {\n require(_tokens.length > 0, \"WithdrawalLimitation: invalid array length\");\n _setLockedThresholds(_tokens, _thresholds);\n }\n\n /**\n * @dev Sets fee percentages to unlock withdrawal.\n *\n * Requirements:\n * - The method caller is admin.\n * - The arrays have the same length and its length larger than 0.\n *\n * Emits the `UnlockFeePercentagesUpdated` event.\n *\n */\n function setUnlockFeePercentages(address[] calldata _tokens, uint256[] calldata _percentages)\n external\n virtual\n onlyAdmin\n {\n require(_tokens.length > 0, \"WithdrawalLimitation: invalid array length\");\n _setUnlockFeePercentages(_tokens, _percentages);\n }\n\n /**\n * @dev Sets daily limit amounts for the withdrawals.\n *\n * Requirements:\n * - The method caller is admin.\n * - The arrays have the same length and its length larger than 0.\n *\n * Emits the `DailyWithdrawalLimitsUpdated` event.\n *\n */\n function setDailyWithdrawalLimits(address[] calldata _tokens, uint256[] calldata _limits) external virtual onlyAdmin {\n require(_tokens.length > 0, \"WithdrawalLimitation: invalid array length\");\n _setDailyWithdrawalLimits(_tokens, _limits);\n }\n\n /**\n * @dev Checks whether the withdrawal reaches the limitation.\n */\n function reachedWithdrawalLimit(address _token, uint256 _quantity) external view virtual returns (bool) {\n return _reachedWithdrawalLimit(_token, _quantity);\n }\n\n /**\n * @dev Sets high-tier vote weight threshold and returns the old one.\n *\n * Emits the `HighTierVoteWeightThresholdUpdated` event.\n *\n */\n function _setHighTierVoteWeightThreshold(uint256 _numerator, uint256 _denominator)\n internal\n returns (uint256 _previousNum, uint256 _previousDenom)\n {\n require(_numerator <= _denominator, \"WithdrawalLimitation: invalid threshold\");\n _previousNum = _highTierVWNum;\n _previousDenom = _highTierVWDenom;\n _highTierVWNum = _numerator;\n _highTierVWDenom = _denominator;\n emit HighTierVoteWeightThresholdUpdated(nonce++, _numerator, _denominator, _previousNum, _previousDenom);\n }\n\n /**\n * @dev Sets the thresholds for high-tier withdrawals that requires high-tier vote weights.\n *\n * Requirements:\n * - The array lengths are equal.\n *\n * Emits the `HighTierThresholdsUpdated` event.\n *\n */\n function _setHighTierThresholds(address[] calldata _tokens, uint256[] calldata _thresholds) internal virtual {\n require(_tokens.length == _thresholds.length, \"WithdrawalLimitation: invalid array length\");\n for (uint256 _i; _i < _tokens.length; _i++) {\n highTierThreshold[_tokens[_i]] = _thresholds[_i];\n }\n emit HighTierThresholdsUpdated(_tokens, _thresholds);\n }\n\n /**\n * @dev Sets the amount thresholds to lock withdrawal.\n *\n * Requirements:\n * - The array lengths are equal.\n *\n * Emits the `LockedThresholdsUpdated` event.\n *\n */\n function _setLockedThresholds(address[] calldata _tokens, uint256[] calldata _thresholds) internal virtual {\n require(_tokens.length == _thresholds.length, \"WithdrawalLimitation: invalid array length\");\n for (uint256 _i; _i < _tokens.length; _i++) {\n lockedThreshold[_tokens[_i]] = _thresholds[_i];\n }\n emit LockedThresholdsUpdated(_tokens, _thresholds);\n }\n\n /**\n * @dev Sets fee percentages to unlock withdrawal.\n *\n * Requirements:\n * - The array lengths are equal.\n * - The percentage is equal to or less than 100_000.\n *\n * Emits the `UnlockFeePercentagesUpdated` event.\n *\n */\n function _setUnlockFeePercentages(address[] calldata _tokens, uint256[] calldata _percentages) internal virtual {\n require(_tokens.length == _percentages.length, \"WithdrawalLimitation: invalid array length\");\n for (uint256 _i; _i < _tokens.length; _i++) {\n require(_percentages[_i] <= _MAX_PERCENTAGE, \"WithdrawalLimitation: invalid percentage\");\n unlockFeePercentages[_tokens[_i]] = _percentages[_i];\n }\n emit UnlockFeePercentagesUpdated(_tokens, _percentages);\n }\n\n /**\n * @dev Sets daily limit amounts for the withdrawals.\n *\n * Requirements:\n * - The array lengths are equal.\n *\n * Emits the `DailyWithdrawalLimitsUpdated` event.\n *\n */\n function _setDailyWithdrawalLimits(address[] calldata _tokens, uint256[] calldata _limits) internal virtual {\n require(_tokens.length == _limits.length, \"WithdrawalLimitation: invalid array length\");\n for (uint256 _i; _i < _tokens.length; _i++) {\n dailyWithdrawalLimit[_tokens[_i]] = _limits[_i];\n }\n emit DailyWithdrawalLimitsUpdated(_tokens, _limits);\n }\n\n /**\n * @dev Checks whether the withdrawal reaches the daily limitation.\n *\n * Requirements:\n * - The daily withdrawal threshold should not apply for locked withdrawals.\n *\n */\n function _reachedWithdrawalLimit(address _token, uint256 _quantity) internal view virtual returns (bool) {\n if (_lockedWithdrawalRequest(_token, _quantity)) {\n return false;\n }\n\n uint256 _currentDate = block.timestamp / 1 days;\n if (_currentDate > lastDateSynced[_token]) {\n return dailyWithdrawalLimit[_token] <= _quantity;\n } else {\n return dailyWithdrawalLimit[_token] <= lastSyncedWithdrawal[_token] + _quantity;\n }\n }\n\n /**\n * @dev Record withdrawal token.\n */\n function _recordWithdrawal(address _token, uint256 _quantity) internal virtual {\n uint256 _currentDate = block.timestamp / 1 days;\n if (_currentDate > lastDateSynced[_token]) {\n lastDateSynced[_token] = _currentDate;\n lastSyncedWithdrawal[_token] = _quantity;\n } else {\n lastSyncedWithdrawal[_token] += _quantity;\n }\n }\n\n /**\n * @dev Returns whether the withdrawal request is locked or not.\n */\n function _lockedWithdrawalRequest(address _token, uint256 _quantity) internal view virtual returns (bool) {\n return lockedThreshold[_token] <= _quantity;\n }\n\n /**\n * @dev Computes fee percentage.\n */\n function _computeFeePercentage(uint256 _amount, uint256 _percentage) internal view virtual returns (uint256) {\n return (_amount * _percentage) / _MAX_PERCENTAGE;\n }\n\n /**\n * @dev Returns high-tier vote weight.\n */\n function _highTierVoteWeight(uint256 _totalWeight) internal view virtual returns (uint256) {\n return (_highTierVWNum * _totalWeight + _highTierVWDenom - 1) / _highTierVWDenom;\n }\n\n /**\n * @dev Validates whether the high-tier vote weight threshold is larger than the normal threshold.\n */\n function _verifyThresholds() internal view {\n require(_num * _highTierVWDenom <= _highTierVWNum * _denom, \"WithdrawalLimitation: invalid thresholds\");\n }\n}\n"},"contracts/v0.8/interfaces/MappedTokenConsumer.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"../library/Token.sol\";\n\ninterface MappedTokenConsumer {\n struct MappedToken {\n Token.Standard erc;\n address tokenAddr;\n }\n}\n"},"contracts/v0.8/library/Token.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC721/IERC721.sol\";\nimport \"@openzeppelin/contracts/utils/Strings.sol\";\nimport \"../interfaces/IWETH.sol\";\n\nlibrary Token {\n enum Standard {\n ERC20,\n ERC721\n }\n struct Info {\n Standard erc;\n // For ERC20: the id must be 0 and the quantity is larger than 0.\n // For ERC721: the quantity must be 0.\n uint256 id;\n uint256 quantity;\n }\n\n // keccak256(\"TokenInfo(uint8 erc,uint256 id,uint256 quantity)\");\n bytes32 public constant INFO_TYPE_HASH = 0x1e2b74b2a792d5c0f0b6e59b037fa9d43d84fbb759337f0112fcc15ca414fc8d;\n\n /**\n * @dev Returns token info struct hash.\n */\n function hash(Info memory _info) internal pure returns (bytes32) {\n return keccak256(abi.encode(INFO_TYPE_HASH, _info.erc, _info.id, _info.quantity));\n }\n\n /**\n * @dev Validates the token info.\n */\n function validate(Info memory _info) internal pure {\n require(\n (_info.erc == Standard.ERC20 && _info.quantity > 0 && _info.id == 0) ||\n (_info.erc == Standard.ERC721 && _info.quantity == 0),\n \"Token: invalid info\"\n );\n }\n\n /**\n * @dev Transfer asset from.\n *\n * Requirements:\n * - The `_from` address must approve for the contract using this library.\n *\n */\n function transferFrom(\n Info memory _info,\n address _from,\n address _to,\n address _token\n ) internal {\n bool _success;\n bytes memory _data;\n if (_info.erc == Standard.ERC20) {\n (_success, _data) = _token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, _from, _to, _info.quantity));\n _success = _success && (_data.length == 0 || abi.decode(_data, (bool)));\n } else if (_info.erc == Standard.ERC721) {\n // bytes4(keccak256(\"transferFrom(address,address,uint256)\"))\n (_success, ) = _token.call(abi.encodeWithSelector(0x23b872dd, _from, _to, _info.id));\n } else {\n revert(\"Token: unsupported token standard\");\n }\n\n if (!_success) {\n revert(\n string(\n abi.encodePacked(\n \"Token: could not transfer \",\n toString(_info),\n \" from \",\n Strings.toHexString(uint160(_from), 20),\n \" to \",\n Strings.toHexString(uint160(_to), 20),\n \" token \",\n Strings.toHexString(uint160(_token), 20)\n )\n )\n );\n }\n }\n\n /**\n * @dev Transfers ERC721 token and returns the result.\n */\n function tryTransferERC721(\n address _token,\n address _to,\n uint256 _id\n ) internal returns (bool _success) {\n (_success, ) = _token.call(abi.encodeWithSelector(IERC721.transferFrom.selector, address(this), _to, _id));\n }\n\n /**\n * @dev Transfers ERC20 token and returns the result.\n */\n function tryTransferERC20(\n address _token,\n address _to,\n uint256 _quantity\n ) internal returns (bool _success) {\n bytes memory _data;\n (_success, _data) = _token.call(abi.encodeWithSelector(IERC20.transfer.selector, _to, _quantity));\n _success = _success && (_data.length == 0 || abi.decode(_data, (bool)));\n }\n\n /**\n * @dev Transfer assets from current address to `_to` address.\n */\n function transfer(\n Info memory _info,\n address _to,\n address _token\n ) internal {\n bool _success;\n if (_info.erc == Standard.ERC20) {\n _success = tryTransferERC20(_token, _to, _info.quantity);\n } else if (_info.erc == Standard.ERC721) {\n _success = tryTransferERC721(_token, _to, _info.id);\n } else {\n revert(\"Token: unsupported token standard\");\n }\n\n if (!_success) {\n revert(\n string(\n abi.encodePacked(\n \"Token: could not transfer \",\n toString(_info),\n \" to \",\n Strings.toHexString(uint160(_to), 20),\n \" token \",\n Strings.toHexString(uint160(_token), 20)\n )\n )\n );\n }\n }\n\n /**\n * @dev Tries minting and transfering assets.\n *\n * @notice Prioritizes transfer native token if the token is wrapped.\n *\n */\n function handleAssetTransfer(\n Info memory _info,\n address payable _to,\n address _token,\n IWETH _wrappedNativeToken\n ) internal {\n bool _success;\n if (_token == address(_wrappedNativeToken)) {\n // Try sending the native token before transferring the wrapped token\n if (!_to.send(_info.quantity)) {\n _wrappedNativeToken.deposit{ value: _info.quantity }();\n transfer(_info, _to, _token);\n }\n } else if (_info.erc == Token.Standard.ERC20) {\n uint256 _balance = IERC20(_token).balanceOf(address(this));\n\n if (_balance < _info.quantity) {\n // bytes4(keccak256(\"mint(address,uint256)\"))\n (_success, ) = _token.call(abi.encodeWithSelector(0x40c10f19, address(this), _info.quantity - _balance));\n require(_success, \"Token: ERC20 minting failed\");\n }\n\n transfer(_info, _to, _token);\n } else if (_info.erc == Token.Standard.ERC721) {\n if (!tryTransferERC721(_token, _to, _info.id)) {\n // bytes4(keccak256(\"mint(address,uint256)\"))\n (_success, ) = _token.call(abi.encodeWithSelector(0x40c10f19, _to, _info.id));\n require(_success, \"Token: ERC721 minting failed\");\n }\n } else {\n revert(\"Token: unsupported token standard\");\n }\n }\n\n /**\n * @dev Returns readable string.\n */\n function toString(Info memory _info) internal pure returns (string memory) {\n return\n string(\n abi.encodePacked(\n \"TokenInfo(\",\n Strings.toHexString(uint160(_info.erc), 1),\n \",\",\n Strings.toHexString(_info.id),\n \",\",\n Strings.toHexString(_info.quantity),\n \")\"\n )\n );\n }\n\n struct Owner {\n address addr;\n address tokenAddr;\n uint256 chainId;\n }\n\n // keccak256(\"TokenOwner(address addr,address tokenAddr,uint256 chainId)\");\n bytes32 public constant OWNER_TYPE_HASH = 0x353bdd8d69b9e3185b3972e08b03845c0c14a21a390215302776a7a34b0e8764;\n\n /**\n * @dev Returns ownership struct hash.\n */\n function hash(Owner memory _owner) internal pure returns (bytes32) {\n return keccak256(abi.encode(OWNER_TYPE_HASH, _owner.addr, _owner.tokenAddr, _owner.chainId));\n }\n}\n"},"contracts/v0.8/mainchain/IMainchainGatewayV2.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"../interfaces/IWETH.sol\";\nimport \"../library/Transfer.sol\";\nimport \"../interfaces/SignatureConsumer.sol\";\nimport \"../interfaces/MappedTokenConsumer.sol\";\n\ninterface IMainchainGatewayV2 is SignatureConsumer, MappedTokenConsumer {\n /// @dev Emitted when the deposit is requested\n event DepositRequested(bytes32 receiptHash, Transfer.Receipt receipt);\n /// @dev Emitted when the assets are withdrawn\n event Withdrew(bytes32 receiptHash, Transfer.Receipt receipt);\n /// @dev Emitted when the tokens are mapped\n event TokenMapped(address[] mainchainTokens, address[] roninTokens, Token.Standard[] standards);\n /// @dev Emitted when the wrapped native token contract is updated\n event WrappedNativeTokenContractUpdated(IWETH weth);\n /// @dev Emitted when the withdrawal is locked\n event WithdrawalLocked(bytes32 receiptHash, Transfer.Receipt receipt);\n /// @dev Emitted when the withdrawal is unlocked\n event WithdrawalUnlocked(bytes32 receiptHash, Transfer.Receipt receipt);\n\n /**\n * @dev Returns the domain seperator.\n */\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n /**\n * @dev Returns deposit count.\n */\n function depositCount() external view returns (uint256);\n\n /**\n * @dev Sets the wrapped native token contract.\n *\n * Requirements:\n * - The method caller is admin.\n *\n * Emits the `WrappedNativeTokenContractUpdated` event.\n *\n */\n function setWrappedNativeTokenContract(IWETH _wrappedToken) external;\n\n /**\n * @dev Returns whether the withdrawal is locked.\n */\n function withdrawalLocked(uint256 withdrawalId) external view returns (bool);\n\n /**\n * @dev Returns the withdrawal hash.\n */\n function withdrawalHash(uint256 withdrawalId) external view returns (bytes32);\n\n /**\n * @dev Locks the assets and request deposit.\n */\n function requestDepositFor(Transfer.Request calldata _request) external payable;\n\n /**\n * @dev Withdraws based on the receipt and the validator signatures.\n * Returns whether the withdrawal is locked.\n *\n * Emits the `Withdrew` once the assets are released.\n *\n */\n function submitWithdrawal(Transfer.Receipt memory _receipt, Signature[] memory _signatures)\n external\n returns (bool _locked);\n\n /**\n * @dev Approves a specific withdrawal.\n *\n * Requirements:\n * - The method caller is a validator.\n *\n * Emits the `Withdrew` once the assets are released.\n *\n */\n function unlockWithdrawal(Transfer.Receipt calldata _receipt) external;\n\n /**\n * @dev Maps mainchain tokens to Ronin network.\n *\n * Requirement:\n * - The method caller is admin.\n * - The arrays have the same length and its length larger than 0.\n *\n * Emits the `TokenMapped` event.\n *\n */\n function mapTokens(\n address[] calldata _mainchainTokens,\n address[] calldata _roninTokens,\n Token.Standard[] calldata _standards\n ) external;\n\n /**\n * @dev Maps mainchain tokens to Ronin network and sets thresholds.\n *\n * Requirement:\n * - The method caller is admin.\n * - The arrays have the same length and its length larger than 0.\n *\n * Emits the `TokenMapped` event.\n *\n */\n function mapTokensAndThresholds(\n address[] calldata _mainchainTokens,\n address[] calldata _roninTokens,\n Token.Standard[] calldata _standards,\n uint256[][4] calldata _thresholds\n ) external;\n\n /**\n * @dev Returns token address on Ronin network.\n * @notice Reverts for unsupported token.\n */\n function getRoninToken(address _mainchainToken) external view returns (MappedToken memory _token);\n}\n"},"contracts/v0.8/mainchain/MainchainGatewayV2.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\nimport \"@openzeppelin/contracts/proxy/utils/Initializable.sol\";\nimport \"../extensions/GatewayV2.sol\";\nimport \"../extensions/WithdrawalLimitation.sol\";\nimport \"../library/Transfer.sol\";\nimport \"./IMainchainGatewayV2.sol\";\n\ncontract MainchainGatewayV2 is WithdrawalLimitation, Initializable, AccessControlEnumerable, IMainchainGatewayV2 {\n using Token for Token.Info;\n using Transfer for Transfer.Request;\n using Transfer for Transfer.Receipt;\n\n /// @dev Withdrawal unlocker role hash\n bytes32 public constant WITHDRAWAL_UNLOCKER_ROLE = keccak256(\"WITHDRAWAL_UNLOCKER_ROLE\");\n\n /// @dev Wrapped native token address\n IWETH public wrappedNativeToken;\n /// @dev Ronin network id\n uint256 public roninChainId;\n /// @dev Total deposit\n uint256 public depositCount;\n /// @dev Domain seperator\n bytes32 internal _domainSeparator;\n /// @dev Mapping from mainchain token => token address on Ronin network\n mapping(address => MappedToken) internal _roninToken;\n /// @dev Mapping from withdrawal id => withdrawal hash\n mapping(uint256 => bytes32) public withdrawalHash;\n /// @dev Mapping from withdrawal id => locked\n mapping(uint256 => bool) public withdrawalLocked;\n\n fallback() external payable {\n _fallback();\n }\n\n receive() external payable {\n _fallback();\n }\n\n /**\n * @dev Initializes contract storage.\n */\n function initialize(\n address _roleSetter,\n IWETH _wrappedToken,\n IWeightedValidator _validatorContract,\n uint256 _roninChainId,\n uint256 _numerator,\n uint256 _highTierVWNumerator,\n uint256 _denominator,\n // _addresses[0]: mainchainTokens\n // _addresses[1]: roninTokens\n // _addresses[2]: withdrawalUnlockers\n address[][3] calldata _addresses,\n // _thresholds[0]: highTierThreshold\n // _thresholds[1]: lockedThreshold\n // _thresholds[2]: unlockFeePercentages\n // _thresholds[3]: dailyWithdrawalLimit\n uint256[][4] calldata _thresholds,\n Token.Standard[] calldata _standards\n ) external payable virtual initializer {\n _setupRole(DEFAULT_ADMIN_ROLE, _roleSetter);\n roninChainId = _roninChainId;\n\n _setWrappedNativeTokenContract(_wrappedToken);\n _setValidatorContract(_validatorContract);\n _updateDomainSeparator();\n _setThreshold(_numerator, _denominator);\n _setHighTierVoteWeightThreshold(_highTierVWNumerator, _denominator);\n _verifyThresholds();\n\n if (_addresses[0].length > 0) {\n // Map mainchain tokens to ronin tokens\n _mapTokens(_addresses[0], _addresses[1], _standards);\n // Sets thresholds based on the mainchain tokens\n _setHighTierThresholds(_addresses[0], _thresholds[0]);\n _setLockedThresholds(_addresses[0], _thresholds[1]);\n _setUnlockFeePercentages(_addresses[0], _thresholds[2]);\n _setDailyWithdrawalLimits(_addresses[0], _thresholds[3]);\n }\n\n // Grant role for withdrawal unlocker\n for (uint256 _i; _i < _addresses[2].length; _i++) {\n _grantRole(WITHDRAWAL_UNLOCKER_ROLE, _addresses[2][_i]);\n }\n }\n\n /**\n * @dev Receives ether without doing anything. Use this function to topup native token.\n */\n function receiveEther() external payable {}\n\n /**\n * @dev See {IMainchainGatewayV2-DOMAIN_SEPARATOR}.\n */\n function DOMAIN_SEPARATOR() external view virtual returns (bytes32) {\n return _domainSeparator;\n }\n\n /**\n * @dev See {IMainchainGatewayV2-setWrappedNativeTokenContract}.\n */\n function setWrappedNativeTokenContract(IWETH _wrappedToken) external virtual onlyAdmin {\n _setWrappedNativeTokenContract(_wrappedToken);\n }\n\n /**\n * @dev See {IMainchainGatewayV2-requestDepositFor}.\n */\n function requestDepositFor(Transfer.Request calldata _request) external payable virtual whenNotPaused {\n _requestDepositFor(_request, msg.sender);\n }\n\n /**\n * @dev See {IMainchainGatewayV2-submitWithdrawal}.\n */\n function submitWithdrawal(Transfer.Receipt calldata _receipt, Signature[] calldata _signatures)\n external\n virtual\n whenNotPaused\n returns (bool _locked)\n {\n return _submitWithdrawal(_receipt, _signatures);\n }\n\n /**\n * @dev See {IMainchainGatewayV2-unlockWithdrawal}.\n */\n function unlockWithdrawal(Transfer.Receipt calldata _receipt) external onlyRole(WITHDRAWAL_UNLOCKER_ROLE) {\n bytes32 _receiptHash = _receipt.hash();\n require(withdrawalHash[_receipt.id] == _receipt.hash(), \"MainchainGatewayV2: invalid receipt\");\n require(withdrawalLocked[_receipt.id], \"MainchainGatewayV2: query for approved withdrawal\");\n delete withdrawalLocked[_receipt.id];\n emit WithdrawalUnlocked(_receiptHash, _receipt);\n\n address _token = _receipt.mainchain.tokenAddr;\n if (_receipt.info.erc == Token.Standard.ERC20) {\n Token.Info memory _feeInfo = _receipt.info;\n _feeInfo.quantity = _computeFeePercentage(_receipt.info.quantity, unlockFeePercentages[_token]);\n Token.Info memory _withdrawInfo = _receipt.info;\n _withdrawInfo.quantity = _receipt.info.quantity - _feeInfo.quantity;\n\n _feeInfo.handleAssetTransfer(payable(msg.sender), _token, wrappedNativeToken);\n _withdrawInfo.handleAssetTransfer(payable(_receipt.mainchain.addr), _token, wrappedNativeToken);\n } else {\n _receipt.info.handleAssetTransfer(payable(_receipt.mainchain.addr), _token, wrappedNativeToken);\n }\n\n emit Withdrew(_receiptHash, _receipt);\n }\n\n /**\n * @dev See {IMainchainGatewayV2-mapTokens}.\n */\n function mapTokens(\n address[] calldata _mainchainTokens,\n address[] calldata _roninTokens,\n Token.Standard[] calldata _standards\n ) external virtual onlyAdmin {\n require(_mainchainTokens.length > 0, \"MainchainGatewayV2: query for empty array\");\n _mapTokens(_mainchainTokens, _roninTokens, _standards);\n }\n\n /**\n * @dev See {IMainchainGatewayV2-mapTokensAndThresholds}.\n */\n function mapTokensAndThresholds(\n address[] calldata _mainchainTokens,\n address[] calldata _roninTokens,\n Token.Standard[] calldata _standards,\n // _thresholds[0]: highTierThreshold\n // _thresholds[1]: lockedThreshold\n // _thresholds[2]: unlockFeePercentages\n // _thresholds[3]: dailyWithdrawalLimit\n uint256[][4] calldata _thresholds\n ) external virtual onlyAdmin {\n require(_mainchainTokens.length > 0, \"MainchainGatewayV2: query for empty array\");\n _mapTokens(_mainchainTokens, _roninTokens, _standards);\n _setHighTierThresholds(_mainchainTokens, _thresholds[0]);\n _setLockedThresholds(_mainchainTokens, _thresholds[1]);\n _setUnlockFeePercentages(_mainchainTokens, _thresholds[2]);\n _setDailyWithdrawalLimits(_mainchainTokens, _thresholds[3]);\n }\n\n /**\n * @dev See {IMainchainGatewayV2-getRoninToken}.\n */\n function getRoninToken(address _mainchainToken) public view returns (MappedToken memory _token) {\n _token = _roninToken[_mainchainToken];\n require(_token.tokenAddr != address(0), \"MainchainGatewayV2: unsupported token\");\n }\n\n /**\n * @dev Maps mainchain tokens to Ronin network.\n *\n * Requirement:\n * - The arrays have the same length.\n *\n * Emits the `TokenMapped` event.\n *\n */\n function _mapTokens(\n address[] calldata _mainchainTokens,\n address[] calldata _roninTokens,\n Token.Standard[] calldata _standards\n ) internal virtual {\n require(\n _mainchainTokens.length == _roninTokens.length && _mainchainTokens.length == _standards.length,\n \"MainchainGatewayV2: invalid array length\"\n );\n\n for (uint256 _i; _i < _mainchainTokens.length; _i++) {\n _roninToken[_mainchainTokens[_i]].tokenAddr = _roninTokens[_i];\n _roninToken[_mainchainTokens[_i]].erc = _standards[_i];\n }\n\n emit TokenMapped(_mainchainTokens, _roninTokens, _standards);\n }\n\n /**\n * @dev Submits withdrawal receipt.\n *\n * Requirements:\n * - The receipt kind is withdrawal.\n * - The receipt is to withdraw on this chain.\n * - The receipt is not used to withdraw before.\n * - The withdrawal is not reached the limit threshold.\n * - The signer weight total is larger than or equal to the minimum threshold.\n * - The signature signers are in order.\n *\n * Emits the `Withdrew` once the assets are released.\n *\n */\n function _submitWithdrawal(Transfer.Receipt calldata _receipt, Signature[] memory _signatures)\n internal\n virtual\n returns (bool _locked)\n {\n uint256 _id = _receipt.id;\n uint256 _quantity = _receipt.info.quantity;\n address _tokenAddr = _receipt.mainchain.tokenAddr;\n\n _receipt.info.validate();\n require(_receipt.kind == Transfer.Kind.Withdrawal, \"MainchainGatewayV2: invalid receipt kind\");\n require(_receipt.mainchain.chainId == block.chainid, \"MainchainGatewayV2: invalid chain id\");\n MappedToken memory _token = getRoninToken(_receipt.mainchain.tokenAddr);\n require(\n _token.erc == _receipt.info.erc && _token.tokenAddr == _receipt.ronin.tokenAddr,\n \"MainchainGatewayV2: invalid receipt\"\n );\n require(withdrawalHash[_id] == bytes32(0), \"MainchainGatewayV2: query for processed withdrawal\");\n require(\n _receipt.info.erc == Token.Standard.ERC721 || !_reachedWithdrawalLimit(_tokenAddr, _quantity),\n \"MainchainGatewayV2: reached daily withdrawal limit\"\n );\n\n bytes32 _receiptHash = _receipt.hash();\n bytes32 _receiptDigest = Transfer.receiptDigest(_domainSeparator, _receiptHash);\n IWeightedValidator _validatorContract = validatorContract;\n\n uint256 _minimumVoteWeight;\n (_minimumVoteWeight, _locked) = _computeMinVoteWeight(_receipt.info.erc, _tokenAddr, _quantity, _validatorContract);\n\n {\n bool _passed;\n address _signer;\n address _lastSigner;\n Signature memory _sig;\n uint256 _weight;\n for (uint256 _i; _i < _signatures.length; _i++) {\n _sig = _signatures[_i];\n _signer = ecrecover(_receiptDigest, _sig.v, _sig.r, _sig.s);\n require(_lastSigner < _signer, \"MainchainGatewayV2: invalid order\");\n _lastSigner = _signer;\n\n _weight += _validatorContract.getValidatorWeight(_signer);\n if (_weight >= _minimumVoteWeight) {\n _passed = true;\n break;\n }\n }\n require(_passed, \"MainchainGatewayV2: query for insufficient vote weight\");\n withdrawalHash[_id] = _receiptHash;\n }\n\n if (_locked) {\n withdrawalLocked[_id] = true;\n emit WithdrawalLocked(_receiptHash, _receipt);\n return _locked;\n }\n\n _recordWithdrawal(_tokenAddr, _quantity);\n _receipt.info.handleAssetTransfer(payable(_receipt.mainchain.addr), _tokenAddr, wrappedNativeToken);\n emit Withdrew(_receiptHash, _receipt);\n }\n\n /**\n * @dev Requests deposit made by `_requester` address.\n *\n * Requirements:\n * - The token info is valid.\n * - The `msg.value` is 0 while depositing ERC20 token.\n * - The `msg.value` is equal to deposit quantity while depositing native token.\n *\n * Emits the `DepositRequested` event.\n *\n */\n function _requestDepositFor(Transfer.Request memory _request, address _requester) internal virtual {\n MappedToken memory _token;\n address _weth = address(wrappedNativeToken);\n\n _request.info.validate();\n if (_request.tokenAddr == address(0)) {\n require(_request.info.quantity == msg.value, \"MainchainGatewayV2: invalid request\");\n _token = getRoninToken(_weth);\n require(_token.erc == _request.info.erc, \"MainchainGatewayV2: invalid token standard\");\n _request.tokenAddr = _weth;\n } else {\n require(msg.value == 0, \"MainchainGatewayV2: invalid request\");\n _token = getRoninToken(_request.tokenAddr);\n require(_token.erc == _request.info.erc, \"MainchainGatewayV2: invalid token standard\");\n _request.info.transferFrom(_requester, address(this), _request.tokenAddr);\n // Withdraw if token is WETH\n if (_weth == _request.tokenAddr) {\n IWETH(_weth).withdraw(_request.info.quantity);\n }\n }\n\n uint256 _depositId = depositCount++;\n Transfer.Receipt memory _receipt = _request.into_deposit_receipt(\n _requester,\n _depositId,\n _token.tokenAddr,\n roninChainId\n );\n\n emit DepositRequested(_receipt.hash(), _receipt);\n }\n\n /**\n * @dev Returns the minimum vote weight for the token.\n */\n function _computeMinVoteWeight(\n Token.Standard _erc,\n address _token,\n uint256 _quantity,\n IWeightedValidator _validatorContract\n ) internal virtual returns (uint256 _weight, bool _locked) {\n uint256 _totalWeights = _validatorContract.totalWeights();\n _weight = _minimumVoteWeight(_totalWeights);\n if (_erc == Token.Standard.ERC20) {\n if (highTierThreshold[_token] <= _quantity) {\n _weight = _highTierVoteWeight(_totalWeights);\n }\n _locked = _lockedWithdrawalRequest(_token, _quantity);\n }\n }\n\n /**\n * @dev Update domain seperator.\n */\n function _updateDomainSeparator() internal {\n _domainSeparator = keccak256(\n abi.encode(\n keccak256(\"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\"),\n keccak256(\"MainchainGatewayV2\"),\n keccak256(\"2\"),\n block.chainid,\n address(this)\n )\n );\n }\n\n /**\n * @dev Sets the WETH contract.\n *\n * Emits the `WrappedNativeTokenContractUpdated` event.\n *\n */\n function _setWrappedNativeTokenContract(IWETH _wrapedToken) internal {\n wrappedNativeToken = _wrapedToken;\n emit WrappedNativeTokenContractUpdated(_wrapedToken);\n }\n\n /**\n * @dev Receives ETH from WETH or creates deposit request.\n */\n function _fallback() internal virtual whenNotPaused {\n if (msg.sender != address(wrappedNativeToken)) {\n Transfer.Request memory _request;\n _request.recipientAddr = msg.sender;\n _request.info.quantity = msg.value;\n _requestDepositFor(_request, _request.recipientAddr);\n }\n }\n}\n"},"contracts/v0.8/interfaces/IWeightedValidator.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"./IQuorum.sol\";\n\ninterface IWeightedValidator is IQuorum {\n struct WeightedValidator {\n address validator;\n address governor;\n uint256 weight;\n }\n\n /// @dev Emitted when the validators are added\n event ValidatorsAdded(uint256 indexed nonce, WeightedValidator[] validators);\n /// @dev Emitted when the validators are updated\n event ValidatorsUpdated(uint256 indexed nonce, WeightedValidator[] validators);\n /// @dev Emitted when the validators are removed\n event ValidatorsRemoved(uint256 indexed nonce, address[] validators);\n\n /**\n * @dev Returns validator weight of the validator.\n */\n function getValidatorWeight(address _addr) external view returns (uint256);\n\n /**\n * @dev Returns governor weight of the governor.\n */\n function getGovernorWeight(address _addr) external view returns (uint256);\n\n /**\n * @dev Returns total validator weights of the address list.\n */\n function sumValidatorWeights(address[] calldata _addrList) external view returns (uint256 _weight);\n\n /**\n * @dev Returns total governor weights of the address list.\n */\n function sumGovernorWeights(address[] calldata _addrList) external view returns (uint256 _weight);\n\n /**\n * @dev Returns the validator list attached with governor address and weight.\n */\n function getValidatorInfo() external view returns (WeightedValidator[] memory _list);\n\n /**\n * @dev Returns the validator list.\n */\n function getValidators() external view returns (address[] memory _validators);\n\n /**\n * @dev Returns the validator at `_index` position.\n */\n function validators(uint256 _index) external view returns (WeightedValidator memory);\n\n /**\n * @dev Returns total of validators.\n */\n function totalValidators() external view returns (uint256);\n\n /**\n * @dev Returns total weights.\n */\n function totalWeights() external view returns (uint256);\n\n /**\n * @dev Adds validators.\n *\n * Requirements:\n * - The weights are larger than 0.\n * - The validators are not added.\n * - The method caller is admin.\n *\n * Emits the `ValidatorsAdded` event.\n *\n */\n function addValidators(WeightedValidator[] calldata _validators) external;\n\n /**\n * @dev Updates validators.\n *\n * Requirements:\n * - The weights are larger than 0.\n * - The validators are added.\n * - The method caller is admin.\n *\n * Emits the `ValidatorsUpdated` event.\n *\n */\n function updateValidators(WeightedValidator[] calldata _validators) external;\n\n /**\n * @dev Removes validators.\n *\n * Requirements:\n * - The validators are added.\n * - The method caller is admin.\n *\n * Emits the `ValidatorsRemoved` event.\n *\n */\n function removeValidators(address[] calldata _validators) external;\n}\n"},"contracts/v0.8/extensions/HasProxyAdmin.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/utils/StorageSlot.sol\";\n\nabstract contract HasProxyAdmin {\n // bytes32(uint256(keccak256(\"eip1967.proxy.admin\")) - 1));\n bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;\n\n modifier onlyAdmin() {\n require(msg.sender == _getAdmin(), \"HasProxyAdmin: unauthorized sender\");\n _;\n }\n\n /**\n * @dev Returns proxy admin.\n */\n function _getAdmin() internal view returns (address) {\n return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;\n }\n}\n"},"@openzeppelin/contracts/utils/introspection/IERC165.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n /**\n * @dev Returns true if this contract implements the interface defined by\n * `interfaceId`. See the corresponding\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n * to learn more about how these ids are created.\n *\n * This function call must use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n"},"contracts/v0.8/interfaces/SignatureConsumer.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface SignatureConsumer {\n struct Signature {\n uint8 v;\n bytes32 r;\n bytes32 s;\n }\n}\n"},"@openzeppelin/contracts/utils/Strings.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n bytes16 private constant _HEX_SYMBOLS = \"0123456789abcdef\";\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n */\n function toString(uint256 value) internal pure returns (string memory) {\n // Inspired by OraclizeAPI's implementation - MIT licence\n // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol\n\n if (value == 0) {\n return \"0\";\n }\n uint256 temp = value;\n uint256 digits;\n while (temp != 0) {\n digits++;\n temp /= 10;\n }\n bytes memory buffer = new bytes(digits);\n while (value != 0) {\n digits -= 1;\n buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));\n value /= 10;\n }\n return string(buffer);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n */\n function toHexString(uint256 value) internal pure returns (string memory) {\n if (value == 0) {\n return \"0x00\";\n }\n uint256 temp = value;\n uint256 length = 0;\n while (temp != 0) {\n length++;\n temp >>= 8;\n }\n return toHexString(value, length);\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n */\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n bytes memory buffer = new bytes(2 * length + 2);\n buffer[0] = \"0\";\n buffer[1] = \"x\";\n for (uint256 i = 2 * length + 1; i > 1; --i) {\n buffer[i] = _HEX_SYMBOLS[value & 0xf];\n value >>= 4;\n }\n require(value == 0, \"Strings: hex length insufficient\");\n return string(buffer);\n }\n}\n"},"@openzeppelin/contracts/utils/introspection/ERC165.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC165.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n *\n * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.\n */\nabstract contract ERC165 is IERC165 {\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IERC165).interfaceId;\n }\n}\n"},"@openzeppelin/contracts/access/AccessControlEnumerable.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControlEnumerable.sol\";\nimport \"./AccessControl.sol\";\nimport \"../utils/structs/EnumerableSet.sol\";\n\n/**\n * @dev Extension of {AccessControl} that allows enumerating the members of each role.\n */\nabstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {\n using EnumerableSet for EnumerableSet.AddressSet;\n\n mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers;\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns one of the accounts that have `role`. `index` must be a\n * value between 0 and {getRoleMemberCount}, non-inclusive.\n *\n * Role bearers are not sorted in any particular way, and their ordering may\n * change at any point.\n *\n * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure\n * you perform all queries on the same block. See the following\n * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]\n * for more information.\n */\n function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) {\n return _roleMembers[role].at(index);\n }\n\n /**\n * @dev Returns the number of accounts that have `role`. Can be used\n * together with {getRoleMember} to enumerate all bearers of a role.\n */\n function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) {\n return _roleMembers[role].length();\n }\n\n /**\n * @dev Overload {_grantRole} to track enumerable memberships\n */\n function _grantRole(bytes32 role, address account) internal virtual override {\n super._grantRole(role, account);\n _roleMembers[role].add(account);\n }\n\n /**\n * @dev Overload {_revokeRole} to track enumerable memberships\n */\n function _revokeRole(bytes32 role, address account) internal virtual override {\n super._revokeRole(role, account);\n _roleMembers[role].remove(account);\n }\n}\n"},"contracts/v0.8/library/Transfer.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/utils/cryptography/ECDSA.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/utils/Strings.sol\";\nimport \"./Token.sol\";\n\nlibrary Transfer {\n using ECDSA for bytes32;\n\n enum Kind {\n Deposit,\n Withdrawal\n }\n\n struct Request {\n // For deposit request: Recipient address on Ronin network\n // For withdrawal request: Recipient address on mainchain network\n address recipientAddr;\n // Token address to deposit/withdraw\n // Value 0: native token\n address tokenAddr;\n Token.Info info;\n }\n\n /**\n * @dev Converts the transfer request into the deposit receipt.\n */\n function into_deposit_receipt(\n Request memory _request,\n address _requester,\n uint256 _id,\n address _roninTokenAddr,\n uint256 _roninChainId\n ) internal view returns (Receipt memory _receipt) {\n _receipt.id = _id;\n _receipt.kind = Kind.Deposit;\n _receipt.mainchain.addr = _requester;\n _receipt.mainchain.tokenAddr = _request.tokenAddr;\n _receipt.mainchain.chainId = block.chainid;\n _receipt.ronin.addr = _request.recipientAddr;\n _receipt.ronin.tokenAddr = _roninTokenAddr;\n _receipt.ronin.chainId = _roninChainId;\n _receipt.info = _request.info;\n }\n\n /**\n * @dev Converts the transfer request into the withdrawal receipt.\n */\n function into_withdrawal_receipt(\n Request memory _request,\n address _requester,\n uint256 _id,\n address _mainchainTokenAddr,\n uint256 _mainchainId\n ) internal view returns (Receipt memory _receipt) {\n _receipt.id = _id;\n _receipt.kind = Kind.Withdrawal;\n _receipt.ronin.addr = _requester;\n _receipt.ronin.tokenAddr = _request.tokenAddr;\n _receipt.ronin.chainId = block.chainid;\n _receipt.mainchain.addr = _request.recipientAddr;\n _receipt.mainchain.tokenAddr = _mainchainTokenAddr;\n _receipt.mainchain.chainId = _mainchainId;\n _receipt.info = _request.info;\n }\n\n struct Receipt {\n uint256 id;\n Kind kind;\n Token.Owner mainchain;\n Token.Owner ronin;\n Token.Info info;\n }\n\n // keccak256(\"Receipt(uint256 id,uint8 kind,TokenOwner mainchain,TokenOwner ronin,TokenInfo info)TokenInfo(uint8 erc,uint256 id,uint256 quantity)TokenOwner(address addr,address tokenAddr,uint256 chainId)\");\n bytes32 public constant TYPE_HASH = 0xb9d1fe7c9deeec5dc90a2f47ff1684239519f2545b2228d3d91fb27df3189eea;\n\n /**\n * @dev Returns token info struct hash.\n */\n function hash(Receipt memory _receipt) internal pure returns (bytes32) {\n return\n keccak256(\n abi.encode(\n TYPE_HASH,\n _receipt.id,\n _receipt.kind,\n Token.hash(_receipt.mainchain),\n Token.hash(_receipt.ronin),\n Token.hash(_receipt.info)\n )\n );\n }\n\n /**\n * @dev Returns the receipt digest.\n */\n function receiptDigest(bytes32 _domainSeparator, bytes32 _receiptHash) internal pure returns (bytes32) {\n return _domainSeparator.toTypedDataHash(_receiptHash);\n }\n}\n"},"@openzeppelin/contracts/utils/Context.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n"},"@openzeppelin/contracts/security/Pausable.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract Pausable is Context {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n constructor() {\n _paused = false;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n require(!paused(), \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n require(paused(), \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n"},"@openzeppelin/contracts/utils/structs/EnumerableSet.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 => uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastvalue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastvalue;\n // Update the index for the moved value\n set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n return _values(set._inner);\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n assembly {\n result := store\n }\n\n return result;\n }\n}\n"},"contracts/v0.8/interfaces/IQuorum.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IQuorum {\n /// @dev Emitted when the threshold is updated\n event ThresholdUpdated(\n uint256 indexed nonce,\n uint256 indexed numerator,\n uint256 indexed denominator,\n uint256 previousNumerator,\n uint256 previousDenominator\n );\n\n /**\n * @dev Returns the threshold.\n */\n function getThreshold() external view returns (uint256 _num, uint256 _denom);\n\n /**\n * @dev Checks whether the `_voteWeight` passes the threshold.\n */\n function checkThreshold(uint256 _voteWeight) external view returns (bool);\n\n /**\n * @dev Returns the minimum vote weight to pass the threshold.\n */\n function minimumVoteWeight() external view returns (uint256);\n\n /**\n * @dev Sets the threshold.\n *\n * Requirements:\n * - The method caller is admin.\n *\n * Emits the `ThresholdUpdated` event.\n *\n */\n function setThreshold(uint256 _numerator, uint256 _denominator)\n external\n returns (uint256 _previousNum, uint256 _previousDenom);\n}\n"},"contracts/v0.8/interfaces/IWETH.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IWETH {\n function deposit() external payable;\n\n function withdraw(uint256 _wad) external;\n\n function balanceOf(address) external view returns (uint256);\n}\n"},"@openzeppelin/contracts/access/AccessControl.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControl.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\nimport \"../utils/Context.sol\";\nimport \"../utils/Strings.sol\";\nimport \"../utils/introspection/ERC165.sol\";\n\n/**\n * @dev Contract module that allows children to implement role-based access\n * control mechanisms. This is a lightweight version that doesn't allow enumerating role\n * members except through off-chain means by accessing the contract event logs. Some\n * applications may benefit from on-chain enumerability, for those cases see\n * {AccessControlEnumerable}.\n *\n * Roles are referred to by their `bytes32` identifier. These should be exposed\n * in the external API and be unique. The best way to achieve this is by\n * using `public constant` hash digests:\n *\n * ```\n * bytes32 public constant MY_ROLE = keccak256(\"MY_ROLE\");\n * ```\n *\n * Roles can be used to represent a set of permissions. To restrict access to a\n * function call, use {hasRole}:\n *\n * ```\n * function foo() public {\n * require(hasRole(MY_ROLE, msg.sender));\n * ...\n * }\n * ```\n *\n * Roles can be granted and revoked dynamically via the {grantRole} and\n * {revokeRole} functions. Each role has an associated admin role, and only\n * accounts that have a role's admin role can call {grantRole} and {revokeRole}.\n *\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\n * that only accounts with this role will be able to grant or revoke other\n * roles. More complex role relationships can be created by using\n * {_setRoleAdmin}.\n *\n * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to\n * grant and revoke this role. Extra precautions should be taken to secure\n * accounts that have been granted it.\n */\nabstract contract AccessControl is Context, IAccessControl, ERC165 {\n struct RoleData {\n mapping(address => bool) members;\n bytes32 adminRole;\n }\n\n mapping(bytes32 => RoleData) private _roles;\n\n bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;\n\n /**\n * @dev Modifier that checks that an account has a specific role. Reverts\n * with a standardized message including the required role.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n *\n * _Available since v4.1._\n */\n modifier onlyRole(bytes32 role) {\n _checkRole(role, _msgSender());\n _;\n }\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) public view virtual override returns (bool) {\n return _roles[role].members[account];\n }\n\n /**\n * @dev Revert with a standard message if `account` is missing `role`.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n */\n function _checkRole(bytes32 role, address account) internal view virtual {\n if (!hasRole(role, account)) {\n revert(\n string(\n abi.encodePacked(\n \"AccessControl: account \",\n Strings.toHexString(uint160(account), 20),\n \" is missing role \",\n Strings.toHexString(uint256(role), 32)\n )\n )\n );\n }\n }\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {\n return _roles[role].adminRole;\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _grantRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _revokeRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been revoked `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) public virtual override {\n require(account == _msgSender(), \"AccessControl: can only renounce roles for self\");\n\n _revokeRole(role, account);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event. Note that unlike {grantRole}, this function doesn't perform any\n * checks on the calling account.\n *\n * [WARNING]\n * ====\n * This function should only be called from the constructor when setting\n * up the initial roles for the system.\n *\n * Using this function in any other way is effectively circumventing the admin\n * system imposed by {AccessControl}.\n * ====\n *\n * NOTE: This function is deprecated in favor of {_grantRole}.\n */\n function _setupRole(bytes32 role, address account) internal virtual {\n _grantRole(role, account);\n }\n\n /**\n * @dev Sets `adminRole` as ``role``'s admin role.\n *\n * Emits a {RoleAdminChanged} event.\n */\n function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\n bytes32 previousAdminRole = getRoleAdmin(role);\n _roles[role].adminRole = adminRole;\n emit RoleAdminChanged(role, previousAdminRole, adminRole);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * Internal function without access restriction.\n */\n function _grantRole(bytes32 role, address account) internal virtual {\n if (!hasRole(role, account)) {\n _roles[role].members[account] = true;\n emit RoleGranted(role, account, _msgSender());\n }\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * Internal function without access restriction.\n */\n function _revokeRole(bytes32 role, address account) internal virtual {\n if (hasRole(role, account)) {\n _roles[role].members[account] = false;\n emit RoleRevoked(role, account, _msgSender());\n }\n }\n}\n"},"@openzeppelin/contracts/utils/Address.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n"},"@openzeppelin/contracts/token/ERC20/IERC20.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n"}},"settings":{"evmVersion":"london","libraries":{},"metadata":{"bytecodeHash":"ipfs","useLiteralContent":true},"optimizer":{"enabled":true,"runs":1000},"remappings":[],"outputSelection":{"*":{"*":["evm.bytecode","evm.deployedBytecode","devdoc","userdoc","metadata","abi"]}}}},"ABI":"[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"tokens\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"limits\",\"type\":\"uint256[]\"}],\"name\":\"DailyWithdrawalLimitsUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"receiptHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"enum Transfer.Kind\",\"name\":\"kind\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"mainchain\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"ronin\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"enum Token.Standard\",\"name\":\"erc\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Info\",\"name\":\"info\",\"type\":\"tuple\"}],\"indexed\":false,\"internalType\":\"struct Transfer.Receipt\",\"name\":\"receipt\",\"type\":\"tuple\"}],\"name\":\"DepositRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"tokens\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"thresholds\",\"type\":\"uint256[]\"}],\"name\":\"HighTierThresholdsUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"numerator\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"denominator\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"previousNumerator\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"previousDenominator\",\"type\":\"uint256\"}],\"name\":\"HighTierVoteWeightThresholdUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"tokens\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"thresholds\",\"type\":\"uint256[]\"}],\"name\":\"LockedThresholdsUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"previousAdminRole\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"newAdminRole\",\"type\":\"bytes32\"}],\"name\":\"RoleAdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleGranted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleRevoked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"numerator\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"denominator\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"previousNumerator\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"previousDenominator\",\"type\":\"uint256\"}],\"name\":\"ThresholdUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"mainchainTokens\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"roninTokens\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"enum Token.Standard[]\",\"name\":\"standards\",\"type\":\"uint8[]\"}],\"name\":\"TokenMapped\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"tokens\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"percentages\",\"type\":\"uint256[]\"}],\"name\":\"UnlockFeePercentagesUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IWeightedValidator\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"ValidatorContractUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"receiptHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"enum Transfer.Kind\",\"name\":\"kind\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"mainchain\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"ronin\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"enum Token.Standard\",\"name\":\"erc\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Info\",\"name\":\"info\",\"type\":\"tuple\"}],\"indexed\":false,\"internalType\":\"struct Transfer.Receipt\",\"name\":\"receipt\",\"type\":\"tuple\"}],\"name\":\"WithdrawalLocked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"receiptHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"enum Transfer.Kind\",\"name\":\"kind\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"mainchain\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"ronin\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"enum Token.Standard\",\"name\":\"erc\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Info\",\"name\":\"info\",\"type\":\"tuple\"}],\"indexed\":false,\"internalType\":\"struct Transfer.Receipt\",\"name\":\"receipt\",\"type\":\"tuple\"}],\"name\":\"WithdrawalUnlocked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"receiptHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"enum Transfer.Kind\",\"name\":\"kind\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"mainchain\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"ronin\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"enum Token.Standard\",\"name\":\"erc\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Info\",\"name\":\"info\",\"type\":\"tuple\"}],\"indexed\":false,\"internalType\":\"struct Transfer.Receipt\",\"name\":\"receipt\",\"type\":\"tuple\"}],\"name\":\"Withdrew\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IWETH\",\"name\":\"weth\",\"type\":\"address\"}],\"name\":\"WrappedNativeTokenContractUpdated\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"DEFAULT_ADMIN_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"DOMAIN_SEPARATOR\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"WITHDRAWAL_UNLOCKER_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"_MAX_PERCENTAGE\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_voteWeight\",\"type\":\"uint256\"}],\"name\":\"checkHighTierVoteWeightThreshold\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_voteWeight\",\"type\":\"uint256\"}],\"name\":\"checkThreshold\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"dailyWithdrawalLimit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"depositCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getHighTierVoteWeightThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"}],\"name\":\"getRoleAdmin\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"getRoleMember\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"}],\"name\":\"getRoleMemberCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_mainchainToken\",\"type\":\"address\"}],\"name\":\"getRoninToken\",\"outputs\":[{\"components\":[{\"internalType\":\"enum Token.Standard\",\"name\":\"erc\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"}],\"internalType\":\"struct MappedTokenConsumer.MappedToken\",\"name\":\"_token\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"grantRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"hasRole\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"highTierThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_roleSetter\",\"type\":\"address\"},{\"internalType\":\"contract IWETH\",\"name\":\"_wrappedToken\",\"type\":\"address\"},{\"internalType\":\"contract IWeightedValidator\",\"name\":\"_validatorContract\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_roninChainId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_numerator\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_highTierVWNumerator\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_denominator\",\"type\":\"uint256\"},{\"internalType\":\"address[][3]\",\"name\":\"_addresses\",\"type\":\"address[][3]\"},{\"internalType\":\"uint256[][4]\",\"name\":\"_thresholds\",\"type\":\"uint256[][4]\"},{\"internalType\":\"enum Token.Standard[]\",\"name\":\"_standards\",\"type\":\"uint8[]\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"lastDateSynced\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"lastSyncedWithdrawal\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"lockedThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_mainchainTokens\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"_roninTokens\",\"type\":\"address[]\"},{\"internalType\":\"enum Token.Standard[]\",\"name\":\"_standards\",\"type\":\"uint8[]\"}],\"name\":\"mapTokens\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_mainchainTokens\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"_roninTokens\",\"type\":\"address[]\"},{\"internalType\":\"enum Token.Standard[]\",\"name\":\"_standards\",\"type\":\"uint8[]\"},{\"internalType\":\"uint256[][4]\",\"name\":\"_thresholds\",\"type\":\"uint256[][4]\"}],\"name\":\"mapTokensAndThresholds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"minimumVoteWeight\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"nonce\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_quantity\",\"type\":\"uint256\"}],\"name\":\"reachedWithdrawalLimit\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"receiveEther\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"renounceRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"recipientAddr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"enum Token.Standard\",\"name\":\"erc\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Info\",\"name\":\"info\",\"type\":\"tuple\"}],\"internalType\":\"struct Transfer.Request\",\"name\":\"_request\",\"type\":\"tuple\"}],\"name\":\"requestDepositFor\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"revokeRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"roninChainId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_tokens\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"_limits\",\"type\":\"uint256[]\"}],\"name\":\"setDailyWithdrawalLimits\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_tokens\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"_thresholds\",\"type\":\"uint256[]\"}],\"name\":\"setHighTierThresholds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_numerator\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_denominator\",\"type\":\"uint256\"}],\"name\":\"setHighTierVoteWeightThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"_previousNum\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_previousDenom\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_tokens\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"_thresholds\",\"type\":\"uint256[]\"}],\"name\":\"setLockedThresholds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_numerator\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_denominator\",\"type\":\"uint256\"}],\"name\":\"setThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"_previousNum\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_previousDenom\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_tokens\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"_percentages\",\"type\":\"uint256[]\"}],\"name\":\"setUnlockFeePercentages\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IWeightedValidator\",\"name\":\"_validatorContract\",\"type\":\"address\"}],\"name\":\"setValidatorContract\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IWETH\",\"name\":\"_wrappedToken\",\"type\":\"address\"}],\"name\":\"setWrappedNativeTokenContract\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"enum Transfer.Kind\",\"name\":\"kind\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"mainchain\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"ronin\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"enum Token.Standard\",\"name\":\"erc\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Info\",\"name\":\"info\",\"type\":\"tuple\"}],\"internalType\":\"struct Transfer.Receipt\",\"name\":\"_receipt\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"internalType\":\"struct SignatureConsumer.Signature[]\",\"name\":\"_signatures\",\"type\":\"tuple[]\"}],\"name\":\"submitWithdrawal\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"_locked\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"unlockFeePercentages\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"enum Transfer.Kind\",\"name\":\"kind\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"mainchain\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Owner\",\"name\":\"ronin\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"enum Token.Standard\",\"name\":\"erc\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"}],\"internalType\":\"struct Token.Info\",\"name\":\"info\",\"type\":\"tuple\"}],\"internalType\":\"struct Transfer.Receipt\",\"name\":\"_receipt\",\"type\":\"tuple\"}],\"name\":\"unlockWithdrawal\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"validatorContract\",\"outputs\":[{\"internalType\":\"contract IWeightedValidator\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"withdrawalHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"withdrawalLocked\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"wrappedNativeToken\",\"outputs\":[{\"internalType\":\"contract IWETH\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]","ContractName":"MainchainGatewayV2","CompilerVersion":"v0.8.9+commit.e5eed63a","OptimizationUsed":1,"Runs":1000,"ConstructorArguments":"0x","EVMVersion":"Default","Library":"","LicenseType":"MIT","Proxy":0,"SwarmSource":""}] \ No newline at end of file diff --git a/testdata/etherscan/0x8B3D32cf2bb4d0D16656f4c0b04Fa546274f1545/creation_data.json b/testdata/etherscan/0x8B3D32cf2bb4d0D16656f4c0b04Fa546274f1545/creation_data.json index fa26d9cad..f4dfea9e7 100644 --- a/testdata/etherscan/0x8B3D32cf2bb4d0D16656f4c0b04Fa546274f1545/creation_data.json +++ b/testdata/etherscan/0x8B3D32cf2bb4d0D16656f4c0b04Fa546274f1545/creation_data.json @@ -1,5 +1 @@ -{ - "contractAddress": "0x8b3d32cf2bb4d0d16656f4c0b04fa546274f1545", - "contractCreator": "0x958892b4a0512b28aaac890fc938868bbd42f064", - "txHash": "0x79820495643caf5a1e7e96578361c9ddba0e0735cd684ada7450254f6fd58f51" -} \ No newline at end of file +{"contractAddress":"0x8b3d32cf2bb4d0d16656f4c0b04fa546274f1545","contractCreator":"0x958892b4a0512b28aaac890fc938868bbd42f064","txHash":"0x79820495643caf5a1e7e96578361c9ddba0e0735cd684ada7450254f6fd58f51"} \ No newline at end of file diff --git a/testdata/etherscan/0x8B3D32cf2bb4d0D16656f4c0b04Fa546274f1545/metadata.json b/testdata/etherscan/0x8B3D32cf2bb4d0D16656f4c0b04Fa546274f1545/metadata.json index 5921b5b30..1fd95fa4f 100644 --- a/testdata/etherscan/0x8B3D32cf2bb4d0D16656f4c0b04Fa546274f1545/metadata.json +++ b/testdata/etherscan/0x8B3D32cf2bb4d0D16656f4c0b04Fa546274f1545/metadata.json @@ -1,63 +1 @@ -[ - { - "SourceCode": { - "language": "Solidity", - "sources": { - "contracts/governance/governor/GovernorStorage.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.9;\n\nimport \"./IIpt.sol\";\nimport \"./Structs.sol\";\n\ncontract GovernorCharlieDelegatorStorage {\n /// @notice Active brains of Governor\n address public implementation;\n}\n\n/**\n * @title Storage for Governor Charlie Delegate\n * @notice For future upgrades, do not change GovernorCharlieDelegateStorage. Create a new\n * contract which implements GovernorCharlieDelegateStorage and following the naming convention\n * GovernorCharlieDelegateStorageVX.\n */\n//solhint-disable-next-line max-states-count\ncontract GovernorCharlieDelegateStorage is GovernorCharlieDelegatorStorage {\n /// @notice The number of votes in support of a proposal required in order for a quorum to be reached and for a vote to succeed\n uint256 public quorumVotes;\n\n /// @notice The number of votes in support of a proposal required in order for an emergency quorum to be reached and for a vote to succeed\n uint256 public emergencyQuorumVotes;\n\n /// @notice The delay before voting on a proposal may take place, once proposed, in blocks\n uint256 public votingDelay;\n\n /// @notice The duration of voting on a proposal, in blocks\n uint256 public votingPeriod;\n\n /// @notice The number of votes required in order for a voter to become a proposer\n uint256 public proposalThreshold;\n\n /// @notice Initial proposal id set at become\n uint256 public initialProposalId;\n\n /// @notice The total number of proposals\n uint256 public proposalCount;\n\n /// @notice The address of the Interest Protocol governance token\n IIpt public ipt;\n\n /// @notice The official record of all proposals ever proposed\n mapping(uint256 => Proposal) public proposals;\n\n /// @notice The latest proposal for each proposer\n mapping(address => uint256) public latestProposalIds;\n\n /// @notice The latest proposal for each proposer\n mapping(bytes32 => bool) public queuedTransactions;\n\n /// @notice The proposal holding period\n uint256 public proposalTimelockDelay;\n\n /// @notice Stores the expiration of account whitelist status as a timestamp\n mapping(address => uint256) public whitelistAccountExpirations;\n\n /// @notice Address which manages whitelisted proposals and whitelist accounts\n address public whitelistGuardian;\n\n /// @notice The duration of the voting on a emergency proposal, in blocks\n uint256 public emergencyVotingPeriod;\n\n /// @notice The emergency proposal holding period\n uint256 public emergencyTimelockDelay;\n\n /// all receipts for proposal\n mapping(uint256 => mapping(address => Receipt)) public proposalReceipts;\n\n /// @notice The emergency proposal holding period\n bool public initialized;\n\n /// @notice The number of votes to reject an optimistic proposal\n uint256 public optimisticQuorumVotes; \n\n /// @notice The delay period before voting begins\n uint256 public optimisticVotingDelay; \n\n /// @notice The maximum number of seconds an address can be whitelisted for\n uint256 public maxWhitelistPeriod; \n}\n" - }, - "hardhat/console.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity >= 0.4.22 <0.9.0;\n\nlibrary console {\n\taddress constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67);\n\n\tfunction _sendLogPayload(bytes memory payload) private view {\n\t\tuint256 payloadLength = payload.length;\n\t\taddress consoleAddress = CONSOLE_ADDRESS;\n\t\tassembly {\n\t\t\tlet payloadStart := add(payload, 32)\n\t\t\tlet r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0)\n\t\t}\n\t}\n\n\tfunction log() internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log()\"));\n\t}\n\n\tfunction logInt(int p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(int)\", p0));\n\t}\n\n\tfunction logUint(uint p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint)\", p0));\n\t}\n\n\tfunction logString(string memory p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n\t}\n\n\tfunction logBool(bool p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n\t}\n\n\tfunction logAddress(address p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n\t}\n\n\tfunction logBytes(bytes memory p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes)\", p0));\n\t}\n\n\tfunction logBytes1(bytes1 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes1)\", p0));\n\t}\n\n\tfunction logBytes2(bytes2 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes2)\", p0));\n\t}\n\n\tfunction logBytes3(bytes3 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes3)\", p0));\n\t}\n\n\tfunction logBytes4(bytes4 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes4)\", p0));\n\t}\n\n\tfunction logBytes5(bytes5 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes5)\", p0));\n\t}\n\n\tfunction logBytes6(bytes6 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes6)\", p0));\n\t}\n\n\tfunction logBytes7(bytes7 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes7)\", p0));\n\t}\n\n\tfunction logBytes8(bytes8 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes8)\", p0));\n\t}\n\n\tfunction logBytes9(bytes9 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes9)\", p0));\n\t}\n\n\tfunction logBytes10(bytes10 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes10)\", p0));\n\t}\n\n\tfunction logBytes11(bytes11 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes11)\", p0));\n\t}\n\n\tfunction logBytes12(bytes12 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes12)\", p0));\n\t}\n\n\tfunction logBytes13(bytes13 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes13)\", p0));\n\t}\n\n\tfunction logBytes14(bytes14 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes14)\", p0));\n\t}\n\n\tfunction logBytes15(bytes15 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes15)\", p0));\n\t}\n\n\tfunction logBytes16(bytes16 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes16)\", p0));\n\t}\n\n\tfunction logBytes17(bytes17 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes17)\", p0));\n\t}\n\n\tfunction logBytes18(bytes18 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes18)\", p0));\n\t}\n\n\tfunction logBytes19(bytes19 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes19)\", p0));\n\t}\n\n\tfunction logBytes20(bytes20 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes20)\", p0));\n\t}\n\n\tfunction logBytes21(bytes21 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes21)\", p0));\n\t}\n\n\tfunction logBytes22(bytes22 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes22)\", p0));\n\t}\n\n\tfunction logBytes23(bytes23 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes23)\", p0));\n\t}\n\n\tfunction logBytes24(bytes24 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes24)\", p0));\n\t}\n\n\tfunction logBytes25(bytes25 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes25)\", p0));\n\t}\n\n\tfunction logBytes26(bytes26 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes26)\", p0));\n\t}\n\n\tfunction logBytes27(bytes27 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes27)\", p0));\n\t}\n\n\tfunction logBytes28(bytes28 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes28)\", p0));\n\t}\n\n\tfunction logBytes29(bytes29 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes29)\", p0));\n\t}\n\n\tfunction logBytes30(bytes30 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes30)\", p0));\n\t}\n\n\tfunction logBytes31(bytes31 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes31)\", p0));\n\t}\n\n\tfunction logBytes32(bytes32 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes32)\", p0));\n\t}\n\n\tfunction log(uint p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint)\", p0));\n\t}\n\n\tfunction log(string memory p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n\t}\n\n\tfunction log(bool p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n\t}\n\n\tfunction log(address p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n\t}\n\n\tfunction log(uint p0, uint p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint)\", p0, p1));\n\t}\n\n\tfunction log(uint p0, string memory p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string)\", p0, p1));\n\t}\n\n\tfunction log(uint p0, bool p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool)\", p0, p1));\n\t}\n\n\tfunction log(uint p0, address p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address)\", p0, p1));\n\t}\n\n\tfunction log(string memory p0, uint p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint)\", p0, p1));\n\t}\n\n\tfunction log(string memory p0, string memory p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string)\", p0, p1));\n\t}\n\n\tfunction log(string memory p0, bool p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool)\", p0, p1));\n\t}\n\n\tfunction log(string memory p0, address p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address)\", p0, p1));\n\t}\n\n\tfunction log(bool p0, uint p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint)\", p0, p1));\n\t}\n\n\tfunction log(bool p0, string memory p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string)\", p0, p1));\n\t}\n\n\tfunction log(bool p0, bool p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool)\", p0, p1));\n\t}\n\n\tfunction log(bool p0, address p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address)\", p0, p1));\n\t}\n\n\tfunction log(address p0, uint p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint)\", p0, p1));\n\t}\n\n\tfunction log(address p0, string memory p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string)\", p0, p1));\n\t}\n\n\tfunction log(address p0, bool p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool)\", p0, p1));\n\t}\n\n\tfunction log(address p0, address p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address)\", p0, p1));\n\t}\n\n\tfunction log(uint p0, uint p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, uint p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, uint p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, uint p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, string memory p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, string memory p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, string memory p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, string memory p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, bool p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, bool p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, bool p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, bool p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, address p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, address p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, address p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, address p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, uint p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, uint p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, uint p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, uint p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, string memory p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, string memory p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, string memory p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, string memory p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, bool p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, bool p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, bool p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, bool p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, address p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, address p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, address p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, address p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, uint p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, uint p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, uint p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, uint p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, string memory p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, string memory p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, string memory p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, string memory p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, bool p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, bool p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, bool p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, bool p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, address p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, address p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, address p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, address p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, uint p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, uint p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, uint p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, uint p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, string memory p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, string memory p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, string memory p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, string memory p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, bool p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, bool p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, bool p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, bool p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, address p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, address p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, address p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, address p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, uint p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,address)\", p0, p1, p2, p3));\n\t}\n\n}\n" - }, - "contracts/governance/governor/IGovernor.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.9;\n\nimport \"./Structs.sol\";\n\n/// @title interface to interact with TokenDelgator\ninterface IGovernorCharlieDelegator {\n function _setImplementation(address implementation_) external;\n\n fallback() external payable;\n\n receive() external payable;\n}\n\n/// @title interface to interact with TokenDelgate\ninterface IGovernorCharlieDelegate {\n function initialize(\n address ipt_\n ) external;\n\n function propose(\n address[] memory targets,\n uint256[] memory values,\n string[] memory signatures,\n bytes[] memory calldatas,\n string memory description,\n bool emergency\n ) external returns (uint256);\n\n function queue(uint256 proposalId) external;\n\n function execute(uint256 proposalId) external payable;\n\n function executeTransaction(\n address target,\n uint256 value,\n string memory signature,\n bytes memory data,\n uint256 eta\n ) external payable;\n\n function cancel(uint256 proposalId) external;\n\n function getActions(uint256 proposalId)\n external\n view\n returns (\n address[] memory targets,\n uint256[] memory values,\n string[] memory signatures,\n bytes[] memory calldatas\n );\n\n function getReceipt(uint256 proposalId, address voter) external view returns (Receipt memory);\n\n function state(uint256 proposalId) external view returns (ProposalState);\n\n function castVote(uint256 proposalId, uint8 support) external;\n\n function castVoteWithReason(\n uint256 proposalId,\n uint8 support,\n string calldata reason\n ) external;\n\n function castVoteBySig(\n uint256 proposalId,\n uint8 support,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n function isWhitelisted(address account) external view returns (bool);\n\n function _setDelay(uint256 proposalTimelockDelay_) external;\n\n function _setEmergencyDelay(uint256 emergencyTimelockDelay_) external;\n\n function _setVotingDelay(uint256 newVotingDelay) external;\n\n function _setVotingPeriod(uint256 newVotingPeriod) external;\n\n function _setEmergencyVotingPeriod(uint256 newEmergencyVotingPeriod) external;\n\n function _setProposalThreshold(uint256 newProposalThreshold) external;\n\n function _setQuorumVotes(uint256 newQuorumVotes) external;\n\n function _setEmergencyQuorumVotes(uint256 newEmergencyQuorumVotes) external;\n\n function _setWhitelistAccountExpiration(address account, uint256 expiration) external;\n\n function _setWhitelistGuardian(address account) external;\n\n function _setOptimisticDelay(uint256 newOptimisticVotingDelay) external;\n\n function _setOptimisticQuorumVotes(uint256 newOptimisticQuorumVotes) external;\n}\n\n/// @title interface which contains all events emitted by delegator & delegate\ninterface GovernorCharlieEvents {\n /// @notice An event emitted when a new proposal is created\n event ProposalCreated(\n uint256 indexed id,\n address indexed proposer,\n address[] targets,\n uint256[] values,\n string[] signatures,\n bytes[] calldatas,\n uint256 indexed startBlock,\n uint256 endBlock,\n string description\n );\n\n /// @notice An event emitted when a vote has been cast on a proposal\n /// @param voter The address which casted a vote\n /// @param proposalId The proposal id which was voted on\n /// @param support Support value for the vote. 0=against, 1=for, 2=abstain\n /// @param votes Number of votes which were cast by the voter\n /// @param reason The reason given for the vote by the voter\n event VoteCast(address indexed voter, uint256 indexed proposalId, uint8 support, uint256 votes, string reason);\n\n /// @notice An event emitted when a proposal has been canceled\n event ProposalCanceled(uint256 indexed id);\n\n /// @notice An event emitted when a proposal has been queued in the Timelock\n event ProposalQueued(uint256 indexed id, uint256 eta);\n\n /// @notice An event emitted when a proposal has been executed in the Timelock\n event ProposalExecuted(uint256 indexed id);\n\n /// @notice An event emitted when the voting delay is set\n event VotingDelaySet(uint256 oldVotingDelay, uint256 newVotingDelay);\n\n /// @notice An event emitted when the voting period is set\n event VotingPeriodSet(uint256 oldVotingPeriod, uint256 newVotingPeriod);\n\n /// @notice An event emitted when the emergency voting period is set\n event EmergencyVotingPeriodSet(uint256 oldEmergencyVotingPeriod, uint256 emergencyVotingPeriod);\n\n /// @notice Emitted when implementation is changed\n event NewImplementation(address oldImplementation, address newImplementation);\n\n /// @notice Emitted when proposal threshold is set\n event ProposalThresholdSet(uint256 oldProposalThreshold, uint256 newProposalThreshold);\n\n /// @notice Emitted when pendingAdmin is changed\n event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin);\n\n /// @notice Emitted when pendingAdmin is accepted, which means admin is updated\n event NewAdmin(address oldAdmin, address newAdmin);\n\n /// @notice Emitted when whitelist account expiration is set\n event WhitelistAccountExpirationSet(address account, uint256 expiration);\n\n /// @notice Emitted when the whitelistGuardian is set\n event WhitelistGuardianSet(address oldGuardian, address newGuardian);\n\n /// @notice Emitted when the a new delay is set\n event NewDelay(uint256 oldTimelockDelay, uint256 proposalTimelockDelay);\n\n /// @notice Emitted when the a new emergency delay is set\n event NewEmergencyDelay(uint256 oldEmergencyTimelockDelay, uint256 emergencyTimelockDelay);\n\n /// @notice Emitted when the quorum is updated\n event NewQuorum(uint256 oldQuorumVotes, uint256 quorumVotes);\n\n /// @notice Emitted when the emergency quorum is updated\n event NewEmergencyQuorum(uint256 oldEmergencyQuorumVotes, uint256 emergencyQuorumVotes);\n\n /// @notice An event emitted when the optimistic voting delay is set\n event OptimisticVotingDelaySet(uint256 oldOptimisticVotingDelay, uint256 optimisticVotingDelay);\n\n /// @notice Emitted when the optimistic quorum is updated\n event OptimisticQuorumVotesSet(uint256 oldOptimisticQuorumVotes, uint256 optimisticQuorumVotes);\n\n /// @notice Emitted when a transaction is canceled\n event CancelTransaction(\n bytes32 indexed txHash,\n address indexed target,\n uint256 value,\n string signature,\n bytes data,\n uint256 eta\n );\n\n /// @notice Emitted when a transaction is executed\n event ExecuteTransaction(\n bytes32 indexed txHash,\n address indexed target,\n uint256 value,\n string signature,\n bytes data,\n uint256 eta\n );\n\n /// @notice Emitted when a transaction is queued\n event QueueTransaction(\n bytes32 indexed txHash,\n address indexed target,\n uint256 value,\n string signature,\n bytes data,\n uint256 eta\n );\n}\n" - }, - "contracts/governance/governor/Structs.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.9;\n\nstruct Proposal {\n /// @notice Unique id for looking up a proposal\n uint256 id;\n /// @notice Creator of the proposal\n address proposer;\n /// @notice The timestamp that the proposal will be available for execution, set once the vote succeeds\n uint256 eta;\n /// @notice the ordered list of target addresses for calls to be made\n address[] targets;\n /// @notice The ordered list of values (i.e. msg.value) to be passed to the calls to be made\n uint256[] values;\n /// @notice The ordered list of function signatures to be called\n string[] signatures;\n /// @notice The ordered list of calldata to be passed to each call\n bytes[] calldatas;\n /// @notice The block at which voting begins: holders must delegate their votes prior to this block\n uint256 startBlock;\n /// @notice The block at which voting ends: votes must be cast prior to this block\n uint256 endBlock;\n /// @notice Current number of votes in favor of this proposal\n uint256 forVotes;\n /// @notice Current number of votes in opposition to this proposal\n uint256 againstVotes;\n /// @notice Current number of votes for abstaining for this proposal\n uint256 abstainVotes;\n /// @notice Flag marking whether the proposal has been canceled\n bool canceled;\n /// @notice Flag marking whether the proposal has been executed\n bool executed;\n /// @notice Whether the proposal is an emergency proposal\n bool emergency;\n /// @notice quorum votes requires\n uint256 quorumVotes;\n /// @notice time delay\n uint256 delay;\n}\n\n/// @notice Ballot receipt record for a voter\nstruct Receipt {\n /// @notice Whether or not a vote has been cast\n bool hasVoted;\n /// @notice Whether or not the voter supports the proposal or abstains\n uint8 support;\n /// @notice The number of votes the voter had, which were cast\n uint96 votes;\n}\n\n/// @notice Possible states that a proposal may be in\nenum ProposalState {\n Pending,\n Active,\n Canceled,\n Defeated,\n Succeeded,\n Queued,\n Expired,\n Executed\n}\n" - }, - "contracts/governance/governor/IIpt.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.9;\n\ninterface IIpt {\n function getPriorVotes(address account, uint256 blockNumber) external view returns (uint96);\n}\n" - }, - "contracts/governance/governor/GovernorDelegate.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.9;\npragma experimental ABIEncoderV2;\nimport \"hardhat/console.sol\";\n\nimport \"./IGovernor.sol\";\nimport \"./GovernorStorage.sol\";\n\ncontract GovernorCharlieDelegate is GovernorCharlieDelegateStorage, GovernorCharlieEvents, IGovernorCharlieDelegate {\n /// @notice The name of this contract\n string public constant name = \"Interest Protocol Governor\";\n\n /// @notice The maximum number of actions that can be included in a proposal\n uint256 public constant proposalMaxOperations = 10;\n\n /// @notice The EIP-712 typehash for the contract's domain\n bytes32 public constant DOMAIN_TYPEHASH =\n keccak256(\"EIP712Domain(string name,uint256 chainId,address verifyingContract)\");\n\n /// @notice The EIP-712 typehash for the ballot struct used by the contract\n bytes32 public constant BALLOT_TYPEHASH = keccak256(\"Ballot(uint256 proposalId,uint8 support)\");\n\n /// @notice The time for a proposal to be executed after passing\n uint256 public constant GRACE_PERIOD = 14 days;\n\n /**\n * @notice Used to initialize the contract during delegator contructor\n * @param ipt_ The address of the IPT token\n */\n function initialize(\n address ipt_\n ) external override {\n require(!initialized, \"already been initialized\");\n ipt = IIpt(ipt_);\n votingPeriod = 40320;\n votingDelay = 13140;\n proposalThreshold = 1000000000000000000000000;\n proposalTimelockDelay = 172800;\n proposalCount = 0;\n quorumVotes = 10000000000000000000000000;\n emergencyQuorumVotes = 40000000000000000000000000;\n emergencyVotingPeriod = 6570;\n emergencyTimelockDelay = 43200;\n optimisticQuorumVotes = 2000000000000000000000000;\n optimisticVotingDelay = 18000;\n maxWhitelistPeriod = 31536000;\n\n initialized = true;\n }\n\n /// @notice any function with this modifier will call the pay_interest() function before\n modifier onlyGov() {\n require(_msgSender() == address(this), \"must come from the gov.\");\n _;\n }\n\n /**\n * @notice Function used to propose a new proposal. Sender must have delegates above the proposal threshold\n * @param targets Target addresses for proposal calls\n * @param values Eth values for proposal calls\n * @param signatures Function signatures for proposal calls\n * @param calldatas Calldatas for proposal calls\n * @param description String description of the proposal\n * @return Proposal id of new proposal\n */\n function propose(\n address[] memory targets,\n uint256[] memory values,\n string[] memory signatures,\n bytes[] memory calldatas,\n string memory description,\n bool emergency\n ) public override returns (uint256) {\n // Reject proposals before initiating as Governor\n require(quorumVotes != 0, \"Charlie not active\");\n // Allow addresses above proposal threshold and whitelisted addresses to propose\n require(\n ipt.getPriorVotes(_msgSender(), (block.number - 1)) >= proposalThreshold || isWhitelisted(_msgSender()),\n \"votes below proposal threshold\"\n );\n require(\n targets.length == values.length && targets.length == signatures.length && targets.length == calldatas.length,\n \"information arity mismatch\"\n );\n require(targets.length != 0, \"must provide actions\");\n require(targets.length <= proposalMaxOperations, \"too many actions\");\n\n uint256 latestProposalId = latestProposalIds[_msgSender()];\n if (latestProposalId != 0) {\n ProposalState proposersLatestProposalState = state(latestProposalId);\n require(proposersLatestProposalState != ProposalState.Active, \"one live proposal per proposer\");\n require(proposersLatestProposalState != ProposalState.Pending, \"one live proposal per proposer\");\n }\n\n proposalCount++;\n Proposal memory newProposal = Proposal({\n id: proposalCount,\n proposer: _msgSender(),\n eta: 0,\n targets: targets,\n values: values,\n signatures: signatures,\n calldatas: calldatas,\n startBlock: block.number + votingDelay,\n endBlock: block.number + votingDelay + votingPeriod,\n forVotes: 0,\n againstVotes: 0,\n abstainVotes: 0,\n canceled: false,\n executed: false,\n emergency: emergency,\n quorumVotes: quorumVotes,\n delay: proposalTimelockDelay\n });\n\n //whitelist can't make emergency\n if (emergency && !isWhitelisted(_msgSender())) {\n newProposal.startBlock = block.number;\n newProposal.endBlock = block.number + emergencyVotingPeriod;\n newProposal.quorumVotes = emergencyQuorumVotes;\n newProposal.delay = emergencyTimelockDelay;\n }\n\n //whitelist can only make optimistic proposals\n if (isWhitelisted(_msgSender())) {\n newProposal.quorumVotes = optimisticQuorumVotes;\n newProposal.startBlock = block.number + optimisticVotingDelay;\n newProposal.endBlock = block.number + optimisticVotingDelay + votingPeriod;\n }\n\n proposals[newProposal.id] = newProposal;\n latestProposalIds[newProposal.proposer] = newProposal.id;\n\n emit ProposalCreated(\n newProposal.id,\n _msgSender(),\n targets,\n values,\n signatures,\n calldatas,\n newProposal.startBlock,\n newProposal.endBlock,\n description\n );\n return newProposal.id;\n }\n\n /**\n * @notice Queues a proposal of state succeeded\n * @param proposalId The id of the proposal to queue\n */\n function queue(uint256 proposalId) external override {\n require(state(proposalId) == ProposalState.Succeeded, \"can only be queued if succeeded\");\n Proposal storage proposal = proposals[proposalId];\n uint256 eta = block.timestamp + proposal.delay;\n for (uint256 i = 0; i < proposal.targets.length; i++) {\n require(\n !queuedTransactions[\n keccak256(\n abi.encode(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], eta)\n )\n ],\n \"proposal already queued\"\n );\n queueTransaction(\n proposal.targets[i],\n proposal.values[i],\n proposal.signatures[i],\n proposal.calldatas[i],\n eta,\n proposal.delay\n );\n }\n proposal.eta = eta;\n emit ProposalQueued(proposalId, eta);\n }\n\n function queueTransaction(\n address target,\n uint256 value,\n string memory signature,\n bytes memory data,\n uint256 eta,\n uint256 delay\n ) internal returns (bytes32) {\n require(eta >= (getBlockTimestamp() + delay), \"must satisfy delay.\");\n\n bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));\n queuedTransactions[txHash] = true;\n\n emit QueueTransaction(txHash, target, value, signature, data, eta);\n return txHash;\n }\n\n /**\n * @notice Executes a queued proposal if eta has passed\n * @param proposalId The id of the proposal to execute\n */\n function execute(uint256 proposalId) external payable override {\n require(state(proposalId) == ProposalState.Queued, \"can only be exec'd if queued\");\n Proposal storage proposal = proposals[proposalId];\n proposal.executed = true;\n for (uint256 i = 0; i < proposal.targets.length; i++) {\n this.executeTransaction{value: proposal.values[i]}(\n proposal.targets[i],\n proposal.values[i],\n proposal.signatures[i],\n proposal.calldatas[i],\n proposal.eta\n );\n }\n emit ProposalExecuted(proposalId);\n }\n\n function executeTransaction(\n address target,\n uint256 value,\n string memory signature,\n bytes memory data,\n uint256 eta\n ) external payable override {\n bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));\n require(queuedTransactions[txHash], \"tx hasn't been queued.\");\n require(getBlockTimestamp() >= eta, \"tx hasn't surpassed timelock.\");\n require(getBlockTimestamp() <= eta + GRACE_PERIOD, \"tx is stale.\");\n\n queuedTransactions[txHash] = false;\n\n bytes memory callData;\n\n if (bytes(signature).length == 0) {\n callData = data;\n } else {\n callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);\n }\n\n // solhint-disable-next-line avoid-low-level-calls\n (\n bool success, /*bytes memory returnData*/\n\n ) = target.call{value: value}(callData);\n require(success, \"tx execution reverted.\");\n\n emit ExecuteTransaction(txHash, target, value, signature, data, eta);\n }\n\n /**\n * @notice Cancels a proposal only if sender is the proposer, or proposer delegates dropped below proposal threshold\n * @param proposalId The id of the proposal to cancel\n */\n function cancel(uint256 proposalId) external override {\n require(state(proposalId) != ProposalState.Executed, \"cant cancel executed proposal\");\n\n Proposal storage proposal = proposals[proposalId];\n\n // Proposer can cancel\n if (_msgSender() != proposal.proposer) {\n // Whitelisted proposers can't be canceled for falling below proposal threshold\n if (isWhitelisted(proposal.proposer)) {\n require(\n (ipt.getPriorVotes(proposal.proposer, (block.number - 1)) < proposalThreshold) &&\n _msgSender() == whitelistGuardian,\n \"cancel: whitelisted proposer\"\n );\n } else {\n require(\n (ipt.getPriorVotes(proposal.proposer, (block.number - 1)) < proposalThreshold),\n \"cancel: proposer above threshold\"\n );\n }\n }\n\n proposal.canceled = true;\n for (uint256 i = 0; i < proposal.targets.length; i++) {\n cancelTransaction(\n proposal.targets[i],\n proposal.values[i],\n proposal.signatures[i],\n proposal.calldatas[i],\n proposal.eta\n );\n }\n\n emit ProposalCanceled(proposalId);\n }\n\n function cancelTransaction(\n address target,\n uint256 value,\n string memory signature,\n bytes memory data,\n uint256 eta\n ) internal {\n bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));\n queuedTransactions[txHash] = false;\n\n emit CancelTransaction(txHash, target, value, signature, data, eta);\n }\n\n /**\n * @notice Gets actions of a proposal\n * @param proposalId the id of the proposal\n * @return targets proposal targets\n * @return values proposal values\n * @return signatures proposal signatures\n * @return calldatas proposal calldatae\n */\n function getActions(uint256 proposalId)\n external\n view\n override\n returns (\n address[] memory targets,\n uint256[] memory values,\n string[] memory signatures,\n bytes[] memory calldatas\n )\n {\n Proposal storage p = proposals[proposalId];\n return (p.targets, p.values, p.signatures, p.calldatas);\n }\n\n /**\n * @notice Gets the receipt for a voter on a given proposal\n * @param proposalId the id of proposal\n * @param voter The address of the voter\n * @return The voting receipt\n */\n function getReceipt(uint256 proposalId, address voter) external view override returns (Receipt memory) {\n return proposalReceipts[proposalId][voter];\n }\n\n /**\n * @notice Gets the state of a proposal\n * @param proposalId The id of the proposal\n * @return Proposal state\n */\n // solhint-disable-next-line code-complexity\n function state(uint256 proposalId) public view override returns (ProposalState) {\n require(proposalCount >= proposalId && proposalId > initialProposalId, \"state: invalid proposal id\");\n Proposal storage proposal = proposals[proposalId];\n bool whitelisted = isWhitelisted(proposal.proposer);\n if (proposal.canceled) {\n return ProposalState.Canceled;\n } else if (block.number <= proposal.startBlock) {\n return ProposalState.Pending;\n } else if (block.number <= proposal.endBlock) {\n return ProposalState.Active;\n } else if (\n (whitelisted && proposal.againstVotes > proposal.quorumVotes) ||\n (!whitelisted && proposal.forVotes <= proposal.againstVotes) ||\n (!whitelisted && proposal.forVotes < proposal.quorumVotes)\n ) {\n return ProposalState.Defeated;\n } else if (proposal.eta == 0) {\n return ProposalState.Succeeded;\n } else if (proposal.executed) {\n return ProposalState.Executed;\n } else if (block.timestamp >= (proposal.eta + GRACE_PERIOD)) {\n return ProposalState.Expired;\n }\n return ProposalState.Queued;\n }\n\n /**\n * @notice Cast a vote for a proposal\n * @param proposalId The id of the proposal to vote on\n * @param support The support value for the vote. 0=against, 1=for, 2=abstain\n */\n function castVote(uint256 proposalId, uint8 support) external override {\n emit VoteCast(_msgSender(), proposalId, support, castVoteInternal(_msgSender(), proposalId, support), \"\");\n }\n\n /**\n * @notice Cast a vote for a proposal with a reason\n * @param proposalId The id of the proposal to vote on\n * @param support The support value for the vote. 0=against, 1=for, 2=abstain\n * @param reason The reason given for the vote by the voter\n */\n function castVoteWithReason(\n uint256 proposalId,\n uint8 support,\n string calldata reason\n ) external override {\n emit VoteCast(_msgSender(), proposalId, support, castVoteInternal(_msgSender(), proposalId, support), reason);\n }\n\n /**\n * @notice Cast a vote for a proposal by signature\n * @dev external override function that accepts EIP-712 signatures for voting on proposals.\n */\n function castVoteBySig(\n uint256 proposalId,\n uint8 support,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external override {\n bytes32 domainSeparator = keccak256(\n abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainIdInternal(), address(this))\n );\n bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));\n bytes32 digest = keccak256(abi.encodePacked(\"\\x19\\x01\", domainSeparator, structHash));\n address signatory = ecrecover(digest, v, r, s);\n require(signatory != address(0), \"castVoteBySig: invalid signature\");\n emit VoteCast(signatory, proposalId, support, castVoteInternal(signatory, proposalId, support), \"\");\n }\n\n /**\n * @notice Internal function that caries out voting logic\n * @param voter The voter that is casting their vote\n * @param proposalId The id of the proposal to vote on\n * @param support The support value for the vote. 0=against, 1=for, 2=abstain\n * @return The number of votes cast\n */\n function castVoteInternal(\n address voter,\n uint256 proposalId,\n uint8 support\n ) internal returns (uint96) {\n require(state(proposalId) == ProposalState.Active, \"voting is closed\");\n require(support <= 2, \"invalid vote type\");\n Proposal storage proposal = proposals[proposalId];\n Receipt storage receipt = proposalReceipts[proposalId][voter];\n require(receipt.hasVoted == false, \"voter already voted\");\n uint96 votes = ipt.getPriorVotes(voter, proposal.startBlock);\n\n if (support == 0) {\n proposal.againstVotes = proposal.againstVotes + votes;\n } else if (support == 1) {\n proposal.forVotes = proposal.forVotes + votes;\n } else if (support == 2) {\n proposal.abstainVotes = proposal.abstainVotes + votes;\n }\n\n receipt.hasVoted = true;\n receipt.support = support;\n receipt.votes = votes;\n\n return votes;\n }\n\n /**\n * @notice View function which returns if an account is whitelisted\n * @param account Account to check white list status of\n * @return If the account is whitelisted\n */\n function isWhitelisted(address account) public view override returns (bool) {\n return (whitelistAccountExpirations[account] > block.timestamp);\n }\n\n /**\n * @notice Governance function for setting the governance token\n * @param token_ new token addr\n */\n function _setNewToken(address token_) external onlyGov {\n ipt = IIpt(token_);\n }\n\n /**\n * @notice Used to update the timelock period\n * @param proposalTimelockDelay_ The proposal holding period\n */\n function _setDelay(uint256 proposalTimelockDelay_) public override onlyGov {\n uint256 oldTimelockDelay = proposalTimelockDelay;\n proposalTimelockDelay = proposalTimelockDelay_;\n\n emit NewDelay(oldTimelockDelay, proposalTimelockDelay);\n }\n\n /**\n * @notice Used to update the emergency timelock period\n * @param emergencyTimelockDelay_ The proposal holding period\n */\n function _setEmergencyDelay(uint256 emergencyTimelockDelay_) public override onlyGov {\n uint256 oldEmergencyTimelockDelay = emergencyTimelockDelay;\n emergencyTimelockDelay = emergencyTimelockDelay_;\n\n emit NewEmergencyDelay(oldEmergencyTimelockDelay, emergencyTimelockDelay);\n }\n\n /**\n * @notice Governance function for setting the voting delay\n * @param newVotingDelay new voting delay, in blocks\n */\n function _setVotingDelay(uint256 newVotingDelay) external override onlyGov {\n uint256 oldVotingDelay = votingDelay;\n votingDelay = newVotingDelay;\n\n emit VotingDelaySet(oldVotingDelay, votingDelay);\n }\n\n /**\n * @notice Governance function for setting the voting period\n * @param newVotingPeriod new voting period, in blocks\n */\n function _setVotingPeriod(uint256 newVotingPeriod) external override onlyGov {\n uint256 oldVotingPeriod = votingPeriod;\n votingPeriod = newVotingPeriod;\n\n emit VotingPeriodSet(oldVotingPeriod, votingPeriod);\n }\n\n /**\n * @notice Governance function for setting the emergency voting period\n * @param newEmergencyVotingPeriod new voting period, in blocks\n */\n function _setEmergencyVotingPeriod(uint256 newEmergencyVotingPeriod) external override onlyGov {\n uint256 oldEmergencyVotingPeriod = emergencyVotingPeriod;\n emergencyVotingPeriod = newEmergencyVotingPeriod;\n\n emit EmergencyVotingPeriodSet(oldEmergencyVotingPeriod, emergencyVotingPeriod);\n }\n\n /**\n * @notice Governance function for setting the proposal threshold\n * @param newProposalThreshold new proposal threshold\n */\n function _setProposalThreshold(uint256 newProposalThreshold) external override onlyGov {\n uint256 oldProposalThreshold = proposalThreshold;\n proposalThreshold = newProposalThreshold;\n\n emit ProposalThresholdSet(oldProposalThreshold, proposalThreshold);\n }\n\n /**\n * @notice Governance function for setting the quorum\n * @param newQuorumVotes new proposal quorum\n */\n function _setQuorumVotes(uint256 newQuorumVotes) external override onlyGov {\n uint256 oldQuorumVotes = quorumVotes;\n quorumVotes = newQuorumVotes;\n\n emit NewQuorum(oldQuorumVotes, quorumVotes);\n }\n\n /**\n * @notice Governance function for setting the emergency quorum\n * @param newEmergencyQuorumVotes new proposal quorum\n */\n function _setEmergencyQuorumVotes(uint256 newEmergencyQuorumVotes) external override onlyGov {\n uint256 oldEmergencyQuorumVotes = emergencyQuorumVotes;\n emergencyQuorumVotes = newEmergencyQuorumVotes;\n\n emit NewEmergencyQuorum(oldEmergencyQuorumVotes, emergencyQuorumVotes);\n }\n\n /**\n * @notice Governance function for setting the whitelist expiration as a timestamp\n * for an account. Whitelist status allows accounts to propose without meeting threshold\n * @param account Account address to set whitelist expiration for\n * @param expiration Expiration for account whitelist status as timestamp (if now < expiration, whitelisted)\n */\n function _setWhitelistAccountExpiration(address account, uint256 expiration) external override onlyGov {\n require (expiration < (maxWhitelistPeriod + block.timestamp), \"expiration exceeds max\");\n whitelistAccountExpirations[account] = expiration;\n\n emit WhitelistAccountExpirationSet(account, expiration);\n }\n\n /**\n * @notice Governance function for setting the whitelistGuardian. WhitelistGuardian can cancel proposals from whitelisted addresses\n * @param account Account to set whitelistGuardian to (0x0 to remove whitelistGuardian)\n */\n function _setWhitelistGuardian(address account) external override onlyGov {\n address oldGuardian = whitelistGuardian;\n whitelistGuardian = account;\n\n emit WhitelistGuardianSet(oldGuardian, whitelistGuardian);\n }\n\n /**\n * @notice Governance function for setting the optimistic voting delay\n * @param newOptimisticVotingDelay new optimistic voting delay, in blocks\n */\n function _setOptimisticDelay(uint256 newOptimisticVotingDelay) external override onlyGov {\n uint256 oldOptimisticVotingDelay = optimisticVotingDelay;\n optimisticVotingDelay = newOptimisticVotingDelay;\n\n emit OptimisticVotingDelaySet(oldOptimisticVotingDelay, optimisticVotingDelay);\n }\n\n /**\n * @notice Governance function for setting the optimistic quorum\n * @param newOptimisticQuorumVotes new optimistic quorum votes, in blocks\n */\n function _setOptimisticQuorumVotes(uint256 newOptimisticQuorumVotes) external override onlyGov {\n uint256 oldOptimisticQuorumVotes = optimisticQuorumVotes;\n optimisticQuorumVotes = newOptimisticQuorumVotes;\n\n emit OptimisticQuorumVotesSet(oldOptimisticQuorumVotes, optimisticQuorumVotes);\n }\n\n function getChainIdInternal() internal view returns (uint256) {\n return block.chainid;\n }\n\n function getBlockTimestamp() internal view returns (uint256) {\n // solium-disable-next-line security/no-block-members\n return block.timestamp;\n }\n\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n}\n" - } - }, - "settings": { - "optimizer": { - "enabled": true, - "runs": 200, - "details": { - "orderLiterals": true, - "deduplicate": true, - "cse": true, - "yul": true - } - }, - "outputSelection": { - "*": { - "*": [ - "evm.bytecode", - "evm.deployedBytecode", - "devdoc", - "userdoc", - "metadata", - "abi" - ] - } - }, - "libraries": {} - } - }, - "ABI": "[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"txHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"signature\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"eta\",\"type\":\"uint256\"}],\"name\":\"CancelTransaction\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldEmergencyVotingPeriod\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"emergencyVotingPeriod\",\"type\":\"uint256\"}],\"name\":\"EmergencyVotingPeriodSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"txHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"signature\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"eta\",\"type\":\"uint256\"}],\"name\":\"ExecuteTransaction\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldAdmin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"NewAdmin\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldTimelockDelay\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"proposalTimelockDelay\",\"type\":\"uint256\"}],\"name\":\"NewDelay\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldEmergencyTimelockDelay\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"emergencyTimelockDelay\",\"type\":\"uint256\"}],\"name\":\"NewEmergencyDelay\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldEmergencyQuorumVotes\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"emergencyQuorumVotes\",\"type\":\"uint256\"}],\"name\":\"NewEmergencyQuorum\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldImplementation\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newImplementation\",\"type\":\"address\"}],\"name\":\"NewImplementation\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldPendingAdmin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newPendingAdmin\",\"type\":\"address\"}],\"name\":\"NewPendingAdmin\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldQuorumVotes\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"quorumVotes\",\"type\":\"uint256\"}],\"name\":\"NewQuorum\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldOptimisticQuorumVotes\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"optimisticQuorumVotes\",\"type\":\"uint256\"}],\"name\":\"OptimisticQuorumVotesSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldOptimisticVotingDelay\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"optimisticVotingDelay\",\"type\":\"uint256\"}],\"name\":\"OptimisticVotingDelaySet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"ProposalCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"proposer\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"},{\"indexed\":false,\"internalType\":\"string[]\",\"name\":\"signatures\",\"type\":\"string[]\"},{\"indexed\":false,\"internalType\":\"bytes[]\",\"name\":\"calldatas\",\"type\":\"bytes[]\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"startBlock\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"endBlock\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"description\",\"type\":\"string\"}],\"name\":\"ProposalCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"ProposalExecuted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"eta\",\"type\":\"uint256\"}],\"name\":\"ProposalQueued\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldProposalThreshold\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newProposalThreshold\",\"type\":\"uint256\"}],\"name\":\"ProposalThresholdSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"txHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"signature\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"eta\",\"type\":\"uint256\"}],\"name\":\"QueueTransaction\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"voter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"support\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"votes\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"reason\",\"type\":\"string\"}],\"name\":\"VoteCast\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldVotingDelay\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newVotingDelay\",\"type\":\"uint256\"}],\"name\":\"VotingDelaySet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldVotingPeriod\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newVotingPeriod\",\"type\":\"uint256\"}],\"name\":\"VotingPeriodSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"expiration\",\"type\":\"uint256\"}],\"name\":\"WhitelistAccountExpirationSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldGuardian\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newGuardian\",\"type\":\"address\"}],\"name\":\"WhitelistGuardianSet\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"BALLOT_TYPEHASH\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"DOMAIN_TYPEHASH\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"GRACE_PERIOD\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalTimelockDelay_\",\"type\":\"uint256\"}],\"name\":\"_setDelay\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"emergencyTimelockDelay_\",\"type\":\"uint256\"}],\"name\":\"_setEmergencyDelay\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newEmergencyQuorumVotes\",\"type\":\"uint256\"}],\"name\":\"_setEmergencyQuorumVotes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newEmergencyVotingPeriod\",\"type\":\"uint256\"}],\"name\":\"_setEmergencyVotingPeriod\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token_\",\"type\":\"address\"}],\"name\":\"_setNewToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newOptimisticVotingDelay\",\"type\":\"uint256\"}],\"name\":\"_setOptimisticDelay\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newOptimisticQuorumVotes\",\"type\":\"uint256\"}],\"name\":\"_setOptimisticQuorumVotes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newProposalThreshold\",\"type\":\"uint256\"}],\"name\":\"_setProposalThreshold\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newQuorumVotes\",\"type\":\"uint256\"}],\"name\":\"_setQuorumVotes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newVotingDelay\",\"type\":\"uint256\"}],\"name\":\"_setVotingDelay\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newVotingPeriod\",\"type\":\"uint256\"}],\"name\":\"_setVotingPeriod\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"expiration\",\"type\":\"uint256\"}],\"name\":\"_setWhitelistAccountExpiration\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"_setWhitelistGuardian\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"}],\"name\":\"cancel\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"support\",\"type\":\"uint8\"}],\"name\":\"castVote\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"support\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"castVoteBySig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"support\",\"type\":\"uint8\"},{\"internalType\":\"string\",\"name\":\"reason\",\"type\":\"string\"}],\"name\":\"castVoteWithReason\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"emergencyQuorumVotes\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"emergencyTimelockDelay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"emergencyVotingPeriod\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"}],\"name\":\"execute\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"signature\",\"type\":\"string\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"eta\",\"type\":\"uint256\"}],\"name\":\"executeTransaction\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"}],\"name\":\"getActions\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"},{\"internalType\":\"string[]\",\"name\":\"signatures\",\"type\":\"string[]\"},{\"internalType\":\"bytes[]\",\"name\":\"calldatas\",\"type\":\"bytes[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"voter\",\"type\":\"address\"}],\"name\":\"getReceipt\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"hasVoted\",\"type\":\"bool\"},{\"internalType\":\"uint8\",\"name\":\"support\",\"type\":\"uint8\"},{\"internalType\":\"uint96\",\"name\":\"votes\",\"type\":\"uint96\"}],\"internalType\":\"struct Receipt\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"implementation\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"initialProposalId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"ipt_\",\"type\":\"address\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"initialized\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"ipt\",\"outputs\":[{\"internalType\":\"contract IIpt\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"isWhitelisted\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"latestProposalIds\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"maxWhitelistPeriod\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"optimisticQuorumVotes\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"optimisticVotingDelay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"proposalCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"proposalMaxOperations\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"proposalReceipts\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"hasVoted\",\"type\":\"bool\"},{\"internalType\":\"uint8\",\"name\":\"support\",\"type\":\"uint8\"},{\"internalType\":\"uint96\",\"name\":\"votes\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"proposalThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"proposalTimelockDelay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"proposals\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"proposer\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"eta\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"startBlock\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"endBlock\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"forVotes\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"againstVotes\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"abstainVotes\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"canceled\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"executed\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"emergency\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"quorumVotes\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"delay\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"},{\"internalType\":\"string[]\",\"name\":\"signatures\",\"type\":\"string[]\"},{\"internalType\":\"bytes[]\",\"name\":\"calldatas\",\"type\":\"bytes[]\"},{\"internalType\":\"string\",\"name\":\"description\",\"type\":\"string\"},{\"internalType\":\"bool\",\"name\":\"emergency\",\"type\":\"bool\"}],\"name\":\"propose\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"}],\"name\":\"queue\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"queuedTransactions\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"quorumVotes\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"}],\"name\":\"state\",\"outputs\":[{\"internalType\":\"enum ProposalState\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"votingDelay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"votingPeriod\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"whitelistAccountExpirations\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"whitelistGuardian\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", - "ContractName": "GovernorCharlieDelegate", - "CompilerVersion": "v0.8.9+commit.e5eed63a", - "OptimizationUsed": 1, - "Runs": 200, - "ConstructorArguments": "0x", - "EVMVersion": "Default", - "Library": "", - "LicenseType": "", - "Proxy": 0, - "SwarmSource": "" - } -] \ No newline at end of file +[{"SourceCode":{"language":"Solidity","sources":{"contracts/governance/governor/GovernorStorage.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.9;\n\nimport \"./IIpt.sol\";\nimport \"./Structs.sol\";\n\ncontract GovernorCharlieDelegatorStorage {\n /// @notice Active brains of Governor\n address public implementation;\n}\n\n/**\n * @title Storage for Governor Charlie Delegate\n * @notice For future upgrades, do not change GovernorCharlieDelegateStorage. Create a new\n * contract which implements GovernorCharlieDelegateStorage and following the naming convention\n * GovernorCharlieDelegateStorageVX.\n */\n//solhint-disable-next-line max-states-count\ncontract GovernorCharlieDelegateStorage is GovernorCharlieDelegatorStorage {\n /// @notice The number of votes in support of a proposal required in order for a quorum to be reached and for a vote to succeed\n uint256 public quorumVotes;\n\n /// @notice The number of votes in support of a proposal required in order for an emergency quorum to be reached and for a vote to succeed\n uint256 public emergencyQuorumVotes;\n\n /// @notice The delay before voting on a proposal may take place, once proposed, in blocks\n uint256 public votingDelay;\n\n /// @notice The duration of voting on a proposal, in blocks\n uint256 public votingPeriod;\n\n /// @notice The number of votes required in order for a voter to become a proposer\n uint256 public proposalThreshold;\n\n /// @notice Initial proposal id set at become\n uint256 public initialProposalId;\n\n /// @notice The total number of proposals\n uint256 public proposalCount;\n\n /// @notice The address of the Interest Protocol governance token\n IIpt public ipt;\n\n /// @notice The official record of all proposals ever proposed\n mapping(uint256 => Proposal) public proposals;\n\n /// @notice The latest proposal for each proposer\n mapping(address => uint256) public latestProposalIds;\n\n /// @notice The latest proposal for each proposer\n mapping(bytes32 => bool) public queuedTransactions;\n\n /// @notice The proposal holding period\n uint256 public proposalTimelockDelay;\n\n /// @notice Stores the expiration of account whitelist status as a timestamp\n mapping(address => uint256) public whitelistAccountExpirations;\n\n /// @notice Address which manages whitelisted proposals and whitelist accounts\n address public whitelistGuardian;\n\n /// @notice The duration of the voting on a emergency proposal, in blocks\n uint256 public emergencyVotingPeriod;\n\n /// @notice The emergency proposal holding period\n uint256 public emergencyTimelockDelay;\n\n /// all receipts for proposal\n mapping(uint256 => mapping(address => Receipt)) public proposalReceipts;\n\n /// @notice The emergency proposal holding period\n bool public initialized;\n\n /// @notice The number of votes to reject an optimistic proposal\n uint256 public optimisticQuorumVotes; \n\n /// @notice The delay period before voting begins\n uint256 public optimisticVotingDelay; \n\n /// @notice The maximum number of seconds an address can be whitelisted for\n uint256 public maxWhitelistPeriod; \n}\n"},"hardhat/console.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity >= 0.4.22 <0.9.0;\n\nlibrary console {\n\taddress constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67);\n\n\tfunction _sendLogPayload(bytes memory payload) private view {\n\t\tuint256 payloadLength = payload.length;\n\t\taddress consoleAddress = CONSOLE_ADDRESS;\n\t\tassembly {\n\t\t\tlet payloadStart := add(payload, 32)\n\t\t\tlet r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0)\n\t\t}\n\t}\n\n\tfunction log() internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log()\"));\n\t}\n\n\tfunction logInt(int p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(int)\", p0));\n\t}\n\n\tfunction logUint(uint p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint)\", p0));\n\t}\n\n\tfunction logString(string memory p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n\t}\n\n\tfunction logBool(bool p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n\t}\n\n\tfunction logAddress(address p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n\t}\n\n\tfunction logBytes(bytes memory p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes)\", p0));\n\t}\n\n\tfunction logBytes1(bytes1 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes1)\", p0));\n\t}\n\n\tfunction logBytes2(bytes2 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes2)\", p0));\n\t}\n\n\tfunction logBytes3(bytes3 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes3)\", p0));\n\t}\n\n\tfunction logBytes4(bytes4 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes4)\", p0));\n\t}\n\n\tfunction logBytes5(bytes5 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes5)\", p0));\n\t}\n\n\tfunction logBytes6(bytes6 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes6)\", p0));\n\t}\n\n\tfunction logBytes7(bytes7 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes7)\", p0));\n\t}\n\n\tfunction logBytes8(bytes8 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes8)\", p0));\n\t}\n\n\tfunction logBytes9(bytes9 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes9)\", p0));\n\t}\n\n\tfunction logBytes10(bytes10 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes10)\", p0));\n\t}\n\n\tfunction logBytes11(bytes11 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes11)\", p0));\n\t}\n\n\tfunction logBytes12(bytes12 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes12)\", p0));\n\t}\n\n\tfunction logBytes13(bytes13 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes13)\", p0));\n\t}\n\n\tfunction logBytes14(bytes14 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes14)\", p0));\n\t}\n\n\tfunction logBytes15(bytes15 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes15)\", p0));\n\t}\n\n\tfunction logBytes16(bytes16 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes16)\", p0));\n\t}\n\n\tfunction logBytes17(bytes17 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes17)\", p0));\n\t}\n\n\tfunction logBytes18(bytes18 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes18)\", p0));\n\t}\n\n\tfunction logBytes19(bytes19 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes19)\", p0));\n\t}\n\n\tfunction logBytes20(bytes20 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes20)\", p0));\n\t}\n\n\tfunction logBytes21(bytes21 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes21)\", p0));\n\t}\n\n\tfunction logBytes22(bytes22 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes22)\", p0));\n\t}\n\n\tfunction logBytes23(bytes23 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes23)\", p0));\n\t}\n\n\tfunction logBytes24(bytes24 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes24)\", p0));\n\t}\n\n\tfunction logBytes25(bytes25 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes25)\", p0));\n\t}\n\n\tfunction logBytes26(bytes26 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes26)\", p0));\n\t}\n\n\tfunction logBytes27(bytes27 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes27)\", p0));\n\t}\n\n\tfunction logBytes28(bytes28 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes28)\", p0));\n\t}\n\n\tfunction logBytes29(bytes29 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes29)\", p0));\n\t}\n\n\tfunction logBytes30(bytes30 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes30)\", p0));\n\t}\n\n\tfunction logBytes31(bytes31 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes31)\", p0));\n\t}\n\n\tfunction logBytes32(bytes32 p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bytes32)\", p0));\n\t}\n\n\tfunction log(uint p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint)\", p0));\n\t}\n\n\tfunction log(string memory p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n\t}\n\n\tfunction log(bool p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n\t}\n\n\tfunction log(address p0) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n\t}\n\n\tfunction log(uint p0, uint p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint)\", p0, p1));\n\t}\n\n\tfunction log(uint p0, string memory p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string)\", p0, p1));\n\t}\n\n\tfunction log(uint p0, bool p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool)\", p0, p1));\n\t}\n\n\tfunction log(uint p0, address p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address)\", p0, p1));\n\t}\n\n\tfunction log(string memory p0, uint p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint)\", p0, p1));\n\t}\n\n\tfunction log(string memory p0, string memory p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string)\", p0, p1));\n\t}\n\n\tfunction log(string memory p0, bool p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool)\", p0, p1));\n\t}\n\n\tfunction log(string memory p0, address p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address)\", p0, p1));\n\t}\n\n\tfunction log(bool p0, uint p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint)\", p0, p1));\n\t}\n\n\tfunction log(bool p0, string memory p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string)\", p0, p1));\n\t}\n\n\tfunction log(bool p0, bool p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool)\", p0, p1));\n\t}\n\n\tfunction log(bool p0, address p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address)\", p0, p1));\n\t}\n\n\tfunction log(address p0, uint p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint)\", p0, p1));\n\t}\n\n\tfunction log(address p0, string memory p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string)\", p0, p1));\n\t}\n\n\tfunction log(address p0, bool p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool)\", p0, p1));\n\t}\n\n\tfunction log(address p0, address p1) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address)\", p0, p1));\n\t}\n\n\tfunction log(uint p0, uint p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, uint p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, uint p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, uint p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, string memory p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, string memory p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, string memory p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, string memory p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, bool p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, bool p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, bool p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, bool p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, address p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, address p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, address p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, address p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, uint p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, uint p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, uint p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, uint p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, string memory p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, string memory p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, string memory p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, string memory p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, bool p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, bool p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, bool p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, bool p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, address p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, address p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, address p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(string memory p0, address p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, uint p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, uint p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, uint p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, uint p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, string memory p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, string memory p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, string memory p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, string memory p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, bool p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, bool p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, bool p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, bool p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, address p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, address p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, address p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(bool p0, address p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, uint p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, uint p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, uint p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, uint p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, string memory p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, string memory p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, string memory p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, string memory p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, bool p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, bool p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, bool p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, bool p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, address p1, uint p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, address p1, string memory p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,string)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, address p1, bool p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool)\", p0, p1, p2));\n\t}\n\n\tfunction log(address p0, address p1, address p2) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,address)\", p0, p1, p2));\n\t}\n\n\tfunction log(uint p0, uint p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, uint p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,uint,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, string memory p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,string,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, bool p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,bool,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(uint p0, address p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(uint,address,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, uint p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,uint,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, string memory p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, bool p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(string memory p0, address p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, uint p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,uint,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, string memory p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, bool p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(bool p0, address p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, uint p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,uint,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, string memory p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, bool p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, uint p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, uint p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, uint p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, uint p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, string memory p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, string memory p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, string memory p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, string memory p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, bool p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, bool p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, bool p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, bool p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,address)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, address p2, uint p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,uint)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, address p2, string memory p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,string)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, address p2, bool p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,bool)\", p0, p1, p2, p3));\n\t}\n\n\tfunction log(address p0, address p1, address p2, address p3) internal view {\n\t\t_sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,address)\", p0, p1, p2, p3));\n\t}\n\n}\n"},"contracts/governance/governor/IGovernor.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.9;\n\nimport \"./Structs.sol\";\n\n/// @title interface to interact with TokenDelgator\ninterface IGovernorCharlieDelegator {\n function _setImplementation(address implementation_) external;\n\n fallback() external payable;\n\n receive() external payable;\n}\n\n/// @title interface to interact with TokenDelgate\ninterface IGovernorCharlieDelegate {\n function initialize(\n address ipt_\n ) external;\n\n function propose(\n address[] memory targets,\n uint256[] memory values,\n string[] memory signatures,\n bytes[] memory calldatas,\n string memory description,\n bool emergency\n ) external returns (uint256);\n\n function queue(uint256 proposalId) external;\n\n function execute(uint256 proposalId) external payable;\n\n function executeTransaction(\n address target,\n uint256 value,\n string memory signature,\n bytes memory data,\n uint256 eta\n ) external payable;\n\n function cancel(uint256 proposalId) external;\n\n function getActions(uint256 proposalId)\n external\n view\n returns (\n address[] memory targets,\n uint256[] memory values,\n string[] memory signatures,\n bytes[] memory calldatas\n );\n\n function getReceipt(uint256 proposalId, address voter) external view returns (Receipt memory);\n\n function state(uint256 proposalId) external view returns (ProposalState);\n\n function castVote(uint256 proposalId, uint8 support) external;\n\n function castVoteWithReason(\n uint256 proposalId,\n uint8 support,\n string calldata reason\n ) external;\n\n function castVoteBySig(\n uint256 proposalId,\n uint8 support,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n function isWhitelisted(address account) external view returns (bool);\n\n function _setDelay(uint256 proposalTimelockDelay_) external;\n\n function _setEmergencyDelay(uint256 emergencyTimelockDelay_) external;\n\n function _setVotingDelay(uint256 newVotingDelay) external;\n\n function _setVotingPeriod(uint256 newVotingPeriod) external;\n\n function _setEmergencyVotingPeriod(uint256 newEmergencyVotingPeriod) external;\n\n function _setProposalThreshold(uint256 newProposalThreshold) external;\n\n function _setQuorumVotes(uint256 newQuorumVotes) external;\n\n function _setEmergencyQuorumVotes(uint256 newEmergencyQuorumVotes) external;\n\n function _setWhitelistAccountExpiration(address account, uint256 expiration) external;\n\n function _setWhitelistGuardian(address account) external;\n\n function _setOptimisticDelay(uint256 newOptimisticVotingDelay) external;\n\n function _setOptimisticQuorumVotes(uint256 newOptimisticQuorumVotes) external;\n}\n\n/// @title interface which contains all events emitted by delegator & delegate\ninterface GovernorCharlieEvents {\n /// @notice An event emitted when a new proposal is created\n event ProposalCreated(\n uint256 indexed id,\n address indexed proposer,\n address[] targets,\n uint256[] values,\n string[] signatures,\n bytes[] calldatas,\n uint256 indexed startBlock,\n uint256 endBlock,\n string description\n );\n\n /// @notice An event emitted when a vote has been cast on a proposal\n /// @param voter The address which casted a vote\n /// @param proposalId The proposal id which was voted on\n /// @param support Support value for the vote. 0=against, 1=for, 2=abstain\n /// @param votes Number of votes which were cast by the voter\n /// @param reason The reason given for the vote by the voter\n event VoteCast(address indexed voter, uint256 indexed proposalId, uint8 support, uint256 votes, string reason);\n\n /// @notice An event emitted when a proposal has been canceled\n event ProposalCanceled(uint256 indexed id);\n\n /// @notice An event emitted when a proposal has been queued in the Timelock\n event ProposalQueued(uint256 indexed id, uint256 eta);\n\n /// @notice An event emitted when a proposal has been executed in the Timelock\n event ProposalExecuted(uint256 indexed id);\n\n /// @notice An event emitted when the voting delay is set\n event VotingDelaySet(uint256 oldVotingDelay, uint256 newVotingDelay);\n\n /// @notice An event emitted when the voting period is set\n event VotingPeriodSet(uint256 oldVotingPeriod, uint256 newVotingPeriod);\n\n /// @notice An event emitted when the emergency voting period is set\n event EmergencyVotingPeriodSet(uint256 oldEmergencyVotingPeriod, uint256 emergencyVotingPeriod);\n\n /// @notice Emitted when implementation is changed\n event NewImplementation(address oldImplementation, address newImplementation);\n\n /// @notice Emitted when proposal threshold is set\n event ProposalThresholdSet(uint256 oldProposalThreshold, uint256 newProposalThreshold);\n\n /// @notice Emitted when pendingAdmin is changed\n event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin);\n\n /// @notice Emitted when pendingAdmin is accepted, which means admin is updated\n event NewAdmin(address oldAdmin, address newAdmin);\n\n /// @notice Emitted when whitelist account expiration is set\n event WhitelistAccountExpirationSet(address account, uint256 expiration);\n\n /// @notice Emitted when the whitelistGuardian is set\n event WhitelistGuardianSet(address oldGuardian, address newGuardian);\n\n /// @notice Emitted when the a new delay is set\n event NewDelay(uint256 oldTimelockDelay, uint256 proposalTimelockDelay);\n\n /// @notice Emitted when the a new emergency delay is set\n event NewEmergencyDelay(uint256 oldEmergencyTimelockDelay, uint256 emergencyTimelockDelay);\n\n /// @notice Emitted when the quorum is updated\n event NewQuorum(uint256 oldQuorumVotes, uint256 quorumVotes);\n\n /// @notice Emitted when the emergency quorum is updated\n event NewEmergencyQuorum(uint256 oldEmergencyQuorumVotes, uint256 emergencyQuorumVotes);\n\n /// @notice An event emitted when the optimistic voting delay is set\n event OptimisticVotingDelaySet(uint256 oldOptimisticVotingDelay, uint256 optimisticVotingDelay);\n\n /// @notice Emitted when the optimistic quorum is updated\n event OptimisticQuorumVotesSet(uint256 oldOptimisticQuorumVotes, uint256 optimisticQuorumVotes);\n\n /// @notice Emitted when a transaction is canceled\n event CancelTransaction(\n bytes32 indexed txHash,\n address indexed target,\n uint256 value,\n string signature,\n bytes data,\n uint256 eta\n );\n\n /// @notice Emitted when a transaction is executed\n event ExecuteTransaction(\n bytes32 indexed txHash,\n address indexed target,\n uint256 value,\n string signature,\n bytes data,\n uint256 eta\n );\n\n /// @notice Emitted when a transaction is queued\n event QueueTransaction(\n bytes32 indexed txHash,\n address indexed target,\n uint256 value,\n string signature,\n bytes data,\n uint256 eta\n );\n}\n"},"contracts/governance/governor/Structs.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.9;\n\nstruct Proposal {\n /// @notice Unique id for looking up a proposal\n uint256 id;\n /// @notice Creator of the proposal\n address proposer;\n /// @notice The timestamp that the proposal will be available for execution, set once the vote succeeds\n uint256 eta;\n /// @notice the ordered list of target addresses for calls to be made\n address[] targets;\n /// @notice The ordered list of values (i.e. msg.value) to be passed to the calls to be made\n uint256[] values;\n /// @notice The ordered list of function signatures to be called\n string[] signatures;\n /// @notice The ordered list of calldata to be passed to each call\n bytes[] calldatas;\n /// @notice The block at which voting begins: holders must delegate their votes prior to this block\n uint256 startBlock;\n /// @notice The block at which voting ends: votes must be cast prior to this block\n uint256 endBlock;\n /// @notice Current number of votes in favor of this proposal\n uint256 forVotes;\n /// @notice Current number of votes in opposition to this proposal\n uint256 againstVotes;\n /// @notice Current number of votes for abstaining for this proposal\n uint256 abstainVotes;\n /// @notice Flag marking whether the proposal has been canceled\n bool canceled;\n /// @notice Flag marking whether the proposal has been executed\n bool executed;\n /// @notice Whether the proposal is an emergency proposal\n bool emergency;\n /// @notice quorum votes requires\n uint256 quorumVotes;\n /// @notice time delay\n uint256 delay;\n}\n\n/// @notice Ballot receipt record for a voter\nstruct Receipt {\n /// @notice Whether or not a vote has been cast\n bool hasVoted;\n /// @notice Whether or not the voter supports the proposal or abstains\n uint8 support;\n /// @notice The number of votes the voter had, which were cast\n uint96 votes;\n}\n\n/// @notice Possible states that a proposal may be in\nenum ProposalState {\n Pending,\n Active,\n Canceled,\n Defeated,\n Succeeded,\n Queued,\n Expired,\n Executed\n}\n"},"contracts/governance/governor/IIpt.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.9;\n\ninterface IIpt {\n function getPriorVotes(address account, uint256 blockNumber) external view returns (uint96);\n}\n"},"contracts/governance/governor/GovernorDelegate.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.9;\npragma experimental ABIEncoderV2;\nimport \"hardhat/console.sol\";\n\nimport \"./IGovernor.sol\";\nimport \"./GovernorStorage.sol\";\n\ncontract GovernorCharlieDelegate is GovernorCharlieDelegateStorage, GovernorCharlieEvents, IGovernorCharlieDelegate {\n /// @notice The name of this contract\n string public constant name = \"Interest Protocol Governor\";\n\n /// @notice The maximum number of actions that can be included in a proposal\n uint256 public constant proposalMaxOperations = 10;\n\n /// @notice The EIP-712 typehash for the contract's domain\n bytes32 public constant DOMAIN_TYPEHASH =\n keccak256(\"EIP712Domain(string name,uint256 chainId,address verifyingContract)\");\n\n /// @notice The EIP-712 typehash for the ballot struct used by the contract\n bytes32 public constant BALLOT_TYPEHASH = keccak256(\"Ballot(uint256 proposalId,uint8 support)\");\n\n /// @notice The time for a proposal to be executed after passing\n uint256 public constant GRACE_PERIOD = 14 days;\n\n /**\n * @notice Used to initialize the contract during delegator contructor\n * @param ipt_ The address of the IPT token\n */\n function initialize(\n address ipt_\n ) external override {\n require(!initialized, \"already been initialized\");\n ipt = IIpt(ipt_);\n votingPeriod = 40320;\n votingDelay = 13140;\n proposalThreshold = 1000000000000000000000000;\n proposalTimelockDelay = 172800;\n proposalCount = 0;\n quorumVotes = 10000000000000000000000000;\n emergencyQuorumVotes = 40000000000000000000000000;\n emergencyVotingPeriod = 6570;\n emergencyTimelockDelay = 43200;\n optimisticQuorumVotes = 2000000000000000000000000;\n optimisticVotingDelay = 18000;\n maxWhitelistPeriod = 31536000;\n\n initialized = true;\n }\n\n /// @notice any function with this modifier will call the pay_interest() function before\n modifier onlyGov() {\n require(_msgSender() == address(this), \"must come from the gov.\");\n _;\n }\n\n /**\n * @notice Function used to propose a new proposal. Sender must have delegates above the proposal threshold\n * @param targets Target addresses for proposal calls\n * @param values Eth values for proposal calls\n * @param signatures Function signatures for proposal calls\n * @param calldatas Calldatas for proposal calls\n * @param description String description of the proposal\n * @return Proposal id of new proposal\n */\n function propose(\n address[] memory targets,\n uint256[] memory values,\n string[] memory signatures,\n bytes[] memory calldatas,\n string memory description,\n bool emergency\n ) public override returns (uint256) {\n // Reject proposals before initiating as Governor\n require(quorumVotes != 0, \"Charlie not active\");\n // Allow addresses above proposal threshold and whitelisted addresses to propose\n require(\n ipt.getPriorVotes(_msgSender(), (block.number - 1)) >= proposalThreshold || isWhitelisted(_msgSender()),\n \"votes below proposal threshold\"\n );\n require(\n targets.length == values.length && targets.length == signatures.length && targets.length == calldatas.length,\n \"information arity mismatch\"\n );\n require(targets.length != 0, \"must provide actions\");\n require(targets.length <= proposalMaxOperations, \"too many actions\");\n\n uint256 latestProposalId = latestProposalIds[_msgSender()];\n if (latestProposalId != 0) {\n ProposalState proposersLatestProposalState = state(latestProposalId);\n require(proposersLatestProposalState != ProposalState.Active, \"one live proposal per proposer\");\n require(proposersLatestProposalState != ProposalState.Pending, \"one live proposal per proposer\");\n }\n\n proposalCount++;\n Proposal memory newProposal = Proposal({\n id: proposalCount,\n proposer: _msgSender(),\n eta: 0,\n targets: targets,\n values: values,\n signatures: signatures,\n calldatas: calldatas,\n startBlock: block.number + votingDelay,\n endBlock: block.number + votingDelay + votingPeriod,\n forVotes: 0,\n againstVotes: 0,\n abstainVotes: 0,\n canceled: false,\n executed: false,\n emergency: emergency,\n quorumVotes: quorumVotes,\n delay: proposalTimelockDelay\n });\n\n //whitelist can't make emergency\n if (emergency && !isWhitelisted(_msgSender())) {\n newProposal.startBlock = block.number;\n newProposal.endBlock = block.number + emergencyVotingPeriod;\n newProposal.quorumVotes = emergencyQuorumVotes;\n newProposal.delay = emergencyTimelockDelay;\n }\n\n //whitelist can only make optimistic proposals\n if (isWhitelisted(_msgSender())) {\n newProposal.quorumVotes = optimisticQuorumVotes;\n newProposal.startBlock = block.number + optimisticVotingDelay;\n newProposal.endBlock = block.number + optimisticVotingDelay + votingPeriod;\n }\n\n proposals[newProposal.id] = newProposal;\n latestProposalIds[newProposal.proposer] = newProposal.id;\n\n emit ProposalCreated(\n newProposal.id,\n _msgSender(),\n targets,\n values,\n signatures,\n calldatas,\n newProposal.startBlock,\n newProposal.endBlock,\n description\n );\n return newProposal.id;\n }\n\n /**\n * @notice Queues a proposal of state succeeded\n * @param proposalId The id of the proposal to queue\n */\n function queue(uint256 proposalId) external override {\n require(state(proposalId) == ProposalState.Succeeded, \"can only be queued if succeeded\");\n Proposal storage proposal = proposals[proposalId];\n uint256 eta = block.timestamp + proposal.delay;\n for (uint256 i = 0; i < proposal.targets.length; i++) {\n require(\n !queuedTransactions[\n keccak256(\n abi.encode(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], eta)\n )\n ],\n \"proposal already queued\"\n );\n queueTransaction(\n proposal.targets[i],\n proposal.values[i],\n proposal.signatures[i],\n proposal.calldatas[i],\n eta,\n proposal.delay\n );\n }\n proposal.eta = eta;\n emit ProposalQueued(proposalId, eta);\n }\n\n function queueTransaction(\n address target,\n uint256 value,\n string memory signature,\n bytes memory data,\n uint256 eta,\n uint256 delay\n ) internal returns (bytes32) {\n require(eta >= (getBlockTimestamp() + delay), \"must satisfy delay.\");\n\n bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));\n queuedTransactions[txHash] = true;\n\n emit QueueTransaction(txHash, target, value, signature, data, eta);\n return txHash;\n }\n\n /**\n * @notice Executes a queued proposal if eta has passed\n * @param proposalId The id of the proposal to execute\n */\n function execute(uint256 proposalId) external payable override {\n require(state(proposalId) == ProposalState.Queued, \"can only be exec'd if queued\");\n Proposal storage proposal = proposals[proposalId];\n proposal.executed = true;\n for (uint256 i = 0; i < proposal.targets.length; i++) {\n this.executeTransaction{value: proposal.values[i]}(\n proposal.targets[i],\n proposal.values[i],\n proposal.signatures[i],\n proposal.calldatas[i],\n proposal.eta\n );\n }\n emit ProposalExecuted(proposalId);\n }\n\n function executeTransaction(\n address target,\n uint256 value,\n string memory signature,\n bytes memory data,\n uint256 eta\n ) external payable override {\n bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));\n require(queuedTransactions[txHash], \"tx hasn't been queued.\");\n require(getBlockTimestamp() >= eta, \"tx hasn't surpassed timelock.\");\n require(getBlockTimestamp() <= eta + GRACE_PERIOD, \"tx is stale.\");\n\n queuedTransactions[txHash] = false;\n\n bytes memory callData;\n\n if (bytes(signature).length == 0) {\n callData = data;\n } else {\n callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);\n }\n\n // solhint-disable-next-line avoid-low-level-calls\n (\n bool success, /*bytes memory returnData*/\n\n ) = target.call{value: value}(callData);\n require(success, \"tx execution reverted.\");\n\n emit ExecuteTransaction(txHash, target, value, signature, data, eta);\n }\n\n /**\n * @notice Cancels a proposal only if sender is the proposer, or proposer delegates dropped below proposal threshold\n * @param proposalId The id of the proposal to cancel\n */\n function cancel(uint256 proposalId) external override {\n require(state(proposalId) != ProposalState.Executed, \"cant cancel executed proposal\");\n\n Proposal storage proposal = proposals[proposalId];\n\n // Proposer can cancel\n if (_msgSender() != proposal.proposer) {\n // Whitelisted proposers can't be canceled for falling below proposal threshold\n if (isWhitelisted(proposal.proposer)) {\n require(\n (ipt.getPriorVotes(proposal.proposer, (block.number - 1)) < proposalThreshold) &&\n _msgSender() == whitelistGuardian,\n \"cancel: whitelisted proposer\"\n );\n } else {\n require(\n (ipt.getPriorVotes(proposal.proposer, (block.number - 1)) < proposalThreshold),\n \"cancel: proposer above threshold\"\n );\n }\n }\n\n proposal.canceled = true;\n for (uint256 i = 0; i < proposal.targets.length; i++) {\n cancelTransaction(\n proposal.targets[i],\n proposal.values[i],\n proposal.signatures[i],\n proposal.calldatas[i],\n proposal.eta\n );\n }\n\n emit ProposalCanceled(proposalId);\n }\n\n function cancelTransaction(\n address target,\n uint256 value,\n string memory signature,\n bytes memory data,\n uint256 eta\n ) internal {\n bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));\n queuedTransactions[txHash] = false;\n\n emit CancelTransaction(txHash, target, value, signature, data, eta);\n }\n\n /**\n * @notice Gets actions of a proposal\n * @param proposalId the id of the proposal\n * @return targets proposal targets\n * @return values proposal values\n * @return signatures proposal signatures\n * @return calldatas proposal calldatae\n */\n function getActions(uint256 proposalId)\n external\n view\n override\n returns (\n address[] memory targets,\n uint256[] memory values,\n string[] memory signatures,\n bytes[] memory calldatas\n )\n {\n Proposal storage p = proposals[proposalId];\n return (p.targets, p.values, p.signatures, p.calldatas);\n }\n\n /**\n * @notice Gets the receipt for a voter on a given proposal\n * @param proposalId the id of proposal\n * @param voter The address of the voter\n * @return The voting receipt\n */\n function getReceipt(uint256 proposalId, address voter) external view override returns (Receipt memory) {\n return proposalReceipts[proposalId][voter];\n }\n\n /**\n * @notice Gets the state of a proposal\n * @param proposalId The id of the proposal\n * @return Proposal state\n */\n // solhint-disable-next-line code-complexity\n function state(uint256 proposalId) public view override returns (ProposalState) {\n require(proposalCount >= proposalId && proposalId > initialProposalId, \"state: invalid proposal id\");\n Proposal storage proposal = proposals[proposalId];\n bool whitelisted = isWhitelisted(proposal.proposer);\n if (proposal.canceled) {\n return ProposalState.Canceled;\n } else if (block.number <= proposal.startBlock) {\n return ProposalState.Pending;\n } else if (block.number <= proposal.endBlock) {\n return ProposalState.Active;\n } else if (\n (whitelisted && proposal.againstVotes > proposal.quorumVotes) ||\n (!whitelisted && proposal.forVotes <= proposal.againstVotes) ||\n (!whitelisted && proposal.forVotes < proposal.quorumVotes)\n ) {\n return ProposalState.Defeated;\n } else if (proposal.eta == 0) {\n return ProposalState.Succeeded;\n } else if (proposal.executed) {\n return ProposalState.Executed;\n } else if (block.timestamp >= (proposal.eta + GRACE_PERIOD)) {\n return ProposalState.Expired;\n }\n return ProposalState.Queued;\n }\n\n /**\n * @notice Cast a vote for a proposal\n * @param proposalId The id of the proposal to vote on\n * @param support The support value for the vote. 0=against, 1=for, 2=abstain\n */\n function castVote(uint256 proposalId, uint8 support) external override {\n emit VoteCast(_msgSender(), proposalId, support, castVoteInternal(_msgSender(), proposalId, support), \"\");\n }\n\n /**\n * @notice Cast a vote for a proposal with a reason\n * @param proposalId The id of the proposal to vote on\n * @param support The support value for the vote. 0=against, 1=for, 2=abstain\n * @param reason The reason given for the vote by the voter\n */\n function castVoteWithReason(\n uint256 proposalId,\n uint8 support,\n string calldata reason\n ) external override {\n emit VoteCast(_msgSender(), proposalId, support, castVoteInternal(_msgSender(), proposalId, support), reason);\n }\n\n /**\n * @notice Cast a vote for a proposal by signature\n * @dev external override function that accepts EIP-712 signatures for voting on proposals.\n */\n function castVoteBySig(\n uint256 proposalId,\n uint8 support,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external override {\n bytes32 domainSeparator = keccak256(\n abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainIdInternal(), address(this))\n );\n bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));\n bytes32 digest = keccak256(abi.encodePacked(\"\\x19\\x01\", domainSeparator, structHash));\n address signatory = ecrecover(digest, v, r, s);\n require(signatory != address(0), \"castVoteBySig: invalid signature\");\n emit VoteCast(signatory, proposalId, support, castVoteInternal(signatory, proposalId, support), \"\");\n }\n\n /**\n * @notice Internal function that caries out voting logic\n * @param voter The voter that is casting their vote\n * @param proposalId The id of the proposal to vote on\n * @param support The support value for the vote. 0=against, 1=for, 2=abstain\n * @return The number of votes cast\n */\n function castVoteInternal(\n address voter,\n uint256 proposalId,\n uint8 support\n ) internal returns (uint96) {\n require(state(proposalId) == ProposalState.Active, \"voting is closed\");\n require(support <= 2, \"invalid vote type\");\n Proposal storage proposal = proposals[proposalId];\n Receipt storage receipt = proposalReceipts[proposalId][voter];\n require(receipt.hasVoted == false, \"voter already voted\");\n uint96 votes = ipt.getPriorVotes(voter, proposal.startBlock);\n\n if (support == 0) {\n proposal.againstVotes = proposal.againstVotes + votes;\n } else if (support == 1) {\n proposal.forVotes = proposal.forVotes + votes;\n } else if (support == 2) {\n proposal.abstainVotes = proposal.abstainVotes + votes;\n }\n\n receipt.hasVoted = true;\n receipt.support = support;\n receipt.votes = votes;\n\n return votes;\n }\n\n /**\n * @notice View function which returns if an account is whitelisted\n * @param account Account to check white list status of\n * @return If the account is whitelisted\n */\n function isWhitelisted(address account) public view override returns (bool) {\n return (whitelistAccountExpirations[account] > block.timestamp);\n }\n\n /**\n * @notice Governance function for setting the governance token\n * @param token_ new token addr\n */\n function _setNewToken(address token_) external onlyGov {\n ipt = IIpt(token_);\n }\n\n /**\n * @notice Used to update the timelock period\n * @param proposalTimelockDelay_ The proposal holding period\n */\n function _setDelay(uint256 proposalTimelockDelay_) public override onlyGov {\n uint256 oldTimelockDelay = proposalTimelockDelay;\n proposalTimelockDelay = proposalTimelockDelay_;\n\n emit NewDelay(oldTimelockDelay, proposalTimelockDelay);\n }\n\n /**\n * @notice Used to update the emergency timelock period\n * @param emergencyTimelockDelay_ The proposal holding period\n */\n function _setEmergencyDelay(uint256 emergencyTimelockDelay_) public override onlyGov {\n uint256 oldEmergencyTimelockDelay = emergencyTimelockDelay;\n emergencyTimelockDelay = emergencyTimelockDelay_;\n\n emit NewEmergencyDelay(oldEmergencyTimelockDelay, emergencyTimelockDelay);\n }\n\n /**\n * @notice Governance function for setting the voting delay\n * @param newVotingDelay new voting delay, in blocks\n */\n function _setVotingDelay(uint256 newVotingDelay) external override onlyGov {\n uint256 oldVotingDelay = votingDelay;\n votingDelay = newVotingDelay;\n\n emit VotingDelaySet(oldVotingDelay, votingDelay);\n }\n\n /**\n * @notice Governance function for setting the voting period\n * @param newVotingPeriod new voting period, in blocks\n */\n function _setVotingPeriod(uint256 newVotingPeriod) external override onlyGov {\n uint256 oldVotingPeriod = votingPeriod;\n votingPeriod = newVotingPeriod;\n\n emit VotingPeriodSet(oldVotingPeriod, votingPeriod);\n }\n\n /**\n * @notice Governance function for setting the emergency voting period\n * @param newEmergencyVotingPeriod new voting period, in blocks\n */\n function _setEmergencyVotingPeriod(uint256 newEmergencyVotingPeriod) external override onlyGov {\n uint256 oldEmergencyVotingPeriod = emergencyVotingPeriod;\n emergencyVotingPeriod = newEmergencyVotingPeriod;\n\n emit EmergencyVotingPeriodSet(oldEmergencyVotingPeriod, emergencyVotingPeriod);\n }\n\n /**\n * @notice Governance function for setting the proposal threshold\n * @param newProposalThreshold new proposal threshold\n */\n function _setProposalThreshold(uint256 newProposalThreshold) external override onlyGov {\n uint256 oldProposalThreshold = proposalThreshold;\n proposalThreshold = newProposalThreshold;\n\n emit ProposalThresholdSet(oldProposalThreshold, proposalThreshold);\n }\n\n /**\n * @notice Governance function for setting the quorum\n * @param newQuorumVotes new proposal quorum\n */\n function _setQuorumVotes(uint256 newQuorumVotes) external override onlyGov {\n uint256 oldQuorumVotes = quorumVotes;\n quorumVotes = newQuorumVotes;\n\n emit NewQuorum(oldQuorumVotes, quorumVotes);\n }\n\n /**\n * @notice Governance function for setting the emergency quorum\n * @param newEmergencyQuorumVotes new proposal quorum\n */\n function _setEmergencyQuorumVotes(uint256 newEmergencyQuorumVotes) external override onlyGov {\n uint256 oldEmergencyQuorumVotes = emergencyQuorumVotes;\n emergencyQuorumVotes = newEmergencyQuorumVotes;\n\n emit NewEmergencyQuorum(oldEmergencyQuorumVotes, emergencyQuorumVotes);\n }\n\n /**\n * @notice Governance function for setting the whitelist expiration as a timestamp\n * for an account. Whitelist status allows accounts to propose without meeting threshold\n * @param account Account address to set whitelist expiration for\n * @param expiration Expiration for account whitelist status as timestamp (if now < expiration, whitelisted)\n */\n function _setWhitelistAccountExpiration(address account, uint256 expiration) external override onlyGov {\n require (expiration < (maxWhitelistPeriod + block.timestamp), \"expiration exceeds max\");\n whitelistAccountExpirations[account] = expiration;\n\n emit WhitelistAccountExpirationSet(account, expiration);\n }\n\n /**\n * @notice Governance function for setting the whitelistGuardian. WhitelistGuardian can cancel proposals from whitelisted addresses\n * @param account Account to set whitelistGuardian to (0x0 to remove whitelistGuardian)\n */\n function _setWhitelistGuardian(address account) external override onlyGov {\n address oldGuardian = whitelistGuardian;\n whitelistGuardian = account;\n\n emit WhitelistGuardianSet(oldGuardian, whitelistGuardian);\n }\n\n /**\n * @notice Governance function for setting the optimistic voting delay\n * @param newOptimisticVotingDelay new optimistic voting delay, in blocks\n */\n function _setOptimisticDelay(uint256 newOptimisticVotingDelay) external override onlyGov {\n uint256 oldOptimisticVotingDelay = optimisticVotingDelay;\n optimisticVotingDelay = newOptimisticVotingDelay;\n\n emit OptimisticVotingDelaySet(oldOptimisticVotingDelay, optimisticVotingDelay);\n }\n\n /**\n * @notice Governance function for setting the optimistic quorum\n * @param newOptimisticQuorumVotes new optimistic quorum votes, in blocks\n */\n function _setOptimisticQuorumVotes(uint256 newOptimisticQuorumVotes) external override onlyGov {\n uint256 oldOptimisticQuorumVotes = optimisticQuorumVotes;\n optimisticQuorumVotes = newOptimisticQuorumVotes;\n\n emit OptimisticQuorumVotesSet(oldOptimisticQuorumVotes, optimisticQuorumVotes);\n }\n\n function getChainIdInternal() internal view returns (uint256) {\n return block.chainid;\n }\n\n function getBlockTimestamp() internal view returns (uint256) {\n // solium-disable-next-line security/no-block-members\n return block.timestamp;\n }\n\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n}\n"}},"settings":{"optimizer":{"enabled":true,"runs":200,"details":{"orderLiterals":true,"deduplicate":true,"cse":true,"yul":true}},"outputSelection":{"*":{"*":["evm.bytecode","evm.deployedBytecode","devdoc","userdoc","metadata","abi"]}},"libraries":{}}},"ABI":"[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"txHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"signature\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"eta\",\"type\":\"uint256\"}],\"name\":\"CancelTransaction\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldEmergencyVotingPeriod\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"emergencyVotingPeriod\",\"type\":\"uint256\"}],\"name\":\"EmergencyVotingPeriodSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"txHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"signature\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"eta\",\"type\":\"uint256\"}],\"name\":\"ExecuteTransaction\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldAdmin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"NewAdmin\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldTimelockDelay\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"proposalTimelockDelay\",\"type\":\"uint256\"}],\"name\":\"NewDelay\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldEmergencyTimelockDelay\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"emergencyTimelockDelay\",\"type\":\"uint256\"}],\"name\":\"NewEmergencyDelay\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldEmergencyQuorumVotes\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"emergencyQuorumVotes\",\"type\":\"uint256\"}],\"name\":\"NewEmergencyQuorum\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldImplementation\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newImplementation\",\"type\":\"address\"}],\"name\":\"NewImplementation\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldPendingAdmin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newPendingAdmin\",\"type\":\"address\"}],\"name\":\"NewPendingAdmin\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldQuorumVotes\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"quorumVotes\",\"type\":\"uint256\"}],\"name\":\"NewQuorum\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldOptimisticQuorumVotes\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"optimisticQuorumVotes\",\"type\":\"uint256\"}],\"name\":\"OptimisticQuorumVotesSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldOptimisticVotingDelay\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"optimisticVotingDelay\",\"type\":\"uint256\"}],\"name\":\"OptimisticVotingDelaySet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"ProposalCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"proposer\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"},{\"indexed\":false,\"internalType\":\"string[]\",\"name\":\"signatures\",\"type\":\"string[]\"},{\"indexed\":false,\"internalType\":\"bytes[]\",\"name\":\"calldatas\",\"type\":\"bytes[]\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"startBlock\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"endBlock\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"description\",\"type\":\"string\"}],\"name\":\"ProposalCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"ProposalExecuted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"eta\",\"type\":\"uint256\"}],\"name\":\"ProposalQueued\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldProposalThreshold\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newProposalThreshold\",\"type\":\"uint256\"}],\"name\":\"ProposalThresholdSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"txHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"signature\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"eta\",\"type\":\"uint256\"}],\"name\":\"QueueTransaction\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"voter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"support\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"votes\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"reason\",\"type\":\"string\"}],\"name\":\"VoteCast\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldVotingDelay\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newVotingDelay\",\"type\":\"uint256\"}],\"name\":\"VotingDelaySet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldVotingPeriod\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newVotingPeriod\",\"type\":\"uint256\"}],\"name\":\"VotingPeriodSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"expiration\",\"type\":\"uint256\"}],\"name\":\"WhitelistAccountExpirationSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldGuardian\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newGuardian\",\"type\":\"address\"}],\"name\":\"WhitelistGuardianSet\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"BALLOT_TYPEHASH\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"DOMAIN_TYPEHASH\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"GRACE_PERIOD\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalTimelockDelay_\",\"type\":\"uint256\"}],\"name\":\"_setDelay\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"emergencyTimelockDelay_\",\"type\":\"uint256\"}],\"name\":\"_setEmergencyDelay\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newEmergencyQuorumVotes\",\"type\":\"uint256\"}],\"name\":\"_setEmergencyQuorumVotes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newEmergencyVotingPeriod\",\"type\":\"uint256\"}],\"name\":\"_setEmergencyVotingPeriod\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token_\",\"type\":\"address\"}],\"name\":\"_setNewToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newOptimisticVotingDelay\",\"type\":\"uint256\"}],\"name\":\"_setOptimisticDelay\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newOptimisticQuorumVotes\",\"type\":\"uint256\"}],\"name\":\"_setOptimisticQuorumVotes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newProposalThreshold\",\"type\":\"uint256\"}],\"name\":\"_setProposalThreshold\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newQuorumVotes\",\"type\":\"uint256\"}],\"name\":\"_setQuorumVotes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newVotingDelay\",\"type\":\"uint256\"}],\"name\":\"_setVotingDelay\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newVotingPeriod\",\"type\":\"uint256\"}],\"name\":\"_setVotingPeriod\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"expiration\",\"type\":\"uint256\"}],\"name\":\"_setWhitelistAccountExpiration\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"_setWhitelistGuardian\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"}],\"name\":\"cancel\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"support\",\"type\":\"uint8\"}],\"name\":\"castVote\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"support\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"castVoteBySig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"support\",\"type\":\"uint8\"},{\"internalType\":\"string\",\"name\":\"reason\",\"type\":\"string\"}],\"name\":\"castVoteWithReason\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"emergencyQuorumVotes\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"emergencyTimelockDelay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"emergencyVotingPeriod\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"}],\"name\":\"execute\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"signature\",\"type\":\"string\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"eta\",\"type\":\"uint256\"}],\"name\":\"executeTransaction\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"}],\"name\":\"getActions\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"},{\"internalType\":\"string[]\",\"name\":\"signatures\",\"type\":\"string[]\"},{\"internalType\":\"bytes[]\",\"name\":\"calldatas\",\"type\":\"bytes[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"voter\",\"type\":\"address\"}],\"name\":\"getReceipt\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"hasVoted\",\"type\":\"bool\"},{\"internalType\":\"uint8\",\"name\":\"support\",\"type\":\"uint8\"},{\"internalType\":\"uint96\",\"name\":\"votes\",\"type\":\"uint96\"}],\"internalType\":\"struct Receipt\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"implementation\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"initialProposalId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"ipt_\",\"type\":\"address\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"initialized\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"ipt\",\"outputs\":[{\"internalType\":\"contract IIpt\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"isWhitelisted\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"latestProposalIds\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"maxWhitelistPeriod\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"optimisticQuorumVotes\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"optimisticVotingDelay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"proposalCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"proposalMaxOperations\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"proposalReceipts\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"hasVoted\",\"type\":\"bool\"},{\"internalType\":\"uint8\",\"name\":\"support\",\"type\":\"uint8\"},{\"internalType\":\"uint96\",\"name\":\"votes\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"proposalThreshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"proposalTimelockDelay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"proposals\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"proposer\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"eta\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"startBlock\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"endBlock\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"forVotes\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"againstVotes\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"abstainVotes\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"canceled\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"executed\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"emergency\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"quorumVotes\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"delay\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"},{\"internalType\":\"string[]\",\"name\":\"signatures\",\"type\":\"string[]\"},{\"internalType\":\"bytes[]\",\"name\":\"calldatas\",\"type\":\"bytes[]\"},{\"internalType\":\"string\",\"name\":\"description\",\"type\":\"string\"},{\"internalType\":\"bool\",\"name\":\"emergency\",\"type\":\"bool\"}],\"name\":\"propose\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"}],\"name\":\"queue\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"queuedTransactions\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"quorumVotes\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"proposalId\",\"type\":\"uint256\"}],\"name\":\"state\",\"outputs\":[{\"internalType\":\"enum ProposalState\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"votingDelay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"votingPeriod\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"whitelistAccountExpirations\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"whitelistGuardian\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]","ContractName":"GovernorCharlieDelegate","CompilerVersion":"v0.8.9+commit.e5eed63a","OptimizationUsed":1,"Runs":200,"ConstructorArguments":"0x","EVMVersion":"Default","Library":"","LicenseType":"","Proxy":0,"SwarmSource":""}] \ No newline at end of file diff --git a/testdata/etherscan/0x9AB6b21cDF116f611110b048987E58894786C244/creation_data.json b/testdata/etherscan/0x9AB6b21cDF116f611110b048987E58894786C244/creation_data.json index d145e56aa..2378ed6aa 100644 --- a/testdata/etherscan/0x9AB6b21cDF116f611110b048987E58894786C244/creation_data.json +++ b/testdata/etherscan/0x9AB6b21cDF116f611110b048987E58894786C244/creation_data.json @@ -1,5 +1 @@ -{ - "contractAddress": "0x9ab6b21cdf116f611110b048987e58894786c244", - "contractCreator": "0x603d50bad151da8becf405e51a8c4abc8ba1c95e", - "txHash": "0x72be611ae1ade09242d9fc9c950a73d076f6c23514564a7b9ac730400dbaf2c0" -} \ No newline at end of file +{"contractAddress":"0x9ab6b21cdf116f611110b048987e58894786c244","contractCreator":"0x603d50bad151da8becf405e51a8c4abc8ba1c95e","txHash":"0x72be611ae1ade09242d9fc9c950a73d076f6c23514564a7b9ac730400dbaf2c0"} \ No newline at end of file diff --git a/testdata/etherscan/0x9AB6b21cDF116f611110b048987E58894786C244/metadata.json b/testdata/etherscan/0x9AB6b21cDF116f611110b048987E58894786C244/metadata.json index ab133f38f..b0f893895 100644 --- a/testdata/etherscan/0x9AB6b21cDF116f611110b048987E58894786C244/metadata.json +++ b/testdata/etherscan/0x9AB6b21cDF116f611110b048987E58894786C244/metadata.json @@ -1,69 +1 @@ -[ - { - "SourceCode": { - "language": "Solidity", - "sources": { - "contracts/InterestRates/InterestRatePositionManager.f.sol": { - "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n}\n\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)\n\n/**\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n */\ninterface IERC20Permit {\n /**\n * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\n * given ``owner``'s signed approval.\n *\n * IMPORTANT: The same issues {IERC20-approve} has related to transaction\n * ordering also apply here.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `deadline` must be a timestamp in the future.\n * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\n * over the EIP712-formatted function arguments.\n * - the signature must use ``owner``'s current nonce (see {nonces}).\n *\n * For more information on the signature format, see the\n * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\n * section].\n */\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n /**\n * @dev Returns the current nonce for `owner`. This value must be\n * included whenever a signature is generated for {permit}.\n *\n * Every successful call to {permit} increases ``owner``'s nonce by one. This\n * prevents a signature from being used multiple times.\n */\n function nonces(address owner) external view returns (uint256);\n\n /**\n * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\n */\n // solhint-disable-next-line func-name-mixedcase\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n\n/// Parameters for ERC20Permit.permit call\nstruct ERC20PermitSignature {\n IERC20Permit token;\n uint256 value;\n uint256 deadline;\n uint8 v;\n bytes32 r;\n bytes32 s;\n}\n\nlibrary PermitHelper {\n function applyPermit(\n ERC20PermitSignature calldata p,\n address owner,\n address spender\n ) internal {\n p.token.permit(owner, spender, p.value, p.deadline, p.v, p.r, p.s);\n }\n\n function applyPermits(\n ERC20PermitSignature[] calldata permits,\n address owner,\n address spender\n ) internal {\n for (uint256 i = 0; i < permits.length; i++) {\n applyPermit(permits[i], owner, spender);\n }\n }\n}\n\n// OpenZeppelin Contracts v4.4.1 (interfaces/IERC3156FlashLender.sol)\n\n// OpenZeppelin Contracts (last updated v4.7.0) (interfaces/IERC3156FlashBorrower.sol)\n\n/**\n * @dev Interface of the ERC3156 FlashBorrower, as defined in\n * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].\n *\n * _Available since v4.1._\n */\ninterface IERC3156FlashBorrower {\n /**\n * @dev Receive a flash loan.\n * @param initiator The initiator of the loan.\n * @param token The loan currency.\n * @param amount The amount of tokens lent.\n * @param fee The additional amount of tokens to repay.\n * @param data Arbitrary data structure, intended to contain user-defined parameters.\n * @return The keccak256 hash of \"IERC3156FlashBorrower.onFlashLoan\"\n */\n function onFlashLoan(\n address initiator,\n address token,\n uint256 amount,\n uint256 fee,\n bytes calldata data\n ) external returns (bytes32);\n}\n\n/**\n * @dev Interface of the ERC3156 FlashLender, as defined in\n * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].\n *\n * _Available since v4.1._\n */\ninterface IERC3156FlashLender {\n /**\n * @dev The amount of currency available to be lended.\n * @param token The loan currency.\n * @return The amount of `token` that can be borrowed.\n */\n function maxFlashLoan(address token) external view returns (uint256);\n\n /**\n * @dev The fee to be charged for a given loan.\n * @param token The loan currency.\n * @param amount The amount of tokens lent.\n * @return The amount of `token` to be charged for the loan, on top of the returned principal.\n */\n function flashFee(address token, uint256 amount) external view returns (uint256);\n\n /**\n * @dev Initiate a flash loan.\n * @param receiver The receiver of the tokens in the loan, and the receiver of the callback.\n * @param token The loan currency.\n * @param amount The amount of tokens lent.\n * @param data Arbitrary data structure, intended to contain user-defined parameters.\n */\n function flashLoan(\n IERC3156FlashBorrower receiver,\n address token,\n uint256 amount,\n bytes calldata data\n ) external returns (bool);\n}\n\n/// @dev Interface to be used by contracts that collect fees. Contains fee recipient that can be changed by owner.\ninterface IFeeCollector {\n // --- Events ---\n\n /// @dev Fee Recipient is changed to @param feeRecipient address.\n /// @param feeRecipient New fee recipient address.\n event FeeRecipientChanged(address feeRecipient);\n\n // --- Errors ---\n\n /// @dev Invalid fee recipient.\n error InvalidFeeRecipient();\n\n // --- Functions ---\n\n /// @return Address of the current fee recipient.\n function feeRecipient() external view returns (address);\n\n /// @dev Sets new fee recipient address\n /// @param newFeeRecipient Address of the new fee recipient.\n function setFeeRecipient(address newFeeRecipient) external;\n}\n\ninterface IPositionManagerDependent {\n // --- Errors ---\n\n /// @dev Position Manager cannot be zero.\n error PositionManagerCannotBeZero();\n\n /// @dev Caller is not Position Manager.\n error CallerIsNotPositionManager(address caller);\n\n // --- Functions ---\n\n /// @dev Returns address of the PositionManager contract.\n function positionManager() external view returns (address);\n}\n\n/// @dev Interface of R stablecoin token. Implements some standards like IERC20, IERC20Permit, and IERC3156FlashLender.\n/// Raft's specific implementation contains IFeeCollector and IPositionManagerDependent.\n/// PositionManager can mint and burn R when particular actions happen with user's position.\ninterface IRToken is IERC20, IERC20Permit, IERC3156FlashLender, IFeeCollector, IPositionManagerDependent {\n // --- Events ---\n\n /// @dev New R token is deployed\n /// @param positionManager Address of the PositionManager contract that is authorized to mint and burn new tokens.\n /// @param flashMintFeeRecipient Address of flash mint fee recipient.\n event RDeployed(address positionManager, address flashMintFeeRecipient);\n\n /// @dev The Flash Mint Fee Percentage has been changed.\n /// @param flashMintFeePercentage The new Flash Mint Fee Percentage value.\n event FlashMintFeePercentageChanged(uint256 flashMintFeePercentage);\n\n /// --- Errors ---\n\n /// @dev Proposed flash mint fee percentage is too big.\n /// @param feePercentage Proposed flash mint fee percentage.\n error FlashFeePercentageTooBig(uint256 feePercentage);\n\n // --- Functions ---\n\n /// @return Number representing 100 percentage.\n function PERCENTAGE_BASE() external view returns (uint256);\n\n /// @dev Mints new tokens. Callable only by PositionManager contract.\n /// @param to Address that will receive newly minted tokens.\n /// @param amount Amount of tokens to mint.\n function mint(address to, uint256 amount) external;\n\n /// @dev Mints new tokens. Callable only by PositionManager contract.\n /// @param from Address of user whose tokens are burnt.\n /// @param amount Amount of tokens to burn.\n function burn(address from, uint256 amount) external;\n\n /// @return Maximum flash mint fee percentage that can be set by owner.\n function MAX_FLASH_MINT_FEE_PERCENTAGE() external view returns (uint256);\n\n /// @return Current flash mint fee percentage.\n function flashMintFeePercentage() external view returns (uint256);\n\n /// @dev Sets new flash mint fee percentage. Callable only by owner.\n /// @notice The proposed flash mint fee percentage cannot exceed `MAX_FLASH_MINT_FEE_PERCENTAGE`.\n /// @param feePercentage New flash fee percentage.\n function setFlashMintFeePercentage(uint256 feePercentage) external;\n}\n\ninterface IPriceOracle {\n // --- Errors ---\n\n /// @dev Contract initialized with an invalid deviation parameter.\n error InvalidDeviation();\n\n // --- Types ---\n\n struct PriceOracleResponse {\n bool isBrokenOrFrozen;\n bool priceChangeAboveMax;\n uint256 price;\n }\n\n // --- Functions ---\n\n /// @dev Return price oracle response which consists the following information: oracle is broken or frozen, the\n /// price change between two rounds is more than max, and the price.\n function getPriceOracleResponse() external returns (PriceOracleResponse memory);\n\n /// @dev Maximum time period allowed since oracle latest round data timestamp, beyond which oracle is considered\n /// frozen.\n function timeout() external view returns (uint256);\n\n /// @dev Used to convert a price answer to an 18-digit precision uint.\n function TARGET_DIGITS() external view returns (uint256);\n\n /// @dev price deviation for the oracle in percentage.\n function DEVIATION() external view returns (uint256);\n}\n\ninterface IPriceFeed {\n // --- Events ---\n\n /// @dev Last good price has been updated.\n event LastGoodPriceUpdated(uint256 lastGoodPrice);\n\n /// @dev Price difference between oracles has been updated.\n /// @param priceDifferenceBetweenOracles New price difference between oracles.\n event PriceDifferenceBetweenOraclesUpdated(uint256 priceDifferenceBetweenOracles);\n\n /// @dev Primary oracle has been updated.\n /// @param primaryOracle New primary oracle.\n event PrimaryOracleUpdated(IPriceOracle primaryOracle);\n\n /// @dev Secondary oracle has been updated.\n /// @param secondaryOracle New secondary oracle.\n event SecondaryOracleUpdated(IPriceOracle secondaryOracle);\n\n // --- Errors ---\n\n /// @dev Invalid primary oracle.\n error InvalidPrimaryOracle();\n\n /// @dev Invalid secondary oracle.\n error InvalidSecondaryOracle();\n\n /// @dev Primary oracle is broken or frozen or has bad result.\n error PrimaryOracleBrokenOrFrozenOrBadResult();\n\n /// @dev Invalid price difference between oracles.\n error InvalidPriceDifferenceBetweenOracles();\n\n // --- Functions ---\n\n /// @dev Return primary oracle address.\n function primaryOracle() external returns (IPriceOracle);\n\n /// @dev Return secondary oracle address\n function secondaryOracle() external returns (IPriceOracle);\n\n /// @dev The last good price seen from an oracle by Raft.\n function lastGoodPrice() external returns (uint256);\n\n /// @dev The maximum relative price difference between two oracle responses.\n function priceDifferenceBetweenOracles() external returns (uint256);\n\n /// @dev Set primary oracle address.\n /// @param newPrimaryOracle Primary oracle address.\n function setPrimaryOracle(IPriceOracle newPrimaryOracle) external;\n\n /// @dev Set secondary oracle address.\n /// @param newSecondaryOracle Secondary oracle address.\n function setSecondaryOracle(IPriceOracle newSecondaryOracle) external;\n\n /// @dev Set the maximum relative price difference between two oracle responses.\n /// @param newPriceDifferenceBetweenOracles The maximum relative price difference between two oracle responses.\n function setPriceDifferenceBetweenOracles(uint256 newPriceDifferenceBetweenOracles) external;\n\n /// @dev Returns the latest price obtained from the Oracle. Called by Raft functions that require a current price.\n ///\n /// Also callable by anyone externally.\n /// Non-view function - it stores the last good price seen by Raft.\n ///\n /// Uses a primary oracle and a fallback oracle in case primary fails. If both fail,\n /// it uses the last good price seen by Raft.\n ///\n /// @return currentPrice Returned price.\n /// @return deviation Deviation of the reported price in percentage.\n /// @notice Actual returned price is in range `currentPrice` +/- `currentPrice * deviation / ONE`\n function fetchPrice() external returns (uint256 currentPrice, uint256 deviation);\n}\n\ninterface IERC20Indexable is IERC20, IPositionManagerDependent {\n // --- Events ---\n\n /// @dev New token is deployed.\n /// @param positionManager Address of the PositionManager contract that is authorized to mint and burn new tokens.\n event ERC20IndexableDeployed(address positionManager);\n\n /// @dev New index has been set.\n /// @param newIndex Value of the new index.\n event IndexUpdated(uint256 newIndex);\n\n // --- Errors ---\n\n /// @dev Unsupported action for ERC20Indexable contract.\n error NotSupported();\n\n // --- Functions ---\n\n /// @return Precision for token index. Represents index that is equal to 1.\n function INDEX_PRECISION() external view returns (uint256);\n\n /// @return Current index value.\n function currentIndex() external view returns (uint256);\n\n /// @dev Sets new token index. Callable only by PositionManager contract.\n /// @param backingAmount Amount of backing token that is covered by total supply.\n function setIndex(uint256 backingAmount) external;\n\n /// @dev Mints new tokens. Callable only by PositionManager contract.\n /// @param to Address that will receive newly minted tokens.\n /// @param amount Amount of tokens to mint.\n function mint(address to, uint256 amount) external;\n\n /// @dev Mints new tokens. Callable only by PositionManager contract.\n /// @param from Address of user whose tokens are burnt.\n /// @param amount Amount of tokens to burn.\n function burn(address from, uint256 amount) external;\n}\n\ninterface ISplitLiquidationCollateral {\n // --- Functions ---\n\n /// @dev Returns lowest total debt that will be split.\n function LOW_TOTAL_DEBT() external view returns (uint256);\n\n /// @dev Minimum collateralization ratio for position\n function MCR() external view returns (uint256);\n\n /// @dev Splits collateral between protocol and liquidator.\n /// @param totalCollateral Amount of collateral to split.\n /// @param totalDebt Amount of debt to split.\n /// @param price Price of collateral.\n /// @param isRedistribution True if this is a redistribution.\n /// @return collateralToSendToProtocol Amount of collateral to send to protocol.\n /// @return collateralToSentToLiquidator Amount of collateral to send to liquidator.\n function split(\n uint256 totalCollateral,\n uint256 totalDebt,\n uint256 price,\n bool isRedistribution\n )\n external\n view\n returns (uint256 collateralToSendToProtocol, uint256 collateralToSentToLiquidator);\n}\n\n/// @dev Common interface for the Position Manager.\ninterface IPositionManager is IFeeCollector {\n // --- Types ---\n\n /// @dev Information for a Raft indexable collateral token.\n /// @param collateralToken The Raft indexable collateral token.\n /// @param debtToken Corresponding Raft indexable debt token.\n /// @param priceFeed The contract that provides a price for the collateral token.\n /// @param splitLiquidation The contract that calculates collateral split in case of liquidation.\n /// @param isEnabled Whether the token can be used as collateral or not.\n /// @param lastFeeOperationTime Timestamp of the last operation for the collateral token.\n /// @param borrowingSpread The current borrowing spread.\n /// @param baseRate The current base rate.\n /// @param redemptionSpread The current redemption spread.\n /// @param redemptionRebate Percentage of the redemption fee returned to redeemed positions.\n struct CollateralTokenInfo {\n IERC20Indexable collateralToken;\n IERC20Indexable debtToken;\n IPriceFeed priceFeed;\n ISplitLiquidationCollateral splitLiquidation;\n bool isEnabled;\n uint256 lastFeeOperationTime;\n uint256 borrowingSpread;\n uint256 baseRate;\n uint256 redemptionSpread;\n uint256 redemptionRebate;\n }\n\n // --- Events ---\n\n /// @dev New position manager has been token deployed.\n /// @param rToken The R token used by the position manager.\n /// @param feeRecipient The address of fee recipient.\n event PositionManagerDeployed(IRToken rToken, address feeRecipient);\n\n /// @dev New collateral token has been added added to the system.\n /// @param collateralToken The token used as collateral.\n /// @param raftCollateralToken The Raft indexable collateral token for the given collateral token.\n /// @param raftDebtToken The Raft indexable debt token for given collateral token.\n /// @param priceFeed The contract that provides price for the collateral token.\n event CollateralTokenAdded(\n IERC20 collateralToken,\n IERC20Indexable raftCollateralToken,\n IERC20Indexable raftDebtToken,\n IPriceFeed priceFeed\n );\n\n /// @dev Collateral token has been enabled or disabled.\n /// @param collateralToken The token used as collateral.\n /// @param isEnabled True if the token is enabled, false otherwise.\n event CollateralTokenModified(IERC20 collateralToken, bool isEnabled);\n\n /// @dev A delegate has been whitelisted for a certain position.\n /// @param position The position for which the delegate was whitelisted.\n /// @param delegate The delegate which was whitelisted.\n /// @param whitelisted Specifies whether the delegate whitelisting has been enabled (true) or disabled (false).\n event DelegateWhitelisted(address indexed position, address indexed delegate, bool whitelisted);\n\n /// @dev New position has been created.\n /// @param position The address of the user opening new position.\n /// @param collateralToken The token used as collateral for the created position.\n event PositionCreated(address indexed position, IERC20 indexed collateralToken);\n\n /// @dev The position has been closed by either repayment, liquidation, or redemption.\n /// @param position The address of the user whose position is closed.\n event PositionClosed(address indexed position);\n\n /// @dev Collateral amount for the position has been changed.\n /// @param position The address of the user that has opened the position.\n /// @param collateralToken The address of the collateral token being added to position.\n /// @param collateralAmount The amount of collateral added or removed.\n /// @param isCollateralIncrease Whether the collateral is added to the position or removed from it.\n event CollateralChanged(\n address indexed position, IERC20 indexed collateralToken, uint256 collateralAmount, bool isCollateralIncrease\n );\n\n /// @dev Debt amount for position has been changed.\n /// @param position The address of the user that has opened the position.\n /// @param collateralToken The address of the collateral token backing the debt.\n /// @param debtAmount The amount of debt added or removed.\n /// @param isDebtIncrease Whether the debt is added to the position or removed from it.\n event DebtChanged(\n address indexed position, IERC20 indexed collateralToken, uint256 debtAmount, bool isDebtIncrease\n );\n\n /// @dev Borrowing fee has been paid. Emitted only if the actual fee was paid - doesn't happen with no fees are\n /// paid.\n /// @param collateralToken Collateral token used to mint R.\n /// @param position The address of position's owner that triggered the fee payment.\n /// @param feeAmount The amount of tokens paid as the borrowing fee.\n event RBorrowingFeePaid(IERC20 collateralToken, address indexed position, uint256 feeAmount);\n\n /// @dev Liquidation has been executed.\n /// @param liquidator The liquidator that executed the liquidation.\n /// @param position The address of position's owner whose position was liquidated.\n /// @param collateralToken The collateral token used for the liquidation.\n /// @param debtLiquidated The total debt that was liquidated or redistributed.\n /// @param collateralLiquidated The total collateral liquidated.\n /// @param collateralSentToLiquidator The collateral amount sent to the liquidator.\n /// @param collateralLiquidationFeePaid The total collateral paid as the liquidation fee to the fee recipient.\n /// @param isRedistribution Whether the executed liquidation was redistribution or not.\n event Liquidation(\n address indexed liquidator,\n address indexed position,\n IERC20 indexed collateralToken,\n uint256 debtLiquidated,\n uint256 collateralLiquidated,\n uint256 collateralSentToLiquidator,\n uint256 collateralLiquidationFeePaid,\n bool isRedistribution\n );\n\n /// @dev Redemption has been executed.\n /// @param redeemer User that redeemed R.\n /// @param amount Amount of R that was redeemed.\n /// @param collateralSent The amount of collateral sent to the redeemer.\n /// @param fee The amount of fee paid to the fee recipient.\n /// @param rebate Redemption rebate amount.\n event Redemption(address indexed redeemer, uint256 amount, uint256 collateralSent, uint256 fee, uint256 rebate);\n\n /// @dev Borrowing spread has been updated.\n /// @param borrowingSpread The new borrowing spread.\n event BorrowingSpreadUpdated(uint256 borrowingSpread);\n\n /// @dev Redemption rebate has been updated.\n /// @param redemptionRebate The new redemption rebate.\n event RedemptionRebateUpdated(uint256 redemptionRebate);\n\n /// @dev Redemption spread has been updated.\n /// @param collateralToken Collateral token that the spread was set for.\n /// @param redemptionSpread The new redemption spread.\n event RedemptionSpreadUpdated(IERC20 collateralToken, uint256 redemptionSpread);\n\n /// @dev Base rate has been updated.\n /// @param collateralToken Collateral token that the baser rate was updated for.\n /// @param baseRate The new base rate.\n event BaseRateUpdated(IERC20 collateralToken, uint256 baseRate);\n\n /// @dev Last fee operation time has been updated.\n /// @param collateralToken Collateral token that the baser rate was updated for.\n /// @param lastFeeOpTime The new operation time.\n event LastFeeOpTimeUpdated(IERC20 collateralToken, uint256 lastFeeOpTime);\n\n /// @dev Split liquidation collateral has been changed.\n /// @param collateralToken Collateral token whose split liquidation collateral contract is set.\n /// @param newSplitLiquidationCollateral New value that was set to be split liquidation collateral.\n event SplitLiquidationCollateralChanged(\n IERC20 collateralToken, ISplitLiquidationCollateral indexed newSplitLiquidationCollateral\n );\n\n // --- Errors ---\n\n /// @dev Max fee percentage must be between borrowing spread and 100%.\n error InvalidMaxFeePercentage();\n\n /// @dev Max fee percentage must be between 0.5% and 100%.\n error MaxFeePercentageOutOfRange();\n\n /// @dev Amount is zero.\n error AmountIsZero();\n\n /// @dev Nothing to liquidate.\n error NothingToLiquidate();\n\n /// @dev Cannot liquidate last position.\n error CannotLiquidateLastPosition();\n\n /// @dev Cannot redeem collateral below minimum debt threshold.\n /// @param collateralToken Collateral token used to redeem.\n /// @param newTotalDebt New total debt backed by collateral, which is lower than minimum debt.\n error TotalDebtCannotBeLowerThanMinDebt(IERC20 collateralToken, uint256 newTotalDebt);\n\n /// @dev Cannot redeem collateral\n /// @param collateralToken Collateral token used to redeem.\n /// @param newTotalCollateral New total collateral, which is lower than minimum collateral.\n /// @param minimumCollateral Minimum collateral required to complete redeem\n error TotalCollateralCannotBeLowerThanMinCollateral(\n IERC20 collateralToken, uint256 newTotalCollateral, uint256 minimumCollateral\n );\n\n /// @dev Fee would eat up all returned collateral.\n error FeeEatsUpAllReturnedCollateral();\n\n /// @dev Borrowing spread exceeds maximum.\n error BorrowingSpreadExceedsMaximum();\n\n /// @dev Redemption rebate exceeds maximum.\n error RedemptionRebateExceedsMaximum();\n\n /// @dev Redemption spread is out of allowed range.\n error RedemptionSpreadOutOfRange();\n\n /// @dev There must be either a collateral change or a debt change.\n error NoCollateralOrDebtChange();\n\n /// @dev There is some collateral for position that doesn't have debt.\n error InvalidPosition();\n\n /// @dev An operation that would result in ICR < MCR is not permitted.\n /// @param newICR Resulting ICR that is below MCR.\n error NewICRLowerThanMCR(uint256 newICR);\n\n /// @dev Position's net debt must be greater than minimum.\n /// @param netDebt Net debt amount that is below minimum.\n error NetDebtBelowMinimum(uint256 netDebt);\n\n /// @dev The provided delegate address is invalid.\n error InvalidDelegateAddress();\n\n /// @dev A non-whitelisted delegate cannot adjust positions.\n error DelegateNotWhitelisted();\n\n /// @dev Fee exceeded provided maximum fee percentage.\n /// @param fee The fee amount.\n /// @param amount The amount of debt or collateral.\n /// @param maxFeePercentage The maximum fee percentage.\n error FeeExceedsMaxFee(uint256 fee, uint256 amount, uint256 maxFeePercentage);\n\n /// @dev Borrower uses a different collateral token already.\n error PositionCollateralTokenMismatch();\n\n /// @dev Collateral token address cannot be zero.\n error CollateralTokenAddressCannotBeZero();\n\n /// @dev Price feed address cannot be zero.\n error PriceFeedAddressCannotBeZero();\n\n /// @dev Collateral token already added.\n error CollateralTokenAlreadyAdded();\n\n /// @dev Collateral token is not added.\n error CollateralTokenNotAdded();\n\n /// @dev Collateral token is not enabled.\n error CollateralTokenDisabled();\n\n /// @dev Split liquidation collateral cannot be zero.\n error SplitLiquidationCollateralCannotBeZero();\n\n /// @dev Cannot change collateral in case of repaying the whole debt.\n error WrongCollateralParamsForFullRepayment();\n\n // --- Functions ---\n\n /// @return The R token used by position manager.\n function rToken() external view returns (IRToken);\n\n /// @dev Retrieves information about certain collateral type.\n /// @param collateralToken The token used as collateral.\n /// @return raftCollateralToken The Raft indexable collateral token.\n /// @return raftDebtToken The Raft indexable debt token.\n /// @return priceFeed The contract that provides a price for the collateral token.\n /// @return splitLiquidation The contract that calculates collateral split in case of liquidation.\n /// @return isEnabled Whether the collateral token can be used as collateral or not.\n /// @return lastFeeOperationTime Timestamp of the last operation for the collateral token.\n /// @return borrowingSpread The current borrowing spread.\n /// @return baseRate The current base rate.\n /// @return redemptionSpread The current redemption spread.\n /// @return redemptionRebate Percentage of the redemption fee returned to redeemed positions.\n function collateralInfo(IERC20 collateralToken)\n external\n view\n returns (\n IERC20Indexable raftCollateralToken,\n IERC20Indexable raftDebtToken,\n IPriceFeed priceFeed,\n ISplitLiquidationCollateral splitLiquidation,\n bool isEnabled,\n uint256 lastFeeOperationTime,\n uint256 borrowingSpread,\n uint256 baseRate,\n uint256 redemptionSpread,\n uint256 redemptionRebate\n );\n\n /// @param collateralToken Collateral token whose raft collateral indexable token is being queried.\n /// @return Raft collateral token address for given collateral token.\n function raftCollateralToken(IERC20 collateralToken) external view returns (IERC20Indexable);\n\n /// @param collateralToken Collateral token whose raft collateral indexable token is being queried.\n /// @return Raft debt token address for given collateral token.\n function raftDebtToken(IERC20 collateralToken) external view returns (IERC20Indexable);\n\n /// @param collateralToken Collateral token whose price feed contract is being queried.\n /// @return Price feed contract address for given collateral token.\n function priceFeed(IERC20 collateralToken) external view returns (IPriceFeed);\n\n /// @param collateralToken Collateral token whose split liquidation collateral is being queried.\n /// @return Returns address of the split liquidation collateral contract.\n function splitLiquidationCollateral(IERC20 collateralToken) external view returns (ISplitLiquidationCollateral);\n\n /// @param collateralToken Collateral token whose split liquidation collateral is being queried.\n /// @return Returns whether collateral is enabled or nor.\n function collateralEnabled(IERC20 collateralToken) external view returns (bool);\n\n /// @param collateralToken Collateral token we query last operation time fee for.\n /// @return The timestamp of the latest fee operation (redemption or new R issuance).\n function lastFeeOperationTime(IERC20 collateralToken) external view returns (uint256);\n\n /// @param collateralToken Collateral token we query borrowing spread for.\n /// @return The current borrowing spread.\n function borrowingSpread(IERC20 collateralToken) external view returns (uint256);\n\n /// @param collateralToken Collateral token we query base rate for.\n /// @return rate The base rate.\n function baseRate(IERC20 collateralToken) external view returns (uint256 rate);\n\n /// @param collateralToken Collateral token we query redemption spread for.\n /// @return The current redemption spread for collateral token.\n function redemptionSpread(IERC20 collateralToken) external view returns (uint256);\n\n /// @param collateralToken Collateral token we query redemption rebate for.\n /// @return rebate Percentage of the redemption fee returned to redeemed positions.\n function redemptionRebate(IERC20 collateralToken) external view returns (uint256);\n\n /// @param collateralToken Collateral token we query redemption rate for.\n /// @return rate The current redemption rate for collateral token.\n function getRedemptionRate(IERC20 collateralToken) external view returns (uint256 rate);\n\n /// @dev Returns the collateral token that a given position used for their position.\n /// @param position The address of the borrower.\n /// @return collateralToken The collateral token of the borrower's position.\n function collateralTokenForPosition(address position) external view returns (IERC20 collateralToken);\n\n /// @dev Adds a new collateral token to the protocol.\n /// @param collateralToken The new collateral token.\n /// @param priceFeed The price feed for the collateral token.\n /// @param newSplitLiquidationCollateral split liquidation collateral contract address.\n function addCollateralToken(\n IERC20 collateralToken,\n IPriceFeed priceFeed,\n ISplitLiquidationCollateral newSplitLiquidationCollateral\n )\n external;\n\n /// @dev Adds a new collateral token to the protocol.\n /// @param collateralToken The new collateral token.\n /// @param priceFeed The price feed for the collateral token.\n /// @param newSplitLiquidationCollateral split liquidation collateral contract address.\n /// @param raftCollateralToken_ Address of raft collateral token.\n /// @param raftDebtToken_ Address of raft debt token.\n function addCollateralToken(\n IERC20 collateralToken,\n IPriceFeed priceFeed,\n ISplitLiquidationCollateral newSplitLiquidationCollateral,\n IERC20Indexable raftCollateralToken_,\n IERC20Indexable raftDebtToken_\n )\n external;\n\n /// @dev Enables or disables a collateral token. Reverts if the collateral token has not been added.\n /// @param collateralToken The collateral token.\n /// @param isEnabled Whether the collateral token can be used as collateral or not.\n function setCollateralEnabled(IERC20 collateralToken, bool isEnabled) external;\n\n /// @dev Sets the new split liquidation collateral contract.\n /// @param collateralToken Collateral token whose split liquidation collateral is being set.\n /// @param newSplitLiquidationCollateral New split liquidation collateral contract address.\n function setSplitLiquidationCollateral(\n IERC20 collateralToken,\n ISplitLiquidationCollateral newSplitLiquidationCollateral\n )\n external;\n\n /// @dev Liquidates the borrower if its position's ICR is lower than the minimum collateral ratio.\n /// @param position The address of the borrower.\n function liquidate(address position) external;\n\n /// @dev Redeems the collateral token for a given debt amount. It sends @param debtAmount R to the system and\n /// redeems the corresponding amount of collateral from as many positions as are needed to fill the redemption\n /// request.\n /// @param collateralToken The token used as collateral.\n /// @param debtAmount The amount of debt to be redeemed. Must be greater than zero.\n /// @param maxFeePercentage The maximum fee percentage to pay for the redemption.\n function redeemCollateral(IERC20 collateralToken, uint256 debtAmount, uint256 maxFeePercentage) external;\n\n /// @dev Manages the position on behalf of a given borrower.\n /// @param collateralToken The token the borrower used as collateral.\n /// @param position The address of the borrower.\n /// @param collateralChange The amount of collateral to add or remove.\n /// @param isCollateralIncrease True if the collateral is being increased, false otherwise.\n /// @param debtChange The amount of R to add or remove. In case of repayment (isDebtIncrease = false)\n /// `type(uint256).max` value can be used to repay the whole outstanding loan.\n /// @param isDebtIncrease True if the debt is being increased, false otherwise.\n /// @param maxFeePercentage The maximum fee percentage to pay for the position management.\n /// @param permitSignature Optional permit signature for tokens that support IERC20Permit interface.\n /// @notice `permitSignature` it is ignored if permit signature is not for `collateralToken`.\n /// @notice In case of full debt repayment, `isCollateralIncrease` is ignored and `collateralChange` must be 0.\n /// These values are set to `false`(collateral decrease), and the whole collateral balance of the user.\n /// @return actualCollateralChange Actual amount of collateral added/removed.\n /// Can be different to `collateralChange` in case of full repayment.\n /// @return actualDebtChange Actual amount of debt added/removed.\n /// Can be different to `debtChange` in case of passing type(uint256).max as `debtChange`.\n function managePosition(\n IERC20 collateralToken,\n address position,\n uint256 collateralChange,\n bool isCollateralIncrease,\n uint256 debtChange,\n bool isDebtIncrease,\n uint256 maxFeePercentage,\n ERC20PermitSignature calldata permitSignature\n )\n external\n returns (uint256 actualCollateralChange, uint256 actualDebtChange);\n\n /// @return The max borrowing spread.\n function MAX_BORROWING_SPREAD() external view returns (uint256);\n\n /// @return The max borrowing rate.\n function MAX_BORROWING_RATE() external view returns (uint256);\n\n /// @dev Sets the new borrowing spread.\n /// @param collateralToken Collateral token we set borrowing spread for.\n /// @param newBorrowingSpread New borrowing spread to be used.\n function setBorrowingSpread(IERC20 collateralToken, uint256 newBorrowingSpread) external;\n\n /// @param collateralToken Collateral token we query borrowing rate for.\n /// @return The current borrowing rate.\n function getBorrowingRate(IERC20 collateralToken) external view returns (uint256);\n\n /// @param collateralToken Collateral token we query borrowing rate with decay for.\n /// @return The current borrowing rate with decay.\n function getBorrowingRateWithDecay(IERC20 collateralToken) external view returns (uint256);\n\n /// @dev Returns the borrowing fee for a given debt amount.\n /// @param collateralToken Collateral token we query borrowing fee for.\n /// @param debtAmount The amount of debt.\n /// @return The borrowing fee.\n function getBorrowingFee(IERC20 collateralToken, uint256 debtAmount) external view returns (uint256);\n\n /// @dev Sets the new redemption spread.\n /// @param newRedemptionSpread New redemption spread to be used.\n function setRedemptionSpread(IERC20 collateralToken, uint256 newRedemptionSpread) external;\n\n /// @dev Sets new redemption rebate percentage.\n /// @param newRedemptionRebate Value that is being set as a redemption rebate percentage.\n function setRedemptionRebate(IERC20 collateralToken, uint256 newRedemptionRebate) external;\n\n /// @param collateralToken Collateral token we query redemption rate with decay for.\n /// @return The current redemption rate with decay.\n function getRedemptionRateWithDecay(IERC20 collateralToken) external view returns (uint256);\n\n /// @dev Returns the redemption fee for a given collateral amount.\n /// @param collateralToken Collateral token we query redemption fee for.\n /// @param collateralAmount The amount of collateral.\n /// @param priceDeviation Deviation for the reported price by oracle in percentage.\n /// @return The redemption fee.\n function getRedemptionFee(\n IERC20 collateralToken,\n uint256 collateralAmount,\n uint256 priceDeviation\n )\n external\n view\n returns (uint256);\n\n /// @dev Returns the redemption fee with decay for a given collateral amount.\n /// @param collateralToken Collateral token we query redemption fee with decay for.\n /// @param collateralAmount The amount of collateral.\n /// @return The redemption fee with decay.\n function getRedemptionFeeWithDecay(\n IERC20 collateralToken,\n uint256 collateralAmount\n )\n external\n view\n returns (uint256);\n\n /// @return Half-life of 12h (720 min).\n /// @dev (1/2) = d^720 => d = (1/2)^(1/720)\n function MINUTE_DECAY_FACTOR() external view returns (uint256);\n\n /// @dev Returns if a given delegate is whitelisted for a given borrower.\n /// @param position The address of the borrower.\n /// @param delegate The address of the delegate.\n /// @return isWhitelisted True if the delegate is whitelisted for a given borrower, false otherwise.\n function isDelegateWhitelisted(address position, address delegate) external view returns (bool isWhitelisted);\n\n /// @dev Whitelists a delegate.\n /// @param delegate The address of the delegate.\n /// @param whitelisted True if delegate is being whitelisted, false otherwise.\n function whitelistDelegate(address delegate, bool whitelisted) external;\n\n /// @return Parameter by which to divide the redeemed fraction, in order to calc the new base rate from a\n /// redemption. Corresponds to (1 / ALPHA) in the white paper.\n function BETA() external view returns (uint256);\n}\n\n/// @dev Common interface for the Position Manager.\ninterface IInterestRatePositionManager is IPositionManager {\n // --- Events ---\n\n /// @dev Fees coming from accrued interest are minted.\n /// @param collateralToken Collateral token that fees are paid for.\n /// @param amount Amount of R minted.\n event MintedFees(IERC20 collateralToken, uint256 amount);\n\n // --- Errors ---\n\n /// @dev Only registered debt token can be caller.\n /// @param sender Actual caller.\n error InvalidDebtToken(address sender);\n\n // --- Functions ---\n\n /// @dev Mints fees coming from accrued interest. Can be called only from matching debt token.\n /// @param collateralToken Collateral token to mint fees for.\n /// @param amount Amount of R to mint.\n function mintFees(IERC20 collateralToken, uint256 amount) external;\n}\n\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n enum Rounding {\n Down, // Toward negative infinity\n Up, // Toward infinity\n Zero // Toward zero\n }\n\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a > b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a == 0 ? 0 : (a - 1) / b + 1;\n }\n\n /**\n * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\n * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)\n * with further edits by Uniswap Labs also under MIT license.\n */\n function mulDiv(\n uint256 x,\n uint256 y,\n uint256 denominator\n ) internal pure returns (uint256 result) {\n unchecked {\n // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use\n // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n // variables such that product = prod1 * 2^256 + prod0.\n uint256 prod0; // Least significant 256 bits of the product\n uint256 prod1; // Most significant 256 bits of the product\n assembly {\n let mm := mulmod(x, y, not(0))\n prod0 := mul(x, y)\n prod1 := sub(sub(mm, prod0), lt(mm, prod0))\n }\n\n // Handle non-overflow cases, 256 by 256 division.\n if (prod1 == 0) {\n return prod0 / denominator;\n }\n\n // Make sure the result is less than 2^256. Also prevents denominator == 0.\n require(denominator > prod1);\n\n ///////////////////////////////////////////////\n // 512 by 256 division.\n ///////////////////////////////////////////////\n\n // Make division exact by subtracting the remainder from [prod1 prod0].\n uint256 remainder;\n assembly {\n // Compute remainder using mulmod.\n remainder := mulmod(x, y, denominator)\n\n // Subtract 256 bit number from 512 bit number.\n prod1 := sub(prod1, gt(remainder, prod0))\n prod0 := sub(prod0, remainder)\n }\n\n // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.\n // See https://cs.stackexchange.com/q/138556/92363.\n\n // Does not overflow because the denominator cannot be zero at this stage in the function.\n uint256 twos = denominator & (~denominator + 1);\n assembly {\n // Divide denominator by twos.\n denominator := div(denominator, twos)\n\n // Divide [prod1 prod0] by twos.\n prod0 := div(prod0, twos)\n\n // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.\n twos := add(div(sub(0, twos), twos), 1)\n }\n\n // Shift in bits from prod1 into prod0.\n prod0 |= prod1 * twos;\n\n // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such\n // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for\n // four bits. That is, denominator * inv = 1 mod 2^4.\n uint256 inverse = (3 * denominator) ^ 2;\n\n // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works\n // in modular arithmetic, doubling the correct bits in each step.\n inverse *= 2 - denominator * inverse; // inverse mod 2^8\n inverse *= 2 - denominator * inverse; // inverse mod 2^16\n inverse *= 2 - denominator * inverse; // inverse mod 2^32\n inverse *= 2 - denominator * inverse; // inverse mod 2^64\n inverse *= 2 - denominator * inverse; // inverse mod 2^128\n inverse *= 2 - denominator * inverse; // inverse mod 2^256\n\n // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is\n // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1\n // is no longer required.\n result = prod0 * inverse;\n return result;\n }\n }\n\n /**\n * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.\n */\n function mulDiv(\n uint256 x,\n uint256 y,\n uint256 denominator,\n Rounding rounding\n ) internal pure returns (uint256) {\n uint256 result = mulDiv(x, y, denominator);\n if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {\n result += 1;\n }\n return result;\n }\n\n /**\n * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.\n *\n * Inspired by Henry S. Warren, Jr.'s \"Hacker's Delight\" (Chapter 11).\n */\n function sqrt(uint256 a) internal pure returns (uint256) {\n if (a == 0) {\n return 0;\n }\n\n // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.\n //\n // We know that the \"msb\" (most significant bit) of our target number `a` is a power of 2 such that we have\n // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.\n //\n // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`\n // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`\n // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`\n //\n // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.\n uint256 result = 1 << (log2(a) >> 1);\n\n // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,\n // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at\n // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision\n // into the expected uint128 result.\n unchecked {\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n return min(result, a / result);\n }\n }\n\n /**\n * @notice Calculates sqrt(a), following the selected rounding direction.\n */\n function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = sqrt(a);\n return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);\n }\n }\n\n /**\n * @dev Return the log in base 2, rounded down, of a positive value.\n * Returns 0 if given 0.\n */\n function log2(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >> 128 > 0) {\n value >>= 128;\n result += 128;\n }\n if (value >> 64 > 0) {\n value >>= 64;\n result += 64;\n }\n if (value >> 32 > 0) {\n value >>= 32;\n result += 32;\n }\n if (value >> 16 > 0) {\n value >>= 16;\n result += 16;\n }\n if (value >> 8 > 0) {\n value >>= 8;\n result += 8;\n }\n if (value >> 4 > 0) {\n value >>= 4;\n result += 4;\n }\n if (value >> 2 > 0) {\n value >>= 2;\n result += 2;\n }\n if (value >> 1 > 0) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log2(value);\n return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);\n }\n }\n\n /**\n * @dev Return the log in base 10, rounded down, of a positive value.\n * Returns 0 if given 0.\n */\n function log10(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >= 10**64) {\n value /= 10**64;\n result += 64;\n }\n if (value >= 10**32) {\n value /= 10**32;\n result += 32;\n }\n if (value >= 10**16) {\n value /= 10**16;\n result += 16;\n }\n if (value >= 10**8) {\n value /= 10**8;\n result += 8;\n }\n if (value >= 10**4) {\n value /= 10**4;\n result += 4;\n }\n if (value >= 10**2) {\n value /= 10**2;\n result += 2;\n }\n if (value >= 10**1) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log10(value);\n return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);\n }\n }\n\n /**\n * @dev Return the log in base 256, rounded down, of a positive value.\n * Returns 0 if given 0.\n *\n * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n */\n function log256(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >> 128 > 0) {\n value >>= 128;\n result += 16;\n }\n if (value >> 64 > 0) {\n value >>= 64;\n result += 8;\n }\n if (value >> 32 > 0) {\n value >>= 32;\n result += 4;\n }\n if (value >> 16 > 0) {\n value >>= 16;\n result += 2;\n }\n if (value >> 8 > 0) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log256(value);\n return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);\n }\n }\n}\n\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20Metadata is IERC20 {\n /**\n * @dev Returns the name of the token.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the symbol of the token.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the decimals places of the token.\n */\n function decimals() external view returns (uint8);\n}\n\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)\n\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\n * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\n *\n * _Available since v4.8._\n */\n function verifyCallResultFromTarget(\n address target,\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n if (success) {\n if (returndata.length == 0) {\n // only check isContract if the call was successful and the return data is empty\n // otherwise we already know that it was a contract\n require(isContract(target), \"Address: call to non-contract\");\n }\n return returndata;\n } else {\n _revert(returndata, errorMessage);\n }\n }\n\n /**\n * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason or using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n _revert(returndata, errorMessage);\n }\n }\n\n function _revert(bytes memory returndata, string memory errorMessage) private pure {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n /// @solidity memory-safe-assembly\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n}\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n function safePermit(\n IERC20Permit token,\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal {\n uint256 nonceBefore = token.nonces(owner);\n token.permit(owner, spender, value, deadline, v, r, s);\n uint256 nonceAfter = token.nonces(owner);\n require(nonceAfter == nonceBefore + 1, \"SafeERC20: permit did not succeed\");\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n\nlibrary Fixed256x18 {\n uint256 internal constant ONE = 1e18; // 18 decimal places\n\n function mulDown(uint256 a, uint256 b) internal pure returns (uint256) {\n return (a * b) / ONE;\n }\n\n function mulUp(uint256 a, uint256 b) internal pure returns (uint256) {\n uint256 product = a * b;\n\n if (product == 0) {\n return 0;\n } else {\n return ((product - 1) / ONE) + 1;\n }\n }\n\n function divDown(uint256 a, uint256 b) internal pure returns (uint256) {\n return (a * ONE) / b;\n }\n\n function divUp(uint256 a, uint256 b) internal pure returns (uint256) {\n if (a == 0) {\n return 0;\n } else {\n return (((a * ONE) - 1) / b) + 1;\n }\n }\n\n function complement(uint256 x) internal pure returns (uint256) {\n return (x < ONE) ? (ONE - x) : 0;\n }\n}\n\nlibrary MathUtils {\n // --- Constants ---\n\n /// @notice Represents 100%.\n /// @dev 1e18 is the scaling factor (100% == 1e18).\n uint256 public constant _100_PERCENT = Fixed256x18.ONE;\n\n /// @notice Precision for Nominal ICR (independent of price).\n /// @dev Rationale for the value:\n /// - Making it “too high” could lead to overflows.\n /// - Making it “too low” could lead to an ICR equal to zero, due to truncation from floor division.\n ///\n /// This value of 1e20 is chosen for safety: the NICR will only overflow for numerator > ~1e39 collateralToken,\n /// and will only truncate to 0 if the denominator is at least 1e20 times greater than the numerator.\n uint256 internal constant _NICR_PRECISION = 1e20;\n\n /// @notice Number of minutes in 1000 years.\n uint256 internal constant _MINUTES_IN_1000_YEARS = 1000 * 365 days / 1 minutes;\n\n // --- Functions ---\n\n /// @notice Multiplies two decimal numbers and use normal rounding rules:\n /// - round product up if 19'th mantissa digit >= 5\n /// - round product down if 19'th mantissa digit < 5.\n /// @param x First number.\n /// @param y Second number.\n function _decMul(uint256 x, uint256 y) internal pure returns (uint256 decProd) {\n decProd = (x * y + Fixed256x18.ONE / 2) / Fixed256x18.ONE;\n }\n\n /// @notice Exponentiation function for 18-digit decimal base, and integer exponent n.\n ///\n /// @dev Uses the efficient \"exponentiation by squaring\" algorithm. O(log(n)) complexity. The exponent is capped to\n /// avoid reverting due to overflow.\n ///\n /// If a period of > 1000 years is ever used as an exponent in either of the above functions, the result will be\n /// negligibly different from just passing the cap, since the decayed base rate will be 0 for 1000 years or > 1000\n /// years.\n /// @param base The decimal base.\n /// @param exponent The exponent.\n /// @return The result of the exponentiation.\n function _decPow(uint256 base, uint256 exponent) internal pure returns (uint256) {\n if (exponent == 0) {\n return Fixed256x18.ONE;\n }\n\n uint256 y = Fixed256x18.ONE;\n uint256 x = base;\n uint256 n = Math.min(exponent, _MINUTES_IN_1000_YEARS); // cap to avoid overflow\n\n // Exponentiation-by-squaring\n while (n > 1) {\n if (n % 2 != 0) {\n y = _decMul(x, y);\n }\n x = _decMul(x, x);\n n /= 2;\n }\n\n return _decMul(x, y);\n }\n\n /// @notice Computes the Nominal Individual Collateral Ratio (NICR) for given collateral and debt. If debt is zero,\n /// it returns the maximal value for uint256 (represents \"infinite\" CR).\n /// @param collateral Collateral amount.\n /// @param debt Debt amount.\n /// @return NICR.\n function _computeNominalCR(uint256 collateral, uint256 debt) internal pure returns (uint256) {\n return debt > 0 ? collateral * _NICR_PRECISION / debt : type(uint256).max;\n }\n\n /// @notice Computes the Collateral Ratio for given collateral, debt and price. If debt is zero, it returns the\n /// maximal value for uint256 (represents \"infinite\" CR).\n /// @param collateral Collateral amount.\n /// @param debt Debt amount.\n /// @param price Collateral price.\n /// @return Collateral ratio.\n function _computeCR(uint256 collateral, uint256 debt, uint256 price) internal pure returns (uint256) {\n return debt > 0 ? collateral * price / debt : type(uint256).max;\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol)\n\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\n * instead returning `false` on failure. This behavior is nonetheless\n * conventional and does not conflict with the expectations of ERC20\n * applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20 is Context, IERC20, IERC20Metadata {\n mapping(address => uint256) private _balances;\n\n mapping(address => mapping(address => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n string private _name;\n string private _symbol;\n\n /**\n * @dev Sets the values for {name} and {symbol}.\n *\n * The default value of {decimals} is 18. To select a different value for\n * {decimals} you should overload it.\n *\n * All two of these values are immutable: they can only be set once during\n * construction.\n */\n constructor(string memory name_, string memory symbol_) {\n _name = name_;\n _symbol = symbol_;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5.05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei. This is the value {ERC20} uses, unless this function is\n * overridden;\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view virtual override returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view virtual override returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `to` cannot be the zero address.\n * - the caller must have a balance of at least `amount`.\n */\n function transfer(address to, uint256 amount) public virtual override returns (bool) {\n address owner = _msgSender();\n _transfer(owner, to, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on\n * `transferFrom`. This is semantically equivalent to an infinite approval.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\n address owner = _msgSender();\n _approve(owner, spender, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20}.\n *\n * NOTE: Does not update the allowance if the current allowance\n * is the maximum `uint256`.\n *\n * Requirements:\n *\n * - `from` and `to` cannot be the zero address.\n * - `from` must have a balance of at least `amount`.\n * - the caller must have allowance for ``from``'s tokens of at least\n * `amount`.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) public virtual override returns (bool) {\n address spender = _msgSender();\n _spendAllowance(from, spender, amount);\n _transfer(from, to, amount);\n return true;\n }\n\n /**\n * @dev Atomically increases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n address owner = _msgSender();\n _approve(owner, spender, allowance(owner, spender) + addedValue);\n return true;\n }\n\n /**\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `spender` must have allowance for the caller of at least\n * `subtractedValue`.\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n address owner = _msgSender();\n uint256 currentAllowance = allowance(owner, spender);\n require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\n unchecked {\n _approve(owner, spender, currentAllowance - subtractedValue);\n }\n\n return true;\n }\n\n /**\n * @dev Moves `amount` of tokens from `from` to `to`.\n *\n * This internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `from` must have a balance of at least `amount`.\n */\n function _transfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {\n require(from != address(0), \"ERC20: transfer from the zero address\");\n require(to != address(0), \"ERC20: transfer to the zero address\");\n\n _beforeTokenTransfer(from, to, amount);\n\n uint256 fromBalance = _balances[from];\n require(fromBalance >= amount, \"ERC20: transfer amount exceeds balance\");\n unchecked {\n _balances[from] = fromBalance - amount;\n // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by\n // decrementing then incrementing.\n _balances[to] += amount;\n }\n\n emit Transfer(from, to, amount);\n\n _afterTokenTransfer(from, to, amount);\n }\n\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n */\n function _mint(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: mint to the zero address\");\n\n _beforeTokenTransfer(address(0), account, amount);\n\n _totalSupply += amount;\n unchecked {\n // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.\n _balances[account] += amount;\n }\n emit Transfer(address(0), account, amount);\n\n _afterTokenTransfer(address(0), account, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`, reducing the\n * total supply.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n */\n function _burn(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: burn from the zero address\");\n\n _beforeTokenTransfer(account, address(0), amount);\n\n uint256 accountBalance = _balances[account];\n require(accountBalance >= amount, \"ERC20: burn amount exceeds balance\");\n unchecked {\n _balances[account] = accountBalance - amount;\n // Overflow not possible: amount <= accountBalance <= totalSupply.\n _totalSupply -= amount;\n }\n\n emit Transfer(account, address(0), amount);\n\n _afterTokenTransfer(account, address(0), amount);\n }\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n *\n * This internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n */\n function _approve(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n require(owner != address(0), \"ERC20: approve from the zero address\");\n require(spender != address(0), \"ERC20: approve to the zero address\");\n\n _allowances[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n /**\n * @dev Updates `owner` s allowance for `spender` based on spent `amount`.\n *\n * Does not update the allowance amount in case of infinite allowance.\n * Revert if not enough allowance is available.\n *\n * Might emit an {Approval} event.\n */\n function _spendAllowance(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n uint256 currentAllowance = allowance(owner, spender);\n if (currentAllowance != type(uint256).max) {\n require(currentAllowance >= amount, \"ERC20: insufficient allowance\");\n unchecked {\n _approve(owner, spender, currentAllowance - amount);\n }\n }\n }\n\n /**\n * @dev Hook that is called before any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * will be transferred to `to`.\n * - when `from` is zero, `amount` tokens will be minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n\n /**\n * @dev Hook that is called after any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * has been transferred to `to`.\n * - when `from` is zero, `amount` tokens have been minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _afterTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n}\n\n// OpenZeppelin Contracts (last updated v4.8.0) (access/Ownable2Step.sol)\n\n// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n _checkOwner();\n _;\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if the sender is not the owner.\n */\n function _checkOwner() internal view virtual {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n\n/**\n * @dev Contract module which provides access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership} and {acceptOwnership}.\n *\n * This module is used through inheritance. It will make available all functions\n * from parent (Ownable).\n */\nabstract contract Ownable2Step is Ownable {\n address private _pendingOwner;\n\n event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Returns the address of the pending owner.\n */\n function pendingOwner() public view virtual returns (address) {\n return _pendingOwner;\n }\n\n /**\n * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual override onlyOwner {\n _pendingOwner = newOwner;\n emit OwnershipTransferStarted(owner(), newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual override {\n delete _pendingOwner;\n super._transferOwnership(newOwner);\n }\n\n /**\n * @dev The new owner accepts the ownership transfer.\n */\n function acceptOwnership() external {\n address sender = _msgSender();\n require(pendingOwner() == sender, \"Ownable2Step: caller is not the new owner\");\n _transferOwnership(sender);\n }\n}\n\n/**\n * @dev Extension of {ERC20} that adds a cap to the supply of tokens.\n */\nabstract contract ERC20Capped is ERC20, Ownable2Step {\n uint256 public cap;\n\n /**\n * @dev Total supply cap has been exceeded.\n */\n error ERC20ExceededCap();\n\n /**\n * @dev The supplied cap is not a valid cap.\n */\n error ERC20InvalidCap(uint256 cap);\n\n constructor(uint256 cap_) {\n setCap(cap_);\n }\n\n /**\n * @dev Sets the value of the `cap`.\n */\n function setCap(uint256 cap_) public onlyOwner {\n if (cap_ == 0) {\n revert ERC20InvalidCap(0);\n }\n cap = cap_;\n }\n\n /**\n * @dev See {ERC20-_mint}.\n */\n function _mint(address account, uint256 amount) internal virtual override {\n if (totalSupply() + amount > cap) {\n revert ERC20ExceededCap();\n }\n super._mint(account, amount);\n }\n}\n\nabstract contract PositionManagerDependent is IPositionManagerDependent {\n // --- Immutable variables ---\n\n address public immutable override positionManager;\n\n // --- Modifiers ---\n\n modifier onlyPositionManager() {\n if (msg.sender != positionManager) {\n revert CallerIsNotPositionManager(msg.sender);\n }\n _;\n }\n\n // --- Constructor ---\n\n constructor(address positionManager_) {\n if (positionManager_ == address(0)) {\n revert PositionManagerCannotBeZero();\n }\n positionManager = positionManager_;\n }\n}\n\ncontract ERC20Indexable is IERC20Indexable, ERC20Capped, PositionManagerDependent {\n // --- Types ---\n\n using Fixed256x18 for uint256;\n\n // --- Constants ---\n\n uint256 public constant override INDEX_PRECISION = Fixed256x18.ONE;\n\n // --- Variables ---\n\n uint256 internal storedIndex;\n\n // --- Constructor ---\n\n constructor(\n address positionManager_,\n string memory name_,\n string memory symbol_,\n uint256 cap_\n )\n ERC20(name_, symbol_)\n ERC20Capped(cap_)\n PositionManagerDependent(positionManager_)\n {\n storedIndex = INDEX_PRECISION;\n emit ERC20IndexableDeployed(positionManager_);\n }\n\n // --- Functions ---\n\n function mint(address to, uint256 amount) public virtual override onlyPositionManager {\n _mint(to, amount.divUp(storedIndex));\n }\n\n function burn(address from, uint256 amount) public virtual override onlyPositionManager {\n _burn(from, amount == type(uint256).max ? ERC20.balanceOf(from) : amount.divUp(storedIndex));\n }\n\n function setIndex(uint256 backingAmount) external override onlyPositionManager {\n uint256 supply = ERC20.totalSupply();\n uint256 newIndex = (backingAmount == 0 && supply == 0) ? INDEX_PRECISION : backingAmount.divUp(supply);\n storedIndex = newIndex;\n emit IndexUpdated(newIndex);\n }\n\n function currentIndex() public view virtual override returns (uint256) {\n return storedIndex;\n }\n\n function totalSupply() public view virtual override(IERC20, ERC20) returns (uint256) {\n return ERC20.totalSupply().mulDown(currentIndex());\n }\n\n function balanceOf(address account) public view virtual override(IERC20, ERC20) returns (uint256) {\n return ERC20.balanceOf(account).mulDown(currentIndex());\n }\n\n function transfer(address, uint256) public virtual override(IERC20, ERC20) returns (bool) {\n revert NotSupported();\n }\n\n function allowance(address, address) public view virtual override(IERC20, ERC20) returns (uint256) {\n revert NotSupported();\n }\n\n function approve(address, uint256) public virtual override(IERC20, ERC20) returns (bool) {\n revert NotSupported();\n }\n\n function transferFrom(address, address, uint256) public virtual override(IERC20, ERC20) returns (bool) {\n revert NotSupported();\n }\n\n function increaseAllowance(address, uint256) public virtual override returns (bool) {\n revert NotSupported();\n }\n\n function decreaseAllowance(address, uint256) public virtual override returns (bool) {\n revert NotSupported();\n }\n}\n\nabstract contract FeeCollector is Ownable2Step, IFeeCollector {\n // --- Variables ---\n\n address public override feeRecipient;\n\n // --- Constructor ---\n\n /// @param feeRecipient_ Address of the fee recipient to initialize contract with.\n constructor(address feeRecipient_) {\n if (feeRecipient_ == address(0)) {\n revert InvalidFeeRecipient();\n }\n\n feeRecipient = feeRecipient_;\n }\n\n // --- Functions ---\n\n function setFeeRecipient(address newFeeRecipient) external onlyOwner {\n if (newFeeRecipient == address(0)) {\n revert InvalidFeeRecipient();\n }\n\n feeRecipient = newFeeRecipient;\n emit FeeRecipientChanged(newFeeRecipient);\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol)\n\n/**\n * @dev Contract module that helps prevent reentrant calls to a function.\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * Note that because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n */\nabstract contract ReentrancyGuard {\n // Booleans are more expensive than uint256 or any type that takes up a full\n // word because each write operation emits an extra SLOAD to first read the\n // slot's contents, replace the bits taken up by the boolean, and then write\n // back. This is the compiler's defense against contract upgrades and\n // pointer aliasing, and it cannot be disabled.\n\n // The values being non-zero value makes deployment a bit more expensive,\n // but in exchange the refund on every call to nonReentrant will be lower in\n // amount. Since refunds are capped to a percentage of the total\n // transaction's gas, it is best to keep them low in cases like this one, to\n // increase the likelihood of the full refund coming into effect.\n uint256 private constant _NOT_ENTERED = 1;\n uint256 private constant _ENTERED = 2;\n\n uint256 private _status;\n\n constructor() {\n _status = _NOT_ENTERED;\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and making it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n _nonReentrantBefore();\n _;\n _nonReentrantAfter();\n }\n\n function _nonReentrantBefore() private {\n // On the first call to nonReentrant, _status will be _NOT_ENTERED\n require(_status != _ENTERED, \"ReentrancyGuard: reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n _status = _ENTERED;\n }\n\n function _nonReentrantAfter() private {\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n _status = _NOT_ENTERED;\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/extensions/draft-ERC20Permit.sol)\n\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol)\n\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n bytes16 private constant _SYMBOLS = \"0123456789abcdef\";\n uint8 private constant _ADDRESS_LENGTH = 20;\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n */\n function toString(uint256 value) internal pure returns (string memory) {\n unchecked {\n uint256 length = Math.log10(value) + 1;\n string memory buffer = new string(length);\n uint256 ptr;\n /// @solidity memory-safe-assembly\n assembly {\n ptr := add(buffer, add(32, length))\n }\n while (true) {\n ptr--;\n /// @solidity memory-safe-assembly\n assembly {\n mstore8(ptr, byte(mod(value, 10), _SYMBOLS))\n }\n value /= 10;\n if (value == 0) break;\n }\n return buffer;\n }\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n */\n function toHexString(uint256 value) internal pure returns (string memory) {\n unchecked {\n return toHexString(value, Math.log256(value) + 1);\n }\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n */\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n bytes memory buffer = new bytes(2 * length + 2);\n buffer[0] = \"0\";\n buffer[1] = \"x\";\n for (uint256 i = 2 * length + 1; i > 1; --i) {\n buffer[i] = _SYMBOLS[value & 0xf];\n value >>= 4;\n }\n require(value == 0, \"Strings: hex length insufficient\");\n return string(buffer);\n }\n\n /**\n * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.\n */\n function toHexString(address addr) internal pure returns (string memory) {\n return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);\n }\n}\n\n/**\n * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.\n *\n * These functions can be used to verify that a message was signed by the holder\n * of the private keys of a given address.\n */\nlibrary ECDSA {\n enum RecoverError {\n NoError,\n InvalidSignature,\n InvalidSignatureLength,\n InvalidSignatureS,\n InvalidSignatureV // Deprecated in v4.8\n }\n\n function _throwError(RecoverError error) private pure {\n if (error == RecoverError.NoError) {\n return; // no error: do nothing\n } else if (error == RecoverError.InvalidSignature) {\n revert(\"ECDSA: invalid signature\");\n } else if (error == RecoverError.InvalidSignatureLength) {\n revert(\"ECDSA: invalid signature length\");\n } else if (error == RecoverError.InvalidSignatureS) {\n revert(\"ECDSA: invalid signature 's' value\");\n }\n }\n\n /**\n * @dev Returns the address that signed a hashed message (`hash`) with\n * `signature` or error string. This address can then be used for verification purposes.\n *\n * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:\n * this function rejects them by requiring the `s` value to be in the lower\n * half order, and the `v` value to be either 27 or 28.\n *\n * IMPORTANT: `hash` _must_ be the result of a hash operation for the\n * verification to be secure: it is possible to craft signatures that\n * recover to arbitrary addresses for non-hashed data. A safe way to ensure\n * this is by receiving a hash of the original message (which may otherwise\n * be too long), and then calling {toEthSignedMessageHash} on it.\n *\n * Documentation for signature generation:\n * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]\n * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]\n *\n * _Available since v4.3._\n */\n function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {\n if (signature.length == 65) {\n bytes32 r;\n bytes32 s;\n uint8 v;\n // ecrecover takes the signature parameters, and the only way to get them\n // currently is to use assembly.\n /// @solidity memory-safe-assembly\n assembly {\n r := mload(add(signature, 0x20))\n s := mload(add(signature, 0x40))\n v := byte(0, mload(add(signature, 0x60)))\n }\n return tryRecover(hash, v, r, s);\n } else {\n return (address(0), RecoverError.InvalidSignatureLength);\n }\n }\n\n /**\n * @dev Returns the address that signed a hashed message (`hash`) with\n * `signature`. This address can then be used for verification purposes.\n *\n * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:\n * this function rejects them by requiring the `s` value to be in the lower\n * half order, and the `v` value to be either 27 or 28.\n *\n * IMPORTANT: `hash` _must_ be the result of a hash operation for the\n * verification to be secure: it is possible to craft signatures that\n * recover to arbitrary addresses for non-hashed data. A safe way to ensure\n * this is by receiving a hash of the original message (which may otherwise\n * be too long), and then calling {toEthSignedMessageHash} on it.\n */\n function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, signature);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.\n *\n * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]\n *\n * _Available since v4.3._\n */\n function tryRecover(\n bytes32 hash,\n bytes32 r,\n bytes32 vs\n ) internal pure returns (address, RecoverError) {\n bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);\n uint8 v = uint8((uint256(vs) >> 255) + 27);\n return tryRecover(hash, v, r, s);\n }\n\n /**\n * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.\n *\n * _Available since v4.2._\n */\n function recover(\n bytes32 hash,\n bytes32 r,\n bytes32 vs\n ) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, r, vs);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Overload of {ECDSA-tryRecover} that receives the `v`,\n * `r` and `s` signature fields separately.\n *\n * _Available since v4.3._\n */\n function tryRecover(\n bytes32 hash,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal pure returns (address, RecoverError) {\n // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature\n // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines\n // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most\n // signatures from current libraries generate a unique signature with an s-value in the lower half order.\n //\n // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value\n // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or\n // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept\n // these malleable signatures as well.\n if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {\n return (address(0), RecoverError.InvalidSignatureS);\n }\n\n // If the signature is valid (and not malleable), return the signer address\n address signer = ecrecover(hash, v, r, s);\n if (signer == address(0)) {\n return (address(0), RecoverError.InvalidSignature);\n }\n\n return (signer, RecoverError.NoError);\n }\n\n /**\n * @dev Overload of {ECDSA-recover} that receives the `v`,\n * `r` and `s` signature fields separately.\n */\n function recover(\n bytes32 hash,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, v, r, s);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Returns an Ethereum Signed Message, created from a `hash`. This\n * produces hash corresponding to the one signed with the\n * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]\n * JSON-RPC method as part of EIP-191.\n *\n * See {recover}.\n */\n function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {\n // 32 is the length in bytes of hash,\n // enforced by the type signature above\n return keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n32\", hash));\n }\n\n /**\n * @dev Returns an Ethereum Signed Message, created from `s`. This\n * produces hash corresponding to the one signed with the\n * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]\n * JSON-RPC method as part of EIP-191.\n *\n * See {recover}.\n */\n function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {\n return keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n\", Strings.toString(s.length), s));\n }\n\n /**\n * @dev Returns an Ethereum Signed Typed Data, created from a\n * `domainSeparator` and a `structHash`. This produces hash corresponding\n * to the one signed with the\n * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]\n * JSON-RPC method as part of EIP-712.\n *\n * See {recover}.\n */\n function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {\n return keccak256(abi.encodePacked(\"\\x19\\x01\", domainSeparator, structHash));\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/EIP712.sol)\n\n/**\n * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.\n *\n * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,\n * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding\n * they need in their contracts using a combination of `abi.encode` and `keccak256`.\n *\n * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding\n * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA\n * ({_hashTypedDataV4}).\n *\n * The implementation of the domain separator was designed to be as efficient as possible while still properly updating\n * the chain id to protect against replay attacks on an eventual fork of the chain.\n *\n * NOTE: This contract implements the version of the encoding known as \"v4\", as implemented by the JSON RPC method\n * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].\n *\n * _Available since v3.4._\n */\nabstract contract EIP712 {\n /* solhint-disable var-name-mixedcase */\n // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to\n // invalidate the cached domain separator if the chain id changes.\n bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;\n uint256 private immutable _CACHED_CHAIN_ID;\n address private immutable _CACHED_THIS;\n\n bytes32 private immutable _HASHED_NAME;\n bytes32 private immutable _HASHED_VERSION;\n bytes32 private immutable _TYPE_HASH;\n\n /* solhint-enable var-name-mixedcase */\n\n /**\n * @dev Initializes the domain separator and parameter caches.\n *\n * The meaning of `name` and `version` is specified in\n * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:\n *\n * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.\n * - `version`: the current major version of the signing domain.\n *\n * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart\n * contract upgrade].\n */\n constructor(string memory name, string memory version) {\n bytes32 hashedName = keccak256(bytes(name));\n bytes32 hashedVersion = keccak256(bytes(version));\n bytes32 typeHash = keccak256(\n \"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\"\n );\n _HASHED_NAME = hashedName;\n _HASHED_VERSION = hashedVersion;\n _CACHED_CHAIN_ID = block.chainid;\n _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);\n _CACHED_THIS = address(this);\n _TYPE_HASH = typeHash;\n }\n\n /**\n * @dev Returns the domain separator for the current chain.\n */\n function _domainSeparatorV4() internal view returns (bytes32) {\n if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) {\n return _CACHED_DOMAIN_SEPARATOR;\n } else {\n return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);\n }\n }\n\n function _buildDomainSeparator(\n bytes32 typeHash,\n bytes32 nameHash,\n bytes32 versionHash\n ) private view returns (bytes32) {\n return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));\n }\n\n /**\n * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this\n * function returns the hash of the fully encoded EIP712 message for this domain.\n *\n * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:\n *\n * ```solidity\n * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(\n * keccak256(\"Mail(address to,string contents)\"),\n * mailTo,\n * keccak256(bytes(mailContents))\n * )));\n * address signer = ECDSA.recover(digest, signature);\n * ```\n */\n function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {\n return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);\n }\n}\n\n// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)\n\n/**\n * @title Counters\n * @author Matt Condon (@shrugs)\n * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number\n * of elements in a mapping, issuing ERC721 ids, or counting request ids.\n *\n * Include with `using Counters for Counters.Counter;`\n */\nlibrary Counters {\n struct Counter {\n // This variable should never be directly accessed by users of the library: interactions must be restricted to\n // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add\n // this feature: see https://github.com/ethereum/solidity/issues/4637\n uint256 _value; // default: 0\n }\n\n function current(Counter storage counter) internal view returns (uint256) {\n return counter._value;\n }\n\n function increment(Counter storage counter) internal {\n unchecked {\n counter._value += 1;\n }\n }\n\n function decrement(Counter storage counter) internal {\n uint256 value = counter._value;\n require(value > 0, \"Counter: decrement overflow\");\n unchecked {\n counter._value = value - 1;\n }\n }\n\n function reset(Counter storage counter) internal {\n counter._value = 0;\n }\n}\n\n/**\n * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n *\n * _Available since v3.4._\n */\nabstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {\n using Counters for Counters.Counter;\n\n mapping(address => Counters.Counter) private _nonces;\n\n // solhint-disable-next-line var-name-mixedcase\n bytes32 private constant _PERMIT_TYPEHASH =\n keccak256(\"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)\");\n /**\n * @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`.\n * However, to ensure consistency with the upgradeable transpiler, we will continue\n * to reserve a slot.\n * @custom:oz-renamed-from _PERMIT_TYPEHASH\n */\n // solhint-disable-next-line var-name-mixedcase\n bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT;\n\n /**\n * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `\"1\"`.\n *\n * It's a good idea to use the same `name` that is defined as the ERC20 token name.\n */\n constructor(string memory name) EIP712(name, \"1\") {}\n\n /**\n * @dev See {IERC20Permit-permit}.\n */\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) public virtual override {\n require(block.timestamp <= deadline, \"ERC20Permit: expired deadline\");\n\n bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));\n\n bytes32 hash = _hashTypedDataV4(structHash);\n\n address signer = ECDSA.recover(hash, v, r, s);\n require(signer == owner, \"ERC20Permit: invalid signature\");\n\n _approve(owner, spender, value);\n }\n\n /**\n * @dev See {IERC20Permit-nonces}.\n */\n function nonces(address owner) public view virtual override returns (uint256) {\n return _nonces[owner].current();\n }\n\n /**\n * @dev See {IERC20Permit-DOMAIN_SEPARATOR}.\n */\n // solhint-disable-next-line func-name-mixedcase\n function DOMAIN_SEPARATOR() external view override returns (bytes32) {\n return _domainSeparatorV4();\n }\n\n /**\n * @dev \"Consume a nonce\": return the current value and increment.\n *\n * _Available since v4.1._\n */\n function _useNonce(address owner) internal virtual returns (uint256 current) {\n Counters.Counter storage nonce = _nonces[owner];\n current = nonce.current();\n nonce.increment();\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/extensions/ERC20FlashMint.sol)\n\n/**\n * @dev Implementation of the ERC3156 Flash loans extension, as defined in\n * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].\n *\n * Adds the {flashLoan} method, which provides flash loan support at the token\n * level. By default there is no fee, but this can be changed by overriding {flashFee}.\n *\n * _Available since v4.1._\n */\nabstract contract ERC20FlashMint is ERC20, IERC3156FlashLender {\n bytes32 private constant _RETURN_VALUE = keccak256(\"ERC3156FlashBorrower.onFlashLoan\");\n\n /**\n * @dev Returns the maximum amount of tokens available for loan.\n * @param token The address of the token that is requested.\n * @return The amount of token that can be loaned.\n */\n function maxFlashLoan(address token) public view virtual override returns (uint256) {\n return token == address(this) ? type(uint256).max - ERC20.totalSupply() : 0;\n }\n\n /**\n * @dev Returns the fee applied when doing flash loans. This function calls\n * the {_flashFee} function which returns the fee applied when doing flash\n * loans.\n * @param token The token to be flash loaned.\n * @param amount The amount of tokens to be loaned.\n * @return The fees applied to the corresponding flash loan.\n */\n function flashFee(address token, uint256 amount) public view virtual override returns (uint256) {\n require(token == address(this), \"ERC20FlashMint: wrong token\");\n return _flashFee(token, amount);\n }\n\n /**\n * @dev Returns the fee applied when doing flash loans. By default this\n * implementation has 0 fees. This function can be overloaded to make\n * the flash loan mechanism deflationary.\n * @param token The token to be flash loaned.\n * @param amount The amount of tokens to be loaned.\n * @return The fees applied to the corresponding flash loan.\n */\n function _flashFee(address token, uint256 amount) internal view virtual returns (uint256) {\n // silence warning about unused variable without the addition of bytecode.\n token;\n amount;\n return 0;\n }\n\n /**\n * @dev Returns the receiver address of the flash fee. By default this\n * implementation returns the address(0) which means the fee amount will be burnt.\n * This function can be overloaded to change the fee receiver.\n * @return The address for which the flash fee will be sent to.\n */\n function _flashFeeReceiver() internal view virtual returns (address) {\n return address(0);\n }\n\n /**\n * @dev Performs a flash loan. New tokens are minted and sent to the\n * `receiver`, who is required to implement the {IERC3156FlashBorrower}\n * interface. By the end of the flash loan, the receiver is expected to own\n * amount + fee tokens and have them approved back to the token contract itself so\n * they can be burned.\n * @param receiver The receiver of the flash loan. Should implement the\n * {IERC3156FlashBorrower-onFlashLoan} interface.\n * @param token The token to be flash loaned. Only `address(this)` is\n * supported.\n * @param amount The amount of tokens to be loaned.\n * @param data An arbitrary datafield that is passed to the receiver.\n * @return `true` if the flash loan was successful.\n */\n // This function can reenter, but it doesn't pose a risk because it always preserves the property that the amount\n // minted at the beginning is always recovered and burned at the end, or else the entire function will revert.\n // slither-disable-next-line reentrancy-no-eth\n function flashLoan(\n IERC3156FlashBorrower receiver,\n address token,\n uint256 amount,\n bytes calldata data\n ) public virtual override returns (bool) {\n require(amount <= maxFlashLoan(token), \"ERC20FlashMint: amount exceeds maxFlashLoan\");\n uint256 fee = flashFee(token, amount);\n _mint(address(receiver), amount);\n require(\n receiver.onFlashLoan(msg.sender, token, amount, fee, data) == _RETURN_VALUE,\n \"ERC20FlashMint: invalid return value\"\n );\n address flashFeeReceiver = _flashFeeReceiver();\n _spendAllowance(address(receiver), address(this), amount + fee);\n if (fee == 0 || flashFeeReceiver == address(0)) {\n _burn(address(receiver), amount + fee);\n } else {\n _burn(address(receiver), amount);\n _transfer(address(receiver), flashFeeReceiver, fee);\n }\n return true;\n }\n}\n\ncontract RToken is ReentrancyGuard, ERC20Permit, ERC20FlashMint, PositionManagerDependent, FeeCollector, IRToken {\n // --- Constants ---\n\n uint256 public constant override PERCENTAGE_BASE = 10_000;\n uint256 public constant override MAX_FLASH_MINT_FEE_PERCENTAGE = 500;\n\n // --- Variables ---\n\n uint256 public override flashMintFeePercentage;\n\n // --- Constructor ---\n\n /// @dev Deploys new R token. Sets flash mint fee percentage to 0. Transfers ownership to @param feeRecipient_.\n /// @param positionManager_ Address of the PositionManager contract that is authorized to mint and burn new tokens.\n /// @param feeRecipient_ Address of flash mint fee recipient.\n constructor(\n address positionManager_,\n address feeRecipient_\n )\n ERC20Permit(\"R Stablecoin\")\n ERC20(\"R Stablecoin\", \"R\")\n PositionManagerDependent(positionManager_)\n FeeCollector(feeRecipient_)\n {\n setFlashMintFeePercentage(PERCENTAGE_BASE / 200); // 0.5%\n\n transferOwnership(feeRecipient_);\n\n emit RDeployed(positionManager_, feeRecipient_);\n }\n\n // --- Functions ---\n\n function mint(address to, uint256 amount) external override onlyPositionManager {\n _mint(to, amount);\n }\n\n function burn(address from, uint256 amount) external override onlyPositionManager {\n _burn(from, amount);\n }\n\n function setFlashMintFeePercentage(uint256 feePercentage) public override onlyOwner {\n if (feePercentage > MAX_FLASH_MINT_FEE_PERCENTAGE) {\n revert FlashFeePercentageTooBig(feePercentage);\n }\n\n flashMintFeePercentage = feePercentage;\n emit FlashMintFeePercentageChanged(flashMintFeePercentage);\n }\n\n function flashLoan(\n IERC3156FlashBorrower receiver,\n address token,\n uint256 amount,\n bytes calldata data\n )\n public\n override(ERC20FlashMint, IERC3156FlashLender)\n nonReentrant\n returns (bool)\n {\n return super.flashLoan(receiver, token, amount, data);\n }\n\n /// @dev Inherited from ERC20FlashMint. Defines maximum size of the flash mint.\n /// @param token Token to be flash minted. Returns 0 amount in case of token != address(this).\n function maxFlashLoan(address token)\n public\n view\n virtual\n override(ERC20FlashMint, IERC3156FlashLender)\n returns (uint256)\n {\n return token == address(this) ? Math.min(totalSupply() / 10, ERC20FlashMint.maxFlashLoan(address(this))) : 0;\n }\n\n /// @dev Inherited from ERC20FlashMint. Defines flash mint fee for the flash mint of @param amount tokens.\n /// @param token Token to be flash minted. Returns 0 fee in case of token != address(this).\n /// @param amount Size of the flash mint.\n function _flashFee(address token, uint256 amount) internal view virtual override returns (uint256) {\n return token == address(this) ? amount * flashMintFeePercentage / PERCENTAGE_BASE : 0;\n }\n\n /// @dev Inherited from ERC20FlashMint. Defines flash mint fee receiver.\n /// @return Address that will receive flash mint fees.\n function _flashFeeReceiver() internal view virtual override returns (address) {\n return feeRecipient;\n }\n}\n\n/// @dev Implementation of Position Manager. Current implementation does not support rebasing tokens as collateral.\ncontract PositionManager is FeeCollector, IPositionManager {\n // --- Types ---\n\n using SafeERC20 for IERC20;\n using Fixed256x18 for uint256;\n\n // --- Constants ---\n\n uint256 public constant override MINUTE_DECAY_FACTOR = 999_037_758_833_783_000;\n\n uint256 public constant override MAX_BORROWING_SPREAD = MathUtils._100_PERCENT / 100; // 1%\n uint256 public constant override MAX_BORROWING_RATE = MathUtils._100_PERCENT / 100 * 5; // 5%\n\n uint256 public constant override BETA = 2;\n\n // --- Immutables ---\n\n IRToken public immutable override rToken;\n\n // --- Variables ---\n\n mapping(address position => IERC20 collateralToken) public override collateralTokenForPosition;\n\n mapping(address position => mapping(address delegate => bool isWhitelisted)) public override isDelegateWhitelisted;\n\n mapping(IERC20 collateralToken => CollateralTokenInfo collateralTokenInfo) public override collateralInfo;\n\n // --- Modifiers ---\n\n /// @dev Checks if the collateral token has been added to the position manager, or reverts otherwise.\n /// @param collateralToken The collateral token to check.\n modifier collateralTokenExists(IERC20 collateralToken) {\n if (address(collateralInfo[collateralToken].collateralToken) == address(0)) {\n revert CollateralTokenNotAdded();\n }\n _;\n }\n\n /// @dev Checks if the collateral token has enabled, or reverts otherwise. When the condition is false, the check\n /// is skipped.\n /// @param collateralToken The collateral token to check.\n /// @param condition If true, the check will be performed.\n modifier onlyEnabledCollateralTokenWhen(IERC20 collateralToken, bool condition) {\n if (condition && !collateralInfo[collateralToken].isEnabled) {\n revert CollateralTokenDisabled();\n }\n _;\n }\n\n /// @dev Checks if the borrower has a position with the collateral token or doesn't have a position at all, or\n /// reverts otherwise.\n /// @param position The borrower to check.\n /// @param collateralToken The collateral token to check.\n modifier onlyDepositedCollateralTokenOrNew(address position, IERC20 collateralToken) {\n if (\n collateralTokenForPosition[position] != IERC20(address(0))\n && collateralTokenForPosition[position] != collateralToken\n ) {\n revert PositionCollateralTokenMismatch();\n }\n _;\n }\n\n /// @dev Checks if the max fee percentage is between the borrowing spread and 100%, or reverts otherwise. When the\n /// condition is false, the check is skipped.\n /// @param maxFeePercentage The max fee percentage to check.\n /// @param condition If true, the check will be performed.\n modifier validMaxFeePercentageWhen(uint256 maxFeePercentage, bool condition) {\n if (condition && maxFeePercentage > MathUtils._100_PERCENT) {\n revert InvalidMaxFeePercentage();\n }\n _;\n }\n\n // --- Constructor ---\n\n /// @dev Initializes the position manager.\n constructor(address rToken_) FeeCollector(msg.sender) {\n rToken = rToken_ == address(0) ? new RToken(address(this), msg.sender) : IRToken(rToken_);\n emit PositionManagerDeployed(rToken, msg.sender);\n }\n\n // --- External functions ---\n\n function managePosition(\n IERC20 collateralToken,\n address position,\n uint256 collateralChange,\n bool isCollateralIncrease,\n uint256 debtChange,\n bool isDebtIncrease,\n uint256 maxFeePercentage,\n ERC20PermitSignature calldata permitSignature\n )\n public\n virtual\n override\n collateralTokenExists(collateralToken)\n validMaxFeePercentageWhen(maxFeePercentage, isDebtIncrease)\n onlyDepositedCollateralTokenOrNew(position, collateralToken)\n onlyEnabledCollateralTokenWhen(collateralToken, isDebtIncrease && debtChange > 0)\n returns (uint256 actualCollateralChange, uint256 actualDebtChange)\n {\n if (position != msg.sender && !isDelegateWhitelisted[position][msg.sender]) {\n revert DelegateNotWhitelisted();\n }\n if (collateralChange == 0 && debtChange == 0) {\n revert NoCollateralOrDebtChange();\n }\n if (address(permitSignature.token) == address(collateralToken)) {\n PermitHelper.applyPermit(permitSignature, msg.sender, address(this));\n }\n\n CollateralTokenInfo storage collateralTokenInfo = collateralInfo[collateralToken];\n IERC20Indexable raftCollateralToken = collateralTokenInfo.collateralToken;\n IERC20Indexable raftDebtToken = collateralTokenInfo.debtToken;\n\n uint256 debtBefore = raftDebtToken.balanceOf(position);\n if (!isDebtIncrease && (debtChange == type(uint256).max || (debtBefore != 0 && debtChange == debtBefore))) {\n if (collateralChange != 0 || isCollateralIncrease) {\n revert WrongCollateralParamsForFullRepayment();\n }\n collateralChange = raftCollateralToken.balanceOf(position);\n debtChange = debtBefore;\n }\n\n _adjustDebt(position, collateralToken, raftDebtToken, debtChange, isDebtIncrease, maxFeePercentage);\n _adjustCollateral(collateralToken, raftCollateralToken, position, collateralChange, isCollateralIncrease);\n\n uint256 positionDebt = raftDebtToken.balanceOf(position);\n uint256 positionCollateral = raftCollateralToken.balanceOf(position);\n\n if (positionDebt == 0) {\n if (positionCollateral != 0) {\n revert InvalidPosition();\n }\n // position was closed, remove it\n _closePosition(raftCollateralToken, raftDebtToken, position, false);\n } else {\n _checkValidPosition(collateralToken, positionDebt, positionCollateral);\n\n if (debtBefore == 0) {\n collateralTokenForPosition[position] = collateralToken;\n emit PositionCreated(position, collateralToken);\n }\n }\n return (collateralChange, debtChange);\n }\n\n function liquidate(address position) external override {\n IERC20 collateralToken = collateralTokenForPosition[position];\n CollateralTokenInfo storage collateralTokenInfo = collateralInfo[collateralToken];\n IERC20Indexable raftCollateralToken = collateralTokenInfo.collateralToken;\n IERC20Indexable raftDebtToken = collateralTokenInfo.debtToken;\n ISplitLiquidationCollateral splitLiquidation = collateralTokenInfo.splitLiquidation;\n\n if (address(collateralToken) == address(0)) {\n revert NothingToLiquidate();\n }\n (uint256 price,) = collateralTokenInfo.priceFeed.fetchPrice();\n uint256 entireCollateral = raftCollateralToken.balanceOf(position);\n uint256 entireDebt = raftDebtToken.balanceOf(position);\n uint256 icr = MathUtils._computeCR(entireCollateral, entireDebt, price);\n\n if (icr >= splitLiquidation.MCR()) {\n revert NothingToLiquidate();\n }\n\n uint256 totalDebt = raftDebtToken.totalSupply();\n if (entireDebt == totalDebt) {\n revert CannotLiquidateLastPosition();\n }\n bool isRedistribution = icr <= MathUtils._100_PERCENT;\n\n // prettier: ignore\n (uint256 collateralLiquidationFee, uint256 collateralToSendToLiquidator) =\n splitLiquidation.split(entireCollateral, entireDebt, price, isRedistribution);\n\n if (!isRedistribution) {\n _burnRTokens(msg.sender, entireDebt);\n totalDebt -= entireDebt;\n\n // Collateral is sent to protocol as a fee only in case of liquidation\n collateralToken.safeTransfer(feeRecipient, collateralLiquidationFee);\n }\n\n collateralToken.safeTransfer(msg.sender, collateralToSendToLiquidator);\n\n _closePosition(raftCollateralToken, raftDebtToken, position, true);\n\n _updateDebtAndCollateralIndex(collateralToken, raftCollateralToken, raftDebtToken, totalDebt);\n\n emit Liquidation(\n msg.sender,\n position,\n collateralToken,\n entireDebt,\n entireCollateral,\n collateralToSendToLiquidator,\n collateralLiquidationFee,\n isRedistribution\n );\n }\n\n function redeemCollateral(\n IERC20 collateralToken,\n uint256 debtAmount,\n uint256 maxFeePercentage\n )\n public\n virtual\n override\n {\n if (maxFeePercentage > MathUtils._100_PERCENT) {\n revert MaxFeePercentageOutOfRange();\n }\n if (debtAmount == 0) {\n revert AmountIsZero();\n }\n IERC20Indexable raftDebtToken = collateralInfo[collateralToken].debtToken;\n\n uint256 newTotalDebt = raftDebtToken.totalSupply() - debtAmount;\n uint256 lowTotalDebt = collateralInfo[collateralToken].splitLiquidation.LOW_TOTAL_DEBT();\n if (newTotalDebt < lowTotalDebt) {\n revert TotalDebtCannotBeLowerThanMinDebt(collateralToken, newTotalDebt);\n }\n\n (uint256 price, uint256 deviation) = collateralInfo[collateralToken].priceFeed.fetchPrice();\n uint256 collateralToRedeem = debtAmount.divDown(price);\n uint256 totalCollateral = collateralToken.balanceOf(address(this));\n if (\n totalCollateral - collateralToRedeem == 0\n || totalCollateral - collateralToRedeem < lowTotalDebt.divDown(price)\n ) {\n revert TotalCollateralCannotBeLowerThanMinCollateral(\n collateralToken, totalCollateral - collateralToRedeem, lowTotalDebt.divDown(price)\n );\n }\n\n // Decay the baseRate due to time passed, and then increase it according to the size of this redemption.\n // Use the saved total R supply value, from before it was reduced by the redemption.\n _updateBaseRateFromRedemption(collateralToken, collateralToRedeem, price, rToken.totalSupply());\n\n // Calculate the redemption fee\n uint256 redemptionFee = getRedemptionFee(collateralToken, collateralToRedeem, deviation);\n uint256 rebate = redemptionFee.mulDown(collateralInfo[collateralToken].redemptionRebate);\n\n _checkValidFee(redemptionFee, collateralToRedeem, maxFeePercentage);\n\n // Send the redemption fee to the recipient\n collateralToken.safeTransfer(feeRecipient, redemptionFee - rebate);\n\n // Burn the total R that is cancelled with debt, and send the redeemed collateral to msg.sender\n _burnRTokens(msg.sender, debtAmount);\n\n // Send collateral to account\n collateralToken.safeTransfer(msg.sender, collateralToRedeem - redemptionFee);\n\n _updateDebtAndCollateralIndex(\n collateralToken, collateralInfo[collateralToken].collateralToken, raftDebtToken, newTotalDebt\n );\n\n emit Redemption(msg.sender, debtAmount, collateralToRedeem, redemptionFee, rebate);\n }\n\n function whitelistDelegate(address delegate, bool whitelisted) external override {\n if (delegate == address(0)) {\n revert InvalidDelegateAddress();\n }\n isDelegateWhitelisted[msg.sender][delegate] = whitelisted;\n\n emit DelegateWhitelisted(msg.sender, delegate, whitelisted);\n }\n\n function setBorrowingSpread(IERC20 collateralToken, uint256 newBorrowingSpread) external override onlyOwner {\n if (newBorrowingSpread > MAX_BORROWING_SPREAD) {\n revert BorrowingSpreadExceedsMaximum();\n }\n collateralInfo[collateralToken].borrowingSpread = newBorrowingSpread;\n emit BorrowingSpreadUpdated(newBorrowingSpread);\n }\n\n function setRedemptionRebate(IERC20 collateralToken, uint256 newRedemptionRebate) public override onlyOwner {\n if (newRedemptionRebate > MathUtils._100_PERCENT) {\n revert RedemptionRebateExceedsMaximum();\n }\n collateralInfo[collateralToken].redemptionRebate = newRedemptionRebate;\n emit RedemptionRebateUpdated(newRedemptionRebate);\n }\n\n function getRedemptionFeeWithDecay(\n IERC20 collateralToken,\n uint256 collateralAmount\n )\n external\n view\n override\n returns (uint256 redemptionFee)\n {\n redemptionFee = getRedemptionRateWithDecay(collateralToken).mulDown(collateralAmount);\n if (redemptionFee >= collateralAmount) {\n revert FeeEatsUpAllReturnedCollateral();\n }\n }\n\n // --- Public functions ---\n\n function addCollateralToken(\n IERC20 collateralToken,\n IPriceFeed priceFeed,\n ISplitLiquidationCollateral newSplitLiquidationCollateral\n )\n public\n virtual\n override\n {\n addCollateralToken(\n collateralToken,\n priceFeed,\n newSplitLiquidationCollateral,\n new ERC20Indexable(\n address(this),\n string(bytes.concat(\"Raft \", bytes(IERC20Metadata(address(collateralToken)).name()), \" collateral\")),\n string(bytes.concat(\"r\", bytes(IERC20Metadata(address(collateralToken)).symbol()), \"-c\")),\n type(uint256).max\n ),\n new ERC20Indexable(\n address(this),\n string(bytes.concat(\"Raft \", bytes(IERC20Metadata(address(collateralToken)).name()), \" debt\")),\n string(bytes.concat(\"r\", bytes(IERC20Metadata(address(collateralToken)).symbol()), \"-d\")),\n type(uint256).max\n )\n );\n }\n\n function addCollateralToken(\n IERC20 collateralToken,\n IPriceFeed priceFeed,\n ISplitLiquidationCollateral newSplitLiquidationCollateral,\n IERC20Indexable raftCollateralToken_,\n IERC20Indexable raftDebtToken_\n )\n public\n override\n onlyOwner\n {\n if (address(collateralToken) == address(0)) {\n revert CollateralTokenAddressCannotBeZero();\n }\n if (address(priceFeed) == address(0)) {\n revert PriceFeedAddressCannotBeZero();\n }\n if (address(collateralInfo[collateralToken].collateralToken) != address(0)) {\n revert CollateralTokenAlreadyAdded();\n }\n\n CollateralTokenInfo memory raftCollateralTokenInfo;\n raftCollateralTokenInfo.collateralToken = raftCollateralToken_;\n raftCollateralTokenInfo.debtToken = raftDebtToken_;\n raftCollateralTokenInfo.isEnabled = true;\n raftCollateralTokenInfo.priceFeed = priceFeed;\n\n collateralInfo[collateralToken] = raftCollateralTokenInfo;\n\n setRedemptionSpread(collateralToken, MathUtils._100_PERCENT);\n setRedemptionRebate(collateralToken, MathUtils._100_PERCENT);\n\n setSplitLiquidationCollateral(collateralToken, newSplitLiquidationCollateral);\n\n emit CollateralTokenAdded(\n collateralToken, raftCollateralTokenInfo.collateralToken, raftCollateralTokenInfo.debtToken, priceFeed\n );\n }\n\n function setCollateralEnabled(\n IERC20 collateralToken,\n bool isEnabled\n )\n public\n override\n onlyOwner\n collateralTokenExists(collateralToken)\n {\n bool previousIsEnabled = collateralInfo[collateralToken].isEnabled;\n collateralInfo[collateralToken].isEnabled = isEnabled;\n\n if (previousIsEnabled != isEnabled) {\n emit CollateralTokenModified(collateralToken, isEnabled);\n }\n }\n\n function setSplitLiquidationCollateral(\n IERC20 collateralToken,\n ISplitLiquidationCollateral newSplitLiquidationCollateral\n )\n public\n override\n onlyOwner\n {\n if (address(newSplitLiquidationCollateral) == address(0)) {\n revert SplitLiquidationCollateralCannotBeZero();\n }\n collateralInfo[collateralToken].splitLiquidation = newSplitLiquidationCollateral;\n emit SplitLiquidationCollateralChanged(collateralToken, newSplitLiquidationCollateral);\n }\n\n function setRedemptionSpread(IERC20 collateralToken, uint256 newRedemptionSpread) public override onlyOwner {\n if (newRedemptionSpread > MathUtils._100_PERCENT) {\n revert RedemptionSpreadOutOfRange();\n }\n collateralInfo[collateralToken].redemptionSpread = newRedemptionSpread;\n emit RedemptionSpreadUpdated(collateralToken, newRedemptionSpread);\n }\n\n function getRedemptionRateWithDecay(IERC20 collateralToken) public view override returns (uint256) {\n return _calcRedemptionRate(collateralToken, _calcDecayedBaseRate(collateralToken));\n }\n\n function raftCollateralToken(IERC20 collateralToken) external view override returns (IERC20Indexable) {\n return collateralInfo[collateralToken].collateralToken;\n }\n\n function raftDebtToken(IERC20 collateralToken) external view override returns (IERC20Indexable) {\n return collateralInfo[collateralToken].debtToken;\n }\n\n function priceFeed(IERC20 collateralToken) external view override returns (IPriceFeed) {\n return collateralInfo[collateralToken].priceFeed;\n }\n\n function splitLiquidationCollateral(IERC20 collateralToken) external view returns (ISplitLiquidationCollateral) {\n return collateralInfo[collateralToken].splitLiquidation;\n }\n\n function collateralEnabled(IERC20 collateralToken) external view override returns (bool) {\n return collateralInfo[collateralToken].isEnabled;\n }\n\n function lastFeeOperationTime(IERC20 collateralToken) external view override returns (uint256) {\n return collateralInfo[collateralToken].lastFeeOperationTime;\n }\n\n function borrowingSpread(IERC20 collateralToken) external view override returns (uint256) {\n return collateralInfo[collateralToken].borrowingSpread;\n }\n\n function baseRate(IERC20 collateralToken) external view override returns (uint256) {\n return collateralInfo[collateralToken].baseRate;\n }\n\n function redemptionSpread(IERC20 collateralToken) external view override returns (uint256) {\n return collateralInfo[collateralToken].redemptionSpread;\n }\n\n function redemptionRebate(IERC20 collateralToken) external view override returns (uint256) {\n return collateralInfo[collateralToken].redemptionRebate;\n }\n\n function getRedemptionRate(IERC20 collateralToken) public view override returns (uint256) {\n return _calcRedemptionRate(collateralToken, collateralInfo[collateralToken].baseRate);\n }\n\n function getRedemptionFee(\n IERC20 collateralToken,\n uint256 collateralAmount,\n uint256 priceDeviation\n )\n public\n view\n override\n returns (uint256)\n {\n return Math.min(getRedemptionRate(collateralToken) + priceDeviation, MathUtils._100_PERCENT).mulDown(\n collateralAmount\n );\n }\n\n function getBorrowingRate(IERC20 collateralToken) public view override returns (uint256) {\n return _calcBorrowingRate(collateralToken, collateralInfo[collateralToken].baseRate);\n }\n\n function getBorrowingRateWithDecay(IERC20 collateralToken) public view override returns (uint256) {\n return _calcBorrowingRate(collateralToken, _calcDecayedBaseRate(collateralToken));\n }\n\n function getBorrowingFee(IERC20 collateralToken, uint256 debtAmount) public view override returns (uint256) {\n return getBorrowingRate(collateralToken).mulDown(debtAmount);\n }\n\n // --- Helper functions ---\n\n /// @dev Adjusts the debt of a given borrower by burning or minting the corresponding amount of R and the Raft\n /// debt token. If the debt is being increased, the borrowing fee is also triggered.\n /// @param position The address of the borrower.\n /// @param debtChange The amount of R to add or remove. Must be positive.\n /// @param isDebtIncrease True if the debt is being increased, false otherwise.\n /// @param maxFeePercentage The maximum fee percentage.\n function _adjustDebt(\n address position,\n IERC20 collateralToken,\n IERC20Indexable raftDebtToken,\n uint256 debtChange,\n bool isDebtIncrease,\n uint256 maxFeePercentage\n )\n internal\n {\n if (debtChange == 0) {\n return;\n }\n\n if (isDebtIncrease) {\n uint256 totalDebtChange =\n debtChange + _triggerBorrowingFee(collateralToken, position, debtChange, maxFeePercentage);\n raftDebtToken.mint(position, totalDebtChange);\n _mintRTokens(msg.sender, debtChange);\n } else {\n raftDebtToken.burn(position, debtChange);\n _burnRTokens(msg.sender, debtChange);\n }\n\n emit DebtChanged(position, collateralToken, debtChange, isDebtIncrease);\n }\n\n /// @dev Mints R tokens\n function _mintRTokens(address to, uint256 amount) internal virtual {\n rToken.mint(to, amount);\n }\n\n /// @dev Burns R tokens\n function _burnRTokens(address from, uint256 amount) internal virtual {\n rToken.burn(from, amount);\n }\n\n /// @dev Adjusts the collateral of a given borrower by burning or minting the corresponding amount of Raft\n /// collateral token and transferring the corresponding amount of collateral token.\n /// @param collateralToken The token the borrower used as collateral.\n /// @param position The address of the borrower.\n /// @param collateralChange The amount of collateral to add or remove. Must be positive.\n /// @param isCollateralIncrease True if the collateral is being increased, false otherwise.\n function _adjustCollateral(\n IERC20 collateralToken,\n IERC20Indexable raftCollateralToken,\n address position,\n uint256 collateralChange,\n bool isCollateralIncrease\n )\n internal\n {\n if (collateralChange == 0) {\n return;\n }\n\n if (isCollateralIncrease) {\n raftCollateralToken.mint(position, collateralChange);\n collateralToken.safeTransferFrom(msg.sender, address(this), collateralChange);\n } else {\n raftCollateralToken.burn(position, collateralChange);\n collateralToken.safeTransfer(msg.sender, collateralChange);\n }\n\n emit CollateralChanged(position, collateralToken, collateralChange, isCollateralIncrease);\n }\n\n /// @dev Updates debt and collateral indexes for a given collateral token.\n /// @param collateralToken The collateral token for which to update the indexes.\n /// @param raftCollateralToken The raft collateral indexable token.\n /// @param raftDebtToken The raft debt indexable token.\n /// @param totalDebtForCollateral Totam amount of debt backed by collateral token.\n function _updateDebtAndCollateralIndex(\n IERC20 collateralToken,\n IERC20Indexable raftCollateralToken,\n IERC20Indexable raftDebtToken,\n uint256 totalDebtForCollateral\n )\n internal\n {\n raftDebtToken.setIndex(totalDebtForCollateral);\n raftCollateralToken.setIndex(collateralToken.balanceOf(address(this)));\n }\n\n function _closePosition(\n IERC20Indexable raftCollateralToken,\n IERC20Indexable raftDebtToken,\n address position,\n bool burnTokens\n )\n internal\n {\n collateralTokenForPosition[position] = IERC20(address(0));\n\n if (burnTokens) {\n raftDebtToken.burn(position, type(uint256).max);\n raftCollateralToken.burn(position, type(uint256).max);\n }\n emit PositionClosed(position);\n }\n\n // --- Borrowing & redemption fee helper functions ---\n\n /// @dev Updates the base rate from a redemption operation. Impacts on the base rate:\n /// 1. decays the base rate based on time passed since last redemption or R borrowing operation,\n /// 2. increases the base rate based on the amount redeemed, as a proportion of total supply.\n function _updateBaseRateFromRedemption(\n IERC20 collateralToken,\n uint256 collateralDrawn,\n uint256 price,\n uint256 totalDebtSupply\n )\n internal\n returns (uint256)\n {\n uint256 decayedBaseRate = _calcDecayedBaseRate(collateralToken);\n\n /* Convert the drawn collateral back to R at face value rate (1 R:1 USD), in order to get\n * the fraction of total supply that was redeemed at face value. */\n uint256 redeemedFraction = collateralDrawn * price / totalDebtSupply;\n\n uint256 newBaseRate = decayedBaseRate + redeemedFraction / BETA;\n newBaseRate = Math.min(newBaseRate, MathUtils._100_PERCENT); // cap baseRate at a maximum of 100%\n assert(newBaseRate > 0); // Base rate is always non-zero after redemption\n\n // Update the baseRate state variable\n collateralInfo[collateralToken].baseRate = newBaseRate;\n emit BaseRateUpdated(collateralToken, newBaseRate);\n\n _updateLastFeeOpTime(collateralToken);\n\n return newBaseRate;\n }\n\n function _calcRedemptionRate(IERC20 collateralToken, uint256 baseRate_) internal view returns (uint256) {\n return baseRate_ + collateralInfo[collateralToken].redemptionSpread;\n }\n\n function _calcBorrowingRate(IERC20 collateralToken, uint256 baseRate_) internal view returns (uint256) {\n return Math.min(collateralInfo[collateralToken].borrowingSpread + baseRate_, MAX_BORROWING_RATE);\n }\n\n /// @dev Updates the base rate based on time elapsed since the last redemption or R borrowing operation.\n function _decayBaseRateFromBorrowing(IERC20 collateralToken) internal {\n uint256 decayedBaseRate = _calcDecayedBaseRate(collateralToken);\n assert(decayedBaseRate <= MathUtils._100_PERCENT); // The baseRate can decay to 0\n\n collateralInfo[collateralToken].baseRate = decayedBaseRate;\n emit BaseRateUpdated(collateralToken, decayedBaseRate);\n\n _updateLastFeeOpTime(collateralToken);\n }\n\n /// @dev Update the last fee operation time only if time passed >= decay interval. This prevents base rate\n /// griefing.\n function _updateLastFeeOpTime(IERC20 collateralToken) internal {\n uint256 timePassed = block.timestamp - collateralInfo[collateralToken].lastFeeOperationTime;\n\n if (timePassed >= 1 minutes) {\n collateralInfo[collateralToken].lastFeeOperationTime = block.timestamp;\n emit LastFeeOpTimeUpdated(collateralToken, block.timestamp);\n }\n }\n\n function _calcDecayedBaseRate(IERC20 collateralToken) internal view returns (uint256) {\n uint256 minutesPassed = (block.timestamp - collateralInfo[collateralToken].lastFeeOperationTime) / 1 minutes;\n uint256 decayFactor = MathUtils._decPow(MINUTE_DECAY_FACTOR, minutesPassed);\n\n return collateralInfo[collateralToken].baseRate.mulDown(decayFactor);\n }\n\n function _triggerBorrowingFee(\n IERC20 collateralToken,\n address position,\n uint256 debtAmount,\n uint256 maxFeePercentage\n )\n internal\n virtual\n returns (uint256 borrowingFee)\n {\n _decayBaseRateFromBorrowing(collateralToken); // decay the baseRate state variable\n borrowingFee = getBorrowingFee(collateralToken, debtAmount);\n\n _checkValidFee(borrowingFee, debtAmount, maxFeePercentage);\n\n if (borrowingFee > 0) {\n _mintRTokens(feeRecipient, borrowingFee);\n emit RBorrowingFeePaid(collateralToken, position, borrowingFee);\n }\n }\n\n // --- Validation check helper functions ---\n\n function _checkValidPosition(IERC20 collateralToken, uint256 positionDebt, uint256 positionCollateral) internal {\n ISplitLiquidationCollateral splitCollateral = collateralInfo[collateralToken].splitLiquidation;\n if (positionDebt < splitCollateral.LOW_TOTAL_DEBT()) {\n revert NetDebtBelowMinimum(positionDebt);\n }\n\n (uint256 price,) = collateralInfo[collateralToken].priceFeed.fetchPrice();\n uint256 newICR = MathUtils._computeCR(positionCollateral, positionDebt, price);\n if (newICR < splitCollateral.MCR()) {\n revert NewICRLowerThanMCR(newICR);\n }\n }\n\n function _checkValidFee(uint256 fee, uint256 amount, uint256 maxFeePercentage) internal pure {\n uint256 feePercentage = fee.divDown(amount);\n\n if (feePercentage > maxFeePercentage) {\n revert FeeExceedsMaxFee(fee, amount, maxFeePercentage);\n }\n }\n}\n\ninterface IRMinter {\n /// @dev Emitted when tokens are recovered from the contract.\n /// @param token The address of the token being recovered.\n /// @param to The address receiving the recovered tokens.\n /// @param amount The amount of tokens recovered.\n event TokensRecovered(IERC20 token, address to, uint256 amount);\n\n /// @return Address of the R token.\n function r() external view returns (IRToken);\n\n /// @return Address of the Position manager contract responsible for minting R.\n function positionManager() external view returns (IPositionManager);\n\n /// @dev Recover accidentally sent tokens to the contract\n /// @param token Address of the token contract.\n /// @param to Address of the receiver of the tokens.\n /// @param amount Number of tokens to recover.\n function recoverTokens(IERC20 token, address to, uint256 amount) external;\n}\n\ninterface ILock {\n /// @dev Thrown when contract usage is locked.\n error ContractLocked();\n\n /// @dev Unauthorized call to lock/unlock.\n error Unauthorized();\n\n /// @dev Retrieves if contract is currently locked or not.\n function locked() external view returns (bool);\n\n /// @dev Retrieves address of the locker who can unlock contract.\n function locker() external view returns (address);\n\n /// @dev Unlcoks the usage of the contract.\n function unlock() external;\n\n /// @dev Locks the usage of the contract.\n function lock() external;\n}\n\nabstract contract ERC20RMinter is IRMinter, ERC20, Ownable2Step {\n using SafeERC20 for IERC20;\n\n IRToken public immutable override r;\n IPositionManager public immutable override positionManager;\n\n constructor(IRToken rToken_, string memory name_, string memory symbol_) ERC20(name_, symbol_) {\n r = rToken_;\n positionManager = IPositionManager(rToken_.positionManager());\n\n _approve(address(this), address(positionManager), type(uint256).max);\n }\n\n modifier unlockCall() {\n ILock lockContract = ILock(address(positionManager.priceFeed(IERC20(this))));\n lockContract.unlock();\n _;\n lockContract.lock();\n }\n\n function recoverTokens(IERC20 token, address to, uint256 amount) external override onlyOwner {\n token.safeTransfer(to, amount);\n emit TokensRecovered(token, to, amount);\n }\n\n function _mintR(address to, uint256 amount) internal unlockCall {\n _mint(address(this), amount);\n ERC20PermitSignature memory emptySignature;\n positionManager.managePosition(\n IERC20(address(this)),\n address(this),\n amount,\n true, // collateral increase\n amount,\n true, // debt increase\n 1e18, // 100%\n emptySignature\n );\n r.transfer(to, amount);\n }\n\n function _burnR(address from, uint256 amount) internal unlockCall {\n r.transferFrom(from, address(this), amount);\n ERC20PermitSignature memory emptySignature;\n positionManager.managePosition(\n IERC20(address(this)),\n address(this),\n amount,\n false, // collateral decrease\n amount,\n false, // debt decrease\n 1e18, // 100%\n emptySignature\n );\n _burn(address(this), amount);\n }\n}\n\ncontract InterestRateDebtToken is ERC20Indexable {\n // --- Types ---\n\n using Fixed256x18 for uint256;\n\n // --- Events ---\n\n event IndexIncreasePerSecondSet(uint256 indexIncreasePerSecond);\n\n // --- Immutables ---\n\n IERC20 immutable collateralToken;\n\n // --- Variables ---\n\n uint256 internal storedIndexUpdatedAt;\n\n uint256 public indexIncreasePerSecond;\n\n // --- Constructor ---\n\n constructor(\n address positionManager_,\n string memory name_,\n string memory symbol_,\n IERC20 collateralToken_,\n uint256 cap_,\n uint256 indexIncreasePerSecond_\n )\n ERC20Indexable(positionManager_, name_, symbol_, cap_)\n {\n storedIndexUpdatedAt = block.timestamp;\n collateralToken = collateralToken_;\n setIndexIncreasePerSecond(indexIncreasePerSecond_);\n }\n\n // --- Functions ---\n\n function mint(address to, uint256 amount) public virtual override {\n updateIndexAndPayFees();\n super.mint(to, amount);\n }\n\n function burn(address from, uint256 amount) public virtual override {\n updateIndexAndPayFees();\n super.burn(from, amount);\n }\n\n function currentIndex() public view virtual override returns (uint256) {\n return storedIndex.mulUp(INDEX_PRECISION + indexIncreasePerSecond * (block.timestamp - storedIndexUpdatedAt));\n }\n\n function updateIndexAndPayFees() public {\n uint256 currentIndex_ = currentIndex();\n _payFees(currentIndex_);\n storedIndexUpdatedAt = block.timestamp;\n storedIndex = currentIndex_;\n emit IndexUpdated(currentIndex_);\n }\n\n function setIndexIncreasePerSecond(uint256 indexIncreasePerSecond_) public onlyOwner {\n updateIndexAndPayFees();\n indexIncreasePerSecond = indexIncreasePerSecond_;\n emit IndexIncreasePerSecondSet(indexIncreasePerSecond_);\n }\n\n function unpaidFees() external view returns (uint256) {\n return _unpaidFees(currentIndex());\n }\n\n function _unpaidFees(uint256 currentIndex_) private view returns (uint256) {\n return totalSupply().mulDown(currentIndex_ - storedIndex);\n }\n\n function _payFees(uint256 currentIndex_) private {\n uint256 unpaidFees = _unpaidFees(currentIndex_);\n if (unpaidFees > 0) {\n IInterestRatePositionManager(positionManager).mintFees(collateralToken, unpaidFees);\n }\n }\n}\n\n/// @dev Implementation of Position Manager. Current implementation does not support rebasing tokens as collateral.\ncontract InterestRatePositionManager is ERC20RMinter, PositionManager, IInterestRatePositionManager {\n // --- Errors ---\n\n error Unsupported();\n\n // --- Constructor ---\n\n /// @dev Initializes the position manager.\n constructor(IRToken rToken_)\n PositionManager(address(rToken_))\n ERC20RMinter(rToken_, \"Interest Rate Posman\", \"IRPM\")\n { }\n\n // --- External functions ---\n\n function managePosition(\n IERC20 collateralToken,\n address position,\n uint256 collateralChange,\n bool isCollateralIncrease,\n uint256 debtChange,\n bool isDebtIncrease,\n uint256 maxFeePercentage,\n ERC20PermitSignature calldata permitSignature\n )\n public\n virtual\n override(IPositionManager, PositionManager)\n returns (uint256 actualCollateralChange, uint256 actualDebtChange)\n {\n if (address(permitSignature.token) == address(r)) {\n PermitHelper.applyPermit(permitSignature, msg.sender, address(this));\n }\n return super.managePosition(\n collateralToken,\n position,\n collateralChange,\n isCollateralIncrease,\n debtChange,\n isDebtIncrease,\n maxFeePercentage,\n permitSignature\n );\n }\n\n function mintFees(IERC20 collateralToken, uint256 amount) external {\n if (msg.sender != address(collateralInfo[collateralToken].debtToken)) {\n revert InvalidDebtToken(msg.sender);\n }\n _mintR(feeRecipient, amount);\n\n emit MintedFees(collateralToken, amount);\n }\n\n function redeemCollateral(IERC20, uint256, uint256) public virtual override(IPositionManager, PositionManager) {\n revert Unsupported();\n }\n\n function addCollateralToken(\n IERC20,\n IPriceFeed,\n ISplitLiquidationCollateral\n )\n public\n override(IPositionManager, PositionManager)\n {\n revert Unsupported();\n }\n\n // --- Helper functions ---\n\n function _mintRTokens(address to, uint256 amount) internal virtual override {\n _mintR(to, amount);\n }\n\n function _burnRTokens(address from, uint256 amount) internal virtual override {\n _burnR(from, amount);\n }\n\n function _triggerBorrowingFee(\n IERC20,\n address,\n uint256,\n uint256\n )\n internal\n pure\n virtual\n override\n returns (uint256)\n {\n return 0;\n }\n}\n" - } - }, - "settings": { - "remappings": [ - "@balancer-labs/=node_modules/@balancer-labs/", - "@balancer-labs/v2-interfaces/contracts/=lib/balancer-v2-monorepo/pkg/interfaces/contracts/", - "@chainlink/=node_modules/@chainlink/", - "@eth-optimism/=node_modules/@eth-optimism/", - "@openzeppelin/=node_modules/@openzeppelin/", - "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", - "@redstone-finance/=node_modules/@redstone-finance/", - "@smartcontractkit/chainlink/=lib/chainlink/contracts/src/v0.8/", - "@tempusfinance/=node_modules/@tempusfinance/", - "@tempusfinance/tempus-utils/contracts/=lib/tempus-utils/contracts/", - "balancer-v2-monorepo/=lib/balancer-v2-monorepo/", - "chainlink/=lib/chainlink/", - "ds-test/=lib/forge-std/lib/ds-test/src/", - "erc4626-tests/=lib/chainlink/contracts/foundry-lib/openzeppelin-contracts/lib/erc4626-tests/", - "eth-gas-reporter/=node_modules/eth-gas-reporter/", - "forge-std/=lib/forge-std/src/", - "hardhat/=node_modules/hardhat/", - "openzeppelin-contracts/=lib/openzeppelin-contracts/", - "tempus-utils/=lib/tempus-utils/contracts/" - ], - "optimizer": { - "enabled": true, - "runs": 200000 - }, - "metadata": { - "bytecodeHash": "ipfs", - "appendCBOR": true - }, - "outputSelection": { - "*": { - "*": [ - "evm.bytecode", - "evm.deployedBytecode", - "devdoc", - "userdoc", - "metadata", - "abi" - ] - } - }, - "evmVersion": "london", - "viaIR": true, - "libraries": {} - } - }, - "ABI": "[{\"inputs\":[{\"internalType\":\"contract IRToken\",\"name\":\"rToken_\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AmountIsZero\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BorrowingSpreadExceedsMaximum\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotLiquidateLastPosition\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CollateralTokenAddressCannotBeZero\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CollateralTokenAlreadyAdded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CollateralTokenDisabled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CollateralTokenNotAdded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DelegateNotWhitelisted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FeeEatsUpAllReturnedCollateral\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxFeePercentage\",\"type\":\"uint256\"}],\"name\":\"FeeExceedsMaxFee\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"InvalidDebtToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidDelegateAddress\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidFeeRecipient\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidMaxFeePercentage\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidPosition\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaxFeePercentageOutOfRange\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"netDebt\",\"type\":\"uint256\"}],\"name\":\"NetDebtBelowMinimum\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newICR\",\"type\":\"uint256\"}],\"name\":\"NewICRLowerThanMCR\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoCollateralOrDebtChange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NothingToLiquidate\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PositionCollateralTokenMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PriceFeedAddressCannotBeZero\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RedemptionRebateExceedsMaximum\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RedemptionSpreadOutOfRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SplitLiquidationCollateralCannotBeZero\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"newTotalCollateral\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minimumCollateral\",\"type\":\"uint256\"}],\"name\":\"TotalCollateralCannotBeLowerThanMinCollateral\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"newTotalDebt\",\"type\":\"uint256\"}],\"name\":\"TotalDebtCannotBeLowerThanMinDebt\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Unsupported\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"WrongCollateralParamsForFullRepayment\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"baseRate\",\"type\":\"uint256\"}],\"name\":\"BaseRateUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"borrowingSpread\",\"type\":\"uint256\"}],\"name\":\"BorrowingSpreadUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"collateralAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isCollateralIncrease\",\"type\":\"bool\"}],\"name\":\"CollateralChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract IERC20Indexable\",\"name\":\"raftCollateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract IERC20Indexable\",\"name\":\"raftDebtToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract IPriceFeed\",\"name\":\"priceFeed\",\"type\":\"address\"}],\"name\":\"CollateralTokenAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"}],\"name\":\"CollateralTokenModified\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"debtAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isDebtIncrease\",\"type\":\"bool\"}],\"name\":\"DebtChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"delegate\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"whitelisted\",\"type\":\"bool\"}],\"name\":\"DelegateWhitelisted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"feeRecipient\",\"type\":\"address\"}],\"name\":\"FeeRecipientChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"lastFeeOpTime\",\"type\":\"uint256\"}],\"name\":\"LastFeeOpTimeUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"liquidator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"debtLiquidated\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"collateralLiquidated\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"collateralSentToLiquidator\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"collateralLiquidationFeePaid\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isRedistribution\",\"type\":\"bool\"}],\"name\":\"Liquidation\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"MintedFees\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferStarted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"}],\"name\":\"PositionClosed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"PositionCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IRToken\",\"name\":\"rToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"feeRecipient\",\"type\":\"address\"}],\"name\":\"PositionManagerDeployed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"feeAmount\",\"type\":\"uint256\"}],\"name\":\"RBorrowingFeePaid\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"redeemer\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"collateralSent\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"rebate\",\"type\":\"uint256\"}],\"name\":\"Redemption\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"redemptionRebate\",\"type\":\"uint256\"}],\"name\":\"RedemptionRebateUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"redemptionSpread\",\"type\":\"uint256\"}],\"name\":\"RedemptionSpreadUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"contract ISplitLiquidationCollateral\",\"name\":\"newSplitLiquidationCollateral\",\"type\":\"address\"}],\"name\":\"SplitLiquidationCollateralChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"TokensRecovered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"BETA\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_BORROWING_RATE\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_BORROWING_SPREAD\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MINUTE_DECAY_FACTOR\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"contract IPriceFeed\",\"name\":\"priceFeed\",\"type\":\"address\"},{\"internalType\":\"contract ISplitLiquidationCollateral\",\"name\":\"newSplitLiquidationCollateral\",\"type\":\"address\"},{\"internalType\":\"contract IERC20Indexable\",\"name\":\"raftCollateralToken_\",\"type\":\"address\"},{\"internalType\":\"contract IERC20Indexable\",\"name\":\"raftDebtToken_\",\"type\":\"address\"}],\"name\":\"addCollateralToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"contract IPriceFeed\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"contract ISplitLiquidationCollateral\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"addCollateralToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"baseRate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"borrowingSpread\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"collateralEnabled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"collateralInfo\",\"outputs\":[{\"internalType\":\"contract IERC20Indexable\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"contract IERC20Indexable\",\"name\":\"debtToken\",\"type\":\"address\"},{\"internalType\":\"contract IPriceFeed\",\"name\":\"priceFeed\",\"type\":\"address\"},{\"internalType\":\"contract ISplitLiquidationCollateral\",\"name\":\"splitLiquidation\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"lastFeeOperationTime\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"borrowingSpread\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"baseRate\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"redemptionSpread\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"redemptionRebate\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"}],\"name\":\"collateralTokenForPosition\",\"outputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"subtractedValue\",\"type\":\"uint256\"}],\"name\":\"decreaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"feeRecipient\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"debtAmount\",\"type\":\"uint256\"}],\"name\":\"getBorrowingFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"getBorrowingRate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"getBorrowingRateWithDecay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"collateralAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"priceDeviation\",\"type\":\"uint256\"}],\"name\":\"getRedemptionFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"collateralAmount\",\"type\":\"uint256\"}],\"name\":\"getRedemptionFeeWithDecay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"redemptionFee\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"getRedemptionRate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"getRedemptionRateWithDecay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"addedValue\",\"type\":\"uint256\"}],\"name\":\"increaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"delegate\",\"type\":\"address\"}],\"name\":\"isDelegateWhitelisted\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"isWhitelisted\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"lastFeeOperationTime\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"}],\"name\":\"liquidate\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"collateralChange\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"isCollateralIncrease\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"debtChange\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"isDebtIncrease\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"maxFeePercentage\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"contract IERC20Permit\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"deadline\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"internalType\":\"struct ERC20PermitSignature\",\"name\":\"permitSignature\",\"type\":\"tuple\"}],\"name\":\"managePosition\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"actualCollateralChange\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actualDebtChange\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mintFees\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pendingOwner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"positionManager\",\"outputs\":[{\"internalType\":\"contract IPositionManager\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"priceFeed\",\"outputs\":[{\"internalType\":\"contract IPriceFeed\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"r\",\"outputs\":[{\"internalType\":\"contract IRToken\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rToken\",\"outputs\":[{\"internalType\":\"contract IRToken\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"raftCollateralToken\",\"outputs\":[{\"internalType\":\"contract IERC20Indexable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"raftDebtToken\",\"outputs\":[{\"internalType\":\"contract IERC20Indexable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"recoverTokens\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"redeemCollateral\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"redemptionRebate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"redemptionSpread\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"newBorrowingSpread\",\"type\":\"uint256\"}],\"name\":\"setBorrowingSpread\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"}],\"name\":\"setCollateralEnabled\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newFeeRecipient\",\"type\":\"address\"}],\"name\":\"setFeeRecipient\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"newRedemptionRebate\",\"type\":\"uint256\"}],\"name\":\"setRedemptionRebate\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"newRedemptionSpread\",\"type\":\"uint256\"}],\"name\":\"setRedemptionSpread\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"contract ISplitLiquidationCollateral\",\"name\":\"newSplitLiquidationCollateral\",\"type\":\"address\"}],\"name\":\"setSplitLiquidationCollateral\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"splitLiquidationCollateral\",\"outputs\":[{\"internalType\":\"contract ISplitLiquidationCollateral\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"delegate\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"whitelisted\",\"type\":\"bool\"}],\"name\":\"whitelistDelegate\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - "ContractName": "InterestRatePositionManager", - "CompilerVersion": "v0.8.19+commit.7dd6d404", - "OptimizationUsed": 1, - "Runs": 200000, - "ConstructorArguments": "0x000000000000000000000000183015a9ba6ff60230fdeadc3f43b3d788b13e21", - "EVMVersion": "london", - "Library": "", - "LicenseType": "", - "Proxy": 0, - "SwarmSource": "" - } -] \ No newline at end of file +[{"SourceCode":{"language":"Solidity","sources":{"contracts/InterestRates/InterestRatePositionManager.f.sol":{"content":"// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.19;\n\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n}\n\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)\n\n/**\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n */\ninterface IERC20Permit {\n /**\n * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\n * given ``owner``'s signed approval.\n *\n * IMPORTANT: The same issues {IERC20-approve} has related to transaction\n * ordering also apply here.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `deadline` must be a timestamp in the future.\n * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\n * over the EIP712-formatted function arguments.\n * - the signature must use ``owner``'s current nonce (see {nonces}).\n *\n * For more information on the signature format, see the\n * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\n * section].\n */\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n /**\n * @dev Returns the current nonce for `owner`. This value must be\n * included whenever a signature is generated for {permit}.\n *\n * Every successful call to {permit} increases ``owner``'s nonce by one. This\n * prevents a signature from being used multiple times.\n */\n function nonces(address owner) external view returns (uint256);\n\n /**\n * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\n */\n // solhint-disable-next-line func-name-mixedcase\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n\n/// Parameters for ERC20Permit.permit call\nstruct ERC20PermitSignature {\n IERC20Permit token;\n uint256 value;\n uint256 deadline;\n uint8 v;\n bytes32 r;\n bytes32 s;\n}\n\nlibrary PermitHelper {\n function applyPermit(\n ERC20PermitSignature calldata p,\n address owner,\n address spender\n ) internal {\n p.token.permit(owner, spender, p.value, p.deadline, p.v, p.r, p.s);\n }\n\n function applyPermits(\n ERC20PermitSignature[] calldata permits,\n address owner,\n address spender\n ) internal {\n for (uint256 i = 0; i < permits.length; i++) {\n applyPermit(permits[i], owner, spender);\n }\n }\n}\n\n// OpenZeppelin Contracts v4.4.1 (interfaces/IERC3156FlashLender.sol)\n\n// OpenZeppelin Contracts (last updated v4.7.0) (interfaces/IERC3156FlashBorrower.sol)\n\n/**\n * @dev Interface of the ERC3156 FlashBorrower, as defined in\n * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].\n *\n * _Available since v4.1._\n */\ninterface IERC3156FlashBorrower {\n /**\n * @dev Receive a flash loan.\n * @param initiator The initiator of the loan.\n * @param token The loan currency.\n * @param amount The amount of tokens lent.\n * @param fee The additional amount of tokens to repay.\n * @param data Arbitrary data structure, intended to contain user-defined parameters.\n * @return The keccak256 hash of \"IERC3156FlashBorrower.onFlashLoan\"\n */\n function onFlashLoan(\n address initiator,\n address token,\n uint256 amount,\n uint256 fee,\n bytes calldata data\n ) external returns (bytes32);\n}\n\n/**\n * @dev Interface of the ERC3156 FlashLender, as defined in\n * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].\n *\n * _Available since v4.1._\n */\ninterface IERC3156FlashLender {\n /**\n * @dev The amount of currency available to be lended.\n * @param token The loan currency.\n * @return The amount of `token` that can be borrowed.\n */\n function maxFlashLoan(address token) external view returns (uint256);\n\n /**\n * @dev The fee to be charged for a given loan.\n * @param token The loan currency.\n * @param amount The amount of tokens lent.\n * @return The amount of `token` to be charged for the loan, on top of the returned principal.\n */\n function flashFee(address token, uint256 amount) external view returns (uint256);\n\n /**\n * @dev Initiate a flash loan.\n * @param receiver The receiver of the tokens in the loan, and the receiver of the callback.\n * @param token The loan currency.\n * @param amount The amount of tokens lent.\n * @param data Arbitrary data structure, intended to contain user-defined parameters.\n */\n function flashLoan(\n IERC3156FlashBorrower receiver,\n address token,\n uint256 amount,\n bytes calldata data\n ) external returns (bool);\n}\n\n/// @dev Interface to be used by contracts that collect fees. Contains fee recipient that can be changed by owner.\ninterface IFeeCollector {\n // --- Events ---\n\n /// @dev Fee Recipient is changed to @param feeRecipient address.\n /// @param feeRecipient New fee recipient address.\n event FeeRecipientChanged(address feeRecipient);\n\n // --- Errors ---\n\n /// @dev Invalid fee recipient.\n error InvalidFeeRecipient();\n\n // --- Functions ---\n\n /// @return Address of the current fee recipient.\n function feeRecipient() external view returns (address);\n\n /// @dev Sets new fee recipient address\n /// @param newFeeRecipient Address of the new fee recipient.\n function setFeeRecipient(address newFeeRecipient) external;\n}\n\ninterface IPositionManagerDependent {\n // --- Errors ---\n\n /// @dev Position Manager cannot be zero.\n error PositionManagerCannotBeZero();\n\n /// @dev Caller is not Position Manager.\n error CallerIsNotPositionManager(address caller);\n\n // --- Functions ---\n\n /// @dev Returns address of the PositionManager contract.\n function positionManager() external view returns (address);\n}\n\n/// @dev Interface of R stablecoin token. Implements some standards like IERC20, IERC20Permit, and IERC3156FlashLender.\n/// Raft's specific implementation contains IFeeCollector and IPositionManagerDependent.\n/// PositionManager can mint and burn R when particular actions happen with user's position.\ninterface IRToken is IERC20, IERC20Permit, IERC3156FlashLender, IFeeCollector, IPositionManagerDependent {\n // --- Events ---\n\n /// @dev New R token is deployed\n /// @param positionManager Address of the PositionManager contract that is authorized to mint and burn new tokens.\n /// @param flashMintFeeRecipient Address of flash mint fee recipient.\n event RDeployed(address positionManager, address flashMintFeeRecipient);\n\n /// @dev The Flash Mint Fee Percentage has been changed.\n /// @param flashMintFeePercentage The new Flash Mint Fee Percentage value.\n event FlashMintFeePercentageChanged(uint256 flashMintFeePercentage);\n\n /// --- Errors ---\n\n /// @dev Proposed flash mint fee percentage is too big.\n /// @param feePercentage Proposed flash mint fee percentage.\n error FlashFeePercentageTooBig(uint256 feePercentage);\n\n // --- Functions ---\n\n /// @return Number representing 100 percentage.\n function PERCENTAGE_BASE() external view returns (uint256);\n\n /// @dev Mints new tokens. Callable only by PositionManager contract.\n /// @param to Address that will receive newly minted tokens.\n /// @param amount Amount of tokens to mint.\n function mint(address to, uint256 amount) external;\n\n /// @dev Mints new tokens. Callable only by PositionManager contract.\n /// @param from Address of user whose tokens are burnt.\n /// @param amount Amount of tokens to burn.\n function burn(address from, uint256 amount) external;\n\n /// @return Maximum flash mint fee percentage that can be set by owner.\n function MAX_FLASH_MINT_FEE_PERCENTAGE() external view returns (uint256);\n\n /// @return Current flash mint fee percentage.\n function flashMintFeePercentage() external view returns (uint256);\n\n /// @dev Sets new flash mint fee percentage. Callable only by owner.\n /// @notice The proposed flash mint fee percentage cannot exceed `MAX_FLASH_MINT_FEE_PERCENTAGE`.\n /// @param feePercentage New flash fee percentage.\n function setFlashMintFeePercentage(uint256 feePercentage) external;\n}\n\ninterface IPriceOracle {\n // --- Errors ---\n\n /// @dev Contract initialized with an invalid deviation parameter.\n error InvalidDeviation();\n\n // --- Types ---\n\n struct PriceOracleResponse {\n bool isBrokenOrFrozen;\n bool priceChangeAboveMax;\n uint256 price;\n }\n\n // --- Functions ---\n\n /// @dev Return price oracle response which consists the following information: oracle is broken or frozen, the\n /// price change between two rounds is more than max, and the price.\n function getPriceOracleResponse() external returns (PriceOracleResponse memory);\n\n /// @dev Maximum time period allowed since oracle latest round data timestamp, beyond which oracle is considered\n /// frozen.\n function timeout() external view returns (uint256);\n\n /// @dev Used to convert a price answer to an 18-digit precision uint.\n function TARGET_DIGITS() external view returns (uint256);\n\n /// @dev price deviation for the oracle in percentage.\n function DEVIATION() external view returns (uint256);\n}\n\ninterface IPriceFeed {\n // --- Events ---\n\n /// @dev Last good price has been updated.\n event LastGoodPriceUpdated(uint256 lastGoodPrice);\n\n /// @dev Price difference between oracles has been updated.\n /// @param priceDifferenceBetweenOracles New price difference between oracles.\n event PriceDifferenceBetweenOraclesUpdated(uint256 priceDifferenceBetweenOracles);\n\n /// @dev Primary oracle has been updated.\n /// @param primaryOracle New primary oracle.\n event PrimaryOracleUpdated(IPriceOracle primaryOracle);\n\n /// @dev Secondary oracle has been updated.\n /// @param secondaryOracle New secondary oracle.\n event SecondaryOracleUpdated(IPriceOracle secondaryOracle);\n\n // --- Errors ---\n\n /// @dev Invalid primary oracle.\n error InvalidPrimaryOracle();\n\n /// @dev Invalid secondary oracle.\n error InvalidSecondaryOracle();\n\n /// @dev Primary oracle is broken or frozen or has bad result.\n error PrimaryOracleBrokenOrFrozenOrBadResult();\n\n /// @dev Invalid price difference between oracles.\n error InvalidPriceDifferenceBetweenOracles();\n\n // --- Functions ---\n\n /// @dev Return primary oracle address.\n function primaryOracle() external returns (IPriceOracle);\n\n /// @dev Return secondary oracle address\n function secondaryOracle() external returns (IPriceOracle);\n\n /// @dev The last good price seen from an oracle by Raft.\n function lastGoodPrice() external returns (uint256);\n\n /// @dev The maximum relative price difference between two oracle responses.\n function priceDifferenceBetweenOracles() external returns (uint256);\n\n /// @dev Set primary oracle address.\n /// @param newPrimaryOracle Primary oracle address.\n function setPrimaryOracle(IPriceOracle newPrimaryOracle) external;\n\n /// @dev Set secondary oracle address.\n /// @param newSecondaryOracle Secondary oracle address.\n function setSecondaryOracle(IPriceOracle newSecondaryOracle) external;\n\n /// @dev Set the maximum relative price difference between two oracle responses.\n /// @param newPriceDifferenceBetweenOracles The maximum relative price difference between two oracle responses.\n function setPriceDifferenceBetweenOracles(uint256 newPriceDifferenceBetweenOracles) external;\n\n /// @dev Returns the latest price obtained from the Oracle. Called by Raft functions that require a current price.\n ///\n /// Also callable by anyone externally.\n /// Non-view function - it stores the last good price seen by Raft.\n ///\n /// Uses a primary oracle and a fallback oracle in case primary fails. If both fail,\n /// it uses the last good price seen by Raft.\n ///\n /// @return currentPrice Returned price.\n /// @return deviation Deviation of the reported price in percentage.\n /// @notice Actual returned price is in range `currentPrice` +/- `currentPrice * deviation / ONE`\n function fetchPrice() external returns (uint256 currentPrice, uint256 deviation);\n}\n\ninterface IERC20Indexable is IERC20, IPositionManagerDependent {\n // --- Events ---\n\n /// @dev New token is deployed.\n /// @param positionManager Address of the PositionManager contract that is authorized to mint and burn new tokens.\n event ERC20IndexableDeployed(address positionManager);\n\n /// @dev New index has been set.\n /// @param newIndex Value of the new index.\n event IndexUpdated(uint256 newIndex);\n\n // --- Errors ---\n\n /// @dev Unsupported action for ERC20Indexable contract.\n error NotSupported();\n\n // --- Functions ---\n\n /// @return Precision for token index. Represents index that is equal to 1.\n function INDEX_PRECISION() external view returns (uint256);\n\n /// @return Current index value.\n function currentIndex() external view returns (uint256);\n\n /// @dev Sets new token index. Callable only by PositionManager contract.\n /// @param backingAmount Amount of backing token that is covered by total supply.\n function setIndex(uint256 backingAmount) external;\n\n /// @dev Mints new tokens. Callable only by PositionManager contract.\n /// @param to Address that will receive newly minted tokens.\n /// @param amount Amount of tokens to mint.\n function mint(address to, uint256 amount) external;\n\n /// @dev Mints new tokens. Callable only by PositionManager contract.\n /// @param from Address of user whose tokens are burnt.\n /// @param amount Amount of tokens to burn.\n function burn(address from, uint256 amount) external;\n}\n\ninterface ISplitLiquidationCollateral {\n // --- Functions ---\n\n /// @dev Returns lowest total debt that will be split.\n function LOW_TOTAL_DEBT() external view returns (uint256);\n\n /// @dev Minimum collateralization ratio for position\n function MCR() external view returns (uint256);\n\n /// @dev Splits collateral between protocol and liquidator.\n /// @param totalCollateral Amount of collateral to split.\n /// @param totalDebt Amount of debt to split.\n /// @param price Price of collateral.\n /// @param isRedistribution True if this is a redistribution.\n /// @return collateralToSendToProtocol Amount of collateral to send to protocol.\n /// @return collateralToSentToLiquidator Amount of collateral to send to liquidator.\n function split(\n uint256 totalCollateral,\n uint256 totalDebt,\n uint256 price,\n bool isRedistribution\n )\n external\n view\n returns (uint256 collateralToSendToProtocol, uint256 collateralToSentToLiquidator);\n}\n\n/// @dev Common interface for the Position Manager.\ninterface IPositionManager is IFeeCollector {\n // --- Types ---\n\n /// @dev Information for a Raft indexable collateral token.\n /// @param collateralToken The Raft indexable collateral token.\n /// @param debtToken Corresponding Raft indexable debt token.\n /// @param priceFeed The contract that provides a price for the collateral token.\n /// @param splitLiquidation The contract that calculates collateral split in case of liquidation.\n /// @param isEnabled Whether the token can be used as collateral or not.\n /// @param lastFeeOperationTime Timestamp of the last operation for the collateral token.\n /// @param borrowingSpread The current borrowing spread.\n /// @param baseRate The current base rate.\n /// @param redemptionSpread The current redemption spread.\n /// @param redemptionRebate Percentage of the redemption fee returned to redeemed positions.\n struct CollateralTokenInfo {\n IERC20Indexable collateralToken;\n IERC20Indexable debtToken;\n IPriceFeed priceFeed;\n ISplitLiquidationCollateral splitLiquidation;\n bool isEnabled;\n uint256 lastFeeOperationTime;\n uint256 borrowingSpread;\n uint256 baseRate;\n uint256 redemptionSpread;\n uint256 redemptionRebate;\n }\n\n // --- Events ---\n\n /// @dev New position manager has been token deployed.\n /// @param rToken The R token used by the position manager.\n /// @param feeRecipient The address of fee recipient.\n event PositionManagerDeployed(IRToken rToken, address feeRecipient);\n\n /// @dev New collateral token has been added added to the system.\n /// @param collateralToken The token used as collateral.\n /// @param raftCollateralToken The Raft indexable collateral token for the given collateral token.\n /// @param raftDebtToken The Raft indexable debt token for given collateral token.\n /// @param priceFeed The contract that provides price for the collateral token.\n event CollateralTokenAdded(\n IERC20 collateralToken,\n IERC20Indexable raftCollateralToken,\n IERC20Indexable raftDebtToken,\n IPriceFeed priceFeed\n );\n\n /// @dev Collateral token has been enabled or disabled.\n /// @param collateralToken The token used as collateral.\n /// @param isEnabled True if the token is enabled, false otherwise.\n event CollateralTokenModified(IERC20 collateralToken, bool isEnabled);\n\n /// @dev A delegate has been whitelisted for a certain position.\n /// @param position The position for which the delegate was whitelisted.\n /// @param delegate The delegate which was whitelisted.\n /// @param whitelisted Specifies whether the delegate whitelisting has been enabled (true) or disabled (false).\n event DelegateWhitelisted(address indexed position, address indexed delegate, bool whitelisted);\n\n /// @dev New position has been created.\n /// @param position The address of the user opening new position.\n /// @param collateralToken The token used as collateral for the created position.\n event PositionCreated(address indexed position, IERC20 indexed collateralToken);\n\n /// @dev The position has been closed by either repayment, liquidation, or redemption.\n /// @param position The address of the user whose position is closed.\n event PositionClosed(address indexed position);\n\n /// @dev Collateral amount for the position has been changed.\n /// @param position The address of the user that has opened the position.\n /// @param collateralToken The address of the collateral token being added to position.\n /// @param collateralAmount The amount of collateral added or removed.\n /// @param isCollateralIncrease Whether the collateral is added to the position or removed from it.\n event CollateralChanged(\n address indexed position, IERC20 indexed collateralToken, uint256 collateralAmount, bool isCollateralIncrease\n );\n\n /// @dev Debt amount for position has been changed.\n /// @param position The address of the user that has opened the position.\n /// @param collateralToken The address of the collateral token backing the debt.\n /// @param debtAmount The amount of debt added or removed.\n /// @param isDebtIncrease Whether the debt is added to the position or removed from it.\n event DebtChanged(\n address indexed position, IERC20 indexed collateralToken, uint256 debtAmount, bool isDebtIncrease\n );\n\n /// @dev Borrowing fee has been paid. Emitted only if the actual fee was paid - doesn't happen with no fees are\n /// paid.\n /// @param collateralToken Collateral token used to mint R.\n /// @param position The address of position's owner that triggered the fee payment.\n /// @param feeAmount The amount of tokens paid as the borrowing fee.\n event RBorrowingFeePaid(IERC20 collateralToken, address indexed position, uint256 feeAmount);\n\n /// @dev Liquidation has been executed.\n /// @param liquidator The liquidator that executed the liquidation.\n /// @param position The address of position's owner whose position was liquidated.\n /// @param collateralToken The collateral token used for the liquidation.\n /// @param debtLiquidated The total debt that was liquidated or redistributed.\n /// @param collateralLiquidated The total collateral liquidated.\n /// @param collateralSentToLiquidator The collateral amount sent to the liquidator.\n /// @param collateralLiquidationFeePaid The total collateral paid as the liquidation fee to the fee recipient.\n /// @param isRedistribution Whether the executed liquidation was redistribution or not.\n event Liquidation(\n address indexed liquidator,\n address indexed position,\n IERC20 indexed collateralToken,\n uint256 debtLiquidated,\n uint256 collateralLiquidated,\n uint256 collateralSentToLiquidator,\n uint256 collateralLiquidationFeePaid,\n bool isRedistribution\n );\n\n /// @dev Redemption has been executed.\n /// @param redeemer User that redeemed R.\n /// @param amount Amount of R that was redeemed.\n /// @param collateralSent The amount of collateral sent to the redeemer.\n /// @param fee The amount of fee paid to the fee recipient.\n /// @param rebate Redemption rebate amount.\n event Redemption(address indexed redeemer, uint256 amount, uint256 collateralSent, uint256 fee, uint256 rebate);\n\n /// @dev Borrowing spread has been updated.\n /// @param borrowingSpread The new borrowing spread.\n event BorrowingSpreadUpdated(uint256 borrowingSpread);\n\n /// @dev Redemption rebate has been updated.\n /// @param redemptionRebate The new redemption rebate.\n event RedemptionRebateUpdated(uint256 redemptionRebate);\n\n /// @dev Redemption spread has been updated.\n /// @param collateralToken Collateral token that the spread was set for.\n /// @param redemptionSpread The new redemption spread.\n event RedemptionSpreadUpdated(IERC20 collateralToken, uint256 redemptionSpread);\n\n /// @dev Base rate has been updated.\n /// @param collateralToken Collateral token that the baser rate was updated for.\n /// @param baseRate The new base rate.\n event BaseRateUpdated(IERC20 collateralToken, uint256 baseRate);\n\n /// @dev Last fee operation time has been updated.\n /// @param collateralToken Collateral token that the baser rate was updated for.\n /// @param lastFeeOpTime The new operation time.\n event LastFeeOpTimeUpdated(IERC20 collateralToken, uint256 lastFeeOpTime);\n\n /// @dev Split liquidation collateral has been changed.\n /// @param collateralToken Collateral token whose split liquidation collateral contract is set.\n /// @param newSplitLiquidationCollateral New value that was set to be split liquidation collateral.\n event SplitLiquidationCollateralChanged(\n IERC20 collateralToken, ISplitLiquidationCollateral indexed newSplitLiquidationCollateral\n );\n\n // --- Errors ---\n\n /// @dev Max fee percentage must be between borrowing spread and 100%.\n error InvalidMaxFeePercentage();\n\n /// @dev Max fee percentage must be between 0.5% and 100%.\n error MaxFeePercentageOutOfRange();\n\n /// @dev Amount is zero.\n error AmountIsZero();\n\n /// @dev Nothing to liquidate.\n error NothingToLiquidate();\n\n /// @dev Cannot liquidate last position.\n error CannotLiquidateLastPosition();\n\n /// @dev Cannot redeem collateral below minimum debt threshold.\n /// @param collateralToken Collateral token used to redeem.\n /// @param newTotalDebt New total debt backed by collateral, which is lower than minimum debt.\n error TotalDebtCannotBeLowerThanMinDebt(IERC20 collateralToken, uint256 newTotalDebt);\n\n /// @dev Cannot redeem collateral\n /// @param collateralToken Collateral token used to redeem.\n /// @param newTotalCollateral New total collateral, which is lower than minimum collateral.\n /// @param minimumCollateral Minimum collateral required to complete redeem\n error TotalCollateralCannotBeLowerThanMinCollateral(\n IERC20 collateralToken, uint256 newTotalCollateral, uint256 minimumCollateral\n );\n\n /// @dev Fee would eat up all returned collateral.\n error FeeEatsUpAllReturnedCollateral();\n\n /// @dev Borrowing spread exceeds maximum.\n error BorrowingSpreadExceedsMaximum();\n\n /// @dev Redemption rebate exceeds maximum.\n error RedemptionRebateExceedsMaximum();\n\n /// @dev Redemption spread is out of allowed range.\n error RedemptionSpreadOutOfRange();\n\n /// @dev There must be either a collateral change or a debt change.\n error NoCollateralOrDebtChange();\n\n /// @dev There is some collateral for position that doesn't have debt.\n error InvalidPosition();\n\n /// @dev An operation that would result in ICR < MCR is not permitted.\n /// @param newICR Resulting ICR that is below MCR.\n error NewICRLowerThanMCR(uint256 newICR);\n\n /// @dev Position's net debt must be greater than minimum.\n /// @param netDebt Net debt amount that is below minimum.\n error NetDebtBelowMinimum(uint256 netDebt);\n\n /// @dev The provided delegate address is invalid.\n error InvalidDelegateAddress();\n\n /// @dev A non-whitelisted delegate cannot adjust positions.\n error DelegateNotWhitelisted();\n\n /// @dev Fee exceeded provided maximum fee percentage.\n /// @param fee The fee amount.\n /// @param amount The amount of debt or collateral.\n /// @param maxFeePercentage The maximum fee percentage.\n error FeeExceedsMaxFee(uint256 fee, uint256 amount, uint256 maxFeePercentage);\n\n /// @dev Borrower uses a different collateral token already.\n error PositionCollateralTokenMismatch();\n\n /// @dev Collateral token address cannot be zero.\n error CollateralTokenAddressCannotBeZero();\n\n /// @dev Price feed address cannot be zero.\n error PriceFeedAddressCannotBeZero();\n\n /// @dev Collateral token already added.\n error CollateralTokenAlreadyAdded();\n\n /// @dev Collateral token is not added.\n error CollateralTokenNotAdded();\n\n /// @dev Collateral token is not enabled.\n error CollateralTokenDisabled();\n\n /// @dev Split liquidation collateral cannot be zero.\n error SplitLiquidationCollateralCannotBeZero();\n\n /// @dev Cannot change collateral in case of repaying the whole debt.\n error WrongCollateralParamsForFullRepayment();\n\n // --- Functions ---\n\n /// @return The R token used by position manager.\n function rToken() external view returns (IRToken);\n\n /// @dev Retrieves information about certain collateral type.\n /// @param collateralToken The token used as collateral.\n /// @return raftCollateralToken The Raft indexable collateral token.\n /// @return raftDebtToken The Raft indexable debt token.\n /// @return priceFeed The contract that provides a price for the collateral token.\n /// @return splitLiquidation The contract that calculates collateral split in case of liquidation.\n /// @return isEnabled Whether the collateral token can be used as collateral or not.\n /// @return lastFeeOperationTime Timestamp of the last operation for the collateral token.\n /// @return borrowingSpread The current borrowing spread.\n /// @return baseRate The current base rate.\n /// @return redemptionSpread The current redemption spread.\n /// @return redemptionRebate Percentage of the redemption fee returned to redeemed positions.\n function collateralInfo(IERC20 collateralToken)\n external\n view\n returns (\n IERC20Indexable raftCollateralToken,\n IERC20Indexable raftDebtToken,\n IPriceFeed priceFeed,\n ISplitLiquidationCollateral splitLiquidation,\n bool isEnabled,\n uint256 lastFeeOperationTime,\n uint256 borrowingSpread,\n uint256 baseRate,\n uint256 redemptionSpread,\n uint256 redemptionRebate\n );\n\n /// @param collateralToken Collateral token whose raft collateral indexable token is being queried.\n /// @return Raft collateral token address for given collateral token.\n function raftCollateralToken(IERC20 collateralToken) external view returns (IERC20Indexable);\n\n /// @param collateralToken Collateral token whose raft collateral indexable token is being queried.\n /// @return Raft debt token address for given collateral token.\n function raftDebtToken(IERC20 collateralToken) external view returns (IERC20Indexable);\n\n /// @param collateralToken Collateral token whose price feed contract is being queried.\n /// @return Price feed contract address for given collateral token.\n function priceFeed(IERC20 collateralToken) external view returns (IPriceFeed);\n\n /// @param collateralToken Collateral token whose split liquidation collateral is being queried.\n /// @return Returns address of the split liquidation collateral contract.\n function splitLiquidationCollateral(IERC20 collateralToken) external view returns (ISplitLiquidationCollateral);\n\n /// @param collateralToken Collateral token whose split liquidation collateral is being queried.\n /// @return Returns whether collateral is enabled or nor.\n function collateralEnabled(IERC20 collateralToken) external view returns (bool);\n\n /// @param collateralToken Collateral token we query last operation time fee for.\n /// @return The timestamp of the latest fee operation (redemption or new R issuance).\n function lastFeeOperationTime(IERC20 collateralToken) external view returns (uint256);\n\n /// @param collateralToken Collateral token we query borrowing spread for.\n /// @return The current borrowing spread.\n function borrowingSpread(IERC20 collateralToken) external view returns (uint256);\n\n /// @param collateralToken Collateral token we query base rate for.\n /// @return rate The base rate.\n function baseRate(IERC20 collateralToken) external view returns (uint256 rate);\n\n /// @param collateralToken Collateral token we query redemption spread for.\n /// @return The current redemption spread for collateral token.\n function redemptionSpread(IERC20 collateralToken) external view returns (uint256);\n\n /// @param collateralToken Collateral token we query redemption rebate for.\n /// @return rebate Percentage of the redemption fee returned to redeemed positions.\n function redemptionRebate(IERC20 collateralToken) external view returns (uint256);\n\n /// @param collateralToken Collateral token we query redemption rate for.\n /// @return rate The current redemption rate for collateral token.\n function getRedemptionRate(IERC20 collateralToken) external view returns (uint256 rate);\n\n /// @dev Returns the collateral token that a given position used for their position.\n /// @param position The address of the borrower.\n /// @return collateralToken The collateral token of the borrower's position.\n function collateralTokenForPosition(address position) external view returns (IERC20 collateralToken);\n\n /// @dev Adds a new collateral token to the protocol.\n /// @param collateralToken The new collateral token.\n /// @param priceFeed The price feed for the collateral token.\n /// @param newSplitLiquidationCollateral split liquidation collateral contract address.\n function addCollateralToken(\n IERC20 collateralToken,\n IPriceFeed priceFeed,\n ISplitLiquidationCollateral newSplitLiquidationCollateral\n )\n external;\n\n /// @dev Adds a new collateral token to the protocol.\n /// @param collateralToken The new collateral token.\n /// @param priceFeed The price feed for the collateral token.\n /// @param newSplitLiquidationCollateral split liquidation collateral contract address.\n /// @param raftCollateralToken_ Address of raft collateral token.\n /// @param raftDebtToken_ Address of raft debt token.\n function addCollateralToken(\n IERC20 collateralToken,\n IPriceFeed priceFeed,\n ISplitLiquidationCollateral newSplitLiquidationCollateral,\n IERC20Indexable raftCollateralToken_,\n IERC20Indexable raftDebtToken_\n )\n external;\n\n /// @dev Enables or disables a collateral token. Reverts if the collateral token has not been added.\n /// @param collateralToken The collateral token.\n /// @param isEnabled Whether the collateral token can be used as collateral or not.\n function setCollateralEnabled(IERC20 collateralToken, bool isEnabled) external;\n\n /// @dev Sets the new split liquidation collateral contract.\n /// @param collateralToken Collateral token whose split liquidation collateral is being set.\n /// @param newSplitLiquidationCollateral New split liquidation collateral contract address.\n function setSplitLiquidationCollateral(\n IERC20 collateralToken,\n ISplitLiquidationCollateral newSplitLiquidationCollateral\n )\n external;\n\n /// @dev Liquidates the borrower if its position's ICR is lower than the minimum collateral ratio.\n /// @param position The address of the borrower.\n function liquidate(address position) external;\n\n /// @dev Redeems the collateral token for a given debt amount. It sends @param debtAmount R to the system and\n /// redeems the corresponding amount of collateral from as many positions as are needed to fill the redemption\n /// request.\n /// @param collateralToken The token used as collateral.\n /// @param debtAmount The amount of debt to be redeemed. Must be greater than zero.\n /// @param maxFeePercentage The maximum fee percentage to pay for the redemption.\n function redeemCollateral(IERC20 collateralToken, uint256 debtAmount, uint256 maxFeePercentage) external;\n\n /// @dev Manages the position on behalf of a given borrower.\n /// @param collateralToken The token the borrower used as collateral.\n /// @param position The address of the borrower.\n /// @param collateralChange The amount of collateral to add or remove.\n /// @param isCollateralIncrease True if the collateral is being increased, false otherwise.\n /// @param debtChange The amount of R to add or remove. In case of repayment (isDebtIncrease = false)\n /// `type(uint256).max` value can be used to repay the whole outstanding loan.\n /// @param isDebtIncrease True if the debt is being increased, false otherwise.\n /// @param maxFeePercentage The maximum fee percentage to pay for the position management.\n /// @param permitSignature Optional permit signature for tokens that support IERC20Permit interface.\n /// @notice `permitSignature` it is ignored if permit signature is not for `collateralToken`.\n /// @notice In case of full debt repayment, `isCollateralIncrease` is ignored and `collateralChange` must be 0.\n /// These values are set to `false`(collateral decrease), and the whole collateral balance of the user.\n /// @return actualCollateralChange Actual amount of collateral added/removed.\n /// Can be different to `collateralChange` in case of full repayment.\n /// @return actualDebtChange Actual amount of debt added/removed.\n /// Can be different to `debtChange` in case of passing type(uint256).max as `debtChange`.\n function managePosition(\n IERC20 collateralToken,\n address position,\n uint256 collateralChange,\n bool isCollateralIncrease,\n uint256 debtChange,\n bool isDebtIncrease,\n uint256 maxFeePercentage,\n ERC20PermitSignature calldata permitSignature\n )\n external\n returns (uint256 actualCollateralChange, uint256 actualDebtChange);\n\n /// @return The max borrowing spread.\n function MAX_BORROWING_SPREAD() external view returns (uint256);\n\n /// @return The max borrowing rate.\n function MAX_BORROWING_RATE() external view returns (uint256);\n\n /// @dev Sets the new borrowing spread.\n /// @param collateralToken Collateral token we set borrowing spread for.\n /// @param newBorrowingSpread New borrowing spread to be used.\n function setBorrowingSpread(IERC20 collateralToken, uint256 newBorrowingSpread) external;\n\n /// @param collateralToken Collateral token we query borrowing rate for.\n /// @return The current borrowing rate.\n function getBorrowingRate(IERC20 collateralToken) external view returns (uint256);\n\n /// @param collateralToken Collateral token we query borrowing rate with decay for.\n /// @return The current borrowing rate with decay.\n function getBorrowingRateWithDecay(IERC20 collateralToken) external view returns (uint256);\n\n /// @dev Returns the borrowing fee for a given debt amount.\n /// @param collateralToken Collateral token we query borrowing fee for.\n /// @param debtAmount The amount of debt.\n /// @return The borrowing fee.\n function getBorrowingFee(IERC20 collateralToken, uint256 debtAmount) external view returns (uint256);\n\n /// @dev Sets the new redemption spread.\n /// @param newRedemptionSpread New redemption spread to be used.\n function setRedemptionSpread(IERC20 collateralToken, uint256 newRedemptionSpread) external;\n\n /// @dev Sets new redemption rebate percentage.\n /// @param newRedemptionRebate Value that is being set as a redemption rebate percentage.\n function setRedemptionRebate(IERC20 collateralToken, uint256 newRedemptionRebate) external;\n\n /// @param collateralToken Collateral token we query redemption rate with decay for.\n /// @return The current redemption rate with decay.\n function getRedemptionRateWithDecay(IERC20 collateralToken) external view returns (uint256);\n\n /// @dev Returns the redemption fee for a given collateral amount.\n /// @param collateralToken Collateral token we query redemption fee for.\n /// @param collateralAmount The amount of collateral.\n /// @param priceDeviation Deviation for the reported price by oracle in percentage.\n /// @return The redemption fee.\n function getRedemptionFee(\n IERC20 collateralToken,\n uint256 collateralAmount,\n uint256 priceDeviation\n )\n external\n view\n returns (uint256);\n\n /// @dev Returns the redemption fee with decay for a given collateral amount.\n /// @param collateralToken Collateral token we query redemption fee with decay for.\n /// @param collateralAmount The amount of collateral.\n /// @return The redemption fee with decay.\n function getRedemptionFeeWithDecay(\n IERC20 collateralToken,\n uint256 collateralAmount\n )\n external\n view\n returns (uint256);\n\n /// @return Half-life of 12h (720 min).\n /// @dev (1/2) = d^720 => d = (1/2)^(1/720)\n function MINUTE_DECAY_FACTOR() external view returns (uint256);\n\n /// @dev Returns if a given delegate is whitelisted for a given borrower.\n /// @param position The address of the borrower.\n /// @param delegate The address of the delegate.\n /// @return isWhitelisted True if the delegate is whitelisted for a given borrower, false otherwise.\n function isDelegateWhitelisted(address position, address delegate) external view returns (bool isWhitelisted);\n\n /// @dev Whitelists a delegate.\n /// @param delegate The address of the delegate.\n /// @param whitelisted True if delegate is being whitelisted, false otherwise.\n function whitelistDelegate(address delegate, bool whitelisted) external;\n\n /// @return Parameter by which to divide the redeemed fraction, in order to calc the new base rate from a\n /// redemption. Corresponds to (1 / ALPHA) in the white paper.\n function BETA() external view returns (uint256);\n}\n\n/// @dev Common interface for the Position Manager.\ninterface IInterestRatePositionManager is IPositionManager {\n // --- Events ---\n\n /// @dev Fees coming from accrued interest are minted.\n /// @param collateralToken Collateral token that fees are paid for.\n /// @param amount Amount of R minted.\n event MintedFees(IERC20 collateralToken, uint256 amount);\n\n // --- Errors ---\n\n /// @dev Only registered debt token can be caller.\n /// @param sender Actual caller.\n error InvalidDebtToken(address sender);\n\n // --- Functions ---\n\n /// @dev Mints fees coming from accrued interest. Can be called only from matching debt token.\n /// @param collateralToken Collateral token to mint fees for.\n /// @param amount Amount of R to mint.\n function mintFees(IERC20 collateralToken, uint256 amount) external;\n}\n\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n enum Rounding {\n Down, // Toward negative infinity\n Up, // Toward infinity\n Zero // Toward zero\n }\n\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a > b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a == 0 ? 0 : (a - 1) / b + 1;\n }\n\n /**\n * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\n * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)\n * with further edits by Uniswap Labs also under MIT license.\n */\n function mulDiv(\n uint256 x,\n uint256 y,\n uint256 denominator\n ) internal pure returns (uint256 result) {\n unchecked {\n // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use\n // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n // variables such that product = prod1 * 2^256 + prod0.\n uint256 prod0; // Least significant 256 bits of the product\n uint256 prod1; // Most significant 256 bits of the product\n assembly {\n let mm := mulmod(x, y, not(0))\n prod0 := mul(x, y)\n prod1 := sub(sub(mm, prod0), lt(mm, prod0))\n }\n\n // Handle non-overflow cases, 256 by 256 division.\n if (prod1 == 0) {\n return prod0 / denominator;\n }\n\n // Make sure the result is less than 2^256. Also prevents denominator == 0.\n require(denominator > prod1);\n\n ///////////////////////////////////////////////\n // 512 by 256 division.\n ///////////////////////////////////////////////\n\n // Make division exact by subtracting the remainder from [prod1 prod0].\n uint256 remainder;\n assembly {\n // Compute remainder using mulmod.\n remainder := mulmod(x, y, denominator)\n\n // Subtract 256 bit number from 512 bit number.\n prod1 := sub(prod1, gt(remainder, prod0))\n prod0 := sub(prod0, remainder)\n }\n\n // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.\n // See https://cs.stackexchange.com/q/138556/92363.\n\n // Does not overflow because the denominator cannot be zero at this stage in the function.\n uint256 twos = denominator & (~denominator + 1);\n assembly {\n // Divide denominator by twos.\n denominator := div(denominator, twos)\n\n // Divide [prod1 prod0] by twos.\n prod0 := div(prod0, twos)\n\n // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.\n twos := add(div(sub(0, twos), twos), 1)\n }\n\n // Shift in bits from prod1 into prod0.\n prod0 |= prod1 * twos;\n\n // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such\n // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for\n // four bits. That is, denominator * inv = 1 mod 2^4.\n uint256 inverse = (3 * denominator) ^ 2;\n\n // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works\n // in modular arithmetic, doubling the correct bits in each step.\n inverse *= 2 - denominator * inverse; // inverse mod 2^8\n inverse *= 2 - denominator * inverse; // inverse mod 2^16\n inverse *= 2 - denominator * inverse; // inverse mod 2^32\n inverse *= 2 - denominator * inverse; // inverse mod 2^64\n inverse *= 2 - denominator * inverse; // inverse mod 2^128\n inverse *= 2 - denominator * inverse; // inverse mod 2^256\n\n // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is\n // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1\n // is no longer required.\n result = prod0 * inverse;\n return result;\n }\n }\n\n /**\n * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.\n */\n function mulDiv(\n uint256 x,\n uint256 y,\n uint256 denominator,\n Rounding rounding\n ) internal pure returns (uint256) {\n uint256 result = mulDiv(x, y, denominator);\n if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {\n result += 1;\n }\n return result;\n }\n\n /**\n * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.\n *\n * Inspired by Henry S. Warren, Jr.'s \"Hacker's Delight\" (Chapter 11).\n */\n function sqrt(uint256 a) internal pure returns (uint256) {\n if (a == 0) {\n return 0;\n }\n\n // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.\n //\n // We know that the \"msb\" (most significant bit) of our target number `a` is a power of 2 such that we have\n // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.\n //\n // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`\n // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`\n // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`\n //\n // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.\n uint256 result = 1 << (log2(a) >> 1);\n\n // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,\n // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at\n // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision\n // into the expected uint128 result.\n unchecked {\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n return min(result, a / result);\n }\n }\n\n /**\n * @notice Calculates sqrt(a), following the selected rounding direction.\n */\n function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = sqrt(a);\n return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);\n }\n }\n\n /**\n * @dev Return the log in base 2, rounded down, of a positive value.\n * Returns 0 if given 0.\n */\n function log2(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >> 128 > 0) {\n value >>= 128;\n result += 128;\n }\n if (value >> 64 > 0) {\n value >>= 64;\n result += 64;\n }\n if (value >> 32 > 0) {\n value >>= 32;\n result += 32;\n }\n if (value >> 16 > 0) {\n value >>= 16;\n result += 16;\n }\n if (value >> 8 > 0) {\n value >>= 8;\n result += 8;\n }\n if (value >> 4 > 0) {\n value >>= 4;\n result += 4;\n }\n if (value >> 2 > 0) {\n value >>= 2;\n result += 2;\n }\n if (value >> 1 > 0) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log2(value);\n return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);\n }\n }\n\n /**\n * @dev Return the log in base 10, rounded down, of a positive value.\n * Returns 0 if given 0.\n */\n function log10(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >= 10**64) {\n value /= 10**64;\n result += 64;\n }\n if (value >= 10**32) {\n value /= 10**32;\n result += 32;\n }\n if (value >= 10**16) {\n value /= 10**16;\n result += 16;\n }\n if (value >= 10**8) {\n value /= 10**8;\n result += 8;\n }\n if (value >= 10**4) {\n value /= 10**4;\n result += 4;\n }\n if (value >= 10**2) {\n value /= 10**2;\n result += 2;\n }\n if (value >= 10**1) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log10(value);\n return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);\n }\n }\n\n /**\n * @dev Return the log in base 256, rounded down, of a positive value.\n * Returns 0 if given 0.\n *\n * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n */\n function log256(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >> 128 > 0) {\n value >>= 128;\n result += 16;\n }\n if (value >> 64 > 0) {\n value >>= 64;\n result += 8;\n }\n if (value >> 32 > 0) {\n value >>= 32;\n result += 4;\n }\n if (value >> 16 > 0) {\n value >>= 16;\n result += 2;\n }\n if (value >> 8 > 0) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log256(value);\n return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);\n }\n }\n}\n\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20Metadata is IERC20 {\n /**\n * @dev Returns the name of the token.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the symbol of the token.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the decimals places of the token.\n */\n function decimals() external view returns (uint8);\n}\n\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)\n\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\n * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\n *\n * _Available since v4.8._\n */\n function verifyCallResultFromTarget(\n address target,\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n if (success) {\n if (returndata.length == 0) {\n // only check isContract if the call was successful and the return data is empty\n // otherwise we already know that it was a contract\n require(isContract(target), \"Address: call to non-contract\");\n }\n return returndata;\n } else {\n _revert(returndata, errorMessage);\n }\n }\n\n /**\n * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason or using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n _revert(returndata, errorMessage);\n }\n }\n\n function _revert(bytes memory returndata, string memory errorMessage) private pure {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n /// @solidity memory-safe-assembly\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n}\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n function safePermit(\n IERC20Permit token,\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal {\n uint256 nonceBefore = token.nonces(owner);\n token.permit(owner, spender, value, deadline, v, r, s);\n uint256 nonceAfter = token.nonces(owner);\n require(nonceAfter == nonceBefore + 1, \"SafeERC20: permit did not succeed\");\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n\nlibrary Fixed256x18 {\n uint256 internal constant ONE = 1e18; // 18 decimal places\n\n function mulDown(uint256 a, uint256 b) internal pure returns (uint256) {\n return (a * b) / ONE;\n }\n\n function mulUp(uint256 a, uint256 b) internal pure returns (uint256) {\n uint256 product = a * b;\n\n if (product == 0) {\n return 0;\n } else {\n return ((product - 1) / ONE) + 1;\n }\n }\n\n function divDown(uint256 a, uint256 b) internal pure returns (uint256) {\n return (a * ONE) / b;\n }\n\n function divUp(uint256 a, uint256 b) internal pure returns (uint256) {\n if (a == 0) {\n return 0;\n } else {\n return (((a * ONE) - 1) / b) + 1;\n }\n }\n\n function complement(uint256 x) internal pure returns (uint256) {\n return (x < ONE) ? (ONE - x) : 0;\n }\n}\n\nlibrary MathUtils {\n // --- Constants ---\n\n /// @notice Represents 100%.\n /// @dev 1e18 is the scaling factor (100% == 1e18).\n uint256 public constant _100_PERCENT = Fixed256x18.ONE;\n\n /// @notice Precision for Nominal ICR (independent of price).\n /// @dev Rationale for the value:\n /// - Making it “too high” could lead to overflows.\n /// - Making it “too low” could lead to an ICR equal to zero, due to truncation from floor division.\n ///\n /// This value of 1e20 is chosen for safety: the NICR will only overflow for numerator > ~1e39 collateralToken,\n /// and will only truncate to 0 if the denominator is at least 1e20 times greater than the numerator.\n uint256 internal constant _NICR_PRECISION = 1e20;\n\n /// @notice Number of minutes in 1000 years.\n uint256 internal constant _MINUTES_IN_1000_YEARS = 1000 * 365 days / 1 minutes;\n\n // --- Functions ---\n\n /// @notice Multiplies two decimal numbers and use normal rounding rules:\n /// - round product up if 19'th mantissa digit >= 5\n /// - round product down if 19'th mantissa digit < 5.\n /// @param x First number.\n /// @param y Second number.\n function _decMul(uint256 x, uint256 y) internal pure returns (uint256 decProd) {\n decProd = (x * y + Fixed256x18.ONE / 2) / Fixed256x18.ONE;\n }\n\n /// @notice Exponentiation function for 18-digit decimal base, and integer exponent n.\n ///\n /// @dev Uses the efficient \"exponentiation by squaring\" algorithm. O(log(n)) complexity. The exponent is capped to\n /// avoid reverting due to overflow.\n ///\n /// If a period of > 1000 years is ever used as an exponent in either of the above functions, the result will be\n /// negligibly different from just passing the cap, since the decayed base rate will be 0 for 1000 years or > 1000\n /// years.\n /// @param base The decimal base.\n /// @param exponent The exponent.\n /// @return The result of the exponentiation.\n function _decPow(uint256 base, uint256 exponent) internal pure returns (uint256) {\n if (exponent == 0) {\n return Fixed256x18.ONE;\n }\n\n uint256 y = Fixed256x18.ONE;\n uint256 x = base;\n uint256 n = Math.min(exponent, _MINUTES_IN_1000_YEARS); // cap to avoid overflow\n\n // Exponentiation-by-squaring\n while (n > 1) {\n if (n % 2 != 0) {\n y = _decMul(x, y);\n }\n x = _decMul(x, x);\n n /= 2;\n }\n\n return _decMul(x, y);\n }\n\n /// @notice Computes the Nominal Individual Collateral Ratio (NICR) for given collateral and debt. If debt is zero,\n /// it returns the maximal value for uint256 (represents \"infinite\" CR).\n /// @param collateral Collateral amount.\n /// @param debt Debt amount.\n /// @return NICR.\n function _computeNominalCR(uint256 collateral, uint256 debt) internal pure returns (uint256) {\n return debt > 0 ? collateral * _NICR_PRECISION / debt : type(uint256).max;\n }\n\n /// @notice Computes the Collateral Ratio for given collateral, debt and price. If debt is zero, it returns the\n /// maximal value for uint256 (represents \"infinite\" CR).\n /// @param collateral Collateral amount.\n /// @param debt Debt amount.\n /// @param price Collateral price.\n /// @return Collateral ratio.\n function _computeCR(uint256 collateral, uint256 debt, uint256 price) internal pure returns (uint256) {\n return debt > 0 ? collateral * price / debt : type(uint256).max;\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol)\n\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\n * instead returning `false` on failure. This behavior is nonetheless\n * conventional and does not conflict with the expectations of ERC20\n * applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20 is Context, IERC20, IERC20Metadata {\n mapping(address => uint256) private _balances;\n\n mapping(address => mapping(address => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n string private _name;\n string private _symbol;\n\n /**\n * @dev Sets the values for {name} and {symbol}.\n *\n * The default value of {decimals} is 18. To select a different value for\n * {decimals} you should overload it.\n *\n * All two of these values are immutable: they can only be set once during\n * construction.\n */\n constructor(string memory name_, string memory symbol_) {\n _name = name_;\n _symbol = symbol_;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5.05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei. This is the value {ERC20} uses, unless this function is\n * overridden;\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view virtual override returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view virtual override returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `to` cannot be the zero address.\n * - the caller must have a balance of at least `amount`.\n */\n function transfer(address to, uint256 amount) public virtual override returns (bool) {\n address owner = _msgSender();\n _transfer(owner, to, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on\n * `transferFrom`. This is semantically equivalent to an infinite approval.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\n address owner = _msgSender();\n _approve(owner, spender, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20}.\n *\n * NOTE: Does not update the allowance if the current allowance\n * is the maximum `uint256`.\n *\n * Requirements:\n *\n * - `from` and `to` cannot be the zero address.\n * - `from` must have a balance of at least `amount`.\n * - the caller must have allowance for ``from``'s tokens of at least\n * `amount`.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) public virtual override returns (bool) {\n address spender = _msgSender();\n _spendAllowance(from, spender, amount);\n _transfer(from, to, amount);\n return true;\n }\n\n /**\n * @dev Atomically increases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n address owner = _msgSender();\n _approve(owner, spender, allowance(owner, spender) + addedValue);\n return true;\n }\n\n /**\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `spender` must have allowance for the caller of at least\n * `subtractedValue`.\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n address owner = _msgSender();\n uint256 currentAllowance = allowance(owner, spender);\n require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\n unchecked {\n _approve(owner, spender, currentAllowance - subtractedValue);\n }\n\n return true;\n }\n\n /**\n * @dev Moves `amount` of tokens from `from` to `to`.\n *\n * This internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `from` must have a balance of at least `amount`.\n */\n function _transfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {\n require(from != address(0), \"ERC20: transfer from the zero address\");\n require(to != address(0), \"ERC20: transfer to the zero address\");\n\n _beforeTokenTransfer(from, to, amount);\n\n uint256 fromBalance = _balances[from];\n require(fromBalance >= amount, \"ERC20: transfer amount exceeds balance\");\n unchecked {\n _balances[from] = fromBalance - amount;\n // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by\n // decrementing then incrementing.\n _balances[to] += amount;\n }\n\n emit Transfer(from, to, amount);\n\n _afterTokenTransfer(from, to, amount);\n }\n\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n */\n function _mint(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: mint to the zero address\");\n\n _beforeTokenTransfer(address(0), account, amount);\n\n _totalSupply += amount;\n unchecked {\n // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.\n _balances[account] += amount;\n }\n emit Transfer(address(0), account, amount);\n\n _afterTokenTransfer(address(0), account, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`, reducing the\n * total supply.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n */\n function _burn(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: burn from the zero address\");\n\n _beforeTokenTransfer(account, address(0), amount);\n\n uint256 accountBalance = _balances[account];\n require(accountBalance >= amount, \"ERC20: burn amount exceeds balance\");\n unchecked {\n _balances[account] = accountBalance - amount;\n // Overflow not possible: amount <= accountBalance <= totalSupply.\n _totalSupply -= amount;\n }\n\n emit Transfer(account, address(0), amount);\n\n _afterTokenTransfer(account, address(0), amount);\n }\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n *\n * This internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n */\n function _approve(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n require(owner != address(0), \"ERC20: approve from the zero address\");\n require(spender != address(0), \"ERC20: approve to the zero address\");\n\n _allowances[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n /**\n * @dev Updates `owner` s allowance for `spender` based on spent `amount`.\n *\n * Does not update the allowance amount in case of infinite allowance.\n * Revert if not enough allowance is available.\n *\n * Might emit an {Approval} event.\n */\n function _spendAllowance(\n address owner,\n address spender,\n uint256 amount\n ) internal virtual {\n uint256 currentAllowance = allowance(owner, spender);\n if (currentAllowance != type(uint256).max) {\n require(currentAllowance >= amount, \"ERC20: insufficient allowance\");\n unchecked {\n _approve(owner, spender, currentAllowance - amount);\n }\n }\n }\n\n /**\n * @dev Hook that is called before any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * will be transferred to `to`.\n * - when `from` is zero, `amount` tokens will be minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n\n /**\n * @dev Hook that is called after any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * has been transferred to `to`.\n * - when `from` is zero, `amount` tokens have been minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _afterTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n}\n\n// OpenZeppelin Contracts (last updated v4.8.0) (access/Ownable2Step.sol)\n\n// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n _checkOwner();\n _;\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if the sender is not the owner.\n */\n function _checkOwner() internal view virtual {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n\n/**\n * @dev Contract module which provides access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership} and {acceptOwnership}.\n *\n * This module is used through inheritance. It will make available all functions\n * from parent (Ownable).\n */\nabstract contract Ownable2Step is Ownable {\n address private _pendingOwner;\n\n event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Returns the address of the pending owner.\n */\n function pendingOwner() public view virtual returns (address) {\n return _pendingOwner;\n }\n\n /**\n * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual override onlyOwner {\n _pendingOwner = newOwner;\n emit OwnershipTransferStarted(owner(), newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual override {\n delete _pendingOwner;\n super._transferOwnership(newOwner);\n }\n\n /**\n * @dev The new owner accepts the ownership transfer.\n */\n function acceptOwnership() external {\n address sender = _msgSender();\n require(pendingOwner() == sender, \"Ownable2Step: caller is not the new owner\");\n _transferOwnership(sender);\n }\n}\n\n/**\n * @dev Extension of {ERC20} that adds a cap to the supply of tokens.\n */\nabstract contract ERC20Capped is ERC20, Ownable2Step {\n uint256 public cap;\n\n /**\n * @dev Total supply cap has been exceeded.\n */\n error ERC20ExceededCap();\n\n /**\n * @dev The supplied cap is not a valid cap.\n */\n error ERC20InvalidCap(uint256 cap);\n\n constructor(uint256 cap_) {\n setCap(cap_);\n }\n\n /**\n * @dev Sets the value of the `cap`.\n */\n function setCap(uint256 cap_) public onlyOwner {\n if (cap_ == 0) {\n revert ERC20InvalidCap(0);\n }\n cap = cap_;\n }\n\n /**\n * @dev See {ERC20-_mint}.\n */\n function _mint(address account, uint256 amount) internal virtual override {\n if (totalSupply() + amount > cap) {\n revert ERC20ExceededCap();\n }\n super._mint(account, amount);\n }\n}\n\nabstract contract PositionManagerDependent is IPositionManagerDependent {\n // --- Immutable variables ---\n\n address public immutable override positionManager;\n\n // --- Modifiers ---\n\n modifier onlyPositionManager() {\n if (msg.sender != positionManager) {\n revert CallerIsNotPositionManager(msg.sender);\n }\n _;\n }\n\n // --- Constructor ---\n\n constructor(address positionManager_) {\n if (positionManager_ == address(0)) {\n revert PositionManagerCannotBeZero();\n }\n positionManager = positionManager_;\n }\n}\n\ncontract ERC20Indexable is IERC20Indexable, ERC20Capped, PositionManagerDependent {\n // --- Types ---\n\n using Fixed256x18 for uint256;\n\n // --- Constants ---\n\n uint256 public constant override INDEX_PRECISION = Fixed256x18.ONE;\n\n // --- Variables ---\n\n uint256 internal storedIndex;\n\n // --- Constructor ---\n\n constructor(\n address positionManager_,\n string memory name_,\n string memory symbol_,\n uint256 cap_\n )\n ERC20(name_, symbol_)\n ERC20Capped(cap_)\n PositionManagerDependent(positionManager_)\n {\n storedIndex = INDEX_PRECISION;\n emit ERC20IndexableDeployed(positionManager_);\n }\n\n // --- Functions ---\n\n function mint(address to, uint256 amount) public virtual override onlyPositionManager {\n _mint(to, amount.divUp(storedIndex));\n }\n\n function burn(address from, uint256 amount) public virtual override onlyPositionManager {\n _burn(from, amount == type(uint256).max ? ERC20.balanceOf(from) : amount.divUp(storedIndex));\n }\n\n function setIndex(uint256 backingAmount) external override onlyPositionManager {\n uint256 supply = ERC20.totalSupply();\n uint256 newIndex = (backingAmount == 0 && supply == 0) ? INDEX_PRECISION : backingAmount.divUp(supply);\n storedIndex = newIndex;\n emit IndexUpdated(newIndex);\n }\n\n function currentIndex() public view virtual override returns (uint256) {\n return storedIndex;\n }\n\n function totalSupply() public view virtual override(IERC20, ERC20) returns (uint256) {\n return ERC20.totalSupply().mulDown(currentIndex());\n }\n\n function balanceOf(address account) public view virtual override(IERC20, ERC20) returns (uint256) {\n return ERC20.balanceOf(account).mulDown(currentIndex());\n }\n\n function transfer(address, uint256) public virtual override(IERC20, ERC20) returns (bool) {\n revert NotSupported();\n }\n\n function allowance(address, address) public view virtual override(IERC20, ERC20) returns (uint256) {\n revert NotSupported();\n }\n\n function approve(address, uint256) public virtual override(IERC20, ERC20) returns (bool) {\n revert NotSupported();\n }\n\n function transferFrom(address, address, uint256) public virtual override(IERC20, ERC20) returns (bool) {\n revert NotSupported();\n }\n\n function increaseAllowance(address, uint256) public virtual override returns (bool) {\n revert NotSupported();\n }\n\n function decreaseAllowance(address, uint256) public virtual override returns (bool) {\n revert NotSupported();\n }\n}\n\nabstract contract FeeCollector is Ownable2Step, IFeeCollector {\n // --- Variables ---\n\n address public override feeRecipient;\n\n // --- Constructor ---\n\n /// @param feeRecipient_ Address of the fee recipient to initialize contract with.\n constructor(address feeRecipient_) {\n if (feeRecipient_ == address(0)) {\n revert InvalidFeeRecipient();\n }\n\n feeRecipient = feeRecipient_;\n }\n\n // --- Functions ---\n\n function setFeeRecipient(address newFeeRecipient) external onlyOwner {\n if (newFeeRecipient == address(0)) {\n revert InvalidFeeRecipient();\n }\n\n feeRecipient = newFeeRecipient;\n emit FeeRecipientChanged(newFeeRecipient);\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol)\n\n/**\n * @dev Contract module that helps prevent reentrant calls to a function.\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * Note that because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n */\nabstract contract ReentrancyGuard {\n // Booleans are more expensive than uint256 or any type that takes up a full\n // word because each write operation emits an extra SLOAD to first read the\n // slot's contents, replace the bits taken up by the boolean, and then write\n // back. This is the compiler's defense against contract upgrades and\n // pointer aliasing, and it cannot be disabled.\n\n // The values being non-zero value makes deployment a bit more expensive,\n // but in exchange the refund on every call to nonReentrant will be lower in\n // amount. Since refunds are capped to a percentage of the total\n // transaction's gas, it is best to keep them low in cases like this one, to\n // increase the likelihood of the full refund coming into effect.\n uint256 private constant _NOT_ENTERED = 1;\n uint256 private constant _ENTERED = 2;\n\n uint256 private _status;\n\n constructor() {\n _status = _NOT_ENTERED;\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and making it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n _nonReentrantBefore();\n _;\n _nonReentrantAfter();\n }\n\n function _nonReentrantBefore() private {\n // On the first call to nonReentrant, _status will be _NOT_ENTERED\n require(_status != _ENTERED, \"ReentrancyGuard: reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n _status = _ENTERED;\n }\n\n function _nonReentrantAfter() private {\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n _status = _NOT_ENTERED;\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/extensions/draft-ERC20Permit.sol)\n\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol)\n\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n bytes16 private constant _SYMBOLS = \"0123456789abcdef\";\n uint8 private constant _ADDRESS_LENGTH = 20;\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n */\n function toString(uint256 value) internal pure returns (string memory) {\n unchecked {\n uint256 length = Math.log10(value) + 1;\n string memory buffer = new string(length);\n uint256 ptr;\n /// @solidity memory-safe-assembly\n assembly {\n ptr := add(buffer, add(32, length))\n }\n while (true) {\n ptr--;\n /// @solidity memory-safe-assembly\n assembly {\n mstore8(ptr, byte(mod(value, 10), _SYMBOLS))\n }\n value /= 10;\n if (value == 0) break;\n }\n return buffer;\n }\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n */\n function toHexString(uint256 value) internal pure returns (string memory) {\n unchecked {\n return toHexString(value, Math.log256(value) + 1);\n }\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n */\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n bytes memory buffer = new bytes(2 * length + 2);\n buffer[0] = \"0\";\n buffer[1] = \"x\";\n for (uint256 i = 2 * length + 1; i > 1; --i) {\n buffer[i] = _SYMBOLS[value & 0xf];\n value >>= 4;\n }\n require(value == 0, \"Strings: hex length insufficient\");\n return string(buffer);\n }\n\n /**\n * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.\n */\n function toHexString(address addr) internal pure returns (string memory) {\n return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);\n }\n}\n\n/**\n * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.\n *\n * These functions can be used to verify that a message was signed by the holder\n * of the private keys of a given address.\n */\nlibrary ECDSA {\n enum RecoverError {\n NoError,\n InvalidSignature,\n InvalidSignatureLength,\n InvalidSignatureS,\n InvalidSignatureV // Deprecated in v4.8\n }\n\n function _throwError(RecoverError error) private pure {\n if (error == RecoverError.NoError) {\n return; // no error: do nothing\n } else if (error == RecoverError.InvalidSignature) {\n revert(\"ECDSA: invalid signature\");\n } else if (error == RecoverError.InvalidSignatureLength) {\n revert(\"ECDSA: invalid signature length\");\n } else if (error == RecoverError.InvalidSignatureS) {\n revert(\"ECDSA: invalid signature 's' value\");\n }\n }\n\n /**\n * @dev Returns the address that signed a hashed message (`hash`) with\n * `signature` or error string. This address can then be used for verification purposes.\n *\n * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:\n * this function rejects them by requiring the `s` value to be in the lower\n * half order, and the `v` value to be either 27 or 28.\n *\n * IMPORTANT: `hash` _must_ be the result of a hash operation for the\n * verification to be secure: it is possible to craft signatures that\n * recover to arbitrary addresses for non-hashed data. A safe way to ensure\n * this is by receiving a hash of the original message (which may otherwise\n * be too long), and then calling {toEthSignedMessageHash} on it.\n *\n * Documentation for signature generation:\n * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]\n * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]\n *\n * _Available since v4.3._\n */\n function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {\n if (signature.length == 65) {\n bytes32 r;\n bytes32 s;\n uint8 v;\n // ecrecover takes the signature parameters, and the only way to get them\n // currently is to use assembly.\n /// @solidity memory-safe-assembly\n assembly {\n r := mload(add(signature, 0x20))\n s := mload(add(signature, 0x40))\n v := byte(0, mload(add(signature, 0x60)))\n }\n return tryRecover(hash, v, r, s);\n } else {\n return (address(0), RecoverError.InvalidSignatureLength);\n }\n }\n\n /**\n * @dev Returns the address that signed a hashed message (`hash`) with\n * `signature`. This address can then be used for verification purposes.\n *\n * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:\n * this function rejects them by requiring the `s` value to be in the lower\n * half order, and the `v` value to be either 27 or 28.\n *\n * IMPORTANT: `hash` _must_ be the result of a hash operation for the\n * verification to be secure: it is possible to craft signatures that\n * recover to arbitrary addresses for non-hashed data. A safe way to ensure\n * this is by receiving a hash of the original message (which may otherwise\n * be too long), and then calling {toEthSignedMessageHash} on it.\n */\n function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, signature);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.\n *\n * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]\n *\n * _Available since v4.3._\n */\n function tryRecover(\n bytes32 hash,\n bytes32 r,\n bytes32 vs\n ) internal pure returns (address, RecoverError) {\n bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);\n uint8 v = uint8((uint256(vs) >> 255) + 27);\n return tryRecover(hash, v, r, s);\n }\n\n /**\n * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.\n *\n * _Available since v4.2._\n */\n function recover(\n bytes32 hash,\n bytes32 r,\n bytes32 vs\n ) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, r, vs);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Overload of {ECDSA-tryRecover} that receives the `v`,\n * `r` and `s` signature fields separately.\n *\n * _Available since v4.3._\n */\n function tryRecover(\n bytes32 hash,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal pure returns (address, RecoverError) {\n // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature\n // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines\n // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most\n // signatures from current libraries generate a unique signature with an s-value in the lower half order.\n //\n // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value\n // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or\n // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept\n // these malleable signatures as well.\n if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {\n return (address(0), RecoverError.InvalidSignatureS);\n }\n\n // If the signature is valid (and not malleable), return the signer address\n address signer = ecrecover(hash, v, r, s);\n if (signer == address(0)) {\n return (address(0), RecoverError.InvalidSignature);\n }\n\n return (signer, RecoverError.NoError);\n }\n\n /**\n * @dev Overload of {ECDSA-recover} that receives the `v`,\n * `r` and `s` signature fields separately.\n */\n function recover(\n bytes32 hash,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, v, r, s);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Returns an Ethereum Signed Message, created from a `hash`. This\n * produces hash corresponding to the one signed with the\n * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]\n * JSON-RPC method as part of EIP-191.\n *\n * See {recover}.\n */\n function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {\n // 32 is the length in bytes of hash,\n // enforced by the type signature above\n return keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n32\", hash));\n }\n\n /**\n * @dev Returns an Ethereum Signed Message, created from `s`. This\n * produces hash corresponding to the one signed with the\n * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]\n * JSON-RPC method as part of EIP-191.\n *\n * See {recover}.\n */\n function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {\n return keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n\", Strings.toString(s.length), s));\n }\n\n /**\n * @dev Returns an Ethereum Signed Typed Data, created from a\n * `domainSeparator` and a `structHash`. This produces hash corresponding\n * to the one signed with the\n * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]\n * JSON-RPC method as part of EIP-712.\n *\n * See {recover}.\n */\n function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {\n return keccak256(abi.encodePacked(\"\\x19\\x01\", domainSeparator, structHash));\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/EIP712.sol)\n\n/**\n * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.\n *\n * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,\n * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding\n * they need in their contracts using a combination of `abi.encode` and `keccak256`.\n *\n * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding\n * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA\n * ({_hashTypedDataV4}).\n *\n * The implementation of the domain separator was designed to be as efficient as possible while still properly updating\n * the chain id to protect against replay attacks on an eventual fork of the chain.\n *\n * NOTE: This contract implements the version of the encoding known as \"v4\", as implemented by the JSON RPC method\n * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].\n *\n * _Available since v3.4._\n */\nabstract contract EIP712 {\n /* solhint-disable var-name-mixedcase */\n // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to\n // invalidate the cached domain separator if the chain id changes.\n bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;\n uint256 private immutable _CACHED_CHAIN_ID;\n address private immutable _CACHED_THIS;\n\n bytes32 private immutable _HASHED_NAME;\n bytes32 private immutable _HASHED_VERSION;\n bytes32 private immutable _TYPE_HASH;\n\n /* solhint-enable var-name-mixedcase */\n\n /**\n * @dev Initializes the domain separator and parameter caches.\n *\n * The meaning of `name` and `version` is specified in\n * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:\n *\n * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.\n * - `version`: the current major version of the signing domain.\n *\n * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart\n * contract upgrade].\n */\n constructor(string memory name, string memory version) {\n bytes32 hashedName = keccak256(bytes(name));\n bytes32 hashedVersion = keccak256(bytes(version));\n bytes32 typeHash = keccak256(\n \"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\"\n );\n _HASHED_NAME = hashedName;\n _HASHED_VERSION = hashedVersion;\n _CACHED_CHAIN_ID = block.chainid;\n _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);\n _CACHED_THIS = address(this);\n _TYPE_HASH = typeHash;\n }\n\n /**\n * @dev Returns the domain separator for the current chain.\n */\n function _domainSeparatorV4() internal view returns (bytes32) {\n if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) {\n return _CACHED_DOMAIN_SEPARATOR;\n } else {\n return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);\n }\n }\n\n function _buildDomainSeparator(\n bytes32 typeHash,\n bytes32 nameHash,\n bytes32 versionHash\n ) private view returns (bytes32) {\n return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));\n }\n\n /**\n * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this\n * function returns the hash of the fully encoded EIP712 message for this domain.\n *\n * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:\n *\n * ```solidity\n * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(\n * keccak256(\"Mail(address to,string contents)\"),\n * mailTo,\n * keccak256(bytes(mailContents))\n * )));\n * address signer = ECDSA.recover(digest, signature);\n * ```\n */\n function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {\n return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);\n }\n}\n\n// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)\n\n/**\n * @title Counters\n * @author Matt Condon (@shrugs)\n * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number\n * of elements in a mapping, issuing ERC721 ids, or counting request ids.\n *\n * Include with `using Counters for Counters.Counter;`\n */\nlibrary Counters {\n struct Counter {\n // This variable should never be directly accessed by users of the library: interactions must be restricted to\n // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add\n // this feature: see https://github.com/ethereum/solidity/issues/4637\n uint256 _value; // default: 0\n }\n\n function current(Counter storage counter) internal view returns (uint256) {\n return counter._value;\n }\n\n function increment(Counter storage counter) internal {\n unchecked {\n counter._value += 1;\n }\n }\n\n function decrement(Counter storage counter) internal {\n uint256 value = counter._value;\n require(value > 0, \"Counter: decrement overflow\");\n unchecked {\n counter._value = value - 1;\n }\n }\n\n function reset(Counter storage counter) internal {\n counter._value = 0;\n }\n}\n\n/**\n * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n *\n * _Available since v3.4._\n */\nabstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {\n using Counters for Counters.Counter;\n\n mapping(address => Counters.Counter) private _nonces;\n\n // solhint-disable-next-line var-name-mixedcase\n bytes32 private constant _PERMIT_TYPEHASH =\n keccak256(\"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)\");\n /**\n * @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`.\n * However, to ensure consistency with the upgradeable transpiler, we will continue\n * to reserve a slot.\n * @custom:oz-renamed-from _PERMIT_TYPEHASH\n */\n // solhint-disable-next-line var-name-mixedcase\n bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT;\n\n /**\n * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `\"1\"`.\n *\n * It's a good idea to use the same `name` that is defined as the ERC20 token name.\n */\n constructor(string memory name) EIP712(name, \"1\") {}\n\n /**\n * @dev See {IERC20Permit-permit}.\n */\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) public virtual override {\n require(block.timestamp <= deadline, \"ERC20Permit: expired deadline\");\n\n bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));\n\n bytes32 hash = _hashTypedDataV4(structHash);\n\n address signer = ECDSA.recover(hash, v, r, s);\n require(signer == owner, \"ERC20Permit: invalid signature\");\n\n _approve(owner, spender, value);\n }\n\n /**\n * @dev See {IERC20Permit-nonces}.\n */\n function nonces(address owner) public view virtual override returns (uint256) {\n return _nonces[owner].current();\n }\n\n /**\n * @dev See {IERC20Permit-DOMAIN_SEPARATOR}.\n */\n // solhint-disable-next-line func-name-mixedcase\n function DOMAIN_SEPARATOR() external view override returns (bytes32) {\n return _domainSeparatorV4();\n }\n\n /**\n * @dev \"Consume a nonce\": return the current value and increment.\n *\n * _Available since v4.1._\n */\n function _useNonce(address owner) internal virtual returns (uint256 current) {\n Counters.Counter storage nonce = _nonces[owner];\n current = nonce.current();\n nonce.increment();\n }\n}\n\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/extensions/ERC20FlashMint.sol)\n\n/**\n * @dev Implementation of the ERC3156 Flash loans extension, as defined in\n * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].\n *\n * Adds the {flashLoan} method, which provides flash loan support at the token\n * level. By default there is no fee, but this can be changed by overriding {flashFee}.\n *\n * _Available since v4.1._\n */\nabstract contract ERC20FlashMint is ERC20, IERC3156FlashLender {\n bytes32 private constant _RETURN_VALUE = keccak256(\"ERC3156FlashBorrower.onFlashLoan\");\n\n /**\n * @dev Returns the maximum amount of tokens available for loan.\n * @param token The address of the token that is requested.\n * @return The amount of token that can be loaned.\n */\n function maxFlashLoan(address token) public view virtual override returns (uint256) {\n return token == address(this) ? type(uint256).max - ERC20.totalSupply() : 0;\n }\n\n /**\n * @dev Returns the fee applied when doing flash loans. This function calls\n * the {_flashFee} function which returns the fee applied when doing flash\n * loans.\n * @param token The token to be flash loaned.\n * @param amount The amount of tokens to be loaned.\n * @return The fees applied to the corresponding flash loan.\n */\n function flashFee(address token, uint256 amount) public view virtual override returns (uint256) {\n require(token == address(this), \"ERC20FlashMint: wrong token\");\n return _flashFee(token, amount);\n }\n\n /**\n * @dev Returns the fee applied when doing flash loans. By default this\n * implementation has 0 fees. This function can be overloaded to make\n * the flash loan mechanism deflationary.\n * @param token The token to be flash loaned.\n * @param amount The amount of tokens to be loaned.\n * @return The fees applied to the corresponding flash loan.\n */\n function _flashFee(address token, uint256 amount) internal view virtual returns (uint256) {\n // silence warning about unused variable without the addition of bytecode.\n token;\n amount;\n return 0;\n }\n\n /**\n * @dev Returns the receiver address of the flash fee. By default this\n * implementation returns the address(0) which means the fee amount will be burnt.\n * This function can be overloaded to change the fee receiver.\n * @return The address for which the flash fee will be sent to.\n */\n function _flashFeeReceiver() internal view virtual returns (address) {\n return address(0);\n }\n\n /**\n * @dev Performs a flash loan. New tokens are minted and sent to the\n * `receiver`, who is required to implement the {IERC3156FlashBorrower}\n * interface. By the end of the flash loan, the receiver is expected to own\n * amount + fee tokens and have them approved back to the token contract itself so\n * they can be burned.\n * @param receiver The receiver of the flash loan. Should implement the\n * {IERC3156FlashBorrower-onFlashLoan} interface.\n * @param token The token to be flash loaned. Only `address(this)` is\n * supported.\n * @param amount The amount of tokens to be loaned.\n * @param data An arbitrary datafield that is passed to the receiver.\n * @return `true` if the flash loan was successful.\n */\n // This function can reenter, but it doesn't pose a risk because it always preserves the property that the amount\n // minted at the beginning is always recovered and burned at the end, or else the entire function will revert.\n // slither-disable-next-line reentrancy-no-eth\n function flashLoan(\n IERC3156FlashBorrower receiver,\n address token,\n uint256 amount,\n bytes calldata data\n ) public virtual override returns (bool) {\n require(amount <= maxFlashLoan(token), \"ERC20FlashMint: amount exceeds maxFlashLoan\");\n uint256 fee = flashFee(token, amount);\n _mint(address(receiver), amount);\n require(\n receiver.onFlashLoan(msg.sender, token, amount, fee, data) == _RETURN_VALUE,\n \"ERC20FlashMint: invalid return value\"\n );\n address flashFeeReceiver = _flashFeeReceiver();\n _spendAllowance(address(receiver), address(this), amount + fee);\n if (fee == 0 || flashFeeReceiver == address(0)) {\n _burn(address(receiver), amount + fee);\n } else {\n _burn(address(receiver), amount);\n _transfer(address(receiver), flashFeeReceiver, fee);\n }\n return true;\n }\n}\n\ncontract RToken is ReentrancyGuard, ERC20Permit, ERC20FlashMint, PositionManagerDependent, FeeCollector, IRToken {\n // --- Constants ---\n\n uint256 public constant override PERCENTAGE_BASE = 10_000;\n uint256 public constant override MAX_FLASH_MINT_FEE_PERCENTAGE = 500;\n\n // --- Variables ---\n\n uint256 public override flashMintFeePercentage;\n\n // --- Constructor ---\n\n /// @dev Deploys new R token. Sets flash mint fee percentage to 0. Transfers ownership to @param feeRecipient_.\n /// @param positionManager_ Address of the PositionManager contract that is authorized to mint and burn new tokens.\n /// @param feeRecipient_ Address of flash mint fee recipient.\n constructor(\n address positionManager_,\n address feeRecipient_\n )\n ERC20Permit(\"R Stablecoin\")\n ERC20(\"R Stablecoin\", \"R\")\n PositionManagerDependent(positionManager_)\n FeeCollector(feeRecipient_)\n {\n setFlashMintFeePercentage(PERCENTAGE_BASE / 200); // 0.5%\n\n transferOwnership(feeRecipient_);\n\n emit RDeployed(positionManager_, feeRecipient_);\n }\n\n // --- Functions ---\n\n function mint(address to, uint256 amount) external override onlyPositionManager {\n _mint(to, amount);\n }\n\n function burn(address from, uint256 amount) external override onlyPositionManager {\n _burn(from, amount);\n }\n\n function setFlashMintFeePercentage(uint256 feePercentage) public override onlyOwner {\n if (feePercentage > MAX_FLASH_MINT_FEE_PERCENTAGE) {\n revert FlashFeePercentageTooBig(feePercentage);\n }\n\n flashMintFeePercentage = feePercentage;\n emit FlashMintFeePercentageChanged(flashMintFeePercentage);\n }\n\n function flashLoan(\n IERC3156FlashBorrower receiver,\n address token,\n uint256 amount,\n bytes calldata data\n )\n public\n override(ERC20FlashMint, IERC3156FlashLender)\n nonReentrant\n returns (bool)\n {\n return super.flashLoan(receiver, token, amount, data);\n }\n\n /// @dev Inherited from ERC20FlashMint. Defines maximum size of the flash mint.\n /// @param token Token to be flash minted. Returns 0 amount in case of token != address(this).\n function maxFlashLoan(address token)\n public\n view\n virtual\n override(ERC20FlashMint, IERC3156FlashLender)\n returns (uint256)\n {\n return token == address(this) ? Math.min(totalSupply() / 10, ERC20FlashMint.maxFlashLoan(address(this))) : 0;\n }\n\n /// @dev Inherited from ERC20FlashMint. Defines flash mint fee for the flash mint of @param amount tokens.\n /// @param token Token to be flash minted. Returns 0 fee in case of token != address(this).\n /// @param amount Size of the flash mint.\n function _flashFee(address token, uint256 amount) internal view virtual override returns (uint256) {\n return token == address(this) ? amount * flashMintFeePercentage / PERCENTAGE_BASE : 0;\n }\n\n /// @dev Inherited from ERC20FlashMint. Defines flash mint fee receiver.\n /// @return Address that will receive flash mint fees.\n function _flashFeeReceiver() internal view virtual override returns (address) {\n return feeRecipient;\n }\n}\n\n/// @dev Implementation of Position Manager. Current implementation does not support rebasing tokens as collateral.\ncontract PositionManager is FeeCollector, IPositionManager {\n // --- Types ---\n\n using SafeERC20 for IERC20;\n using Fixed256x18 for uint256;\n\n // --- Constants ---\n\n uint256 public constant override MINUTE_DECAY_FACTOR = 999_037_758_833_783_000;\n\n uint256 public constant override MAX_BORROWING_SPREAD = MathUtils._100_PERCENT / 100; // 1%\n uint256 public constant override MAX_BORROWING_RATE = MathUtils._100_PERCENT / 100 * 5; // 5%\n\n uint256 public constant override BETA = 2;\n\n // --- Immutables ---\n\n IRToken public immutable override rToken;\n\n // --- Variables ---\n\n mapping(address position => IERC20 collateralToken) public override collateralTokenForPosition;\n\n mapping(address position => mapping(address delegate => bool isWhitelisted)) public override isDelegateWhitelisted;\n\n mapping(IERC20 collateralToken => CollateralTokenInfo collateralTokenInfo) public override collateralInfo;\n\n // --- Modifiers ---\n\n /// @dev Checks if the collateral token has been added to the position manager, or reverts otherwise.\n /// @param collateralToken The collateral token to check.\n modifier collateralTokenExists(IERC20 collateralToken) {\n if (address(collateralInfo[collateralToken].collateralToken) == address(0)) {\n revert CollateralTokenNotAdded();\n }\n _;\n }\n\n /// @dev Checks if the collateral token has enabled, or reverts otherwise. When the condition is false, the check\n /// is skipped.\n /// @param collateralToken The collateral token to check.\n /// @param condition If true, the check will be performed.\n modifier onlyEnabledCollateralTokenWhen(IERC20 collateralToken, bool condition) {\n if (condition && !collateralInfo[collateralToken].isEnabled) {\n revert CollateralTokenDisabled();\n }\n _;\n }\n\n /// @dev Checks if the borrower has a position with the collateral token or doesn't have a position at all, or\n /// reverts otherwise.\n /// @param position The borrower to check.\n /// @param collateralToken The collateral token to check.\n modifier onlyDepositedCollateralTokenOrNew(address position, IERC20 collateralToken) {\n if (\n collateralTokenForPosition[position] != IERC20(address(0))\n && collateralTokenForPosition[position] != collateralToken\n ) {\n revert PositionCollateralTokenMismatch();\n }\n _;\n }\n\n /// @dev Checks if the max fee percentage is between the borrowing spread and 100%, or reverts otherwise. When the\n /// condition is false, the check is skipped.\n /// @param maxFeePercentage The max fee percentage to check.\n /// @param condition If true, the check will be performed.\n modifier validMaxFeePercentageWhen(uint256 maxFeePercentage, bool condition) {\n if (condition && maxFeePercentage > MathUtils._100_PERCENT) {\n revert InvalidMaxFeePercentage();\n }\n _;\n }\n\n // --- Constructor ---\n\n /// @dev Initializes the position manager.\n constructor(address rToken_) FeeCollector(msg.sender) {\n rToken = rToken_ == address(0) ? new RToken(address(this), msg.sender) : IRToken(rToken_);\n emit PositionManagerDeployed(rToken, msg.sender);\n }\n\n // --- External functions ---\n\n function managePosition(\n IERC20 collateralToken,\n address position,\n uint256 collateralChange,\n bool isCollateralIncrease,\n uint256 debtChange,\n bool isDebtIncrease,\n uint256 maxFeePercentage,\n ERC20PermitSignature calldata permitSignature\n )\n public\n virtual\n override\n collateralTokenExists(collateralToken)\n validMaxFeePercentageWhen(maxFeePercentage, isDebtIncrease)\n onlyDepositedCollateralTokenOrNew(position, collateralToken)\n onlyEnabledCollateralTokenWhen(collateralToken, isDebtIncrease && debtChange > 0)\n returns (uint256 actualCollateralChange, uint256 actualDebtChange)\n {\n if (position != msg.sender && !isDelegateWhitelisted[position][msg.sender]) {\n revert DelegateNotWhitelisted();\n }\n if (collateralChange == 0 && debtChange == 0) {\n revert NoCollateralOrDebtChange();\n }\n if (address(permitSignature.token) == address(collateralToken)) {\n PermitHelper.applyPermit(permitSignature, msg.sender, address(this));\n }\n\n CollateralTokenInfo storage collateralTokenInfo = collateralInfo[collateralToken];\n IERC20Indexable raftCollateralToken = collateralTokenInfo.collateralToken;\n IERC20Indexable raftDebtToken = collateralTokenInfo.debtToken;\n\n uint256 debtBefore = raftDebtToken.balanceOf(position);\n if (!isDebtIncrease && (debtChange == type(uint256).max || (debtBefore != 0 && debtChange == debtBefore))) {\n if (collateralChange != 0 || isCollateralIncrease) {\n revert WrongCollateralParamsForFullRepayment();\n }\n collateralChange = raftCollateralToken.balanceOf(position);\n debtChange = debtBefore;\n }\n\n _adjustDebt(position, collateralToken, raftDebtToken, debtChange, isDebtIncrease, maxFeePercentage);\n _adjustCollateral(collateralToken, raftCollateralToken, position, collateralChange, isCollateralIncrease);\n\n uint256 positionDebt = raftDebtToken.balanceOf(position);\n uint256 positionCollateral = raftCollateralToken.balanceOf(position);\n\n if (positionDebt == 0) {\n if (positionCollateral != 0) {\n revert InvalidPosition();\n }\n // position was closed, remove it\n _closePosition(raftCollateralToken, raftDebtToken, position, false);\n } else {\n _checkValidPosition(collateralToken, positionDebt, positionCollateral);\n\n if (debtBefore == 0) {\n collateralTokenForPosition[position] = collateralToken;\n emit PositionCreated(position, collateralToken);\n }\n }\n return (collateralChange, debtChange);\n }\n\n function liquidate(address position) external override {\n IERC20 collateralToken = collateralTokenForPosition[position];\n CollateralTokenInfo storage collateralTokenInfo = collateralInfo[collateralToken];\n IERC20Indexable raftCollateralToken = collateralTokenInfo.collateralToken;\n IERC20Indexable raftDebtToken = collateralTokenInfo.debtToken;\n ISplitLiquidationCollateral splitLiquidation = collateralTokenInfo.splitLiquidation;\n\n if (address(collateralToken) == address(0)) {\n revert NothingToLiquidate();\n }\n (uint256 price,) = collateralTokenInfo.priceFeed.fetchPrice();\n uint256 entireCollateral = raftCollateralToken.balanceOf(position);\n uint256 entireDebt = raftDebtToken.balanceOf(position);\n uint256 icr = MathUtils._computeCR(entireCollateral, entireDebt, price);\n\n if (icr >= splitLiquidation.MCR()) {\n revert NothingToLiquidate();\n }\n\n uint256 totalDebt = raftDebtToken.totalSupply();\n if (entireDebt == totalDebt) {\n revert CannotLiquidateLastPosition();\n }\n bool isRedistribution = icr <= MathUtils._100_PERCENT;\n\n // prettier: ignore\n (uint256 collateralLiquidationFee, uint256 collateralToSendToLiquidator) =\n splitLiquidation.split(entireCollateral, entireDebt, price, isRedistribution);\n\n if (!isRedistribution) {\n _burnRTokens(msg.sender, entireDebt);\n totalDebt -= entireDebt;\n\n // Collateral is sent to protocol as a fee only in case of liquidation\n collateralToken.safeTransfer(feeRecipient, collateralLiquidationFee);\n }\n\n collateralToken.safeTransfer(msg.sender, collateralToSendToLiquidator);\n\n _closePosition(raftCollateralToken, raftDebtToken, position, true);\n\n _updateDebtAndCollateralIndex(collateralToken, raftCollateralToken, raftDebtToken, totalDebt);\n\n emit Liquidation(\n msg.sender,\n position,\n collateralToken,\n entireDebt,\n entireCollateral,\n collateralToSendToLiquidator,\n collateralLiquidationFee,\n isRedistribution\n );\n }\n\n function redeemCollateral(\n IERC20 collateralToken,\n uint256 debtAmount,\n uint256 maxFeePercentage\n )\n public\n virtual\n override\n {\n if (maxFeePercentage > MathUtils._100_PERCENT) {\n revert MaxFeePercentageOutOfRange();\n }\n if (debtAmount == 0) {\n revert AmountIsZero();\n }\n IERC20Indexable raftDebtToken = collateralInfo[collateralToken].debtToken;\n\n uint256 newTotalDebt = raftDebtToken.totalSupply() - debtAmount;\n uint256 lowTotalDebt = collateralInfo[collateralToken].splitLiquidation.LOW_TOTAL_DEBT();\n if (newTotalDebt < lowTotalDebt) {\n revert TotalDebtCannotBeLowerThanMinDebt(collateralToken, newTotalDebt);\n }\n\n (uint256 price, uint256 deviation) = collateralInfo[collateralToken].priceFeed.fetchPrice();\n uint256 collateralToRedeem = debtAmount.divDown(price);\n uint256 totalCollateral = collateralToken.balanceOf(address(this));\n if (\n totalCollateral - collateralToRedeem == 0\n || totalCollateral - collateralToRedeem < lowTotalDebt.divDown(price)\n ) {\n revert TotalCollateralCannotBeLowerThanMinCollateral(\n collateralToken, totalCollateral - collateralToRedeem, lowTotalDebt.divDown(price)\n );\n }\n\n // Decay the baseRate due to time passed, and then increase it according to the size of this redemption.\n // Use the saved total R supply value, from before it was reduced by the redemption.\n _updateBaseRateFromRedemption(collateralToken, collateralToRedeem, price, rToken.totalSupply());\n\n // Calculate the redemption fee\n uint256 redemptionFee = getRedemptionFee(collateralToken, collateralToRedeem, deviation);\n uint256 rebate = redemptionFee.mulDown(collateralInfo[collateralToken].redemptionRebate);\n\n _checkValidFee(redemptionFee, collateralToRedeem, maxFeePercentage);\n\n // Send the redemption fee to the recipient\n collateralToken.safeTransfer(feeRecipient, redemptionFee - rebate);\n\n // Burn the total R that is cancelled with debt, and send the redeemed collateral to msg.sender\n _burnRTokens(msg.sender, debtAmount);\n\n // Send collateral to account\n collateralToken.safeTransfer(msg.sender, collateralToRedeem - redemptionFee);\n\n _updateDebtAndCollateralIndex(\n collateralToken, collateralInfo[collateralToken].collateralToken, raftDebtToken, newTotalDebt\n );\n\n emit Redemption(msg.sender, debtAmount, collateralToRedeem, redemptionFee, rebate);\n }\n\n function whitelistDelegate(address delegate, bool whitelisted) external override {\n if (delegate == address(0)) {\n revert InvalidDelegateAddress();\n }\n isDelegateWhitelisted[msg.sender][delegate] = whitelisted;\n\n emit DelegateWhitelisted(msg.sender, delegate, whitelisted);\n }\n\n function setBorrowingSpread(IERC20 collateralToken, uint256 newBorrowingSpread) external override onlyOwner {\n if (newBorrowingSpread > MAX_BORROWING_SPREAD) {\n revert BorrowingSpreadExceedsMaximum();\n }\n collateralInfo[collateralToken].borrowingSpread = newBorrowingSpread;\n emit BorrowingSpreadUpdated(newBorrowingSpread);\n }\n\n function setRedemptionRebate(IERC20 collateralToken, uint256 newRedemptionRebate) public override onlyOwner {\n if (newRedemptionRebate > MathUtils._100_PERCENT) {\n revert RedemptionRebateExceedsMaximum();\n }\n collateralInfo[collateralToken].redemptionRebate = newRedemptionRebate;\n emit RedemptionRebateUpdated(newRedemptionRebate);\n }\n\n function getRedemptionFeeWithDecay(\n IERC20 collateralToken,\n uint256 collateralAmount\n )\n external\n view\n override\n returns (uint256 redemptionFee)\n {\n redemptionFee = getRedemptionRateWithDecay(collateralToken).mulDown(collateralAmount);\n if (redemptionFee >= collateralAmount) {\n revert FeeEatsUpAllReturnedCollateral();\n }\n }\n\n // --- Public functions ---\n\n function addCollateralToken(\n IERC20 collateralToken,\n IPriceFeed priceFeed,\n ISplitLiquidationCollateral newSplitLiquidationCollateral\n )\n public\n virtual\n override\n {\n addCollateralToken(\n collateralToken,\n priceFeed,\n newSplitLiquidationCollateral,\n new ERC20Indexable(\n address(this),\n string(bytes.concat(\"Raft \", bytes(IERC20Metadata(address(collateralToken)).name()), \" collateral\")),\n string(bytes.concat(\"r\", bytes(IERC20Metadata(address(collateralToken)).symbol()), \"-c\")),\n type(uint256).max\n ),\n new ERC20Indexable(\n address(this),\n string(bytes.concat(\"Raft \", bytes(IERC20Metadata(address(collateralToken)).name()), \" debt\")),\n string(bytes.concat(\"r\", bytes(IERC20Metadata(address(collateralToken)).symbol()), \"-d\")),\n type(uint256).max\n )\n );\n }\n\n function addCollateralToken(\n IERC20 collateralToken,\n IPriceFeed priceFeed,\n ISplitLiquidationCollateral newSplitLiquidationCollateral,\n IERC20Indexable raftCollateralToken_,\n IERC20Indexable raftDebtToken_\n )\n public\n override\n onlyOwner\n {\n if (address(collateralToken) == address(0)) {\n revert CollateralTokenAddressCannotBeZero();\n }\n if (address(priceFeed) == address(0)) {\n revert PriceFeedAddressCannotBeZero();\n }\n if (address(collateralInfo[collateralToken].collateralToken) != address(0)) {\n revert CollateralTokenAlreadyAdded();\n }\n\n CollateralTokenInfo memory raftCollateralTokenInfo;\n raftCollateralTokenInfo.collateralToken = raftCollateralToken_;\n raftCollateralTokenInfo.debtToken = raftDebtToken_;\n raftCollateralTokenInfo.isEnabled = true;\n raftCollateralTokenInfo.priceFeed = priceFeed;\n\n collateralInfo[collateralToken] = raftCollateralTokenInfo;\n\n setRedemptionSpread(collateralToken, MathUtils._100_PERCENT);\n setRedemptionRebate(collateralToken, MathUtils._100_PERCENT);\n\n setSplitLiquidationCollateral(collateralToken, newSplitLiquidationCollateral);\n\n emit CollateralTokenAdded(\n collateralToken, raftCollateralTokenInfo.collateralToken, raftCollateralTokenInfo.debtToken, priceFeed\n );\n }\n\n function setCollateralEnabled(\n IERC20 collateralToken,\n bool isEnabled\n )\n public\n override\n onlyOwner\n collateralTokenExists(collateralToken)\n {\n bool previousIsEnabled = collateralInfo[collateralToken].isEnabled;\n collateralInfo[collateralToken].isEnabled = isEnabled;\n\n if (previousIsEnabled != isEnabled) {\n emit CollateralTokenModified(collateralToken, isEnabled);\n }\n }\n\n function setSplitLiquidationCollateral(\n IERC20 collateralToken,\n ISplitLiquidationCollateral newSplitLiquidationCollateral\n )\n public\n override\n onlyOwner\n {\n if (address(newSplitLiquidationCollateral) == address(0)) {\n revert SplitLiquidationCollateralCannotBeZero();\n }\n collateralInfo[collateralToken].splitLiquidation = newSplitLiquidationCollateral;\n emit SplitLiquidationCollateralChanged(collateralToken, newSplitLiquidationCollateral);\n }\n\n function setRedemptionSpread(IERC20 collateralToken, uint256 newRedemptionSpread) public override onlyOwner {\n if (newRedemptionSpread > MathUtils._100_PERCENT) {\n revert RedemptionSpreadOutOfRange();\n }\n collateralInfo[collateralToken].redemptionSpread = newRedemptionSpread;\n emit RedemptionSpreadUpdated(collateralToken, newRedemptionSpread);\n }\n\n function getRedemptionRateWithDecay(IERC20 collateralToken) public view override returns (uint256) {\n return _calcRedemptionRate(collateralToken, _calcDecayedBaseRate(collateralToken));\n }\n\n function raftCollateralToken(IERC20 collateralToken) external view override returns (IERC20Indexable) {\n return collateralInfo[collateralToken].collateralToken;\n }\n\n function raftDebtToken(IERC20 collateralToken) external view override returns (IERC20Indexable) {\n return collateralInfo[collateralToken].debtToken;\n }\n\n function priceFeed(IERC20 collateralToken) external view override returns (IPriceFeed) {\n return collateralInfo[collateralToken].priceFeed;\n }\n\n function splitLiquidationCollateral(IERC20 collateralToken) external view returns (ISplitLiquidationCollateral) {\n return collateralInfo[collateralToken].splitLiquidation;\n }\n\n function collateralEnabled(IERC20 collateralToken) external view override returns (bool) {\n return collateralInfo[collateralToken].isEnabled;\n }\n\n function lastFeeOperationTime(IERC20 collateralToken) external view override returns (uint256) {\n return collateralInfo[collateralToken].lastFeeOperationTime;\n }\n\n function borrowingSpread(IERC20 collateralToken) external view override returns (uint256) {\n return collateralInfo[collateralToken].borrowingSpread;\n }\n\n function baseRate(IERC20 collateralToken) external view override returns (uint256) {\n return collateralInfo[collateralToken].baseRate;\n }\n\n function redemptionSpread(IERC20 collateralToken) external view override returns (uint256) {\n return collateralInfo[collateralToken].redemptionSpread;\n }\n\n function redemptionRebate(IERC20 collateralToken) external view override returns (uint256) {\n return collateralInfo[collateralToken].redemptionRebate;\n }\n\n function getRedemptionRate(IERC20 collateralToken) public view override returns (uint256) {\n return _calcRedemptionRate(collateralToken, collateralInfo[collateralToken].baseRate);\n }\n\n function getRedemptionFee(\n IERC20 collateralToken,\n uint256 collateralAmount,\n uint256 priceDeviation\n )\n public\n view\n override\n returns (uint256)\n {\n return Math.min(getRedemptionRate(collateralToken) + priceDeviation, MathUtils._100_PERCENT).mulDown(\n collateralAmount\n );\n }\n\n function getBorrowingRate(IERC20 collateralToken) public view override returns (uint256) {\n return _calcBorrowingRate(collateralToken, collateralInfo[collateralToken].baseRate);\n }\n\n function getBorrowingRateWithDecay(IERC20 collateralToken) public view override returns (uint256) {\n return _calcBorrowingRate(collateralToken, _calcDecayedBaseRate(collateralToken));\n }\n\n function getBorrowingFee(IERC20 collateralToken, uint256 debtAmount) public view override returns (uint256) {\n return getBorrowingRate(collateralToken).mulDown(debtAmount);\n }\n\n // --- Helper functions ---\n\n /// @dev Adjusts the debt of a given borrower by burning or minting the corresponding amount of R and the Raft\n /// debt token. If the debt is being increased, the borrowing fee is also triggered.\n /// @param position The address of the borrower.\n /// @param debtChange The amount of R to add or remove. Must be positive.\n /// @param isDebtIncrease True if the debt is being increased, false otherwise.\n /// @param maxFeePercentage The maximum fee percentage.\n function _adjustDebt(\n address position,\n IERC20 collateralToken,\n IERC20Indexable raftDebtToken,\n uint256 debtChange,\n bool isDebtIncrease,\n uint256 maxFeePercentage\n )\n internal\n {\n if (debtChange == 0) {\n return;\n }\n\n if (isDebtIncrease) {\n uint256 totalDebtChange =\n debtChange + _triggerBorrowingFee(collateralToken, position, debtChange, maxFeePercentage);\n raftDebtToken.mint(position, totalDebtChange);\n _mintRTokens(msg.sender, debtChange);\n } else {\n raftDebtToken.burn(position, debtChange);\n _burnRTokens(msg.sender, debtChange);\n }\n\n emit DebtChanged(position, collateralToken, debtChange, isDebtIncrease);\n }\n\n /// @dev Mints R tokens\n function _mintRTokens(address to, uint256 amount) internal virtual {\n rToken.mint(to, amount);\n }\n\n /// @dev Burns R tokens\n function _burnRTokens(address from, uint256 amount) internal virtual {\n rToken.burn(from, amount);\n }\n\n /// @dev Adjusts the collateral of a given borrower by burning or minting the corresponding amount of Raft\n /// collateral token and transferring the corresponding amount of collateral token.\n /// @param collateralToken The token the borrower used as collateral.\n /// @param position The address of the borrower.\n /// @param collateralChange The amount of collateral to add or remove. Must be positive.\n /// @param isCollateralIncrease True if the collateral is being increased, false otherwise.\n function _adjustCollateral(\n IERC20 collateralToken,\n IERC20Indexable raftCollateralToken,\n address position,\n uint256 collateralChange,\n bool isCollateralIncrease\n )\n internal\n {\n if (collateralChange == 0) {\n return;\n }\n\n if (isCollateralIncrease) {\n raftCollateralToken.mint(position, collateralChange);\n collateralToken.safeTransferFrom(msg.sender, address(this), collateralChange);\n } else {\n raftCollateralToken.burn(position, collateralChange);\n collateralToken.safeTransfer(msg.sender, collateralChange);\n }\n\n emit CollateralChanged(position, collateralToken, collateralChange, isCollateralIncrease);\n }\n\n /// @dev Updates debt and collateral indexes for a given collateral token.\n /// @param collateralToken The collateral token for which to update the indexes.\n /// @param raftCollateralToken The raft collateral indexable token.\n /// @param raftDebtToken The raft debt indexable token.\n /// @param totalDebtForCollateral Totam amount of debt backed by collateral token.\n function _updateDebtAndCollateralIndex(\n IERC20 collateralToken,\n IERC20Indexable raftCollateralToken,\n IERC20Indexable raftDebtToken,\n uint256 totalDebtForCollateral\n )\n internal\n {\n raftDebtToken.setIndex(totalDebtForCollateral);\n raftCollateralToken.setIndex(collateralToken.balanceOf(address(this)));\n }\n\n function _closePosition(\n IERC20Indexable raftCollateralToken,\n IERC20Indexable raftDebtToken,\n address position,\n bool burnTokens\n )\n internal\n {\n collateralTokenForPosition[position] = IERC20(address(0));\n\n if (burnTokens) {\n raftDebtToken.burn(position, type(uint256).max);\n raftCollateralToken.burn(position, type(uint256).max);\n }\n emit PositionClosed(position);\n }\n\n // --- Borrowing & redemption fee helper functions ---\n\n /// @dev Updates the base rate from a redemption operation. Impacts on the base rate:\n /// 1. decays the base rate based on time passed since last redemption or R borrowing operation,\n /// 2. increases the base rate based on the amount redeemed, as a proportion of total supply.\n function _updateBaseRateFromRedemption(\n IERC20 collateralToken,\n uint256 collateralDrawn,\n uint256 price,\n uint256 totalDebtSupply\n )\n internal\n returns (uint256)\n {\n uint256 decayedBaseRate = _calcDecayedBaseRate(collateralToken);\n\n /* Convert the drawn collateral back to R at face value rate (1 R:1 USD), in order to get\n * the fraction of total supply that was redeemed at face value. */\n uint256 redeemedFraction = collateralDrawn * price / totalDebtSupply;\n\n uint256 newBaseRate = decayedBaseRate + redeemedFraction / BETA;\n newBaseRate = Math.min(newBaseRate, MathUtils._100_PERCENT); // cap baseRate at a maximum of 100%\n assert(newBaseRate > 0); // Base rate is always non-zero after redemption\n\n // Update the baseRate state variable\n collateralInfo[collateralToken].baseRate = newBaseRate;\n emit BaseRateUpdated(collateralToken, newBaseRate);\n\n _updateLastFeeOpTime(collateralToken);\n\n return newBaseRate;\n }\n\n function _calcRedemptionRate(IERC20 collateralToken, uint256 baseRate_) internal view returns (uint256) {\n return baseRate_ + collateralInfo[collateralToken].redemptionSpread;\n }\n\n function _calcBorrowingRate(IERC20 collateralToken, uint256 baseRate_) internal view returns (uint256) {\n return Math.min(collateralInfo[collateralToken].borrowingSpread + baseRate_, MAX_BORROWING_RATE);\n }\n\n /// @dev Updates the base rate based on time elapsed since the last redemption or R borrowing operation.\n function _decayBaseRateFromBorrowing(IERC20 collateralToken) internal {\n uint256 decayedBaseRate = _calcDecayedBaseRate(collateralToken);\n assert(decayedBaseRate <= MathUtils._100_PERCENT); // The baseRate can decay to 0\n\n collateralInfo[collateralToken].baseRate = decayedBaseRate;\n emit BaseRateUpdated(collateralToken, decayedBaseRate);\n\n _updateLastFeeOpTime(collateralToken);\n }\n\n /// @dev Update the last fee operation time only if time passed >= decay interval. This prevents base rate\n /// griefing.\n function _updateLastFeeOpTime(IERC20 collateralToken) internal {\n uint256 timePassed = block.timestamp - collateralInfo[collateralToken].lastFeeOperationTime;\n\n if (timePassed >= 1 minutes) {\n collateralInfo[collateralToken].lastFeeOperationTime = block.timestamp;\n emit LastFeeOpTimeUpdated(collateralToken, block.timestamp);\n }\n }\n\n function _calcDecayedBaseRate(IERC20 collateralToken) internal view returns (uint256) {\n uint256 minutesPassed = (block.timestamp - collateralInfo[collateralToken].lastFeeOperationTime) / 1 minutes;\n uint256 decayFactor = MathUtils._decPow(MINUTE_DECAY_FACTOR, minutesPassed);\n\n return collateralInfo[collateralToken].baseRate.mulDown(decayFactor);\n }\n\n function _triggerBorrowingFee(\n IERC20 collateralToken,\n address position,\n uint256 debtAmount,\n uint256 maxFeePercentage\n )\n internal\n virtual\n returns (uint256 borrowingFee)\n {\n _decayBaseRateFromBorrowing(collateralToken); // decay the baseRate state variable\n borrowingFee = getBorrowingFee(collateralToken, debtAmount);\n\n _checkValidFee(borrowingFee, debtAmount, maxFeePercentage);\n\n if (borrowingFee > 0) {\n _mintRTokens(feeRecipient, borrowingFee);\n emit RBorrowingFeePaid(collateralToken, position, borrowingFee);\n }\n }\n\n // --- Validation check helper functions ---\n\n function _checkValidPosition(IERC20 collateralToken, uint256 positionDebt, uint256 positionCollateral) internal {\n ISplitLiquidationCollateral splitCollateral = collateralInfo[collateralToken].splitLiquidation;\n if (positionDebt < splitCollateral.LOW_TOTAL_DEBT()) {\n revert NetDebtBelowMinimum(positionDebt);\n }\n\n (uint256 price,) = collateralInfo[collateralToken].priceFeed.fetchPrice();\n uint256 newICR = MathUtils._computeCR(positionCollateral, positionDebt, price);\n if (newICR < splitCollateral.MCR()) {\n revert NewICRLowerThanMCR(newICR);\n }\n }\n\n function _checkValidFee(uint256 fee, uint256 amount, uint256 maxFeePercentage) internal pure {\n uint256 feePercentage = fee.divDown(amount);\n\n if (feePercentage > maxFeePercentage) {\n revert FeeExceedsMaxFee(fee, amount, maxFeePercentage);\n }\n }\n}\n\ninterface IRMinter {\n /// @dev Emitted when tokens are recovered from the contract.\n /// @param token The address of the token being recovered.\n /// @param to The address receiving the recovered tokens.\n /// @param amount The amount of tokens recovered.\n event TokensRecovered(IERC20 token, address to, uint256 amount);\n\n /// @return Address of the R token.\n function r() external view returns (IRToken);\n\n /// @return Address of the Position manager contract responsible for minting R.\n function positionManager() external view returns (IPositionManager);\n\n /// @dev Recover accidentally sent tokens to the contract\n /// @param token Address of the token contract.\n /// @param to Address of the receiver of the tokens.\n /// @param amount Number of tokens to recover.\n function recoverTokens(IERC20 token, address to, uint256 amount) external;\n}\n\ninterface ILock {\n /// @dev Thrown when contract usage is locked.\n error ContractLocked();\n\n /// @dev Unauthorized call to lock/unlock.\n error Unauthorized();\n\n /// @dev Retrieves if contract is currently locked or not.\n function locked() external view returns (bool);\n\n /// @dev Retrieves address of the locker who can unlock contract.\n function locker() external view returns (address);\n\n /// @dev Unlcoks the usage of the contract.\n function unlock() external;\n\n /// @dev Locks the usage of the contract.\n function lock() external;\n}\n\nabstract contract ERC20RMinter is IRMinter, ERC20, Ownable2Step {\n using SafeERC20 for IERC20;\n\n IRToken public immutable override r;\n IPositionManager public immutable override positionManager;\n\n constructor(IRToken rToken_, string memory name_, string memory symbol_) ERC20(name_, symbol_) {\n r = rToken_;\n positionManager = IPositionManager(rToken_.positionManager());\n\n _approve(address(this), address(positionManager), type(uint256).max);\n }\n\n modifier unlockCall() {\n ILock lockContract = ILock(address(positionManager.priceFeed(IERC20(this))));\n lockContract.unlock();\n _;\n lockContract.lock();\n }\n\n function recoverTokens(IERC20 token, address to, uint256 amount) external override onlyOwner {\n token.safeTransfer(to, amount);\n emit TokensRecovered(token, to, amount);\n }\n\n function _mintR(address to, uint256 amount) internal unlockCall {\n _mint(address(this), amount);\n ERC20PermitSignature memory emptySignature;\n positionManager.managePosition(\n IERC20(address(this)),\n address(this),\n amount,\n true, // collateral increase\n amount,\n true, // debt increase\n 1e18, // 100%\n emptySignature\n );\n r.transfer(to, amount);\n }\n\n function _burnR(address from, uint256 amount) internal unlockCall {\n r.transferFrom(from, address(this), amount);\n ERC20PermitSignature memory emptySignature;\n positionManager.managePosition(\n IERC20(address(this)),\n address(this),\n amount,\n false, // collateral decrease\n amount,\n false, // debt decrease\n 1e18, // 100%\n emptySignature\n );\n _burn(address(this), amount);\n }\n}\n\ncontract InterestRateDebtToken is ERC20Indexable {\n // --- Types ---\n\n using Fixed256x18 for uint256;\n\n // --- Events ---\n\n event IndexIncreasePerSecondSet(uint256 indexIncreasePerSecond);\n\n // --- Immutables ---\n\n IERC20 immutable collateralToken;\n\n // --- Variables ---\n\n uint256 internal storedIndexUpdatedAt;\n\n uint256 public indexIncreasePerSecond;\n\n // --- Constructor ---\n\n constructor(\n address positionManager_,\n string memory name_,\n string memory symbol_,\n IERC20 collateralToken_,\n uint256 cap_,\n uint256 indexIncreasePerSecond_\n )\n ERC20Indexable(positionManager_, name_, symbol_, cap_)\n {\n storedIndexUpdatedAt = block.timestamp;\n collateralToken = collateralToken_;\n setIndexIncreasePerSecond(indexIncreasePerSecond_);\n }\n\n // --- Functions ---\n\n function mint(address to, uint256 amount) public virtual override {\n updateIndexAndPayFees();\n super.mint(to, amount);\n }\n\n function burn(address from, uint256 amount) public virtual override {\n updateIndexAndPayFees();\n super.burn(from, amount);\n }\n\n function currentIndex() public view virtual override returns (uint256) {\n return storedIndex.mulUp(INDEX_PRECISION + indexIncreasePerSecond * (block.timestamp - storedIndexUpdatedAt));\n }\n\n function updateIndexAndPayFees() public {\n uint256 currentIndex_ = currentIndex();\n _payFees(currentIndex_);\n storedIndexUpdatedAt = block.timestamp;\n storedIndex = currentIndex_;\n emit IndexUpdated(currentIndex_);\n }\n\n function setIndexIncreasePerSecond(uint256 indexIncreasePerSecond_) public onlyOwner {\n updateIndexAndPayFees();\n indexIncreasePerSecond = indexIncreasePerSecond_;\n emit IndexIncreasePerSecondSet(indexIncreasePerSecond_);\n }\n\n function unpaidFees() external view returns (uint256) {\n return _unpaidFees(currentIndex());\n }\n\n function _unpaidFees(uint256 currentIndex_) private view returns (uint256) {\n return totalSupply().mulDown(currentIndex_ - storedIndex);\n }\n\n function _payFees(uint256 currentIndex_) private {\n uint256 unpaidFees = _unpaidFees(currentIndex_);\n if (unpaidFees > 0) {\n IInterestRatePositionManager(positionManager).mintFees(collateralToken, unpaidFees);\n }\n }\n}\n\n/// @dev Implementation of Position Manager. Current implementation does not support rebasing tokens as collateral.\ncontract InterestRatePositionManager is ERC20RMinter, PositionManager, IInterestRatePositionManager {\n // --- Errors ---\n\n error Unsupported();\n\n // --- Constructor ---\n\n /// @dev Initializes the position manager.\n constructor(IRToken rToken_)\n PositionManager(address(rToken_))\n ERC20RMinter(rToken_, \"Interest Rate Posman\", \"IRPM\")\n { }\n\n // --- External functions ---\n\n function managePosition(\n IERC20 collateralToken,\n address position,\n uint256 collateralChange,\n bool isCollateralIncrease,\n uint256 debtChange,\n bool isDebtIncrease,\n uint256 maxFeePercentage,\n ERC20PermitSignature calldata permitSignature\n )\n public\n virtual\n override(IPositionManager, PositionManager)\n returns (uint256 actualCollateralChange, uint256 actualDebtChange)\n {\n if (address(permitSignature.token) == address(r)) {\n PermitHelper.applyPermit(permitSignature, msg.sender, address(this));\n }\n return super.managePosition(\n collateralToken,\n position,\n collateralChange,\n isCollateralIncrease,\n debtChange,\n isDebtIncrease,\n maxFeePercentage,\n permitSignature\n );\n }\n\n function mintFees(IERC20 collateralToken, uint256 amount) external {\n if (msg.sender != address(collateralInfo[collateralToken].debtToken)) {\n revert InvalidDebtToken(msg.sender);\n }\n _mintR(feeRecipient, amount);\n\n emit MintedFees(collateralToken, amount);\n }\n\n function redeemCollateral(IERC20, uint256, uint256) public virtual override(IPositionManager, PositionManager) {\n revert Unsupported();\n }\n\n function addCollateralToken(\n IERC20,\n IPriceFeed,\n ISplitLiquidationCollateral\n )\n public\n override(IPositionManager, PositionManager)\n {\n revert Unsupported();\n }\n\n // --- Helper functions ---\n\n function _mintRTokens(address to, uint256 amount) internal virtual override {\n _mintR(to, amount);\n }\n\n function _burnRTokens(address from, uint256 amount) internal virtual override {\n _burnR(from, amount);\n }\n\n function _triggerBorrowingFee(\n IERC20,\n address,\n uint256,\n uint256\n )\n internal\n pure\n virtual\n override\n returns (uint256)\n {\n return 0;\n }\n}\n"}},"settings":{"remappings":["@balancer-labs/=node_modules/@balancer-labs/","@balancer-labs/v2-interfaces/contracts/=lib/balancer-v2-monorepo/pkg/interfaces/contracts/","@chainlink/=node_modules/@chainlink/","@eth-optimism/=node_modules/@eth-optimism/","@openzeppelin/=node_modules/@openzeppelin/","@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/","@redstone-finance/=node_modules/@redstone-finance/","@smartcontractkit/chainlink/=lib/chainlink/contracts/src/v0.8/","@tempusfinance/=node_modules/@tempusfinance/","@tempusfinance/tempus-utils/contracts/=lib/tempus-utils/contracts/","balancer-v2-monorepo/=lib/balancer-v2-monorepo/","chainlink/=lib/chainlink/","ds-test/=lib/forge-std/lib/ds-test/src/","erc4626-tests/=lib/chainlink/contracts/foundry-lib/openzeppelin-contracts/lib/erc4626-tests/","eth-gas-reporter/=node_modules/eth-gas-reporter/","forge-std/=lib/forge-std/src/","hardhat/=node_modules/hardhat/","openzeppelin-contracts/=lib/openzeppelin-contracts/","tempus-utils/=lib/tempus-utils/contracts/"],"optimizer":{"enabled":true,"runs":200000},"metadata":{"bytecodeHash":"ipfs","appendCBOR":true},"outputSelection":{"*":{"*":["evm.bytecode","evm.deployedBytecode","devdoc","userdoc","metadata","abi"]}},"evmVersion":"london","viaIR":true,"libraries":{}}},"ABI":"[{\"inputs\":[{\"internalType\":\"contract IRToken\",\"name\":\"rToken_\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AmountIsZero\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BorrowingSpreadExceedsMaximum\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotLiquidateLastPosition\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CollateralTokenAddressCannotBeZero\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CollateralTokenAlreadyAdded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CollateralTokenDisabled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CollateralTokenNotAdded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DelegateNotWhitelisted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FeeEatsUpAllReturnedCollateral\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxFeePercentage\",\"type\":\"uint256\"}],\"name\":\"FeeExceedsMaxFee\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"InvalidDebtToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidDelegateAddress\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidFeeRecipient\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidMaxFeePercentage\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidPosition\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaxFeePercentageOutOfRange\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"netDebt\",\"type\":\"uint256\"}],\"name\":\"NetDebtBelowMinimum\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newICR\",\"type\":\"uint256\"}],\"name\":\"NewICRLowerThanMCR\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoCollateralOrDebtChange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NothingToLiquidate\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PositionCollateralTokenMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PriceFeedAddressCannotBeZero\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RedemptionRebateExceedsMaximum\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RedemptionSpreadOutOfRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SplitLiquidationCollateralCannotBeZero\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"newTotalCollateral\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minimumCollateral\",\"type\":\"uint256\"}],\"name\":\"TotalCollateralCannotBeLowerThanMinCollateral\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"newTotalDebt\",\"type\":\"uint256\"}],\"name\":\"TotalDebtCannotBeLowerThanMinDebt\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Unsupported\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"WrongCollateralParamsForFullRepayment\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"baseRate\",\"type\":\"uint256\"}],\"name\":\"BaseRateUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"borrowingSpread\",\"type\":\"uint256\"}],\"name\":\"BorrowingSpreadUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"collateralAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isCollateralIncrease\",\"type\":\"bool\"}],\"name\":\"CollateralChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract IERC20Indexable\",\"name\":\"raftCollateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract IERC20Indexable\",\"name\":\"raftDebtToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract IPriceFeed\",\"name\":\"priceFeed\",\"type\":\"address\"}],\"name\":\"CollateralTokenAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"}],\"name\":\"CollateralTokenModified\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"debtAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isDebtIncrease\",\"type\":\"bool\"}],\"name\":\"DebtChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"delegate\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"whitelisted\",\"type\":\"bool\"}],\"name\":\"DelegateWhitelisted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"feeRecipient\",\"type\":\"address\"}],\"name\":\"FeeRecipientChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"lastFeeOpTime\",\"type\":\"uint256\"}],\"name\":\"LastFeeOpTimeUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"liquidator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"debtLiquidated\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"collateralLiquidated\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"collateralSentToLiquidator\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"collateralLiquidationFeePaid\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isRedistribution\",\"type\":\"bool\"}],\"name\":\"Liquidation\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"MintedFees\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferStarted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"}],\"name\":\"PositionClosed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"PositionCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IRToken\",\"name\":\"rToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"feeRecipient\",\"type\":\"address\"}],\"name\":\"PositionManagerDeployed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"feeAmount\",\"type\":\"uint256\"}],\"name\":\"RBorrowingFeePaid\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"redeemer\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"collateralSent\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"rebate\",\"type\":\"uint256\"}],\"name\":\"Redemption\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"redemptionRebate\",\"type\":\"uint256\"}],\"name\":\"RedemptionRebateUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"redemptionSpread\",\"type\":\"uint256\"}],\"name\":\"RedemptionSpreadUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"contract ISplitLiquidationCollateral\",\"name\":\"newSplitLiquidationCollateral\",\"type\":\"address\"}],\"name\":\"SplitLiquidationCollateralChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"TokensRecovered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"BETA\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_BORROWING_RATE\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_BORROWING_SPREAD\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MINUTE_DECAY_FACTOR\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"contract IPriceFeed\",\"name\":\"priceFeed\",\"type\":\"address\"},{\"internalType\":\"contract ISplitLiquidationCollateral\",\"name\":\"newSplitLiquidationCollateral\",\"type\":\"address\"},{\"internalType\":\"contract IERC20Indexable\",\"name\":\"raftCollateralToken_\",\"type\":\"address\"},{\"internalType\":\"contract IERC20Indexable\",\"name\":\"raftDebtToken_\",\"type\":\"address\"}],\"name\":\"addCollateralToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"contract IPriceFeed\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"contract ISplitLiquidationCollateral\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"addCollateralToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"baseRate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"borrowingSpread\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"collateralEnabled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"collateralInfo\",\"outputs\":[{\"internalType\":\"contract IERC20Indexable\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"contract IERC20Indexable\",\"name\":\"debtToken\",\"type\":\"address\"},{\"internalType\":\"contract IPriceFeed\",\"name\":\"priceFeed\",\"type\":\"address\"},{\"internalType\":\"contract ISplitLiquidationCollateral\",\"name\":\"splitLiquidation\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"lastFeeOperationTime\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"borrowingSpread\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"baseRate\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"redemptionSpread\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"redemptionRebate\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"}],\"name\":\"collateralTokenForPosition\",\"outputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"subtractedValue\",\"type\":\"uint256\"}],\"name\":\"decreaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"feeRecipient\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"debtAmount\",\"type\":\"uint256\"}],\"name\":\"getBorrowingFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"getBorrowingRate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"getBorrowingRateWithDecay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"collateralAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"priceDeviation\",\"type\":\"uint256\"}],\"name\":\"getRedemptionFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"collateralAmount\",\"type\":\"uint256\"}],\"name\":\"getRedemptionFeeWithDecay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"redemptionFee\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"getRedemptionRate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"getRedemptionRateWithDecay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"addedValue\",\"type\":\"uint256\"}],\"name\":\"increaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"delegate\",\"type\":\"address\"}],\"name\":\"isDelegateWhitelisted\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"isWhitelisted\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"lastFeeOperationTime\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"}],\"name\":\"liquidate\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"position\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"collateralChange\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"isCollateralIncrease\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"debtChange\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"isDebtIncrease\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"maxFeePercentage\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"contract IERC20Permit\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"deadline\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"internalType\":\"struct ERC20PermitSignature\",\"name\":\"permitSignature\",\"type\":\"tuple\"}],\"name\":\"managePosition\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"actualCollateralChange\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actualDebtChange\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mintFees\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pendingOwner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"positionManager\",\"outputs\":[{\"internalType\":\"contract IPositionManager\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"priceFeed\",\"outputs\":[{\"internalType\":\"contract IPriceFeed\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"r\",\"outputs\":[{\"internalType\":\"contract IRToken\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rToken\",\"outputs\":[{\"internalType\":\"contract IRToken\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"raftCollateralToken\",\"outputs\":[{\"internalType\":\"contract IERC20Indexable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"raftDebtToken\",\"outputs\":[{\"internalType\":\"contract IERC20Indexable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"recoverTokens\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"redeemCollateral\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"redemptionRebate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"redemptionSpread\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"newBorrowingSpread\",\"type\":\"uint256\"}],\"name\":\"setBorrowingSpread\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"}],\"name\":\"setCollateralEnabled\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newFeeRecipient\",\"type\":\"address\"}],\"name\":\"setFeeRecipient\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"newRedemptionRebate\",\"type\":\"uint256\"}],\"name\":\"setRedemptionRebate\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"newRedemptionSpread\",\"type\":\"uint256\"}],\"name\":\"setRedemptionSpread\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"},{\"internalType\":\"contract ISplitLiquidationCollateral\",\"name\":\"newSplitLiquidationCollateral\",\"type\":\"address\"}],\"name\":\"setSplitLiquidationCollateral\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"collateralToken\",\"type\":\"address\"}],\"name\":\"splitLiquidationCollateral\",\"outputs\":[{\"internalType\":\"contract ISplitLiquidationCollateral\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"delegate\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"whitelisted\",\"type\":\"bool\"}],\"name\":\"whitelistDelegate\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]","ContractName":"InterestRatePositionManager","CompilerVersion":"v0.8.19+commit.7dd6d404","OptimizationUsed":1,"Runs":200000,"ConstructorArguments":"0x000000000000000000000000183015a9ba6ff60230fdeadc3f43b3d788b13e21","EVMVersion":"london","Library":"","LicenseType":"","Proxy":0,"SwarmSource":""}] \ No newline at end of file diff --git a/testdata/etherscan/0xDb53f47aC61FE54F456A4eb3E09832D08Dd7BEec/creation_data.json b/testdata/etherscan/0xDb53f47aC61FE54F456A4eb3E09832D08Dd7BEec/creation_data.json index 6a1311bb4..646ea065a 100644 --- a/testdata/etherscan/0xDb53f47aC61FE54F456A4eb3E09832D08Dd7BEec/creation_data.json +++ b/testdata/etherscan/0xDb53f47aC61FE54F456A4eb3E09832D08Dd7BEec/creation_data.json @@ -1,5 +1 @@ -{ - "contractAddress": "0xdb53f47ac61fe54f456a4eb3e09832d08dd7beec", - "contractCreator": "0xc7f8d87734ab2cbf70030ac8aa82abfe3e8126cb", - "txHash": "0x196898c69f6b1944f1011120b15c0903329d46259c8cdc0fbcad71da1fe58245" -} \ No newline at end of file +{"contractAddress":"0xdb53f47ac61fe54f456a4eb3e09832d08dd7beec","contractCreator":"0xc7f8d87734ab2cbf70030ac8aa82abfe3e8126cb","txHash":"0x196898c69f6b1944f1011120b15c0903329d46259c8cdc0fbcad71da1fe58245"} \ No newline at end of file diff --git a/testdata/etherscan/0xDb53f47aC61FE54F456A4eb3E09832D08Dd7BEec/metadata.json b/testdata/etherscan/0xDb53f47aC61FE54F456A4eb3E09832D08Dd7BEec/metadata.json index 83c1a0635..df45d9be3 100644 --- a/testdata/etherscan/0xDb53f47aC61FE54F456A4eb3E09832D08Dd7BEec/metadata.json +++ b/testdata/etherscan/0xDb53f47aC61FE54F456A4eb3E09832D08Dd7BEec/metadata.json @@ -1,148 +1 @@ -[ - { - "SourceCode": { - "language": "Solidity", - "sources": { - "@solidstate/contracts/token/ERC1155/base/ERC1155Base.sol": { - "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC1155 } from '../IERC1155.sol';\nimport { IERC1155Receiver } from '../IERC1155Receiver.sol';\nimport { ERC1155BaseInternal, ERC1155BaseStorage } from './ERC1155BaseInternal.sol';\n\n/**\n * @title Base ERC1155 contract\n * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts/ (MIT license)\n */\nabstract contract ERC1155Base is IERC1155, ERC1155BaseInternal {\n /**\n * @inheritdoc IERC1155\n */\n function balanceOf(address account, uint256 id)\n public\n view\n virtual\n override\n returns (uint256)\n {\n return _balanceOf(account, id);\n }\n\n /**\n * @inheritdoc IERC1155\n */\n function balanceOfBatch(address[] memory accounts, uint256[] memory ids)\n public\n view\n virtual\n override\n returns (uint256[] memory)\n {\n require(\n accounts.length == ids.length,\n 'ERC1155: accounts and ids length mismatch'\n );\n\n mapping(uint256 => mapping(address => uint256))\n storage balances = ERC1155BaseStorage.layout().balances;\n\n uint256[] memory batchBalances = new uint256[](accounts.length);\n\n unchecked {\n for (uint256 i; i < accounts.length; i++) {\n require(\n accounts[i] != address(0),\n 'ERC1155: batch balance query for the zero address'\n );\n batchBalances[i] = balances[ids[i]][accounts[i]];\n }\n }\n\n return batchBalances;\n }\n\n /**\n * @inheritdoc IERC1155\n */\n function isApprovedForAll(address account, address operator)\n public\n view\n virtual\n override\n returns (bool)\n {\n return ERC1155BaseStorage.layout().operatorApprovals[account][operator];\n }\n\n /**\n * @inheritdoc IERC1155\n */\n function setApprovalForAll(address operator, bool status)\n public\n virtual\n override\n {\n require(\n msg.sender != operator,\n 'ERC1155: setting approval status for self'\n );\n ERC1155BaseStorage.layout().operatorApprovals[msg.sender][\n operator\n ] = status;\n emit ApprovalForAll(msg.sender, operator, status);\n }\n\n /**\n * @inheritdoc IERC1155\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 id,\n uint256 amount,\n bytes memory data\n ) public virtual override {\n require(\n from == msg.sender || isApprovedForAll(from, msg.sender),\n 'ERC1155: caller is not owner nor approved'\n );\n _safeTransfer(msg.sender, from, to, id, amount, data);\n }\n\n /**\n * @inheritdoc IERC1155\n */\n function safeBatchTransferFrom(\n address from,\n address to,\n uint256[] memory ids,\n uint256[] memory amounts,\n bytes memory data\n ) public virtual override {\n require(\n from == msg.sender || isApprovedForAll(from, msg.sender),\n 'ERC1155: caller is not owner nor approved'\n );\n _safeTransferBatch(msg.sender, from, to, ids, amounts, data);\n }\n}\n" - }, - "@solidstate/contracts/introspection/IERC165.sol": { - "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @title ERC165 interface registration interface\n * @dev see https://eips.ethereum.org/EIPS/eip-165\n */\ninterface IERC165 {\n /**\n * @notice query whether contract has registered support for given interface\n * @param interfaceId interface id\n * @return bool whether interface is supported\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n" - }, - "abdk-libraries-solidity/ABDKMath64x64.sol": { - "content": "// SPDX-License-Identifier: BSD-4-Clause\n/*\n * ABDK Math 64.64 Smart Contract Library. Copyright © 2019 by ABDK Consulting.\n * Author: Mikhail Vladimirov \n */\npragma solidity ^0.8.0;\n\n/**\n * Smart contract library of mathematical functions operating with signed\n * 64.64-bit fixed point numbers. Signed 64.64-bit fixed point number is\n * basically a simple fraction whose numerator is signed 128-bit integer and\n * denominator is 2^64. As long as denominator is always the same, there is no\n * need to store it, thus in Solidity signed 64.64-bit fixed point numbers are\n * represented by int128 type holding only the numerator.\n */\nlibrary ABDKMath64x64 {\n /*\n * Minimum value signed 64.64-bit fixed point number may have. \n */\n int128 private constant MIN_64x64 = -0x80000000000000000000000000000000;\n\n /*\n * Maximum value signed 64.64-bit fixed point number may have. \n */\n int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;\n\n /**\n * Convert signed 256-bit integer number into signed 64.64-bit fixed point\n * number. Revert on overflow.\n *\n * @param x signed 256-bit integer number\n * @return signed 64.64-bit fixed point number\n */\n function fromInt (int256 x) internal pure returns (int128) {\n unchecked {\n require (x >= -0x8000000000000000 && x <= 0x7FFFFFFFFFFFFFFF);\n return int128 (x << 64);\n }\n }\n\n /**\n * Convert signed 64.64 fixed point number into signed 64-bit integer number\n * rounding down.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64-bit integer number\n */\n function toInt (int128 x) internal pure returns (int64) {\n unchecked {\n return int64 (x >> 64);\n }\n }\n\n /**\n * Convert unsigned 256-bit integer number into signed 64.64-bit fixed point\n * number. Revert on overflow.\n *\n * @param x unsigned 256-bit integer number\n * @return signed 64.64-bit fixed point number\n */\n function fromUInt (uint256 x) internal pure returns (int128) {\n unchecked {\n require (x <= 0x7FFFFFFFFFFFFFFF);\n return int128 (int256 (x << 64));\n }\n }\n\n /**\n * Convert signed 64.64 fixed point number into unsigned 64-bit integer\n * number rounding down. Revert on underflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @return unsigned 64-bit integer number\n */\n function toUInt (int128 x) internal pure returns (uint64) {\n unchecked {\n require (x >= 0);\n return uint64 (uint128 (x >> 64));\n }\n }\n\n /**\n * Convert signed 128.128 fixed point number into signed 64.64-bit fixed point\n * number rounding down. Revert on overflow.\n *\n * @param x signed 128.128-bin fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function from128x128 (int256 x) internal pure returns (int128) {\n unchecked {\n int256 result = x >> 64;\n require (result >= MIN_64x64 && result <= MAX_64x64);\n return int128 (result);\n }\n }\n\n /**\n * Convert signed 64.64 fixed point number into signed 128.128 fixed point\n * number.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 128.128 fixed point number\n */\n function to128x128 (int128 x) internal pure returns (int256) {\n unchecked {\n return int256 (x) << 64;\n }\n }\n\n /**\n * Calculate x + y. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @param y signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function add (int128 x, int128 y) internal pure returns (int128) {\n unchecked {\n int256 result = int256(x) + y;\n require (result >= MIN_64x64 && result <= MAX_64x64);\n return int128 (result);\n }\n }\n\n /**\n * Calculate x - y. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @param y signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function sub (int128 x, int128 y) internal pure returns (int128) {\n unchecked {\n int256 result = int256(x) - y;\n require (result >= MIN_64x64 && result <= MAX_64x64);\n return int128 (result);\n }\n }\n\n /**\n * Calculate x * y rounding down. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @param y signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function mul (int128 x, int128 y) internal pure returns (int128) {\n unchecked {\n int256 result = int256(x) * y >> 64;\n require (result >= MIN_64x64 && result <= MAX_64x64);\n return int128 (result);\n }\n }\n\n /**\n * Calculate x * y rounding towards zero, where x is signed 64.64 fixed point\n * number and y is signed 256-bit integer number. Revert on overflow.\n *\n * @param x signed 64.64 fixed point number\n * @param y signed 256-bit integer number\n * @return signed 256-bit integer number\n */\n function muli (int128 x, int256 y) internal pure returns (int256) {\n unchecked {\n if (x == MIN_64x64) {\n require (y >= -0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF &&\n y <= 0x1000000000000000000000000000000000000000000000000);\n return -y << 63;\n } else {\n bool negativeResult = false;\n if (x < 0) {\n x = -x;\n negativeResult = true;\n }\n if (y < 0) {\n y = -y; // We rely on overflow behavior here\n negativeResult = !negativeResult;\n }\n uint256 absoluteResult = mulu (x, uint256 (y));\n if (negativeResult) {\n require (absoluteResult <=\n 0x8000000000000000000000000000000000000000000000000000000000000000);\n return -int256 (absoluteResult); // We rely on overflow behavior here\n } else {\n require (absoluteResult <=\n 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);\n return int256 (absoluteResult);\n }\n }\n }\n }\n\n /**\n * Calculate x * y rounding down, where x is signed 64.64 fixed point number\n * and y is unsigned 256-bit integer number. Revert on overflow.\n *\n * @param x signed 64.64 fixed point number\n * @param y unsigned 256-bit integer number\n * @return unsigned 256-bit integer number\n */\n function mulu (int128 x, uint256 y) internal pure returns (uint256) {\n unchecked {\n if (y == 0) return 0;\n\n require (x >= 0);\n\n uint256 lo = (uint256 (int256 (x)) * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) >> 64;\n uint256 hi = uint256 (int256 (x)) * (y >> 128);\n\n require (hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);\n hi <<= 64;\n\n require (hi <=\n 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - lo);\n return hi + lo;\n }\n }\n\n /**\n * Calculate x / y rounding towards zero. Revert on overflow or when y is\n * zero.\n *\n * @param x signed 64.64-bit fixed point number\n * @param y signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function div (int128 x, int128 y) internal pure returns (int128) {\n unchecked {\n require (y != 0);\n int256 result = (int256 (x) << 64) / y;\n require (result >= MIN_64x64 && result <= MAX_64x64);\n return int128 (result);\n }\n }\n\n /**\n * Calculate x / y rounding towards zero, where x and y are signed 256-bit\n * integer numbers. Revert on overflow or when y is zero.\n *\n * @param x signed 256-bit integer number\n * @param y signed 256-bit integer number\n * @return signed 64.64-bit fixed point number\n */\n function divi (int256 x, int256 y) internal pure returns (int128) {\n unchecked {\n require (y != 0);\n\n bool negativeResult = false;\n if (x < 0) {\n x = -x; // We rely on overflow behavior here\n negativeResult = true;\n }\n if (y < 0) {\n y = -y; // We rely on overflow behavior here\n negativeResult = !negativeResult;\n }\n uint128 absoluteResult = divuu (uint256 (x), uint256 (y));\n if (negativeResult) {\n require (absoluteResult <= 0x80000000000000000000000000000000);\n return -int128 (absoluteResult); // We rely on overflow behavior here\n } else {\n require (absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);\n return int128 (absoluteResult); // We rely on overflow behavior here\n }\n }\n }\n\n /**\n * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit\n * integer numbers. Revert on overflow or when y is zero.\n *\n * @param x unsigned 256-bit integer number\n * @param y unsigned 256-bit integer number\n * @return signed 64.64-bit fixed point number\n */\n function divu (uint256 x, uint256 y) internal pure returns (int128) {\n unchecked {\n require (y != 0);\n uint128 result = divuu (x, y);\n require (result <= uint128 (MAX_64x64));\n return int128 (result);\n }\n }\n\n /**\n * Calculate -x. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function neg (int128 x) internal pure returns (int128) {\n unchecked {\n require (x != MIN_64x64);\n return -x;\n }\n }\n\n /**\n * Calculate |x|. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function abs (int128 x) internal pure returns (int128) {\n unchecked {\n require (x != MIN_64x64);\n return x < 0 ? -x : x;\n }\n }\n\n /**\n * Calculate 1 / x rounding towards zero. Revert on overflow or when x is\n * zero.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function inv (int128 x) internal pure returns (int128) {\n unchecked {\n require (x != 0);\n int256 result = int256 (0x100000000000000000000000000000000) / x;\n require (result >= MIN_64x64 && result <= MAX_64x64);\n return int128 (result);\n }\n }\n\n /**\n * Calculate arithmetics average of x and y, i.e. (x + y) / 2 rounding down.\n *\n * @param x signed 64.64-bit fixed point number\n * @param y signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function avg (int128 x, int128 y) internal pure returns (int128) {\n unchecked {\n return int128 ((int256 (x) + int256 (y)) >> 1);\n }\n }\n\n /**\n * Calculate geometric average of x and y, i.e. sqrt (x * y) rounding down.\n * Revert on overflow or in case x * y is negative.\n *\n * @param x signed 64.64-bit fixed point number\n * @param y signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function gavg (int128 x, int128 y) internal pure returns (int128) {\n unchecked {\n int256 m = int256 (x) * int256 (y);\n require (m >= 0);\n require (m <\n 0x4000000000000000000000000000000000000000000000000000000000000000);\n return int128 (sqrtu (uint256 (m)));\n }\n }\n\n /**\n * Calculate x^y assuming 0^0 is 1, where x is signed 64.64 fixed point number\n * and y is unsigned 256-bit integer number. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @param y uint256 value\n * @return signed 64.64-bit fixed point number\n */\n function pow (int128 x, uint256 y) internal pure returns (int128) {\n unchecked {\n bool negative = x < 0 && y & 1 == 1;\n\n uint256 absX = uint128 (x < 0 ? -x : x);\n uint256 absResult;\n absResult = 0x100000000000000000000000000000000;\n\n if (absX <= 0x10000000000000000) {\n absX <<= 63;\n while (y != 0) {\n if (y & 0x1 != 0) {\n absResult = absResult * absX >> 127;\n }\n absX = absX * absX >> 127;\n\n if (y & 0x2 != 0) {\n absResult = absResult * absX >> 127;\n }\n absX = absX * absX >> 127;\n\n if (y & 0x4 != 0) {\n absResult = absResult * absX >> 127;\n }\n absX = absX * absX >> 127;\n\n if (y & 0x8 != 0) {\n absResult = absResult * absX >> 127;\n }\n absX = absX * absX >> 127;\n\n y >>= 4;\n }\n\n absResult >>= 64;\n } else {\n uint256 absXShift = 63;\n if (absX < 0x1000000000000000000000000) { absX <<= 32; absXShift -= 32; }\n if (absX < 0x10000000000000000000000000000) { absX <<= 16; absXShift -= 16; }\n if (absX < 0x1000000000000000000000000000000) { absX <<= 8; absXShift -= 8; }\n if (absX < 0x10000000000000000000000000000000) { absX <<= 4; absXShift -= 4; }\n if (absX < 0x40000000000000000000000000000000) { absX <<= 2; absXShift -= 2; }\n if (absX < 0x80000000000000000000000000000000) { absX <<= 1; absXShift -= 1; }\n\n uint256 resultShift = 0;\n while (y != 0) {\n require (absXShift < 64);\n\n if (y & 0x1 != 0) {\n absResult = absResult * absX >> 127;\n resultShift += absXShift;\n if (absResult > 0x100000000000000000000000000000000) {\n absResult >>= 1;\n resultShift += 1;\n }\n }\n absX = absX * absX >> 127;\n absXShift <<= 1;\n if (absX >= 0x100000000000000000000000000000000) {\n absX >>= 1;\n absXShift += 1;\n }\n\n y >>= 1;\n }\n\n require (resultShift < 64);\n absResult >>= 64 - resultShift;\n }\n int256 result = negative ? -int256 (absResult) : int256 (absResult);\n require (result >= MIN_64x64 && result <= MAX_64x64);\n return int128 (result);\n }\n }\n\n /**\n * Calculate sqrt (x) rounding down. Revert if x < 0.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function sqrt (int128 x) internal pure returns (int128) {\n unchecked {\n require (x >= 0);\n return int128 (sqrtu (uint256 (int256 (x)) << 64));\n }\n }\n\n /**\n * Calculate binary logarithm of x. Revert if x <= 0.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function log_2 (int128 x) internal pure returns (int128) {\n unchecked {\n require (x > 0);\n\n int256 msb = 0;\n int256 xc = x;\n if (xc >= 0x10000000000000000) { xc >>= 64; msb += 64; }\n if (xc >= 0x100000000) { xc >>= 32; msb += 32; }\n if (xc >= 0x10000) { xc >>= 16; msb += 16; }\n if (xc >= 0x100) { xc >>= 8; msb += 8; }\n if (xc >= 0x10) { xc >>= 4; msb += 4; }\n if (xc >= 0x4) { xc >>= 2; msb += 2; }\n if (xc >= 0x2) msb += 1; // No need to shift xc anymore\n\n int256 result = msb - 64 << 64;\n uint256 ux = uint256 (int256 (x)) << uint256 (127 - msb);\n for (int256 bit = 0x8000000000000000; bit > 0; bit >>= 1) {\n ux *= ux;\n uint256 b = ux >> 255;\n ux >>= 127 + b;\n result += bit * int256 (b);\n }\n\n return int128 (result);\n }\n }\n\n /**\n * Calculate natural logarithm of x. Revert if x <= 0.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function ln (int128 x) internal pure returns (int128) {\n unchecked {\n require (x > 0);\n\n return int128 (int256 (\n uint256 (int256 (log_2 (x))) * 0xB17217F7D1CF79ABC9E3B39803F2F6AF >> 128));\n }\n }\n\n /**\n * Calculate binary exponent of x. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function exp_2 (int128 x) internal pure returns (int128) {\n unchecked {\n require (x < 0x400000000000000000); // Overflow\n\n if (x < -0x400000000000000000) return 0; // Underflow\n\n uint256 result = 0x80000000000000000000000000000000;\n\n if (x & 0x8000000000000000 > 0)\n result = result * 0x16A09E667F3BCC908B2FB1366EA957D3E >> 128;\n if (x & 0x4000000000000000 > 0)\n result = result * 0x1306FE0A31B7152DE8D5A46305C85EDEC >> 128;\n if (x & 0x2000000000000000 > 0)\n result = result * 0x1172B83C7D517ADCDF7C8C50EB14A791F >> 128;\n if (x & 0x1000000000000000 > 0)\n result = result * 0x10B5586CF9890F6298B92B71842A98363 >> 128;\n if (x & 0x800000000000000 > 0)\n result = result * 0x1059B0D31585743AE7C548EB68CA417FD >> 128;\n if (x & 0x400000000000000 > 0)\n result = result * 0x102C9A3E778060EE6F7CACA4F7A29BDE8 >> 128;\n if (x & 0x200000000000000 > 0)\n result = result * 0x10163DA9FB33356D84A66AE336DCDFA3F >> 128;\n if (x & 0x100000000000000 > 0)\n result = result * 0x100B1AFA5ABCBED6129AB13EC11DC9543 >> 128;\n if (x & 0x80000000000000 > 0)\n result = result * 0x10058C86DA1C09EA1FF19D294CF2F679B >> 128;\n if (x & 0x40000000000000 > 0)\n result = result * 0x1002C605E2E8CEC506D21BFC89A23A00F >> 128;\n if (x & 0x20000000000000 > 0)\n result = result * 0x100162F3904051FA128BCA9C55C31E5DF >> 128;\n if (x & 0x10000000000000 > 0)\n result = result * 0x1000B175EFFDC76BA38E31671CA939725 >> 128;\n if (x & 0x8000000000000 > 0)\n result = result * 0x100058BA01FB9F96D6CACD4B180917C3D >> 128;\n if (x & 0x4000000000000 > 0)\n result = result * 0x10002C5CC37DA9491D0985C348C68E7B3 >> 128;\n if (x & 0x2000000000000 > 0)\n result = result * 0x1000162E525EE054754457D5995292026 >> 128;\n if (x & 0x1000000000000 > 0)\n result = result * 0x10000B17255775C040618BF4A4ADE83FC >> 128;\n if (x & 0x800000000000 > 0)\n result = result * 0x1000058B91B5BC9AE2EED81E9B7D4CFAB >> 128;\n if (x & 0x400000000000 > 0)\n result = result * 0x100002C5C89D5EC6CA4D7C8ACC017B7C9 >> 128;\n if (x & 0x200000000000 > 0)\n result = result * 0x10000162E43F4F831060E02D839A9D16D >> 128;\n if (x & 0x100000000000 > 0)\n result = result * 0x100000B1721BCFC99D9F890EA06911763 >> 128;\n if (x & 0x80000000000 > 0)\n result = result * 0x10000058B90CF1E6D97F9CA14DBCC1628 >> 128;\n if (x & 0x40000000000 > 0)\n result = result * 0x1000002C5C863B73F016468F6BAC5CA2B >> 128;\n if (x & 0x20000000000 > 0)\n result = result * 0x100000162E430E5A18F6119E3C02282A5 >> 128;\n if (x & 0x10000000000 > 0)\n result = result * 0x1000000B1721835514B86E6D96EFD1BFE >> 128;\n if (x & 0x8000000000 > 0)\n result = result * 0x100000058B90C0B48C6BE5DF846C5B2EF >> 128;\n if (x & 0x4000000000 > 0)\n result = result * 0x10000002C5C8601CC6B9E94213C72737A >> 128;\n if (x & 0x2000000000 > 0)\n result = result * 0x1000000162E42FFF037DF38AA2B219F06 >> 128;\n if (x & 0x1000000000 > 0)\n result = result * 0x10000000B17217FBA9C739AA5819F44F9 >> 128;\n if (x & 0x800000000 > 0)\n result = result * 0x1000000058B90BFCDEE5ACD3C1CEDC823 >> 128;\n if (x & 0x400000000 > 0)\n result = result * 0x100000002C5C85FE31F35A6A30DA1BE50 >> 128;\n if (x & 0x200000000 > 0)\n result = result * 0x10000000162E42FF0999CE3541B9FFFCF >> 128;\n if (x & 0x100000000 > 0)\n result = result * 0x100000000B17217F80F4EF5AADDA45554 >> 128;\n if (x & 0x80000000 > 0)\n result = result * 0x10000000058B90BFBF8479BD5A81B51AD >> 128;\n if (x & 0x40000000 > 0)\n result = result * 0x1000000002C5C85FDF84BD62AE30A74CC >> 128;\n if (x & 0x20000000 > 0)\n result = result * 0x100000000162E42FEFB2FED257559BDAA >> 128;\n if (x & 0x10000000 > 0)\n result = result * 0x1000000000B17217F7D5A7716BBA4A9AE >> 128;\n if (x & 0x8000000 > 0)\n result = result * 0x100000000058B90BFBE9DDBAC5E109CCE >> 128;\n if (x & 0x4000000 > 0)\n result = result * 0x10000000002C5C85FDF4B15DE6F17EB0D >> 128;\n if (x & 0x2000000 > 0)\n result = result * 0x1000000000162E42FEFA494F1478FDE05 >> 128;\n if (x & 0x1000000 > 0)\n result = result * 0x10000000000B17217F7D20CF927C8E94C >> 128;\n if (x & 0x800000 > 0)\n result = result * 0x1000000000058B90BFBE8F71CB4E4B33D >> 128;\n if (x & 0x400000 > 0)\n result = result * 0x100000000002C5C85FDF477B662B26945 >> 128;\n if (x & 0x200000 > 0)\n result = result * 0x10000000000162E42FEFA3AE53369388C >> 128;\n if (x & 0x100000 > 0)\n result = result * 0x100000000000B17217F7D1D351A389D40 >> 128;\n if (x & 0x80000 > 0)\n result = result * 0x10000000000058B90BFBE8E8B2D3D4EDE >> 128;\n if (x & 0x40000 > 0)\n result = result * 0x1000000000002C5C85FDF4741BEA6E77E >> 128;\n if (x & 0x20000 > 0)\n result = result * 0x100000000000162E42FEFA39FE95583C2 >> 128;\n if (x & 0x10000 > 0)\n result = result * 0x1000000000000B17217F7D1CFB72B45E1 >> 128;\n if (x & 0x8000 > 0)\n result = result * 0x100000000000058B90BFBE8E7CC35C3F0 >> 128;\n if (x & 0x4000 > 0)\n result = result * 0x10000000000002C5C85FDF473E242EA38 >> 128;\n if (x & 0x2000 > 0)\n result = result * 0x1000000000000162E42FEFA39F02B772C >> 128;\n if (x & 0x1000 > 0)\n result = result * 0x10000000000000B17217F7D1CF7D83C1A >> 128;\n if (x & 0x800 > 0)\n result = result * 0x1000000000000058B90BFBE8E7BDCBE2E >> 128;\n if (x & 0x400 > 0)\n result = result * 0x100000000000002C5C85FDF473DEA871F >> 128;\n if (x & 0x200 > 0)\n result = result * 0x10000000000000162E42FEFA39EF44D91 >> 128;\n if (x & 0x100 > 0)\n result = result * 0x100000000000000B17217F7D1CF79E949 >> 128;\n if (x & 0x80 > 0)\n result = result * 0x10000000000000058B90BFBE8E7BCE544 >> 128;\n if (x & 0x40 > 0)\n result = result * 0x1000000000000002C5C85FDF473DE6ECA >> 128;\n if (x & 0x20 > 0)\n result = result * 0x100000000000000162E42FEFA39EF366F >> 128;\n if (x & 0x10 > 0)\n result = result * 0x1000000000000000B17217F7D1CF79AFA >> 128;\n if (x & 0x8 > 0)\n result = result * 0x100000000000000058B90BFBE8E7BCD6D >> 128;\n if (x & 0x4 > 0)\n result = result * 0x10000000000000002C5C85FDF473DE6B2 >> 128;\n if (x & 0x2 > 0)\n result = result * 0x1000000000000000162E42FEFA39EF358 >> 128;\n if (x & 0x1 > 0)\n result = result * 0x10000000000000000B17217F7D1CF79AB >> 128;\n\n result >>= uint256 (int256 (63 - (x >> 64)));\n require (result <= uint256 (int256 (MAX_64x64)));\n\n return int128 (int256 (result));\n }\n }\n\n /**\n * Calculate natural exponent of x. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function exp (int128 x) internal pure returns (int128) {\n unchecked {\n require (x < 0x400000000000000000); // Overflow\n\n if (x < -0x400000000000000000) return 0; // Underflow\n\n return exp_2 (\n int128 (int256 (x) * 0x171547652B82FE1777D0FFDA0D23A7D12 >> 128));\n }\n }\n\n /**\n * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit\n * integer numbers. Revert on overflow or when y is zero.\n *\n * @param x unsigned 256-bit integer number\n * @param y unsigned 256-bit integer number\n * @return unsigned 64.64-bit fixed point number\n */\n function divuu (uint256 x, uint256 y) private pure returns (uint128) {\n unchecked {\n require (y != 0);\n\n uint256 result;\n\n if (x <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)\n result = (x << 64) / y;\n else {\n uint256 msb = 192;\n uint256 xc = x >> 192;\n if (xc >= 0x100000000) { xc >>= 32; msb += 32; }\n if (xc >= 0x10000) { xc >>= 16; msb += 16; }\n if (xc >= 0x100) { xc >>= 8; msb += 8; }\n if (xc >= 0x10) { xc >>= 4; msb += 4; }\n if (xc >= 0x4) { xc >>= 2; msb += 2; }\n if (xc >= 0x2) msb += 1; // No need to shift xc anymore\n\n result = (x << 255 - msb) / ((y - 1 >> msb - 191) + 1);\n require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);\n\n uint256 hi = result * (y >> 128);\n uint256 lo = result * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);\n\n uint256 xh = x >> 192;\n uint256 xl = x << 64;\n\n if (xl < lo) xh -= 1;\n xl -= lo; // We rely on overflow behavior here\n lo = hi << 128;\n if (xl < lo) xh -= 1;\n xl -= lo; // We rely on overflow behavior here\n\n assert (xh == hi >> 128);\n\n result += xl / y;\n }\n\n require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);\n return uint128 (result);\n }\n }\n\n /**\n * Calculate sqrt (x) rounding down, where x is unsigned 256-bit integer\n * number.\n *\n * @param x unsigned 256-bit integer number\n * @return unsigned 128-bit integer number\n */\n function sqrtu (uint256 x) private pure returns (uint128) {\n unchecked {\n if (x == 0) return 0;\n else {\n uint256 xx = x;\n uint256 r = 1;\n if (xx >= 0x100000000000000000000000000000000) { xx >>= 128; r <<= 64; }\n if (xx >= 0x10000000000000000) { xx >>= 64; r <<= 32; }\n if (xx >= 0x100000000) { xx >>= 32; r <<= 16; }\n if (xx >= 0x10000) { xx >>= 16; r <<= 8; }\n if (xx >= 0x100) { xx >>= 8; r <<= 4; }\n if (xx >= 0x10) { xx >>= 4; r <<= 2; }\n if (xx >= 0x8) { r <<= 1; }\n r = (r + x / r) >> 1;\n r = (r + x / r) >> 1;\n r = (r + x / r) >> 1;\n r = (r + x / r) >> 1;\n r = (r + x / r) >> 1;\n r = (r + x / r) >> 1;\n r = (r + x / r) >> 1; // Seven iterations should be enough\n uint256 r1 = x / r;\n return uint128 (r < r1 ? r : r1);\n }\n }\n }\n}\n" - }, - "@solidstate/contracts/token/ERC20/metadata/IERC20Metadata.sol": { - "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @title ERC20 metadata interface\n */\ninterface IERC20Metadata {\n /**\n * @notice return token name\n * @return token name\n */\n function name() external view returns (string memory);\n\n /**\n * @notice return token symbol\n * @return token symbol\n */\n function symbol() external view returns (string memory);\n\n /**\n * @notice return token decimals, generally used only for display purposes\n * @return token decimals\n */\n function decimals() external view returns (uint8);\n}\n" - }, - "contracts/staking/FeeDiscountStorage.sol": { - "content": "// SPDX-License-Identifier: BUSL-1.1\r\n// For further clarification please see https://license.premia.legal\r\n\r\npragma solidity ^0.8.0;\r\n\r\nlibrary FeeDiscountStorage {\r\n bytes32 internal constant STORAGE_SLOT =\r\n keccak256(\"premia.contracts.staking.PremiaFeeDiscount\");\r\n\r\n struct UserInfo {\r\n uint256 balance; // Balance staked by user\r\n uint64 stakePeriod; // Stake period selected by user\r\n uint64 lockedUntil; // Timestamp at which the lock ends\r\n }\r\n\r\n struct Layout {\r\n // User data with xPREMIA balance staked and date at which lock ends\r\n mapping(address => UserInfo) userInfo;\r\n }\r\n\r\n function layout() internal pure returns (Layout storage l) {\r\n bytes32 slot = STORAGE_SLOT;\r\n assembly {\r\n l.slot := slot\r\n }\r\n }\r\n}\r\n" - }, - "contracts/libraries/OptionMath.sol": { - "content": "// SPDX-License-Identifier: BUSL-1.1\r\n// For further clarification please see https://license.premia.legal\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport {ABDKMath64x64} from \"abdk-libraries-solidity/ABDKMath64x64.sol\";\r\n\r\nlibrary OptionMath {\r\n using ABDKMath64x64 for int128;\r\n\r\n struct QuoteArgs {\r\n int128 varianceAnnualized64x64; // 64x64 fixed point representation of annualized variance\r\n int128 strike64x64; // 64x64 fixed point representation of strike price\r\n int128 spot64x64; // 64x64 fixed point representation of spot price\r\n int128 timeToMaturity64x64; // 64x64 fixed point representation of duration of option contract (in years)\r\n int128 oldCLevel64x64; // 64x64 fixed point representation of C-Level of Pool before purchase\r\n int128 oldPoolState; // 64x64 fixed point representation of current state of the pool\r\n int128 newPoolState; // 64x64 fixed point representation of state of the pool after trade\r\n int128 steepness64x64; // 64x64 fixed point representation of Pool state delta multiplier\r\n int128 minAPY64x64; // 64x64 fixed point representation of minimum APY for capital locked up to underwrite options\r\n bool isCall; // whether to price \"call\" or \"put\" option\r\n }\r\n\r\n struct CalculateCLevelDecayArgs {\r\n int128 timeIntervalsElapsed64x64; // 64x64 fixed point representation of quantity of discrete arbitrary intervals elapsed since last update\r\n int128 oldCLevel64x64; // 64x64 fixed point representation of C-Level prior to accounting for decay\r\n int128 utilization64x64; // 64x64 fixed point representation of pool capital utilization rate\r\n int128 utilizationLowerBound64x64;\r\n int128 utilizationUpperBound64x64;\r\n int128 cLevelLowerBound64x64;\r\n int128 cLevelUpperBound64x64;\r\n int128 cConvergenceULowerBound64x64;\r\n int128 cConvergenceUUpperBound64x64;\r\n }\r\n\r\n // 64x64 fixed point integer constants\r\n int128 internal constant ONE_64x64 = 0x10000000000000000;\r\n int128 internal constant THREE_64x64 = 0x30000000000000000;\r\n\r\n // 64x64 fixed point constants used in Choudhury’s approximation of the Black-Scholes CDF\r\n int128 private constant CDF_CONST_0 = 0x09109f285df452394; // 2260 / 3989\r\n int128 private constant CDF_CONST_1 = 0x19abac0ea1da65036; // 6400 / 3989\r\n int128 private constant CDF_CONST_2 = 0x0d3c84b78b749bd6b; // 3300 / 3989\r\n\r\n /**\r\n * @notice recalculate C-Level based on change in liquidity\r\n * @param initialCLevel64x64 64x64 fixed point representation of C-Level of Pool before update\r\n * @param oldPoolState64x64 64x64 fixed point representation of liquidity in pool before update\r\n * @param newPoolState64x64 64x64 fixed point representation of liquidity in pool after update\r\n * @param steepness64x64 64x64 fixed point representation of steepness coefficient\r\n * @return 64x64 fixed point representation of new C-Level\r\n */\r\n function calculateCLevel(\r\n int128 initialCLevel64x64,\r\n int128 oldPoolState64x64,\r\n int128 newPoolState64x64,\r\n int128 steepness64x64\r\n ) external pure returns (int128) {\r\n return\r\n newPoolState64x64\r\n .sub(oldPoolState64x64)\r\n .div(\r\n oldPoolState64x64 > newPoolState64x64\r\n ? oldPoolState64x64\r\n : newPoolState64x64\r\n )\r\n .mul(steepness64x64)\r\n .neg()\r\n .exp()\r\n .mul(initialCLevel64x64);\r\n }\r\n\r\n /**\r\n * @notice calculate the price of an option using the Premia Finance model\r\n * @param args arguments of quotePrice\r\n * @return premiaPrice64x64 64x64 fixed point representation of Premia option price\r\n * @return cLevel64x64 64x64 fixed point representation of C-Level of Pool after purchase\r\n */\r\n function quotePrice(QuoteArgs memory args)\r\n external\r\n pure\r\n returns (\r\n int128 premiaPrice64x64,\r\n int128 cLevel64x64,\r\n int128 slippageCoefficient64x64\r\n )\r\n {\r\n int128 deltaPoolState64x64 = args\r\n .newPoolState\r\n .sub(args.oldPoolState)\r\n .div(args.oldPoolState)\r\n .mul(args.steepness64x64);\r\n int128 tradingDelta64x64 = deltaPoolState64x64.neg().exp();\r\n\r\n int128 blackScholesPrice64x64 = _blackScholesPrice(\r\n args.varianceAnnualized64x64,\r\n args.strike64x64,\r\n args.spot64x64,\r\n args.timeToMaturity64x64,\r\n args.isCall\r\n );\r\n\r\n cLevel64x64 = tradingDelta64x64.mul(args.oldCLevel64x64);\r\n slippageCoefficient64x64 = ONE_64x64.sub(tradingDelta64x64).div(\r\n deltaPoolState64x64\r\n );\r\n\r\n premiaPrice64x64 = blackScholesPrice64x64.mul(cLevel64x64).mul(\r\n slippageCoefficient64x64\r\n );\r\n\r\n int128 intrinsicValue64x64;\r\n\r\n if (args.isCall && args.strike64x64 < args.spot64x64) {\r\n intrinsicValue64x64 = args.spot64x64.sub(args.strike64x64);\r\n } else if (!args.isCall && args.strike64x64 > args.spot64x64) {\r\n intrinsicValue64x64 = args.strike64x64.sub(args.spot64x64);\r\n }\r\n\r\n int128 collateralValue64x64 = args.isCall\r\n ? args.spot64x64\r\n : args.strike64x64;\r\n\r\n int128 minPrice64x64 = intrinsicValue64x64.add(\r\n collateralValue64x64.mul(args.minAPY64x64).mul(\r\n args.timeToMaturity64x64\r\n )\r\n );\r\n\r\n if (minPrice64x64 > premiaPrice64x64) {\r\n premiaPrice64x64 = minPrice64x64;\r\n }\r\n }\r\n\r\n /**\r\n * @notice calculate the decay of C-Level based on heat diffusion function\r\n * @param args structured CalculateCLevelDecayArgs\r\n * @return cLevelDecayed64x64 C-Level after accounting for decay\r\n */\r\n function calculateCLevelDecay(CalculateCLevelDecayArgs memory args)\r\n external\r\n pure\r\n returns (int128 cLevelDecayed64x64)\r\n {\r\n int128 convFHighU64x64 = (args.utilization64x64 >=\r\n args.utilizationUpperBound64x64 &&\r\n args.oldCLevel64x64 <= args.cLevelLowerBound64x64)\r\n ? ONE_64x64\r\n : int128(0);\r\n\r\n int128 convFLowU64x64 = (args.utilization64x64 <=\r\n args.utilizationLowerBound64x64 &&\r\n args.oldCLevel64x64 >= args.cLevelUpperBound64x64)\r\n ? ONE_64x64\r\n : int128(0);\r\n\r\n cLevelDecayed64x64 = args\r\n .oldCLevel64x64\r\n .sub(args.cConvergenceULowerBound64x64.mul(convFLowU64x64))\r\n .sub(args.cConvergenceUUpperBound64x64.mul(convFHighU64x64))\r\n .mul(\r\n convFLowU64x64\r\n .mul(ONE_64x64.sub(args.utilization64x64))\r\n .add(convFHighU64x64.mul(args.utilization64x64))\r\n .mul(args.timeIntervalsElapsed64x64)\r\n .neg()\r\n .exp()\r\n )\r\n .add(\r\n args.cConvergenceULowerBound64x64.mul(convFLowU64x64).add(\r\n args.cConvergenceUUpperBound64x64.mul(convFHighU64x64)\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * @notice calculate the exponential decay coefficient for a given interval\r\n * @param oldTimestamp timestamp of previous update\r\n * @param newTimestamp current timestamp\r\n * @return 64x64 fixed point representation of exponential decay coefficient\r\n */\r\n function _decay(uint256 oldTimestamp, uint256 newTimestamp)\r\n internal\r\n pure\r\n returns (int128)\r\n {\r\n return\r\n ONE_64x64.sub(\r\n (-ABDKMath64x64.divu(newTimestamp - oldTimestamp, 7 days)).exp()\r\n );\r\n }\r\n\r\n /**\r\n * @notice calculate Choudhury’s approximation of the Black-Scholes CDF\r\n * @param input64x64 64x64 fixed point representation of random variable\r\n * @return 64x64 fixed point representation of the approximated CDF of x\r\n */\r\n function _N(int128 input64x64) internal pure returns (int128) {\r\n // squaring via mul is cheaper than via pow\r\n int128 inputSquared64x64 = input64x64.mul(input64x64);\r\n\r\n int128 value64x64 = (-inputSquared64x64 >> 1).exp().div(\r\n CDF_CONST_0.add(CDF_CONST_1.mul(input64x64.abs())).add(\r\n CDF_CONST_2.mul(inputSquared64x64.add(THREE_64x64).sqrt())\r\n )\r\n );\r\n\r\n return input64x64 > 0 ? ONE_64x64.sub(value64x64) : value64x64;\r\n }\r\n\r\n /**\r\n * @notice calculate the price of an option using the Black-Scholes model\r\n * @param varianceAnnualized64x64 64x64 fixed point representation of annualized variance\r\n * @param strike64x64 64x64 fixed point representation of strike price\r\n * @param spot64x64 64x64 fixed point representation of spot price\r\n * @param timeToMaturity64x64 64x64 fixed point representation of duration of option contract (in years)\r\n * @param isCall whether to price \"call\" or \"put\" option\r\n * @return 64x64 fixed point representation of Black-Scholes option price\r\n */\r\n function _blackScholesPrice(\r\n int128 varianceAnnualized64x64,\r\n int128 strike64x64,\r\n int128 spot64x64,\r\n int128 timeToMaturity64x64,\r\n bool isCall\r\n ) internal pure returns (int128) {\r\n int128 cumulativeVariance64x64 = timeToMaturity64x64.mul(\r\n varianceAnnualized64x64\r\n );\r\n int128 cumulativeVarianceSqrt64x64 = cumulativeVariance64x64.sqrt();\r\n\r\n int128 d1_64x64 = spot64x64\r\n .div(strike64x64)\r\n .ln()\r\n .add(cumulativeVariance64x64 >> 1)\r\n .div(cumulativeVarianceSqrt64x64);\r\n int128 d2_64x64 = d1_64x64.sub(cumulativeVarianceSqrt64x64);\r\n\r\n if (isCall) {\r\n return\r\n spot64x64.mul(_N(d1_64x64)).sub(strike64x64.mul(_N(d2_64x64)));\r\n } else {\r\n return\r\n -spot64x64.mul(_N(-d1_64x64)).sub(\r\n strike64x64.mul(_N(-d2_64x64))\r\n );\r\n }\r\n }\r\n}\r\n" - }, - "@solidstate/contracts/access/IERC173.sol": { - "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @title Contract ownership standard interface\n * @dev see https://eips.ethereum.org/EIPS/eip-173\n */\ninterface IERC173 {\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n\n /**\n * @notice get the ERC173 contract owner\n * @return conract owner\n */\n function owner() external view returns (address);\n\n /**\n * @notice transfer contract ownership to new account\n * @param account address of new owner\n */\n function transferOwnership(address account) external;\n}\n" - }, - "contracts/libraries/ABDKMath64x64Token.sol": { - "content": "// SPDX-License-Identifier: BUSL-1.1\r\n// For further clarification please see https://license.premia.legal\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport {ABDKMath64x64} from \"abdk-libraries-solidity/ABDKMath64x64.sol\";\r\n\r\nlibrary ABDKMath64x64Token {\r\n using ABDKMath64x64 for int128;\r\n\r\n /**\r\n * @notice convert 64x64 fixed point representation of token amount to decimal\r\n * @param value64x64 64x64 fixed point representation of token amount\r\n * @param decimals token display decimals\r\n * @return value decimal representation of token amount\r\n */\r\n function toDecimals(int128 value64x64, uint8 decimals)\r\n internal\r\n pure\r\n returns (uint256 value)\r\n {\r\n value = value64x64.mulu(10**decimals);\r\n }\r\n\r\n /**\r\n * @notice convert decimal representation of token amount to 64x64 fixed point\r\n * @param value decimal representation of token amount\r\n * @param decimals token display decimals\r\n * @return value64x64 64x64 fixed point representation of token amount\r\n */\r\n function fromDecimals(uint256 value, uint8 decimals)\r\n internal\r\n pure\r\n returns (int128 value64x64)\r\n {\r\n value64x64 = ABDKMath64x64.divu(value, 10**decimals);\r\n }\r\n\r\n /**\r\n * @notice convert 64x64 fixed point representation of token amount to wei (18 decimals)\r\n * @param value64x64 64x64 fixed point representation of token amount\r\n * @return value wei representation of token amount\r\n */\r\n function toWei(int128 value64x64) internal pure returns (uint256 value) {\r\n value = toDecimals(value64x64, 18);\r\n }\r\n\r\n /**\r\n * @notice convert wei representation (18 decimals) of token amount to 64x64 fixed point\r\n * @param value wei representation of token amount\r\n * @return value64x64 64x64 fixed point representation of token amount\r\n */\r\n function fromWei(uint256 value) internal pure returns (int128 value64x64) {\r\n value64x64 = fromDecimals(value, 18);\r\n }\r\n}\r\n" - }, - "@solidstate/contracts/token/ERC1155/IERC1155.sol": { - "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC1155Internal } from './IERC1155Internal.sol';\nimport { IERC165 } from '../../introspection/IERC165.sol';\n\n/**\n * @notice ERC1155 interface\n * @dev see https://github.com/ethereum/EIPs/issues/1155\n */\ninterface IERC1155 is IERC1155Internal, IERC165 {\n /**\n * @notice query the balance of given token held by given address\n * @param account address to query\n * @param id token to query\n * @return token balance\n */\n function balanceOf(address account, uint256 id)\n external\n view\n returns (uint256);\n\n /**\n * @notice query the balances of given tokens held by given addresses\n * @param accounts addresss to query\n * @param ids tokens to query\n * @return token balances\n */\n function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)\n external\n view\n returns (uint256[] memory);\n\n /**\n * @notice query approval status of given operator with respect to given address\n * @param account address to query for approval granted\n * @param operator address to query for approval received\n * @return whether operator is approved to spend tokens held by account\n */\n function isApprovedForAll(address account, address operator)\n external\n view\n returns (bool);\n\n /**\n * @notice grant approval to or revoke approval from given operator to spend held tokens\n * @param operator address whose approval status to update\n * @param status whether operator should be considered approved\n */\n function setApprovalForAll(address operator, bool status) external;\n\n /**\n * @notice transfer tokens between given addresses, checking for ERC1155Receiver implementation if applicable\n * @param from sender of tokens\n * @param to receiver of tokens\n * @param id token ID\n * @param amount quantity of tokens to transfer\n * @param data data payload\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 id,\n uint256 amount,\n bytes calldata data\n ) external;\n\n /**\n * @notice transfer batch of tokens between given addresses, checking for ERC1155Receiver implementation if applicable\n * @param from sender of tokens\n * @param to receiver of tokens\n * @param ids list of token IDs\n * @param amounts list of quantities of tokens to transfer\n * @param data data payload\n */\n function safeBatchTransferFrom(\n address from,\n address to,\n uint256[] calldata ids,\n uint256[] calldata amounts,\n bytes calldata data\n ) external;\n}\n" - }, - "contracts/staking/IFeeDiscount.sol": { - "content": "// SPDX-License-Identifier: LGPL-3.0-or-later\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport {FeeDiscountStorage} from \"./FeeDiscountStorage.sol\";\r\n\r\ninterface IFeeDiscount {\r\n event Staked(\r\n address indexed user,\r\n uint256 amount,\r\n uint256 stakePeriod,\r\n uint256 lockedUntil\r\n );\r\n event Unstaked(address indexed user, uint256 amount);\r\n\r\n struct StakeLevel {\r\n uint256 amount; // Amount to stake\r\n uint256 discount; // Discount when amount is reached\r\n }\r\n\r\n /**\r\n * @notice Stake using IERC2612 permit\r\n * @param amount The amount of xPremia to stake\r\n * @param period The lockup period (in seconds)\r\n * @param deadline Deadline after which permit will fail\r\n * @param v V\r\n * @param r R\r\n * @param s S\r\n */\r\n function stakeWithPermit(\r\n uint256 amount,\r\n uint256 period,\r\n uint256 deadline,\r\n uint8 v,\r\n bytes32 r,\r\n bytes32 s\r\n ) external;\r\n\r\n /**\r\n * @notice Lockup xPremia for protocol fee discounts\r\n * Longer period of locking will apply a multiplier on the amount staked, in the fee discount calculation\r\n * @param amount The amount of xPremia to stake\r\n * @param period The lockup period (in seconds)\r\n */\r\n function stake(uint256 amount, uint256 period) external;\r\n\r\n /**\r\n * @notice Unstake xPremia (If lockup period has ended)\r\n * @param amount The amount of xPremia to unstake\r\n */\r\n function unstake(uint256 amount) external;\r\n\r\n //////////\r\n // View //\r\n //////////\r\n\r\n /**\r\n * Calculate the stake amount of a user, after applying the bonus from the lockup period chosen\r\n * @param user The user from which to query the stake amount\r\n * @return The user stake amount after applying the bonus\r\n */\r\n function getStakeAmountWithBonus(address user)\r\n external\r\n view\r\n returns (uint256);\r\n\r\n /**\r\n * @notice Calculate the % of fee discount for user, based on his stake\r\n * @param user The _user for which the discount is for\r\n * @return Percentage of protocol fee discount (in basis point)\r\n * Ex : 1000 = 10% fee discount\r\n */\r\n function getDiscount(address user) external view returns (uint256);\r\n\r\n /**\r\n * @notice Get stake levels\r\n * @return Stake levels\r\n * Ex : 2500 = -25%\r\n */\r\n function getStakeLevels() external returns (StakeLevel[] memory);\r\n\r\n /**\r\n * @notice Get stake period multiplier\r\n * @param period The duration (in seconds) for which tokens are locked\r\n * @return The multiplier for this staking period\r\n * Ex : 20000 = x2\r\n */\r\n function getStakePeriodMultiplier(uint256 period)\r\n external\r\n returns (uint256);\r\n\r\n /**\r\n * @notice Get staking infos of a user\r\n * @param user The user address for which to get staking infos\r\n * @return The staking infos of the user\r\n */\r\n function getUserInfo(address user)\r\n external\r\n view\r\n returns (FeeDiscountStorage.UserInfo memory);\r\n}\r\n" - }, - "@solidstate/contracts/token/ERC1155/base/ERC1155BaseInternal.sol": { - "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { AddressUtils } from '../../../utils/AddressUtils.sol';\nimport { IERC1155Internal } from '../IERC1155Internal.sol';\nimport { IERC1155Receiver } from '../IERC1155Receiver.sol';\nimport { ERC1155BaseStorage } from './ERC1155BaseStorage.sol';\n\n/**\n * @title Base ERC1155 internal functions\n * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts/ (MIT license)\n */\nabstract contract ERC1155BaseInternal is IERC1155Internal {\n using AddressUtils for address;\n\n /**\n * @notice query the balance of given token held by given address\n * @param account address to query\n * @param id token to query\n * @return token balance\n */\n function _balanceOf(address account, uint256 id)\n internal\n view\n virtual\n returns (uint256)\n {\n require(\n account != address(0),\n 'ERC1155: balance query for the zero address'\n );\n return ERC1155BaseStorage.layout().balances[id][account];\n }\n\n /**\n * @notice mint given quantity of tokens for given address\n * @dev ERC1155Receiver implementation is not checked\n * @param account beneficiary of minting\n * @param id token ID\n * @param amount quantity of tokens to mint\n * @param data data payload\n */\n function _mint(\n address account,\n uint256 id,\n uint256 amount,\n bytes memory data\n ) internal virtual {\n require(account != address(0), 'ERC1155: mint to the zero address');\n\n _beforeTokenTransfer(\n msg.sender,\n address(0),\n account,\n _asSingletonArray(id),\n _asSingletonArray(amount),\n data\n );\n\n mapping(address => uint256) storage balances = ERC1155BaseStorage\n .layout()\n .balances[id];\n balances[account] += amount;\n\n emit TransferSingle(msg.sender, address(0), account, id, amount);\n }\n\n /**\n * @notice mint given quantity of tokens for given address\n * @param account beneficiary of minting\n * @param id token ID\n * @param amount quantity of tokens to mint\n * @param data data payload\n */\n function _safeMint(\n address account,\n uint256 id,\n uint256 amount,\n bytes memory data\n ) internal virtual {\n _mint(account, id, amount, data);\n\n _doSafeTransferAcceptanceCheck(\n msg.sender,\n address(0),\n account,\n id,\n amount,\n data\n );\n }\n\n /**\n * @notice mint batch of tokens for given address\n * @dev ERC1155Receiver implementation is not checked\n * @param account beneficiary of minting\n * @param ids list of token IDs\n * @param amounts list of quantities of tokens to mint\n * @param data data payload\n */\n function _mintBatch(\n address account,\n uint256[] memory ids,\n uint256[] memory amounts,\n bytes memory data\n ) internal virtual {\n require(account != address(0), 'ERC1155: mint to the zero address');\n require(\n ids.length == amounts.length,\n 'ERC1155: ids and amounts length mismatch'\n );\n\n _beforeTokenTransfer(\n msg.sender,\n address(0),\n account,\n ids,\n amounts,\n data\n );\n\n mapping(uint256 => mapping(address => uint256))\n storage balances = ERC1155BaseStorage.layout().balances;\n\n for (uint256 i; i < ids.length; i++) {\n balances[ids[i]][account] += amounts[i];\n }\n\n emit TransferBatch(msg.sender, address(0), account, ids, amounts);\n }\n\n /**\n * @notice mint batch of tokens for given address\n * @param account beneficiary of minting\n * @param ids list of token IDs\n * @param amounts list of quantities of tokens to mint\n * @param data data payload\n */\n function _safeMintBatch(\n address account,\n uint256[] memory ids,\n uint256[] memory amounts,\n bytes memory data\n ) internal virtual {\n _mintBatch(account, ids, amounts, data);\n\n _doSafeBatchTransferAcceptanceCheck(\n msg.sender,\n address(0),\n account,\n ids,\n amounts,\n data\n );\n }\n\n /**\n * @notice burn given quantity of tokens held by given address\n * @param account holder of tokens to burn\n * @param id token ID\n * @param amount quantity of tokens to burn\n */\n function _burn(\n address account,\n uint256 id,\n uint256 amount\n ) internal virtual {\n require(account != address(0), 'ERC1155: burn from the zero address');\n\n _beforeTokenTransfer(\n msg.sender,\n account,\n address(0),\n _asSingletonArray(id),\n _asSingletonArray(amount),\n ''\n );\n\n mapping(address => uint256) storage balances = ERC1155BaseStorage\n .layout()\n .balances[id];\n\n unchecked {\n require(\n balances[account] >= amount,\n 'ERC1155: burn amount exceeds balances'\n );\n balances[account] -= amount;\n }\n\n emit TransferSingle(msg.sender, account, address(0), id, amount);\n }\n\n /**\n * @notice burn given batch of tokens held by given address\n * @param account holder of tokens to burn\n * @param ids token IDs\n * @param amounts quantities of tokens to burn\n */\n function _burnBatch(\n address account,\n uint256[] memory ids,\n uint256[] memory amounts\n ) internal virtual {\n require(account != address(0), 'ERC1155: burn from the zero address');\n require(\n ids.length == amounts.length,\n 'ERC1155: ids and amounts length mismatch'\n );\n\n _beforeTokenTransfer(msg.sender, account, address(0), ids, amounts, '');\n\n mapping(uint256 => mapping(address => uint256))\n storage balances = ERC1155BaseStorage.layout().balances;\n\n unchecked {\n for (uint256 i; i < ids.length; i++) {\n uint256 id = ids[i];\n require(\n balances[id][account] >= amounts[i],\n 'ERC1155: burn amount exceeds balance'\n );\n balances[id][account] -= amounts[i];\n }\n }\n\n emit TransferBatch(msg.sender, account, address(0), ids, amounts);\n }\n\n /**\n * @notice transfer tokens between given addresses\n * @dev ERC1155Receiver implementation is not checked\n * @param operator executor of transfer\n * @param sender sender of tokens\n * @param recipient receiver of tokens\n * @param id token ID\n * @param amount quantity of tokens to transfer\n * @param data data payload\n */\n function _transfer(\n address operator,\n address sender,\n address recipient,\n uint256 id,\n uint256 amount,\n bytes memory data\n ) internal virtual {\n require(\n recipient != address(0),\n 'ERC1155: transfer to the zero address'\n );\n\n _beforeTokenTransfer(\n operator,\n sender,\n recipient,\n _asSingletonArray(id),\n _asSingletonArray(amount),\n data\n );\n\n mapping(uint256 => mapping(address => uint256))\n storage balances = ERC1155BaseStorage.layout().balances;\n\n unchecked {\n uint256 senderBalance = balances[id][sender];\n require(\n senderBalance >= amount,\n 'ERC1155: insufficient balances for transfer'\n );\n balances[id][sender] = senderBalance - amount;\n }\n\n balances[id][recipient] += amount;\n\n emit TransferSingle(operator, sender, recipient, id, amount);\n }\n\n /**\n * @notice transfer tokens between given addresses\n * @param operator executor of transfer\n * @param sender sender of tokens\n * @param recipient receiver of tokens\n * @param id token ID\n * @param amount quantity of tokens to transfer\n * @param data data payload\n */\n function _safeTransfer(\n address operator,\n address sender,\n address recipient,\n uint256 id,\n uint256 amount,\n bytes memory data\n ) internal virtual {\n _transfer(operator, sender, recipient, id, amount, data);\n\n _doSafeTransferAcceptanceCheck(\n operator,\n sender,\n recipient,\n id,\n amount,\n data\n );\n }\n\n /**\n * @notice transfer batch of tokens between given addresses\n * @dev ERC1155Receiver implementation is not checked\n * @param operator executor of transfer\n * @param sender sender of tokens\n * @param recipient receiver of tokens\n * @param ids token IDs\n * @param amounts quantities of tokens to transfer\n * @param data data payload\n */\n function _transferBatch(\n address operator,\n address sender,\n address recipient,\n uint256[] memory ids,\n uint256[] memory amounts,\n bytes memory data\n ) internal virtual {\n require(\n recipient != address(0),\n 'ERC1155: transfer to the zero address'\n );\n require(\n ids.length == amounts.length,\n 'ERC1155: ids and amounts length mismatch'\n );\n\n _beforeTokenTransfer(operator, sender, recipient, ids, amounts, data);\n\n mapping(uint256 => mapping(address => uint256))\n storage balances = ERC1155BaseStorage.layout().balances;\n\n for (uint256 i; i < ids.length; i++) {\n uint256 token = ids[i];\n uint256 amount = amounts[i];\n\n unchecked {\n uint256 senderBalance = balances[token][sender];\n require(\n senderBalance >= amount,\n 'ERC1155: insufficient balances for transfer'\n );\n balances[token][sender] = senderBalance - amount;\n }\n\n balances[token][recipient] += amount;\n }\n\n emit TransferBatch(operator, sender, recipient, ids, amounts);\n }\n\n /**\n * @notice transfer batch of tokens between given addresses\n * @param operator executor of transfer\n * @param sender sender of tokens\n * @param recipient receiver of tokens\n * @param ids token IDs\n * @param amounts quantities of tokens to transfer\n * @param data data payload\n */\n function _safeTransferBatch(\n address operator,\n address sender,\n address recipient,\n uint256[] memory ids,\n uint256[] memory amounts,\n bytes memory data\n ) internal virtual {\n _transferBatch(operator, sender, recipient, ids, amounts, data);\n\n _doSafeBatchTransferAcceptanceCheck(\n operator,\n sender,\n recipient,\n ids,\n amounts,\n data\n );\n }\n\n /**\n * @notice wrap given element in array of length 1\n * @param element element to wrap\n * @return singleton array\n */\n function _asSingletonArray(uint256 element)\n private\n pure\n returns (uint256[] memory)\n {\n uint256[] memory array = new uint256[](1);\n array[0] = element;\n return array;\n }\n\n /**\n * @notice revert if applicable transfer recipient is not valid ERC1155Receiver\n * @param operator executor of transfer\n * @param from sender of tokens\n * @param to receiver of tokens\n * @param id token ID\n * @param amount quantity of tokens to transfer\n * @param data data payload\n */\n function _doSafeTransferAcceptanceCheck(\n address operator,\n address from,\n address to,\n uint256 id,\n uint256 amount,\n bytes memory data\n ) private {\n if (to.isContract()) {\n try\n IERC1155Receiver(to).onERC1155Received(\n operator,\n from,\n id,\n amount,\n data\n )\n returns (bytes4 response) {\n require(\n response == IERC1155Receiver.onERC1155Received.selector,\n 'ERC1155: ERC1155Receiver rejected tokens'\n );\n } catch Error(string memory reason) {\n revert(reason);\n } catch {\n revert('ERC1155: transfer to non ERC1155Receiver implementer');\n }\n }\n }\n\n /**\n * @notice revert if applicable transfer recipient is not valid ERC1155Receiver\n * @param operator executor of transfer\n * @param from sender of tokens\n * @param to receiver of tokens\n * @param ids token IDs\n * @param amounts quantities of tokens to transfer\n * @param data data payload\n */\n function _doSafeBatchTransferAcceptanceCheck(\n address operator,\n address from,\n address to,\n uint256[] memory ids,\n uint256[] memory amounts,\n bytes memory data\n ) private {\n if (to.isContract()) {\n try\n IERC1155Receiver(to).onERC1155BatchReceived(\n operator,\n from,\n ids,\n amounts,\n data\n )\n returns (bytes4 response) {\n require(\n response ==\n IERC1155Receiver.onERC1155BatchReceived.selector,\n 'ERC1155: ERC1155Receiver rejected tokens'\n );\n } catch Error(string memory reason) {\n revert(reason);\n } catch {\n revert('ERC1155: transfer to non ERC1155Receiver implementer');\n }\n }\n }\n\n /**\n * @notice ERC1155 hook, called before all transfers including mint and burn\n * @dev function should be overridden and new implementation must call super\n * @dev called for both single and batch transfers\n * @param operator executor of transfer\n * @param from sender of tokens\n * @param to receiver of tokens\n * @param ids token IDs\n * @param amounts quantities of tokens to transfer\n * @param data data payload\n */\n function _beforeTokenTransfer(\n address operator,\n address from,\n address to,\n uint256[] memory ids,\n uint256[] memory amounts,\n bytes memory data\n ) internal virtual {}\n}\n" - }, - "@solidstate/contracts/token/ERC20/IERC20Internal.sol": { - "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @title Partial ERC20 interface needed by internal functions\n */\ninterface IERC20Internal {\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n}\n" - }, - "@chainlink/contracts/src/v0.8/interfaces/AggregatorInterface.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AggregatorInterface {\n function latestAnswer()\n external\n view\n returns (\n int256\n );\n \n function latestTimestamp()\n external\n view\n returns (\n uint256\n );\n\n function latestRound()\n external\n view\n returns (\n uint256\n );\n\n function getAnswer(\n uint256 roundId\n )\n external\n view\n returns (\n int256\n );\n\n function getTimestamp(\n uint256 roundId\n )\n external\n view\n returns (\n uint256\n );\n\n event AnswerUpdated(\n int256 indexed current,\n uint256 indexed roundId,\n uint256 updatedAt\n );\n\n event NewRound(\n uint256 indexed roundId,\n address indexed startedBy,\n uint256 startedAt\n );\n}\n" - }, - "contracts/pool/PoolExercise.sol": { - "content": "// SPDX-License-Identifier: BUSL-1.1\r\n// For further clarification please see https://license.premia.legal\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport {ERC1155BaseStorage} from \"@solidstate/contracts/token/ERC1155/base/ERC1155BaseStorage.sol\";\r\n\r\nimport {PoolInternal} from \"./PoolInternal.sol\";\r\nimport {IPoolExercise} from \"./IPoolExercise.sol\";\r\n\r\n/**\r\n * @title Premia option pool\r\n * @dev deployed standalone and referenced by PoolProxy\r\n */\r\ncontract PoolExercise is IPoolExercise, PoolInternal {\r\n constructor(\r\n address ivolOracle,\r\n address weth,\r\n address premiaMining,\r\n address feeReceiver,\r\n address feeDiscountAddress,\r\n int128 fee64x64\r\n )\r\n PoolInternal(\r\n ivolOracle,\r\n weth,\r\n premiaMining,\r\n feeReceiver,\r\n feeDiscountAddress,\r\n fee64x64\r\n )\r\n {}\r\n\r\n /**\r\n * @inheritdoc IPoolExercise\r\n */\r\n function exerciseFrom(\r\n address holder,\r\n uint256 longTokenId,\r\n uint256 contractSize\r\n ) external override {\r\n if (msg.sender != holder) {\r\n require(\r\n ERC1155BaseStorage.layout().operatorApprovals[holder][\r\n msg.sender\r\n ],\r\n \"not approved\"\r\n );\r\n }\r\n\r\n _exercise(holder, longTokenId, contractSize);\r\n }\r\n\r\n /**\r\n * @inheritdoc IPoolExercise\r\n */\r\n function processExpired(uint256 longTokenId, uint256 contractSize)\r\n external\r\n override\r\n {\r\n _exercise(address(0), longTokenId, contractSize);\r\n }\r\n}\r\n" - }, - "contracts/pool/IPoolExercise.sol": { - "content": "// SPDX-License-Identifier: LGPL-3.0-or-later\r\n\r\npragma solidity ^0.8.0;\r\n\r\n/**\r\n * @notice Pool interface for exercising and processing of expired options\r\n */\r\ninterface IPoolExercise {\r\n /**\r\n * @notice exercise option on behalf of holder\r\n * @param holder owner of long option tokens to exercise\r\n * @param longTokenId long option token id\r\n * @param contractSize quantity of tokens to exercise\r\n */\r\n function exerciseFrom(\r\n address holder,\r\n uint256 longTokenId,\r\n uint256 contractSize\r\n ) external;\r\n\r\n /**\r\n * @notice process expired option, freeing liquidity and distributing profits\r\n * @param longTokenId long option token id\r\n * @param contractSize quantity of tokens to process\r\n */\r\n function processExpired(uint256 longTokenId, uint256 contractSize) external;\r\n}\r\n" - }, - "contracts/pool/PoolStorage.sol": { - "content": "// SPDX-License-Identifier: BUSL-1.1\r\n// For further clarification please see https://license.premia.legal\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport {AggregatorInterface} from \"@chainlink/contracts/src/v0.8/interfaces/AggregatorInterface.sol\";\r\nimport {AggregatorV3Interface} from \"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\";\r\nimport {EnumerableSet, ERC1155EnumerableStorage} from \"@solidstate/contracts/token/ERC1155/enumerable/ERC1155EnumerableStorage.sol\";\r\n\r\nimport {ABDKMath64x64} from \"abdk-libraries-solidity/ABDKMath64x64.sol\";\r\nimport {ABDKMath64x64Token} from \"../libraries/ABDKMath64x64Token.sol\";\r\nimport {OptionMath} from \"../libraries/OptionMath.sol\";\r\n\r\nlibrary PoolStorage {\r\n using ABDKMath64x64 for int128;\r\n using PoolStorage for PoolStorage.Layout;\r\n\r\n enum TokenType {\r\n UNDERLYING_FREE_LIQ,\r\n BASE_FREE_LIQ,\r\n UNDERLYING_RESERVED_LIQ,\r\n BASE_RESERVED_LIQ,\r\n LONG_CALL,\r\n SHORT_CALL,\r\n LONG_PUT,\r\n SHORT_PUT\r\n }\r\n\r\n struct PoolSettings {\r\n address underlying;\r\n address base;\r\n address underlyingOracle;\r\n address baseOracle;\r\n }\r\n\r\n struct QuoteArgsInternal {\r\n address feePayer; // address of the fee payer\r\n uint64 maturity; // timestamp of option maturity\r\n int128 strike64x64; // 64x64 fixed point representation of strike price\r\n int128 spot64x64; // 64x64 fixed point representation of spot price\r\n uint256 contractSize; // size of option contract\r\n bool isCall; // true for call, false for put\r\n }\r\n\r\n struct QuoteResultInternal {\r\n int128 baseCost64x64; // 64x64 fixed point representation of option cost denominated in underlying currency (without fee)\r\n int128 feeCost64x64; // 64x64 fixed point representation of option fee cost denominated in underlying currency for call, or base currency for put\r\n int128 cLevel64x64; // 64x64 fixed point representation of C-Level of Pool after purchase\r\n int128 slippageCoefficient64x64; // 64x64 fixed point representation of slippage coefficient for given order size\r\n }\r\n\r\n struct BatchData {\r\n uint256 eta;\r\n uint256 totalPendingDeposits;\r\n }\r\n\r\n bytes32 internal constant STORAGE_SLOT =\r\n keccak256(\"premia.contracts.storage.Pool\");\r\n\r\n uint256 private constant C_DECAY_BUFFER = 12 hours;\r\n uint256 private constant C_DECAY_INTERVAL = 4 hours;\r\n\r\n struct Layout {\r\n // ERC20 token addresses\r\n address base;\r\n address underlying;\r\n // AggregatorV3Interface oracle addresses\r\n address baseOracle;\r\n address underlyingOracle;\r\n // token metadata\r\n uint8 underlyingDecimals;\r\n uint8 baseDecimals;\r\n // minimum amounts\r\n uint256 baseMinimum;\r\n uint256 underlyingMinimum;\r\n // deposit caps\r\n uint256 basePoolCap;\r\n uint256 underlyingPoolCap;\r\n // market state\r\n int128 _deprecated_steepness64x64;\r\n int128 cLevelBase64x64;\r\n int128 cLevelUnderlying64x64;\r\n uint256 cLevelBaseUpdatedAt;\r\n uint256 cLevelUnderlyingUpdatedAt;\r\n uint256 updatedAt;\r\n // User -> isCall -> depositedAt\r\n mapping(address => mapping(bool => uint256)) depositedAt;\r\n mapping(address => mapping(bool => uint256)) divestmentTimestamps;\r\n // doubly linked list of free liquidity intervals\r\n // isCall -> User -> User\r\n mapping(bool => mapping(address => address)) liquidityQueueAscending;\r\n mapping(bool => mapping(address => address)) liquidityQueueDescending;\r\n // minimum resolution price bucket => price\r\n mapping(uint256 => int128) bucketPrices64x64;\r\n // sequence id (minimum resolution price bucket / 256) => price update sequence\r\n mapping(uint256 => uint256) priceUpdateSequences;\r\n // isCall -> batch data\r\n mapping(bool => BatchData) nextDeposits;\r\n // user -> batch timestamp -> isCall -> pending amount\r\n mapping(address => mapping(uint256 => mapping(bool => uint256))) pendingDeposits;\r\n EnumerableSet.UintSet tokenIds;\r\n // user -> isCallPool -> total value locked of user (Used for liquidity mining)\r\n mapping(address => mapping(bool => uint256)) userTVL;\r\n // isCallPool -> total value locked\r\n mapping(bool => uint256) totalTVL;\r\n // steepness values\r\n int128 steepnessBase64x64;\r\n int128 steepnessUnderlying64x64;\r\n }\r\n\r\n function layout() internal pure returns (Layout storage l) {\r\n bytes32 slot = STORAGE_SLOT;\r\n assembly {\r\n l.slot := slot\r\n }\r\n }\r\n\r\n /**\r\n * @notice calculate ERC1155 token id for given option parameters\r\n * @param tokenType TokenType enum\r\n * @param maturity timestamp of option maturity\r\n * @param strike64x64 64x64 fixed point representation of strike price\r\n * @return tokenId token id\r\n */\r\n function formatTokenId(\r\n TokenType tokenType,\r\n uint64 maturity,\r\n int128 strike64x64\r\n ) internal pure returns (uint256 tokenId) {\r\n tokenId =\r\n (uint256(tokenType) << 248) +\r\n (uint256(maturity) << 128) +\r\n uint256(int256(strike64x64));\r\n }\r\n\r\n /**\r\n * @notice derive option maturity and strike price from ERC1155 token id\r\n * @param tokenId token id\r\n * @return tokenType TokenType enum\r\n * @return maturity timestamp of option maturity\r\n * @return strike64x64 option strike price\r\n */\r\n function parseTokenId(uint256 tokenId)\r\n internal\r\n pure\r\n returns (\r\n TokenType tokenType,\r\n uint64 maturity,\r\n int128 strike64x64\r\n )\r\n {\r\n assembly {\r\n tokenType := shr(248, tokenId)\r\n maturity := shr(128, tokenId)\r\n strike64x64 := tokenId\r\n }\r\n }\r\n\r\n function getTokenDecimals(Layout storage l, bool isCall)\r\n internal\r\n view\r\n returns (uint8 decimals)\r\n {\r\n decimals = isCall ? l.underlyingDecimals : l.baseDecimals;\r\n }\r\n\r\n /**\r\n * @notice get the total supply of free liquidity tokens, minus pending deposits\r\n * @param l storage layout struct\r\n * @param isCall whether query is for call or put pool\r\n * @return 64x64 fixed point representation of total free liquidity\r\n */\r\n function totalFreeLiquiditySupply64x64(Layout storage l, bool isCall)\r\n internal\r\n view\r\n returns (int128)\r\n {\r\n uint256 tokenId = formatTokenId(\r\n isCall ? TokenType.UNDERLYING_FREE_LIQ : TokenType.BASE_FREE_LIQ,\r\n 0,\r\n 0\r\n );\r\n\r\n return\r\n ABDKMath64x64Token.fromDecimals(\r\n ERC1155EnumerableStorage.layout().totalSupply[tokenId] -\r\n l.nextDeposits[isCall].totalPendingDeposits,\r\n l.getTokenDecimals(isCall)\r\n );\r\n }\r\n\r\n function getReinvestmentStatus(\r\n Layout storage l,\r\n address account,\r\n bool isCallPool\r\n ) internal view returns (bool) {\r\n uint256 timestamp = l.divestmentTimestamps[account][isCallPool];\r\n return timestamp == 0 || timestamp > block.timestamp;\r\n }\r\n\r\n function addUnderwriter(\r\n Layout storage l,\r\n address account,\r\n bool isCallPool\r\n ) internal {\r\n require(account != address(0));\r\n\r\n mapping(address => address) storage asc = l.liquidityQueueAscending[\r\n isCallPool\r\n ];\r\n mapping(address => address) storage desc = l.liquidityQueueDescending[\r\n isCallPool\r\n ];\r\n\r\n if (_isInQueue(account, asc, desc)) return;\r\n\r\n address last = desc[address(0)];\r\n\r\n asc[last] = account;\r\n desc[account] = last;\r\n desc[address(0)] = account;\r\n }\r\n\r\n function removeUnderwriter(\r\n Layout storage l,\r\n address account,\r\n bool isCallPool\r\n ) internal {\r\n require(account != address(0));\r\n\r\n mapping(address => address) storage asc = l.liquidityQueueAscending[\r\n isCallPool\r\n ];\r\n mapping(address => address) storage desc = l.liquidityQueueDescending[\r\n isCallPool\r\n ];\r\n\r\n if (!_isInQueue(account, asc, desc)) return;\r\n\r\n address prev = desc[account];\r\n address next = asc[account];\r\n asc[prev] = next;\r\n desc[next] = prev;\r\n delete asc[account];\r\n delete desc[account];\r\n }\r\n\r\n function isInQueue(\r\n Layout storage l,\r\n address account,\r\n bool isCallPool\r\n ) internal view returns (bool) {\r\n mapping(address => address) storage asc = l.liquidityQueueAscending[\r\n isCallPool\r\n ];\r\n mapping(address => address) storage desc = l.liquidityQueueDescending[\r\n isCallPool\r\n ];\r\n\r\n return _isInQueue(account, asc, desc);\r\n }\r\n\r\n function _isInQueue(\r\n address account,\r\n mapping(address => address) storage asc,\r\n mapping(address => address) storage desc\r\n ) private view returns (bool) {\r\n return asc[account] != address(0) || desc[address(0)] == account;\r\n }\r\n\r\n /**\r\n * @notice get current C-Level, without accounting for pending adjustments\r\n * @param l storage layout struct\r\n * @param isCall whether query is for call or put pool\r\n * @return cLevel64x64 64x64 fixed point representation of C-Level\r\n */\r\n function getRawCLevel64x64(Layout storage l, bool isCall)\r\n internal\r\n view\r\n returns (int128 cLevel64x64)\r\n {\r\n cLevel64x64 = isCall ? l.cLevelUnderlying64x64 : l.cLevelBase64x64;\r\n }\r\n\r\n /**\r\n * @notice get current C-Level, accounting for unrealized decay\r\n * @param l storage layout struct\r\n * @param isCall whether query is for call or put pool\r\n * @return cLevel64x64 64x64 fixed point representation of C-Level\r\n */\r\n function getDecayAdjustedCLevel64x64(Layout storage l, bool isCall)\r\n internal\r\n view\r\n returns (int128 cLevel64x64)\r\n {\r\n // get raw C-Level from storage\r\n cLevel64x64 = l.getRawCLevel64x64(isCall);\r\n\r\n // account for C-Level decay\r\n cLevel64x64 = l.applyCLevelDecayAdjustment(cLevel64x64, isCall);\r\n }\r\n\r\n /**\r\n * @notice calculate updated C-Level, accounting for unrealized decay\r\n * @param l storage layout struct\r\n * @param oldCLevel64x64 64x64 fixed point representation pool C-Level before accounting for decay\r\n * @param isCall whether query is for call or put pool\r\n * @return cLevel64x64 64x64 fixed point representation of C-Level of Pool after accounting for decay\r\n */\r\n function applyCLevelDecayAdjustment(\r\n Layout storage l,\r\n int128 oldCLevel64x64,\r\n bool isCall\r\n ) internal view returns (int128 cLevel64x64) {\r\n uint256 timeElapsed = block.timestamp -\r\n (isCall ? l.cLevelUnderlyingUpdatedAt : l.cLevelBaseUpdatedAt);\r\n\r\n // do not apply C decay if less than 24 hours have elapsed\r\n\r\n if (timeElapsed > C_DECAY_BUFFER) {\r\n timeElapsed -= C_DECAY_BUFFER;\r\n } else {\r\n return oldCLevel64x64;\r\n }\r\n\r\n int128 timeIntervalsElapsed64x64 = ABDKMath64x64.divu(\r\n timeElapsed,\r\n C_DECAY_INTERVAL\r\n );\r\n\r\n uint256 tokenId = formatTokenId(\r\n isCall ? TokenType.UNDERLYING_FREE_LIQ : TokenType.BASE_FREE_LIQ,\r\n 0,\r\n 0\r\n );\r\n\r\n uint256 tvl = l.totalTVL[isCall];\r\n\r\n int128 utilization = ABDKMath64x64.divu(\r\n tvl -\r\n (ERC1155EnumerableStorage.layout().totalSupply[tokenId] -\r\n l.nextDeposits[isCall].totalPendingDeposits),\r\n tvl\r\n );\r\n\r\n return\r\n OptionMath.calculateCLevelDecay(\r\n OptionMath.CalculateCLevelDecayArgs(\r\n timeIntervalsElapsed64x64,\r\n oldCLevel64x64,\r\n utilization,\r\n 0xb333333333333333, // 0.7\r\n 0xe666666666666666, // 0.9\r\n 0x10000000000000000, // 1.0\r\n 0x10000000000000000, // 1.0\r\n 0xe666666666666666, // 0.9\r\n 0x56fc2a2c515da32ea // 2e\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * @notice calculate updated C-Level, accounting for pending deposits\r\n * @param l storage layout struct\r\n * @param oldCLevel64x64 64x64 fixed point representation pool C-Level before accounting for liquidity change\r\n * @param oldLiquidity64x64 64x64 fixed point representation of previous liquidity\r\n * @param isCall whether to update C-Level for call or put pool\r\n * @return cLevel64x64 64x64 fixed point representation of C-Level\r\n * @return liquidity64x64 64x64 fixed point representation of new liquidity amount\r\n */\r\n function applyCLevelPendingDepositAdjustment(\r\n Layout storage l,\r\n int128 oldCLevel64x64,\r\n int128 oldLiquidity64x64,\r\n bool isCall\r\n ) internal view returns (int128 cLevel64x64, int128 liquidity64x64) {\r\n PoolStorage.BatchData storage batchData = l.nextDeposits[isCall];\r\n int128 pendingDeposits64x64;\r\n\r\n if (\r\n batchData.totalPendingDeposits > 0 &&\r\n batchData.eta != 0 &&\r\n block.timestamp >= batchData.eta\r\n ) {\r\n pendingDeposits64x64 = ABDKMath64x64Token.fromDecimals(\r\n batchData.totalPendingDeposits,\r\n l.getTokenDecimals(isCall)\r\n );\r\n\r\n liquidity64x64 = oldLiquidity64x64.add(pendingDeposits64x64);\r\n\r\n cLevel64x64 = l.applyCLevelLiquidityChangeAdjustment(\r\n oldCLevel64x64,\r\n oldLiquidity64x64,\r\n liquidity64x64,\r\n isCall\r\n );\r\n } else {\r\n cLevel64x64 = oldCLevel64x64;\r\n liquidity64x64 = oldLiquidity64x64;\r\n }\r\n }\r\n\r\n /**\r\n * @notice calculate updated C-Level, accounting for change in liquidity\r\n * @param l storage layout struct\r\n * @param oldCLevel64x64 64x64 fixed point representation pool C-Level before accounting for liquidity change\r\n * @param oldLiquidity64x64 64x64 fixed point representation of previous liquidity\r\n * @param newLiquidity64x64 64x64 fixed point representation of current liquidity\r\n * @param isCallPool whether to update C-Level for call or put pool\r\n * @return cLevel64x64 64x64 fixed point representation of C-Level\r\n */\r\n function applyCLevelLiquidityChangeAdjustment(\r\n Layout storage l,\r\n int128 oldCLevel64x64,\r\n int128 oldLiquidity64x64,\r\n int128 newLiquidity64x64,\r\n bool isCallPool\r\n ) internal view returns (int128 cLevel64x64) {\r\n int128 steepness64x64 = isCallPool\r\n ? l.steepnessUnderlying64x64\r\n : l.steepnessBase64x64;\r\n\r\n // fallback to deprecated storage value if side-specific value is not set\r\n if (steepness64x64 == 0) steepness64x64 = l._deprecated_steepness64x64;\r\n\r\n cLevel64x64 = OptionMath.calculateCLevel(\r\n oldCLevel64x64,\r\n oldLiquidity64x64,\r\n newLiquidity64x64,\r\n steepness64x64\r\n );\r\n\r\n if (cLevel64x64 < 0xb333333333333333) {\r\n cLevel64x64 = int128(0xb333333333333333); // 64x64 fixed point representation of 0.7\r\n }\r\n }\r\n\r\n /**\r\n * @notice set C-Level to arbitrary pre-calculated value\r\n * @param cLevel64x64 new C-Level of pool\r\n * @param isCallPool whether to update C-Level for call or put pool\r\n */\r\n function setCLevel(\r\n Layout storage l,\r\n int128 cLevel64x64,\r\n bool isCallPool\r\n ) internal {\r\n if (isCallPool) {\r\n l.cLevelUnderlying64x64 = cLevel64x64;\r\n l.cLevelUnderlyingUpdatedAt = block.timestamp;\r\n } else {\r\n l.cLevelBase64x64 = cLevel64x64;\r\n l.cLevelBaseUpdatedAt = block.timestamp;\r\n }\r\n }\r\n\r\n function setOracles(\r\n Layout storage l,\r\n address baseOracle,\r\n address underlyingOracle\r\n ) internal {\r\n require(\r\n AggregatorV3Interface(baseOracle).decimals() ==\r\n AggregatorV3Interface(underlyingOracle).decimals(),\r\n \"Pool: oracle decimals must match\"\r\n );\r\n\r\n l.baseOracle = baseOracle;\r\n l.underlyingOracle = underlyingOracle;\r\n }\r\n\r\n function fetchPriceUpdate(Layout storage l)\r\n internal\r\n view\r\n returns (int128 price64x64)\r\n {\r\n int256 priceUnderlying = AggregatorInterface(l.underlyingOracle)\r\n .latestAnswer();\r\n int256 priceBase = AggregatorInterface(l.baseOracle).latestAnswer();\r\n\r\n return ABDKMath64x64.divi(priceUnderlying, priceBase);\r\n }\r\n\r\n /**\r\n * @notice set price update for hourly bucket corresponding to given timestamp\r\n * @param l storage layout struct\r\n * @param timestamp timestamp to update\r\n * @param price64x64 64x64 fixed point representation of price\r\n */\r\n function setPriceUpdate(\r\n Layout storage l,\r\n uint256 timestamp,\r\n int128 price64x64\r\n ) internal {\r\n uint256 bucket = timestamp / (1 hours);\r\n l.bucketPrices64x64[bucket] = price64x64;\r\n l.priceUpdateSequences[bucket >> 8] += 1 << (255 - (bucket & 255));\r\n }\r\n\r\n /**\r\n * @notice get price update for hourly bucket corresponding to given timestamp\r\n * @param l storage layout struct\r\n * @param timestamp timestamp to query\r\n * @return 64x64 fixed point representation of price\r\n */\r\n function getPriceUpdate(Layout storage l, uint256 timestamp)\r\n internal\r\n view\r\n returns (int128)\r\n {\r\n return l.bucketPrices64x64[timestamp / (1 hours)];\r\n }\r\n\r\n /**\r\n * @notice get first price update available following given timestamp\r\n * @param l storage layout struct\r\n * @param timestamp timestamp to query\r\n * @return 64x64 fixed point representation of price\r\n */\r\n function getPriceUpdateAfter(Layout storage l, uint256 timestamp)\r\n internal\r\n view\r\n returns (int128)\r\n {\r\n // price updates are grouped into hourly buckets\r\n uint256 bucket = timestamp / (1 hours);\r\n // divide by 256 to get the index of the relevant price update sequence\r\n uint256 sequenceId = bucket >> 8;\r\n\r\n // get position within sequence relevant to current price update\r\n\r\n uint256 offset = bucket & 255;\r\n // shift to skip buckets from earlier in sequence\r\n uint256 sequence = (l.priceUpdateSequences[sequenceId] << offset) >>\r\n offset;\r\n\r\n // iterate through future sequences until a price update is found\r\n // sequence corresponding to current timestamp used as upper bound\r\n\r\n uint256 currentPriceUpdateSequenceId = block.timestamp / (256 hours);\r\n\r\n while (sequence == 0 && sequenceId <= currentPriceUpdateSequenceId) {\r\n sequence = l.priceUpdateSequences[++sequenceId];\r\n }\r\n\r\n // if no price update is found (sequence == 0) function will return 0\r\n // this should never occur, as each relevant external function triggers a price update\r\n\r\n // the most significant bit of the sequence corresponds to the offset of the relevant bucket\r\n\r\n uint256 msb;\r\n\r\n for (uint256 i = 128; i > 0; i >>= 1) {\r\n if (sequence >> i > 0) {\r\n msb += i;\r\n sequence >>= i;\r\n }\r\n }\r\n\r\n return l.bucketPrices64x64[((sequenceId + 1) << 8) - msb - 1];\r\n }\r\n\r\n function fromBaseToUnderlyingDecimals(Layout storage l, uint256 value)\r\n internal\r\n view\r\n returns (uint256)\r\n {\r\n int128 valueFixed64x64 = ABDKMath64x64Token.fromDecimals(\r\n value,\r\n l.baseDecimals\r\n );\r\n return\r\n ABDKMath64x64Token.toDecimals(\r\n valueFixed64x64,\r\n l.underlyingDecimals\r\n );\r\n }\r\n\r\n function fromUnderlyingToBaseDecimals(Layout storage l, uint256 value)\r\n internal\r\n view\r\n returns (uint256)\r\n {\r\n int128 valueFixed64x64 = ABDKMath64x64Token.fromDecimals(\r\n value,\r\n l.underlyingDecimals\r\n );\r\n return ABDKMath64x64Token.toDecimals(valueFixed64x64, l.baseDecimals);\r\n }\r\n}\r\n" - }, - "@solidstate/contracts/utils/AddressUtils.sol": { - "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nlibrary AddressUtils {\n function toString(address account) internal pure returns (string memory) {\n bytes32 value = bytes32(uint256(uint160(account)));\n bytes memory alphabet = '0123456789abcdef';\n bytes memory chars = new bytes(42);\n\n chars[0] = '0';\n chars[1] = 'x';\n\n for (uint256 i = 0; i < 20; i++) {\n chars[2 + i * 2] = alphabet[uint8(value[i + 12] >> 4)];\n chars[3 + i * 2] = alphabet[uint8(value[i + 12] & 0x0f)];\n }\n\n return string(chars);\n }\n\n function isContract(address account) internal view returns (bool) {\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n function sendValue(address payable account, uint256 amount) internal {\n (bool success, ) = account.call{ value: amount }('');\n require(success, 'AddressUtils: failed to send value');\n }\n\n function functionCall(address target, bytes memory data)\n internal\n returns (bytes memory)\n {\n return\n functionCall(target, data, 'AddressUtils: failed low-level call');\n }\n\n function functionCall(\n address target,\n bytes memory data,\n string memory error\n ) internal returns (bytes memory) {\n return _functionCallWithValue(target, data, 0, error);\n }\n\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return\n functionCallWithValue(\n target,\n data,\n value,\n 'AddressUtils: failed low-level call with value'\n );\n }\n\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory error\n ) internal returns (bytes memory) {\n require(\n address(this).balance >= value,\n 'AddressUtils: insufficient balance for call'\n );\n return _functionCallWithValue(target, data, value, error);\n }\n\n function _functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory error\n ) private returns (bytes memory) {\n require(\n isContract(target),\n 'AddressUtils: function call to non-contract'\n );\n\n (bool success, bytes memory returnData) = target.call{ value: value }(\n data\n );\n\n if (success) {\n return returnData;\n } else if (returnData.length > 0) {\n assembly {\n let returnData_size := mload(returnData)\n revert(add(32, returnData), returnData_size)\n }\n } else {\n revert(error);\n }\n }\n}\n" - }, - "@solidstate/contracts/token/ERC1155/enumerable/ERC1155Enumerable.sol": { - "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { EnumerableSet } from '../../../utils/EnumerableSet.sol';\nimport { ERC1155Base, ERC1155BaseInternal } from '../base/ERC1155Base.sol';\nimport { IERC1155Enumerable } from './IERC1155Enumerable.sol';\nimport { ERC1155EnumerableInternal, ERC1155EnumerableStorage } from './ERC1155EnumerableInternal.sol';\n\n/**\n * @title ERC1155 implementation including enumerable and aggregate functions\n */\nabstract contract ERC1155Enumerable is\n IERC1155Enumerable,\n ERC1155Base,\n ERC1155EnumerableInternal\n{\n using EnumerableSet for EnumerableSet.AddressSet;\n using EnumerableSet for EnumerableSet.UintSet;\n\n /**\n * @inheritdoc IERC1155Enumerable\n */\n function totalSupply(uint256 id)\n public\n view\n virtual\n override\n returns (uint256)\n {\n return ERC1155EnumerableStorage.layout().totalSupply[id];\n }\n\n /**\n * @inheritdoc IERC1155Enumerable\n */\n function totalHolders(uint256 id)\n public\n view\n virtual\n override\n returns (uint256)\n {\n return ERC1155EnumerableStorage.layout().accountsByToken[id].length();\n }\n\n /**\n * @inheritdoc IERC1155Enumerable\n */\n function accountsByToken(uint256 id)\n public\n view\n virtual\n override\n returns (address[] memory)\n {\n EnumerableSet.AddressSet storage accounts = ERC1155EnumerableStorage\n .layout()\n .accountsByToken[id];\n\n address[] memory addresses = new address[](accounts.length());\n\n for (uint256 i; i < accounts.length(); i++) {\n addresses[i] = accounts.at(i);\n }\n\n return addresses;\n }\n\n /**\n * @inheritdoc IERC1155Enumerable\n */\n function tokensByAccount(address account)\n public\n view\n virtual\n override\n returns (uint256[] memory)\n {\n EnumerableSet.UintSet storage tokens = ERC1155EnumerableStorage\n .layout()\n .tokensByAccount[account];\n\n uint256[] memory ids = new uint256[](tokens.length());\n\n for (uint256 i; i < tokens.length(); i++) {\n ids[i] = tokens.at(i);\n }\n\n return ids;\n }\n\n /**\n * @notice ERC1155 hook: update aggregate values\n * @inheritdoc ERC1155EnumerableInternal\n */\n function _beforeTokenTransfer(\n address operator,\n address from,\n address to,\n uint256[] memory ids,\n uint256[] memory amounts,\n bytes memory data\n )\n internal\n virtual\n override(ERC1155BaseInternal, ERC1155EnumerableInternal)\n {\n super._beforeTokenTransfer(operator, from, to, ids, amounts, data);\n }\n}\n" - }, - "@solidstate/contracts/token/ERC1155/IERC1155Receiver.sol": { - "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC165 } from '../../introspection/IERC165.sol';\n\n/**\n * @title ERC1155 transfer receiver interface\n */\ninterface IERC1155Receiver is IERC165 {\n /**\n * @notice validate receipt of ERC1155 transfer\n * @param operator executor of transfer\n * @param from sender of tokens\n * @param id token ID received\n * @param value quantity of tokens received\n * @param data data payload\n * @return function's own selector if transfer is accepted\n */\n function onERC1155Received(\n address operator,\n address from,\n uint256 id,\n uint256 value,\n bytes calldata data\n ) external returns (bytes4);\n\n /**\n * @notice validate receipt of ERC1155 batch transfer\n * @param operator executor of transfer\n * @param from sender of tokens\n * @param ids token IDs received\n * @param values quantities of tokens received\n * @param data data payload\n * @return function's own selector if transfer is accepted\n */\n function onERC1155BatchReceived(\n address operator,\n address from,\n uint256[] calldata ids,\n uint256[] calldata values,\n bytes calldata data\n ) external returns (bytes4);\n}\n" - }, - "contracts/pool/IPoolEvents.sol": { - "content": "// SPDX-License-Identifier: LGPL-3.0-or-later\r\n\r\npragma solidity ^0.8.0;\r\n\r\ninterface IPoolEvents {\r\n event Purchase(\r\n address indexed user,\r\n uint256 longTokenId,\r\n uint256 contractSize,\r\n uint256 baseCost,\r\n uint256 feeCost,\r\n int128 spot64x64\r\n );\r\n\r\n event Exercise(\r\n address indexed user,\r\n uint256 longTokenId,\r\n uint256 contractSize,\r\n uint256 exerciseValue,\r\n uint256 fee\r\n );\r\n\r\n event Underwrite(\r\n address indexed underwriter,\r\n address indexed longReceiver,\r\n uint256 shortTokenId,\r\n uint256 intervalContractSize,\r\n uint256 intervalPremium,\r\n bool isManualUnderwrite\r\n );\r\n\r\n event AssignExercise(\r\n address indexed underwriter,\r\n uint256 shortTokenId,\r\n uint256 freedAmount,\r\n uint256 intervalContractSize,\r\n uint256 fee\r\n );\r\n\r\n event Deposit(address indexed user, bool isCallPool, uint256 amount);\r\n\r\n event Withdrawal(\r\n address indexed user,\r\n bool isCallPool,\r\n uint256 depositedAt,\r\n uint256 amount\r\n );\r\n\r\n event FeeWithdrawal(bool indexed isCallPool, uint256 amount);\r\n\r\n event Annihilate(uint256 shortTokenId, uint256 amount);\r\n\r\n event UpdateCLevel(\r\n bool indexed isCall,\r\n int128 cLevel64x64,\r\n int128 oldLiquidity64x64,\r\n int128 newLiquidity64x64\r\n );\r\n\r\n event UpdateSteepness(int128 steepness64x64, bool isCallPool);\r\n}\r\n" - }, - "contracts/oracle/VolatilitySurfaceOracleStorage.sol": { - "content": "// SPDX-License-Identifier: BUSL-1.1\r\n// For further clarification please see https://license.premia.legal\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport {EnumerableSet} from \"@solidstate/contracts/utils/EnumerableSet.sol\";\r\n\r\nlibrary VolatilitySurfaceOracleStorage {\r\n bytes32 internal constant STORAGE_SLOT =\r\n keccak256(\"premia.contracts.storage.VolatilitySurfaceOracle\");\r\n\r\n uint256 internal constant COEFF_BITS = 51;\r\n uint256 internal constant COEFF_BITS_MINUS_ONE = 50;\r\n uint256 internal constant COEFF_AMOUNT = 5;\r\n // START_BIT = COEFF_BITS * (COEFF_AMOUNT - 1)\r\n uint256 internal constant START_BIT = 204;\r\n\r\n struct Update {\r\n uint256 updatedAt;\r\n bytes32 callCoefficients;\r\n bytes32 putCoefficients;\r\n }\r\n\r\n struct Layout {\r\n // Base token -> Underlying token -> Update\r\n mapping(address => mapping(address => Update)) volatilitySurfaces;\r\n // Relayer addresses which can be trusted to provide accurate option trades\r\n EnumerableSet.AddressSet whitelistedRelayers;\r\n }\r\n\r\n function layout() internal pure returns (Layout storage l) {\r\n bytes32 slot = STORAGE_SLOT;\r\n assembly {\r\n l.slot := slot\r\n }\r\n }\r\n\r\n function getCoefficients(\r\n Layout storage l,\r\n address baseToken,\r\n address underlyingToken,\r\n bool isCall\r\n ) internal view returns (bytes32) {\r\n Update storage u = l.volatilitySurfaces[baseToken][underlyingToken];\r\n return isCall ? u.callCoefficients : u.putCoefficients;\r\n }\r\n\r\n function parseVolatilitySurfaceCoefficients(bytes32 input)\r\n internal\r\n pure\r\n returns (int256[] memory coefficients)\r\n {\r\n coefficients = new int256[](COEFF_AMOUNT);\r\n\r\n // Value to add to negative numbers to cast them to int256\r\n int256 toAdd = (int256(-1) >> COEFF_BITS) << COEFF_BITS;\r\n\r\n assembly {\r\n let i := 0\r\n // Value equal to -1\r\n let mid := shl(COEFF_BITS_MINUS_ONE, 1)\r\n\r\n for {\r\n\r\n } lt(i, COEFF_AMOUNT) {\r\n\r\n } {\r\n let offset := sub(START_BIT, mul(COEFF_BITS, i))\r\n let coeff := shr(\r\n offset,\r\n sub(\r\n input,\r\n shl(\r\n add(offset, COEFF_BITS),\r\n shr(add(offset, COEFF_BITS), input)\r\n )\r\n )\r\n )\r\n\r\n // Check if value is a negative number and needs casting\r\n if or(eq(coeff, mid), gt(coeff, mid)) {\r\n coeff := add(coeff, toAdd)\r\n }\r\n\r\n // Store result in the coefficients array\r\n mstore(add(coefficients, add(0x20, mul(0x20, i))), coeff)\r\n\r\n i := add(i, 1)\r\n }\r\n }\r\n }\r\n\r\n function formatVolatilitySurfaceCoefficients(int256[5] memory coefficients)\r\n internal\r\n pure\r\n returns (bytes32 result)\r\n {\r\n for (uint256 i = 0; i < COEFF_AMOUNT; i++) {\r\n int256 max = int256(1 << COEFF_BITS_MINUS_ONE);\r\n require(\r\n coefficients[i] < max && coefficients[i] > -max,\r\n \"Out of bounds\"\r\n );\r\n }\r\n\r\n assembly {\r\n let i := 0\r\n\r\n for {\r\n\r\n } lt(i, COEFF_AMOUNT) {\r\n\r\n } {\r\n let offset := sub(START_BIT, mul(COEFF_BITS, i))\r\n let coeff := mload(add(coefficients, mul(0x20, i)))\r\n\r\n result := add(\r\n result,\r\n shl(\r\n offset,\r\n sub(coeff, shl(COEFF_BITS, shr(COEFF_BITS, coeff)))\r\n )\r\n )\r\n\r\n i := add(i, 1)\r\n }\r\n }\r\n }\r\n}\r\n" - }, - "@solidstate/contracts/token/ERC1155/enumerable/IERC1155Enumerable.sol": { - "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @title ERC1155 enumerable and aggregate function interface\n */\ninterface IERC1155Enumerable {\n /**\n * @notice query total minted supply of given token\n * @param id token id to query\n * @return token supply\n */\n function totalSupply(uint256 id) external view returns (uint256);\n\n /**\n * @notice query total number of holders for given token\n * @param id token id to query\n * @return quantity of holders\n */\n function totalHolders(uint256 id) external view returns (uint256);\n\n /**\n * @notice query holders of given token\n * @param id token id to query\n * @return list of holder addresses\n */\n function accountsByToken(uint256 id)\n external\n view\n returns (address[] memory);\n\n /**\n * @notice query tokens held by given address\n * @param account address to query\n * @return list of token ids\n */\n function tokensByAccount(address account)\n external\n view\n returns (uint256[] memory);\n}\n" - }, - "@solidstate/contracts/token/ERC20/IERC20.sol": { - "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC20Internal } from './IERC20Internal.sol';\n\n/**\n * @title ERC20 interface\n * @dev see https://github.com/ethereum/EIPs/issues/20\n */\ninterface IERC20 is IERC20Internal {\n /**\n * @notice query the total minted token supply\n * @return token supply\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @notice query the token balance of given account\n * @param account address to query\n * @return token balance\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @notice query the allowance granted from given holder to given spender\n * @param holder approver of allowance\n * @param spender recipient of allowance\n * @return token allowance\n */\n function allowance(address holder, address spender)\n external\n view\n returns (uint256);\n\n /**\n * @notice grant approval to spender to spend tokens\n * @dev prefer ERC20Extended functions to avoid transaction-ordering vulnerability (see https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729)\n * @param spender recipient of allowance\n * @param amount quantity of tokens approved for spending\n * @return success status (always true; otherwise function should revert)\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @notice transfer tokens to given recipient\n * @param recipient beneficiary of token transfer\n * @param amount quantity of tokens to transfer\n * @return success status (always true; otherwise function should revert)\n */\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n /**\n * @notice transfer tokens to given recipient on behalf of given holder\n * @param holder holder of tokens prior to transfer\n * @param recipient beneficiary of token transfer\n * @param amount quantity of tokens to transfer\n * @return success status (always true; otherwise function should revert)\n */\n function transferFrom(\n address holder,\n address recipient,\n uint256 amount\n ) external returns (bool);\n}\n" - }, - "contracts/mining/IPremiaMining.sol": { - "content": "// SPDX-License-Identifier: LGPL-3.0-or-later\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport {PremiaMiningStorage} from \"./PremiaMiningStorage.sol\";\r\n\r\ninterface IPremiaMining {\r\n function addPremiaRewards(uint256 _amount) external;\r\n\r\n function premiaRewardsAvailable() external view returns (uint256);\r\n\r\n function getTotalAllocationPoints() external view returns (uint256);\r\n\r\n function getPoolInfo(address pool, bool isCallPool)\r\n external\r\n view\r\n returns (PremiaMiningStorage.PoolInfo memory);\r\n\r\n function getPremiaPerYear() external view returns (uint256);\r\n\r\n function addPool(address _pool, uint256 _allocPoints) external;\r\n\r\n function setPoolAllocPoints(\r\n address[] memory _pools,\r\n uint256[] memory _allocPoints\r\n ) external;\r\n\r\n function pendingPremia(\r\n address _pool,\r\n bool _isCallPool,\r\n address _user\r\n ) external view returns (uint256);\r\n\r\n function updatePool(\r\n address _pool,\r\n bool _isCallPool,\r\n uint256 _totalTVL\r\n ) external;\r\n\r\n function allocatePending(\r\n address _user,\r\n address _pool,\r\n bool _isCallPool,\r\n uint256 _userTVLOld,\r\n uint256 _userTVLNew,\r\n uint256 _totalTVL\r\n ) external;\r\n\r\n function claim(\r\n address _user,\r\n address _pool,\r\n bool _isCallPool,\r\n uint256 _userTVLOld,\r\n uint256 _userTVLNew,\r\n uint256 _totalTVL\r\n ) external;\r\n}\r\n" - }, - "@solidstate/contracts/token/ERC1155/enumerable/ERC1155EnumerableStorage.sol": { - "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { EnumerableSet } from '../../../utils/EnumerableSet.sol';\n\nlibrary ERC1155EnumerableStorage {\n struct Layout {\n mapping(uint256 => uint256) totalSupply;\n mapping(uint256 => EnumerableSet.AddressSet) accountsByToken;\n mapping(address => EnumerableSet.UintSet) tokensByAccount;\n }\n\n bytes32 internal constant STORAGE_SLOT =\n keccak256('solidstate.contracts.storage.ERC1155Enumerable');\n\n function layout() internal pure returns (Layout storage l) {\n bytes32 slot = STORAGE_SLOT;\n assembly {\n l.slot := slot\n }\n }\n}\n" - }, - "@solidstate/contracts/token/ERC1155/enumerable/ERC1155EnumerableInternal.sol": { - "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { EnumerableSet } from '../../../utils/EnumerableSet.sol';\nimport { ERC1155BaseInternal, ERC1155BaseStorage } from '../base/ERC1155BaseInternal.sol';\nimport { ERC1155EnumerableStorage } from './ERC1155EnumerableStorage.sol';\n\n/**\n * @title ERC1155Enumerable internal functions\n */\nabstract contract ERC1155EnumerableInternal is ERC1155BaseInternal {\n using EnumerableSet for EnumerableSet.AddressSet;\n using EnumerableSet for EnumerableSet.UintSet;\n\n /**\n * @notice ERC1155 hook: update aggregate values\n * @inheritdoc ERC1155BaseInternal\n */\n function _beforeTokenTransfer(\n address operator,\n address from,\n address to,\n uint256[] memory ids,\n uint256[] memory amounts,\n bytes memory data\n ) internal virtual override {\n super._beforeTokenTransfer(operator, from, to, ids, amounts, data);\n\n if (from != to) {\n ERC1155EnumerableStorage.Layout storage l = ERC1155EnumerableStorage\n .layout();\n mapping(uint256 => EnumerableSet.AddressSet)\n storage tokenAccounts = l.accountsByToken;\n EnumerableSet.UintSet storage fromTokens = l.tokensByAccount[from];\n EnumerableSet.UintSet storage toTokens = l.tokensByAccount[to];\n\n for (uint256 i; i < ids.length; i++) {\n uint256 amount = amounts[i];\n\n if (amount > 0) {\n uint256 id = ids[i];\n\n if (from == address(0)) {\n l.totalSupply[id] += amount;\n } else if (_balanceOf(from, id) == amount) {\n tokenAccounts[id].remove(from);\n fromTokens.remove(id);\n }\n\n if (to == address(0)) {\n l.totalSupply[id] -= amount;\n } else if (_balanceOf(to, id) == 0) {\n tokenAccounts[id].add(to);\n toTokens.add(id);\n }\n }\n }\n }\n }\n}\n" - }, - "contracts/oracle/IVolatilitySurfaceOracle.sol": { - "content": "// SPDX-License-Identifier: LGPL-3.0-or-later\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport {VolatilitySurfaceOracleStorage} from \"./VolatilitySurfaceOracleStorage.sol\";\r\n\r\ninterface IVolatilitySurfaceOracle {\r\n function getWhitelistedRelayers() external view returns (address[] memory);\r\n\r\n function getVolatilitySurface(address baseToken, address underlyingToken)\r\n external\r\n view\r\n returns (VolatilitySurfaceOracleStorage.Update memory);\r\n\r\n function getVolatilitySurfaceCoefficientsUnpacked(\r\n address baseToken,\r\n address underlyingToken,\r\n bool isCall\r\n ) external view returns (int256[] memory);\r\n\r\n function getTimeToMaturity64x64(uint64 maturity)\r\n external\r\n view\r\n returns (int128);\r\n\r\n function getAnnualizedVolatility64x64(\r\n address baseToken,\r\n address underlyingToken,\r\n int128 spot64x64,\r\n int128 strike64x64,\r\n int128 timeToMaturity64x64,\r\n bool isCall\r\n ) external view returns (int128);\r\n\r\n function getBlackScholesPrice64x64(\r\n address baseToken,\r\n address underlyingToken,\r\n int128 strike64x64,\r\n int128 spot64x64,\r\n int128 timeToMaturity64x64,\r\n bool isCall\r\n ) external view returns (int128);\r\n\r\n function getBlackScholesPrice(\r\n address baseToken,\r\n address underlyingToken,\r\n int128 strike64x64,\r\n int128 spot64x64,\r\n int128 timeToMaturity64x64,\r\n bool isCall\r\n ) external view returns (uint256);\r\n}\r\n" - }, - "contracts/pool/PoolInternal.sol": { - "content": "// SPDX-License-Identifier: BUSL-1.1\r\n// For further clarification please see https://license.premia.legal\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport {IERC173} from \"@solidstate/contracts/access/IERC173.sol\";\r\nimport {OwnableStorage} from \"@solidstate/contracts/access/OwnableStorage.sol\";\r\nimport {IERC20} from \"@solidstate/contracts/token/ERC20/IERC20.sol\";\r\nimport {ERC1155EnumerableInternal, ERC1155EnumerableStorage, EnumerableSet} from \"@solidstate/contracts/token/ERC1155/enumerable/ERC1155Enumerable.sol\";\r\nimport {IWETH} from \"@solidstate/contracts/utils/IWETH.sol\";\r\n\r\nimport {PoolStorage} from \"./PoolStorage.sol\";\r\n\r\nimport {ABDKMath64x64} from \"abdk-libraries-solidity/ABDKMath64x64.sol\";\r\nimport {ABDKMath64x64Token} from \"../libraries/ABDKMath64x64Token.sol\";\r\nimport {OptionMath} from \"../libraries/OptionMath.sol\";\r\nimport {IFeeDiscount} from \"../staking/IFeeDiscount.sol\";\r\nimport {IPoolEvents} from \"./IPoolEvents.sol\";\r\nimport {IPremiaMining} from \"../mining/IPremiaMining.sol\";\r\nimport {IVolatilitySurfaceOracle} from \"../oracle/IVolatilitySurfaceOracle.sol\";\r\n\r\n/**\r\n * @title Premia option pool\r\n * @dev deployed standalone and referenced by PoolProxy\r\n */\r\ncontract PoolInternal is IPoolEvents, ERC1155EnumerableInternal {\r\n using ABDKMath64x64 for int128;\r\n using EnumerableSet for EnumerableSet.AddressSet;\r\n using EnumerableSet for EnumerableSet.UintSet;\r\n using PoolStorage for PoolStorage.Layout;\r\n\r\n address internal immutable WETH_ADDRESS;\r\n address internal immutable PREMIA_MINING_ADDRESS;\r\n address internal immutable FEE_RECEIVER_ADDRESS;\r\n address internal immutable FEE_DISCOUNT_ADDRESS;\r\n address internal immutable IVOL_ORACLE_ADDRESS;\r\n\r\n int128 internal immutable FEE_64x64;\r\n\r\n uint256 internal immutable UNDERLYING_FREE_LIQ_TOKEN_ID;\r\n uint256 internal immutable BASE_FREE_LIQ_TOKEN_ID;\r\n\r\n uint256 internal immutable UNDERLYING_RESERVED_LIQ_TOKEN_ID;\r\n uint256 internal immutable BASE_RESERVED_LIQ_TOKEN_ID;\r\n\r\n uint256 internal constant INVERSE_BASIS_POINT = 1e4;\r\n uint256 internal constant BATCHING_PERIOD = 260;\r\n\r\n // Minimum APY for capital locked up to underwrite options.\r\n // The quote will return a minimum price corresponding to this APY\r\n int128 internal constant MIN_APY_64x64 = 0x4ccccccccccccccd; // 0.3\r\n\r\n constructor(\r\n address ivolOracle,\r\n address weth,\r\n address premiaMining,\r\n address feeReceiver,\r\n address feeDiscountAddress,\r\n int128 fee64x64\r\n ) {\r\n IVOL_ORACLE_ADDRESS = ivolOracle;\r\n WETH_ADDRESS = weth;\r\n PREMIA_MINING_ADDRESS = premiaMining;\r\n FEE_RECEIVER_ADDRESS = feeReceiver;\r\n // PremiaFeeDiscount contract address\r\n FEE_DISCOUNT_ADDRESS = feeDiscountAddress;\r\n FEE_64x64 = fee64x64;\r\n\r\n UNDERLYING_FREE_LIQ_TOKEN_ID = PoolStorage.formatTokenId(\r\n PoolStorage.TokenType.UNDERLYING_FREE_LIQ,\r\n 0,\r\n 0\r\n );\r\n BASE_FREE_LIQ_TOKEN_ID = PoolStorage.formatTokenId(\r\n PoolStorage.TokenType.BASE_FREE_LIQ,\r\n 0,\r\n 0\r\n );\r\n\r\n UNDERLYING_RESERVED_LIQ_TOKEN_ID = PoolStorage.formatTokenId(\r\n PoolStorage.TokenType.UNDERLYING_RESERVED_LIQ,\r\n 0,\r\n 0\r\n );\r\n BASE_RESERVED_LIQ_TOKEN_ID = PoolStorage.formatTokenId(\r\n PoolStorage.TokenType.BASE_RESERVED_LIQ,\r\n 0,\r\n 0\r\n );\r\n }\r\n\r\n modifier onlyProtocolOwner() {\r\n require(\r\n msg.sender == IERC173(OwnableStorage.layout().owner).owner(),\r\n \"Not protocol owner\"\r\n );\r\n _;\r\n }\r\n\r\n function _getFeeDiscount(address feePayer)\r\n internal\r\n view\r\n returns (uint256 discount)\r\n {\r\n if (FEE_DISCOUNT_ADDRESS != address(0)) {\r\n discount = IFeeDiscount(FEE_DISCOUNT_ADDRESS).getDiscount(feePayer);\r\n }\r\n }\r\n\r\n function _getFeeWithDiscount(address feePayer, uint256 fee)\r\n internal\r\n view\r\n returns (uint256)\r\n {\r\n uint256 discount = _getFeeDiscount(feePayer);\r\n return fee - ((fee * discount) / INVERSE_BASIS_POINT);\r\n }\r\n\r\n function _withdrawFees(bool isCall) internal returns (uint256 amount) {\r\n uint256 tokenId = _getReservedLiquidityTokenId(isCall);\r\n amount = _balanceOf(FEE_RECEIVER_ADDRESS, tokenId);\r\n\r\n if (amount > 0) {\r\n _burn(FEE_RECEIVER_ADDRESS, tokenId, amount);\r\n emit FeeWithdrawal(isCall, amount);\r\n }\r\n }\r\n\r\n /**\r\n * @notice calculate price of option contract\r\n * @param args structured quote arguments\r\n * @return result quote result\r\n */\r\n function _quote(PoolStorage.QuoteArgsInternal memory args)\r\n internal\r\n view\r\n returns (PoolStorage.QuoteResultInternal memory result)\r\n {\r\n require(\r\n args.strike64x64 > 0 && args.spot64x64 > 0 && args.maturity > 0,\r\n \"invalid args\"\r\n );\r\n PoolStorage.Layout storage l = PoolStorage.layout();\r\n\r\n int128 contractSize64x64 = ABDKMath64x64Token.fromDecimals(\r\n args.contractSize,\r\n l.underlyingDecimals\r\n );\r\n bool isCall = args.isCall;\r\n\r\n (int128 adjustedCLevel64x64, int128 oldLiquidity64x64) = l\r\n .applyCLevelPendingDepositAdjustment(\r\n l.getDecayAdjustedCLevel64x64(isCall),\r\n l.totalFreeLiquiditySupply64x64(isCall),\r\n isCall\r\n );\r\n\r\n require(oldLiquidity64x64 > 0, \"no liq\");\r\n\r\n int128 timeToMaturity64x64 = ABDKMath64x64.divu(\r\n args.maturity - block.timestamp,\r\n 365 days\r\n );\r\n\r\n int128 annualizedVolatility64x64 = IVolatilitySurfaceOracle(\r\n IVOL_ORACLE_ADDRESS\r\n ).getAnnualizedVolatility64x64(\r\n l.base,\r\n l.underlying,\r\n args.spot64x64,\r\n args.strike64x64,\r\n timeToMaturity64x64,\r\n isCall\r\n );\r\n\r\n require(annualizedVolatility64x64 > 0, \"vol = 0\");\r\n\r\n (\r\n int128 price64x64,\r\n int128 cLevel64x64,\r\n int128 slippageCoefficient64x64\r\n ) = OptionMath.quotePrice(\r\n OptionMath.QuoteArgs(\r\n annualizedVolatility64x64.mul(annualizedVolatility64x64),\r\n args.strike64x64,\r\n args.spot64x64,\r\n timeToMaturity64x64,\r\n adjustedCLevel64x64,\r\n oldLiquidity64x64,\r\n oldLiquidity64x64.sub(contractSize64x64),\r\n 0x10000000000000000, // 64x64 fixed point representation of 1\r\n MIN_APY_64x64,\r\n isCall\r\n )\r\n );\r\n\r\n result.baseCost64x64 = isCall\r\n ? price64x64.mul(contractSize64x64).div(args.spot64x64)\r\n : price64x64.mul(contractSize64x64);\r\n result.feeCost64x64 = result.baseCost64x64.mul(FEE_64x64);\r\n result.cLevel64x64 = cLevel64x64;\r\n result.slippageCoefficient64x64 = slippageCoefficient64x64;\r\n\r\n int128 discount = ABDKMath64x64.divu(\r\n _getFeeDiscount(args.feePayer),\r\n INVERSE_BASIS_POINT\r\n );\r\n result.feeCost64x64 -= result.feeCost64x64.mul(discount);\r\n }\r\n\r\n /**\r\n * @notice burn corresponding long and short option tokens\r\n * @param account holder of tokens to annihilate\r\n * @param maturity timestamp of option maturity\r\n * @param strike64x64 64x64 fixed point representation of strike price\r\n * @param isCall true for call, false for put\r\n * @param contractSize quantity of option contract tokens to annihilate\r\n */\r\n function _annihilate(\r\n address account,\r\n uint64 maturity,\r\n int128 strike64x64,\r\n bool isCall,\r\n uint256 contractSize\r\n ) internal {\r\n uint256 longTokenId = PoolStorage.formatTokenId(\r\n _getTokenType(isCall, true),\r\n maturity,\r\n strike64x64\r\n );\r\n uint256 shortTokenId = PoolStorage.formatTokenId(\r\n _getTokenType(isCall, false),\r\n maturity,\r\n strike64x64\r\n );\r\n\r\n _burn(account, longTokenId, contractSize);\r\n _burn(account, shortTokenId, contractSize);\r\n\r\n emit Annihilate(shortTokenId, contractSize);\r\n }\r\n\r\n /**\r\n * @notice purchase option\r\n * @param l storage layout struct\r\n * @param account recipient of purchased option\r\n * @param maturity timestamp of option maturity\r\n * @param strike64x64 64x64 fixed point representation of strike price\r\n * @param isCall true for call, false for put\r\n * @param contractSize size of option contract\r\n * @param newPrice64x64 64x64 fixed point representation of current spot price\r\n * @return baseCost quantity of tokens required to purchase long position\r\n * @return feeCost quantity of tokens required to pay fees\r\n */\r\n function _purchase(\r\n PoolStorage.Layout storage l,\r\n address account,\r\n uint64 maturity,\r\n int128 strike64x64,\r\n bool isCall,\r\n uint256 contractSize,\r\n int128 newPrice64x64\r\n ) internal returns (uint256 baseCost, uint256 feeCost) {\r\n require(maturity > block.timestamp, \"expired\");\r\n require(contractSize >= l.underlyingMinimum, \"too small\");\r\n\r\n {\r\n uint256 size = isCall\r\n ? contractSize\r\n : l.fromUnderlyingToBaseDecimals(\r\n strike64x64.mulu(contractSize)\r\n );\r\n\r\n require(\r\n size <=\r\n ERC1155EnumerableStorage.layout().totalSupply[\r\n _getFreeLiquidityTokenId(isCall)\r\n ] -\r\n l.nextDeposits[isCall].totalPendingDeposits,\r\n \"insuf liq\"\r\n );\r\n }\r\n\r\n PoolStorage.QuoteResultInternal memory quote = _quote(\r\n PoolStorage.QuoteArgsInternal(\r\n account,\r\n maturity,\r\n strike64x64,\r\n newPrice64x64,\r\n contractSize,\r\n isCall\r\n )\r\n );\r\n\r\n baseCost = ABDKMath64x64Token.toDecimals(\r\n quote.baseCost64x64,\r\n l.getTokenDecimals(isCall)\r\n );\r\n\r\n feeCost = ABDKMath64x64Token.toDecimals(\r\n quote.feeCost64x64,\r\n l.getTokenDecimals(isCall)\r\n );\r\n\r\n uint256 longTokenId = PoolStorage.formatTokenId(\r\n _getTokenType(isCall, true),\r\n maturity,\r\n strike64x64\r\n );\r\n\r\n uint256 shortTokenId = PoolStorage.formatTokenId(\r\n _getTokenType(isCall, false),\r\n maturity,\r\n strike64x64\r\n );\r\n\r\n // mint long option token for buyer\r\n _mint(account, longTokenId, contractSize);\r\n\r\n int128 oldLiquidity64x64 = l.totalFreeLiquiditySupply64x64(isCall);\r\n // burn free liquidity tokens from other underwriters\r\n _mintShortTokenLoop(\r\n l,\r\n account,\r\n contractSize,\r\n baseCost,\r\n shortTokenId,\r\n isCall\r\n );\r\n int128 newLiquidity64x64 = l.totalFreeLiquiditySupply64x64(isCall);\r\n\r\n _setCLevel(l, oldLiquidity64x64, newLiquidity64x64, isCall);\r\n\r\n // mint reserved liquidity tokens for fee receiver\r\n _mint(\r\n FEE_RECEIVER_ADDRESS,\r\n _getReservedLiquidityTokenId(isCall),\r\n feeCost\r\n );\r\n\r\n emit Purchase(\r\n account,\r\n longTokenId,\r\n contractSize,\r\n baseCost,\r\n feeCost,\r\n newPrice64x64\r\n );\r\n }\r\n\r\n /**\r\n * @notice reassign short position to new underwriter\r\n * @param l storage layout struct\r\n * @param account holder of positions to be reassigned\r\n * @param maturity timestamp of option maturity\r\n * @param strike64x64 64x64 fixed point representation of strike price\r\n * @param isCall true for call, false for put\r\n * @param contractSize quantity of option contract tokens to reassign\r\n * @param newPrice64x64 64x64 fixed point representation of current spot price\r\n * @return baseCost quantity of tokens required to reassign short position\r\n * @return feeCost quantity of tokens required to pay fees\r\n * @return amountOut quantity of liquidity freed\r\n */\r\n function _reassign(\r\n PoolStorage.Layout storage l,\r\n address account,\r\n uint64 maturity,\r\n int128 strike64x64,\r\n bool isCall,\r\n uint256 contractSize,\r\n int128 newPrice64x64\r\n )\r\n internal\r\n returns (\r\n uint256 baseCost,\r\n uint256 feeCost,\r\n uint256 amountOut\r\n )\r\n {\r\n (baseCost, feeCost) = _purchase(\r\n l,\r\n account,\r\n maturity,\r\n strike64x64,\r\n isCall,\r\n contractSize,\r\n newPrice64x64\r\n );\r\n\r\n _annihilate(account, maturity, strike64x64, isCall, contractSize);\r\n\r\n uint256 annihilateAmount = isCall\r\n ? contractSize\r\n : l.fromUnderlyingToBaseDecimals(strike64x64.mulu(contractSize));\r\n\r\n amountOut = annihilateAmount - baseCost - feeCost;\r\n }\r\n\r\n /**\r\n * @notice exercise option on behalf of holder\r\n * @dev used for processing of expired options if passed holder is zero address\r\n * @param holder owner of long option tokens to exercise\r\n * @param longTokenId long option token id\r\n * @param contractSize quantity of tokens to exercise\r\n */\r\n function _exercise(\r\n address holder,\r\n uint256 longTokenId,\r\n uint256 contractSize\r\n ) internal {\r\n uint64 maturity;\r\n int128 strike64x64;\r\n bool isCall;\r\n\r\n bool onlyExpired = holder == address(0);\r\n\r\n {\r\n PoolStorage.TokenType tokenType;\r\n (tokenType, maturity, strike64x64) = PoolStorage.parseTokenId(\r\n longTokenId\r\n );\r\n require(\r\n tokenType == PoolStorage.TokenType.LONG_CALL ||\r\n tokenType == PoolStorage.TokenType.LONG_PUT,\r\n \"invalid type\"\r\n );\r\n require(!onlyExpired || maturity < block.timestamp, \"not expired\");\r\n isCall = tokenType == PoolStorage.TokenType.LONG_CALL;\r\n }\r\n\r\n PoolStorage.Layout storage l = PoolStorage.layout();\r\n\r\n int128 spot64x64 = _update(l);\r\n\r\n if (maturity < block.timestamp) {\r\n spot64x64 = l.getPriceUpdateAfter(maturity);\r\n }\r\n\r\n require(\r\n onlyExpired ||\r\n (\r\n isCall\r\n ? (spot64x64 > strike64x64)\r\n : (spot64x64 < strike64x64)\r\n ),\r\n \"not ITM\"\r\n );\r\n\r\n uint256 exerciseValue;\r\n // option has a non-zero exercise value\r\n if (isCall) {\r\n if (spot64x64 > strike64x64) {\r\n exerciseValue = spot64x64.sub(strike64x64).div(spot64x64).mulu(\r\n contractSize\r\n );\r\n }\r\n } else {\r\n if (spot64x64 < strike64x64) {\r\n exerciseValue = l.fromUnderlyingToBaseDecimals(\r\n strike64x64.sub(spot64x64).mulu(contractSize)\r\n );\r\n }\r\n }\r\n\r\n uint256 totalFee;\r\n\r\n if (onlyExpired) {\r\n totalFee += _burnLongTokenLoop(\r\n contractSize,\r\n exerciseValue,\r\n longTokenId,\r\n isCall\r\n );\r\n } else {\r\n // burn long option tokens from sender\r\n _burn(holder, longTokenId, contractSize);\r\n\r\n uint256 fee;\r\n\r\n if (exerciseValue > 0) {\r\n fee = _getFeeWithDiscount(\r\n holder,\r\n FEE_64x64.mulu(exerciseValue)\r\n );\r\n totalFee += fee;\r\n\r\n _pushTo(holder, _getPoolToken(isCall), exerciseValue - fee);\r\n }\r\n\r\n emit Exercise(\r\n holder,\r\n longTokenId,\r\n contractSize,\r\n exerciseValue,\r\n fee\r\n );\r\n }\r\n\r\n totalFee += _burnShortTokenLoop(\r\n contractSize,\r\n exerciseValue,\r\n PoolStorage.formatTokenId(\r\n _getTokenType(isCall, false),\r\n maturity,\r\n strike64x64\r\n ),\r\n isCall\r\n );\r\n\r\n _mint(\r\n FEE_RECEIVER_ADDRESS,\r\n _getReservedLiquidityTokenId(isCall),\r\n totalFee\r\n );\r\n }\r\n\r\n function _mintShortTokenLoop(\r\n PoolStorage.Layout storage l,\r\n address buyer,\r\n uint256 contractSize,\r\n uint256 premium,\r\n uint256 shortTokenId,\r\n bool isCall\r\n ) internal {\r\n uint256 freeLiqTokenId = _getFreeLiquidityTokenId(isCall);\r\n (, , int128 strike64x64) = PoolStorage.parseTokenId(shortTokenId);\r\n\r\n uint256 toPay = isCall\r\n ? contractSize\r\n : l.fromUnderlyingToBaseDecimals(strike64x64.mulu(contractSize));\r\n\r\n while (toPay > 0) {\r\n address underwriter = l.liquidityQueueAscending[isCall][address(0)];\r\n uint256 balance = _balanceOf(underwriter, freeLiqTokenId);\r\n\r\n // If dust left, we remove underwriter and skip to next\r\n if (balance < _getMinimumAmount(l, isCall)) {\r\n l.removeUnderwriter(underwriter, isCall);\r\n continue;\r\n }\r\n\r\n if (!l.getReinvestmentStatus(underwriter, isCall)) {\r\n _burn(underwriter, freeLiqTokenId, balance);\r\n _mint(\r\n underwriter,\r\n _getReservedLiquidityTokenId(isCall),\r\n balance\r\n );\r\n _subUserTVL(l, underwriter, isCall, balance);\r\n continue;\r\n }\r\n\r\n // amount of liquidity provided by underwriter, accounting for reinvested premium\r\n uint256 intervalContractSize = ((balance -\r\n l.pendingDeposits[underwriter][l.nextDeposits[isCall].eta][\r\n isCall\r\n ]) * (toPay + premium)) / toPay;\r\n if (intervalContractSize == 0) continue;\r\n if (intervalContractSize > toPay) intervalContractSize = toPay;\r\n\r\n // amount of premium paid to underwriter\r\n uint256 intervalPremium = (premium * intervalContractSize) / toPay;\r\n premium -= intervalPremium;\r\n toPay -= intervalContractSize;\r\n _addUserTVL(l, underwriter, isCall, intervalPremium);\r\n\r\n // burn free liquidity tokens from underwriter\r\n _burn(\r\n underwriter,\r\n freeLiqTokenId,\r\n intervalContractSize - intervalPremium\r\n );\r\n\r\n if (isCall == false) {\r\n // For PUT, conversion to contract amount is done here (Prior to this line, it is token amount)\r\n intervalContractSize = l.fromBaseToUnderlyingDecimals(\r\n strike64x64.inv().mulu(intervalContractSize)\r\n );\r\n }\r\n\r\n // mint short option tokens for underwriter\r\n // toPay == 0 ? contractSize : intervalContractSize : To prevent minting less than amount,\r\n // because of rounding (Can happen for put, because of fixed point precision)\r\n _mint(\r\n underwriter,\r\n shortTokenId,\r\n toPay == 0 ? contractSize : intervalContractSize\r\n );\r\n\r\n emit Underwrite(\r\n underwriter,\r\n buyer,\r\n shortTokenId,\r\n toPay == 0 ? contractSize : intervalContractSize,\r\n intervalPremium,\r\n false\r\n );\r\n\r\n contractSize -= intervalContractSize;\r\n }\r\n }\r\n\r\n function _burnLongTokenLoop(\r\n uint256 contractSize,\r\n uint256 exerciseValue,\r\n uint256 longTokenId,\r\n bool isCall\r\n ) internal returns (uint256 totalFee) {\r\n EnumerableSet.AddressSet storage holders = ERC1155EnumerableStorage\r\n .layout()\r\n .accountsByToken[longTokenId];\r\n\r\n while (contractSize > 0) {\r\n address longTokenHolder = holders.at(holders.length() - 1);\r\n\r\n uint256 intervalContractSize = _balanceOf(\r\n longTokenHolder,\r\n longTokenId\r\n );\r\n if (intervalContractSize > contractSize)\r\n intervalContractSize = contractSize;\r\n\r\n uint256 intervalExerciseValue;\r\n\r\n uint256 fee;\r\n if (exerciseValue > 0) {\r\n intervalExerciseValue =\r\n (exerciseValue * intervalContractSize) /\r\n contractSize;\r\n\r\n fee = _getFeeWithDiscount(\r\n longTokenHolder,\r\n FEE_64x64.mulu(intervalExerciseValue)\r\n );\r\n totalFee += fee;\r\n\r\n exerciseValue -= intervalExerciseValue;\r\n _pushTo(\r\n longTokenHolder,\r\n _getPoolToken(isCall),\r\n intervalExerciseValue - fee\r\n );\r\n }\r\n\r\n contractSize -= intervalContractSize;\r\n\r\n emit Exercise(\r\n longTokenHolder,\r\n longTokenId,\r\n intervalContractSize,\r\n intervalExerciseValue - fee,\r\n fee\r\n );\r\n\r\n _burn(longTokenHolder, longTokenId, intervalContractSize);\r\n }\r\n }\r\n\r\n function _burnShortTokenLoop(\r\n uint256 contractSize,\r\n uint256 exerciseValue,\r\n uint256 shortTokenId,\r\n bool isCall\r\n ) internal returns (uint256 totalFee) {\r\n EnumerableSet.AddressSet storage underwriters = ERC1155EnumerableStorage\r\n .layout()\r\n .accountsByToken[shortTokenId];\r\n (, , int128 strike64x64) = PoolStorage.parseTokenId(shortTokenId);\r\n\r\n while (contractSize > 0) {\r\n address underwriter = underwriters.at(underwriters.length() - 1);\r\n\r\n // amount of liquidity provided by underwriter\r\n uint256 intervalContractSize = _balanceOf(\r\n underwriter,\r\n shortTokenId\r\n );\r\n if (intervalContractSize > contractSize)\r\n intervalContractSize = contractSize;\r\n\r\n // amount of value claimed by buyer\r\n uint256 intervalExerciseValue = (exerciseValue *\r\n intervalContractSize) / contractSize;\r\n exerciseValue -= intervalExerciseValue;\r\n contractSize -= intervalContractSize;\r\n\r\n uint256 freeLiq = isCall\r\n ? intervalContractSize - intervalExerciseValue\r\n : PoolStorage.layout().fromUnderlyingToBaseDecimals(\r\n strike64x64.mulu(intervalContractSize)\r\n ) - intervalExerciseValue;\r\n\r\n uint256 fee = _getFeeWithDiscount(\r\n underwriter,\r\n FEE_64x64.mulu(freeLiq)\r\n );\r\n totalFee += fee;\r\n\r\n uint256 tvlToSubtract = intervalExerciseValue;\r\n\r\n // mint free liquidity tokens for underwriter\r\n if (\r\n PoolStorage.layout().getReinvestmentStatus(underwriter, isCall)\r\n ) {\r\n _addToDepositQueue(underwriter, freeLiq - fee, isCall);\r\n tvlToSubtract += fee;\r\n } else {\r\n _mint(\r\n underwriter,\r\n _getReservedLiquidityTokenId(isCall),\r\n freeLiq - fee\r\n );\r\n tvlToSubtract += freeLiq;\r\n }\r\n\r\n _subUserTVL(\r\n PoolStorage.layout(),\r\n underwriter,\r\n isCall,\r\n tvlToSubtract\r\n );\r\n\r\n // burn short option tokens from underwriter\r\n _burn(underwriter, shortTokenId, intervalContractSize);\r\n\r\n emit AssignExercise(\r\n underwriter,\r\n shortTokenId,\r\n freeLiq - fee,\r\n intervalContractSize,\r\n fee\r\n );\r\n }\r\n }\r\n\r\n function _addToDepositQueue(\r\n address account,\r\n uint256 amount,\r\n bool isCallPool\r\n ) internal {\r\n PoolStorage.Layout storage l = PoolStorage.layout();\r\n\r\n _mint(account, _getFreeLiquidityTokenId(isCallPool), amount);\r\n\r\n uint256 nextBatch = (block.timestamp / BATCHING_PERIOD) *\r\n BATCHING_PERIOD +\r\n BATCHING_PERIOD;\r\n l.pendingDeposits[account][nextBatch][isCallPool] += amount;\r\n\r\n PoolStorage.BatchData storage batchData = l.nextDeposits[isCallPool];\r\n batchData.totalPendingDeposits += amount;\r\n batchData.eta = nextBatch;\r\n }\r\n\r\n function _processPendingDeposits(PoolStorage.Layout storage l, bool isCall)\r\n internal\r\n {\r\n PoolStorage.BatchData storage data = l.nextDeposits[isCall];\r\n\r\n if (data.eta == 0 || block.timestamp < data.eta) return;\r\n\r\n int128 oldLiquidity64x64 = l.totalFreeLiquiditySupply64x64(isCall);\r\n\r\n _setCLevel(\r\n l,\r\n oldLiquidity64x64,\r\n oldLiquidity64x64.add(\r\n ABDKMath64x64Token.fromDecimals(\r\n data.totalPendingDeposits,\r\n l.getTokenDecimals(isCall)\r\n )\r\n ),\r\n isCall\r\n );\r\n\r\n delete l.nextDeposits[isCall];\r\n }\r\n\r\n function _getFreeLiquidityTokenId(bool isCall)\r\n internal\r\n view\r\n returns (uint256 freeLiqTokenId)\r\n {\r\n freeLiqTokenId = isCall\r\n ? UNDERLYING_FREE_LIQ_TOKEN_ID\r\n : BASE_FREE_LIQ_TOKEN_ID;\r\n }\r\n\r\n function _getReservedLiquidityTokenId(bool isCall)\r\n internal\r\n view\r\n returns (uint256 reservedLiqTokenId)\r\n {\r\n reservedLiqTokenId = isCall\r\n ? UNDERLYING_RESERVED_LIQ_TOKEN_ID\r\n : BASE_RESERVED_LIQ_TOKEN_ID;\r\n }\r\n\r\n function _getPoolToken(bool isCall) internal view returns (address token) {\r\n token = isCall\r\n ? PoolStorage.layout().underlying\r\n : PoolStorage.layout().base;\r\n }\r\n\r\n function _getTokenType(bool isCall, bool isLong)\r\n internal\r\n pure\r\n returns (PoolStorage.TokenType tokenType)\r\n {\r\n if (isCall) {\r\n tokenType = isLong\r\n ? PoolStorage.TokenType.LONG_CALL\r\n : PoolStorage.TokenType.SHORT_CALL;\r\n } else {\r\n tokenType = isLong\r\n ? PoolStorage.TokenType.LONG_PUT\r\n : PoolStorage.TokenType.SHORT_PUT;\r\n }\r\n }\r\n\r\n function _getMinimumAmount(PoolStorage.Layout storage l, bool isCall)\r\n internal\r\n view\r\n returns (uint256 minimumAmount)\r\n {\r\n minimumAmount = isCall ? l.underlyingMinimum : l.baseMinimum;\r\n }\r\n\r\n function _getPoolCapAmount(PoolStorage.Layout storage l, bool isCall)\r\n internal\r\n view\r\n returns (uint256 poolCapAmount)\r\n {\r\n poolCapAmount = isCall ? l.underlyingPoolCap : l.basePoolCap;\r\n }\r\n\r\n function _setCLevel(\r\n PoolStorage.Layout storage l,\r\n int128 oldLiquidity64x64,\r\n int128 newLiquidity64x64,\r\n bool isCallPool\r\n ) internal {\r\n int128 oldCLevel64x64 = l.getDecayAdjustedCLevel64x64(isCallPool);\r\n\r\n int128 cLevel64x64 = l.applyCLevelLiquidityChangeAdjustment(\r\n oldCLevel64x64,\r\n oldLiquidity64x64,\r\n newLiquidity64x64,\r\n isCallPool\r\n );\r\n\r\n l.setCLevel(cLevel64x64, isCallPool);\r\n\r\n emit UpdateCLevel(\r\n isCallPool,\r\n cLevel64x64,\r\n oldLiquidity64x64,\r\n newLiquidity64x64\r\n );\r\n }\r\n\r\n /**\r\n * @notice calculate and store updated market state\r\n * @param l storage layout struct\r\n * @return newPrice64x64 64x64 fixed point representation of current spot price\r\n */\r\n function _update(PoolStorage.Layout storage l)\r\n internal\r\n returns (int128 newPrice64x64)\r\n {\r\n if (l.updatedAt == block.timestamp) {\r\n return (l.getPriceUpdate(block.timestamp));\r\n }\r\n\r\n newPrice64x64 = l.fetchPriceUpdate();\r\n\r\n if (l.getPriceUpdate(block.timestamp) == 0) {\r\n l.setPriceUpdate(block.timestamp, newPrice64x64);\r\n }\r\n\r\n l.updatedAt = block.timestamp;\r\n\r\n _processPendingDeposits(l, true);\r\n _processPendingDeposits(l, false);\r\n }\r\n\r\n /**\r\n * @notice transfer ERC20 tokens to message sender\r\n * @param token ERC20 token address\r\n * @param amount quantity of token to transfer\r\n */\r\n function _pushTo(\r\n address to,\r\n address token,\r\n uint256 amount\r\n ) internal {\r\n if (amount == 0) return;\r\n\r\n require(IERC20(token).transfer(to, amount), \"ERC20 transfer failed\");\r\n }\r\n\r\n /**\r\n * @notice transfer ERC20 tokens from message sender\r\n * @param from address from which tokens are pulled from\r\n * @param token ERC20 token address\r\n * @param amount quantity of token to transfer\r\n * @param skipWethDeposit if false, will not try to deposit weth from attach eth\r\n */\r\n function _pullFrom(\r\n address from,\r\n address token,\r\n uint256 amount,\r\n bool skipWethDeposit\r\n ) internal {\r\n if (!skipWethDeposit) {\r\n if (token == WETH_ADDRESS) {\r\n if (msg.value > 0) {\r\n if (msg.value > amount) {\r\n IWETH(WETH_ADDRESS).deposit{value: amount}();\r\n\r\n (bool success, ) = payable(msg.sender).call{\r\n value: msg.value - amount\r\n }(\"\");\r\n\r\n require(success, \"ETH refund failed\");\r\n\r\n amount = 0;\r\n } else {\r\n unchecked {\r\n amount -= msg.value;\r\n }\r\n\r\n IWETH(WETH_ADDRESS).deposit{value: msg.value}();\r\n }\r\n }\r\n } else {\r\n require(msg.value == 0, \"not WETH deposit\");\r\n }\r\n }\r\n\r\n if (amount > 0) {\r\n require(\r\n IERC20(token).transferFrom(from, address(this), amount),\r\n \"ERC20 transfer failed\"\r\n );\r\n }\r\n }\r\n\r\n function _mint(\r\n address account,\r\n uint256 tokenId,\r\n uint256 amount\r\n ) internal {\r\n _mint(account, tokenId, amount, \"\");\r\n }\r\n\r\n function _addUserTVL(\r\n PoolStorage.Layout storage l,\r\n address user,\r\n bool isCallPool,\r\n uint256 amount\r\n ) internal {\r\n uint256 userTVL = l.userTVL[user][isCallPool];\r\n uint256 totalTVL = l.totalTVL[isCallPool];\r\n\r\n IPremiaMining(PREMIA_MINING_ADDRESS).allocatePending(\r\n user,\r\n address(this),\r\n isCallPool,\r\n userTVL,\r\n userTVL + amount,\r\n totalTVL\r\n );\r\n\r\n l.userTVL[user][isCallPool] = userTVL + amount;\r\n l.totalTVL[isCallPool] = totalTVL + amount;\r\n }\r\n\r\n function _subUserTVL(\r\n PoolStorage.Layout storage l,\r\n address user,\r\n bool isCallPool,\r\n uint256 amount\r\n ) internal {\r\n uint256 userTVL = l.userTVL[user][isCallPool];\r\n uint256 totalTVL = l.totalTVL[isCallPool];\r\n\r\n IPremiaMining(PREMIA_MINING_ADDRESS).allocatePending(\r\n user,\r\n address(this),\r\n isCallPool,\r\n userTVL,\r\n userTVL - amount,\r\n totalTVL\r\n );\r\n l.userTVL[user][isCallPool] = userTVL - amount;\r\n l.totalTVL[isCallPool] = totalTVL - amount;\r\n }\r\n\r\n /**\r\n * @notice ERC1155 hook: track eligible underwriters\r\n * @param operator transaction sender\r\n * @param from token sender\r\n * @param to token receiver\r\n * @param ids token ids transferred\r\n * @param amounts token quantities transferred\r\n * @param data data payload\r\n */\r\n function _beforeTokenTransfer(\r\n address operator,\r\n address from,\r\n address to,\r\n uint256[] memory ids,\r\n uint256[] memory amounts,\r\n bytes memory data\r\n ) internal virtual override {\r\n super._beforeTokenTransfer(operator, from, to, ids, amounts, data);\r\n\r\n PoolStorage.Layout storage l = PoolStorage.layout();\r\n\r\n for (uint256 i; i < ids.length; i++) {\r\n uint256 id = ids[i];\r\n uint256 amount = amounts[i];\r\n\r\n if (amount == 0) continue;\r\n\r\n if (from == address(0)) {\r\n l.tokenIds.add(id);\r\n }\r\n\r\n if (\r\n to == address(0) &&\r\n ERC1155EnumerableStorage.layout().totalSupply[id] == 0\r\n ) {\r\n l.tokenIds.remove(id);\r\n }\r\n\r\n // prevent transfer of free and reserved liquidity during waiting period\r\n\r\n if (\r\n id == UNDERLYING_FREE_LIQ_TOKEN_ID ||\r\n id == BASE_FREE_LIQ_TOKEN_ID ||\r\n id == UNDERLYING_RESERVED_LIQ_TOKEN_ID ||\r\n id == BASE_RESERVED_LIQ_TOKEN_ID\r\n ) {\r\n if (from != address(0) && to != address(0)) {\r\n bool isCallPool = id == UNDERLYING_FREE_LIQ_TOKEN_ID ||\r\n id == UNDERLYING_RESERVED_LIQ_TOKEN_ID;\r\n\r\n require(\r\n l.depositedAt[from][isCallPool] + (1 days) <\r\n block.timestamp,\r\n \"liq lock 1d\"\r\n );\r\n }\r\n }\r\n\r\n if (\r\n id == UNDERLYING_FREE_LIQ_TOKEN_ID ||\r\n id == BASE_FREE_LIQ_TOKEN_ID\r\n ) {\r\n bool isCallPool = id == UNDERLYING_FREE_LIQ_TOKEN_ID;\r\n uint256 minimum = _getMinimumAmount(l, isCallPool);\r\n\r\n if (from != address(0)) {\r\n uint256 balance = _balanceOf(from, id);\r\n\r\n if (balance > minimum && balance <= amount + minimum) {\r\n require(\r\n balance -\r\n l.pendingDeposits[from][\r\n l.nextDeposits[isCallPool].eta\r\n ][isCallPool] >=\r\n amount,\r\n \"Insuf balance\"\r\n );\r\n l.removeUnderwriter(from, isCallPool);\r\n }\r\n\r\n if (to != address(0)) {\r\n _subUserTVL(l, from, isCallPool, amounts[i]);\r\n _addUserTVL(l, to, isCallPool, amounts[i]);\r\n }\r\n }\r\n\r\n if (to != address(0)) {\r\n uint256 balance = _balanceOf(to, id);\r\n\r\n if (balance <= minimum && balance + amount > minimum) {\r\n l.addUnderwriter(to, isCallPool);\r\n }\r\n }\r\n }\r\n\r\n // Update userTVL on SHORT options transfers\r\n (\r\n PoolStorage.TokenType tokenType,\r\n ,\r\n int128 strike64x64\r\n ) = PoolStorage.parseTokenId(id);\r\n\r\n if (\r\n (from != address(0) && to != address(0)) &&\r\n (tokenType == PoolStorage.TokenType.SHORT_CALL ||\r\n tokenType == PoolStorage.TokenType.SHORT_PUT)\r\n ) {\r\n bool isCall = tokenType == PoolStorage.TokenType.SHORT_CALL;\r\n uint256 collateral = isCall\r\n ? amount\r\n : l.fromUnderlyingToBaseDecimals(strike64x64.mulu(amount));\r\n\r\n _subUserTVL(l, from, isCall, collateral);\r\n _addUserTVL(l, to, isCall, collateral);\r\n }\r\n }\r\n }\r\n}\r\n" - }, - "@solidstate/contracts/token/ERC1155/base/ERC1155BaseStorage.sol": { - "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nlibrary ERC1155BaseStorage {\n struct Layout {\n mapping(uint256 => mapping(address => uint256)) balances;\n mapping(address => mapping(address => bool)) operatorApprovals;\n }\n\n bytes32 internal constant STORAGE_SLOT =\n keccak256('solidstate.contracts.storage.ERC1155Base');\n\n function layout() internal pure returns (Layout storage l) {\n bytes32 slot = STORAGE_SLOT;\n assembly {\n l.slot := slot\n }\n }\n}\n" - }, - "@solidstate/contracts/utils/EnumerableSet.sol": { - "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @title Set implementation with enumeration functions\n * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts (MIT license)\n */\nlibrary EnumerableSet {\n struct Set {\n bytes32[] _values;\n // 1-indexed to allow 0 to signify nonexistence\n mapping(bytes32 => uint256) _indexes;\n }\n\n struct Bytes32Set {\n Set _inner;\n }\n\n struct AddressSet {\n Set _inner;\n }\n\n struct UintSet {\n Set _inner;\n }\n\n function at(Bytes32Set storage set, uint256 index)\n internal\n view\n returns (bytes32)\n {\n return _at(set._inner, index);\n }\n\n function at(AddressSet storage set, uint256 index)\n internal\n view\n returns (address)\n {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n function at(UintSet storage set, uint256 index)\n internal\n view\n returns (uint256)\n {\n return uint256(_at(set._inner, index));\n }\n\n function contains(Bytes32Set storage set, bytes32 value)\n internal\n view\n returns (bool)\n {\n return _contains(set._inner, value);\n }\n\n function contains(AddressSet storage set, address value)\n internal\n view\n returns (bool)\n {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n function contains(UintSet storage set, uint256 value)\n internal\n view\n returns (bool)\n {\n return _contains(set._inner, bytes32(value));\n }\n\n function indexOf(Bytes32Set storage set, bytes32 value)\n internal\n view\n returns (uint256)\n {\n return _indexOf(set._inner, value);\n }\n\n function indexOf(AddressSet storage set, address value)\n internal\n view\n returns (uint256)\n {\n return _indexOf(set._inner, bytes32(uint256(uint160(value))));\n }\n\n function indexOf(UintSet storage set, uint256 value)\n internal\n view\n returns (uint256)\n {\n return _indexOf(set._inner, bytes32(value));\n }\n\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n function add(Bytes32Set storage set, bytes32 value)\n internal\n returns (bool)\n {\n return _add(set._inner, value);\n }\n\n function add(AddressSet storage set, address value)\n internal\n returns (bool)\n {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n function remove(Bytes32Set storage set, bytes32 value)\n internal\n returns (bool)\n {\n return _remove(set._inner, value);\n }\n\n function remove(AddressSet storage set, address value)\n internal\n returns (bool)\n {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n function remove(UintSet storage set, uint256 value)\n internal\n returns (bool)\n {\n return _remove(set._inner, bytes32(value));\n }\n\n function _at(Set storage set, uint256 index)\n private\n view\n returns (bytes32)\n {\n require(\n set._values.length > index,\n 'EnumerableSet: index out of bounds'\n );\n return set._values[index];\n }\n\n function _contains(Set storage set, bytes32 value)\n private\n view\n returns (bool)\n {\n return set._indexes[value] != 0;\n }\n\n function _indexOf(Set storage set, bytes32 value)\n private\n view\n returns (uint256)\n {\n unchecked {\n return set._indexes[value] - 1;\n }\n }\n\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n uint256 index = valueIndex - 1;\n bytes32 last = set._values[set._values.length - 1];\n\n // move last value to now-vacant index\n\n set._values[index] = last;\n set._indexes[last] = index + 1;\n\n // clear last index\n\n set._values.pop();\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n}\n" - }, - "contracts/mining/PremiaMiningStorage.sol": { - "content": "// SPDX-License-Identifier: BUSL-1.1\r\n// For further clarification please see https://license.premia.legal\r\n\r\npragma solidity ^0.8.0;\r\n\r\nlibrary PremiaMiningStorage {\r\n bytes32 internal constant STORAGE_SLOT =\r\n keccak256(\"premia.contracts.storage.PremiaMining\");\r\n\r\n // Info of each pool.\r\n struct PoolInfo {\r\n uint256 allocPoint; // How many allocation points assigned to this pool. PREMIA to distribute per block.\r\n uint256 lastRewardTimestamp; // Last timestamp that PREMIA distribution occurs\r\n uint256 accPremiaPerShare; // Accumulated PREMIA per share, times 1e12. See below.\r\n }\r\n\r\n // Info of each user.\r\n struct UserInfo {\r\n uint256 reward; // Total allocated unclaimed reward\r\n uint256 rewardDebt; // Reward debt. See explanation below.\r\n //\r\n // We do some fancy math here. Basically, any point in time, the amount of PREMIA\r\n // entitled to a user but is pending to be distributed is:\r\n //\r\n // pending reward = (user.amount * pool.accPremiaPerShare) - user.rewardDebt\r\n //\r\n // Whenever a user deposits or withdraws LP tokens to a pool. Here's what happens:\r\n // 1. The pool's `accPremiaPerShare` (and `lastRewardBlock`) gets updated.\r\n // 2. User receives the pending reward sent to his/her address.\r\n // 3. User's `amount` gets updated.\r\n // 4. User's `rewardDebt` gets updated.\r\n }\r\n\r\n struct Layout {\r\n // Total PREMIA left to distribute\r\n uint256 premiaAvailable;\r\n // Amount of premia distributed per year\r\n uint256 premiaPerYear;\r\n // pool -> isCallPool -> PoolInfo\r\n mapping(address => mapping(bool => PoolInfo)) poolInfo;\r\n // pool -> isCallPool -> user -> UserInfo\r\n mapping(address => mapping(bool => mapping(address => UserInfo))) userInfo;\r\n // Total allocation points. Must be the sum of all allocation points in all pools.\r\n uint256 totalAllocPoint;\r\n }\r\n\r\n function layout() internal pure returns (Layout storage l) {\r\n bytes32 slot = STORAGE_SLOT;\r\n assembly {\r\n l.slot := slot\r\n }\r\n }\r\n}\r\n" - }, - "@solidstate/contracts/utils/IWETH.sol": { - "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC20 } from '../token/ERC20/IERC20.sol';\nimport { IERC20Metadata } from '../token/ERC20/metadata/IERC20Metadata.sol';\n\n/**\n * @title WETH (Wrapped ETH) interface\n */\ninterface IWETH is IERC20, IERC20Metadata {\n /**\n * @notice convert ETH to WETH\n */\n function deposit() external payable;\n\n /**\n * @notice convert WETH to ETH\n * @dev if caller is a contract, it should have a fallback or receive function\n * @param amount quantity of WETH to convert, denominated in wei\n */\n function withdraw(uint256 amount) external;\n}\n" - }, - "@solidstate/contracts/token/ERC1155/IERC1155Internal.sol": { - "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC165 } from '../../introspection/IERC165.sol';\n\n/**\n * @notice Partial ERC1155 interface needed by internal functions\n */\ninterface IERC1155Internal {\n event TransferSingle(\n address indexed operator,\n address indexed from,\n address indexed to,\n uint256 id,\n uint256 value\n );\n\n event TransferBatch(\n address indexed operator,\n address indexed from,\n address indexed to,\n uint256[] ids,\n uint256[] values\n );\n\n event ApprovalForAll(\n address indexed account,\n address indexed operator,\n bool approved\n );\n}\n" - }, - "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol": { - "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AggregatorV3Interface {\n\n function decimals()\n external\n view\n returns (\n uint8\n );\n\n function description()\n external\n view\n returns (\n string memory\n );\n\n function version()\n external\n view\n returns (\n uint256\n );\n\n // getRoundData and latestRoundData should both raise \"No data present\"\n // if they do not have data to report, instead of returning unset values\n // which could be misinterpreted as actual reported values.\n function getRoundData(\n uint80 _roundId\n )\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n\n function latestRoundData()\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n\n}\n" - }, - "@solidstate/contracts/access/OwnableStorage.sol": { - "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nlibrary OwnableStorage {\n struct Layout {\n address owner;\n }\n\n bytes32 internal constant STORAGE_SLOT =\n keccak256('solidstate.contracts.storage.Ownable');\n\n function layout() internal pure returns (Layout storage l) {\n bytes32 slot = STORAGE_SLOT;\n assembly {\n l.slot := slot\n }\n }\n\n function setOwner(Layout storage l, address owner) internal {\n l.owner = owner;\n }\n}\n" - } - }, - "settings": { - "optimizer": { - "enabled": true, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "evm.bytecode", - "evm.deployedBytecode", - "devdoc", - "userdoc", - "metadata", - "abi" - ] - } - }, - "libraries": { - "contracts/libraries/OptionMath.sol": { - "OptionMath": "0x0f6e8ef18fb5bb61d545fee60f779d8aed60408f" - } - } - } - }, - "ABI": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"ivolOracle\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"weth\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"premiaMining\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeReceiver\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeDiscountAddress\",\"type\":\"address\"},{\"internalType\":\"int128\",\"name\":\"fee64x64\",\"type\":\"int128\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"shortTokenId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Annihilate\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"ApprovalForAll\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"underwriter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"shortTokenId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"freedAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"intervalContractSize\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"}],\"name\":\"AssignExercise\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isCallPool\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Deposit\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"longTokenId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"contractSize\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"exerciseValue\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"}],\"name\":\"Exercise\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"isCallPool\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"FeeWithdrawal\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"longTokenId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"contractSize\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"baseCost\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"feeCost\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"int128\",\"name\":\"spot64x64\",\"type\":\"int128\"}],\"name\":\"Purchase\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"ids\",\"type\":\"uint256[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"}],\"name\":\"TransferBatch\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"TransferSingle\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"underwriter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"longReceiver\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"shortTokenId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"intervalContractSize\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"intervalPremium\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isManualUnderwrite\",\"type\":\"bool\"}],\"name\":\"Underwrite\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"isCall\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"int128\",\"name\":\"cLevel64x64\",\"type\":\"int128\"},{\"indexed\":false,\"internalType\":\"int128\",\"name\":\"oldLiquidity64x64\",\"type\":\"int128\"},{\"indexed\":false,\"internalType\":\"int128\",\"name\":\"newLiquidity64x64\",\"type\":\"int128\"}],\"name\":\"UpdateCLevel\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"int128\",\"name\":\"steepness64x64\",\"type\":\"int128\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isCallPool\",\"type\":\"bool\"}],\"name\":\"UpdateSteepness\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isCallPool\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"depositedAt\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Withdrawal\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"holder\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"longTokenId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"contractSize\",\"type\":\"uint256\"}],\"name\":\"exerciseFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"longTokenId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"contractSize\",\"type\":\"uint256\"}],\"name\":\"processExpired\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - "ContractName": "PoolExercise", - "CompilerVersion": "v0.8.9+commit.e5eed63a", - "OptimizationUsed": 1, - "Runs": 200, - "ConstructorArguments": "0x0000000000000000000000003a87bb29b984d672664aa1dd2d19d2e8b24f0f2a000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000009abb27581c2e46a114f8c367355851e0580e9703000000000000000000000000c4b2c51f969e0713e799de73b7f130fb7bb604cf000000000000000000000000f1bb87563a122211d40d393ebf1c633c330377f900000000000000000000000000000000000000000000000007ae147ae147ae14", - "EVMVersion": "Default", - "Library": "", - "LicenseType": "", - "Proxy": 0, - "SwarmSource": "" - } -] \ No newline at end of file +[{"SourceCode":{"language":"Solidity","sources":{"@solidstate/contracts/token/ERC1155/base/ERC1155Base.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC1155 } from '../IERC1155.sol';\nimport { IERC1155Receiver } from '../IERC1155Receiver.sol';\nimport { ERC1155BaseInternal, ERC1155BaseStorage } from './ERC1155BaseInternal.sol';\n\n/**\n * @title Base ERC1155 contract\n * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts/ (MIT license)\n */\nabstract contract ERC1155Base is IERC1155, ERC1155BaseInternal {\n /**\n * @inheritdoc IERC1155\n */\n function balanceOf(address account, uint256 id)\n public\n view\n virtual\n override\n returns (uint256)\n {\n return _balanceOf(account, id);\n }\n\n /**\n * @inheritdoc IERC1155\n */\n function balanceOfBatch(address[] memory accounts, uint256[] memory ids)\n public\n view\n virtual\n override\n returns (uint256[] memory)\n {\n require(\n accounts.length == ids.length,\n 'ERC1155: accounts and ids length mismatch'\n );\n\n mapping(uint256 => mapping(address => uint256))\n storage balances = ERC1155BaseStorage.layout().balances;\n\n uint256[] memory batchBalances = new uint256[](accounts.length);\n\n unchecked {\n for (uint256 i; i < accounts.length; i++) {\n require(\n accounts[i] != address(0),\n 'ERC1155: batch balance query for the zero address'\n );\n batchBalances[i] = balances[ids[i]][accounts[i]];\n }\n }\n\n return batchBalances;\n }\n\n /**\n * @inheritdoc IERC1155\n */\n function isApprovedForAll(address account, address operator)\n public\n view\n virtual\n override\n returns (bool)\n {\n return ERC1155BaseStorage.layout().operatorApprovals[account][operator];\n }\n\n /**\n * @inheritdoc IERC1155\n */\n function setApprovalForAll(address operator, bool status)\n public\n virtual\n override\n {\n require(\n msg.sender != operator,\n 'ERC1155: setting approval status for self'\n );\n ERC1155BaseStorage.layout().operatorApprovals[msg.sender][\n operator\n ] = status;\n emit ApprovalForAll(msg.sender, operator, status);\n }\n\n /**\n * @inheritdoc IERC1155\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 id,\n uint256 amount,\n bytes memory data\n ) public virtual override {\n require(\n from == msg.sender || isApprovedForAll(from, msg.sender),\n 'ERC1155: caller is not owner nor approved'\n );\n _safeTransfer(msg.sender, from, to, id, amount, data);\n }\n\n /**\n * @inheritdoc IERC1155\n */\n function safeBatchTransferFrom(\n address from,\n address to,\n uint256[] memory ids,\n uint256[] memory amounts,\n bytes memory data\n ) public virtual override {\n require(\n from == msg.sender || isApprovedForAll(from, msg.sender),\n 'ERC1155: caller is not owner nor approved'\n );\n _safeTransferBatch(msg.sender, from, to, ids, amounts, data);\n }\n}\n"},"@solidstate/contracts/introspection/IERC165.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @title ERC165 interface registration interface\n * @dev see https://eips.ethereum.org/EIPS/eip-165\n */\ninterface IERC165 {\n /**\n * @notice query whether contract has registered support for given interface\n * @param interfaceId interface id\n * @return bool whether interface is supported\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n"},"abdk-libraries-solidity/ABDKMath64x64.sol":{"content":"// SPDX-License-Identifier: BSD-4-Clause\n/*\n * ABDK Math 64.64 Smart Contract Library. Copyright © 2019 by ABDK Consulting.\n * Author: Mikhail Vladimirov \n */\npragma solidity ^0.8.0;\n\n/**\n * Smart contract library of mathematical functions operating with signed\n * 64.64-bit fixed point numbers. Signed 64.64-bit fixed point number is\n * basically a simple fraction whose numerator is signed 128-bit integer and\n * denominator is 2^64. As long as denominator is always the same, there is no\n * need to store it, thus in Solidity signed 64.64-bit fixed point numbers are\n * represented by int128 type holding only the numerator.\n */\nlibrary ABDKMath64x64 {\n /*\n * Minimum value signed 64.64-bit fixed point number may have. \n */\n int128 private constant MIN_64x64 = -0x80000000000000000000000000000000;\n\n /*\n * Maximum value signed 64.64-bit fixed point number may have. \n */\n int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;\n\n /**\n * Convert signed 256-bit integer number into signed 64.64-bit fixed point\n * number. Revert on overflow.\n *\n * @param x signed 256-bit integer number\n * @return signed 64.64-bit fixed point number\n */\n function fromInt (int256 x) internal pure returns (int128) {\n unchecked {\n require (x >= -0x8000000000000000 && x <= 0x7FFFFFFFFFFFFFFF);\n return int128 (x << 64);\n }\n }\n\n /**\n * Convert signed 64.64 fixed point number into signed 64-bit integer number\n * rounding down.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64-bit integer number\n */\n function toInt (int128 x) internal pure returns (int64) {\n unchecked {\n return int64 (x >> 64);\n }\n }\n\n /**\n * Convert unsigned 256-bit integer number into signed 64.64-bit fixed point\n * number. Revert on overflow.\n *\n * @param x unsigned 256-bit integer number\n * @return signed 64.64-bit fixed point number\n */\n function fromUInt (uint256 x) internal pure returns (int128) {\n unchecked {\n require (x <= 0x7FFFFFFFFFFFFFFF);\n return int128 (int256 (x << 64));\n }\n }\n\n /**\n * Convert signed 64.64 fixed point number into unsigned 64-bit integer\n * number rounding down. Revert on underflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @return unsigned 64-bit integer number\n */\n function toUInt (int128 x) internal pure returns (uint64) {\n unchecked {\n require (x >= 0);\n return uint64 (uint128 (x >> 64));\n }\n }\n\n /**\n * Convert signed 128.128 fixed point number into signed 64.64-bit fixed point\n * number rounding down. Revert on overflow.\n *\n * @param x signed 128.128-bin fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function from128x128 (int256 x) internal pure returns (int128) {\n unchecked {\n int256 result = x >> 64;\n require (result >= MIN_64x64 && result <= MAX_64x64);\n return int128 (result);\n }\n }\n\n /**\n * Convert signed 64.64 fixed point number into signed 128.128 fixed point\n * number.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 128.128 fixed point number\n */\n function to128x128 (int128 x) internal pure returns (int256) {\n unchecked {\n return int256 (x) << 64;\n }\n }\n\n /**\n * Calculate x + y. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @param y signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function add (int128 x, int128 y) internal pure returns (int128) {\n unchecked {\n int256 result = int256(x) + y;\n require (result >= MIN_64x64 && result <= MAX_64x64);\n return int128 (result);\n }\n }\n\n /**\n * Calculate x - y. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @param y signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function sub (int128 x, int128 y) internal pure returns (int128) {\n unchecked {\n int256 result = int256(x) - y;\n require (result >= MIN_64x64 && result <= MAX_64x64);\n return int128 (result);\n }\n }\n\n /**\n * Calculate x * y rounding down. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @param y signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function mul (int128 x, int128 y) internal pure returns (int128) {\n unchecked {\n int256 result = int256(x) * y >> 64;\n require (result >= MIN_64x64 && result <= MAX_64x64);\n return int128 (result);\n }\n }\n\n /**\n * Calculate x * y rounding towards zero, where x is signed 64.64 fixed point\n * number and y is signed 256-bit integer number. Revert on overflow.\n *\n * @param x signed 64.64 fixed point number\n * @param y signed 256-bit integer number\n * @return signed 256-bit integer number\n */\n function muli (int128 x, int256 y) internal pure returns (int256) {\n unchecked {\n if (x == MIN_64x64) {\n require (y >= -0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF &&\n y <= 0x1000000000000000000000000000000000000000000000000);\n return -y << 63;\n } else {\n bool negativeResult = false;\n if (x < 0) {\n x = -x;\n negativeResult = true;\n }\n if (y < 0) {\n y = -y; // We rely on overflow behavior here\n negativeResult = !negativeResult;\n }\n uint256 absoluteResult = mulu (x, uint256 (y));\n if (negativeResult) {\n require (absoluteResult <=\n 0x8000000000000000000000000000000000000000000000000000000000000000);\n return -int256 (absoluteResult); // We rely on overflow behavior here\n } else {\n require (absoluteResult <=\n 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);\n return int256 (absoluteResult);\n }\n }\n }\n }\n\n /**\n * Calculate x * y rounding down, where x is signed 64.64 fixed point number\n * and y is unsigned 256-bit integer number. Revert on overflow.\n *\n * @param x signed 64.64 fixed point number\n * @param y unsigned 256-bit integer number\n * @return unsigned 256-bit integer number\n */\n function mulu (int128 x, uint256 y) internal pure returns (uint256) {\n unchecked {\n if (y == 0) return 0;\n\n require (x >= 0);\n\n uint256 lo = (uint256 (int256 (x)) * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) >> 64;\n uint256 hi = uint256 (int256 (x)) * (y >> 128);\n\n require (hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);\n hi <<= 64;\n\n require (hi <=\n 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - lo);\n return hi + lo;\n }\n }\n\n /**\n * Calculate x / y rounding towards zero. Revert on overflow or when y is\n * zero.\n *\n * @param x signed 64.64-bit fixed point number\n * @param y signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function div (int128 x, int128 y) internal pure returns (int128) {\n unchecked {\n require (y != 0);\n int256 result = (int256 (x) << 64) / y;\n require (result >= MIN_64x64 && result <= MAX_64x64);\n return int128 (result);\n }\n }\n\n /**\n * Calculate x / y rounding towards zero, where x and y are signed 256-bit\n * integer numbers. Revert on overflow or when y is zero.\n *\n * @param x signed 256-bit integer number\n * @param y signed 256-bit integer number\n * @return signed 64.64-bit fixed point number\n */\n function divi (int256 x, int256 y) internal pure returns (int128) {\n unchecked {\n require (y != 0);\n\n bool negativeResult = false;\n if (x < 0) {\n x = -x; // We rely on overflow behavior here\n negativeResult = true;\n }\n if (y < 0) {\n y = -y; // We rely on overflow behavior here\n negativeResult = !negativeResult;\n }\n uint128 absoluteResult = divuu (uint256 (x), uint256 (y));\n if (negativeResult) {\n require (absoluteResult <= 0x80000000000000000000000000000000);\n return -int128 (absoluteResult); // We rely on overflow behavior here\n } else {\n require (absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);\n return int128 (absoluteResult); // We rely on overflow behavior here\n }\n }\n }\n\n /**\n * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit\n * integer numbers. Revert on overflow or when y is zero.\n *\n * @param x unsigned 256-bit integer number\n * @param y unsigned 256-bit integer number\n * @return signed 64.64-bit fixed point number\n */\n function divu (uint256 x, uint256 y) internal pure returns (int128) {\n unchecked {\n require (y != 0);\n uint128 result = divuu (x, y);\n require (result <= uint128 (MAX_64x64));\n return int128 (result);\n }\n }\n\n /**\n * Calculate -x. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function neg (int128 x) internal pure returns (int128) {\n unchecked {\n require (x != MIN_64x64);\n return -x;\n }\n }\n\n /**\n * Calculate |x|. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function abs (int128 x) internal pure returns (int128) {\n unchecked {\n require (x != MIN_64x64);\n return x < 0 ? -x : x;\n }\n }\n\n /**\n * Calculate 1 / x rounding towards zero. Revert on overflow or when x is\n * zero.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function inv (int128 x) internal pure returns (int128) {\n unchecked {\n require (x != 0);\n int256 result = int256 (0x100000000000000000000000000000000) / x;\n require (result >= MIN_64x64 && result <= MAX_64x64);\n return int128 (result);\n }\n }\n\n /**\n * Calculate arithmetics average of x and y, i.e. (x + y) / 2 rounding down.\n *\n * @param x signed 64.64-bit fixed point number\n * @param y signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function avg (int128 x, int128 y) internal pure returns (int128) {\n unchecked {\n return int128 ((int256 (x) + int256 (y)) >> 1);\n }\n }\n\n /**\n * Calculate geometric average of x and y, i.e. sqrt (x * y) rounding down.\n * Revert on overflow or in case x * y is negative.\n *\n * @param x signed 64.64-bit fixed point number\n * @param y signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function gavg (int128 x, int128 y) internal pure returns (int128) {\n unchecked {\n int256 m = int256 (x) * int256 (y);\n require (m >= 0);\n require (m <\n 0x4000000000000000000000000000000000000000000000000000000000000000);\n return int128 (sqrtu (uint256 (m)));\n }\n }\n\n /**\n * Calculate x^y assuming 0^0 is 1, where x is signed 64.64 fixed point number\n * and y is unsigned 256-bit integer number. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @param y uint256 value\n * @return signed 64.64-bit fixed point number\n */\n function pow (int128 x, uint256 y) internal pure returns (int128) {\n unchecked {\n bool negative = x < 0 && y & 1 == 1;\n\n uint256 absX = uint128 (x < 0 ? -x : x);\n uint256 absResult;\n absResult = 0x100000000000000000000000000000000;\n\n if (absX <= 0x10000000000000000) {\n absX <<= 63;\n while (y != 0) {\n if (y & 0x1 != 0) {\n absResult = absResult * absX >> 127;\n }\n absX = absX * absX >> 127;\n\n if (y & 0x2 != 0) {\n absResult = absResult * absX >> 127;\n }\n absX = absX * absX >> 127;\n\n if (y & 0x4 != 0) {\n absResult = absResult * absX >> 127;\n }\n absX = absX * absX >> 127;\n\n if (y & 0x8 != 0) {\n absResult = absResult * absX >> 127;\n }\n absX = absX * absX >> 127;\n\n y >>= 4;\n }\n\n absResult >>= 64;\n } else {\n uint256 absXShift = 63;\n if (absX < 0x1000000000000000000000000) { absX <<= 32; absXShift -= 32; }\n if (absX < 0x10000000000000000000000000000) { absX <<= 16; absXShift -= 16; }\n if (absX < 0x1000000000000000000000000000000) { absX <<= 8; absXShift -= 8; }\n if (absX < 0x10000000000000000000000000000000) { absX <<= 4; absXShift -= 4; }\n if (absX < 0x40000000000000000000000000000000) { absX <<= 2; absXShift -= 2; }\n if (absX < 0x80000000000000000000000000000000) { absX <<= 1; absXShift -= 1; }\n\n uint256 resultShift = 0;\n while (y != 0) {\n require (absXShift < 64);\n\n if (y & 0x1 != 0) {\n absResult = absResult * absX >> 127;\n resultShift += absXShift;\n if (absResult > 0x100000000000000000000000000000000) {\n absResult >>= 1;\n resultShift += 1;\n }\n }\n absX = absX * absX >> 127;\n absXShift <<= 1;\n if (absX >= 0x100000000000000000000000000000000) {\n absX >>= 1;\n absXShift += 1;\n }\n\n y >>= 1;\n }\n\n require (resultShift < 64);\n absResult >>= 64 - resultShift;\n }\n int256 result = negative ? -int256 (absResult) : int256 (absResult);\n require (result >= MIN_64x64 && result <= MAX_64x64);\n return int128 (result);\n }\n }\n\n /**\n * Calculate sqrt (x) rounding down. Revert if x < 0.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function sqrt (int128 x) internal pure returns (int128) {\n unchecked {\n require (x >= 0);\n return int128 (sqrtu (uint256 (int256 (x)) << 64));\n }\n }\n\n /**\n * Calculate binary logarithm of x. Revert if x <= 0.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function log_2 (int128 x) internal pure returns (int128) {\n unchecked {\n require (x > 0);\n\n int256 msb = 0;\n int256 xc = x;\n if (xc >= 0x10000000000000000) { xc >>= 64; msb += 64; }\n if (xc >= 0x100000000) { xc >>= 32; msb += 32; }\n if (xc >= 0x10000) { xc >>= 16; msb += 16; }\n if (xc >= 0x100) { xc >>= 8; msb += 8; }\n if (xc >= 0x10) { xc >>= 4; msb += 4; }\n if (xc >= 0x4) { xc >>= 2; msb += 2; }\n if (xc >= 0x2) msb += 1; // No need to shift xc anymore\n\n int256 result = msb - 64 << 64;\n uint256 ux = uint256 (int256 (x)) << uint256 (127 - msb);\n for (int256 bit = 0x8000000000000000; bit > 0; bit >>= 1) {\n ux *= ux;\n uint256 b = ux >> 255;\n ux >>= 127 + b;\n result += bit * int256 (b);\n }\n\n return int128 (result);\n }\n }\n\n /**\n * Calculate natural logarithm of x. Revert if x <= 0.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function ln (int128 x) internal pure returns (int128) {\n unchecked {\n require (x > 0);\n\n return int128 (int256 (\n uint256 (int256 (log_2 (x))) * 0xB17217F7D1CF79ABC9E3B39803F2F6AF >> 128));\n }\n }\n\n /**\n * Calculate binary exponent of x. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function exp_2 (int128 x) internal pure returns (int128) {\n unchecked {\n require (x < 0x400000000000000000); // Overflow\n\n if (x < -0x400000000000000000) return 0; // Underflow\n\n uint256 result = 0x80000000000000000000000000000000;\n\n if (x & 0x8000000000000000 > 0)\n result = result * 0x16A09E667F3BCC908B2FB1366EA957D3E >> 128;\n if (x & 0x4000000000000000 > 0)\n result = result * 0x1306FE0A31B7152DE8D5A46305C85EDEC >> 128;\n if (x & 0x2000000000000000 > 0)\n result = result * 0x1172B83C7D517ADCDF7C8C50EB14A791F >> 128;\n if (x & 0x1000000000000000 > 0)\n result = result * 0x10B5586CF9890F6298B92B71842A98363 >> 128;\n if (x & 0x800000000000000 > 0)\n result = result * 0x1059B0D31585743AE7C548EB68CA417FD >> 128;\n if (x & 0x400000000000000 > 0)\n result = result * 0x102C9A3E778060EE6F7CACA4F7A29BDE8 >> 128;\n if (x & 0x200000000000000 > 0)\n result = result * 0x10163DA9FB33356D84A66AE336DCDFA3F >> 128;\n if (x & 0x100000000000000 > 0)\n result = result * 0x100B1AFA5ABCBED6129AB13EC11DC9543 >> 128;\n if (x & 0x80000000000000 > 0)\n result = result * 0x10058C86DA1C09EA1FF19D294CF2F679B >> 128;\n if (x & 0x40000000000000 > 0)\n result = result * 0x1002C605E2E8CEC506D21BFC89A23A00F >> 128;\n if (x & 0x20000000000000 > 0)\n result = result * 0x100162F3904051FA128BCA9C55C31E5DF >> 128;\n if (x & 0x10000000000000 > 0)\n result = result * 0x1000B175EFFDC76BA38E31671CA939725 >> 128;\n if (x & 0x8000000000000 > 0)\n result = result * 0x100058BA01FB9F96D6CACD4B180917C3D >> 128;\n if (x & 0x4000000000000 > 0)\n result = result * 0x10002C5CC37DA9491D0985C348C68E7B3 >> 128;\n if (x & 0x2000000000000 > 0)\n result = result * 0x1000162E525EE054754457D5995292026 >> 128;\n if (x & 0x1000000000000 > 0)\n result = result * 0x10000B17255775C040618BF4A4ADE83FC >> 128;\n if (x & 0x800000000000 > 0)\n result = result * 0x1000058B91B5BC9AE2EED81E9B7D4CFAB >> 128;\n if (x & 0x400000000000 > 0)\n result = result * 0x100002C5C89D5EC6CA4D7C8ACC017B7C9 >> 128;\n if (x & 0x200000000000 > 0)\n result = result * 0x10000162E43F4F831060E02D839A9D16D >> 128;\n if (x & 0x100000000000 > 0)\n result = result * 0x100000B1721BCFC99D9F890EA06911763 >> 128;\n if (x & 0x80000000000 > 0)\n result = result * 0x10000058B90CF1E6D97F9CA14DBCC1628 >> 128;\n if (x & 0x40000000000 > 0)\n result = result * 0x1000002C5C863B73F016468F6BAC5CA2B >> 128;\n if (x & 0x20000000000 > 0)\n result = result * 0x100000162E430E5A18F6119E3C02282A5 >> 128;\n if (x & 0x10000000000 > 0)\n result = result * 0x1000000B1721835514B86E6D96EFD1BFE >> 128;\n if (x & 0x8000000000 > 0)\n result = result * 0x100000058B90C0B48C6BE5DF846C5B2EF >> 128;\n if (x & 0x4000000000 > 0)\n result = result * 0x10000002C5C8601CC6B9E94213C72737A >> 128;\n if (x & 0x2000000000 > 0)\n result = result * 0x1000000162E42FFF037DF38AA2B219F06 >> 128;\n if (x & 0x1000000000 > 0)\n result = result * 0x10000000B17217FBA9C739AA5819F44F9 >> 128;\n if (x & 0x800000000 > 0)\n result = result * 0x1000000058B90BFCDEE5ACD3C1CEDC823 >> 128;\n if (x & 0x400000000 > 0)\n result = result * 0x100000002C5C85FE31F35A6A30DA1BE50 >> 128;\n if (x & 0x200000000 > 0)\n result = result * 0x10000000162E42FF0999CE3541B9FFFCF >> 128;\n if (x & 0x100000000 > 0)\n result = result * 0x100000000B17217F80F4EF5AADDA45554 >> 128;\n if (x & 0x80000000 > 0)\n result = result * 0x10000000058B90BFBF8479BD5A81B51AD >> 128;\n if (x & 0x40000000 > 0)\n result = result * 0x1000000002C5C85FDF84BD62AE30A74CC >> 128;\n if (x & 0x20000000 > 0)\n result = result * 0x100000000162E42FEFB2FED257559BDAA >> 128;\n if (x & 0x10000000 > 0)\n result = result * 0x1000000000B17217F7D5A7716BBA4A9AE >> 128;\n if (x & 0x8000000 > 0)\n result = result * 0x100000000058B90BFBE9DDBAC5E109CCE >> 128;\n if (x & 0x4000000 > 0)\n result = result * 0x10000000002C5C85FDF4B15DE6F17EB0D >> 128;\n if (x & 0x2000000 > 0)\n result = result * 0x1000000000162E42FEFA494F1478FDE05 >> 128;\n if (x & 0x1000000 > 0)\n result = result * 0x10000000000B17217F7D20CF927C8E94C >> 128;\n if (x & 0x800000 > 0)\n result = result * 0x1000000000058B90BFBE8F71CB4E4B33D >> 128;\n if (x & 0x400000 > 0)\n result = result * 0x100000000002C5C85FDF477B662B26945 >> 128;\n if (x & 0x200000 > 0)\n result = result * 0x10000000000162E42FEFA3AE53369388C >> 128;\n if (x & 0x100000 > 0)\n result = result * 0x100000000000B17217F7D1D351A389D40 >> 128;\n if (x & 0x80000 > 0)\n result = result * 0x10000000000058B90BFBE8E8B2D3D4EDE >> 128;\n if (x & 0x40000 > 0)\n result = result * 0x1000000000002C5C85FDF4741BEA6E77E >> 128;\n if (x & 0x20000 > 0)\n result = result * 0x100000000000162E42FEFA39FE95583C2 >> 128;\n if (x & 0x10000 > 0)\n result = result * 0x1000000000000B17217F7D1CFB72B45E1 >> 128;\n if (x & 0x8000 > 0)\n result = result * 0x100000000000058B90BFBE8E7CC35C3F0 >> 128;\n if (x & 0x4000 > 0)\n result = result * 0x10000000000002C5C85FDF473E242EA38 >> 128;\n if (x & 0x2000 > 0)\n result = result * 0x1000000000000162E42FEFA39F02B772C >> 128;\n if (x & 0x1000 > 0)\n result = result * 0x10000000000000B17217F7D1CF7D83C1A >> 128;\n if (x & 0x800 > 0)\n result = result * 0x1000000000000058B90BFBE8E7BDCBE2E >> 128;\n if (x & 0x400 > 0)\n result = result * 0x100000000000002C5C85FDF473DEA871F >> 128;\n if (x & 0x200 > 0)\n result = result * 0x10000000000000162E42FEFA39EF44D91 >> 128;\n if (x & 0x100 > 0)\n result = result * 0x100000000000000B17217F7D1CF79E949 >> 128;\n if (x & 0x80 > 0)\n result = result * 0x10000000000000058B90BFBE8E7BCE544 >> 128;\n if (x & 0x40 > 0)\n result = result * 0x1000000000000002C5C85FDF473DE6ECA >> 128;\n if (x & 0x20 > 0)\n result = result * 0x100000000000000162E42FEFA39EF366F >> 128;\n if (x & 0x10 > 0)\n result = result * 0x1000000000000000B17217F7D1CF79AFA >> 128;\n if (x & 0x8 > 0)\n result = result * 0x100000000000000058B90BFBE8E7BCD6D >> 128;\n if (x & 0x4 > 0)\n result = result * 0x10000000000000002C5C85FDF473DE6B2 >> 128;\n if (x & 0x2 > 0)\n result = result * 0x1000000000000000162E42FEFA39EF358 >> 128;\n if (x & 0x1 > 0)\n result = result * 0x10000000000000000B17217F7D1CF79AB >> 128;\n\n result >>= uint256 (int256 (63 - (x >> 64)));\n require (result <= uint256 (int256 (MAX_64x64)));\n\n return int128 (int256 (result));\n }\n }\n\n /**\n * Calculate natural exponent of x. Revert on overflow.\n *\n * @param x signed 64.64-bit fixed point number\n * @return signed 64.64-bit fixed point number\n */\n function exp (int128 x) internal pure returns (int128) {\n unchecked {\n require (x < 0x400000000000000000); // Overflow\n\n if (x < -0x400000000000000000) return 0; // Underflow\n\n return exp_2 (\n int128 (int256 (x) * 0x171547652B82FE1777D0FFDA0D23A7D12 >> 128));\n }\n }\n\n /**\n * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit\n * integer numbers. Revert on overflow or when y is zero.\n *\n * @param x unsigned 256-bit integer number\n * @param y unsigned 256-bit integer number\n * @return unsigned 64.64-bit fixed point number\n */\n function divuu (uint256 x, uint256 y) private pure returns (uint128) {\n unchecked {\n require (y != 0);\n\n uint256 result;\n\n if (x <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)\n result = (x << 64) / y;\n else {\n uint256 msb = 192;\n uint256 xc = x >> 192;\n if (xc >= 0x100000000) { xc >>= 32; msb += 32; }\n if (xc >= 0x10000) { xc >>= 16; msb += 16; }\n if (xc >= 0x100) { xc >>= 8; msb += 8; }\n if (xc >= 0x10) { xc >>= 4; msb += 4; }\n if (xc >= 0x4) { xc >>= 2; msb += 2; }\n if (xc >= 0x2) msb += 1; // No need to shift xc anymore\n\n result = (x << 255 - msb) / ((y - 1 >> msb - 191) + 1);\n require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);\n\n uint256 hi = result * (y >> 128);\n uint256 lo = result * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);\n\n uint256 xh = x >> 192;\n uint256 xl = x << 64;\n\n if (xl < lo) xh -= 1;\n xl -= lo; // We rely on overflow behavior here\n lo = hi << 128;\n if (xl < lo) xh -= 1;\n xl -= lo; // We rely on overflow behavior here\n\n assert (xh == hi >> 128);\n\n result += xl / y;\n }\n\n require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);\n return uint128 (result);\n }\n }\n\n /**\n * Calculate sqrt (x) rounding down, where x is unsigned 256-bit integer\n * number.\n *\n * @param x unsigned 256-bit integer number\n * @return unsigned 128-bit integer number\n */\n function sqrtu (uint256 x) private pure returns (uint128) {\n unchecked {\n if (x == 0) return 0;\n else {\n uint256 xx = x;\n uint256 r = 1;\n if (xx >= 0x100000000000000000000000000000000) { xx >>= 128; r <<= 64; }\n if (xx >= 0x10000000000000000) { xx >>= 64; r <<= 32; }\n if (xx >= 0x100000000) { xx >>= 32; r <<= 16; }\n if (xx >= 0x10000) { xx >>= 16; r <<= 8; }\n if (xx >= 0x100) { xx >>= 8; r <<= 4; }\n if (xx >= 0x10) { xx >>= 4; r <<= 2; }\n if (xx >= 0x8) { r <<= 1; }\n r = (r + x / r) >> 1;\n r = (r + x / r) >> 1;\n r = (r + x / r) >> 1;\n r = (r + x / r) >> 1;\n r = (r + x / r) >> 1;\n r = (r + x / r) >> 1;\n r = (r + x / r) >> 1; // Seven iterations should be enough\n uint256 r1 = x / r;\n return uint128 (r < r1 ? r : r1);\n }\n }\n }\n}\n"},"@solidstate/contracts/token/ERC20/metadata/IERC20Metadata.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @title ERC20 metadata interface\n */\ninterface IERC20Metadata {\n /**\n * @notice return token name\n * @return token name\n */\n function name() external view returns (string memory);\n\n /**\n * @notice return token symbol\n * @return token symbol\n */\n function symbol() external view returns (string memory);\n\n /**\n * @notice return token decimals, generally used only for display purposes\n * @return token decimals\n */\n function decimals() external view returns (uint8);\n}\n"},"contracts/staking/FeeDiscountStorage.sol":{"content":"// SPDX-License-Identifier: BUSL-1.1\r\n// For further clarification please see https://license.premia.legal\r\n\r\npragma solidity ^0.8.0;\r\n\r\nlibrary FeeDiscountStorage {\r\n bytes32 internal constant STORAGE_SLOT =\r\n keccak256(\"premia.contracts.staking.PremiaFeeDiscount\");\r\n\r\n struct UserInfo {\r\n uint256 balance; // Balance staked by user\r\n uint64 stakePeriod; // Stake period selected by user\r\n uint64 lockedUntil; // Timestamp at which the lock ends\r\n }\r\n\r\n struct Layout {\r\n // User data with xPREMIA balance staked and date at which lock ends\r\n mapping(address => UserInfo) userInfo;\r\n }\r\n\r\n function layout() internal pure returns (Layout storage l) {\r\n bytes32 slot = STORAGE_SLOT;\r\n assembly {\r\n l.slot := slot\r\n }\r\n }\r\n}\r\n"},"contracts/libraries/OptionMath.sol":{"content":"// SPDX-License-Identifier: BUSL-1.1\r\n// For further clarification please see https://license.premia.legal\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport {ABDKMath64x64} from \"abdk-libraries-solidity/ABDKMath64x64.sol\";\r\n\r\nlibrary OptionMath {\r\n using ABDKMath64x64 for int128;\r\n\r\n struct QuoteArgs {\r\n int128 varianceAnnualized64x64; // 64x64 fixed point representation of annualized variance\r\n int128 strike64x64; // 64x64 fixed point representation of strike price\r\n int128 spot64x64; // 64x64 fixed point representation of spot price\r\n int128 timeToMaturity64x64; // 64x64 fixed point representation of duration of option contract (in years)\r\n int128 oldCLevel64x64; // 64x64 fixed point representation of C-Level of Pool before purchase\r\n int128 oldPoolState; // 64x64 fixed point representation of current state of the pool\r\n int128 newPoolState; // 64x64 fixed point representation of state of the pool after trade\r\n int128 steepness64x64; // 64x64 fixed point representation of Pool state delta multiplier\r\n int128 minAPY64x64; // 64x64 fixed point representation of minimum APY for capital locked up to underwrite options\r\n bool isCall; // whether to price \"call\" or \"put\" option\r\n }\r\n\r\n struct CalculateCLevelDecayArgs {\r\n int128 timeIntervalsElapsed64x64; // 64x64 fixed point representation of quantity of discrete arbitrary intervals elapsed since last update\r\n int128 oldCLevel64x64; // 64x64 fixed point representation of C-Level prior to accounting for decay\r\n int128 utilization64x64; // 64x64 fixed point representation of pool capital utilization rate\r\n int128 utilizationLowerBound64x64;\r\n int128 utilizationUpperBound64x64;\r\n int128 cLevelLowerBound64x64;\r\n int128 cLevelUpperBound64x64;\r\n int128 cConvergenceULowerBound64x64;\r\n int128 cConvergenceUUpperBound64x64;\r\n }\r\n\r\n // 64x64 fixed point integer constants\r\n int128 internal constant ONE_64x64 = 0x10000000000000000;\r\n int128 internal constant THREE_64x64 = 0x30000000000000000;\r\n\r\n // 64x64 fixed point constants used in Choudhury’s approximation of the Black-Scholes CDF\r\n int128 private constant CDF_CONST_0 = 0x09109f285df452394; // 2260 / 3989\r\n int128 private constant CDF_CONST_1 = 0x19abac0ea1da65036; // 6400 / 3989\r\n int128 private constant CDF_CONST_2 = 0x0d3c84b78b749bd6b; // 3300 / 3989\r\n\r\n /**\r\n * @notice recalculate C-Level based on change in liquidity\r\n * @param initialCLevel64x64 64x64 fixed point representation of C-Level of Pool before update\r\n * @param oldPoolState64x64 64x64 fixed point representation of liquidity in pool before update\r\n * @param newPoolState64x64 64x64 fixed point representation of liquidity in pool after update\r\n * @param steepness64x64 64x64 fixed point representation of steepness coefficient\r\n * @return 64x64 fixed point representation of new C-Level\r\n */\r\n function calculateCLevel(\r\n int128 initialCLevel64x64,\r\n int128 oldPoolState64x64,\r\n int128 newPoolState64x64,\r\n int128 steepness64x64\r\n ) external pure returns (int128) {\r\n return\r\n newPoolState64x64\r\n .sub(oldPoolState64x64)\r\n .div(\r\n oldPoolState64x64 > newPoolState64x64\r\n ? oldPoolState64x64\r\n : newPoolState64x64\r\n )\r\n .mul(steepness64x64)\r\n .neg()\r\n .exp()\r\n .mul(initialCLevel64x64);\r\n }\r\n\r\n /**\r\n * @notice calculate the price of an option using the Premia Finance model\r\n * @param args arguments of quotePrice\r\n * @return premiaPrice64x64 64x64 fixed point representation of Premia option price\r\n * @return cLevel64x64 64x64 fixed point representation of C-Level of Pool after purchase\r\n */\r\n function quotePrice(QuoteArgs memory args)\r\n external\r\n pure\r\n returns (\r\n int128 premiaPrice64x64,\r\n int128 cLevel64x64,\r\n int128 slippageCoefficient64x64\r\n )\r\n {\r\n int128 deltaPoolState64x64 = args\r\n .newPoolState\r\n .sub(args.oldPoolState)\r\n .div(args.oldPoolState)\r\n .mul(args.steepness64x64);\r\n int128 tradingDelta64x64 = deltaPoolState64x64.neg().exp();\r\n\r\n int128 blackScholesPrice64x64 = _blackScholesPrice(\r\n args.varianceAnnualized64x64,\r\n args.strike64x64,\r\n args.spot64x64,\r\n args.timeToMaturity64x64,\r\n args.isCall\r\n );\r\n\r\n cLevel64x64 = tradingDelta64x64.mul(args.oldCLevel64x64);\r\n slippageCoefficient64x64 = ONE_64x64.sub(tradingDelta64x64).div(\r\n deltaPoolState64x64\r\n );\r\n\r\n premiaPrice64x64 = blackScholesPrice64x64.mul(cLevel64x64).mul(\r\n slippageCoefficient64x64\r\n );\r\n\r\n int128 intrinsicValue64x64;\r\n\r\n if (args.isCall && args.strike64x64 < args.spot64x64) {\r\n intrinsicValue64x64 = args.spot64x64.sub(args.strike64x64);\r\n } else if (!args.isCall && args.strike64x64 > args.spot64x64) {\r\n intrinsicValue64x64 = args.strike64x64.sub(args.spot64x64);\r\n }\r\n\r\n int128 collateralValue64x64 = args.isCall\r\n ? args.spot64x64\r\n : args.strike64x64;\r\n\r\n int128 minPrice64x64 = intrinsicValue64x64.add(\r\n collateralValue64x64.mul(args.minAPY64x64).mul(\r\n args.timeToMaturity64x64\r\n )\r\n );\r\n\r\n if (minPrice64x64 > premiaPrice64x64) {\r\n premiaPrice64x64 = minPrice64x64;\r\n }\r\n }\r\n\r\n /**\r\n * @notice calculate the decay of C-Level based on heat diffusion function\r\n * @param args structured CalculateCLevelDecayArgs\r\n * @return cLevelDecayed64x64 C-Level after accounting for decay\r\n */\r\n function calculateCLevelDecay(CalculateCLevelDecayArgs memory args)\r\n external\r\n pure\r\n returns (int128 cLevelDecayed64x64)\r\n {\r\n int128 convFHighU64x64 = (args.utilization64x64 >=\r\n args.utilizationUpperBound64x64 &&\r\n args.oldCLevel64x64 <= args.cLevelLowerBound64x64)\r\n ? ONE_64x64\r\n : int128(0);\r\n\r\n int128 convFLowU64x64 = (args.utilization64x64 <=\r\n args.utilizationLowerBound64x64 &&\r\n args.oldCLevel64x64 >= args.cLevelUpperBound64x64)\r\n ? ONE_64x64\r\n : int128(0);\r\n\r\n cLevelDecayed64x64 = args\r\n .oldCLevel64x64\r\n .sub(args.cConvergenceULowerBound64x64.mul(convFLowU64x64))\r\n .sub(args.cConvergenceUUpperBound64x64.mul(convFHighU64x64))\r\n .mul(\r\n convFLowU64x64\r\n .mul(ONE_64x64.sub(args.utilization64x64))\r\n .add(convFHighU64x64.mul(args.utilization64x64))\r\n .mul(args.timeIntervalsElapsed64x64)\r\n .neg()\r\n .exp()\r\n )\r\n .add(\r\n args.cConvergenceULowerBound64x64.mul(convFLowU64x64).add(\r\n args.cConvergenceUUpperBound64x64.mul(convFHighU64x64)\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * @notice calculate the exponential decay coefficient for a given interval\r\n * @param oldTimestamp timestamp of previous update\r\n * @param newTimestamp current timestamp\r\n * @return 64x64 fixed point representation of exponential decay coefficient\r\n */\r\n function _decay(uint256 oldTimestamp, uint256 newTimestamp)\r\n internal\r\n pure\r\n returns (int128)\r\n {\r\n return\r\n ONE_64x64.sub(\r\n (-ABDKMath64x64.divu(newTimestamp - oldTimestamp, 7 days)).exp()\r\n );\r\n }\r\n\r\n /**\r\n * @notice calculate Choudhury’s approximation of the Black-Scholes CDF\r\n * @param input64x64 64x64 fixed point representation of random variable\r\n * @return 64x64 fixed point representation of the approximated CDF of x\r\n */\r\n function _N(int128 input64x64) internal pure returns (int128) {\r\n // squaring via mul is cheaper than via pow\r\n int128 inputSquared64x64 = input64x64.mul(input64x64);\r\n\r\n int128 value64x64 = (-inputSquared64x64 >> 1).exp().div(\r\n CDF_CONST_0.add(CDF_CONST_1.mul(input64x64.abs())).add(\r\n CDF_CONST_2.mul(inputSquared64x64.add(THREE_64x64).sqrt())\r\n )\r\n );\r\n\r\n return input64x64 > 0 ? ONE_64x64.sub(value64x64) : value64x64;\r\n }\r\n\r\n /**\r\n * @notice calculate the price of an option using the Black-Scholes model\r\n * @param varianceAnnualized64x64 64x64 fixed point representation of annualized variance\r\n * @param strike64x64 64x64 fixed point representation of strike price\r\n * @param spot64x64 64x64 fixed point representation of spot price\r\n * @param timeToMaturity64x64 64x64 fixed point representation of duration of option contract (in years)\r\n * @param isCall whether to price \"call\" or \"put\" option\r\n * @return 64x64 fixed point representation of Black-Scholes option price\r\n */\r\n function _blackScholesPrice(\r\n int128 varianceAnnualized64x64,\r\n int128 strike64x64,\r\n int128 spot64x64,\r\n int128 timeToMaturity64x64,\r\n bool isCall\r\n ) internal pure returns (int128) {\r\n int128 cumulativeVariance64x64 = timeToMaturity64x64.mul(\r\n varianceAnnualized64x64\r\n );\r\n int128 cumulativeVarianceSqrt64x64 = cumulativeVariance64x64.sqrt();\r\n\r\n int128 d1_64x64 = spot64x64\r\n .div(strike64x64)\r\n .ln()\r\n .add(cumulativeVariance64x64 >> 1)\r\n .div(cumulativeVarianceSqrt64x64);\r\n int128 d2_64x64 = d1_64x64.sub(cumulativeVarianceSqrt64x64);\r\n\r\n if (isCall) {\r\n return\r\n spot64x64.mul(_N(d1_64x64)).sub(strike64x64.mul(_N(d2_64x64)));\r\n } else {\r\n return\r\n -spot64x64.mul(_N(-d1_64x64)).sub(\r\n strike64x64.mul(_N(-d2_64x64))\r\n );\r\n }\r\n }\r\n}\r\n"},"@solidstate/contracts/access/IERC173.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @title Contract ownership standard interface\n * @dev see https://eips.ethereum.org/EIPS/eip-173\n */\ninterface IERC173 {\n event OwnershipTransferred(\n address indexed previousOwner,\n address indexed newOwner\n );\n\n /**\n * @notice get the ERC173 contract owner\n * @return conract owner\n */\n function owner() external view returns (address);\n\n /**\n * @notice transfer contract ownership to new account\n * @param account address of new owner\n */\n function transferOwnership(address account) external;\n}\n"},"contracts/libraries/ABDKMath64x64Token.sol":{"content":"// SPDX-License-Identifier: BUSL-1.1\r\n// For further clarification please see https://license.premia.legal\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport {ABDKMath64x64} from \"abdk-libraries-solidity/ABDKMath64x64.sol\";\r\n\r\nlibrary ABDKMath64x64Token {\r\n using ABDKMath64x64 for int128;\r\n\r\n /**\r\n * @notice convert 64x64 fixed point representation of token amount to decimal\r\n * @param value64x64 64x64 fixed point representation of token amount\r\n * @param decimals token display decimals\r\n * @return value decimal representation of token amount\r\n */\r\n function toDecimals(int128 value64x64, uint8 decimals)\r\n internal\r\n pure\r\n returns (uint256 value)\r\n {\r\n value = value64x64.mulu(10**decimals);\r\n }\r\n\r\n /**\r\n * @notice convert decimal representation of token amount to 64x64 fixed point\r\n * @param value decimal representation of token amount\r\n * @param decimals token display decimals\r\n * @return value64x64 64x64 fixed point representation of token amount\r\n */\r\n function fromDecimals(uint256 value, uint8 decimals)\r\n internal\r\n pure\r\n returns (int128 value64x64)\r\n {\r\n value64x64 = ABDKMath64x64.divu(value, 10**decimals);\r\n }\r\n\r\n /**\r\n * @notice convert 64x64 fixed point representation of token amount to wei (18 decimals)\r\n * @param value64x64 64x64 fixed point representation of token amount\r\n * @return value wei representation of token amount\r\n */\r\n function toWei(int128 value64x64) internal pure returns (uint256 value) {\r\n value = toDecimals(value64x64, 18);\r\n }\r\n\r\n /**\r\n * @notice convert wei representation (18 decimals) of token amount to 64x64 fixed point\r\n * @param value wei representation of token amount\r\n * @return value64x64 64x64 fixed point representation of token amount\r\n */\r\n function fromWei(uint256 value) internal pure returns (int128 value64x64) {\r\n value64x64 = fromDecimals(value, 18);\r\n }\r\n}\r\n"},"@solidstate/contracts/token/ERC1155/IERC1155.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC1155Internal } from './IERC1155Internal.sol';\nimport { IERC165 } from '../../introspection/IERC165.sol';\n\n/**\n * @notice ERC1155 interface\n * @dev see https://github.com/ethereum/EIPs/issues/1155\n */\ninterface IERC1155 is IERC1155Internal, IERC165 {\n /**\n * @notice query the balance of given token held by given address\n * @param account address to query\n * @param id token to query\n * @return token balance\n */\n function balanceOf(address account, uint256 id)\n external\n view\n returns (uint256);\n\n /**\n * @notice query the balances of given tokens held by given addresses\n * @param accounts addresss to query\n * @param ids tokens to query\n * @return token balances\n */\n function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)\n external\n view\n returns (uint256[] memory);\n\n /**\n * @notice query approval status of given operator with respect to given address\n * @param account address to query for approval granted\n * @param operator address to query for approval received\n * @return whether operator is approved to spend tokens held by account\n */\n function isApprovedForAll(address account, address operator)\n external\n view\n returns (bool);\n\n /**\n * @notice grant approval to or revoke approval from given operator to spend held tokens\n * @param operator address whose approval status to update\n * @param status whether operator should be considered approved\n */\n function setApprovalForAll(address operator, bool status) external;\n\n /**\n * @notice transfer tokens between given addresses, checking for ERC1155Receiver implementation if applicable\n * @param from sender of tokens\n * @param to receiver of tokens\n * @param id token ID\n * @param amount quantity of tokens to transfer\n * @param data data payload\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 id,\n uint256 amount,\n bytes calldata data\n ) external;\n\n /**\n * @notice transfer batch of tokens between given addresses, checking for ERC1155Receiver implementation if applicable\n * @param from sender of tokens\n * @param to receiver of tokens\n * @param ids list of token IDs\n * @param amounts list of quantities of tokens to transfer\n * @param data data payload\n */\n function safeBatchTransferFrom(\n address from,\n address to,\n uint256[] calldata ids,\n uint256[] calldata amounts,\n bytes calldata data\n ) external;\n}\n"},"contracts/staking/IFeeDiscount.sol":{"content":"// SPDX-License-Identifier: LGPL-3.0-or-later\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport {FeeDiscountStorage} from \"./FeeDiscountStorage.sol\";\r\n\r\ninterface IFeeDiscount {\r\n event Staked(\r\n address indexed user,\r\n uint256 amount,\r\n uint256 stakePeriod,\r\n uint256 lockedUntil\r\n );\r\n event Unstaked(address indexed user, uint256 amount);\r\n\r\n struct StakeLevel {\r\n uint256 amount; // Amount to stake\r\n uint256 discount; // Discount when amount is reached\r\n }\r\n\r\n /**\r\n * @notice Stake using IERC2612 permit\r\n * @param amount The amount of xPremia to stake\r\n * @param period The lockup period (in seconds)\r\n * @param deadline Deadline after which permit will fail\r\n * @param v V\r\n * @param r R\r\n * @param s S\r\n */\r\n function stakeWithPermit(\r\n uint256 amount,\r\n uint256 period,\r\n uint256 deadline,\r\n uint8 v,\r\n bytes32 r,\r\n bytes32 s\r\n ) external;\r\n\r\n /**\r\n * @notice Lockup xPremia for protocol fee discounts\r\n * Longer period of locking will apply a multiplier on the amount staked, in the fee discount calculation\r\n * @param amount The amount of xPremia to stake\r\n * @param period The lockup period (in seconds)\r\n */\r\n function stake(uint256 amount, uint256 period) external;\r\n\r\n /**\r\n * @notice Unstake xPremia (If lockup period has ended)\r\n * @param amount The amount of xPremia to unstake\r\n */\r\n function unstake(uint256 amount) external;\r\n\r\n //////////\r\n // View //\r\n //////////\r\n\r\n /**\r\n * Calculate the stake amount of a user, after applying the bonus from the lockup period chosen\r\n * @param user The user from which to query the stake amount\r\n * @return The user stake amount after applying the bonus\r\n */\r\n function getStakeAmountWithBonus(address user)\r\n external\r\n view\r\n returns (uint256);\r\n\r\n /**\r\n * @notice Calculate the % of fee discount for user, based on his stake\r\n * @param user The _user for which the discount is for\r\n * @return Percentage of protocol fee discount (in basis point)\r\n * Ex : 1000 = 10% fee discount\r\n */\r\n function getDiscount(address user) external view returns (uint256);\r\n\r\n /**\r\n * @notice Get stake levels\r\n * @return Stake levels\r\n * Ex : 2500 = -25%\r\n */\r\n function getStakeLevels() external returns (StakeLevel[] memory);\r\n\r\n /**\r\n * @notice Get stake period multiplier\r\n * @param period The duration (in seconds) for which tokens are locked\r\n * @return The multiplier for this staking period\r\n * Ex : 20000 = x2\r\n */\r\n function getStakePeriodMultiplier(uint256 period)\r\n external\r\n returns (uint256);\r\n\r\n /**\r\n * @notice Get staking infos of a user\r\n * @param user The user address for which to get staking infos\r\n * @return The staking infos of the user\r\n */\r\n function getUserInfo(address user)\r\n external\r\n view\r\n returns (FeeDiscountStorage.UserInfo memory);\r\n}\r\n"},"@solidstate/contracts/token/ERC1155/base/ERC1155BaseInternal.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { AddressUtils } from '../../../utils/AddressUtils.sol';\nimport { IERC1155Internal } from '../IERC1155Internal.sol';\nimport { IERC1155Receiver } from '../IERC1155Receiver.sol';\nimport { ERC1155BaseStorage } from './ERC1155BaseStorage.sol';\n\n/**\n * @title Base ERC1155 internal functions\n * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts/ (MIT license)\n */\nabstract contract ERC1155BaseInternal is IERC1155Internal {\n using AddressUtils for address;\n\n /**\n * @notice query the balance of given token held by given address\n * @param account address to query\n * @param id token to query\n * @return token balance\n */\n function _balanceOf(address account, uint256 id)\n internal\n view\n virtual\n returns (uint256)\n {\n require(\n account != address(0),\n 'ERC1155: balance query for the zero address'\n );\n return ERC1155BaseStorage.layout().balances[id][account];\n }\n\n /**\n * @notice mint given quantity of tokens for given address\n * @dev ERC1155Receiver implementation is not checked\n * @param account beneficiary of minting\n * @param id token ID\n * @param amount quantity of tokens to mint\n * @param data data payload\n */\n function _mint(\n address account,\n uint256 id,\n uint256 amount,\n bytes memory data\n ) internal virtual {\n require(account != address(0), 'ERC1155: mint to the zero address');\n\n _beforeTokenTransfer(\n msg.sender,\n address(0),\n account,\n _asSingletonArray(id),\n _asSingletonArray(amount),\n data\n );\n\n mapping(address => uint256) storage balances = ERC1155BaseStorage\n .layout()\n .balances[id];\n balances[account] += amount;\n\n emit TransferSingle(msg.sender, address(0), account, id, amount);\n }\n\n /**\n * @notice mint given quantity of tokens for given address\n * @param account beneficiary of minting\n * @param id token ID\n * @param amount quantity of tokens to mint\n * @param data data payload\n */\n function _safeMint(\n address account,\n uint256 id,\n uint256 amount,\n bytes memory data\n ) internal virtual {\n _mint(account, id, amount, data);\n\n _doSafeTransferAcceptanceCheck(\n msg.sender,\n address(0),\n account,\n id,\n amount,\n data\n );\n }\n\n /**\n * @notice mint batch of tokens for given address\n * @dev ERC1155Receiver implementation is not checked\n * @param account beneficiary of minting\n * @param ids list of token IDs\n * @param amounts list of quantities of tokens to mint\n * @param data data payload\n */\n function _mintBatch(\n address account,\n uint256[] memory ids,\n uint256[] memory amounts,\n bytes memory data\n ) internal virtual {\n require(account != address(0), 'ERC1155: mint to the zero address');\n require(\n ids.length == amounts.length,\n 'ERC1155: ids and amounts length mismatch'\n );\n\n _beforeTokenTransfer(\n msg.sender,\n address(0),\n account,\n ids,\n amounts,\n data\n );\n\n mapping(uint256 => mapping(address => uint256))\n storage balances = ERC1155BaseStorage.layout().balances;\n\n for (uint256 i; i < ids.length; i++) {\n balances[ids[i]][account] += amounts[i];\n }\n\n emit TransferBatch(msg.sender, address(0), account, ids, amounts);\n }\n\n /**\n * @notice mint batch of tokens for given address\n * @param account beneficiary of minting\n * @param ids list of token IDs\n * @param amounts list of quantities of tokens to mint\n * @param data data payload\n */\n function _safeMintBatch(\n address account,\n uint256[] memory ids,\n uint256[] memory amounts,\n bytes memory data\n ) internal virtual {\n _mintBatch(account, ids, amounts, data);\n\n _doSafeBatchTransferAcceptanceCheck(\n msg.sender,\n address(0),\n account,\n ids,\n amounts,\n data\n );\n }\n\n /**\n * @notice burn given quantity of tokens held by given address\n * @param account holder of tokens to burn\n * @param id token ID\n * @param amount quantity of tokens to burn\n */\n function _burn(\n address account,\n uint256 id,\n uint256 amount\n ) internal virtual {\n require(account != address(0), 'ERC1155: burn from the zero address');\n\n _beforeTokenTransfer(\n msg.sender,\n account,\n address(0),\n _asSingletonArray(id),\n _asSingletonArray(amount),\n ''\n );\n\n mapping(address => uint256) storage balances = ERC1155BaseStorage\n .layout()\n .balances[id];\n\n unchecked {\n require(\n balances[account] >= amount,\n 'ERC1155: burn amount exceeds balances'\n );\n balances[account] -= amount;\n }\n\n emit TransferSingle(msg.sender, account, address(0), id, amount);\n }\n\n /**\n * @notice burn given batch of tokens held by given address\n * @param account holder of tokens to burn\n * @param ids token IDs\n * @param amounts quantities of tokens to burn\n */\n function _burnBatch(\n address account,\n uint256[] memory ids,\n uint256[] memory amounts\n ) internal virtual {\n require(account != address(0), 'ERC1155: burn from the zero address');\n require(\n ids.length == amounts.length,\n 'ERC1155: ids and amounts length mismatch'\n );\n\n _beforeTokenTransfer(msg.sender, account, address(0), ids, amounts, '');\n\n mapping(uint256 => mapping(address => uint256))\n storage balances = ERC1155BaseStorage.layout().balances;\n\n unchecked {\n for (uint256 i; i < ids.length; i++) {\n uint256 id = ids[i];\n require(\n balances[id][account] >= amounts[i],\n 'ERC1155: burn amount exceeds balance'\n );\n balances[id][account] -= amounts[i];\n }\n }\n\n emit TransferBatch(msg.sender, account, address(0), ids, amounts);\n }\n\n /**\n * @notice transfer tokens between given addresses\n * @dev ERC1155Receiver implementation is not checked\n * @param operator executor of transfer\n * @param sender sender of tokens\n * @param recipient receiver of tokens\n * @param id token ID\n * @param amount quantity of tokens to transfer\n * @param data data payload\n */\n function _transfer(\n address operator,\n address sender,\n address recipient,\n uint256 id,\n uint256 amount,\n bytes memory data\n ) internal virtual {\n require(\n recipient != address(0),\n 'ERC1155: transfer to the zero address'\n );\n\n _beforeTokenTransfer(\n operator,\n sender,\n recipient,\n _asSingletonArray(id),\n _asSingletonArray(amount),\n data\n );\n\n mapping(uint256 => mapping(address => uint256))\n storage balances = ERC1155BaseStorage.layout().balances;\n\n unchecked {\n uint256 senderBalance = balances[id][sender];\n require(\n senderBalance >= amount,\n 'ERC1155: insufficient balances for transfer'\n );\n balances[id][sender] = senderBalance - amount;\n }\n\n balances[id][recipient] += amount;\n\n emit TransferSingle(operator, sender, recipient, id, amount);\n }\n\n /**\n * @notice transfer tokens between given addresses\n * @param operator executor of transfer\n * @param sender sender of tokens\n * @param recipient receiver of tokens\n * @param id token ID\n * @param amount quantity of tokens to transfer\n * @param data data payload\n */\n function _safeTransfer(\n address operator,\n address sender,\n address recipient,\n uint256 id,\n uint256 amount,\n bytes memory data\n ) internal virtual {\n _transfer(operator, sender, recipient, id, amount, data);\n\n _doSafeTransferAcceptanceCheck(\n operator,\n sender,\n recipient,\n id,\n amount,\n data\n );\n }\n\n /**\n * @notice transfer batch of tokens between given addresses\n * @dev ERC1155Receiver implementation is not checked\n * @param operator executor of transfer\n * @param sender sender of tokens\n * @param recipient receiver of tokens\n * @param ids token IDs\n * @param amounts quantities of tokens to transfer\n * @param data data payload\n */\n function _transferBatch(\n address operator,\n address sender,\n address recipient,\n uint256[] memory ids,\n uint256[] memory amounts,\n bytes memory data\n ) internal virtual {\n require(\n recipient != address(0),\n 'ERC1155: transfer to the zero address'\n );\n require(\n ids.length == amounts.length,\n 'ERC1155: ids and amounts length mismatch'\n );\n\n _beforeTokenTransfer(operator, sender, recipient, ids, amounts, data);\n\n mapping(uint256 => mapping(address => uint256))\n storage balances = ERC1155BaseStorage.layout().balances;\n\n for (uint256 i; i < ids.length; i++) {\n uint256 token = ids[i];\n uint256 amount = amounts[i];\n\n unchecked {\n uint256 senderBalance = balances[token][sender];\n require(\n senderBalance >= amount,\n 'ERC1155: insufficient balances for transfer'\n );\n balances[token][sender] = senderBalance - amount;\n }\n\n balances[token][recipient] += amount;\n }\n\n emit TransferBatch(operator, sender, recipient, ids, amounts);\n }\n\n /**\n * @notice transfer batch of tokens between given addresses\n * @param operator executor of transfer\n * @param sender sender of tokens\n * @param recipient receiver of tokens\n * @param ids token IDs\n * @param amounts quantities of tokens to transfer\n * @param data data payload\n */\n function _safeTransferBatch(\n address operator,\n address sender,\n address recipient,\n uint256[] memory ids,\n uint256[] memory amounts,\n bytes memory data\n ) internal virtual {\n _transferBatch(operator, sender, recipient, ids, amounts, data);\n\n _doSafeBatchTransferAcceptanceCheck(\n operator,\n sender,\n recipient,\n ids,\n amounts,\n data\n );\n }\n\n /**\n * @notice wrap given element in array of length 1\n * @param element element to wrap\n * @return singleton array\n */\n function _asSingletonArray(uint256 element)\n private\n pure\n returns (uint256[] memory)\n {\n uint256[] memory array = new uint256[](1);\n array[0] = element;\n return array;\n }\n\n /**\n * @notice revert if applicable transfer recipient is not valid ERC1155Receiver\n * @param operator executor of transfer\n * @param from sender of tokens\n * @param to receiver of tokens\n * @param id token ID\n * @param amount quantity of tokens to transfer\n * @param data data payload\n */\n function _doSafeTransferAcceptanceCheck(\n address operator,\n address from,\n address to,\n uint256 id,\n uint256 amount,\n bytes memory data\n ) private {\n if (to.isContract()) {\n try\n IERC1155Receiver(to).onERC1155Received(\n operator,\n from,\n id,\n amount,\n data\n )\n returns (bytes4 response) {\n require(\n response == IERC1155Receiver.onERC1155Received.selector,\n 'ERC1155: ERC1155Receiver rejected tokens'\n );\n } catch Error(string memory reason) {\n revert(reason);\n } catch {\n revert('ERC1155: transfer to non ERC1155Receiver implementer');\n }\n }\n }\n\n /**\n * @notice revert if applicable transfer recipient is not valid ERC1155Receiver\n * @param operator executor of transfer\n * @param from sender of tokens\n * @param to receiver of tokens\n * @param ids token IDs\n * @param amounts quantities of tokens to transfer\n * @param data data payload\n */\n function _doSafeBatchTransferAcceptanceCheck(\n address operator,\n address from,\n address to,\n uint256[] memory ids,\n uint256[] memory amounts,\n bytes memory data\n ) private {\n if (to.isContract()) {\n try\n IERC1155Receiver(to).onERC1155BatchReceived(\n operator,\n from,\n ids,\n amounts,\n data\n )\n returns (bytes4 response) {\n require(\n response ==\n IERC1155Receiver.onERC1155BatchReceived.selector,\n 'ERC1155: ERC1155Receiver rejected tokens'\n );\n } catch Error(string memory reason) {\n revert(reason);\n } catch {\n revert('ERC1155: transfer to non ERC1155Receiver implementer');\n }\n }\n }\n\n /**\n * @notice ERC1155 hook, called before all transfers including mint and burn\n * @dev function should be overridden and new implementation must call super\n * @dev called for both single and batch transfers\n * @param operator executor of transfer\n * @param from sender of tokens\n * @param to receiver of tokens\n * @param ids token IDs\n * @param amounts quantities of tokens to transfer\n * @param data data payload\n */\n function _beforeTokenTransfer(\n address operator,\n address from,\n address to,\n uint256[] memory ids,\n uint256[] memory amounts,\n bytes memory data\n ) internal virtual {}\n}\n"},"@solidstate/contracts/token/ERC20/IERC20Internal.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @title Partial ERC20 interface needed by internal functions\n */\ninterface IERC20Internal {\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n event Approval(\n address indexed owner,\n address indexed spender,\n uint256 value\n );\n}\n"},"@chainlink/contracts/src/v0.8/interfaces/AggregatorInterface.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AggregatorInterface {\n function latestAnswer()\n external\n view\n returns (\n int256\n );\n \n function latestTimestamp()\n external\n view\n returns (\n uint256\n );\n\n function latestRound()\n external\n view\n returns (\n uint256\n );\n\n function getAnswer(\n uint256 roundId\n )\n external\n view\n returns (\n int256\n );\n\n function getTimestamp(\n uint256 roundId\n )\n external\n view\n returns (\n uint256\n );\n\n event AnswerUpdated(\n int256 indexed current,\n uint256 indexed roundId,\n uint256 updatedAt\n );\n\n event NewRound(\n uint256 indexed roundId,\n address indexed startedBy,\n uint256 startedAt\n );\n}\n"},"contracts/pool/PoolExercise.sol":{"content":"// SPDX-License-Identifier: BUSL-1.1\r\n// For further clarification please see https://license.premia.legal\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport {ERC1155BaseStorage} from \"@solidstate/contracts/token/ERC1155/base/ERC1155BaseStorage.sol\";\r\n\r\nimport {PoolInternal} from \"./PoolInternal.sol\";\r\nimport {IPoolExercise} from \"./IPoolExercise.sol\";\r\n\r\n/**\r\n * @title Premia option pool\r\n * @dev deployed standalone and referenced by PoolProxy\r\n */\r\ncontract PoolExercise is IPoolExercise, PoolInternal {\r\n constructor(\r\n address ivolOracle,\r\n address weth,\r\n address premiaMining,\r\n address feeReceiver,\r\n address feeDiscountAddress,\r\n int128 fee64x64\r\n )\r\n PoolInternal(\r\n ivolOracle,\r\n weth,\r\n premiaMining,\r\n feeReceiver,\r\n feeDiscountAddress,\r\n fee64x64\r\n )\r\n {}\r\n\r\n /**\r\n * @inheritdoc IPoolExercise\r\n */\r\n function exerciseFrom(\r\n address holder,\r\n uint256 longTokenId,\r\n uint256 contractSize\r\n ) external override {\r\n if (msg.sender != holder) {\r\n require(\r\n ERC1155BaseStorage.layout().operatorApprovals[holder][\r\n msg.sender\r\n ],\r\n \"not approved\"\r\n );\r\n }\r\n\r\n _exercise(holder, longTokenId, contractSize);\r\n }\r\n\r\n /**\r\n * @inheritdoc IPoolExercise\r\n */\r\n function processExpired(uint256 longTokenId, uint256 contractSize)\r\n external\r\n override\r\n {\r\n _exercise(address(0), longTokenId, contractSize);\r\n }\r\n}\r\n"},"contracts/pool/IPoolExercise.sol":{"content":"// SPDX-License-Identifier: LGPL-3.0-or-later\r\n\r\npragma solidity ^0.8.0;\r\n\r\n/**\r\n * @notice Pool interface for exercising and processing of expired options\r\n */\r\ninterface IPoolExercise {\r\n /**\r\n * @notice exercise option on behalf of holder\r\n * @param holder owner of long option tokens to exercise\r\n * @param longTokenId long option token id\r\n * @param contractSize quantity of tokens to exercise\r\n */\r\n function exerciseFrom(\r\n address holder,\r\n uint256 longTokenId,\r\n uint256 contractSize\r\n ) external;\r\n\r\n /**\r\n * @notice process expired option, freeing liquidity and distributing profits\r\n * @param longTokenId long option token id\r\n * @param contractSize quantity of tokens to process\r\n */\r\n function processExpired(uint256 longTokenId, uint256 contractSize) external;\r\n}\r\n"},"contracts/pool/PoolStorage.sol":{"content":"// SPDX-License-Identifier: BUSL-1.1\r\n// For further clarification please see https://license.premia.legal\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport {AggregatorInterface} from \"@chainlink/contracts/src/v0.8/interfaces/AggregatorInterface.sol\";\r\nimport {AggregatorV3Interface} from \"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\";\r\nimport {EnumerableSet, ERC1155EnumerableStorage} from \"@solidstate/contracts/token/ERC1155/enumerable/ERC1155EnumerableStorage.sol\";\r\n\r\nimport {ABDKMath64x64} from \"abdk-libraries-solidity/ABDKMath64x64.sol\";\r\nimport {ABDKMath64x64Token} from \"../libraries/ABDKMath64x64Token.sol\";\r\nimport {OptionMath} from \"../libraries/OptionMath.sol\";\r\n\r\nlibrary PoolStorage {\r\n using ABDKMath64x64 for int128;\r\n using PoolStorage for PoolStorage.Layout;\r\n\r\n enum TokenType {\r\n UNDERLYING_FREE_LIQ,\r\n BASE_FREE_LIQ,\r\n UNDERLYING_RESERVED_LIQ,\r\n BASE_RESERVED_LIQ,\r\n LONG_CALL,\r\n SHORT_CALL,\r\n LONG_PUT,\r\n SHORT_PUT\r\n }\r\n\r\n struct PoolSettings {\r\n address underlying;\r\n address base;\r\n address underlyingOracle;\r\n address baseOracle;\r\n }\r\n\r\n struct QuoteArgsInternal {\r\n address feePayer; // address of the fee payer\r\n uint64 maturity; // timestamp of option maturity\r\n int128 strike64x64; // 64x64 fixed point representation of strike price\r\n int128 spot64x64; // 64x64 fixed point representation of spot price\r\n uint256 contractSize; // size of option contract\r\n bool isCall; // true for call, false for put\r\n }\r\n\r\n struct QuoteResultInternal {\r\n int128 baseCost64x64; // 64x64 fixed point representation of option cost denominated in underlying currency (without fee)\r\n int128 feeCost64x64; // 64x64 fixed point representation of option fee cost denominated in underlying currency for call, or base currency for put\r\n int128 cLevel64x64; // 64x64 fixed point representation of C-Level of Pool after purchase\r\n int128 slippageCoefficient64x64; // 64x64 fixed point representation of slippage coefficient for given order size\r\n }\r\n\r\n struct BatchData {\r\n uint256 eta;\r\n uint256 totalPendingDeposits;\r\n }\r\n\r\n bytes32 internal constant STORAGE_SLOT =\r\n keccak256(\"premia.contracts.storage.Pool\");\r\n\r\n uint256 private constant C_DECAY_BUFFER = 12 hours;\r\n uint256 private constant C_DECAY_INTERVAL = 4 hours;\r\n\r\n struct Layout {\r\n // ERC20 token addresses\r\n address base;\r\n address underlying;\r\n // AggregatorV3Interface oracle addresses\r\n address baseOracle;\r\n address underlyingOracle;\r\n // token metadata\r\n uint8 underlyingDecimals;\r\n uint8 baseDecimals;\r\n // minimum amounts\r\n uint256 baseMinimum;\r\n uint256 underlyingMinimum;\r\n // deposit caps\r\n uint256 basePoolCap;\r\n uint256 underlyingPoolCap;\r\n // market state\r\n int128 _deprecated_steepness64x64;\r\n int128 cLevelBase64x64;\r\n int128 cLevelUnderlying64x64;\r\n uint256 cLevelBaseUpdatedAt;\r\n uint256 cLevelUnderlyingUpdatedAt;\r\n uint256 updatedAt;\r\n // User -> isCall -> depositedAt\r\n mapping(address => mapping(bool => uint256)) depositedAt;\r\n mapping(address => mapping(bool => uint256)) divestmentTimestamps;\r\n // doubly linked list of free liquidity intervals\r\n // isCall -> User -> User\r\n mapping(bool => mapping(address => address)) liquidityQueueAscending;\r\n mapping(bool => mapping(address => address)) liquidityQueueDescending;\r\n // minimum resolution price bucket => price\r\n mapping(uint256 => int128) bucketPrices64x64;\r\n // sequence id (minimum resolution price bucket / 256) => price update sequence\r\n mapping(uint256 => uint256) priceUpdateSequences;\r\n // isCall -> batch data\r\n mapping(bool => BatchData) nextDeposits;\r\n // user -> batch timestamp -> isCall -> pending amount\r\n mapping(address => mapping(uint256 => mapping(bool => uint256))) pendingDeposits;\r\n EnumerableSet.UintSet tokenIds;\r\n // user -> isCallPool -> total value locked of user (Used for liquidity mining)\r\n mapping(address => mapping(bool => uint256)) userTVL;\r\n // isCallPool -> total value locked\r\n mapping(bool => uint256) totalTVL;\r\n // steepness values\r\n int128 steepnessBase64x64;\r\n int128 steepnessUnderlying64x64;\r\n }\r\n\r\n function layout() internal pure returns (Layout storage l) {\r\n bytes32 slot = STORAGE_SLOT;\r\n assembly {\r\n l.slot := slot\r\n }\r\n }\r\n\r\n /**\r\n * @notice calculate ERC1155 token id for given option parameters\r\n * @param tokenType TokenType enum\r\n * @param maturity timestamp of option maturity\r\n * @param strike64x64 64x64 fixed point representation of strike price\r\n * @return tokenId token id\r\n */\r\n function formatTokenId(\r\n TokenType tokenType,\r\n uint64 maturity,\r\n int128 strike64x64\r\n ) internal pure returns (uint256 tokenId) {\r\n tokenId =\r\n (uint256(tokenType) << 248) +\r\n (uint256(maturity) << 128) +\r\n uint256(int256(strike64x64));\r\n }\r\n\r\n /**\r\n * @notice derive option maturity and strike price from ERC1155 token id\r\n * @param tokenId token id\r\n * @return tokenType TokenType enum\r\n * @return maturity timestamp of option maturity\r\n * @return strike64x64 option strike price\r\n */\r\n function parseTokenId(uint256 tokenId)\r\n internal\r\n pure\r\n returns (\r\n TokenType tokenType,\r\n uint64 maturity,\r\n int128 strike64x64\r\n )\r\n {\r\n assembly {\r\n tokenType := shr(248, tokenId)\r\n maturity := shr(128, tokenId)\r\n strike64x64 := tokenId\r\n }\r\n }\r\n\r\n function getTokenDecimals(Layout storage l, bool isCall)\r\n internal\r\n view\r\n returns (uint8 decimals)\r\n {\r\n decimals = isCall ? l.underlyingDecimals : l.baseDecimals;\r\n }\r\n\r\n /**\r\n * @notice get the total supply of free liquidity tokens, minus pending deposits\r\n * @param l storage layout struct\r\n * @param isCall whether query is for call or put pool\r\n * @return 64x64 fixed point representation of total free liquidity\r\n */\r\n function totalFreeLiquiditySupply64x64(Layout storage l, bool isCall)\r\n internal\r\n view\r\n returns (int128)\r\n {\r\n uint256 tokenId = formatTokenId(\r\n isCall ? TokenType.UNDERLYING_FREE_LIQ : TokenType.BASE_FREE_LIQ,\r\n 0,\r\n 0\r\n );\r\n\r\n return\r\n ABDKMath64x64Token.fromDecimals(\r\n ERC1155EnumerableStorage.layout().totalSupply[tokenId] -\r\n l.nextDeposits[isCall].totalPendingDeposits,\r\n l.getTokenDecimals(isCall)\r\n );\r\n }\r\n\r\n function getReinvestmentStatus(\r\n Layout storage l,\r\n address account,\r\n bool isCallPool\r\n ) internal view returns (bool) {\r\n uint256 timestamp = l.divestmentTimestamps[account][isCallPool];\r\n return timestamp == 0 || timestamp > block.timestamp;\r\n }\r\n\r\n function addUnderwriter(\r\n Layout storage l,\r\n address account,\r\n bool isCallPool\r\n ) internal {\r\n require(account != address(0));\r\n\r\n mapping(address => address) storage asc = l.liquidityQueueAscending[\r\n isCallPool\r\n ];\r\n mapping(address => address) storage desc = l.liquidityQueueDescending[\r\n isCallPool\r\n ];\r\n\r\n if (_isInQueue(account, asc, desc)) return;\r\n\r\n address last = desc[address(0)];\r\n\r\n asc[last] = account;\r\n desc[account] = last;\r\n desc[address(0)] = account;\r\n }\r\n\r\n function removeUnderwriter(\r\n Layout storage l,\r\n address account,\r\n bool isCallPool\r\n ) internal {\r\n require(account != address(0));\r\n\r\n mapping(address => address) storage asc = l.liquidityQueueAscending[\r\n isCallPool\r\n ];\r\n mapping(address => address) storage desc = l.liquidityQueueDescending[\r\n isCallPool\r\n ];\r\n\r\n if (!_isInQueue(account, asc, desc)) return;\r\n\r\n address prev = desc[account];\r\n address next = asc[account];\r\n asc[prev] = next;\r\n desc[next] = prev;\r\n delete asc[account];\r\n delete desc[account];\r\n }\r\n\r\n function isInQueue(\r\n Layout storage l,\r\n address account,\r\n bool isCallPool\r\n ) internal view returns (bool) {\r\n mapping(address => address) storage asc = l.liquidityQueueAscending[\r\n isCallPool\r\n ];\r\n mapping(address => address) storage desc = l.liquidityQueueDescending[\r\n isCallPool\r\n ];\r\n\r\n return _isInQueue(account, asc, desc);\r\n }\r\n\r\n function _isInQueue(\r\n address account,\r\n mapping(address => address) storage asc,\r\n mapping(address => address) storage desc\r\n ) private view returns (bool) {\r\n return asc[account] != address(0) || desc[address(0)] == account;\r\n }\r\n\r\n /**\r\n * @notice get current C-Level, without accounting for pending adjustments\r\n * @param l storage layout struct\r\n * @param isCall whether query is for call or put pool\r\n * @return cLevel64x64 64x64 fixed point representation of C-Level\r\n */\r\n function getRawCLevel64x64(Layout storage l, bool isCall)\r\n internal\r\n view\r\n returns (int128 cLevel64x64)\r\n {\r\n cLevel64x64 = isCall ? l.cLevelUnderlying64x64 : l.cLevelBase64x64;\r\n }\r\n\r\n /**\r\n * @notice get current C-Level, accounting for unrealized decay\r\n * @param l storage layout struct\r\n * @param isCall whether query is for call or put pool\r\n * @return cLevel64x64 64x64 fixed point representation of C-Level\r\n */\r\n function getDecayAdjustedCLevel64x64(Layout storage l, bool isCall)\r\n internal\r\n view\r\n returns (int128 cLevel64x64)\r\n {\r\n // get raw C-Level from storage\r\n cLevel64x64 = l.getRawCLevel64x64(isCall);\r\n\r\n // account for C-Level decay\r\n cLevel64x64 = l.applyCLevelDecayAdjustment(cLevel64x64, isCall);\r\n }\r\n\r\n /**\r\n * @notice calculate updated C-Level, accounting for unrealized decay\r\n * @param l storage layout struct\r\n * @param oldCLevel64x64 64x64 fixed point representation pool C-Level before accounting for decay\r\n * @param isCall whether query is for call or put pool\r\n * @return cLevel64x64 64x64 fixed point representation of C-Level of Pool after accounting for decay\r\n */\r\n function applyCLevelDecayAdjustment(\r\n Layout storage l,\r\n int128 oldCLevel64x64,\r\n bool isCall\r\n ) internal view returns (int128 cLevel64x64) {\r\n uint256 timeElapsed = block.timestamp -\r\n (isCall ? l.cLevelUnderlyingUpdatedAt : l.cLevelBaseUpdatedAt);\r\n\r\n // do not apply C decay if less than 24 hours have elapsed\r\n\r\n if (timeElapsed > C_DECAY_BUFFER) {\r\n timeElapsed -= C_DECAY_BUFFER;\r\n } else {\r\n return oldCLevel64x64;\r\n }\r\n\r\n int128 timeIntervalsElapsed64x64 = ABDKMath64x64.divu(\r\n timeElapsed,\r\n C_DECAY_INTERVAL\r\n );\r\n\r\n uint256 tokenId = formatTokenId(\r\n isCall ? TokenType.UNDERLYING_FREE_LIQ : TokenType.BASE_FREE_LIQ,\r\n 0,\r\n 0\r\n );\r\n\r\n uint256 tvl = l.totalTVL[isCall];\r\n\r\n int128 utilization = ABDKMath64x64.divu(\r\n tvl -\r\n (ERC1155EnumerableStorage.layout().totalSupply[tokenId] -\r\n l.nextDeposits[isCall].totalPendingDeposits),\r\n tvl\r\n );\r\n\r\n return\r\n OptionMath.calculateCLevelDecay(\r\n OptionMath.CalculateCLevelDecayArgs(\r\n timeIntervalsElapsed64x64,\r\n oldCLevel64x64,\r\n utilization,\r\n 0xb333333333333333, // 0.7\r\n 0xe666666666666666, // 0.9\r\n 0x10000000000000000, // 1.0\r\n 0x10000000000000000, // 1.0\r\n 0xe666666666666666, // 0.9\r\n 0x56fc2a2c515da32ea // 2e\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * @notice calculate updated C-Level, accounting for pending deposits\r\n * @param l storage layout struct\r\n * @param oldCLevel64x64 64x64 fixed point representation pool C-Level before accounting for liquidity change\r\n * @param oldLiquidity64x64 64x64 fixed point representation of previous liquidity\r\n * @param isCall whether to update C-Level for call or put pool\r\n * @return cLevel64x64 64x64 fixed point representation of C-Level\r\n * @return liquidity64x64 64x64 fixed point representation of new liquidity amount\r\n */\r\n function applyCLevelPendingDepositAdjustment(\r\n Layout storage l,\r\n int128 oldCLevel64x64,\r\n int128 oldLiquidity64x64,\r\n bool isCall\r\n ) internal view returns (int128 cLevel64x64, int128 liquidity64x64) {\r\n PoolStorage.BatchData storage batchData = l.nextDeposits[isCall];\r\n int128 pendingDeposits64x64;\r\n\r\n if (\r\n batchData.totalPendingDeposits > 0 &&\r\n batchData.eta != 0 &&\r\n block.timestamp >= batchData.eta\r\n ) {\r\n pendingDeposits64x64 = ABDKMath64x64Token.fromDecimals(\r\n batchData.totalPendingDeposits,\r\n l.getTokenDecimals(isCall)\r\n );\r\n\r\n liquidity64x64 = oldLiquidity64x64.add(pendingDeposits64x64);\r\n\r\n cLevel64x64 = l.applyCLevelLiquidityChangeAdjustment(\r\n oldCLevel64x64,\r\n oldLiquidity64x64,\r\n liquidity64x64,\r\n isCall\r\n );\r\n } else {\r\n cLevel64x64 = oldCLevel64x64;\r\n liquidity64x64 = oldLiquidity64x64;\r\n }\r\n }\r\n\r\n /**\r\n * @notice calculate updated C-Level, accounting for change in liquidity\r\n * @param l storage layout struct\r\n * @param oldCLevel64x64 64x64 fixed point representation pool C-Level before accounting for liquidity change\r\n * @param oldLiquidity64x64 64x64 fixed point representation of previous liquidity\r\n * @param newLiquidity64x64 64x64 fixed point representation of current liquidity\r\n * @param isCallPool whether to update C-Level for call or put pool\r\n * @return cLevel64x64 64x64 fixed point representation of C-Level\r\n */\r\n function applyCLevelLiquidityChangeAdjustment(\r\n Layout storage l,\r\n int128 oldCLevel64x64,\r\n int128 oldLiquidity64x64,\r\n int128 newLiquidity64x64,\r\n bool isCallPool\r\n ) internal view returns (int128 cLevel64x64) {\r\n int128 steepness64x64 = isCallPool\r\n ? l.steepnessUnderlying64x64\r\n : l.steepnessBase64x64;\r\n\r\n // fallback to deprecated storage value if side-specific value is not set\r\n if (steepness64x64 == 0) steepness64x64 = l._deprecated_steepness64x64;\r\n\r\n cLevel64x64 = OptionMath.calculateCLevel(\r\n oldCLevel64x64,\r\n oldLiquidity64x64,\r\n newLiquidity64x64,\r\n steepness64x64\r\n );\r\n\r\n if (cLevel64x64 < 0xb333333333333333) {\r\n cLevel64x64 = int128(0xb333333333333333); // 64x64 fixed point representation of 0.7\r\n }\r\n }\r\n\r\n /**\r\n * @notice set C-Level to arbitrary pre-calculated value\r\n * @param cLevel64x64 new C-Level of pool\r\n * @param isCallPool whether to update C-Level for call or put pool\r\n */\r\n function setCLevel(\r\n Layout storage l,\r\n int128 cLevel64x64,\r\n bool isCallPool\r\n ) internal {\r\n if (isCallPool) {\r\n l.cLevelUnderlying64x64 = cLevel64x64;\r\n l.cLevelUnderlyingUpdatedAt = block.timestamp;\r\n } else {\r\n l.cLevelBase64x64 = cLevel64x64;\r\n l.cLevelBaseUpdatedAt = block.timestamp;\r\n }\r\n }\r\n\r\n function setOracles(\r\n Layout storage l,\r\n address baseOracle,\r\n address underlyingOracle\r\n ) internal {\r\n require(\r\n AggregatorV3Interface(baseOracle).decimals() ==\r\n AggregatorV3Interface(underlyingOracle).decimals(),\r\n \"Pool: oracle decimals must match\"\r\n );\r\n\r\n l.baseOracle = baseOracle;\r\n l.underlyingOracle = underlyingOracle;\r\n }\r\n\r\n function fetchPriceUpdate(Layout storage l)\r\n internal\r\n view\r\n returns (int128 price64x64)\r\n {\r\n int256 priceUnderlying = AggregatorInterface(l.underlyingOracle)\r\n .latestAnswer();\r\n int256 priceBase = AggregatorInterface(l.baseOracle).latestAnswer();\r\n\r\n return ABDKMath64x64.divi(priceUnderlying, priceBase);\r\n }\r\n\r\n /**\r\n * @notice set price update for hourly bucket corresponding to given timestamp\r\n * @param l storage layout struct\r\n * @param timestamp timestamp to update\r\n * @param price64x64 64x64 fixed point representation of price\r\n */\r\n function setPriceUpdate(\r\n Layout storage l,\r\n uint256 timestamp,\r\n int128 price64x64\r\n ) internal {\r\n uint256 bucket = timestamp / (1 hours);\r\n l.bucketPrices64x64[bucket] = price64x64;\r\n l.priceUpdateSequences[bucket >> 8] += 1 << (255 - (bucket & 255));\r\n }\r\n\r\n /**\r\n * @notice get price update for hourly bucket corresponding to given timestamp\r\n * @param l storage layout struct\r\n * @param timestamp timestamp to query\r\n * @return 64x64 fixed point representation of price\r\n */\r\n function getPriceUpdate(Layout storage l, uint256 timestamp)\r\n internal\r\n view\r\n returns (int128)\r\n {\r\n return l.bucketPrices64x64[timestamp / (1 hours)];\r\n }\r\n\r\n /**\r\n * @notice get first price update available following given timestamp\r\n * @param l storage layout struct\r\n * @param timestamp timestamp to query\r\n * @return 64x64 fixed point representation of price\r\n */\r\n function getPriceUpdateAfter(Layout storage l, uint256 timestamp)\r\n internal\r\n view\r\n returns (int128)\r\n {\r\n // price updates are grouped into hourly buckets\r\n uint256 bucket = timestamp / (1 hours);\r\n // divide by 256 to get the index of the relevant price update sequence\r\n uint256 sequenceId = bucket >> 8;\r\n\r\n // get position within sequence relevant to current price update\r\n\r\n uint256 offset = bucket & 255;\r\n // shift to skip buckets from earlier in sequence\r\n uint256 sequence = (l.priceUpdateSequences[sequenceId] << offset) >>\r\n offset;\r\n\r\n // iterate through future sequences until a price update is found\r\n // sequence corresponding to current timestamp used as upper bound\r\n\r\n uint256 currentPriceUpdateSequenceId = block.timestamp / (256 hours);\r\n\r\n while (sequence == 0 && sequenceId <= currentPriceUpdateSequenceId) {\r\n sequence = l.priceUpdateSequences[++sequenceId];\r\n }\r\n\r\n // if no price update is found (sequence == 0) function will return 0\r\n // this should never occur, as each relevant external function triggers a price update\r\n\r\n // the most significant bit of the sequence corresponds to the offset of the relevant bucket\r\n\r\n uint256 msb;\r\n\r\n for (uint256 i = 128; i > 0; i >>= 1) {\r\n if (sequence >> i > 0) {\r\n msb += i;\r\n sequence >>= i;\r\n }\r\n }\r\n\r\n return l.bucketPrices64x64[((sequenceId + 1) << 8) - msb - 1];\r\n }\r\n\r\n function fromBaseToUnderlyingDecimals(Layout storage l, uint256 value)\r\n internal\r\n view\r\n returns (uint256)\r\n {\r\n int128 valueFixed64x64 = ABDKMath64x64Token.fromDecimals(\r\n value,\r\n l.baseDecimals\r\n );\r\n return\r\n ABDKMath64x64Token.toDecimals(\r\n valueFixed64x64,\r\n l.underlyingDecimals\r\n );\r\n }\r\n\r\n function fromUnderlyingToBaseDecimals(Layout storage l, uint256 value)\r\n internal\r\n view\r\n returns (uint256)\r\n {\r\n int128 valueFixed64x64 = ABDKMath64x64Token.fromDecimals(\r\n value,\r\n l.underlyingDecimals\r\n );\r\n return ABDKMath64x64Token.toDecimals(valueFixed64x64, l.baseDecimals);\r\n }\r\n}\r\n"},"@solidstate/contracts/utils/AddressUtils.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nlibrary AddressUtils {\n function toString(address account) internal pure returns (string memory) {\n bytes32 value = bytes32(uint256(uint160(account)));\n bytes memory alphabet = '0123456789abcdef';\n bytes memory chars = new bytes(42);\n\n chars[0] = '0';\n chars[1] = 'x';\n\n for (uint256 i = 0; i < 20; i++) {\n chars[2 + i * 2] = alphabet[uint8(value[i + 12] >> 4)];\n chars[3 + i * 2] = alphabet[uint8(value[i + 12] & 0x0f)];\n }\n\n return string(chars);\n }\n\n function isContract(address account) internal view returns (bool) {\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n function sendValue(address payable account, uint256 amount) internal {\n (bool success, ) = account.call{ value: amount }('');\n require(success, 'AddressUtils: failed to send value');\n }\n\n function functionCall(address target, bytes memory data)\n internal\n returns (bytes memory)\n {\n return\n functionCall(target, data, 'AddressUtils: failed low-level call');\n }\n\n function functionCall(\n address target,\n bytes memory data,\n string memory error\n ) internal returns (bytes memory) {\n return _functionCallWithValue(target, data, 0, error);\n }\n\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return\n functionCallWithValue(\n target,\n data,\n value,\n 'AddressUtils: failed low-level call with value'\n );\n }\n\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory error\n ) internal returns (bytes memory) {\n require(\n address(this).balance >= value,\n 'AddressUtils: insufficient balance for call'\n );\n return _functionCallWithValue(target, data, value, error);\n }\n\n function _functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory error\n ) private returns (bytes memory) {\n require(\n isContract(target),\n 'AddressUtils: function call to non-contract'\n );\n\n (bool success, bytes memory returnData) = target.call{ value: value }(\n data\n );\n\n if (success) {\n return returnData;\n } else if (returnData.length > 0) {\n assembly {\n let returnData_size := mload(returnData)\n revert(add(32, returnData), returnData_size)\n }\n } else {\n revert(error);\n }\n }\n}\n"},"@solidstate/contracts/token/ERC1155/enumerable/ERC1155Enumerable.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { EnumerableSet } from '../../../utils/EnumerableSet.sol';\nimport { ERC1155Base, ERC1155BaseInternal } from '../base/ERC1155Base.sol';\nimport { IERC1155Enumerable } from './IERC1155Enumerable.sol';\nimport { ERC1155EnumerableInternal, ERC1155EnumerableStorage } from './ERC1155EnumerableInternal.sol';\n\n/**\n * @title ERC1155 implementation including enumerable and aggregate functions\n */\nabstract contract ERC1155Enumerable is\n IERC1155Enumerable,\n ERC1155Base,\n ERC1155EnumerableInternal\n{\n using EnumerableSet for EnumerableSet.AddressSet;\n using EnumerableSet for EnumerableSet.UintSet;\n\n /**\n * @inheritdoc IERC1155Enumerable\n */\n function totalSupply(uint256 id)\n public\n view\n virtual\n override\n returns (uint256)\n {\n return ERC1155EnumerableStorage.layout().totalSupply[id];\n }\n\n /**\n * @inheritdoc IERC1155Enumerable\n */\n function totalHolders(uint256 id)\n public\n view\n virtual\n override\n returns (uint256)\n {\n return ERC1155EnumerableStorage.layout().accountsByToken[id].length();\n }\n\n /**\n * @inheritdoc IERC1155Enumerable\n */\n function accountsByToken(uint256 id)\n public\n view\n virtual\n override\n returns (address[] memory)\n {\n EnumerableSet.AddressSet storage accounts = ERC1155EnumerableStorage\n .layout()\n .accountsByToken[id];\n\n address[] memory addresses = new address[](accounts.length());\n\n for (uint256 i; i < accounts.length(); i++) {\n addresses[i] = accounts.at(i);\n }\n\n return addresses;\n }\n\n /**\n * @inheritdoc IERC1155Enumerable\n */\n function tokensByAccount(address account)\n public\n view\n virtual\n override\n returns (uint256[] memory)\n {\n EnumerableSet.UintSet storage tokens = ERC1155EnumerableStorage\n .layout()\n .tokensByAccount[account];\n\n uint256[] memory ids = new uint256[](tokens.length());\n\n for (uint256 i; i < tokens.length(); i++) {\n ids[i] = tokens.at(i);\n }\n\n return ids;\n }\n\n /**\n * @notice ERC1155 hook: update aggregate values\n * @inheritdoc ERC1155EnumerableInternal\n */\n function _beforeTokenTransfer(\n address operator,\n address from,\n address to,\n uint256[] memory ids,\n uint256[] memory amounts,\n bytes memory data\n )\n internal\n virtual\n override(ERC1155BaseInternal, ERC1155EnumerableInternal)\n {\n super._beforeTokenTransfer(operator, from, to, ids, amounts, data);\n }\n}\n"},"@solidstate/contracts/token/ERC1155/IERC1155Receiver.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC165 } from '../../introspection/IERC165.sol';\n\n/**\n * @title ERC1155 transfer receiver interface\n */\ninterface IERC1155Receiver is IERC165 {\n /**\n * @notice validate receipt of ERC1155 transfer\n * @param operator executor of transfer\n * @param from sender of tokens\n * @param id token ID received\n * @param value quantity of tokens received\n * @param data data payload\n * @return function's own selector if transfer is accepted\n */\n function onERC1155Received(\n address operator,\n address from,\n uint256 id,\n uint256 value,\n bytes calldata data\n ) external returns (bytes4);\n\n /**\n * @notice validate receipt of ERC1155 batch transfer\n * @param operator executor of transfer\n * @param from sender of tokens\n * @param ids token IDs received\n * @param values quantities of tokens received\n * @param data data payload\n * @return function's own selector if transfer is accepted\n */\n function onERC1155BatchReceived(\n address operator,\n address from,\n uint256[] calldata ids,\n uint256[] calldata values,\n bytes calldata data\n ) external returns (bytes4);\n}\n"},"contracts/pool/IPoolEvents.sol":{"content":"// SPDX-License-Identifier: LGPL-3.0-or-later\r\n\r\npragma solidity ^0.8.0;\r\n\r\ninterface IPoolEvents {\r\n event Purchase(\r\n address indexed user,\r\n uint256 longTokenId,\r\n uint256 contractSize,\r\n uint256 baseCost,\r\n uint256 feeCost,\r\n int128 spot64x64\r\n );\r\n\r\n event Exercise(\r\n address indexed user,\r\n uint256 longTokenId,\r\n uint256 contractSize,\r\n uint256 exerciseValue,\r\n uint256 fee\r\n );\r\n\r\n event Underwrite(\r\n address indexed underwriter,\r\n address indexed longReceiver,\r\n uint256 shortTokenId,\r\n uint256 intervalContractSize,\r\n uint256 intervalPremium,\r\n bool isManualUnderwrite\r\n );\r\n\r\n event AssignExercise(\r\n address indexed underwriter,\r\n uint256 shortTokenId,\r\n uint256 freedAmount,\r\n uint256 intervalContractSize,\r\n uint256 fee\r\n );\r\n\r\n event Deposit(address indexed user, bool isCallPool, uint256 amount);\r\n\r\n event Withdrawal(\r\n address indexed user,\r\n bool isCallPool,\r\n uint256 depositedAt,\r\n uint256 amount\r\n );\r\n\r\n event FeeWithdrawal(bool indexed isCallPool, uint256 amount);\r\n\r\n event Annihilate(uint256 shortTokenId, uint256 amount);\r\n\r\n event UpdateCLevel(\r\n bool indexed isCall,\r\n int128 cLevel64x64,\r\n int128 oldLiquidity64x64,\r\n int128 newLiquidity64x64\r\n );\r\n\r\n event UpdateSteepness(int128 steepness64x64, bool isCallPool);\r\n}\r\n"},"contracts/oracle/VolatilitySurfaceOracleStorage.sol":{"content":"// SPDX-License-Identifier: BUSL-1.1\r\n// For further clarification please see https://license.premia.legal\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport {EnumerableSet} from \"@solidstate/contracts/utils/EnumerableSet.sol\";\r\n\r\nlibrary VolatilitySurfaceOracleStorage {\r\n bytes32 internal constant STORAGE_SLOT =\r\n keccak256(\"premia.contracts.storage.VolatilitySurfaceOracle\");\r\n\r\n uint256 internal constant COEFF_BITS = 51;\r\n uint256 internal constant COEFF_BITS_MINUS_ONE = 50;\r\n uint256 internal constant COEFF_AMOUNT = 5;\r\n // START_BIT = COEFF_BITS * (COEFF_AMOUNT - 1)\r\n uint256 internal constant START_BIT = 204;\r\n\r\n struct Update {\r\n uint256 updatedAt;\r\n bytes32 callCoefficients;\r\n bytes32 putCoefficients;\r\n }\r\n\r\n struct Layout {\r\n // Base token -> Underlying token -> Update\r\n mapping(address => mapping(address => Update)) volatilitySurfaces;\r\n // Relayer addresses which can be trusted to provide accurate option trades\r\n EnumerableSet.AddressSet whitelistedRelayers;\r\n }\r\n\r\n function layout() internal pure returns (Layout storage l) {\r\n bytes32 slot = STORAGE_SLOT;\r\n assembly {\r\n l.slot := slot\r\n }\r\n }\r\n\r\n function getCoefficients(\r\n Layout storage l,\r\n address baseToken,\r\n address underlyingToken,\r\n bool isCall\r\n ) internal view returns (bytes32) {\r\n Update storage u = l.volatilitySurfaces[baseToken][underlyingToken];\r\n return isCall ? u.callCoefficients : u.putCoefficients;\r\n }\r\n\r\n function parseVolatilitySurfaceCoefficients(bytes32 input)\r\n internal\r\n pure\r\n returns (int256[] memory coefficients)\r\n {\r\n coefficients = new int256[](COEFF_AMOUNT);\r\n\r\n // Value to add to negative numbers to cast them to int256\r\n int256 toAdd = (int256(-1) >> COEFF_BITS) << COEFF_BITS;\r\n\r\n assembly {\r\n let i := 0\r\n // Value equal to -1\r\n let mid := shl(COEFF_BITS_MINUS_ONE, 1)\r\n\r\n for {\r\n\r\n } lt(i, COEFF_AMOUNT) {\r\n\r\n } {\r\n let offset := sub(START_BIT, mul(COEFF_BITS, i))\r\n let coeff := shr(\r\n offset,\r\n sub(\r\n input,\r\n shl(\r\n add(offset, COEFF_BITS),\r\n shr(add(offset, COEFF_BITS), input)\r\n )\r\n )\r\n )\r\n\r\n // Check if value is a negative number and needs casting\r\n if or(eq(coeff, mid), gt(coeff, mid)) {\r\n coeff := add(coeff, toAdd)\r\n }\r\n\r\n // Store result in the coefficients array\r\n mstore(add(coefficients, add(0x20, mul(0x20, i))), coeff)\r\n\r\n i := add(i, 1)\r\n }\r\n }\r\n }\r\n\r\n function formatVolatilitySurfaceCoefficients(int256[5] memory coefficients)\r\n internal\r\n pure\r\n returns (bytes32 result)\r\n {\r\n for (uint256 i = 0; i < COEFF_AMOUNT; i++) {\r\n int256 max = int256(1 << COEFF_BITS_MINUS_ONE);\r\n require(\r\n coefficients[i] < max && coefficients[i] > -max,\r\n \"Out of bounds\"\r\n );\r\n }\r\n\r\n assembly {\r\n let i := 0\r\n\r\n for {\r\n\r\n } lt(i, COEFF_AMOUNT) {\r\n\r\n } {\r\n let offset := sub(START_BIT, mul(COEFF_BITS, i))\r\n let coeff := mload(add(coefficients, mul(0x20, i)))\r\n\r\n result := add(\r\n result,\r\n shl(\r\n offset,\r\n sub(coeff, shl(COEFF_BITS, shr(COEFF_BITS, coeff)))\r\n )\r\n )\r\n\r\n i := add(i, 1)\r\n }\r\n }\r\n }\r\n}\r\n"},"@solidstate/contracts/token/ERC1155/enumerable/IERC1155Enumerable.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @title ERC1155 enumerable and aggregate function interface\n */\ninterface IERC1155Enumerable {\n /**\n * @notice query total minted supply of given token\n * @param id token id to query\n * @return token supply\n */\n function totalSupply(uint256 id) external view returns (uint256);\n\n /**\n * @notice query total number of holders for given token\n * @param id token id to query\n * @return quantity of holders\n */\n function totalHolders(uint256 id) external view returns (uint256);\n\n /**\n * @notice query holders of given token\n * @param id token id to query\n * @return list of holder addresses\n */\n function accountsByToken(uint256 id)\n external\n view\n returns (address[] memory);\n\n /**\n * @notice query tokens held by given address\n * @param account address to query\n * @return list of token ids\n */\n function tokensByAccount(address account)\n external\n view\n returns (uint256[] memory);\n}\n"},"@solidstate/contracts/token/ERC20/IERC20.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC20Internal } from './IERC20Internal.sol';\n\n/**\n * @title ERC20 interface\n * @dev see https://github.com/ethereum/EIPs/issues/20\n */\ninterface IERC20 is IERC20Internal {\n /**\n * @notice query the total minted token supply\n * @return token supply\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @notice query the token balance of given account\n * @param account address to query\n * @return token balance\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @notice query the allowance granted from given holder to given spender\n * @param holder approver of allowance\n * @param spender recipient of allowance\n * @return token allowance\n */\n function allowance(address holder, address spender)\n external\n view\n returns (uint256);\n\n /**\n * @notice grant approval to spender to spend tokens\n * @dev prefer ERC20Extended functions to avoid transaction-ordering vulnerability (see https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729)\n * @param spender recipient of allowance\n * @param amount quantity of tokens approved for spending\n * @return success status (always true; otherwise function should revert)\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @notice transfer tokens to given recipient\n * @param recipient beneficiary of token transfer\n * @param amount quantity of tokens to transfer\n * @return success status (always true; otherwise function should revert)\n */\n function transfer(address recipient, uint256 amount)\n external\n returns (bool);\n\n /**\n * @notice transfer tokens to given recipient on behalf of given holder\n * @param holder holder of tokens prior to transfer\n * @param recipient beneficiary of token transfer\n * @param amount quantity of tokens to transfer\n * @return success status (always true; otherwise function should revert)\n */\n function transferFrom(\n address holder,\n address recipient,\n uint256 amount\n ) external returns (bool);\n}\n"},"contracts/mining/IPremiaMining.sol":{"content":"// SPDX-License-Identifier: LGPL-3.0-or-later\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport {PremiaMiningStorage} from \"./PremiaMiningStorage.sol\";\r\n\r\ninterface IPremiaMining {\r\n function addPremiaRewards(uint256 _amount) external;\r\n\r\n function premiaRewardsAvailable() external view returns (uint256);\r\n\r\n function getTotalAllocationPoints() external view returns (uint256);\r\n\r\n function getPoolInfo(address pool, bool isCallPool)\r\n external\r\n view\r\n returns (PremiaMiningStorage.PoolInfo memory);\r\n\r\n function getPremiaPerYear() external view returns (uint256);\r\n\r\n function addPool(address _pool, uint256 _allocPoints) external;\r\n\r\n function setPoolAllocPoints(\r\n address[] memory _pools,\r\n uint256[] memory _allocPoints\r\n ) external;\r\n\r\n function pendingPremia(\r\n address _pool,\r\n bool _isCallPool,\r\n address _user\r\n ) external view returns (uint256);\r\n\r\n function updatePool(\r\n address _pool,\r\n bool _isCallPool,\r\n uint256 _totalTVL\r\n ) external;\r\n\r\n function allocatePending(\r\n address _user,\r\n address _pool,\r\n bool _isCallPool,\r\n uint256 _userTVLOld,\r\n uint256 _userTVLNew,\r\n uint256 _totalTVL\r\n ) external;\r\n\r\n function claim(\r\n address _user,\r\n address _pool,\r\n bool _isCallPool,\r\n uint256 _userTVLOld,\r\n uint256 _userTVLNew,\r\n uint256 _totalTVL\r\n ) external;\r\n}\r\n"},"@solidstate/contracts/token/ERC1155/enumerable/ERC1155EnumerableStorage.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { EnumerableSet } from '../../../utils/EnumerableSet.sol';\n\nlibrary ERC1155EnumerableStorage {\n struct Layout {\n mapping(uint256 => uint256) totalSupply;\n mapping(uint256 => EnumerableSet.AddressSet) accountsByToken;\n mapping(address => EnumerableSet.UintSet) tokensByAccount;\n }\n\n bytes32 internal constant STORAGE_SLOT =\n keccak256('solidstate.contracts.storage.ERC1155Enumerable');\n\n function layout() internal pure returns (Layout storage l) {\n bytes32 slot = STORAGE_SLOT;\n assembly {\n l.slot := slot\n }\n }\n}\n"},"@solidstate/contracts/token/ERC1155/enumerable/ERC1155EnumerableInternal.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { EnumerableSet } from '../../../utils/EnumerableSet.sol';\nimport { ERC1155BaseInternal, ERC1155BaseStorage } from '../base/ERC1155BaseInternal.sol';\nimport { ERC1155EnumerableStorage } from './ERC1155EnumerableStorage.sol';\n\n/**\n * @title ERC1155Enumerable internal functions\n */\nabstract contract ERC1155EnumerableInternal is ERC1155BaseInternal {\n using EnumerableSet for EnumerableSet.AddressSet;\n using EnumerableSet for EnumerableSet.UintSet;\n\n /**\n * @notice ERC1155 hook: update aggregate values\n * @inheritdoc ERC1155BaseInternal\n */\n function _beforeTokenTransfer(\n address operator,\n address from,\n address to,\n uint256[] memory ids,\n uint256[] memory amounts,\n bytes memory data\n ) internal virtual override {\n super._beforeTokenTransfer(operator, from, to, ids, amounts, data);\n\n if (from != to) {\n ERC1155EnumerableStorage.Layout storage l = ERC1155EnumerableStorage\n .layout();\n mapping(uint256 => EnumerableSet.AddressSet)\n storage tokenAccounts = l.accountsByToken;\n EnumerableSet.UintSet storage fromTokens = l.tokensByAccount[from];\n EnumerableSet.UintSet storage toTokens = l.tokensByAccount[to];\n\n for (uint256 i; i < ids.length; i++) {\n uint256 amount = amounts[i];\n\n if (amount > 0) {\n uint256 id = ids[i];\n\n if (from == address(0)) {\n l.totalSupply[id] += amount;\n } else if (_balanceOf(from, id) == amount) {\n tokenAccounts[id].remove(from);\n fromTokens.remove(id);\n }\n\n if (to == address(0)) {\n l.totalSupply[id] -= amount;\n } else if (_balanceOf(to, id) == 0) {\n tokenAccounts[id].add(to);\n toTokens.add(id);\n }\n }\n }\n }\n }\n}\n"},"contracts/oracle/IVolatilitySurfaceOracle.sol":{"content":"// SPDX-License-Identifier: LGPL-3.0-or-later\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport {VolatilitySurfaceOracleStorage} from \"./VolatilitySurfaceOracleStorage.sol\";\r\n\r\ninterface IVolatilitySurfaceOracle {\r\n function getWhitelistedRelayers() external view returns (address[] memory);\r\n\r\n function getVolatilitySurface(address baseToken, address underlyingToken)\r\n external\r\n view\r\n returns (VolatilitySurfaceOracleStorage.Update memory);\r\n\r\n function getVolatilitySurfaceCoefficientsUnpacked(\r\n address baseToken,\r\n address underlyingToken,\r\n bool isCall\r\n ) external view returns (int256[] memory);\r\n\r\n function getTimeToMaturity64x64(uint64 maturity)\r\n external\r\n view\r\n returns (int128);\r\n\r\n function getAnnualizedVolatility64x64(\r\n address baseToken,\r\n address underlyingToken,\r\n int128 spot64x64,\r\n int128 strike64x64,\r\n int128 timeToMaturity64x64,\r\n bool isCall\r\n ) external view returns (int128);\r\n\r\n function getBlackScholesPrice64x64(\r\n address baseToken,\r\n address underlyingToken,\r\n int128 strike64x64,\r\n int128 spot64x64,\r\n int128 timeToMaturity64x64,\r\n bool isCall\r\n ) external view returns (int128);\r\n\r\n function getBlackScholesPrice(\r\n address baseToken,\r\n address underlyingToken,\r\n int128 strike64x64,\r\n int128 spot64x64,\r\n int128 timeToMaturity64x64,\r\n bool isCall\r\n ) external view returns (uint256);\r\n}\r\n"},"contracts/pool/PoolInternal.sol":{"content":"// SPDX-License-Identifier: BUSL-1.1\r\n// For further clarification please see https://license.premia.legal\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport {IERC173} from \"@solidstate/contracts/access/IERC173.sol\";\r\nimport {OwnableStorage} from \"@solidstate/contracts/access/OwnableStorage.sol\";\r\nimport {IERC20} from \"@solidstate/contracts/token/ERC20/IERC20.sol\";\r\nimport {ERC1155EnumerableInternal, ERC1155EnumerableStorage, EnumerableSet} from \"@solidstate/contracts/token/ERC1155/enumerable/ERC1155Enumerable.sol\";\r\nimport {IWETH} from \"@solidstate/contracts/utils/IWETH.sol\";\r\n\r\nimport {PoolStorage} from \"./PoolStorage.sol\";\r\n\r\nimport {ABDKMath64x64} from \"abdk-libraries-solidity/ABDKMath64x64.sol\";\r\nimport {ABDKMath64x64Token} from \"../libraries/ABDKMath64x64Token.sol\";\r\nimport {OptionMath} from \"../libraries/OptionMath.sol\";\r\nimport {IFeeDiscount} from \"../staking/IFeeDiscount.sol\";\r\nimport {IPoolEvents} from \"./IPoolEvents.sol\";\r\nimport {IPremiaMining} from \"../mining/IPremiaMining.sol\";\r\nimport {IVolatilitySurfaceOracle} from \"../oracle/IVolatilitySurfaceOracle.sol\";\r\n\r\n/**\r\n * @title Premia option pool\r\n * @dev deployed standalone and referenced by PoolProxy\r\n */\r\ncontract PoolInternal is IPoolEvents, ERC1155EnumerableInternal {\r\n using ABDKMath64x64 for int128;\r\n using EnumerableSet for EnumerableSet.AddressSet;\r\n using EnumerableSet for EnumerableSet.UintSet;\r\n using PoolStorage for PoolStorage.Layout;\r\n\r\n address internal immutable WETH_ADDRESS;\r\n address internal immutable PREMIA_MINING_ADDRESS;\r\n address internal immutable FEE_RECEIVER_ADDRESS;\r\n address internal immutable FEE_DISCOUNT_ADDRESS;\r\n address internal immutable IVOL_ORACLE_ADDRESS;\r\n\r\n int128 internal immutable FEE_64x64;\r\n\r\n uint256 internal immutable UNDERLYING_FREE_LIQ_TOKEN_ID;\r\n uint256 internal immutable BASE_FREE_LIQ_TOKEN_ID;\r\n\r\n uint256 internal immutable UNDERLYING_RESERVED_LIQ_TOKEN_ID;\r\n uint256 internal immutable BASE_RESERVED_LIQ_TOKEN_ID;\r\n\r\n uint256 internal constant INVERSE_BASIS_POINT = 1e4;\r\n uint256 internal constant BATCHING_PERIOD = 260;\r\n\r\n // Minimum APY for capital locked up to underwrite options.\r\n // The quote will return a minimum price corresponding to this APY\r\n int128 internal constant MIN_APY_64x64 = 0x4ccccccccccccccd; // 0.3\r\n\r\n constructor(\r\n address ivolOracle,\r\n address weth,\r\n address premiaMining,\r\n address feeReceiver,\r\n address feeDiscountAddress,\r\n int128 fee64x64\r\n ) {\r\n IVOL_ORACLE_ADDRESS = ivolOracle;\r\n WETH_ADDRESS = weth;\r\n PREMIA_MINING_ADDRESS = premiaMining;\r\n FEE_RECEIVER_ADDRESS = feeReceiver;\r\n // PremiaFeeDiscount contract address\r\n FEE_DISCOUNT_ADDRESS = feeDiscountAddress;\r\n FEE_64x64 = fee64x64;\r\n\r\n UNDERLYING_FREE_LIQ_TOKEN_ID = PoolStorage.formatTokenId(\r\n PoolStorage.TokenType.UNDERLYING_FREE_LIQ,\r\n 0,\r\n 0\r\n );\r\n BASE_FREE_LIQ_TOKEN_ID = PoolStorage.formatTokenId(\r\n PoolStorage.TokenType.BASE_FREE_LIQ,\r\n 0,\r\n 0\r\n );\r\n\r\n UNDERLYING_RESERVED_LIQ_TOKEN_ID = PoolStorage.formatTokenId(\r\n PoolStorage.TokenType.UNDERLYING_RESERVED_LIQ,\r\n 0,\r\n 0\r\n );\r\n BASE_RESERVED_LIQ_TOKEN_ID = PoolStorage.formatTokenId(\r\n PoolStorage.TokenType.BASE_RESERVED_LIQ,\r\n 0,\r\n 0\r\n );\r\n }\r\n\r\n modifier onlyProtocolOwner() {\r\n require(\r\n msg.sender == IERC173(OwnableStorage.layout().owner).owner(),\r\n \"Not protocol owner\"\r\n );\r\n _;\r\n }\r\n\r\n function _getFeeDiscount(address feePayer)\r\n internal\r\n view\r\n returns (uint256 discount)\r\n {\r\n if (FEE_DISCOUNT_ADDRESS != address(0)) {\r\n discount = IFeeDiscount(FEE_DISCOUNT_ADDRESS).getDiscount(feePayer);\r\n }\r\n }\r\n\r\n function _getFeeWithDiscount(address feePayer, uint256 fee)\r\n internal\r\n view\r\n returns (uint256)\r\n {\r\n uint256 discount = _getFeeDiscount(feePayer);\r\n return fee - ((fee * discount) / INVERSE_BASIS_POINT);\r\n }\r\n\r\n function _withdrawFees(bool isCall) internal returns (uint256 amount) {\r\n uint256 tokenId = _getReservedLiquidityTokenId(isCall);\r\n amount = _balanceOf(FEE_RECEIVER_ADDRESS, tokenId);\r\n\r\n if (amount > 0) {\r\n _burn(FEE_RECEIVER_ADDRESS, tokenId, amount);\r\n emit FeeWithdrawal(isCall, amount);\r\n }\r\n }\r\n\r\n /**\r\n * @notice calculate price of option contract\r\n * @param args structured quote arguments\r\n * @return result quote result\r\n */\r\n function _quote(PoolStorage.QuoteArgsInternal memory args)\r\n internal\r\n view\r\n returns (PoolStorage.QuoteResultInternal memory result)\r\n {\r\n require(\r\n args.strike64x64 > 0 && args.spot64x64 > 0 && args.maturity > 0,\r\n \"invalid args\"\r\n );\r\n PoolStorage.Layout storage l = PoolStorage.layout();\r\n\r\n int128 contractSize64x64 = ABDKMath64x64Token.fromDecimals(\r\n args.contractSize,\r\n l.underlyingDecimals\r\n );\r\n bool isCall = args.isCall;\r\n\r\n (int128 adjustedCLevel64x64, int128 oldLiquidity64x64) = l\r\n .applyCLevelPendingDepositAdjustment(\r\n l.getDecayAdjustedCLevel64x64(isCall),\r\n l.totalFreeLiquiditySupply64x64(isCall),\r\n isCall\r\n );\r\n\r\n require(oldLiquidity64x64 > 0, \"no liq\");\r\n\r\n int128 timeToMaturity64x64 = ABDKMath64x64.divu(\r\n args.maturity - block.timestamp,\r\n 365 days\r\n );\r\n\r\n int128 annualizedVolatility64x64 = IVolatilitySurfaceOracle(\r\n IVOL_ORACLE_ADDRESS\r\n ).getAnnualizedVolatility64x64(\r\n l.base,\r\n l.underlying,\r\n args.spot64x64,\r\n args.strike64x64,\r\n timeToMaturity64x64,\r\n isCall\r\n );\r\n\r\n require(annualizedVolatility64x64 > 0, \"vol = 0\");\r\n\r\n (\r\n int128 price64x64,\r\n int128 cLevel64x64,\r\n int128 slippageCoefficient64x64\r\n ) = OptionMath.quotePrice(\r\n OptionMath.QuoteArgs(\r\n annualizedVolatility64x64.mul(annualizedVolatility64x64),\r\n args.strike64x64,\r\n args.spot64x64,\r\n timeToMaturity64x64,\r\n adjustedCLevel64x64,\r\n oldLiquidity64x64,\r\n oldLiquidity64x64.sub(contractSize64x64),\r\n 0x10000000000000000, // 64x64 fixed point representation of 1\r\n MIN_APY_64x64,\r\n isCall\r\n )\r\n );\r\n\r\n result.baseCost64x64 = isCall\r\n ? price64x64.mul(contractSize64x64).div(args.spot64x64)\r\n : price64x64.mul(contractSize64x64);\r\n result.feeCost64x64 = result.baseCost64x64.mul(FEE_64x64);\r\n result.cLevel64x64 = cLevel64x64;\r\n result.slippageCoefficient64x64 = slippageCoefficient64x64;\r\n\r\n int128 discount = ABDKMath64x64.divu(\r\n _getFeeDiscount(args.feePayer),\r\n INVERSE_BASIS_POINT\r\n );\r\n result.feeCost64x64 -= result.feeCost64x64.mul(discount);\r\n }\r\n\r\n /**\r\n * @notice burn corresponding long and short option tokens\r\n * @param account holder of tokens to annihilate\r\n * @param maturity timestamp of option maturity\r\n * @param strike64x64 64x64 fixed point representation of strike price\r\n * @param isCall true for call, false for put\r\n * @param contractSize quantity of option contract tokens to annihilate\r\n */\r\n function _annihilate(\r\n address account,\r\n uint64 maturity,\r\n int128 strike64x64,\r\n bool isCall,\r\n uint256 contractSize\r\n ) internal {\r\n uint256 longTokenId = PoolStorage.formatTokenId(\r\n _getTokenType(isCall, true),\r\n maturity,\r\n strike64x64\r\n );\r\n uint256 shortTokenId = PoolStorage.formatTokenId(\r\n _getTokenType(isCall, false),\r\n maturity,\r\n strike64x64\r\n );\r\n\r\n _burn(account, longTokenId, contractSize);\r\n _burn(account, shortTokenId, contractSize);\r\n\r\n emit Annihilate(shortTokenId, contractSize);\r\n }\r\n\r\n /**\r\n * @notice purchase option\r\n * @param l storage layout struct\r\n * @param account recipient of purchased option\r\n * @param maturity timestamp of option maturity\r\n * @param strike64x64 64x64 fixed point representation of strike price\r\n * @param isCall true for call, false for put\r\n * @param contractSize size of option contract\r\n * @param newPrice64x64 64x64 fixed point representation of current spot price\r\n * @return baseCost quantity of tokens required to purchase long position\r\n * @return feeCost quantity of tokens required to pay fees\r\n */\r\n function _purchase(\r\n PoolStorage.Layout storage l,\r\n address account,\r\n uint64 maturity,\r\n int128 strike64x64,\r\n bool isCall,\r\n uint256 contractSize,\r\n int128 newPrice64x64\r\n ) internal returns (uint256 baseCost, uint256 feeCost) {\r\n require(maturity > block.timestamp, \"expired\");\r\n require(contractSize >= l.underlyingMinimum, \"too small\");\r\n\r\n {\r\n uint256 size = isCall\r\n ? contractSize\r\n : l.fromUnderlyingToBaseDecimals(\r\n strike64x64.mulu(contractSize)\r\n );\r\n\r\n require(\r\n size <=\r\n ERC1155EnumerableStorage.layout().totalSupply[\r\n _getFreeLiquidityTokenId(isCall)\r\n ] -\r\n l.nextDeposits[isCall].totalPendingDeposits,\r\n \"insuf liq\"\r\n );\r\n }\r\n\r\n PoolStorage.QuoteResultInternal memory quote = _quote(\r\n PoolStorage.QuoteArgsInternal(\r\n account,\r\n maturity,\r\n strike64x64,\r\n newPrice64x64,\r\n contractSize,\r\n isCall\r\n )\r\n );\r\n\r\n baseCost = ABDKMath64x64Token.toDecimals(\r\n quote.baseCost64x64,\r\n l.getTokenDecimals(isCall)\r\n );\r\n\r\n feeCost = ABDKMath64x64Token.toDecimals(\r\n quote.feeCost64x64,\r\n l.getTokenDecimals(isCall)\r\n );\r\n\r\n uint256 longTokenId = PoolStorage.formatTokenId(\r\n _getTokenType(isCall, true),\r\n maturity,\r\n strike64x64\r\n );\r\n\r\n uint256 shortTokenId = PoolStorage.formatTokenId(\r\n _getTokenType(isCall, false),\r\n maturity,\r\n strike64x64\r\n );\r\n\r\n // mint long option token for buyer\r\n _mint(account, longTokenId, contractSize);\r\n\r\n int128 oldLiquidity64x64 = l.totalFreeLiquiditySupply64x64(isCall);\r\n // burn free liquidity tokens from other underwriters\r\n _mintShortTokenLoop(\r\n l,\r\n account,\r\n contractSize,\r\n baseCost,\r\n shortTokenId,\r\n isCall\r\n );\r\n int128 newLiquidity64x64 = l.totalFreeLiquiditySupply64x64(isCall);\r\n\r\n _setCLevel(l, oldLiquidity64x64, newLiquidity64x64, isCall);\r\n\r\n // mint reserved liquidity tokens for fee receiver\r\n _mint(\r\n FEE_RECEIVER_ADDRESS,\r\n _getReservedLiquidityTokenId(isCall),\r\n feeCost\r\n );\r\n\r\n emit Purchase(\r\n account,\r\n longTokenId,\r\n contractSize,\r\n baseCost,\r\n feeCost,\r\n newPrice64x64\r\n );\r\n }\r\n\r\n /**\r\n * @notice reassign short position to new underwriter\r\n * @param l storage layout struct\r\n * @param account holder of positions to be reassigned\r\n * @param maturity timestamp of option maturity\r\n * @param strike64x64 64x64 fixed point representation of strike price\r\n * @param isCall true for call, false for put\r\n * @param contractSize quantity of option contract tokens to reassign\r\n * @param newPrice64x64 64x64 fixed point representation of current spot price\r\n * @return baseCost quantity of tokens required to reassign short position\r\n * @return feeCost quantity of tokens required to pay fees\r\n * @return amountOut quantity of liquidity freed\r\n */\r\n function _reassign(\r\n PoolStorage.Layout storage l,\r\n address account,\r\n uint64 maturity,\r\n int128 strike64x64,\r\n bool isCall,\r\n uint256 contractSize,\r\n int128 newPrice64x64\r\n )\r\n internal\r\n returns (\r\n uint256 baseCost,\r\n uint256 feeCost,\r\n uint256 amountOut\r\n )\r\n {\r\n (baseCost, feeCost) = _purchase(\r\n l,\r\n account,\r\n maturity,\r\n strike64x64,\r\n isCall,\r\n contractSize,\r\n newPrice64x64\r\n );\r\n\r\n _annihilate(account, maturity, strike64x64, isCall, contractSize);\r\n\r\n uint256 annihilateAmount = isCall\r\n ? contractSize\r\n : l.fromUnderlyingToBaseDecimals(strike64x64.mulu(contractSize));\r\n\r\n amountOut = annihilateAmount - baseCost - feeCost;\r\n }\r\n\r\n /**\r\n * @notice exercise option on behalf of holder\r\n * @dev used for processing of expired options if passed holder is zero address\r\n * @param holder owner of long option tokens to exercise\r\n * @param longTokenId long option token id\r\n * @param contractSize quantity of tokens to exercise\r\n */\r\n function _exercise(\r\n address holder,\r\n uint256 longTokenId,\r\n uint256 contractSize\r\n ) internal {\r\n uint64 maturity;\r\n int128 strike64x64;\r\n bool isCall;\r\n\r\n bool onlyExpired = holder == address(0);\r\n\r\n {\r\n PoolStorage.TokenType tokenType;\r\n (tokenType, maturity, strike64x64) = PoolStorage.parseTokenId(\r\n longTokenId\r\n );\r\n require(\r\n tokenType == PoolStorage.TokenType.LONG_CALL ||\r\n tokenType == PoolStorage.TokenType.LONG_PUT,\r\n \"invalid type\"\r\n );\r\n require(!onlyExpired || maturity < block.timestamp, \"not expired\");\r\n isCall = tokenType == PoolStorage.TokenType.LONG_CALL;\r\n }\r\n\r\n PoolStorage.Layout storage l = PoolStorage.layout();\r\n\r\n int128 spot64x64 = _update(l);\r\n\r\n if (maturity < block.timestamp) {\r\n spot64x64 = l.getPriceUpdateAfter(maturity);\r\n }\r\n\r\n require(\r\n onlyExpired ||\r\n (\r\n isCall\r\n ? (spot64x64 > strike64x64)\r\n : (spot64x64 < strike64x64)\r\n ),\r\n \"not ITM\"\r\n );\r\n\r\n uint256 exerciseValue;\r\n // option has a non-zero exercise value\r\n if (isCall) {\r\n if (spot64x64 > strike64x64) {\r\n exerciseValue = spot64x64.sub(strike64x64).div(spot64x64).mulu(\r\n contractSize\r\n );\r\n }\r\n } else {\r\n if (spot64x64 < strike64x64) {\r\n exerciseValue = l.fromUnderlyingToBaseDecimals(\r\n strike64x64.sub(spot64x64).mulu(contractSize)\r\n );\r\n }\r\n }\r\n\r\n uint256 totalFee;\r\n\r\n if (onlyExpired) {\r\n totalFee += _burnLongTokenLoop(\r\n contractSize,\r\n exerciseValue,\r\n longTokenId,\r\n isCall\r\n );\r\n } else {\r\n // burn long option tokens from sender\r\n _burn(holder, longTokenId, contractSize);\r\n\r\n uint256 fee;\r\n\r\n if (exerciseValue > 0) {\r\n fee = _getFeeWithDiscount(\r\n holder,\r\n FEE_64x64.mulu(exerciseValue)\r\n );\r\n totalFee += fee;\r\n\r\n _pushTo(holder, _getPoolToken(isCall), exerciseValue - fee);\r\n }\r\n\r\n emit Exercise(\r\n holder,\r\n longTokenId,\r\n contractSize,\r\n exerciseValue,\r\n fee\r\n );\r\n }\r\n\r\n totalFee += _burnShortTokenLoop(\r\n contractSize,\r\n exerciseValue,\r\n PoolStorage.formatTokenId(\r\n _getTokenType(isCall, false),\r\n maturity,\r\n strike64x64\r\n ),\r\n isCall\r\n );\r\n\r\n _mint(\r\n FEE_RECEIVER_ADDRESS,\r\n _getReservedLiquidityTokenId(isCall),\r\n totalFee\r\n );\r\n }\r\n\r\n function _mintShortTokenLoop(\r\n PoolStorage.Layout storage l,\r\n address buyer,\r\n uint256 contractSize,\r\n uint256 premium,\r\n uint256 shortTokenId,\r\n bool isCall\r\n ) internal {\r\n uint256 freeLiqTokenId = _getFreeLiquidityTokenId(isCall);\r\n (, , int128 strike64x64) = PoolStorage.parseTokenId(shortTokenId);\r\n\r\n uint256 toPay = isCall\r\n ? contractSize\r\n : l.fromUnderlyingToBaseDecimals(strike64x64.mulu(contractSize));\r\n\r\n while (toPay > 0) {\r\n address underwriter = l.liquidityQueueAscending[isCall][address(0)];\r\n uint256 balance = _balanceOf(underwriter, freeLiqTokenId);\r\n\r\n // If dust left, we remove underwriter and skip to next\r\n if (balance < _getMinimumAmount(l, isCall)) {\r\n l.removeUnderwriter(underwriter, isCall);\r\n continue;\r\n }\r\n\r\n if (!l.getReinvestmentStatus(underwriter, isCall)) {\r\n _burn(underwriter, freeLiqTokenId, balance);\r\n _mint(\r\n underwriter,\r\n _getReservedLiquidityTokenId(isCall),\r\n balance\r\n );\r\n _subUserTVL(l, underwriter, isCall, balance);\r\n continue;\r\n }\r\n\r\n // amount of liquidity provided by underwriter, accounting for reinvested premium\r\n uint256 intervalContractSize = ((balance -\r\n l.pendingDeposits[underwriter][l.nextDeposits[isCall].eta][\r\n isCall\r\n ]) * (toPay + premium)) / toPay;\r\n if (intervalContractSize == 0) continue;\r\n if (intervalContractSize > toPay) intervalContractSize = toPay;\r\n\r\n // amount of premium paid to underwriter\r\n uint256 intervalPremium = (premium * intervalContractSize) / toPay;\r\n premium -= intervalPremium;\r\n toPay -= intervalContractSize;\r\n _addUserTVL(l, underwriter, isCall, intervalPremium);\r\n\r\n // burn free liquidity tokens from underwriter\r\n _burn(\r\n underwriter,\r\n freeLiqTokenId,\r\n intervalContractSize - intervalPremium\r\n );\r\n\r\n if (isCall == false) {\r\n // For PUT, conversion to contract amount is done here (Prior to this line, it is token amount)\r\n intervalContractSize = l.fromBaseToUnderlyingDecimals(\r\n strike64x64.inv().mulu(intervalContractSize)\r\n );\r\n }\r\n\r\n // mint short option tokens for underwriter\r\n // toPay == 0 ? contractSize : intervalContractSize : To prevent minting less than amount,\r\n // because of rounding (Can happen for put, because of fixed point precision)\r\n _mint(\r\n underwriter,\r\n shortTokenId,\r\n toPay == 0 ? contractSize : intervalContractSize\r\n );\r\n\r\n emit Underwrite(\r\n underwriter,\r\n buyer,\r\n shortTokenId,\r\n toPay == 0 ? contractSize : intervalContractSize,\r\n intervalPremium,\r\n false\r\n );\r\n\r\n contractSize -= intervalContractSize;\r\n }\r\n }\r\n\r\n function _burnLongTokenLoop(\r\n uint256 contractSize,\r\n uint256 exerciseValue,\r\n uint256 longTokenId,\r\n bool isCall\r\n ) internal returns (uint256 totalFee) {\r\n EnumerableSet.AddressSet storage holders = ERC1155EnumerableStorage\r\n .layout()\r\n .accountsByToken[longTokenId];\r\n\r\n while (contractSize > 0) {\r\n address longTokenHolder = holders.at(holders.length() - 1);\r\n\r\n uint256 intervalContractSize = _balanceOf(\r\n longTokenHolder,\r\n longTokenId\r\n );\r\n if (intervalContractSize > contractSize)\r\n intervalContractSize = contractSize;\r\n\r\n uint256 intervalExerciseValue;\r\n\r\n uint256 fee;\r\n if (exerciseValue > 0) {\r\n intervalExerciseValue =\r\n (exerciseValue * intervalContractSize) /\r\n contractSize;\r\n\r\n fee = _getFeeWithDiscount(\r\n longTokenHolder,\r\n FEE_64x64.mulu(intervalExerciseValue)\r\n );\r\n totalFee += fee;\r\n\r\n exerciseValue -= intervalExerciseValue;\r\n _pushTo(\r\n longTokenHolder,\r\n _getPoolToken(isCall),\r\n intervalExerciseValue - fee\r\n );\r\n }\r\n\r\n contractSize -= intervalContractSize;\r\n\r\n emit Exercise(\r\n longTokenHolder,\r\n longTokenId,\r\n intervalContractSize,\r\n intervalExerciseValue - fee,\r\n fee\r\n );\r\n\r\n _burn(longTokenHolder, longTokenId, intervalContractSize);\r\n }\r\n }\r\n\r\n function _burnShortTokenLoop(\r\n uint256 contractSize,\r\n uint256 exerciseValue,\r\n uint256 shortTokenId,\r\n bool isCall\r\n ) internal returns (uint256 totalFee) {\r\n EnumerableSet.AddressSet storage underwriters = ERC1155EnumerableStorage\r\n .layout()\r\n .accountsByToken[shortTokenId];\r\n (, , int128 strike64x64) = PoolStorage.parseTokenId(shortTokenId);\r\n\r\n while (contractSize > 0) {\r\n address underwriter = underwriters.at(underwriters.length() - 1);\r\n\r\n // amount of liquidity provided by underwriter\r\n uint256 intervalContractSize = _balanceOf(\r\n underwriter,\r\n shortTokenId\r\n );\r\n if (intervalContractSize > contractSize)\r\n intervalContractSize = contractSize;\r\n\r\n // amount of value claimed by buyer\r\n uint256 intervalExerciseValue = (exerciseValue *\r\n intervalContractSize) / contractSize;\r\n exerciseValue -= intervalExerciseValue;\r\n contractSize -= intervalContractSize;\r\n\r\n uint256 freeLiq = isCall\r\n ? intervalContractSize - intervalExerciseValue\r\n : PoolStorage.layout().fromUnderlyingToBaseDecimals(\r\n strike64x64.mulu(intervalContractSize)\r\n ) - intervalExerciseValue;\r\n\r\n uint256 fee = _getFeeWithDiscount(\r\n underwriter,\r\n FEE_64x64.mulu(freeLiq)\r\n );\r\n totalFee += fee;\r\n\r\n uint256 tvlToSubtract = intervalExerciseValue;\r\n\r\n // mint free liquidity tokens for underwriter\r\n if (\r\n PoolStorage.layout().getReinvestmentStatus(underwriter, isCall)\r\n ) {\r\n _addToDepositQueue(underwriter, freeLiq - fee, isCall);\r\n tvlToSubtract += fee;\r\n } else {\r\n _mint(\r\n underwriter,\r\n _getReservedLiquidityTokenId(isCall),\r\n freeLiq - fee\r\n );\r\n tvlToSubtract += freeLiq;\r\n }\r\n\r\n _subUserTVL(\r\n PoolStorage.layout(),\r\n underwriter,\r\n isCall,\r\n tvlToSubtract\r\n );\r\n\r\n // burn short option tokens from underwriter\r\n _burn(underwriter, shortTokenId, intervalContractSize);\r\n\r\n emit AssignExercise(\r\n underwriter,\r\n shortTokenId,\r\n freeLiq - fee,\r\n intervalContractSize,\r\n fee\r\n );\r\n }\r\n }\r\n\r\n function _addToDepositQueue(\r\n address account,\r\n uint256 amount,\r\n bool isCallPool\r\n ) internal {\r\n PoolStorage.Layout storage l = PoolStorage.layout();\r\n\r\n _mint(account, _getFreeLiquidityTokenId(isCallPool), amount);\r\n\r\n uint256 nextBatch = (block.timestamp / BATCHING_PERIOD) *\r\n BATCHING_PERIOD +\r\n BATCHING_PERIOD;\r\n l.pendingDeposits[account][nextBatch][isCallPool] += amount;\r\n\r\n PoolStorage.BatchData storage batchData = l.nextDeposits[isCallPool];\r\n batchData.totalPendingDeposits += amount;\r\n batchData.eta = nextBatch;\r\n }\r\n\r\n function _processPendingDeposits(PoolStorage.Layout storage l, bool isCall)\r\n internal\r\n {\r\n PoolStorage.BatchData storage data = l.nextDeposits[isCall];\r\n\r\n if (data.eta == 0 || block.timestamp < data.eta) return;\r\n\r\n int128 oldLiquidity64x64 = l.totalFreeLiquiditySupply64x64(isCall);\r\n\r\n _setCLevel(\r\n l,\r\n oldLiquidity64x64,\r\n oldLiquidity64x64.add(\r\n ABDKMath64x64Token.fromDecimals(\r\n data.totalPendingDeposits,\r\n l.getTokenDecimals(isCall)\r\n )\r\n ),\r\n isCall\r\n );\r\n\r\n delete l.nextDeposits[isCall];\r\n }\r\n\r\n function _getFreeLiquidityTokenId(bool isCall)\r\n internal\r\n view\r\n returns (uint256 freeLiqTokenId)\r\n {\r\n freeLiqTokenId = isCall\r\n ? UNDERLYING_FREE_LIQ_TOKEN_ID\r\n : BASE_FREE_LIQ_TOKEN_ID;\r\n }\r\n\r\n function _getReservedLiquidityTokenId(bool isCall)\r\n internal\r\n view\r\n returns (uint256 reservedLiqTokenId)\r\n {\r\n reservedLiqTokenId = isCall\r\n ? UNDERLYING_RESERVED_LIQ_TOKEN_ID\r\n : BASE_RESERVED_LIQ_TOKEN_ID;\r\n }\r\n\r\n function _getPoolToken(bool isCall) internal view returns (address token) {\r\n token = isCall\r\n ? PoolStorage.layout().underlying\r\n : PoolStorage.layout().base;\r\n }\r\n\r\n function _getTokenType(bool isCall, bool isLong)\r\n internal\r\n pure\r\n returns (PoolStorage.TokenType tokenType)\r\n {\r\n if (isCall) {\r\n tokenType = isLong\r\n ? PoolStorage.TokenType.LONG_CALL\r\n : PoolStorage.TokenType.SHORT_CALL;\r\n } else {\r\n tokenType = isLong\r\n ? PoolStorage.TokenType.LONG_PUT\r\n : PoolStorage.TokenType.SHORT_PUT;\r\n }\r\n }\r\n\r\n function _getMinimumAmount(PoolStorage.Layout storage l, bool isCall)\r\n internal\r\n view\r\n returns (uint256 minimumAmount)\r\n {\r\n minimumAmount = isCall ? l.underlyingMinimum : l.baseMinimum;\r\n }\r\n\r\n function _getPoolCapAmount(PoolStorage.Layout storage l, bool isCall)\r\n internal\r\n view\r\n returns (uint256 poolCapAmount)\r\n {\r\n poolCapAmount = isCall ? l.underlyingPoolCap : l.basePoolCap;\r\n }\r\n\r\n function _setCLevel(\r\n PoolStorage.Layout storage l,\r\n int128 oldLiquidity64x64,\r\n int128 newLiquidity64x64,\r\n bool isCallPool\r\n ) internal {\r\n int128 oldCLevel64x64 = l.getDecayAdjustedCLevel64x64(isCallPool);\r\n\r\n int128 cLevel64x64 = l.applyCLevelLiquidityChangeAdjustment(\r\n oldCLevel64x64,\r\n oldLiquidity64x64,\r\n newLiquidity64x64,\r\n isCallPool\r\n );\r\n\r\n l.setCLevel(cLevel64x64, isCallPool);\r\n\r\n emit UpdateCLevel(\r\n isCallPool,\r\n cLevel64x64,\r\n oldLiquidity64x64,\r\n newLiquidity64x64\r\n );\r\n }\r\n\r\n /**\r\n * @notice calculate and store updated market state\r\n * @param l storage layout struct\r\n * @return newPrice64x64 64x64 fixed point representation of current spot price\r\n */\r\n function _update(PoolStorage.Layout storage l)\r\n internal\r\n returns (int128 newPrice64x64)\r\n {\r\n if (l.updatedAt == block.timestamp) {\r\n return (l.getPriceUpdate(block.timestamp));\r\n }\r\n\r\n newPrice64x64 = l.fetchPriceUpdate();\r\n\r\n if (l.getPriceUpdate(block.timestamp) == 0) {\r\n l.setPriceUpdate(block.timestamp, newPrice64x64);\r\n }\r\n\r\n l.updatedAt = block.timestamp;\r\n\r\n _processPendingDeposits(l, true);\r\n _processPendingDeposits(l, false);\r\n }\r\n\r\n /**\r\n * @notice transfer ERC20 tokens to message sender\r\n * @param token ERC20 token address\r\n * @param amount quantity of token to transfer\r\n */\r\n function _pushTo(\r\n address to,\r\n address token,\r\n uint256 amount\r\n ) internal {\r\n if (amount == 0) return;\r\n\r\n require(IERC20(token).transfer(to, amount), \"ERC20 transfer failed\");\r\n }\r\n\r\n /**\r\n * @notice transfer ERC20 tokens from message sender\r\n * @param from address from which tokens are pulled from\r\n * @param token ERC20 token address\r\n * @param amount quantity of token to transfer\r\n * @param skipWethDeposit if false, will not try to deposit weth from attach eth\r\n */\r\n function _pullFrom(\r\n address from,\r\n address token,\r\n uint256 amount,\r\n bool skipWethDeposit\r\n ) internal {\r\n if (!skipWethDeposit) {\r\n if (token == WETH_ADDRESS) {\r\n if (msg.value > 0) {\r\n if (msg.value > amount) {\r\n IWETH(WETH_ADDRESS).deposit{value: amount}();\r\n\r\n (bool success, ) = payable(msg.sender).call{\r\n value: msg.value - amount\r\n }(\"\");\r\n\r\n require(success, \"ETH refund failed\");\r\n\r\n amount = 0;\r\n } else {\r\n unchecked {\r\n amount -= msg.value;\r\n }\r\n\r\n IWETH(WETH_ADDRESS).deposit{value: msg.value}();\r\n }\r\n }\r\n } else {\r\n require(msg.value == 0, \"not WETH deposit\");\r\n }\r\n }\r\n\r\n if (amount > 0) {\r\n require(\r\n IERC20(token).transferFrom(from, address(this), amount),\r\n \"ERC20 transfer failed\"\r\n );\r\n }\r\n }\r\n\r\n function _mint(\r\n address account,\r\n uint256 tokenId,\r\n uint256 amount\r\n ) internal {\r\n _mint(account, tokenId, amount, \"\");\r\n }\r\n\r\n function _addUserTVL(\r\n PoolStorage.Layout storage l,\r\n address user,\r\n bool isCallPool,\r\n uint256 amount\r\n ) internal {\r\n uint256 userTVL = l.userTVL[user][isCallPool];\r\n uint256 totalTVL = l.totalTVL[isCallPool];\r\n\r\n IPremiaMining(PREMIA_MINING_ADDRESS).allocatePending(\r\n user,\r\n address(this),\r\n isCallPool,\r\n userTVL,\r\n userTVL + amount,\r\n totalTVL\r\n );\r\n\r\n l.userTVL[user][isCallPool] = userTVL + amount;\r\n l.totalTVL[isCallPool] = totalTVL + amount;\r\n }\r\n\r\n function _subUserTVL(\r\n PoolStorage.Layout storage l,\r\n address user,\r\n bool isCallPool,\r\n uint256 amount\r\n ) internal {\r\n uint256 userTVL = l.userTVL[user][isCallPool];\r\n uint256 totalTVL = l.totalTVL[isCallPool];\r\n\r\n IPremiaMining(PREMIA_MINING_ADDRESS).allocatePending(\r\n user,\r\n address(this),\r\n isCallPool,\r\n userTVL,\r\n userTVL - amount,\r\n totalTVL\r\n );\r\n l.userTVL[user][isCallPool] = userTVL - amount;\r\n l.totalTVL[isCallPool] = totalTVL - amount;\r\n }\r\n\r\n /**\r\n * @notice ERC1155 hook: track eligible underwriters\r\n * @param operator transaction sender\r\n * @param from token sender\r\n * @param to token receiver\r\n * @param ids token ids transferred\r\n * @param amounts token quantities transferred\r\n * @param data data payload\r\n */\r\n function _beforeTokenTransfer(\r\n address operator,\r\n address from,\r\n address to,\r\n uint256[] memory ids,\r\n uint256[] memory amounts,\r\n bytes memory data\r\n ) internal virtual override {\r\n super._beforeTokenTransfer(operator, from, to, ids, amounts, data);\r\n\r\n PoolStorage.Layout storage l = PoolStorage.layout();\r\n\r\n for (uint256 i; i < ids.length; i++) {\r\n uint256 id = ids[i];\r\n uint256 amount = amounts[i];\r\n\r\n if (amount == 0) continue;\r\n\r\n if (from == address(0)) {\r\n l.tokenIds.add(id);\r\n }\r\n\r\n if (\r\n to == address(0) &&\r\n ERC1155EnumerableStorage.layout().totalSupply[id] == 0\r\n ) {\r\n l.tokenIds.remove(id);\r\n }\r\n\r\n // prevent transfer of free and reserved liquidity during waiting period\r\n\r\n if (\r\n id == UNDERLYING_FREE_LIQ_TOKEN_ID ||\r\n id == BASE_FREE_LIQ_TOKEN_ID ||\r\n id == UNDERLYING_RESERVED_LIQ_TOKEN_ID ||\r\n id == BASE_RESERVED_LIQ_TOKEN_ID\r\n ) {\r\n if (from != address(0) && to != address(0)) {\r\n bool isCallPool = id == UNDERLYING_FREE_LIQ_TOKEN_ID ||\r\n id == UNDERLYING_RESERVED_LIQ_TOKEN_ID;\r\n\r\n require(\r\n l.depositedAt[from][isCallPool] + (1 days) <\r\n block.timestamp,\r\n \"liq lock 1d\"\r\n );\r\n }\r\n }\r\n\r\n if (\r\n id == UNDERLYING_FREE_LIQ_TOKEN_ID ||\r\n id == BASE_FREE_LIQ_TOKEN_ID\r\n ) {\r\n bool isCallPool = id == UNDERLYING_FREE_LIQ_TOKEN_ID;\r\n uint256 minimum = _getMinimumAmount(l, isCallPool);\r\n\r\n if (from != address(0)) {\r\n uint256 balance = _balanceOf(from, id);\r\n\r\n if (balance > minimum && balance <= amount + minimum) {\r\n require(\r\n balance -\r\n l.pendingDeposits[from][\r\n l.nextDeposits[isCallPool].eta\r\n ][isCallPool] >=\r\n amount,\r\n \"Insuf balance\"\r\n );\r\n l.removeUnderwriter(from, isCallPool);\r\n }\r\n\r\n if (to != address(0)) {\r\n _subUserTVL(l, from, isCallPool, amounts[i]);\r\n _addUserTVL(l, to, isCallPool, amounts[i]);\r\n }\r\n }\r\n\r\n if (to != address(0)) {\r\n uint256 balance = _balanceOf(to, id);\r\n\r\n if (balance <= minimum && balance + amount > minimum) {\r\n l.addUnderwriter(to, isCallPool);\r\n }\r\n }\r\n }\r\n\r\n // Update userTVL on SHORT options transfers\r\n (\r\n PoolStorage.TokenType tokenType,\r\n ,\r\n int128 strike64x64\r\n ) = PoolStorage.parseTokenId(id);\r\n\r\n if (\r\n (from != address(0) && to != address(0)) &&\r\n (tokenType == PoolStorage.TokenType.SHORT_CALL ||\r\n tokenType == PoolStorage.TokenType.SHORT_PUT)\r\n ) {\r\n bool isCall = tokenType == PoolStorage.TokenType.SHORT_CALL;\r\n uint256 collateral = isCall\r\n ? amount\r\n : l.fromUnderlyingToBaseDecimals(strike64x64.mulu(amount));\r\n\r\n _subUserTVL(l, from, isCall, collateral);\r\n _addUserTVL(l, to, isCall, collateral);\r\n }\r\n }\r\n }\r\n}\r\n"},"@solidstate/contracts/token/ERC1155/base/ERC1155BaseStorage.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nlibrary ERC1155BaseStorage {\n struct Layout {\n mapping(uint256 => mapping(address => uint256)) balances;\n mapping(address => mapping(address => bool)) operatorApprovals;\n }\n\n bytes32 internal constant STORAGE_SLOT =\n keccak256('solidstate.contracts.storage.ERC1155Base');\n\n function layout() internal pure returns (Layout storage l) {\n bytes32 slot = STORAGE_SLOT;\n assembly {\n l.slot := slot\n }\n }\n}\n"},"@solidstate/contracts/utils/EnumerableSet.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @title Set implementation with enumeration functions\n * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts (MIT license)\n */\nlibrary EnumerableSet {\n struct Set {\n bytes32[] _values;\n // 1-indexed to allow 0 to signify nonexistence\n mapping(bytes32 => uint256) _indexes;\n }\n\n struct Bytes32Set {\n Set _inner;\n }\n\n struct AddressSet {\n Set _inner;\n }\n\n struct UintSet {\n Set _inner;\n }\n\n function at(Bytes32Set storage set, uint256 index)\n internal\n view\n returns (bytes32)\n {\n return _at(set._inner, index);\n }\n\n function at(AddressSet storage set, uint256 index)\n internal\n view\n returns (address)\n {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n function at(UintSet storage set, uint256 index)\n internal\n view\n returns (uint256)\n {\n return uint256(_at(set._inner, index));\n }\n\n function contains(Bytes32Set storage set, bytes32 value)\n internal\n view\n returns (bool)\n {\n return _contains(set._inner, value);\n }\n\n function contains(AddressSet storage set, address value)\n internal\n view\n returns (bool)\n {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n function contains(UintSet storage set, uint256 value)\n internal\n view\n returns (bool)\n {\n return _contains(set._inner, bytes32(value));\n }\n\n function indexOf(Bytes32Set storage set, bytes32 value)\n internal\n view\n returns (uint256)\n {\n return _indexOf(set._inner, value);\n }\n\n function indexOf(AddressSet storage set, address value)\n internal\n view\n returns (uint256)\n {\n return _indexOf(set._inner, bytes32(uint256(uint160(value))));\n }\n\n function indexOf(UintSet storage set, uint256 value)\n internal\n view\n returns (uint256)\n {\n return _indexOf(set._inner, bytes32(value));\n }\n\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n function add(Bytes32Set storage set, bytes32 value)\n internal\n returns (bool)\n {\n return _add(set._inner, value);\n }\n\n function add(AddressSet storage set, address value)\n internal\n returns (bool)\n {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n function remove(Bytes32Set storage set, bytes32 value)\n internal\n returns (bool)\n {\n return _remove(set._inner, value);\n }\n\n function remove(AddressSet storage set, address value)\n internal\n returns (bool)\n {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n function remove(UintSet storage set, uint256 value)\n internal\n returns (bool)\n {\n return _remove(set._inner, bytes32(value));\n }\n\n function _at(Set storage set, uint256 index)\n private\n view\n returns (bytes32)\n {\n require(\n set._values.length > index,\n 'EnumerableSet: index out of bounds'\n );\n return set._values[index];\n }\n\n function _contains(Set storage set, bytes32 value)\n private\n view\n returns (bool)\n {\n return set._indexes[value] != 0;\n }\n\n function _indexOf(Set storage set, bytes32 value)\n private\n view\n returns (uint256)\n {\n unchecked {\n return set._indexes[value] - 1;\n }\n }\n\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n uint256 index = valueIndex - 1;\n bytes32 last = set._values[set._values.length - 1];\n\n // move last value to now-vacant index\n\n set._values[index] = last;\n set._indexes[last] = index + 1;\n\n // clear last index\n\n set._values.pop();\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n}\n"},"contracts/mining/PremiaMiningStorage.sol":{"content":"// SPDX-License-Identifier: BUSL-1.1\r\n// For further clarification please see https://license.premia.legal\r\n\r\npragma solidity ^0.8.0;\r\n\r\nlibrary PremiaMiningStorage {\r\n bytes32 internal constant STORAGE_SLOT =\r\n keccak256(\"premia.contracts.storage.PremiaMining\");\r\n\r\n // Info of each pool.\r\n struct PoolInfo {\r\n uint256 allocPoint; // How many allocation points assigned to this pool. PREMIA to distribute per block.\r\n uint256 lastRewardTimestamp; // Last timestamp that PREMIA distribution occurs\r\n uint256 accPremiaPerShare; // Accumulated PREMIA per share, times 1e12. See below.\r\n }\r\n\r\n // Info of each user.\r\n struct UserInfo {\r\n uint256 reward; // Total allocated unclaimed reward\r\n uint256 rewardDebt; // Reward debt. See explanation below.\r\n //\r\n // We do some fancy math here. Basically, any point in time, the amount of PREMIA\r\n // entitled to a user but is pending to be distributed is:\r\n //\r\n // pending reward = (user.amount * pool.accPremiaPerShare) - user.rewardDebt\r\n //\r\n // Whenever a user deposits or withdraws LP tokens to a pool. Here's what happens:\r\n // 1. The pool's `accPremiaPerShare` (and `lastRewardBlock`) gets updated.\r\n // 2. User receives the pending reward sent to his/her address.\r\n // 3. User's `amount` gets updated.\r\n // 4. User's `rewardDebt` gets updated.\r\n }\r\n\r\n struct Layout {\r\n // Total PREMIA left to distribute\r\n uint256 premiaAvailable;\r\n // Amount of premia distributed per year\r\n uint256 premiaPerYear;\r\n // pool -> isCallPool -> PoolInfo\r\n mapping(address => mapping(bool => PoolInfo)) poolInfo;\r\n // pool -> isCallPool -> user -> UserInfo\r\n mapping(address => mapping(bool => mapping(address => UserInfo))) userInfo;\r\n // Total allocation points. Must be the sum of all allocation points in all pools.\r\n uint256 totalAllocPoint;\r\n }\r\n\r\n function layout() internal pure returns (Layout storage l) {\r\n bytes32 slot = STORAGE_SLOT;\r\n assembly {\r\n l.slot := slot\r\n }\r\n }\r\n}\r\n"},"@solidstate/contracts/utils/IWETH.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC20 } from '../token/ERC20/IERC20.sol';\nimport { IERC20Metadata } from '../token/ERC20/metadata/IERC20Metadata.sol';\n\n/**\n * @title WETH (Wrapped ETH) interface\n */\ninterface IWETH is IERC20, IERC20Metadata {\n /**\n * @notice convert ETH to WETH\n */\n function deposit() external payable;\n\n /**\n * @notice convert WETH to ETH\n * @dev if caller is a contract, it should have a fallback or receive function\n * @param amount quantity of WETH to convert, denominated in wei\n */\n function withdraw(uint256 amount) external;\n}\n"},"@solidstate/contracts/token/ERC1155/IERC1155Internal.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { IERC165 } from '../../introspection/IERC165.sol';\n\n/**\n * @notice Partial ERC1155 interface needed by internal functions\n */\ninterface IERC1155Internal {\n event TransferSingle(\n address indexed operator,\n address indexed from,\n address indexed to,\n uint256 id,\n uint256 value\n );\n\n event TransferBatch(\n address indexed operator,\n address indexed from,\n address indexed to,\n uint256[] ids,\n uint256[] values\n );\n\n event ApprovalForAll(\n address indexed account,\n address indexed operator,\n bool approved\n );\n}\n"},"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AggregatorV3Interface {\n\n function decimals()\n external\n view\n returns (\n uint8\n );\n\n function description()\n external\n view\n returns (\n string memory\n );\n\n function version()\n external\n view\n returns (\n uint256\n );\n\n // getRoundData and latestRoundData should both raise \"No data present\"\n // if they do not have data to report, instead of returning unset values\n // which could be misinterpreted as actual reported values.\n function getRoundData(\n uint80 _roundId\n )\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n\n function latestRoundData()\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n\n}\n"},"@solidstate/contracts/access/OwnableStorage.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nlibrary OwnableStorage {\n struct Layout {\n address owner;\n }\n\n bytes32 internal constant STORAGE_SLOT =\n keccak256('solidstate.contracts.storage.Ownable');\n\n function layout() internal pure returns (Layout storage l) {\n bytes32 slot = STORAGE_SLOT;\n assembly {\n l.slot := slot\n }\n }\n\n function setOwner(Layout storage l, address owner) internal {\n l.owner = owner;\n }\n}\n"}},"settings":{"optimizer":{"enabled":true,"runs":200},"outputSelection":{"*":{"*":["evm.bytecode","evm.deployedBytecode","devdoc","userdoc","metadata","abi"]}},"libraries":{"contracts/libraries/OptionMath.sol":{"OptionMath":"0x0f6e8ef18fb5bb61d545fee60f779d8aed60408f"}}}},"ABI":"[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"ivolOracle\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"weth\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"premiaMining\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeReceiver\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeDiscountAddress\",\"type\":\"address\"},{\"internalType\":\"int128\",\"name\":\"fee64x64\",\"type\":\"int128\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"shortTokenId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Annihilate\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"ApprovalForAll\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"underwriter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"shortTokenId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"freedAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"intervalContractSize\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"}],\"name\":\"AssignExercise\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isCallPool\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Deposit\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"longTokenId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"contractSize\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"exerciseValue\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"}],\"name\":\"Exercise\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"isCallPool\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"FeeWithdrawal\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"longTokenId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"contractSize\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"baseCost\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"feeCost\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"int128\",\"name\":\"spot64x64\",\"type\":\"int128\"}],\"name\":\"Purchase\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"ids\",\"type\":\"uint256[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"}],\"name\":\"TransferBatch\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"TransferSingle\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"underwriter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"longReceiver\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"shortTokenId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"intervalContractSize\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"intervalPremium\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isManualUnderwrite\",\"type\":\"bool\"}],\"name\":\"Underwrite\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"isCall\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"int128\",\"name\":\"cLevel64x64\",\"type\":\"int128\"},{\"indexed\":false,\"internalType\":\"int128\",\"name\":\"oldLiquidity64x64\",\"type\":\"int128\"},{\"indexed\":false,\"internalType\":\"int128\",\"name\":\"newLiquidity64x64\",\"type\":\"int128\"}],\"name\":\"UpdateCLevel\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"int128\",\"name\":\"steepness64x64\",\"type\":\"int128\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isCallPool\",\"type\":\"bool\"}],\"name\":\"UpdateSteepness\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isCallPool\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"depositedAt\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Withdrawal\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"holder\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"longTokenId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"contractSize\",\"type\":\"uint256\"}],\"name\":\"exerciseFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"longTokenId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"contractSize\",\"type\":\"uint256\"}],\"name\":\"processExpired\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]","ContractName":"PoolExercise","CompilerVersion":"v0.8.9+commit.e5eed63a","OptimizationUsed":1,"Runs":200,"ConstructorArguments":"0x0000000000000000000000003a87bb29b984d672664aa1dd2d19d2e8b24f0f2a000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000009abb27581c2e46a114f8c367355851e0580e9703000000000000000000000000c4b2c51f969e0713e799de73b7f130fb7bb604cf000000000000000000000000f1bb87563a122211d40d393ebf1c633c330377f900000000000000000000000000000000000000000000000007ae147ae147ae14","EVMVersion":"Default","Library":"","LicenseType":"","Proxy":0,"SwarmSource":""}] \ No newline at end of file From b7437499c5802f10d5b98b60f2c6d77a11b3d2f3 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 18 Apr 2024 20:09:00 +0200 Subject: [PATCH 189/430] feat: re-enable jemalloc for anvil (#7708) --- crates/anvil/Cargo.toml | 4 +--- crates/anvil/src/anvil.rs | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index d9d443643..7930d437a 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -105,6 +105,4 @@ default = ["cli"] cmd = ["clap", "clap_complete", "ctrlc", "anvil-server/clap"] cli = ["tokio/full", "cmd", "fdlimit"] asm-keccak = ["alloy-primitives/asm-keccak"] -# TODO: parity dependencies are not compatible with a different global allocator. -# jemalloc = ["dep:tikv-jemallocator"] -jemalloc = [] +jemalloc = ["dep:tikv-jemallocator"] diff --git a/crates/anvil/src/anvil.rs b/crates/anvil/src/anvil.rs index 54a5e41cf..1aa204587 100644 --- a/crates/anvil/src/anvil.rs +++ b/crates/anvil/src/anvil.rs @@ -4,8 +4,6 @@ use anvil::cmd::NodeArgs; use clap::{CommandFactory, Parser, Subcommand}; use foundry_cli::utils; -// TODO: parity dependencies are not compatible with a different global allocator. -#[cfg(any())] #[cfg(all(feature = "jemalloc", unix))] #[global_allocator] static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; From 358107683da49f4f57b8ff7123c5e119af7d9544 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 18 Apr 2024 20:12:23 +0200 Subject: [PATCH 190/430] chore(deps): bump ariadne to 0.4.0 (#7710) --- Cargo.lock | 4 ++-- crates/fmt/Cargo.toml | 2 +- crates/fmt/src/helpers.rs | 4 ++++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c7dcd51e0..17f662ae2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -800,9 +800,9 @@ dependencies = [ [[package]] name = "ariadne" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72fe02fc62033df9ba41cba57ee19acf5e742511a140c7dbc3a873e19a19a1bd" +checksum = "dd002a6223f12c7a95cdd4b1cb3a0149d22d37f7a9ecdb2cb691a071fe236c29" dependencies = [ "unicode-width", "yansi 0.5.1", diff --git a/crates/fmt/Cargo.toml b/crates/fmt/Cargo.toml index 0f49a10a1..c8937a281 100644 --- a/crates/fmt/Cargo.toml +++ b/crates/fmt/Cargo.toml @@ -14,7 +14,7 @@ foundry-config.workspace = true alloy-primitives.workspace = true -ariadne = "0.3" +ariadne = "0.4" itertools.workspace = true solang-parser.workspace = true thiserror = "1" diff --git a/crates/fmt/src/helpers.rs b/crates/fmt/src/helpers.rs index 8d472e3bb..9a31edeb0 100644 --- a/crates/fmt/src/helpers.rs +++ b/crates/fmt/src/helpers.rs @@ -81,6 +81,10 @@ pub fn print_diagnostics_report( path: Option<&Path>, diagnostics: Vec, ) -> std::io::Result<()> { + if diagnostics.is_empty() { + return Ok(()); + } + let filename = path.map(|p| p.file_name().unwrap().to_string_lossy().to_string()).unwrap_or_default(); for diag in diagnostics { From 18e0d030d259d8d9129e955516c6eaf919f937f8 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 18 Apr 2024 20:32:04 +0200 Subject: [PATCH 191/430] chore(deps): update to hyper 1.0 (#6470) --- Cargo.lock | 155 ++++++++++++++--------------- Cargo.toml | 12 +-- crates/anvil/server/Cargo.toml | 1 - crates/anvil/server/src/ipc.rs | 4 +- crates/anvil/server/src/lib.rs | 93 +++++++---------- crates/anvil/src/cmd.rs | 2 +- crates/anvil/src/lib.rs | 67 +++++++++---- crates/anvil/src/server/mod.rs | 57 +++++++---- crates/chisel/Cargo.toml | 2 +- crates/common/Cargo.toml | 2 +- crates/config/Cargo.toml | 2 +- crates/forge/Cargo.toml | 4 +- crates/forge/bin/cmd/doc/server.rs | 13 ++- crates/verify/Cargo.toml | 2 +- 14 files changed, 219 insertions(+), 197 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 17f662ae2..bd1fe244a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -558,7 +558,7 @@ dependencies = [ "http 0.2.12", "serde_json", "tokio", - "tokio-tungstenite", + "tokio-tungstenite 0.20.1", "tracing", "ws_stream_wasm", ] @@ -708,7 +708,7 @@ dependencies = [ "foundry-evm", "foundry-test-utils", "futures", - "hyper 0.14.28", + "hyper 1.2.0", "itertools 0.12.1", "k256", "parking_lot", @@ -771,7 +771,6 @@ dependencies = [ "bytes", "clap", "futures", - "hyper 0.14.28", "parity-tokio-ipc", "parking_lot", "pin-project", @@ -1409,19 +1408,20 @@ dependencies = [ [[package]] name = "axum" -version = "0.6.20" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" dependencies = [ "async-trait", "axum-core", "base64 0.21.7", - "bitflags 1.3.2", "bytes", "futures-util", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.28", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.2.0", + "hyper-util", "itoa", "matchit", "memchr", @@ -1434,29 +1434,34 @@ dependencies = [ "serde_path_to_error", "serde_urlencoded", "sha1", - "sync_wrapper", + "sync_wrapper 1.0.1", "tokio", - "tokio-tungstenite", + "tokio-tungstenite 0.21.0", "tower", "tower-layer", "tower-service", + "tracing", ] [[package]] name = "axum-core" -version = "0.3.4" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" dependencies = [ "async-trait", "bytes", "futures-util", - "http 0.2.12", - "http-body 0.4.6", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", "mime", + "pin-project-lite", "rustversion", + "sync_wrapper 0.1.2", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -1877,7 +1882,7 @@ dependencies = [ "foundry-evm", "once_cell", "regex", - "reqwest 0.11.27", + "reqwest 0.12.3", "revm", "rustyline", "semver 1.0.22", @@ -3158,7 +3163,7 @@ dependencies = [ "serde_json", "thiserror", "tokio", - "tokio-tungstenite", + "tokio-tungstenite 0.20.1", "tracing", "tracing-futures", "url", @@ -3494,7 +3499,7 @@ dependencies = [ "foundry-wallets", "futures", "globset", - "hyper 0.14.28", + "hyper 1.2.0", "indicatif", "itertools 0.12.1", "mockall", @@ -3507,7 +3512,7 @@ dependencies = [ "proptest", "rayon", "regex", - "reqwest 0.11.27", + "reqwest 0.12.3", "revm-inspectors", "rustc-hash", "semver 1.0.22", @@ -3516,7 +3521,7 @@ dependencies = [ "similar", "solang-parser", "strum", - "svm-rs 0.4.1", + "svm-rs 0.5.1", "tempfile", "thiserror", "tikv-jemallocator", @@ -3637,7 +3642,7 @@ dependencies = [ "futures", "once_cell", "regex", - "reqwest 0.11.27", + "reqwest 0.12.3", "revm-primitives", "semver 1.0.22", "serde", @@ -4128,16 +4133,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "fs4" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29f9df8a11882c4e3335eb2d18a0137c505d9ca927470b0cac9c6f0ae07d28f7" -dependencies = [ - "rustix", - "windows-sys 0.48.0", -] - [[package]] name = "fs4" version = "0.8.2" @@ -4812,9 +4807,9 @@ dependencies = [ [[package]] name = "http-range-header" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" +checksum = "3ce4ef31cda248bbdb6e6820603b82dfcd9e833db65a43e997a0ccec777d11fe" [[package]] name = "httparse" @@ -4870,6 +4865,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.0", "httparse", + "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -4910,19 +4906,6 @@ dependencies = [ "tower-service", ] -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper 0.14.28", - "native-tls", - "tokio", - "tokio-native-tls", -] - [[package]] name = "hyper-tls" version = "0.6.0" @@ -6927,12 +6910,10 @@ dependencies = [ "http-body 0.4.6", "hyper 0.14.28", "hyper-rustls 0.24.2", - "hyper-tls 0.5.0", "ipnet", "js-sys", "log", "mime", - "native-tls", "once_cell", "percent-encoding", "pin-project-lite", @@ -6942,10 +6923,9 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "system-configuration", "tokio", - "tokio-native-tls", "tokio-rustls 0.24.1", "tower-service", "url", @@ -6972,7 +6952,7 @@ dependencies = [ "http-body-util", "hyper 1.2.0", "hyper-rustls 0.26.0", - "hyper-tls 0.6.0", + "hyper-tls", "hyper-util", "ipnet", "js-sys", @@ -6989,7 +6969,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "tokio", "tokio-native-tls", "tokio-rustls 0.25.0", @@ -8054,26 +8034,6 @@ dependencies = [ "zip", ] -[[package]] -name = "svm-rs" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bd5e919f01c9280dce59ab66296449d0e9144b8472b8892fbacf9612998b653" -dependencies = [ - "const-hex", - "dirs 5.0.1", - "fs4 0.7.0", - "once_cell", - "reqwest 0.11.27", - "semver 1.0.22", - "serde", - "serde_json", - "sha2", - "thiserror", - "url", - "zip", -] - [[package]] name = "svm-rs" version = "0.5.1" @@ -8082,7 +8042,7 @@ checksum = "c912d2f0dfbf9d8ba683b3181c4bd6d042bac9279d5c062346c253c1eadf46e2" dependencies = [ "const-hex", "dirs 5.0.1", - "fs4 0.8.2", + "fs4", "once_cell", "reqwest 0.12.3", "semver 1.0.22", @@ -8147,6 +8107,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" + [[package]] name = "system-configuration" version = "0.5.1" @@ -8453,10 +8419,22 @@ dependencies = [ "rustls 0.21.10", "tokio", "tokio-rustls 0.24.1", - "tungstenite", + "tungstenite 0.20.1", "webpki-roots 0.25.4", ] +[[package]] +name = "tokio-tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite 0.21.0", +] + [[package]] name = "tokio-util" version = "0.7.10" @@ -8573,16 +8551,16 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.4.4" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" +checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" dependencies = [ "bitflags 2.5.0", "bytes", - "futures-core", "futures-util", - "http 0.2.12", - "http-body 0.4.6", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", "http-range-header", "httpdate", "mime", @@ -8730,6 +8708,25 @@ dependencies = [ "utf-8", ] +[[package]] +name = "tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 1.1.0", + "httparse", + "log", + "rand 0.8.5", + "sha1", + "thiserror", + "url", + "utf-8", +] + [[package]] name = "typed-arena" version = "2.0.2" diff --git a/Cargo.toml b/Cargo.toml index 622b2cf3f..2859b9349 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -192,10 +192,7 @@ solang-parser = "=0.3.3" ## misc arrayvec = "0.7" base64 = "0.22" -chrono = { version = "0.4", default-features = false, features = [ - "clock", - "std", -] } +chrono = { version = "0.4", default-features = false, features = ["clock", "std"] } color-eyre = "0.6" derive_more = "0.99" evm-disassembler = "0.5" @@ -218,7 +215,8 @@ indexmap = "2.2" tikv-jemallocator = "0.5.4" num-format = "0.4.4" -axum = "0.6" -hyper = "0.14" +axum = "0.7" +hyper = "1.0" +reqwest = { version = "0.12", default-features = false } tower = "0.4" -tower-http = "0.4" +tower-http = "0.5" diff --git a/crates/anvil/server/Cargo.toml b/crates/anvil/server/Cargo.toml index fb880a422..a9d870510 100644 --- a/crates/anvil/server/Cargo.toml +++ b/crates/anvil/server/Cargo.toml @@ -15,7 +15,6 @@ anvil-rpc = { path = "../rpc" } # axum related axum = { workspace = true, features = ["ws"] } -hyper.workspace = true tower-http = { workspace = true, features = ["trace", "cors"] } # tracing diff --git a/crates/anvil/server/src/ipc.rs b/crates/anvil/server/src/ipc.rs index 97ec07098..152b41873 100644 --- a/crates/anvil/server/src/ipc.rs +++ b/crates/anvil/server/src/ipc.rs @@ -24,8 +24,8 @@ pub struct IpcEndpoint { impl IpcEndpoint { /// Creates a new endpoint with the given handler - pub fn new(handler: Handler, endpoint: impl Into) -> Self { - Self { handler, endpoint: Endpoint::new(endpoint.into()) } + pub fn new(handler: Handler, endpoint: String) -> Self { + Self { handler, endpoint: Endpoint::new(endpoint) } } /// Returns a stream of incoming connection handlers diff --git a/crates/anvil/server/src/lib.rs b/crates/anvil/server/src/lib.rs index 5ff287c96..5bd81b043 100644 --- a/crates/anvil/server/src/lib.rs +++ b/crates/anvil/server/src/lib.rs @@ -1,4 +1,4 @@ -//! Bootstrap [axum] RPC servers +//! Bootstrap [axum] RPC servers. #![warn(missing_docs, unused_crate_dependencies)] @@ -12,91 +12,66 @@ use anvil_rpc::{ }; use axum::{ http::{header, HeaderValue, Method}, - routing::{post, IntoMakeService}, - Router, Server, + routing::{post, MethodRouter}, + Router, }; -use hyper::server::conn::AddrIncoming; use serde::de::DeserializeOwned; -use std::{fmt, net::SocketAddr}; +use std::fmt; use tower_http::{cors::CorsLayer, trace::TraceLayer}; mod config; +pub use config::ServerConfig; mod error; -/// handlers for axum server mod handler; -#[cfg(feature = "ipc")] -pub mod ipc; + mod pubsub; -mod ws; +pub use pubsub::{PubSubContext, PubSubRpcHandler}; -pub use crate::pubsub::{PubSubContext, PubSubRpcHandler}; -pub use config::ServerConfig; +mod ws; -/// Type alias for the configured axum server -pub type AnvilServer = Server>; +#[cfg(feature = "ipc")] +pub mod ipc; -/// Configures an [axum::Server] that handles RPC-Calls, both HTTP requests and requests via -/// websocket -pub fn serve_http_ws( - addr: SocketAddr, - config: ServerConfig, - http: Http, - ws: Ws, -) -> AnvilServer +/// Configures an [`axum::Router`] that handles JSON-RPC calls via both HTTP and WS. +pub fn http_ws_router(config: ServerConfig, http: Http, ws: Ws) -> Router where Http: RpcHandler, Ws: PubSubRpcHandler, { - let ServerConfig { allow_origin, no_cors } = config; - - let svc = Router::new() - .route("/", post(handler::handle).get(ws::handle_ws)) - .with_state((http, ws)) - .layer(TraceLayer::new_for_http()); - - let svc = if no_cors { - svc - } else { - svc.layer( - // see https://docs.rs/tower-http/latest/tower_http/cors/index.html - // for more details - CorsLayer::new() - .allow_origin(allow_origin.0) - .allow_headers(vec![header::CONTENT_TYPE]) - .allow_methods(vec![Method::GET, Method::POST]), - ) - } - .into_make_service(); - Server::bind(&addr).serve(svc) + router_inner(config, post(handler::handle).get(ws::handle_ws), (http, ws)) } -/// Configures an [axum::Server] that handles RPC-Calls listing for POST on `/` -pub fn serve_http(addr: SocketAddr, config: ServerConfig, http: Http) -> AnvilServer +/// Configures an [`axum::Router`] that handles JSON-RPC calls via HTTP. +pub fn http_router(config: ServerConfig, http: Http) -> Router where Http: RpcHandler, { + router_inner(config, post(handler::handle), (http, ())) +} + +fn router_inner( + config: ServerConfig, + root_method_router: MethodRouter, + state: S, +) -> Router { let ServerConfig { allow_origin, no_cors } = config; - let svc = Router::new() - .route("/", post(handler::handle)) - .with_state((http, ())) + let mut router = Router::new() + .route("/", root_method_router) + .with_state(state) .layer(TraceLayer::new_for_http()); - let svc = if no_cors { - svc - } else { - svc.layer( - // see https://docs.rs/tower-http/latest/tower_http/cors/index.html - // for more details + if !no_cors { + // See [`tower_http::cors`](https://docs.rs/tower-http/latest/tower_http/cors/index.html) + // for more details. + router = router.layer( CorsLayer::new() .allow_origin(allow_origin.0) - .allow_headers(vec![header::CONTENT_TYPE]) - .allow_methods(vec![Method::GET, Method::POST]), - ) + .allow_headers([header::CONTENT_TYPE]) + .allow_methods([Method::GET, Method::POST]), + ); } - .into_make_service(); - - Server::bind(&addr).serve(svc) + router } /// Helper trait that is used to execute ethereum rpc calls diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index 8e2915241..c64d9471f 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -275,7 +275,7 @@ impl NodeArgs { let dump_interval = self.state_interval.map(Duration::from_secs).unwrap_or(DEFAULT_DUMP_INTERVAL); - let (api, mut handle) = crate::spawn(self.into_node_config()).await; + let (api, mut handle) = crate::try_spawn(self.into_node_config()).await?; // sets the signal handler to gracefully shutdown. let mut fork = api.get_fork(); diff --git a/crates/anvil/src/lib.rs b/crates/anvil/src/lib.rs index 31c635990..b08fd7e9c 100644 --- a/crates/anvil/src/lib.rs +++ b/crates/anvil/src/lib.rs @@ -70,26 +70,56 @@ mod tasks; #[cfg(feature = "cmd")] pub mod cmd; -/// Creates the node and runs the server +/// Creates the node and runs the server. /// /// Returns the [EthApi] that can be used to interact with the node and the [JoinHandle] of the /// task. /// -/// # Example +/// # Panics +/// +/// Panics if any error occurs. For a non-panicking version, use [`try_spawn`]. +/// /// -/// ```rust +/// # Examples +/// +/// ```no_run /// # use anvil::NodeConfig; -/// # async fn spawn() { +/// # async fn spawn() -> eyre::Result<()> { /// let config = NodeConfig::default(); /// let (api, handle) = anvil::spawn(config).await; /// /// // use api /// /// // wait forever -/// handle.await.unwrap(); +/// handle.await.unwrap().unwrap(); +/// # Ok(()) +/// # } +/// ``` +pub async fn spawn(config: NodeConfig) -> (EthApi, NodeHandle) { + try_spawn(config).await.expect("failed to spawn node") +} + +/// Creates the node and runs the server +/// +/// Returns the [EthApi] that can be used to interact with the node and the [JoinHandle] of the +/// task. +/// +/// # Examples +/// +/// ```no_run +/// # use anvil::NodeConfig; +/// # async fn spawn() -> eyre::Result<()> { +/// let config = NodeConfig::default(); +/// let (api, handle) = anvil::try_spawn(config).await?; +/// +/// // use api +/// +/// // wait forever +/// handle.await??; +/// # Ok(()) /// # } /// ``` -pub async fn spawn(mut config: NodeConfig) -> (EthApi, NodeHandle) { +pub async fn try_spawn(mut config: NodeConfig) -> io::Result<(EthApi, NodeHandle)> { let logger = if config.enable_tracing { init_tracing() } else { Default::default() }; logger.set_enabled(!config.silent); @@ -174,18 +204,19 @@ pub async fn spawn(mut config: NodeConfig) -> (EthApi, NodeHandle) { let node_service = tokio::task::spawn(NodeService::new(pool, backend, miner, fee_history_service, filters)); - let mut servers = Vec::new(); - let mut addresses = Vec::new(); + let mut servers = Vec::with_capacity(config.host.len()); + let mut addresses = Vec::with_capacity(config.host.len()); - for addr in config.host.iter() { - let sock_addr = SocketAddr::new(addr.to_owned(), port); - let srv = server::serve(sock_addr, api.clone(), server_config.clone()); + for addr in &config.host { + let sock_addr = SocketAddr::new(*addr, port); - addresses.push(srv.local_addr()); + // Create a TCP listener. + let tcp_listener = tokio::net::TcpListener::bind(sock_addr).await?; + addresses.push(tcp_listener.local_addr()?); - // spawn the server on a new task - let srv = tokio::task::spawn(srv.map_err(NodeError::from)); - servers.push(srv); + // Spawn the server future on a new task. + let srv = server::serve_on(tcp_listener, api.clone(), server_config.clone()); + servers.push(tokio::task::spawn(srv.map_err(Into::into))); } let tokio_handle = Handle::current(); @@ -206,10 +237,10 @@ pub async fn spawn(mut config: NodeConfig) -> (EthApi, NodeHandle) { handle.print(fork.as_ref()); - (api, handle) + Ok((api, handle)) } -type IpcTask = JoinHandle>; +type IpcTask = JoinHandle<()>; /// A handle to the spawned node and server tasks /// @@ -359,7 +390,7 @@ impl Future for NodeHandle { // poll the ipc task if let Some(mut ipc) = pin.ipc_task.take() { if let Poll::Ready(res) = ipc.poll_unpin(cx) { - return Poll::Ready(res.map(|res| res.map_err(NodeError::from))); + return Poll::Ready(res.map(|()| Ok(()))); } else { pin.ipc_task = Some(ipc); } diff --git a/crates/anvil/src/server/mod.rs b/crates/anvil/src/server/mod.rs index 10d86f671..c488bcdc1 100644 --- a/crates/anvil/src/server/mod.rs +++ b/crates/anvil/src/server/mod.rs @@ -1,48 +1,67 @@ -//! Contains the code to launch an ethereum RPC-Server -use crate::EthApi; -use anvil_server::{ipc::IpcEndpoint, AnvilServer, ServerConfig}; +//! Contains the code to launch an Ethereum RPC server. + +use crate::{EthApi, IpcTask}; +use anvil_server::{ipc::IpcEndpoint, ServerConfig}; +use axum::Router; use futures::StreamExt; use handler::{HttpEthRpcHandler, PubSubEthRpcHandler}; -use std::net::SocketAddr; -use tokio::{io, task::JoinHandle}; +use std::{future::Future, io, net::SocketAddr, pin::pin}; +use tokio::net::TcpListener; +pub mod error; mod handler; -pub mod error; +/// Configures a server that handles [`EthApi`] related JSON-RPC calls via HTTP and WS. +/// +/// The returned future creates a new server, binding it to the given address, which returns another +/// future that runs it. +pub async fn serve( + addr: SocketAddr, + api: EthApi, + config: ServerConfig, +) -> io::Result>> { + let tcp_listener = TcpListener::bind(addr).await?; + Ok(serve_on(tcp_listener, api, config)) +} -/// Configures an [axum::Server] that handles [EthApi] related JSON-RPC calls via HTTP and WS -pub fn serve(addr: SocketAddr, api: EthApi, config: ServerConfig) -> AnvilServer { +/// Configures a server that handles [`EthApi`] related JSON-RPC calls via HTTP and WS. +pub async fn serve_on( + tcp_listener: TcpListener, + api: EthApi, + config: ServerConfig, +) -> io::Result<()> { + axum::serve(tcp_listener, router(api, config).into_make_service()).await +} + +/// Configures an [`axum::Router`] that handles [`EthApi`] related JSON-RPC calls via HTTP and WS. +pub fn router(api: EthApi, config: ServerConfig) -> Router { let http = HttpEthRpcHandler::new(api.clone()); let ws = PubSubEthRpcHandler::new(api); - anvil_server::serve_http_ws(addr, config, http, ws) + anvil_server::http_ws_router(config, http, ws) } /// Launches an ipc server at the given path in a new task /// /// # Panics /// -/// if setting up the ipc connection was unsuccessful -pub fn spawn_ipc(api: EthApi, path: impl Into) -> JoinHandle> { +/// Panics if setting up the IPC connection was unsuccessful. +#[track_caller] +pub fn spawn_ipc(api: EthApi, path: String) -> IpcTask { try_spawn_ipc(api, path).expect("failed to establish ipc connection") } -/// Launches an ipc server at the given path in a new task -pub fn try_spawn_ipc( - api: EthApi, - path: impl Into, -) -> io::Result>> { - let path = path.into(); +/// Launches an ipc server at the given path in a new task. +pub fn try_spawn_ipc(api: EthApi, path: String) -> io::Result { let handler = PubSubEthRpcHandler::new(api); let ipc = IpcEndpoint::new(handler, path); let incoming = ipc.incoming()?; let task = tokio::task::spawn(async move { - tokio::pin!(incoming); + let mut incoming = pin!(incoming); while let Some(stream) = incoming.next().await { trace!(target: "ipc", "new ipc connection"); tokio::task::spawn(stream); } - Ok(()) }); Ok(task) diff --git a/crates/chisel/Cargo.toml b/crates/chisel/Cargo.toml index 9829abb7c..9d07ae800 100644 --- a/crates/chisel/Cargo.toml +++ b/crates/chisel/Cargo.toml @@ -37,7 +37,7 @@ dirs = "5" eyre.workspace = true once_cell = "1.18.0" regex = "1" -reqwest = { version = "0.11", default-features = false } +reqwest.workspace = true revm.workspace = true rustyline = "12" semver = "1" diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index cafa1938a..07f8837ca 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -50,7 +50,7 @@ glob = "0.3" globset = "0.4" hex.workspace = true once_cell = "1" -reqwest = { version = "0.12", default-features = false } +reqwest.workspace = true semver = "1" serde_json.workspace = true serde.workspace = true diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml index da1371c75..1b639ee55 100644 --- a/crates/config/Cargo.toml +++ b/crates/config/Cargo.toml @@ -29,7 +29,7 @@ Inflector = "0.11" number_prefix = "0.4" once_cell = "1" regex = "1" -reqwest = { version = "0.12", default-features = false } +reqwest.workspace = true semver = { version = "1", features = ["serde"] } serde_json.workspace = true serde_regex = "1" diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 30b597576..9ccec2150 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -71,7 +71,7 @@ itertools.workspace = true once_cell = "1" parking_lot = "0.12" regex = { version = "1", default-features = false } -reqwest = { version = "0.11", default-features = false, features = ["json"] } +reqwest = { workspace = true, features = ["json"] } semver = "1" serde_json.workspace = true similar = { version = "2", features = ["inline"] } @@ -104,7 +104,7 @@ globset = "0.4" paste = "1.0" path-slash = "0.2" pretty_assertions.workspace = true -svm = { package = "svm-rs", version = "0.4", default-features = false, features = ["rustls"] } +svm = { package = "svm-rs", version = "0.5", default-features = false, features = ["rustls"] } tempfile = "3" tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] } diff --git a/crates/forge/bin/cmd/doc/server.rs b/crates/forge/bin/cmd/doc/server.rs index 7c0927136..f5991ba43 100644 --- a/crates/forge/bin/cmd/doc/server.rs +++ b/crates/forge/bin/cmd/doc/server.rs @@ -1,6 +1,7 @@ use axum::{routing::get_service, Router}; use forge_doc::mdbook::{utils::fs::get_404_output_file, MDBook}; use std::{ + io, net::{SocketAddr, ToSocketAddrs}, path::PathBuf, }; @@ -82,18 +83,20 @@ impl Server { open(serving_url); } - let _ = thread_handle.join(); - - Ok(()) + match thread_handle.join() { + Ok(r) => r.map_err(Into::into), + Err(e) => std::panic::resume_unwind(e), + } } } #[tokio::main] -async fn serve(build_dir: PathBuf, address: SocketAddr, file_404: &str) { +async fn serve(build_dir: PathBuf, address: SocketAddr, file_404: &str) -> io::Result<()> { let file_404 = build_dir.join(file_404); let svc = ServeDir::new(build_dir).not_found_service(ServeFile::new(file_404)); let app = Router::new().nest_service("/", get_service(svc)); - hyper::Server::bind(&address).serve(app.into_make_service()).await.unwrap(); + let tcp_listener = tokio::net::TcpListener::bind(address).await?; + axum::serve(tcp_listener, app.into_make_service()).await } fn open>(path: P) { diff --git a/crates/verify/Cargo.toml b/crates/verify/Cargo.toml index 16efe6888..d365388d0 100644 --- a/crates/verify/Cargo.toml +++ b/crates/verify/Cargo.toml @@ -29,7 +29,7 @@ foundry-compilers = { workspace = true, features = ["full"] } foundry-block-explorers = { workspace = true, features = ["foundry-compilers"] } clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } -reqwest = { version = "0.11", default-features = false, features = ["json"] } +reqwest = { workspace = true, features = ["json"] } async-trait = "0.1" futures = "0.3" semver = "1" From e97a35abf66e76f96a86f6018575f0a8d017f6b9 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 18 Apr 2024 21:18:11 +0200 Subject: [PATCH 192/430] feat: temporarily re-introduce custom trace printer (#7716) --- crates/cast/bin/cmd/run.rs | 10 +++------- crates/evm/evm/src/executors/mod.rs | 6 ++++++ crates/evm/evm/src/inspectors/stack.rs | 19 +++++++++++++++++++ 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index 33c26e2a9..5a5d362fd 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -30,8 +30,7 @@ pub struct RunArgs { debug: bool, /// Print out opcode traces. - #[deprecated] - #[arg(long, short, hide = true)] + #[arg(long, short)] trace_printer: bool, /// Executes the transaction only with the state from the previous block. @@ -83,11 +82,6 @@ impl RunArgs { /// /// Note: This executes the transaction(s) as is: Cheatcodes are disabled pub async fn run(self) -> Result<()> { - #[allow(deprecated)] - if self.trace_printer { - eprintln!("WARNING: --trace-printer is deprecated and has no effect\n"); - } - let figment = Config::figment_with_root(find_project_root_path(None).unwrap()).merge(self.rpc); let evm_opts = figment.extract::()?; @@ -214,6 +208,8 @@ impl RunArgs { // Execute our transaction let result = { + executor.set_trace_printer(self.trace_printer); + configure_tx_env(&mut env, &tx); if let Some(to) = tx.to { diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 76f67abd9..45abbfc9c 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -171,6 +171,12 @@ impl Executor { self } + #[inline] + pub fn set_trace_printer(&mut self, trace_printer: bool) -> &mut Self { + self.inspector.print(trace_printer); + self + } + #[inline] pub fn set_gas_limit(&mut self, gas_limit: U256) -> &mut Self { self.gas_limit = gas_limit; diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 627ca2438..abcfee490 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -11,6 +11,7 @@ use foundry_evm_core::{ use foundry_evm_coverage::HitMaps; use foundry_evm_traces::CallTraceArena; use revm::{ + inspectors::CustomPrintTracer, interpreter::{ CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, Gas, InstructionResult, Interpreter, InterpreterResult, @@ -45,6 +46,8 @@ pub struct InspectorStackBuilder { pub logs: Option, /// Whether coverage info should be collected. pub coverage: Option, + /// Whether to print all opcode traces into the console. Useful for debugging the EVM. + pub print: Option, /// The chisel state inspector. pub chisel_state: Option, /// Whether to enable call isolation. @@ -116,6 +119,13 @@ impl InspectorStackBuilder { self } + /// Set whether to enable the trace printer. + #[inline] + pub fn print(mut self, yes: bool) -> Self { + self.print = Some(yes); + self + } + /// Set whether to enable the tracer. #[inline] pub fn trace(mut self, yes: bool) -> Self { @@ -144,6 +154,7 @@ impl InspectorStackBuilder { debug, logs, coverage, + print, chisel_state, enable_isolation, } = self; @@ -162,6 +173,7 @@ impl InspectorStackBuilder { stack.collect_coverage(coverage.unwrap_or(false)); stack.collect_logs(logs.unwrap_or(true)); stack.enable_debugger(debug.unwrap_or(false)); + stack.print(print.unwrap_or(false)); stack.tracing(trace.unwrap_or(false)); stack.enable_isolation(enable_isolation); @@ -273,6 +285,7 @@ pub struct InspectorStack { pub debugger: Option, pub fuzzer: Option, pub log_collector: Option, + pub printer: Option, pub tracer: Option, pub enable_isolation: bool, @@ -357,6 +370,12 @@ impl InspectorStack { self.log_collector = yes.then(Default::default); } + /// Set whether to enable the trace printer. + #[inline] + pub fn print(&mut self, yes: bool) { + self.printer = yes.then(Default::default); + } + /// Set whether to enable the tracer. #[inline] pub fn tracing(&mut self, yes: bool) { From 8d547757cc4d7fef062f383efcf79fe90304618e Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 18 Apr 2024 22:47:29 +0200 Subject: [PATCH 193/430] feat: support ipc path as rpc url (#7717) * feat: support ipc path as rpc url * typo --- crates/cheatcodes/src/config.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/crates/cheatcodes/src/config.rs b/crates/cheatcodes/src/config.rs index a2b9a5f5b..64e02070a 100644 --- a/crates/cheatcodes/src/config.rs +++ b/crates/cheatcodes/src/config.rs @@ -155,13 +155,15 @@ impl CheatsConfig { /// /// If `url_or_alias` is a known alias in the `ResolvedRpcEndpoints` then it returns the /// corresponding URL of that alias. otherwise this assumes `url_or_alias` is itself a URL - /// if it starts with a `http` or `ws` scheme + /// if it starts with a `http` or `ws` scheme. + /// + /// If the url is a path to an existing file, it is also considered a valid RPC URL, IPC path. /// /// # Errors /// /// - Returns an error if `url_or_alias` is a known alias but references an unresolved env var. /// - Returns an error if `url_or_alias` is not an alias but does not start with a `http` or - /// `scheme` + /// `ws` `scheme` and is not a path to an existing file pub fn rpc_url(&self, url_or_alias: &str) -> Result { match self.rpc_endpoints.get(url_or_alias) { Some(Ok(url)) => Ok(url.clone()), @@ -170,7 +172,12 @@ impl CheatsConfig { err.try_resolve().map_err(Into::into) } None => { - if url_or_alias.starts_with("http") || url_or_alias.starts_with("ws") { + // check if it's a URL or a path to an existing file to an ipc socket + if url_or_alias.starts_with("http") || + url_or_alias.starts_with("ws") || + // check for existing ipc file + Path::new(url_or_alias).exists() + { Ok(url_or_alias.into()) } else { Err(fmt_err!("invalid rpc url: {url_or_alias}")) From 3f5c61528e9b936f77cae3b8ac246b59a82f729d Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Fri, 19 Apr 2024 01:49:56 +0400 Subject: [PATCH 194/430] fix: use 2718 encoding for transactions trie root calculation (#7718) * fix: use 2718 encoding for tx root * fmt * rm doc * fix --- crates/anvil/core/src/eth/block.rs | 3 +- crates/anvil/core/src/eth/transaction/mod.rs | 31 +++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/crates/anvil/core/src/eth/block.rs b/crates/anvil/core/src/eth/block.rs index 44fe1c517..fd7e530e0 100644 --- a/crates/anvil/core/src/eth/block.rs +++ b/crates/anvil/core/src/eth/block.rs @@ -3,6 +3,7 @@ use super::{ trie, }; use alloy_consensus::Header; +use alloy_eips::eip2718::Encodable2718; use alloy_primitives::{Address, Bloom, Bytes, B256, B64, U256}; use alloy_rlp::{RlpDecodable, RlpEncodable}; @@ -47,7 +48,7 @@ impl Block { let ommers_hash = B256::from_slice(alloy_primitives::utils::keccak256(encoded_ommers).as_slice()); let transactions_root = - trie::ordered_trie_root(transactions.iter().map(|r| Bytes::from(alloy_rlp::encode(r)))); + trie::ordered_trie_root(transactions.iter().map(|r| r.encoded_2718())); Self { header: Header { diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index e42300d6a..70f887f1e 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -6,7 +6,7 @@ use alloy_consensus::{ TxEip1559, TxEip2930, TxEip4844, TxEip4844Variant, TxEip4844WithSidecar, TxEnvelope, TxLegacy, TxReceipt, }; -use alloy_eips::eip2718::Decodable2718; +use alloy_eips::eip2718::{Decodable2718, Encodable2718}; use alloy_primitives::{Address, Bloom, Bytes, Log, Signature, TxHash, TxKind, B256, U256}; use alloy_rlp::{length_of_length, Decodable, Encodable, Header}; use alloy_rpc_types::{ @@ -924,6 +924,35 @@ impl Decodable for TypedTransaction { } } +impl Encodable2718 for TypedTransaction { + fn type_flag(&self) -> Option { + self.r#type() + } + + fn encode_2718_len(&self) -> usize { + match self { + TypedTransaction::Legacy(tx) => TxEnvelope::from(tx.clone()).encode_2718_len(), + TypedTransaction::EIP2930(tx) => TxEnvelope::from(tx.clone()).encode_2718_len(), + TypedTransaction::EIP1559(tx) => TxEnvelope::from(tx.clone()).encode_2718_len(), + TypedTransaction::EIP4844(tx) => TxEnvelope::from(tx.clone()).encode_2718_len(), + TypedTransaction::Deposit(tx) => 1 + tx.length(), + } + } + + fn encode_2718(&self, out: &mut dyn BufMut) { + match self { + TypedTransaction::Legacy(tx) => TxEnvelope::from(tx.clone()).encode_2718(out), + TypedTransaction::EIP2930(tx) => TxEnvelope::from(tx.clone()).encode_2718(out), + TypedTransaction::EIP1559(tx) => TxEnvelope::from(tx.clone()).encode_2718(out), + TypedTransaction::EIP4844(tx) => TxEnvelope::from(tx.clone()).encode_2718(out), + TypedTransaction::Deposit(tx) => { + out.put_u8(0x7E); + tx.encode(out); + } + } + } +} + impl Decodable2718 for TypedTransaction { fn typed_decode(ty: u8, buf: &mut &[u8]) -> alloy_rlp::Result { if ty == 0x7E { From 79dd88c29794b9c7eb47bca792dc7ea8ab4f114a Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Fri, 19 Apr 2024 02:11:34 +0400 Subject: [PATCH 195/430] fix: exclude empty artifacts from `ContractsByArtifact` (#7713) * fix: exclude empty artifacts from ContractsByArtifact * additional check * fmt * doc * use Option for bytecodes --- crates/cheatcodes/src/fs.rs | 11 ++++++---- crates/common/src/contracts.rs | 25 ++++++++++++++++++----- crates/evm/traces/src/identifier/local.rs | 21 +++++++++++-------- crates/script/src/execute.rs | 4 +++- crates/script/src/lib.rs | 8 +++----- crates/script/src/transaction.rs | 5 +++-- crates/script/src/verify.rs | 6 +++--- 7 files changed, 51 insertions(+), 29 deletions(-) diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index a729d53bb..728358bd6 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -355,11 +355,14 @@ fn get_artifact_code(state: &Cheatcodes, path: &str, deployed: bool) -> Result { diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index 561d7229b..1627e7958 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -20,9 +20,9 @@ pub struct ContractData { /// Contract ABI. pub abi: JsonAbi, /// Contract creation code. - pub bytecode: Bytes, + pub bytecode: Option, /// Contract runtime code. - pub deployed_bytecode: Bytes, + pub deployed_bytecode: Option, } type ArtifactWithContractRef<'a> = (&'a ArtifactId, &'a ContractData); @@ -45,6 +45,12 @@ impl ContractsByArtifact { let bytecode = artifact.bytecode.and_then(|b| b.into_bytes())?; let deployed_bytecode = artifact.deployed_bytecode.and_then(|b| b.into_bytes())?; + + // Exclude artifacts with present but empty bytecode. Such artifacts are usually + // interfaces and abstract contracts. + let bytecode = (bytecode.len() > 0).then_some(bytecode); + let deployed_bytecode = + (deployed_bytecode.len() > 0).then_some(deployed_bytecode); let abi = artifact.abi?; Some((id, ContractData { name, abi, bytecode, deployed_bytecode })) @@ -55,14 +61,23 @@ impl ContractsByArtifact { /// Finds a contract which has a similar bytecode as `code`. pub fn find_by_creation_code(&self, code: &[u8]) -> Option { - self.iter() - .find(|(_, contract)| bytecode_diff_score(contract.bytecode.as_ref(), code) <= 0.1) + self.iter().find(|(_, contract)| { + if let Some(bytecode) = &contract.bytecode { + bytecode_diff_score(bytecode.as_ref(), code) <= 0.1 + } else { + false + } + }) } /// Finds a contract which has a similar deployed bytecode as `code`. pub fn find_by_deployed_code(&self, code: &[u8]) -> Option { self.iter().find(|(_, contract)| { - bytecode_diff_score(contract.deployed_bytecode.as_ref(), code) <= 0.1 + if let Some(deployed_bytecode) = &contract.deployed_bytecode { + bytecode_diff_score(deployed_bytecode.as_ref(), code) <= 0.1 + } else { + false + } }) } diff --git a/crates/evm/traces/src/identifier/local.rs b/crates/evm/traces/src/identifier/local.rs index 175414a7c..129656b95 100644 --- a/crates/evm/traces/src/identifier/local.rs +++ b/crates/evm/traces/src/identifier/local.rs @@ -19,7 +19,8 @@ impl<'a> LocalTraceIdentifier<'a> { pub fn new(known_contracts: &'a ContractsByArtifact) -> Self { let mut ordered_ids = known_contracts .iter() - .map(|(id, contract)| (id, contract.deployed_bytecode.len())) + .filter_map(|(id, contract)| Some((id, contract.deployed_bytecode.as_ref()?))) + .map(|(id, bytecode)| (id, bytecode.len())) .collect::>(); ordered_ids.sort_by_key(|(_, len)| *len); Self { known_contracts, ordered_ids } @@ -40,14 +41,16 @@ impl<'a> LocalTraceIdentifier<'a> { let mut check = |id| { let contract = self.known_contracts.get(id)?; - let score = bytecode_diff_score(&contract.deployed_bytecode, code); - if score == 0.0 { - trace!(target: "evm::traces", "found exact match"); - return Some((id, &contract.abi)); - } - if score < min_score { - min_score = score; - min_score_id = Some((id, &contract.abi)); + if let Some(deployed_bytecode) = &contract.deployed_bytecode { + let score = bytecode_diff_score(deployed_bytecode, code); + if score == 0.0 { + trace!(target: "evm::traces", "found exact match"); + return Some((id, &contract.abi)); + } + if score < min_score { + min_score = score; + min_score_id = Some((id, &contract.abi)); + } } None }; diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index 1239cda1f..6dc6a9617 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -11,7 +11,7 @@ use alloy_primitives::{Address, Bytes}; use alloy_provider::Provider; use alloy_rpc_types::request::TransactionRequest; use async_recursion::async_recursion; -use eyre::Result; +use eyre::{OptionExt, Result}; use foundry_cheatcodes::ScriptWallets; use foundry_cli::utils::{ensure_clean_constructor, needs_setup}; use foundry_common::{ @@ -63,6 +63,8 @@ impl LinkedState { let ContractData { abi, bytecode, .. } = build_data.get_target_contract()?; + let bytecode = bytecode.ok_or_eyre("target contract has no bytecode")?; + let (func, calldata) = args.get_method_and_calldata(&abi)?; ensure_clean_constructor(&abi)?; diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index dc4e05e1a..cc43929a6 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -362,11 +362,9 @@ impl ScriptArgs { // From artifacts for (artifact, contract) in known_contracts.iter() { - bytecodes.push(( - artifact.name.clone(), - &contract.bytecode, - &contract.deployed_bytecode, - )); + let Some(bytecode) = &contract.bytecode else { continue }; + let Some(deployed_bytecode) = &contract.deployed_bytecode else { continue }; + bytecodes.push((artifact.name.clone(), bytecode, deployed_bytecode)); } // From traces diff --git a/crates/script/src/transaction.rs b/crates/script/src/transaction.rs index cf3921290..3f5405db6 100644 --- a/crates/script/src/transaction.rs +++ b/crates/script/src/transaction.rs @@ -131,6 +131,7 @@ impl TransactionWithMetadata { let Some(data) = self.transaction.input.input() else { return Ok(()) }; let Some(info) = info else { return Ok(()) }; + let Some(bytecode) = info.bytecode.as_ref() else { return Ok(()) }; // `create2` transactions are prefixed by a 32 byte salt. let creation_code = if is_create2 { @@ -143,11 +144,11 @@ impl TransactionWithMetadata { }; // The constructor args start after bytecode. - let contains_constructor_args = creation_code.len() > info.bytecode.len(); + let contains_constructor_args = creation_code.len() > bytecode.len(); if !contains_constructor_args { return Ok(()); } - let constructor_args = &creation_code[info.bytecode.len()..]; + let constructor_args = &creation_code[bytecode.len()..]; let Some(constructor) = info.abi.constructor() else { return Ok(()) }; let values = constructor.abi_decode_input(constructor_args, false).map_err(|e| { diff --git a/crates/script/src/verify.rs b/crates/script/src/verify.rs index c9e102004..f6545c301 100644 --- a/crates/script/src/verify.rs +++ b/crates/script/src/verify.rs @@ -106,11 +106,11 @@ impl VerifyBundle { libraries: &[String], ) -> Option { for (artifact, contract) in self.known_contracts.iter() { + let Some(bytecode) = contract.bytecode.as_ref() else { continue }; // If it's a CREATE2, the tx.data comes with a 32-byte salt in the beginning // of the transaction - if data.split_at(create2_offset).1.starts_with(&contract.bytecode) { - let constructor_args = - data.split_at(create2_offset + contract.bytecode.len()).1.to_vec(); + if data.split_at(create2_offset).1.starts_with(bytecode) { + let constructor_args = data.split_at(create2_offset + bytecode.len()).1.to_vec(); let contract = ContractInfo { path: Some( From 5c6c68c63c08aba410d0013889e2336aa9dbb916 Mon Sep 17 00:00:00 2001 From: ThreeHrSleep <151536303+ThreeHrSleep@users.noreply.github.com> Date: Fri, 19 Apr 2024 15:12:32 +0530 Subject: [PATCH 196/430] feat: use humatime serde for SuiteResult duration (#7722) added humantime serde --- Cargo.lock | 11 +++++++++++ crates/forge/Cargo.toml | 1 + crates/forge/src/result.rs | 1 + 3 files changed, 13 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index bd1fe244a..6f2fe9630 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3499,6 +3499,7 @@ dependencies = [ "foundry-wallets", "futures", "globset", + "humantime-serde", "hyper 1.2.0", "indicatif", "itertools 0.12.1", @@ -4829,6 +4830,16 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "humantime-serde" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" +dependencies = [ + "humantime", + "serde", +] + [[package]] name = "hyper" version = "0.14.28" diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 9ccec2150..7b05ee06e 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -38,6 +38,7 @@ rayon = "1" serde.workspace = true tracing.workspace = true yansi = "0.5" +humantime-serde = "1.1.1" # bin forge-doc.workspace = true diff --git a/crates/forge/src/result.rs b/crates/forge/src/result.rs index 09c3661dc..7cef52a9e 100644 --- a/crates/forge/src/result.rs +++ b/crates/forge/src/result.rs @@ -192,6 +192,7 @@ impl TestOutcome { #[derive(Clone, Debug, Serialize)] pub struct SuiteResult { /// Wall clock time it took to execute all tests in this suite. + #[serde(with = "humantime_serde")] pub duration: Duration, /// Individual test results: `test fn signature -> TestResult`. pub test_results: BTreeMap, From 21b3346bc441505d06e15a758d57fe556fc51756 Mon Sep 17 00:00:00 2001 From: Qiwei Yang Date: Fri, 19 Apr 2024 17:43:22 +0800 Subject: [PATCH 197/430] fix: Type parameter of Vm.prevrandao as uint256 (#7695) * fix: Type parameter of Vm.prevrandao as uint256 * fix: tests types * fix: overload instead of breaking --- crates/cheatcodes/assets/cheatcodes.json | 22 +++++++++++++++++++++- crates/cheatcodes/spec/src/vm.rs | 5 +++++ crates/cheatcodes/src/evm.rs | 15 ++++++++++++++- testdata/cheats/Vm.sol | 1 + testdata/default/cheats/Prevrandao.t.sol | 6 +++--- testdata/default/cheats/Setup.t.sol | 2 +- testdata/default/cheats/Snapshots.t.sol | 2 +- 7 files changed, 46 insertions(+), 7 deletions(-) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 1f2518226..30a63009a 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -6173,7 +6173,7 @@ }, { "func": { - "id": "prevrandao", + "id": "prevrandao_0", "description": "Sets `block.prevrandao`.\nNot available on EVM versions before Paris. Use `difficulty` instead.\nIf used on unsupported EVM versions it will revert.", "declaration": "function prevrandao(bytes32 newPrevrandao) external;", "visibility": "external", @@ -6191,6 +6191,26 @@ "status": "stable", "safety": "unsafe" }, + { + "func": { + "id": "prevrandao_1", + "description": "Sets `block.prevrandao`.\nNot available on EVM versions before Paris. Use `difficulty` instead.\nIf used on unsupported EVM versions it will revert.", + "declaration": "function prevrandao(uint256 newPrevrandao) external;", + "visibility": "external", + "mutability": "", + "signature": "prevrandao(uint256)", + "selector": "0x9cb1c0d4", + "selectorBytes": [ + 156, + 177, + 192, + 212 + ] + }, + "group": "evm", + "status": "stable", + "safety": "unsafe" + }, { "func": { "id": "projectRoot", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 01d1290a6..e9d5a49ca 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -375,6 +375,11 @@ interface Vm { /// If used on unsupported EVM versions it will revert. #[cheatcode(group = Evm, safety = Unsafe)] function prevrandao(bytes32 newPrevrandao) external; + /// Sets `block.prevrandao`. + /// Not available on EVM versions before Paris. Use `difficulty` instead. + /// If used on unsupported EVM versions it will revert. + #[cheatcode(group = Evm, safety = Unsafe)] + function prevrandao(uint256 newPrevrandao) external; /// Sets `block.height`. #[cheatcode(group = Evm, safety = Unsafe)] diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index d9c0f5cff..bfe43d684 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -277,7 +277,7 @@ impl Cheatcode for feeCall { } } -impl Cheatcode for prevrandaoCall { +impl Cheatcode for prevrandao_0Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newPrevrandao } = self; ensure!( @@ -290,6 +290,19 @@ impl Cheatcode for prevrandaoCall { } } +impl Cheatcode for prevrandao_1Call { + fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { newPrevrandao } = self; + ensure!( + ccx.ecx.spec_id() >= SpecId::MERGE, + "`prevrandao` is not supported before the Paris hard fork, use `difficulty` instead; \ + see EIP-4399: https://eips.ethereum.org/EIPS/eip-4399" + ); + ccx.ecx.env.block.prevrandao = Some((*newPrevrandao).into()); + Ok(Default::default()) + } +} + impl Cheatcode for rollCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newHeight } = self; diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 65c657843..b04a07e1d 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -305,6 +305,7 @@ interface Vm { function prank(address msgSender) external; function prank(address msgSender, address txOrigin) external; function prevrandao(bytes32 newPrevrandao) external; + function prevrandao(uint256 newPrevrandao) external; function projectRoot() external view returns (string memory path); function prompt(string calldata promptText) external returns (string memory input); function promptAddress(string calldata promptText) external returns (address); diff --git a/testdata/default/cheats/Prevrandao.t.sol b/testdata/default/cheats/Prevrandao.t.sol index a356fcd4e..7011ce3be 100644 --- a/testdata/default/cheats/Prevrandao.t.sol +++ b/testdata/default/cheats/Prevrandao.t.sol @@ -9,14 +9,14 @@ contract PrevrandaoTest is DSTest { function testPrevrandao() public { assertEq(block.prevrandao, 0); - vm.prevrandao(bytes32(uint256(10))); + vm.prevrandao(uint256(10)); assertEq(block.prevrandao, 10, "prevrandao cheatcode failed"); } function testPrevrandaoFuzzed(uint256 newPrevrandao) public { vm.assume(newPrevrandao != block.prevrandao); assertEq(block.prevrandao, 0); - vm.prevrandao(bytes32(newPrevrandao)); + vm.prevrandao(newPrevrandao); assertEq(block.prevrandao, newPrevrandao); } @@ -25,7 +25,7 @@ contract PrevrandaoTest is DSTest { uint256 oldPrevrandao = block.prevrandao; uint256 snapshot = vm.snapshot(); - vm.prevrandao(bytes32(newPrevrandao)); + vm.prevrandao(newPrevrandao); assertEq(block.prevrandao, newPrevrandao); assert(vm.revertTo(snapshot)); diff --git a/testdata/default/cheats/Setup.t.sol b/testdata/default/cheats/Setup.t.sol index e94bf34ec..d694fb2c1 100644 --- a/testdata/default/cheats/Setup.t.sol +++ b/testdata/default/cheats/Setup.t.sol @@ -21,7 +21,7 @@ contract VmSetupTest is DSTest { vm.chainId(99); vm.roll(100); vm.fee(1000); - vm.prevrandao(bytes32(uint256(10000))); + vm.prevrandao(uint256(10000)); vm.startPrank(address(1337)); } diff --git a/testdata/default/cheats/Snapshots.t.sol b/testdata/default/cheats/Snapshots.t.sol index 8f85fee40..bb7b4e0e6 100644 --- a/testdata/default/cheats/Snapshots.t.sol +++ b/testdata/default/cheats/Snapshots.t.sol @@ -93,7 +93,7 @@ contract SnapshotTest is DSTest { vm.roll(99); assertEq(block.number, 99); - vm.prevrandao(bytes32(uint256(123))); + vm.prevrandao(uint256(123)); assertEq(block.prevrandao, 123); assert(vm.revertTo(snapshot)); From b18c149547e0bcdc968297b12c651e6fc2eed005 Mon Sep 17 00:00:00 2001 From: Krishang <93703995+kamuik16@users.noreply.github.com> Date: Fri, 19 Apr 2024 20:16:03 +0530 Subject: [PATCH 198/430] fix(forge): fix some typos in forge clone (#7725) fix some typos --- crates/forge/bin/cmd/clone.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/forge/bin/cmd/clone.rs b/crates/forge/bin/cmd/clone.rs index 86ef41539..b7b83d04c 100644 --- a/crates/forge/bin/cmd/clone.rs +++ b/crates/forge/bin/cmd/clone.rs @@ -31,7 +31,7 @@ pub struct CloneMetadata { pub path: PathBuf, /// The name of the contract in the file. pub target_contract: String, - /// The address of the contract on the blockchian. + /// The address of the contract on the blockchain. pub address: Address, /// The chain id. pub chain_id: ChainId, @@ -67,7 +67,7 @@ pub struct CloneArgs { #[arg(value_hint = ValueHint::DirPath, default_value = ".", value_name = "PATH")] pub root: PathBuf, - /// Do not generaet the remappings.txt file. Instead, keep the remappings in the configuration. + /// Do not generate the remappings.txt file. Instead, keep the remappings in the configuration. #[arg(long)] pub no_remappings_txt: bool, @@ -160,7 +160,7 @@ impl CloneArgs { /// Collect the compilation metadata of the cloned contract. /// This function compiles the cloned contract and collects the compilation metadata. /// - /// * `meta` - the metadata of the contract (from Etherscam). + /// * `meta` - the metadata of the contract (from Etherscan). /// * `chain` - the chain where the contract to be cloned locates. /// * `address` - the address of the contract to be cloned. /// * `root` - the root directory of the cloned project. From 844caa88082d48e2b2df5b447dba09530a654cb1 Mon Sep 17 00:00:00 2001 From: Krishang <93703995+kamuik16@users.noreply.github.com> Date: Sat, 20 Apr 2024 03:03:02 +0530 Subject: [PATCH 199/430] fix(cast): return logs in all cases (#7731) * return logs in all cases * make clippy happy * nits --- crates/cast/bin/cmd/logs.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/cast/bin/cmd/logs.rs b/crates/cast/bin/cmd/logs.rs index dd66176e8..85f0c8414 100644 --- a/crates/cast/bin/cmd/logs.rs +++ b/crates/cast/bin/cmd/logs.rs @@ -81,8 +81,10 @@ impl LogsArgs { None => None, }; - let from_block = cast.convert_block_number(from_block).await?; - let to_block = cast.convert_block_number(to_block).await?; + let from_block = + cast.convert_block_number(Some(from_block.unwrap_or_else(BlockId::earliest))).await?; + let to_block = + cast.convert_block_number(Some(to_block.unwrap_or_else(BlockId::latest))).await?; let filter = build_filter(from_block, to_block, address, sig_or_topic, topics_or_args)?; From dba274da4fbd7fe966215357c65909463c88ee0e Mon Sep 17 00:00:00 2001 From: "Du, Chengbin" Date: Sat, 20 Apr 2024 18:59:13 +0800 Subject: [PATCH 200/430] chore(deps): update rustls to 0.21.11 and 0.22.4 for security reason (#7734) details: https://cxsecurity.com/cveshow/CVE-2024-32650/ --- Cargo.lock | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6f2fe9630..a52ff0b62 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1337,7 +1337,7 @@ dependencies = [ "once_cell", "pin-project-lite", "pin-utils", - "rustls 0.21.10", + "rustls 0.21.11", "tokio", "tracing", ] @@ -4894,7 +4894,7 @@ dependencies = [ "http 0.2.12", "hyper 0.14.28", "log", - "rustls 0.21.10", + "rustls 0.21.11", "rustls-native-certs 0.6.3", "tokio", "tokio-rustls 0.24.1", @@ -4910,7 +4910,7 @@ dependencies = [ "http 1.1.0", "hyper 1.2.0", "hyper-util", - "rustls 0.22.3", + "rustls 0.22.4", "rustls-pki-types", "tokio", "tokio-rustls 0.25.0", @@ -6928,7 +6928,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.10", + "rustls 0.21.11", "rustls-native-certs 0.6.3", "rustls-pemfile 1.0.4", "serde", @@ -6973,7 +6973,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.22.3", + "rustls 0.22.4", "rustls-native-certs 0.7.0", "rustls-pemfile 2.1.2", "rustls-pki-types", @@ -7255,9 +7255,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.10" +version = "0.21.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +checksum = "7fecbfb7b1444f477b345853b1fce097a2c6fb637b2bfb87e6bc5db0f043fae4" dependencies = [ "log", "ring 0.17.8", @@ -7267,9 +7267,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.22.3" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99008d7ad0bbbea527ec27bddbc0e432c5b87d8175178cee68d2eec9c4a1813c" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ "log", "ring 0.17.8", @@ -8392,7 +8392,7 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.21.10", + "rustls 0.21.11", "tokio", ] @@ -8402,7 +8402,7 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" dependencies = [ - "rustls 0.22.3", + "rustls 0.22.4", "rustls-pki-types", "tokio", ] @@ -8427,7 +8427,7 @@ checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" dependencies = [ "futures-util", "log", - "rustls 0.21.10", + "rustls 0.21.11", "tokio", "tokio-rustls 0.24.1", "tungstenite 0.20.1", @@ -8712,7 +8712,7 @@ dependencies = [ "httparse", "log", "rand 0.8.5", - "rustls 0.21.10", + "rustls 0.21.11", "sha1", "thiserror", "url", From 167295ee0dd8425300656135a4570ac039e77296 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sat, 20 Apr 2024 16:39:57 +0400 Subject: [PATCH 201/430] fix: always compile sources when running scripts (#7738) --- crates/script/src/build.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index bdc940097..99c1ff7a7 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -11,13 +11,14 @@ use alloy_provider::Provider; use eyre::{OptionExt, Result}; use foundry_cheatcodes::ScriptWallets; use foundry_common::{ - compile::{self, ContractSources}, + compile::{ContractSources, ProjectCompiler}, provider::alloy::try_get_http_provider, ContractData, ContractsByArtifact, }; use foundry_compilers::{ artifacts::{BytecodeObject, Libraries}, info::ContractInfo, + utils::source_files_iter, ArtifactId, ProjectCompileOutput, }; use foundry_linking::{LinkOutput, Linker}; @@ -149,7 +150,13 @@ impl PreprocessedState { } }; - let output = compile::compile_target(&target_path, &project, args.opts.silent)?; + let sources_to_compile = + source_files_iter(project.paths.sources.as_path()).chain([target_path.to_path_buf()]); + + let output = ProjectCompiler::new() + .quiet_if(args.opts.silent) + .files(sources_to_compile) + .compile(&project)?; let mut target_id: Option = None; From 30f145ff677eb01361f7f05c3233c133d1fd0d5b Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sat, 20 Apr 2024 16:40:05 +0400 Subject: [PATCH 202/430] fix: correctly process relative paths as `--skip` values (#7737) * fix: correctly process relative paths as values * clippy * nit --- crates/common/src/compile.rs | 24 ++++++++++++++++++++---- crates/forge/bin/cmd/build.rs | 3 ++- crates/forge/tests/cli/cmd.rs | 7 ++++++- crates/verify/src/bytecode.rs | 5 ++++- 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index a498a61b3..3b1d83c18 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -560,19 +560,35 @@ pub fn etherscan_project(metadata: &Metadata, target_path: impl AsRef) -> /// Bundles multiple `SkipBuildFilter` into a single `FileFilter` #[derive(Clone, Debug)] -pub struct SkipBuildFilters(Vec); +pub struct SkipBuildFilters { + /// All provided filters. + pub matchers: Vec, + /// Root of the project. + pub project_root: PathBuf, +} impl FileFilter for SkipBuildFilters { /// Only returns a match if _no_ exclusion filter matches fn is_match(&self, file: &Path) -> bool { - self.0.iter().all(|matcher| is_match_exclude(matcher, file)) + self.matchers.iter().all(|matcher| { + if !is_match_exclude(matcher, file) { + false + } else { + file.strip_prefix(&self.project_root) + .map_or(true, |stripped| is_match_exclude(matcher, stripped)) + } + }) } } impl SkipBuildFilters { /// Creates a new `SkipBuildFilters` from multiple `SkipBuildFilter`. - pub fn new(matchers: impl IntoIterator) -> Result { - matchers.into_iter().map(|m| m.compile()).collect::>().map(Self) + pub fn new( + filters: impl IntoIterator, + project_root: PathBuf, + ) -> Result { + let matchers = filters.into_iter().map(|m| m.compile()).collect::>(); + matchers.map(|filters| Self { matchers: filters, project_root }) } } diff --git a/crates/forge/bin/cmd/build.rs b/crates/forge/bin/cmd/build.rs index 66dae630e..c2e6715a4 100644 --- a/crates/forge/bin/cmd/build.rs +++ b/crates/forge/bin/cmd/build.rs @@ -94,7 +94,8 @@ impl BuildArgs { .bail(!self.format_json); if let Some(skip) = self.skip { if !skip.is_empty() { - compiler = compiler.filter(Box::new(SkipBuildFilters::new(skip)?)); + compiler = compiler + .filter(Box::new(SkipBuildFilters::new(skip, project.root().to_path_buf())?)); } } let output = compiler.compile(&project)?; diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 0da6191b0..ab1583d2c 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -1620,7 +1620,12 @@ function test_run() external {} // only builds the single template contract `src/*` even if `*.t.sol` or `.s.sol` is absent prj.clear(); - cmd.args(["build", "--skip", "*/test/**", "--skip", "*/script/**"]); + cmd.args(["build", "--skip", "*/test/**", "--skip", "*/script/**", "--force"]); + cmd.unchecked_output().stdout_matches_path( + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/can_build_skip_glob.stdout"), + ); + + cmd.forge_fuse().args(["build", "--skip", "./test/**", "--skip", "./script/**", "--force"]); cmd.unchecked_output().stdout_matches_path( PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/can_build_skip_glob.stdout"), ); diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index d84acbccc..b89fc91f1 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -373,7 +373,10 @@ impl VerifyBytecodeArgs { if let Some(skip) = &self.skip { if !skip.is_empty() { - compiler = compiler.filter(Box::new(SkipBuildFilters::new(skip.to_owned())?)); + compiler = compiler.filter(Box::new(SkipBuildFilters::new( + skip.to_owned(), + project.root().to_path_buf(), + )?)); } } let output = compiler.compile(&project)?; From 6f2668f925ca0cd98d19e0f2208ec3fe89ac8832 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sun, 21 Apr 2024 02:56:31 +0400 Subject: [PATCH 203/430] fix: do not require `--sender` with `--unlocked` (#7742) * fix: do not require --sender with --unlocked * test --- crates/forge/tests/cli/script.rs | 33 ++++++++++++++++++++++++++++++++ crates/script/src/lib.rs | 1 - 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index 21b5837bb..70b97b58f 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -1288,3 +1288,36 @@ contract SimpleScript is Script { let output = cmd.stdout_lossy(); assert!(output.contains("ONCHAIN EXECUTION COMPLETE & SUCCESSFUL")); }); + +// https://github.com/foundry-rs/foundry/pull/7742 +forgetest_async!(unlocked_no_sender, |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 { + vm.startBroadcast(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266); + address(0).call(""); + } +} + "#, + ) + .unwrap(); + + let (_api, handle) = spawn(NodeConfig::test()).await; + + cmd.args([ + "script", + "SimpleScript", + "--fork-url", + &handle.http_endpoint(), + "--broadcast", + "--unlocked", + ]); + + let output = cmd.stdout_lossy(); + assert!(output.contains("ONCHAIN EXECUTION COMPLETE & SUCCESSFUL")); +}); diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index cc43929a6..0f9d8ad47 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -120,7 +120,6 @@ pub struct ScriptArgs { /// Send via `eth_sendTransaction` using the `--from` argument or `$ETH_FROM` as sender #[arg( long, - requires = "sender", conflicts_with_all = &["private_key", "private_keys", "froms", "ledger", "trezor", "aws"], )] pub unlocked: bool, From db74e6ecfecdc19b3821a908721b7c00b0808b22 Mon Sep 17 00:00:00 2001 From: 0xtekgrinder <72015889+0xtekgrinder@users.noreply.github.com> Date: Sat, 20 Apr 2024 19:10:30 -0400 Subject: [PATCH 204/430] feat: add envExists cheatcode to check if a environment variable exists (#7744) --- crates/cheatcodes/assets/cheatcodes.json | 20 ++++++++++++++++++++ crates/cheatcodes/spec/src/vm.rs | 4 ++++ crates/cheatcodes/src/env.rs | 7 +++++++ testdata/cheats/Vm.sol | 1 + testdata/default/cheats/Env.t.sol | 8 ++++++++ 5 files changed, 40 insertions(+) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 30a63009a..9ce73e931 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -3811,6 +3811,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "envExists", + "description": "Gets the environment variable `name` and returns true if it exists, else returns false.", + "declaration": "function envExists(string calldata name) external view returns (bool exists);", + "visibility": "external", + "mutability": "view", + "signature": "envExists(string)", + "selector": "0xce8365f9", + "selectorBytes": [ + 206, + 131, + 101, + 249 + ] + }, + "group": "environment", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "envInt_0", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index e9d5a49ca..39f0fd8b0 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1501,6 +1501,10 @@ interface Vm { #[cheatcode(group = Environment)] function setEnv(string calldata name, string calldata value) external; + /// Gets the environment variable `name` and returns true if it exists, else returns false. + #[cheatcode(group = Environment)] + function envExists(string calldata name) external view returns (bool exists); + /// Gets the environment variable `name` and parses it as `bool`. /// Reverts if the variable was not found or could not be parsed. #[cheatcode(group = Environment)] diff --git a/crates/cheatcodes/src/env.rs b/crates/cheatcodes/src/env.rs index d9022d1b3..b8245dda1 100644 --- a/crates/cheatcodes/src/env.rs +++ b/crates/cheatcodes/src/env.rs @@ -26,6 +26,13 @@ impl Cheatcode for setEnvCall { } } +impl Cheatcode for envExistsCall { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { name } = self; + Ok(env::var(name).is_ok().abi_encode()) + } +} + impl Cheatcode for envBool_0Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name } = self; diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index b04a07e1d..02eeac480 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -186,6 +186,7 @@ interface Vm { function envBytes32(string calldata name, string calldata delim) external view returns (bytes32[] memory value); function envBytes(string calldata name) external view returns (bytes memory value); function envBytes(string calldata name, string calldata delim) external view returns (bytes[] memory value); + function envExists(string calldata name) external view returns (bool exists); function envInt(string calldata name) external view returns (int256 value); function envInt(string calldata name, string calldata delim) external view returns (int256[] memory value); function envOr(string calldata name, bool defaultValue) external view returns (bool value); diff --git a/testdata/default/cheats/Env.t.sol b/testdata/default/cheats/Env.t.sol index 523ab34dc..e325df2fa 100644 --- a/testdata/default/cheats/Env.t.sol +++ b/testdata/default/cheats/Env.t.sol @@ -13,6 +13,14 @@ contract EnvTest is DSTest { vm.setEnv(key, val); } + function testEnvExists() public { + string memory key = "_foundryCheatcodeEnvExistsTestKey"; + string memory val = "_foundryCheatcodeEnvExistsTestVal"; + vm.setEnv(key, val); + require(vm.envExists(key), "envExists failed"); + require(!vm.envExists("nonexistent"), "envExists failed"); + } + uint256 constant numEnvBoolTests = 2; function testEnvBool() public { From 63fff3510408b552f11efb8196f48cfe6c1da664 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sun, 21 Apr 2024 03:13:18 +0400 Subject: [PATCH 205/430] fix: print test results while running coverage (#7743) --- crates/forge/bin/cmd/coverage.rs | 57 +++++++++----------------------- 1 file changed, 16 insertions(+), 41 deletions(-) diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index fc919934f..66f45b770 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -1,4 +1,4 @@ -use super::{install, test::FilterArgs}; +use super::{install, test::TestArgs}; use alloy_primitives::{Address, Bytes, U256}; use clap::{Parser, ValueEnum, ValueHint}; use eyre::{Context, Result}; @@ -8,17 +8,15 @@ use forge::{ CoverageReport, CoverageReporter, DebugReporter, LcovReporter, SummaryReporter, }, opts::EvmOpts, - result::SuiteResult, revm::primitives::SpecId, utils::IcPcMap, MultiContractRunnerBuilder, TestOptions, }; use foundry_cli::{ - opts::CoreBuildArgs, p_println, utils::{LoadConfig, STATIC_FUZZ_SEED}, }; -use foundry_common::{compile::ProjectCompiler, evm::EvmArgs, fs}; +use foundry_common::{compile::ProjectCompiler, fs}; use foundry_compilers::{ artifacts::{contract::CompactContractBytecode, Ast, CompactBytecode, CompactDeployedBytecode}, sourcemap::SourceMap, @@ -27,18 +25,14 @@ use foundry_compilers::{ use foundry_config::{Config, SolcReq}; use rustc_hash::FxHashMap; use semver::Version; -use std::{ - collections::HashMap, - path::PathBuf, - sync::{mpsc::channel, Arc}, -}; +use std::{collections::HashMap, path::PathBuf, sync::Arc}; use yansi::Paint; /// A map, keyed by contract ID, to a tuple of the deployment source map and the runtime source map. type SourceMaps = HashMap; // Loads project's figment and merges the build cli arguments into it -foundry_config::impl_figment_convert!(CoverageArgs, opts, evm_opts); +foundry_config::impl_figment_convert!(CoverageArgs, test); /// CLI arguments for `forge coverage`. #[derive(Clone, Debug, Parser)] @@ -72,13 +66,7 @@ pub struct CoverageArgs { include_libs: bool, #[command(flatten)] - filter: FilterArgs, - - #[command(flatten)] - evm_opts: EvmArgs, - - #[command(flatten)] - opts: CoreBuildArgs, + test: TestArgs, } impl CoverageArgs { @@ -86,7 +74,7 @@ impl CoverageArgs { let (mut config, evm_opts) = self.load_config_and_evm_opts_emit_warnings()?; // install missing dependencies - if install::install_missing_dependencies(&mut config, self.build_args().silent) && + if install::install_missing_dependencies(&mut config, self.test.build_args().silent) && config.auto_detect_remappings { // need to re-configure here to also catch additional remappings @@ -100,10 +88,10 @@ impl CoverageArgs { config.ast = true; let (project, output) = self.build(&config)?; - p_println!(!self.opts.silent => "Analysing contracts..."); + p_println!(!self.test.build_args().silent => "Analysing contracts..."); let report = self.prepare(&config, output.clone())?; - p_println!(!self.opts.silent => "Running tests..."); + p_println!(!self.test.build_args().silent => "Running tests..."); self.collect(project, output, report, Arc::new(config), evm_opts).await } @@ -131,7 +119,7 @@ impl CoverageArgs { "Note that \"viaIR\" is only available in Solidity 0.8.13 and above.\n", "See more: https://github.com/foundry-rs/foundry/issues/3357", )); - p_println!(!self.opts.silent => "{msg}"); + p_println!(!self.test.build_args().silent => "{msg}"); // Enable viaIR with minimum optimization // https://github.com/ethereum/solidity/issues/12533#issuecomment-1013073350 @@ -315,10 +303,11 @@ impl CoverageArgs { evm_opts: EvmOpts, ) -> Result<()> { let root = project.paths.root; + let verbosity = evm_opts.verbosity; // Build the contract runner let env = evm_opts.evm_env().await?; - let mut runner = MultiContractRunnerBuilder::new(config.clone()) + let runner = MultiContractRunnerBuilder::new(config.clone()) .initial_balance(evm_opts.initial_balance) .evm_spec(config.evm_spec_id()) .sender(evm_opts.sender) @@ -331,13 +320,13 @@ impl CoverageArgs { .set_coverage(true) .build(&root, output, env, evm_opts)?; - // Run tests - let filter = self.filter; - let (tx, rx) = channel::<(String, SuiteResult)>(); - let handle = tokio::task::spawn_blocking(move || runner.test(&filter, tx)); + let outcome = self + .test + .run_tests(runner, config.clone(), verbosity, &self.test.filter(&config)) + .await?; // Add hit data to the coverage report - let data = rx.into_iter().flat_map(|(_, suite)| { + let data = outcome.results.into_iter().flat_map(|(_, suite)| { let mut hits = Vec::new(); for (_, mut result) in suite.test_results { let Some(hit_maps) = result.coverage.take() else { continue }; @@ -359,7 +348,6 @@ impl CoverageArgs { }); for (artifact_id, hits, is_deployed_code) in data { - // TODO: Note down failing tests if let Some(source_id) = report.get_source_id( artifact_id.version.clone(), artifact_id.source.to_string_lossy().to_string(), @@ -377,14 +365,6 @@ impl CoverageArgs { } } - // Reattach the thread - if let Err(e) = handle.await { - match e.try_into_panic() { - Ok(payload) => std::panic::resume_unwind(payload), - Err(e) => return Err(e.into()), - } - } - // Output final report for report_kind in self.report { match report_kind { @@ -409,11 +389,6 @@ impl CoverageArgs { } Ok(()) } - - /// Returns the flattened [`CoreBuildArgs`] - pub fn build_args(&self) -> &CoreBuildArgs { - &self.opts - } } // TODO: HTML From 68006b65f3df56d262a3402b7cfb4b87fed71efc Mon Sep 17 00:00:00 2001 From: Zhuo Zhang <14835483+ZhangZhuoSJTU@users.noreply.github.com> Date: Mon, 22 Apr 2024 17:04:27 +0800 Subject: [PATCH 206/430] fix(forge): fix `forge clone` when there is nested `src` (#7747) * test: add a test case with nested src * fix: fix the bug caused by nested src * chore: make the directory of the cloned project more readable * chore: add --keep-directory-structure option to improve compilation robustness if necessary test: add two more tests for forge clone * chore: use instead of --- crates/forge/bin/cmd/clone.rs | 189 ++++++++++++------ crates/forge/tests/cli/cmd.rs | 42 ++++ .../creation_data.json | 5 + .../metadata.json | 96 +++++++++ 4 files changed, 276 insertions(+), 56 deletions(-) create mode 100644 testdata/etherscan/0x9d27527Ada2CF29fBDAB2973cfa243845a08Bd3F/creation_data.json create mode 100644 testdata/etherscan/0x9d27527Ada2CF29fBDAB2973cfa243845a08Bd3F/metadata.json diff --git a/crates/forge/bin/cmd/clone.rs b/crates/forge/bin/cmd/clone.rs index b7b83d04c..ca6fdf7be 100644 --- a/crates/forge/bin/cmd/clone.rs +++ b/crates/forge/bin/cmd/clone.rs @@ -71,6 +71,14 @@ pub struct CloneArgs { #[arg(long)] pub no_remappings_txt: bool, + /// Keep the original directory structure collected from Etherscan. + /// + /// If this flag is set, the directory structure of the cloned project will be kept as is. + /// By default, the directory structure is re-orgnized to increase the readability, but may + /// risk some compilation failures. + #[arg(long)] + pub keep_directory_structure: bool, + #[command(flatten)] pub etherscan: EtherscanOpts, @@ -80,7 +88,14 @@ pub struct CloneArgs { impl CloneArgs { pub async fn run(self) -> Result<()> { - let CloneArgs { address, root, opts, etherscan, no_remappings_txt } = self; + let CloneArgs { + address, + root, + opts, + etherscan, + no_remappings_txt, + keep_directory_structure, + } = self; // step 0. get the chain and api key from the config let config = Config::from(ðerscan); @@ -99,7 +114,8 @@ impl CloneArgs { let root = dunce::canonicalize(&root)?; // step 3. parse the metadata - Self::parse_metadata(&meta, chain, &root, no_remappings_txt).await?; + Self::parse_metadata(&meta, chain, &root, no_remappings_txt, keep_directory_structure) + .await?; // step 4. collect the compilation metadata // if the etherscan api key is not set, we need to wait for 3 seconds between calls @@ -214,33 +230,24 @@ impl CloneArgs { chain: Chain, root: &PathBuf, no_remappings_txt: bool, + keep_directory_structure: bool, ) -> Result<()> { // dump sources and update the remapping in configuration - let Settings { remappings: original_remappings, .. } = meta.settings()?; - let (remappings, strip_old_src) = dump_sources(meta, root)?; + let remappings = dump_sources(meta, root, keep_directory_structure)?; Config::update_at(root, |config, doc| { let profile = config.profile.as_str().as_str(); + + // update the remappings in the configuration let mut remapping_array = toml_edit::Array::new(); - // original remappings - for r in original_remappings.iter() { - // we should update its remapped path in the same way as we dump sources - // i.e., remove prefix `contracts` (if any) and add prefix `src` - let mut r = r.to_owned(); - if strip_old_src { - let new_path = PathBuf::from(r.path.clone()) - .strip_prefix("contracts") - .map(|p| p.to_path_buf()) - .unwrap_or(PathBuf::from(r.path)); - r.path = PathBuf::from("src").join(new_path).to_string_lossy().to_string(); - } - remapping_array.push(r.to_string()); - } - // new remappings for r in remappings { remapping_array.push(r.to_string()); } doc[Config::PROFILE_SECTION][profile]["remappings"] = toml_edit::value(remapping_array); + // make sure auto_detect_remappings is false (it is very important because cloned + // project may not follow the common remappings) + doc[Config::PROFILE_SECTION][profile]["auto_detect_remappings"] = + toml_edit::value(false); true })?; @@ -407,13 +414,13 @@ fn update_config_by_metadata( /// Dump the contract sources to the root directory. /// The sources are dumped to the `src` directory. /// IO errors may be returned. -/// A list of remappings is returned, as well as a boolean indicating whether the old `contract` -/// or `src` directories are stripped. -fn dump_sources(meta: &Metadata, root: &PathBuf) -> Result<(Vec, bool)> { +/// A list of remappings is returned +fn dump_sources(meta: &Metadata, root: &PathBuf, no_reorg: bool) -> Result> { // get config let path_config = ProjectPathsConfig::builder().build_with_root(root); // we will canonicalize the sources directory later let src_dir = &path_config.sources; + let lib_dir = &path_config.libraries[0]; let contract_name = &meta.contract_name; let source_tree = meta.source_tree(); @@ -421,8 +428,6 @@ fn dump_sources(meta: &Metadata, root: &PathBuf) -> Result<(Vec = Remapping::find_many(root); - // we also load the original remappings from the metadata - remappings.extend(meta.settings()?.remappings); // first we dump the sources to a temporary directory let tmp_dump_dir = root.join("raw_sources"); @@ -430,51 +435,115 @@ fn dump_sources(meta: &Metadata, root: &PathBuf) -> Result<(Vec Option<(&'static str, &'static str)> { for (addr, contract_name, creation_code) in CREATION_ARRAY.iter() { if address == *addr { diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index ab1583d2c..df6a306f5 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -468,6 +468,48 @@ forgetest!(can_clone_quiet, |prj, cmd| { cmd.assert_empty_stdout(); }); +// checks that clone works with --no-remappings-txt +forgetest!(can_clone_no_remappings_txt, |prj, cmd| { + prj.wipe(); + + let foundry_toml = prj.root().join(Config::FILE_NAME); + assert!(!foundry_toml.exists()); + + cmd.args([ + "clone", + "--etherscan-api-key", + next_etherscan_api_key().as_str(), + "--no-remappings-txt", + "0x33e690aEa97E4Ef25F0d140F1bf044d663091DAf", + ]) + .arg(prj.root()); + cmd.assert_non_empty_stdout(); + + let s = read_string(&foundry_toml); + let _config: BasicConfig = parse_with_profile(&s).unwrap().unwrap().1; +}); + +// checks that clone works with --keep-directory-structure +forgetest!(can_clone_keep_directory_structure, |prj, cmd| { + prj.wipe(); + + let foundry_toml = prj.root().join(Config::FILE_NAME); + assert!(!foundry_toml.exists()); + + cmd.args([ + "clone", + "--etherscan-api-key", + next_etherscan_api_key().as_str(), + "--keep-directory-structure", + "0x33e690aEa97E4Ef25F0d140F1bf044d663091DAf", + ]) + .arg(prj.root()); + cmd.assert_non_empty_stdout(); + + let s = read_string(&foundry_toml); + let _config: BasicConfig = parse_with_profile(&s).unwrap().unwrap().1; +}); + // checks that `clean` removes dapptools style paths forgetest!(can_clean, |prj, cmd| { prj.assert_create_dirs_exists(); diff --git a/testdata/etherscan/0x9d27527Ada2CF29fBDAB2973cfa243845a08Bd3F/creation_data.json b/testdata/etherscan/0x9d27527Ada2CF29fBDAB2973cfa243845a08Bd3F/creation_data.json new file mode 100644 index 000000000..7896ef856 --- /dev/null +++ b/testdata/etherscan/0x9d27527Ada2CF29fBDAB2973cfa243845a08Bd3F/creation_data.json @@ -0,0 +1,5 @@ +{ + "contractAddress": "0x9d27527ada2cf29fbdab2973cfa243845a08bd3f", + "contractCreator": "0x7773ae67403d2e30102a84c48cc939919c4c881c", + "txHash": "0xc757719b7ae11ea651b1b23249978a3e8fca94d358610f5809a5dc19fbf850ce" +} \ No newline at end of file diff --git a/testdata/etherscan/0x9d27527Ada2CF29fBDAB2973cfa243845a08Bd3F/metadata.json b/testdata/etherscan/0x9d27527Ada2CF29fBDAB2973cfa243845a08Bd3F/metadata.json new file mode 100644 index 000000000..514b2571c --- /dev/null +++ b/testdata/etherscan/0x9d27527Ada2CF29fBDAB2973cfa243845a08Bd3F/metadata.json @@ -0,0 +1,96 @@ +[ + { + "SourceCode": { + "language": "Solidity", + "sources": { + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for contracts in\n // construction, since the code is only stored at the end of the\n // constructor execution.\n\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "src/base/ERC721Checkpointable.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\n\n/// @title Vote checkpointing for an ERC-721 token\n\n/*********************************\n * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *\n * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *\n * ░░░░░░█████████░░█████████░░░ *\n * ░░░░░░██░░░████░░██░░░████░░░ *\n * ░░██████░░░████████░░░████░░░ *\n * ░░██░░██░░░████░░██░░░████░░░ *\n * ░░██░░██░░░████░░██░░░████░░░ *\n * ░░░░░░█████████░░█████████░░░ *\n * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *\n * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *\n *********************************/\n\n// LICENSE\n// ERC721Checkpointable.sol uses and modifies part of Compound Lab's Comp.sol:\n// https://github.com/compound-finance/compound-protocol/blob/ae4388e780a8d596d97619d9704a931a2752c2bc/contracts/Governance/Comp.sol\n//\n// Comp.sol source code Copyright 2020 Compound Labs, Inc. licensed under the BSD-3-Clause license.\n// With modifications by Nounders DAO.\n//\n// Additional conditions of BSD-3-Clause can be found here: https://opensource.org/licenses/BSD-3-Clause\n//\n// MODIFICATIONS\n// Checkpointing logic from Comp.sol has been used with the following modifications:\n// - `delegates` is renamed to `_delegates` and is set to private\n// - `delegates` is a public function that uses the `_delegates` mapping look-up, but unlike\n// Comp.sol, returns the delegator's own address if there is no delegate.\n// This avoids the delegator needing to \"delegate to self\" with an additional transaction\n// - `_transferTokens()` is renamed `_beforeTokenTransfer()` and adapted to hook into OpenZeppelin's ERC721 hooks.\n\npragma solidity 0.8.9;\n\nimport \"./ERC721BaseWithERC4494Permit.sol\";\n\nabstract contract ERC721Checkpointable is ERC721BaseWithERC4494Permit {\n bool internal _useCheckpoints = true; // can only be disabled and never re-enabled\n\n /// @notice Defines decimals as per ERC-20 convention to make integrations with 3rd party governance platforms easier\n uint8 public constant decimals = 0;\n\n /// @notice A record of each accounts delegate\n mapping(address => address) private _delegates;\n\n /// @notice A checkpoint for marking number of votes from a given block\n struct Checkpoint {\n uint32 fromBlock;\n uint96 votes;\n }\n\n /// @notice A record of votes checkpoints for each account, by index\n mapping(address => mapping(uint32 => Checkpoint)) public checkpoints;\n\n /// @notice The number of checkpoints for each account\n mapping(address => uint32) public numCheckpoints;\n\n /// @notice The EIP-712 typehash for the delegation struct used by the contract\n bytes32 public constant DELEGATION_TYPEHASH =\n keccak256(\"Delegation(address delegatee,uint256 nonce,uint256 expiry)\");\n\n /// @notice An event thats emitted when an account changes its delegate\n event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);\n\n /// @notice An event thats emitted when a delegate account's vote balance changes\n event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance);\n\n /**\n * @notice The votes a delegator can delegate, which is the current balance of the delegator.\n * @dev Used when calling `_delegate()`\n */\n function votesToDelegate(address delegator) public view returns (uint96) {\n return safe96(balanceOf(delegator), \"ERC721Checkpointable::votesToDelegate: amount exceeds 96 bits\");\n }\n\n /**\n * @notice Overrides the standard `Comp.sol` delegates mapping to return\n * the delegator's own address if they haven't delegated.\n * This avoids having to delegate to oneself.\n */\n function delegates(address delegator) public view returns (address) {\n address current = _delegates[delegator];\n return current == address(0) ? delegator : current;\n }\n\n /**\n * @notice Adapted from `_transferTokens()` in `Comp.sol` to update delegate votes.\n * @dev hooks into ERC721Base's `ERC721._transfer`\n */\n function _beforeTokenTransfer(\n address from,\n address to,\n uint256 tokenId\n ) internal override {\n super._beforeTokenTransfer(from, to, tokenId);\n if (_useCheckpoints) {\n /// @notice Differs from `_transferTokens()` to use `delegates` override method to simulate auto-delegation\n _moveDelegates(delegates(from), delegates(to), 1);\n }\n }\n\n /**\n * @notice Delegate votes from `msg.sender` to `delegatee`\n * @param delegatee The address to delegate votes to\n */\n function delegate(address delegatee) public {\n if (delegatee == address(0)) delegatee = msg.sender;\n return _delegate(msg.sender, delegatee);\n }\n\n /**\n * @notice Delegates votes from signatory to `delegatee`\n * @param delegatee The address to delegate votes to\n * @param nonce The contract state required to match the signature\n * @param expiry The time at which to expire the signature\n * @param v The recovery byte of the signature\n * @param r Half of the ECDSA signature pair\n * @param s Half of the ECDSA signature pair\n */\n function delegateBySig(\n address delegatee,\n uint256 nonce,\n uint256 expiry,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) public {\n bytes32 digest = keccak256(\n abi.encodePacked(\n \"\\x19\\x01\",\n _DOMAIN_SEPARATOR(),\n keccak256(abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry))\n )\n );\n // TODO support smart contract wallet via IERC721, require change in function signature to know which signer to call first\n address signatory = ecrecover(digest, v, r, s);\n require(signatory != address(0), \"ERC721Checkpointable::delegateBySig: invalid signature\");\n require(nonce == _userNonces[signatory]++, \"ERC721Checkpointable::delegateBySig: invalid nonce\");\n require(block.timestamp <= expiry, \"ERC721Checkpointable::delegateBySig: signature expired\");\n return _delegate(signatory, delegatee);\n }\n\n /**\n * @notice Gets the current votes balance for `account`\n * @param account The address to get votes balance\n * @return The number of current votes for `account`\n */\n function getCurrentVotes(address account) public view returns (uint96) {\n uint32 nCheckpoints = numCheckpoints[account];\n return nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0;\n }\n\n /**\n * @notice Gets the current votes balance for `account`\n * @param account The address to get votes balance\n * @return The number of current votes for `account`\n */\n function getVotes(address account) external view returns (uint96) {\n return getCurrentVotes(account);\n }\n\n /**\n * @notice Determine the prior number of votes for an account as of a block number\n * @dev Block number must be a finalized block or else this function will revert to prevent misinformation.\n * @param account The address of the account to check\n * @param blockNumber The block number to get the vote balance at\n * @return The number of votes the account had as of the given block\n */\n function getPriorVotes(address account, uint256 blockNumber) public view returns (uint96) {\n require(blockNumber < block.number, \"ERC721Checkpointable::getPriorVotes: not yet determined\");\n\n uint32 nCheckpoints = numCheckpoints[account];\n if (nCheckpoints == 0) {\n return 0;\n }\n\n // First check most recent balance\n if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) {\n return checkpoints[account][nCheckpoints - 1].votes;\n }\n\n // Next check implicit zero balance\n if (checkpoints[account][0].fromBlock > blockNumber) {\n return 0;\n }\n\n uint32 lower = 0;\n uint32 upper = nCheckpoints - 1;\n while (upper > lower) {\n uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow\n Checkpoint memory cp = checkpoints[account][center];\n if (cp.fromBlock == blockNumber) {\n return cp.votes;\n } else if (cp.fromBlock < blockNumber) {\n lower = center;\n } else {\n upper = center - 1;\n }\n }\n return checkpoints[account][lower].votes;\n }\n\n /**\n * @notice Determine the prior number of votes for an account as of a block number\n * @dev Block number must be a finalized block or else this function will revert to prevent misinformation.\n * @param account The address of the account to check\n * @param blockNumber The block number to get the vote balance at\n * @return The number of votes the account had as of the given block\n */\n function getPastVotes(address account, uint256 blockNumber) external view returns (uint96) {\n return this.getPriorVotes(account, blockNumber);\n }\n\n function _delegate(address delegator, address delegatee) internal {\n /// @notice differs from `_delegate()` in `Comp.sol` to use `delegates` override method to simulate auto-delegation\n address currentDelegate = delegates(delegator);\n\n _delegates[delegator] = delegatee;\n\n emit DelegateChanged(delegator, currentDelegate, delegatee);\n\n uint96 amount = votesToDelegate(delegator);\n\n _moveDelegates(currentDelegate, delegatee, amount);\n }\n\n function _moveDelegates(\n address srcRep,\n address dstRep,\n uint96 amount\n ) internal {\n if (srcRep != dstRep && amount > 0) {\n if (srcRep != address(0)) {\n uint32 srcRepNum = numCheckpoints[srcRep];\n uint96 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0;\n uint96 srcRepNew = sub96(srcRepOld, amount, \"ERC721Checkpointable::_moveDelegates: amount underflows\");\n _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew);\n }\n\n if (dstRep != address(0)) {\n uint32 dstRepNum = numCheckpoints[dstRep];\n uint96 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0;\n uint96 dstRepNew = add96(dstRepOld, amount, \"ERC721Checkpointable::_moveDelegates: amount overflows\");\n _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);\n }\n }\n }\n\n function _writeCheckpoint(\n address delegatee,\n uint32 nCheckpoints,\n uint96 oldVotes,\n uint96 newVotes\n ) internal {\n uint32 blockNumber = safe32(\n block.number,\n \"ERC721Checkpointable::_writeCheckpoint: block number exceeds 32 bits\"\n );\n\n if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber) {\n checkpoints[delegatee][nCheckpoints - 1].votes = newVotes;\n } else {\n checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes);\n numCheckpoints[delegatee] = nCheckpoints + 1;\n }\n\n emit DelegateVotesChanged(delegatee, oldVotes, newVotes);\n }\n\n function safe32(uint256 n, string memory errorMessage) internal pure returns (uint32) {\n require(n < 2**32, errorMessage);\n return uint32(n);\n }\n\n function safe96(uint256 n, string memory errorMessage) internal pure returns (uint96) {\n require(n < 2**96, errorMessage);\n return uint96(n);\n }\n\n function add96(\n uint96 a,\n uint96 b,\n string memory errorMessage\n ) internal pure returns (uint96) {\n uint96 c = a + b;\n require(c >= a, errorMessage);\n return c;\n }\n\n function sub96(\n uint96 a,\n uint96 b,\n string memory errorMessage\n ) internal pure returns (uint96) {\n require(b <= a, errorMessage);\n return a - b;\n }\n}\n" + }, + "src/base/ERC721Base.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.9;\n\nimport \"@openzeppelin/contracts/utils/Address.sol\";\nimport \"@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol\";\nimport \"@openzeppelin/contracts/token/ERC721/IERC721.sol\";\nimport \"@openzeppelin/contracts/interfaces/IERC165.sol\";\n\nabstract contract ERC721Base is IERC165, IERC721 {\n using Address for address;\n\n bytes4 internal constant ERC721_RECEIVED = 0x150b7a02;\n bytes4 internal constant ERC165ID = 0x01ffc9a7;\n\n uint256 internal constant OPERATOR_FLAG = 0x8000000000000000000000000000000000000000000000000000000000000000;\n uint256 internal constant NOT_OPERATOR_FLAG = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;\n\n mapping(uint256 => uint256) internal _owners;\n mapping(address => uint256) internal _balances;\n mapping(address => mapping(address => bool)) internal _operatorsForAll;\n mapping(uint256 => address) internal _operators;\n\n function name() public pure virtual returns (string memory) {\n revert(\"NOT_IMPLEMENTED\");\n }\n\n /// @notice Approve an operator to transfer a specific token on the senders behalf.\n /// @param operator The address receiving the approval.\n /// @param id The id of the token.\n function approve(address operator, uint256 id) external override {\n (address owner, uint256 blockNumber) = _ownerAndBlockNumberOf(id);\n require(owner != address(0), \"NONEXISTENT_TOKEN\");\n require(msg.sender == owner || isApprovedForAll(owner, msg.sender), \"UNAUTHORIZED_APPROVAL\");\n _approveFor(owner, blockNumber, operator, id);\n }\n\n /// @notice Transfer a token between 2 addresses.\n /// @param from The sender of the token.\n /// @param to The recipient of the token.\n /// @param id The id of the token.\n function transferFrom(\n address from,\n address to,\n uint256 id\n ) external override {\n (address owner, bool operatorEnabled) = _ownerAndOperatorEnabledOf(id);\n require(owner != address(0), \"NONEXISTENT_TOKEN\");\n require(owner == from, \"NOT_OWNER\");\n require(to != address(0), \"NOT_TO_ZEROADDRESS\");\n require(to != address(this), \"NOT_TO_THIS\");\n if (msg.sender != from) {\n require(\n (operatorEnabled && _operators[id] == msg.sender) || isApprovedForAll(from, msg.sender),\n \"UNAUTHORIZED_TRANSFER\"\n );\n }\n _transferFrom(from, to, id);\n }\n\n /// @notice Transfer a token between 2 addresses letting the receiver know of the transfer.\n /// @param from The send of the token.\n /// @param to The recipient of the token.\n /// @param id The id of the token.\n function safeTransferFrom(\n address from,\n address to,\n uint256 id\n ) external override {\n safeTransferFrom(from, to, id, \"\");\n }\n\n /// @notice Set the approval for an operator to manage all the tokens of the sender.\n /// @param operator The address receiving the approval.\n /// @param approved The determination of the approval.\n function setApprovalForAll(address operator, bool approved) external override {\n _setApprovalForAll(msg.sender, operator, approved);\n }\n\n /// @notice Get the number of tokens owned by an address.\n /// @param owner The address to look for.\n /// @return balance The number of tokens owned by the address.\n function balanceOf(address owner) public view override returns (uint256 balance) {\n require(owner != address(0), \"ZERO_ADDRESS_OWNER\");\n balance = _balances[owner];\n }\n\n /// @notice Get the owner of a token.\n /// @param id The id of the token.\n /// @return owner The address of the token owner.\n function ownerOf(uint256 id) external view override returns (address owner) {\n owner = _ownerOf(id);\n require(owner != address(0), \"NONEXISTENT_TOKEN\");\n }\n\n /// @notice Get the owner of a token and the blockNumber of the last transfer, useful to voting mechanism.\n /// @param id The id of the token.\n /// @return owner The address of the token owner.\n /// @return blockNumber The blocknumber at which the last transfer of that id happened.\n function ownerAndLastTransferBlockNumberOf(uint256 id) internal view returns (address owner, uint256 blockNumber) {\n return _ownerAndBlockNumberOf(id);\n }\n\n struct OwnerData {\n address owner;\n uint256 lastTransferBlockNumber;\n }\n\n /// @notice Get the list of owner of a token and the blockNumber of its last transfer, useful to voting mechanism.\n /// @param ids The list of token ids to check.\n /// @return ownersData The list of (owner, lastTransferBlockNumber) for each ids given as input.\n function ownerAndLastTransferBlockNumberList(uint256[] calldata ids)\n external\n view\n returns (OwnerData[] memory ownersData)\n {\n ownersData = new OwnerData[](ids.length);\n for (uint256 i = 0; i < ids.length; i++) {\n uint256 data = _owners[ids[i]];\n ownersData[i].owner = address(uint160(data));\n ownersData[i].lastTransferBlockNumber = (data >> 160) & 0xFFFFFFFFFFFFFFFFFFFFFF;\n }\n }\n\n /// @notice Get the approved operator for a specific token.\n /// @param id The id of the token.\n /// @return The address of the operator.\n function getApproved(uint256 id) external view override returns (address) {\n (address owner, bool operatorEnabled) = _ownerAndOperatorEnabledOf(id);\n require(owner != address(0), \"NONEXISTENT_TOKEN\");\n if (operatorEnabled) {\n return _operators[id];\n } else {\n return address(0);\n }\n }\n\n /// @notice Check if the sender approved the operator.\n /// @param owner The address of the owner.\n /// @param operator The address of the operator.\n /// @return isOperator The status of the approval.\n function isApprovedForAll(address owner, address operator) public view virtual override returns (bool isOperator) {\n return _operatorsForAll[owner][operator];\n }\n\n /// @notice Transfer a token between 2 addresses letting the receiver knows of the transfer.\n /// @param from The sender of the token.\n /// @param to The recipient of the token.\n /// @param id The id of the token.\n /// @param data Additional data.\n function safeTransferFrom(\n address from,\n address to,\n uint256 id,\n bytes memory data\n ) public override {\n (address owner, bool operatorEnabled) = _ownerAndOperatorEnabledOf(id);\n require(owner != address(0), \"NONEXISTENT_TOKEN\");\n require(owner == from, \"NOT_OWNER\");\n require(to != address(0), \"NOT_TO_ZEROADDRESS\");\n require(to != address(this), \"NOT_TO_THIS\");\n if (msg.sender != from) {\n require(\n (operatorEnabled && _operators[id] == msg.sender) || isApprovedForAll(from, msg.sender),\n \"UNAUTHORIZED_TRANSFER\"\n );\n }\n _safeTransferFrom(from, to, id, data);\n }\n\n /// @notice Check if the contract supports an interface.\n /// @param id The id of the interface.\n /// @return Whether the interface is supported.\n function supportsInterface(bytes4 id) public pure virtual override returns (bool) {\n /// 0x01ffc9a7 is ERC165.\n /// 0x80ac58cd is ERC721\n /// 0x5b5e139f is for ERC721 metadata\n return id == 0x01ffc9a7 || id == 0x80ac58cd || id == 0x5b5e139f;\n }\n\n function _safeTransferFrom(\n address from,\n address to,\n uint256 id,\n bytes memory data\n ) internal {\n _transferFrom(from, to, id);\n if (to.isContract()) {\n require(_checkOnERC721Received(msg.sender, from, to, id, data), \"ERC721_TRANSFER_REJECTED\");\n }\n }\n\n function _beforeTokenTransfer(\n address from,\n address to,\n uint256 id\n ) internal virtual {}\n\n function _transferFrom(\n address from,\n address to,\n uint256 id\n ) internal {\n _beforeTokenTransfer(from, to, id);\n unchecked {\n _balances[to]++;\n if (from != address(0)) {\n _balances[from]--;\n }\n }\n _owners[id] = (block.number << 160) | uint256(uint160(to));\n emit Transfer(from, to, id);\n }\n\n /// @dev See approve.\n function _approveFor(\n address owner,\n uint256 blockNumber,\n address operator,\n uint256 id\n ) internal {\n if (operator == address(0)) {\n _owners[id] = (blockNumber << 160) | uint256(uint160(owner));\n } else {\n _owners[id] = OPERATOR_FLAG | (blockNumber << 160) | uint256(uint160(owner));\n _operators[id] = operator;\n }\n emit Approval(owner, operator, id);\n }\n\n /// @dev See setApprovalForAll.\n function _setApprovalForAll(\n address sender,\n address operator,\n bool approved\n ) internal {\n _operatorsForAll[sender][operator] = approved;\n\n emit ApprovalForAll(sender, operator, approved);\n }\n\n /// @dev Check if receiving contract accepts erc721 transfers.\n /// @param operator The address of the operator.\n /// @param from The from address, may be different from msg.sender.\n /// @param to The adddress we want to transfer to.\n /// @param id The id of the token we would like to transfer.\n /// @param _data Any additional data to send with the transfer.\n /// @return Whether the expected value of 0x150b7a02 is returned.\n function _checkOnERC721Received(\n address operator,\n address from,\n address to,\n uint256 id,\n bytes memory _data\n ) internal returns (bool) {\n bytes4 retval = IERC721Receiver(to).onERC721Received(operator, from, id, _data);\n return (retval == ERC721_RECEIVED);\n }\n\n /// @dev See ownerOf\n function _ownerOf(uint256 id) internal view returns (address owner) {\n return address(uint160(_owners[id]));\n }\n\n /// @dev Get the owner and operatorEnabled status of a token.\n /// @param id The token to query.\n /// @return owner The owner of the token.\n /// @return operatorEnabled Whether or not operators are enabled for this token.\n function _ownerAndOperatorEnabledOf(uint256 id) internal view returns (address owner, bool operatorEnabled) {\n uint256 data = _owners[id];\n owner = address(uint160(data));\n operatorEnabled = (data & OPERATOR_FLAG) == OPERATOR_FLAG;\n }\n\n // @dev Get the owner and operatorEnabled status of a token.\n /// @param id The token to query.\n /// @return owner The owner of the token.\n /// @return blockNumber the blockNumber at which the owner became the owner (last transfer).\n function _ownerAndBlockNumberOf(uint256 id) internal view returns (address owner, uint256 blockNumber) {\n uint256 data = _owners[id];\n owner = address(uint160(data));\n blockNumber = (data >> 160) & 0xFFFFFFFFFFFFFFFFFFFFFF;\n }\n\n // from https://github.com/Uniswap/v3-periphery/blob/main/contracts/base/Multicall.sol\n /// @notice Call multiple functions in the current contract and return the data from all of them if they all succeed.\n /// @dev The `msg.value` should not be trusted for any method callable from multicall.\n /// @param data The encoded function data for each of the calls to make to this contract.\n /// @return results The results from each of the calls passed in via data.\n function multicall(bytes[] calldata data) public payable returns (bytes[] memory results) {\n results = new bytes[](data.length);\n for (uint256 i = 0; i < data.length; i++) {\n (bool success, bytes memory result) = address(this).delegatecall(data[i]);\n\n if (!success) {\n // Next 5 lines from https://ethereum.stackexchange.com/a/83577\n if (result.length < 68) revert();\n assembly {\n result := add(result, 0x04)\n }\n revert(abi.decode(result, (string)));\n }\n\n results[i] = result;\n }\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "src/base/IERC4494.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.9;\n\ninterface IERC4494 {\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n /// @notice Allows to retrieve current nonce for token\n /// @param tokenId token id\n /// @return current token nonce\n function nonces(uint256 tokenId) external view returns (uint256);\n\n /// @notice function to be called by anyone to approve `spender` using a Permit signature\n /// @dev Anyone can call this to approve `spender`, even a third-party\n /// @param spender the actor to approve\n /// @param tokenId the token id\n /// @param deadline the deadline for the permit to be used\n /// @param signature permit\n function permit(\n address spender,\n uint256 tokenId,\n uint256 deadline,\n bytes memory signature\n ) external;\n}\n\ninterface IERC4494Alternative {\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n /// @notice Allows to retrieve current nonce for token\n /// @param tokenId token id\n /// @return current token nonce\n function tokenNonces(uint256 tokenId) external view returns (uint256);\n\n /// @notice function to be called by anyone to approve `spender` using a Permit signature\n /// @dev Anyone can call this to approve `spender`, even a third-party\n /// @param spender the actor to approve\n /// @param tokenId the token id\n /// @param deadline the deadline for the permit to be used\n /// @param signature permit\n function permit(\n address spender,\n uint256 tokenId,\n uint256 deadline,\n bytes memory signature\n ) external;\n}\n" + }, + "src/bleeps/BleepsRoles.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.9;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface ReverseRegistrar {\n function setName(string memory name) external returns (bytes32);\n}\n\ninterface ENS {\n function owner(bytes32 node) external view returns (address);\n}\n\ncontract BleepsRoles {\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n event TokenURIAdminSet(address newTokenURIAdmin);\n event RoyaltyAdminSet(address newRoyaltyAdmin);\n event MinterAdminSet(address newMinterAdmin);\n event GuardianSet(address newGuardian);\n event MinterSet(address newMinter);\n\n bytes32 internal constant ADDR_REVERSE_NODE = 0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2;\n ENS internal immutable _ens;\n\n ///@notice the address of the current owner, that is able to set ENS names and withdraw ERC20 owned by the contract.\n address public owner;\n\n /// @notice tokenURIAdmin can update the tokenURI contract, this is intended to be relinquished once the tokenURI has been heavily tested in the wild and that no modification are needed.\n address public tokenURIAdmin;\n\n /// @notice address allowed to set royalty parameters\n address public royaltyAdmin;\n\n /// @notice minterAdmin can update the minter. At the time being there is 576 Bleeps but there is space for extra instrument and the upper limit is 1024.\n /// could be given to the DAO later so instrument can be added, the sale of these new bleeps could benenfit the DAO too and add new members.\n address public minterAdmin;\n\n /// @notice address allowed to mint, allow the sale contract to be separated from the token contract that can focus on the core logic\n /// Once all 1024 potential bleeps (there could be less, at minimum there are 576 bleeps) are minted, no minter can mint anymore\n address public minter;\n\n /// @notice guardian has some special vetoing power to guide the direction of the DAO. It can only remove rights from the DAO. It could be used to immortalize rules.\n /// For example: the royalty setup could be frozen.\n address public guardian;\n\n constructor(\n address ens,\n address initialOwner,\n address initialTokenURIAdmin,\n address initialMinterAdmin,\n address initialRoyaltyAdmin,\n address initialGuardian\n ) {\n _ens = ENS(ens);\n owner = initialOwner;\n tokenURIAdmin = initialTokenURIAdmin;\n royaltyAdmin = initialRoyaltyAdmin;\n minterAdmin = initialMinterAdmin;\n guardian = initialGuardian;\n emit OwnershipTransferred(address(0), initialOwner);\n emit TokenURIAdminSet(initialTokenURIAdmin);\n emit RoyaltyAdminSet(initialRoyaltyAdmin);\n emit MinterAdminSet(initialMinterAdmin);\n emit GuardianSet(initialGuardian);\n }\n\n function setENSName(string memory name) external {\n require(msg.sender == owner, \"NOT_AUTHORIZED\");\n ReverseRegistrar reverseRegistrar = ReverseRegistrar(_ens.owner(ADDR_REVERSE_NODE));\n reverseRegistrar.setName(name);\n }\n\n function withdrawERC20(IERC20 token, address to) external {\n require(msg.sender == owner, \"NOT_AUTHORIZED\");\n token.transfer(to, token.balanceOf(address(this)));\n }\n\n /**\n * @notice Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) external {\n address oldOwner = owner;\n require(msg.sender == oldOwner);\n owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n\n /**\n * @notice set the new tokenURIAdmin that can change the tokenURI\n * Can only be called by the current tokenURI admin.\n */\n function setTokenURIAdmin(address newTokenURIAdmin) external {\n require(\n msg.sender == tokenURIAdmin || (msg.sender == guardian && newTokenURIAdmin == address(0)),\n \"NOT_AUTHORIZED\"\n );\n tokenURIAdmin = newTokenURIAdmin;\n emit TokenURIAdminSet(newTokenURIAdmin);\n }\n\n /**\n * @notice set the new royaltyAdmin that can change the royalties\n * Can only be called by the current royalty admin.\n */\n function setRoyaltyAdmin(address newRoyaltyAdmin) external {\n require(\n msg.sender == royaltyAdmin || (msg.sender == guardian && newRoyaltyAdmin == address(0)),\n \"NOT_AUTHORIZED\"\n );\n royaltyAdmin = newRoyaltyAdmin;\n emit RoyaltyAdminSet(newRoyaltyAdmin);\n }\n\n /**\n * @notice set the new minterAdmin that can set the minter for Bleeps\n * Can only be called by the current minter admin.\n */\n function setMinterAdmin(address newMinterAdmin) external {\n require(\n msg.sender == minterAdmin || (msg.sender == guardian && newMinterAdmin == address(0)),\n \"NOT_AUTHORIZED\"\n );\n minterAdmin = newMinterAdmin;\n emit MinterAdminSet(newMinterAdmin);\n }\n\n /**\n * @notice set the new guardian that can freeze the other admins (except owner).\n * Can only be called by the current guardian.\n */\n function setGuardian(address newGuardian) external {\n require(msg.sender == guardian, \"NOT_AUTHORIZED\");\n guardian = newGuardian;\n emit GuardianSet(newGuardian);\n }\n\n /**\n * @notice set the new minter that can mint Bleeps (up to 1024).\n * Can only be called by the minter admin.\n */\n function setMinter(address newMinter) external {\n require(msg.sender == minterAdmin, \"NOT_AUTHORIZED\");\n minter = newMinter;\n emit MinterSet(newMinter);\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC721/IERC721.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport \"../../utils/introspection/IERC165.sol\";\n\n/**\n * @dev Required interface of an ERC721 compliant contract.\n */\ninterface IERC721 is IERC165 {\n /**\n * @dev Emitted when `tokenId` token is transferred from `from` to `to`.\n */\n event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);\n\n /**\n * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.\n */\n event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);\n\n /**\n * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.\n */\n event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n\n /**\n * @dev Returns the number of tokens in ``owner``'s account.\n */\n function balanceOf(address owner) external view returns (uint256 balance);\n\n /**\n * @dev Returns the owner of the `tokenId` token.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function ownerOf(uint256 tokenId) external view returns (address owner);\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\n * are aware of the ERC721 protocol to prevent tokens from being forever locked.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 tokenId\n ) external;\n\n /**\n * @dev Transfers `tokenId` token from `from` to `to`.\n *\n * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must be owned by `from`.\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 tokenId\n ) external;\n\n /**\n * @dev Gives permission to `to` to transfer `tokenId` token to another account.\n * The approval is cleared when the token is transferred.\n *\n * Only a single account can be approved at a time, so approving the zero address clears previous approvals.\n *\n * Requirements:\n *\n * - The caller must own the token or be an approved operator.\n * - `tokenId` must exist.\n *\n * Emits an {Approval} event.\n */\n function approve(address to, uint256 tokenId) external;\n\n /**\n * @dev Returns the account approved for `tokenId` token.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function getApproved(uint256 tokenId) external view returns (address operator);\n\n /**\n * @dev Approve or remove `operator` as an operator for the caller.\n * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.\n *\n * Requirements:\n *\n * - The `operator` cannot be the caller.\n *\n * Emits an {ApprovalForAll} event.\n */\n function setApprovalForAll(address operator, bool _approved) external;\n\n /**\n * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.\n *\n * See {setApprovalForAll}\n */\n function isApprovedForAll(address owner, address operator) external view returns (bool);\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function safeTransferFrom(\n address from,\n address to,\n uint256 tokenId,\n bytes calldata data\n ) external;\n}\n" + }, + "@openzeppelin/contracts/utils/introspection/IERC165.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n /**\n * @dev Returns true if this contract implements the interface defined by\n * `interfaceId`. See the corresponding\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n * to learn more about how these ids are created.\n *\n * This function call must use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n" + }, + "src/interfaces/ITokenURI.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.9;\n\ninterface ITokenURI {\n function tokenURI(uint256 id) external view returns (string memory);\n\n function contractURI(address receiver, uint96 per10Thousands) external view returns (string memory);\n}\n" + }, + "src/base/WithSupportForOpenSeaProxies.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.9;\n\ncontract OwnableDelegateProxy {}\n\ncontract ProxyRegistry {\n mapping(address => OwnableDelegateProxy) public proxies;\n}\n\nabstract contract WithSupportForOpenSeaProxies {\n address internal immutable _proxyRegistryAddress;\n\n constructor(address proxyRegistryAddress) {\n _proxyRegistryAddress = proxyRegistryAddress;\n }\n\n function _isOpenSeaProxy(address owner, address operator) internal view returns (bool) {\n if (_proxyRegistryAddress == address(0)) {\n return false;\n }\n // Whitelist OpenSea proxy contract for easy trading.\n ProxyRegistry proxyRegistry = ProxyRegistry(_proxyRegistryAddress);\n return address(proxyRegistry.proxies(owner)) == operator;\n }\n}\n" + }, + "@openzeppelin/contracts/interfaces/IERC1271.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC1271 standard signature validation method for\n * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].\n *\n * _Available since v4.1._\n */\ninterface IERC1271 {\n /**\n * @dev Should return whether the signature provided is valid for the provided data\n * @param hash Hash of the data to be signed\n * @param signature Signature byte array associated with _data\n */\n function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);\n}\n" + }, + "@openzeppelin/contracts/interfaces/IERC165.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport \"../utils/introspection/IERC165.sol\";\n" + }, + "src/bleeps/Bleeps.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.9;\n\nimport \"./BleepsRoles.sol\";\nimport \"../base/ERC721Checkpointable.sol\";\n\nimport \"../interfaces/ITokenURI.sol\";\nimport \"@openzeppelin/contracts/token/ERC721/IERC721.sol\";\n\nimport \"../base/WithSupportForOpenSeaProxies.sol\";\n\ncontract Bleeps is IERC721, WithSupportForOpenSeaProxies, ERC721Checkpointable, BleepsRoles {\n event RoyaltySet(address receiver, uint256 royaltyPer10Thousands);\n event TokenURIContractSet(ITokenURI newTokenURIContract);\n event CheckpointingDisablerSet(address newCheckpointingDisabler);\n event CheckpointingDisabled();\n\n /// @notice the contract that actually generate the sound (and all metadata via the a data: uri via tokenURI call).\n ITokenURI public tokenURIContract;\n\n struct Royalty {\n address receiver;\n uint96 per10Thousands;\n }\n\n Royalty internal _royalty;\n\n /// @dev address that is able to switch off the use of checkpointing, this will make token transfers much cheaper in term of gas, but require the design of a new governance system.\n address public checkpointingDisabler;\n\n /// @dev Create the Bleeps contract\n /// @param ens ENS address for the network the contract is deployed to\n /// @param initialOwner address that can set the ENS name of the contract and that can witthdraw ERC20 tokens sent by mistake here.\n /// @param initialTokenURIAdmin admin able to update the tokenURI contract.\n /// @param initialMinterAdmin admin able to set the minter contract.\n /// @param initialRoyaltyAdmin admin able to update the royalty receiver and rates.\n /// @param initialGuardian guardian able to immortalize rules\n /// @param openseaProxyRegistry allow Bleeps to be sold on opensea without prior approval tx as long as the user have already an opensea proxy.\n /// @param initialRoyaltyReceiver receiver of royalties\n /// @param imitialRoyaltyPer10Thousands amount of royalty in 10,000 basis point\n /// @param initialTokenURIContract initial tokenURI contract that generate the metadata including the wav file.\n /// @param initialCheckpointingDisabler admin able to cancel checkpointing\n constructor(\n address ens,\n address initialOwner,\n address initialTokenURIAdmin,\n address initialMinterAdmin,\n address initialRoyaltyAdmin,\n address initialGuardian,\n address openseaProxyRegistry,\n address initialRoyaltyReceiver,\n uint96 imitialRoyaltyPer10Thousands,\n ITokenURI initialTokenURIContract,\n address initialCheckpointingDisabler\n )\n WithSupportForOpenSeaProxies(openseaProxyRegistry)\n BleepsRoles(ens, initialOwner, initialTokenURIAdmin, initialMinterAdmin, initialRoyaltyAdmin, initialGuardian)\n {\n tokenURIContract = initialTokenURIContract;\n emit TokenURIContractSet(initialTokenURIContract);\n checkpointingDisabler = initialCheckpointingDisabler;\n emit CheckpointingDisablerSet(initialCheckpointingDisabler);\n\n _royalty.receiver = initialRoyaltyReceiver;\n _royalty.per10Thousands = imitialRoyaltyPer10Thousands;\n emit RoyaltySet(initialRoyaltyReceiver, imitialRoyaltyPer10Thousands);\n }\n\n /// @notice A descriptive name for a collection of NFTs in this contract.\n function name() public pure override returns (string memory) {\n return \"Bleeps\";\n }\n\n /// @notice An abbreviated name for NFTs in this contract.\n function symbol() external pure returns (string memory) {\n return \"BLEEP\";\n }\n\n /// @notice Returns the Uniform Resource Identifier (URI) for the token collection.\n function contractURI() external view returns (string memory) {\n return tokenURIContract.contractURI(_royalty.receiver, _royalty.per10Thousands);\n }\n\n /// @notice Returns the Uniform Resource Identifier (URI) for token `id`.\n function tokenURI(uint256 id) external view returns (string memory) {\n return tokenURIContract.tokenURI(id);\n }\n\n /// @notice set a new tokenURI contract, that generate the metadata including the wav file, Can only be set by the `tokenURIAdmin`.\n /// @param newTokenURIContract The address of the new tokenURI contract.\n function setTokenURIContract(ITokenURI newTokenURIContract) external {\n require(msg.sender == tokenURIAdmin, \"NOT_AUTHORIZED\");\n tokenURIContract = newTokenURIContract;\n emit TokenURIContractSet(newTokenURIContract);\n }\n\n /// @notice give the list of owners for the list of ids given.\n /// @param ids The list if token ids to check.\n /// @return addresses The list of addresses, corresponding to the list of ids.\n function owners(uint256[] calldata ids) external view returns (address[] memory addresses) {\n addresses = new address[](ids.length);\n for (uint256 i = 0; i < ids.length; i++) {\n uint256 id = ids[i];\n addresses[i] = address(uint160(_owners[id]));\n }\n }\n\n /// @notice Check if the sender approved the operator.\n /// @param owner The address of the owner.\n /// @param operator The address of the operator.\n /// @return isOperator The status of the approval.\n function isApprovedForAll(address owner, address operator)\n public\n view\n virtual\n override(ERC721Base, IERC721)\n returns (bool isOperator)\n {\n return super.isApprovedForAll(owner, operator) || _isOpenSeaProxy(owner, operator);\n }\n\n /// @notice Check if the contract supports an interface.\n /// @param id The id of the interface.\n /// @return Whether the interface is supported.\n function supportsInterface(bytes4 id)\n public\n pure\n virtual\n override(ERC721BaseWithERC4494Permit, IERC165)\n returns (bool)\n {\n return super.supportsInterface(id) || id == 0x2a55205a; /// 0x2a55205a is ERC2981 (royalty standard)\n }\n\n /// @notice Called with the sale price to determine how much royalty is owed and to whom.\n /// @param id - the token queried for royalty information.\n /// @param salePrice - the sale price of the token specified by id.\n /// @return receiver - address of who should be sent the royalty payment.\n /// @return royaltyAmount - the royalty payment amount for salePrice.\n function royaltyInfo(uint256 id, uint256 salePrice)\n external\n view\n returns (address receiver, uint256 royaltyAmount)\n {\n receiver = _royalty.receiver;\n royaltyAmount = (salePrice * uint256(_royalty.per10Thousands)) / 10000;\n }\n\n /// @notice set a new royalty receiver and rate, Can only be set by the `royaltyAdmin`.\n /// @param newReceiver the address that should receive the royalty proceeds.\n /// @param royaltyPer10Thousands the share of the salePrice (in 1/10000) given to the receiver.\n function setRoyaltyParameters(address newReceiver, uint96 royaltyPer10Thousands) external {\n require(msg.sender == royaltyAdmin, \"NOT_AUTHORIZED\");\n // require(royaltyPer10Thousands <= 50, \"ROYALTY_TOO_HIGH\"); ?\n _royalty.receiver = newReceiver;\n _royalty.per10Thousands = royaltyPer10Thousands;\n emit RoyaltySet(newReceiver, royaltyPer10Thousands);\n }\n\n /// @notice disable checkpointing overhead\n /// This can be used if the governance system can switch to use ownerAndLastTransferBlockNumberOf instead of checkpoints\n function disableTheUseOfCheckpoints() external {\n require(msg.sender == checkpointingDisabler, \"NOT_AUTHORIZED\");\n _useCheckpoints = false;\n checkpointingDisabler = address(0);\n emit CheckpointingDisablerSet(address(0));\n emit CheckpointingDisabled();\n }\n\n /// @notice update the address that can disable the use of checkpinting, can be used to disable it entirely.\n /// @param newCheckpointingDisabler new address that can disable the use of checkpointing. can be the zero address to remove the ability to change.\n function setCheckpointingDisabler(address newCheckpointingDisabler) external {\n require(msg.sender == checkpointingDisabler, \"NOT_AUTHORIZED\");\n checkpointingDisabler = newCheckpointingDisabler;\n emit CheckpointingDisablerSet(newCheckpointingDisabler);\n }\n\n /// @notice mint one of bleep if not already minted. Can only be called by `minter`.\n /// @param id bleep id which represent a pair of (note, instrument).\n /// @param to address that will receive the Bleep.\n function mint(uint16 id, address to) external {\n require(msg.sender == minter, \"ONLY_MINTER_ALLOWED\");\n require(id < 1024, \"INVALID_BLEEP\");\n\n require(to != address(0), \"NOT_TO_ZEROADDRESS\");\n require(to != address(this), \"NOT_TO_THIS\");\n address owner = _ownerOf(id);\n require(owner == address(0), \"ALREADY_CREATED\");\n _safeTransferFrom(address(0), to, id, \"\");\n }\n\n /// @notice mint multiple bleeps if not already minted. Can only be called by `minter`.\n /// @param ids list of bleep id which represent each a pair of (note, instrument).\n /// @param tos addresses that will receive the Bleeps. (if only one, use for all)\n function multiMint(uint16[] calldata ids, address[] calldata tos) external {\n require(msg.sender == minter, \"ONLY_MINTER_ALLOWED\");\n\n address to;\n if (tos.length == 1) {\n to = tos[0];\n }\n for (uint256 i = 0; i < ids.length; i++) {\n uint256 id = ids[i];\n if (tos.length > 1) {\n to = tos[i];\n }\n require(to != address(0), \"NOT_TO_ZEROADDRESS\");\n require(to != address(this), \"NOT_TO_THIS\");\n require(id < 1024, \"INVALID_BLEEP\");\n address owner = _ownerOf(id);\n require(owner == address(0), \"ALREADY_CREATED\");\n _safeTransferFrom(address(0), to, id, \"\");\n }\n }\n\n /// @notice gives the note and instrument for a particular Bleep id.\n /// @param id bleep id which represent a pair of (note, instrument).\n /// @return note the note index (0 to 63) starting from C2 to D#7\n /// @return instrument the instrument index (0 to 16). At launch there is only 9 instrument but the DAO could add more (up to 16 in total).\n function sound(uint256 id) external pure returns (uint8 note, uint8 instrument) {\n note = uint8(id & 0x3F);\n instrument = uint8(uint256(id >> 6) & 0x0F);\n }\n}\n" + }, + "@openzeppelin/contracts/utils/cryptography/ECDSA.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.\n *\n * These functions can be used to verify that a message was signed by the holder\n * of the private keys of a given address.\n */\nlibrary ECDSA {\n enum RecoverError {\n NoError,\n InvalidSignature,\n InvalidSignatureLength,\n InvalidSignatureS,\n InvalidSignatureV\n }\n\n function _throwError(RecoverError error) private pure {\n if (error == RecoverError.NoError) {\n return; // no error: do nothing\n } else if (error == RecoverError.InvalidSignature) {\n revert(\"ECDSA: invalid signature\");\n } else if (error == RecoverError.InvalidSignatureLength) {\n revert(\"ECDSA: invalid signature length\");\n } else if (error == RecoverError.InvalidSignatureS) {\n revert(\"ECDSA: invalid signature 's' value\");\n } else if (error == RecoverError.InvalidSignatureV) {\n revert(\"ECDSA: invalid signature 'v' value\");\n }\n }\n\n /**\n * @dev Returns the address that signed a hashed message (`hash`) with\n * `signature` or error string. This address can then be used for verification purposes.\n *\n * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:\n * this function rejects them by requiring the `s` value to be in the lower\n * half order, and the `v` value to be either 27 or 28.\n *\n * IMPORTANT: `hash` _must_ be the result of a hash operation for the\n * verification to be secure: it is possible to craft signatures that\n * recover to arbitrary addresses for non-hashed data. A safe way to ensure\n * this is by receiving a hash of the original message (which may otherwise\n * be too long), and then calling {toEthSignedMessageHash} on it.\n *\n * Documentation for signature generation:\n * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]\n * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]\n *\n * _Available since v4.3._\n */\n function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {\n // Check the signature length\n // - case 65: r,s,v signature (standard)\n // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._\n if (signature.length == 65) {\n bytes32 r;\n bytes32 s;\n uint8 v;\n // ecrecover takes the signature parameters, and the only way to get them\n // currently is to use assembly.\n assembly {\n r := mload(add(signature, 0x20))\n s := mload(add(signature, 0x40))\n v := byte(0, mload(add(signature, 0x60)))\n }\n return tryRecover(hash, v, r, s);\n } else if (signature.length == 64) {\n bytes32 r;\n bytes32 vs;\n // ecrecover takes the signature parameters, and the only way to get them\n // currently is to use assembly.\n assembly {\n r := mload(add(signature, 0x20))\n vs := mload(add(signature, 0x40))\n }\n return tryRecover(hash, r, vs);\n } else {\n return (address(0), RecoverError.InvalidSignatureLength);\n }\n }\n\n /**\n * @dev Returns the address that signed a hashed message (`hash`) with\n * `signature`. This address can then be used for verification purposes.\n *\n * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:\n * this function rejects them by requiring the `s` value to be in the lower\n * half order, and the `v` value to be either 27 or 28.\n *\n * IMPORTANT: `hash` _must_ be the result of a hash operation for the\n * verification to be secure: it is possible to craft signatures that\n * recover to arbitrary addresses for non-hashed data. A safe way to ensure\n * this is by receiving a hash of the original message (which may otherwise\n * be too long), and then calling {toEthSignedMessageHash} on it.\n */\n function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, signature);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.\n *\n * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]\n *\n * _Available since v4.3._\n */\n function tryRecover(\n bytes32 hash,\n bytes32 r,\n bytes32 vs\n ) internal pure returns (address, RecoverError) {\n bytes32 s;\n uint8 v;\n assembly {\n s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)\n v := add(shr(255, vs), 27)\n }\n return tryRecover(hash, v, r, s);\n }\n\n /**\n * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.\n *\n * _Available since v4.2._\n */\n function recover(\n bytes32 hash,\n bytes32 r,\n bytes32 vs\n ) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, r, vs);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Overload of {ECDSA-tryRecover} that receives the `v`,\n * `r` and `s` signature fields separately.\n *\n * _Available since v4.3._\n */\n function tryRecover(\n bytes32 hash,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal pure returns (address, RecoverError) {\n // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature\n // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines\n // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most\n // signatures from current libraries generate a unique signature with an s-value in the lower half order.\n //\n // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value\n // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or\n // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept\n // these malleable signatures as well.\n if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {\n return (address(0), RecoverError.InvalidSignatureS);\n }\n if (v != 27 && v != 28) {\n return (address(0), RecoverError.InvalidSignatureV);\n }\n\n // If the signature is valid (and not malleable), return the signer address\n address signer = ecrecover(hash, v, r, s);\n if (signer == address(0)) {\n return (address(0), RecoverError.InvalidSignature);\n }\n\n return (signer, RecoverError.NoError);\n }\n\n /**\n * @dev Overload of {ECDSA-recover} that receives the `v`,\n * `r` and `s` signature fields separately.\n */\n function recover(\n bytes32 hash,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal pure returns (address) {\n (address recovered, RecoverError error) = tryRecover(hash, v, r, s);\n _throwError(error);\n return recovered;\n }\n\n /**\n * @dev Returns an Ethereum Signed Message, created from a `hash`. This\n * produces hash corresponding to the one signed with the\n * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]\n * JSON-RPC method as part of EIP-191.\n *\n * See {recover}.\n */\n function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {\n // 32 is the length in bytes of hash,\n // enforced by the type signature above\n return keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n32\", hash));\n }\n\n /**\n * @dev Returns an Ethereum Signed Typed Data, created from a\n * `domainSeparator` and a `structHash`. This produces hash corresponding\n * to the one signed with the\n * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]\n * JSON-RPC method as part of EIP-712.\n *\n * See {recover}.\n */\n function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {\n return keccak256(abi.encodePacked(\"\\x19\\x01\", domainSeparator, structHash));\n }\n}\n" + }, + "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport \"./ECDSA.sol\";\nimport \"../Address.sol\";\nimport \"../../interfaces/IERC1271.sol\";\n\n/**\n * @dev Signature verification helper: Provide a single mechanism to verify both private-key (EOA) ECDSA signature and\n * ERC1271 contract sigantures. Using this instead of ECDSA.recover in your contract will make them compatible with\n * smart contract wallets such as Argent and Gnosis.\n *\n * Note: unlike ECDSA signatures, contract signature's are revocable, and the outcome of this function can thus change\n * through time. It could return true at block N and false at block N+1 (or the opposite).\n *\n * _Available since v4.1._\n */\nlibrary SignatureChecker {\n function isValidSignatureNow(\n address signer,\n bytes32 hash,\n bytes memory signature\n ) internal view returns (bool) {\n (address recovered, ECDSA.RecoverError error) = ECDSA.tryRecover(hash, signature);\n if (error == ECDSA.RecoverError.NoError && recovered == signer) {\n return true;\n }\n\n (bool success, bytes memory result) = signer.staticcall(\n abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, signature)\n );\n return (success && result.length == 32 && abi.decode(result, (bytes4)) == IERC1271.isValidSignature.selector);\n }\n}\n" + }, + "src/base/ERC721BaseWithERC4494Permit.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.9;\n\nimport \"./ERC721Base.sol\";\nimport \"@openzeppelin/contracts/utils/Address.sol\";\nimport \"@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol\";\nimport \"./IERC4494.sol\";\n\nabstract contract ERC721BaseWithERC4494Permit is ERC721Base {\n using Address for address;\n\n bytes32 public constant PERMIT_TYPEHASH =\n keccak256(\"Permit(address spender,uint256 tokenId,uint256 nonce,uint256 deadline)\");\n bytes32 public constant PERMIT_FOR_ALL_TYPEHASH =\n keccak256(\"PermitForAll(address spender,uint256 nonce,uint256 deadline)\");\n bytes32 public constant DOMAIN_TYPEHASH =\n keccak256(\"EIP712Domain(string name,uint256 chainId,address verifyingContract)\");\n\n uint256 private immutable _deploymentChainId;\n bytes32 private immutable _deploymentDomainSeparator;\n\n mapping(address => uint256) internal _userNonces;\n\n constructor() {\n uint256 chainId;\n //solhint-disable-next-line no-inline-assembly\n assembly {\n chainId := chainid()\n }\n _deploymentChainId = chainId;\n _deploymentDomainSeparator = _calculateDomainSeparator(chainId);\n }\n\n /// @dev Return the DOMAIN_SEPARATOR.\n function DOMAIN_SEPARATOR() external view returns (bytes32) {\n return _DOMAIN_SEPARATOR();\n }\n\n /// @notice return the account nonce, used for approvalForAll permit or other account related matter\n /// @param account the account to query\n /// @return nonce\n function nonces(address account) external view virtual returns (uint256 nonce) {\n return _userNonces[account];\n }\n\n /// @notice return the token nonce, used for individual approve permit or other token related matter\n /// @param id token id to query\n /// @return nonce\n function nonces(uint256 id) public view virtual returns (uint256 nonce) {\n (address owner, uint256 blockNumber) = _ownerAndBlockNumberOf(id);\n require(owner != address(0), \"NONEXISTENT_TOKEN\");\n return blockNumber;\n }\n\n /// @notice return the token nonce, used for individual approve permit or other token related matter\n /// @param id token id to query\n /// @return nonce\n function tokenNonces(uint256 id) external view returns (uint256 nonce) {\n return nonces(id);\n }\n\n function permit(\n address spender,\n uint256 tokenId,\n uint256 deadline,\n bytes memory sig\n ) external {\n require(deadline >= block.timestamp, \"PERMIT_DEADLINE_EXPIRED\");\n\n (address owner, uint256 blockNumber) = _ownerAndBlockNumberOf(tokenId);\n require(owner != address(0), \"NONEXISTENT_TOKEN\");\n\n // We use blockNumber as nonce as we already store it per tokens. It can thus act as an increasing transfer counter.\n // while technically multiple transfer could happen in the same block, the signed message would be using a previous block.\n // And the transfer would use then a more recent blockNumber, invalidating that message when transfer is executed.\n _requireValidPermit(owner, spender, tokenId, deadline, blockNumber, sig);\n\n _approveFor(owner, blockNumber, spender, tokenId);\n }\n\n function permitForAll(\n address signer,\n address spender,\n uint256 deadline,\n bytes memory sig\n ) external {\n require(deadline >= block.timestamp, \"PERMIT_DEADLINE_EXPIRED\");\n\n _requireValidPermitForAll(signer, spender, deadline, _userNonces[signer]++, sig);\n\n _setApprovalForAll(signer, spender, true);\n }\n\n /// @notice Check if the contract supports an interface.\n /// @param id The id of the interface.\n /// @return Whether the interface is supported.\n function supportsInterface(bytes4 id) public pure virtual override returns (bool) {\n return\n super.supportsInterface(id) ||\n id == type(IERC4494).interfaceId ||\n id == type(IERC4494Alternative).interfaceId;\n }\n\n // -------------------------------------------------------- INTERNAL --------------------------------------------------------------------\n\n function _requireValidPermit(\n address signer,\n address spender,\n uint256 id,\n uint256 deadline,\n uint256 nonce,\n bytes memory sig\n ) internal view {\n bytes32 digest = keccak256(\n abi.encodePacked(\n \"\\x19\\x01\",\n _DOMAIN_SEPARATOR(),\n keccak256(abi.encode(PERMIT_TYPEHASH, spender, id, nonce, deadline))\n )\n );\n require(SignatureChecker.isValidSignatureNow(signer, digest, sig), \"INVALID_SIGNATURE\");\n }\n\n function _requireValidPermitForAll(\n address signer,\n address spender,\n uint256 deadline,\n uint256 nonce,\n bytes memory sig\n ) internal view {\n bytes32 digest = keccak256(\n abi.encodePacked(\n \"\\x19\\x01\",\n _DOMAIN_SEPARATOR(),\n keccak256(abi.encode(PERMIT_FOR_ALL_TYPEHASH, spender, nonce, deadline))\n )\n );\n require(SignatureChecker.isValidSignatureNow(signer, digest, sig), \"INVALID_SIGNATURE\");\n }\n\n /// @dev Return the DOMAIN_SEPARATOR.\n function _DOMAIN_SEPARATOR() internal view returns (bytes32) {\n uint256 chainId;\n //solhint-disable-next-line no-inline-assembly\n assembly {\n chainId := chainid()\n }\n\n // in case a fork happen, to support the chain that had to change its chainId,, we compute the domain operator\n return chainId == _deploymentChainId ? _deploymentDomainSeparator : _calculateDomainSeparator(chainId);\n }\n\n /// @dev Calculate the DOMAIN_SEPARATOR.\n function _calculateDomainSeparator(uint256 chainId) private view returns (bytes32) {\n return keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name())), chainId, address(this)));\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @title ERC721 token receiver interface\n * @dev Interface for any contract that wants to support safeTransfers\n * from ERC721 asset contracts.\n */\ninterface IERC721Receiver {\n /**\n * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}\n * by `operator` from `from`, this function is called.\n *\n * It must return its Solidity selector to confirm the token transfer.\n * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.\n *\n * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.\n */\n function onERC721Received(\n address operator,\n address from,\n uint256 tokenId,\n bytes calldata data\n ) external returns (bytes4);\n}\n" + } + }, + "settings": { + "evmVersion": "london", + "libraries": {}, + "metadata": { + "bytecodeHash": "ipfs", + "useLiteralContent": true + }, + "optimizer": { + "enabled": true, + "runs": 999999 + }, + "remappings": [], + "outputSelection": { + "*": { + "*": [ + "evm.bytecode", + "evm.deployedBytecode", + "devdoc", + "userdoc", + "metadata", + "abi" + ] + } + } + } + }, + "ABI": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"ens\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"initialOwner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"initialTokenURIAdmin\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"initialMinterAdmin\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"initialRoyaltyAdmin\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"initialGuardian\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"openseaProxyRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"initialRoyaltyReceiver\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"imitialRoyaltyPer10Thousands\",\"type\":\"uint96\"},{\"internalType\":\"contract ITokenURI\",\"name\":\"initialTokenURIContract\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"initialCheckpointingDisabler\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"approved\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"ApprovalForAll\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"CheckpointingDisabled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newCheckpointingDisabler\",\"type\":\"address\"}],\"name\":\"CheckpointingDisablerSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"fromDelegate\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"toDelegate\",\"type\":\"address\"}],\"name\":\"DelegateChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"delegate\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"previousBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newBalance\",\"type\":\"uint256\"}],\"name\":\"DelegateVotesChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newGuardian\",\"type\":\"address\"}],\"name\":\"GuardianSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newMinterAdmin\",\"type\":\"address\"}],\"name\":\"MinterAdminSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newMinter\",\"type\":\"address\"}],\"name\":\"MinterSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newRoyaltyAdmin\",\"type\":\"address\"}],\"name\":\"RoyaltyAdminSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"royaltyPer10Thousands\",\"type\":\"uint256\"}],\"name\":\"RoyaltySet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newTokenURIAdmin\",\"type\":\"address\"}],\"name\":\"TokenURIAdminSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract ITokenURI\",\"name\":\"newTokenURIContract\",\"type\":\"address\"}],\"name\":\"TokenURIContractSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"DELEGATION_TYPEHASH\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"DOMAIN_SEPARATOR\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"DOMAIN_TYPEHASH\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PERMIT_FOR_ALL_TYPEHASH\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PERMIT_TYPEHASH\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"checkpointingDisabler\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"name\":\"checkpoints\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"fromBlock\",\"type\":\"uint32\"},{\"internalType\":\"uint96\",\"name\":\"votes\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"contractURI\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"delegatee\",\"type\":\"address\"}],\"name\":\"delegate\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"delegatee\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"expiry\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"delegateBySig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"}],\"name\":\"delegates\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"disableTheUseOfCheckpoints\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getApproved\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"getCurrentVotes\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"name\":\"getPastVotes\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"name\":\"getPriorVotes\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"getVotes\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"guardian\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"}],\"name\":\"isApprovedForAll\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"isOperator\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"id\",\"type\":\"uint16\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"mint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"minter\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"minterAdmin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16[]\",\"name\":\"ids\",\"type\":\"uint16[]\"},{\"internalType\":\"address[]\",\"name\":\"tos\",\"type\":\"address[]\"}],\"name\":\"multiMint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes[]\",\"name\":\"data\",\"type\":\"bytes[]\"}],\"name\":\"multicall\",\"outputs\":[{\"internalType\":\"bytes[]\",\"name\":\"results\",\"type\":\"bytes[]\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"nonces\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"nonces\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"numCheckpoints\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"ids\",\"type\":\"uint256[]\"}],\"name\":\"ownerAndLastTransferBlockNumberList\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"lastTransferBlockNumber\",\"type\":\"uint256\"}],\"internalType\":\"struct ERC721Base.OwnerData[]\",\"name\":\"ownersData\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"ownerOf\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"ids\",\"type\":\"uint256[]\"}],\"name\":\"owners\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"addresses\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"deadline\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"sig\",\"type\":\"bytes\"}],\"name\":\"permit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"deadline\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"sig\",\"type\":\"bytes\"}],\"name\":\"permitForAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"royaltyAdmin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"salePrice\",\"type\":\"uint256\"}],\"name\":\"royaltyInfo\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"royaltyAmount\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"safeTransferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"safeTransferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"setApprovalForAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newCheckpointingDisabler\",\"type\":\"address\"}],\"name\":\"setCheckpointingDisabler\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"setENSName\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newGuardian\",\"type\":\"address\"}],\"name\":\"setGuardian\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newMinter\",\"type\":\"address\"}],\"name\":\"setMinter\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newMinterAdmin\",\"type\":\"address\"}],\"name\":\"setMinterAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newRoyaltyAdmin\",\"type\":\"address\"}],\"name\":\"setRoyaltyAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newReceiver\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"royaltyPer10Thousands\",\"type\":\"uint96\"}],\"name\":\"setRoyaltyParameters\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newTokenURIAdmin\",\"type\":\"address\"}],\"name\":\"setTokenURIAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract ITokenURI\",\"name\":\"newTokenURIContract\",\"type\":\"address\"}],\"name\":\"setTokenURIContract\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"sound\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"note\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"instrument\",\"type\":\"uint8\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"id\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"tokenNonces\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"tokenURI\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"tokenURIAdmin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"tokenURIContract\",\"outputs\":[{\"internalType\":\"contract ITokenURI\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"}],\"name\":\"votesToDelegate\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"withdrawERC20\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + "ContractName": "Bleeps", + "CompilerVersion": "v0.8.9+commit.e5eed63a", + "OptimizationUsed": 1, + "Runs": 999999, + "ConstructorArguments": "0x00000000000000000000000000000000000c2e074ec69a0dfb2997ba6c7d2e1e0000000000000000000000007773ae67403d2e30102a84c48cc939919c4c881c000000000000000000000000dca9d1fa839bb9fe65ddc4de5161bca43751d4b40000000000000000000000007773ae67403d2e30102a84c48cc939919c4c881c000000000000000000000000dca9d1fa839bb9fe65ddc4de5161bca43751d4b4000000000000000000000000dca9d1fa839bb9fe65ddc4de5161bca43751d4b4000000000000000000000000a5409ec958c83c3f309868babaca7c86dcb077c10000000000000000000000008350c9989ef11325b36ce6f7549004d418dbcee700000000000000000000000000000000000000000000000000000000000001f4000000000000000000000000e114dce59a333f8d351371f54188f92c287b73e6000000000000000000000000dca9d1fa839bb9fe65ddc4de5161bca43751d4b4", + "EVMVersion": "Default", + "Library": "", + "LicenseType": "MIT", + "Proxy": 0, + "SwarmSource": "" + } +] \ No newline at end of file From ecd637373b557d8df95dc20b8b7ce1dccff988cf Mon Sep 17 00:00:00 2001 From: "Du, Chengbin" Date: Mon, 22 Apr 2024 17:34:10 +0800 Subject: [PATCH 207/430] chore(deps): update yansi to v1.0 (#7735) * chore(deps): update yansi to v1.0 * move yansi to workspace deps * move yansi to workspace deps * adjustments --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- Cargo.lock | 27 +++++----- Cargo.toml | 3 +- crates/anvil/Cargo.toml | 2 +- crates/anvil/src/config.rs | 23 ++++---- crates/cast/Cargo.toml | 2 +- crates/cast/bin/cmd/wallet/mod.rs | 4 +- crates/chisel/Cargo.toml | 2 +- crates/chisel/bin/main.rs | 29 +++++----- crates/chisel/src/dispatcher.rs | 32 +++++------ crates/chisel/src/executor.rs | 80 ++++++++++++++-------------- crates/chisel/src/session_source.rs | 13 ++--- crates/chisel/src/solidity_helper.rs | 32 ++++++----- crates/cli/Cargo.toml | 2 +- crates/cli/src/handler.rs | 2 +- crates/cli/src/utils/cmd.rs | 6 +-- crates/cli/src/utils/mod.rs | 12 +---- crates/common/Cargo.toml | 2 +- crates/common/src/fmt/mod.rs | 8 +-- crates/common/src/term.rs | 8 +-- crates/evm/traces/Cargo.toml | 2 +- crates/evm/traces/src/lib.rs | 20 +++---- crates/forge/Cargo.toml | 2 +- crates/forge/bin/cmd/coverage.rs | 4 +- crates/forge/bin/cmd/fmt.rs | 16 +++--- crates/forge/bin/cmd/geiger/find.rs | 13 ++--- crates/forge/bin/cmd/geiger/mod.rs | 2 +- crates/forge/bin/cmd/generate/mod.rs | 2 +- crates/forge/bin/cmd/init.rs | 2 +- crates/forge/bin/cmd/install.rs | 6 +-- crates/forge/bin/cmd/snapshot.rs | 8 +-- crates/forge/bin/cmd/test/mod.rs | 2 +- crates/forge/src/gas_report.rs | 3 +- crates/forge/src/result.rs | 24 ++++----- crates/script/Cargo.toml | 2 +- crates/script/src/execute.rs | 8 ++- crates/script/src/lib.rs | 4 +- crates/script/src/runner.rs | 2 +- crates/script/src/sequence.rs | 5 +- crates/verify/Cargo.toml | 2 +- crates/verify/src/bytecode.rs | 17 +++--- 40 files changed, 210 insertions(+), 225 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a52ff0b62..1ba16aacc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -725,7 +725,7 @@ dependencies = [ "tracing", "tracing-subscriber", "vergen", - "yansi 0.5.1", + "yansi 1.0.1", ] [[package]] @@ -1821,7 +1821,7 @@ dependencies = [ "tokio", "tracing", "vergen", - "yansi 0.5.1", + "yansi 1.0.1", ] [[package]] @@ -1897,7 +1897,7 @@ dependencies = [ "tracing", "tracing-subscriber", "vergen", - "yansi 0.5.1", + "yansi 1.0.1", ] [[package]] @@ -3534,7 +3534,7 @@ dependencies = [ "tracing-subscriber", "vergen", "watchexec", - "yansi 0.5.1", + "yansi 1.0.1", ] [[package]] @@ -3618,7 +3618,7 @@ dependencies = [ "serde_json", "tempfile", "tracing", - "yansi 0.5.1", + "yansi 1.0.1", ] [[package]] @@ -3651,7 +3651,7 @@ dependencies = [ "tempfile", "tokio", "tracing", - "yansi 0.5.1", + "yansi 1.0.1", ] [[package]] @@ -3764,7 +3764,7 @@ dependencies = [ "tracing", "tracing-error", "tracing-subscriber", - "yansi 0.5.1", + "yansi 1.0.1", ] [[package]] @@ -3820,14 +3820,14 @@ dependencies = [ "tracing", "url", "walkdir", - "yansi 0.5.1", + "yansi 1.0.1", ] [[package]] name = "foundry-compilers" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8caca67f174741b05c2f4188d3ee93420342130eb2a768abb9b885bb8b918df2" +checksum = "10a73252c7753488cf7779e2788e05088b3b3d9198fbbc5d4da94b5afd0f751d" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3857,7 +3857,7 @@ dependencies = [ "tokio", "tracing", "walkdir", - "yansi 0.5.1", + "yansi 1.0.1", ] [[package]] @@ -4042,7 +4042,7 @@ dependencies = [ "tempfile", "tokio", "tracing", - "yansi 0.5.1", + "yansi 1.0.1", ] [[package]] @@ -9459,6 +9459,9 @@ name = "yansi" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" +dependencies = [ + "is-terminal", +] [[package]] name = "zerocopy" diff --git a/Cargo.toml b/Cargo.toml index 2859b9349..d14288a38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -138,7 +138,7 @@ foundry-linking = { path = "crates/linking" } # solc & compilation utilities foundry-block-explorers = { version = "0.2.6", default-features = false } -foundry-compilers = { version = "0.3.17", default-features = false } +foundry-compilers = { version = "0.3.18", default-features = false } ## revm # no default features to avoid c-kzg @@ -214,6 +214,7 @@ vergen = { version = "8", default-features = false } indexmap = "2.2" tikv-jemallocator = "0.5.4" num-format = "0.4.4" +yansi = { version = "1.0", features = ["detect-tty", "detect-env"] } axum = "0.7" hyper = "1.0" diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index 7930d437a..b7619eb5a 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -70,7 +70,7 @@ serde_repr = "0.1" serde_json.workspace = true serde.workspace = true thiserror = "1" -yansi = "0.5" +yansi.workspace = true tempfile = "3" itertools.workspace = true rand = "0.8" diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 7c35f0c9d..883e1dd44 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -182,13 +182,9 @@ pub struct NodeConfig { impl NodeConfig { fn as_string(&self, fork: Option<&ClientFork>) -> String { let mut config_string: String = String::new(); - let _ = write!(config_string, "\n{}", Paint::green(BANNER)); + let _ = write!(config_string, "\n{}", BANNER.green()); let _ = write!(config_string, "\n {VERSION_MESSAGE}"); - let _ = write!( - config_string, - "\n {}", - Paint::green("https://github.com/foundry-rs/foundry") - ); + let _ = write!(config_string, "\n {}", "https://github.com/foundry-rs/foundry".green()); let _ = write!( config_string, @@ -256,9 +252,10 @@ Chain ID: {} Chain ID ================== + {} "#, - Paint::green(format!("\n{}", self.get_chain_id())) + self.get_chain_id().green() ); } @@ -268,9 +265,10 @@ Chain ID r#" Gas Price ================== + {} "#, - Paint::green(format!("\n{}", self.get_gas_price())) + self.get_gas_price().green() ); } else { let _ = write!( @@ -278,9 +276,10 @@ Gas Price r#" Base Fee ================== + {} "#, - Paint::green(format!("\n{}", self.get_base_fee())) + self.get_base_fee().green() ); } @@ -289,9 +288,10 @@ Base Fee r#" Gas Limit ================== + {} "#, - Paint::green(format!("\n{}", self.gas_limit)) + self.gas_limit.green() ); let _ = write!( @@ -299,9 +299,10 @@ Gas Limit r#" Genesis Timestamp ================== + {} "#, - Paint::green(format!("\n{}", self.get_genesis_timestamp())) + self.get_genesis_timestamp().green() ); config_string diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index 04160fa3f..e96737ce5 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -78,7 +78,7 @@ semver = "1" tempfile = "3" tokio = { version = "1", features = ["macros", "signal"] } tracing.workspace = true -yansi = "0.5" +yansi.workspace = true evmole = "0.3.1" [target.'cfg(unix)'.dependencies] diff --git a/crates/cast/bin/cmd/wallet/mod.rs b/crates/cast/bin/cmd/wallet/mod.rs index 60e21a2fb..1ac943204 100644 --- a/crates/cast/bin/cmd/wallet/mod.rs +++ b/crates/cast/bin/cmd/wallet/mod.rs @@ -236,7 +236,7 @@ impl WalletSubcommands { let wallets = wallets.into_iter().map(|b| b.build()).collect::, _>>()?; - println!("{}", Paint::green("Successfully generated a new mnemonic.")); + println!("{}", "Successfully generated a new mnemonic.".green()); println!("Phrase:\n{phrase}"); println!("\nAccounts:"); for (i, wallet) in wallets.iter().enumerate() { @@ -347,7 +347,7 @@ flag to set your key via: "`{}` keystore was saved successfully. Address: {:?}", &account_name, address, ); - println!("{}", Paint::green(success_message)); + println!("{}", success_message.green()); } WalletSubcommands::List(cmd) => { cmd.run().await?; diff --git a/crates/chisel/Cargo.toml b/crates/chisel/Cargo.toml index 9d07ae800..05ad5aefc 100644 --- a/crates/chisel/Cargo.toml +++ b/crates/chisel/Cargo.toml @@ -47,7 +47,7 @@ solang-parser.workspace = true strum = { workspace = true, features = ["derive"] } time = { version = "0.3", features = ["formatting"] } tokio = { version = "1", features = ["full"] } -yansi = "0.5" +yansi.workspace = true tracing.workspace = true [target.'cfg(unix)'.dependencies] diff --git a/crates/chisel/bin/main.rs b/crates/chisel/bin/main.rs index 0cca09bf9..706c6af99 100644 --- a/crates/chisel/bin/main.rs +++ b/crates/chisel/bin/main.rs @@ -98,11 +98,6 @@ pub enum ChiselSubcommand { async fn main() -> eyre::Result<()> { handler::install(); utils::subscriber(); - #[cfg(windows)] - if !Paint::enable_windows_ascii() { - Paint::disable() - } - utils::load_dotenv(); // Parse command args @@ -165,7 +160,7 @@ async fn main() -> eyre::Result<()> { } Some(ChiselSubcommand::ClearCache) => { match dispatcher.dispatch_command(ChiselCommand::ClearCache, &[]).await { - DispatchResult::CommandSuccess(Some(msg)) => println!("{}", Paint::green(msg)), + DispatchResult::CommandSuccess(Some(msg)) => println!("{}", msg.green()), DispatchResult::CommandFailed(e) => eprintln!("{e}"), _ => panic!("Unexpected result! Please report this bug."), } @@ -187,7 +182,7 @@ async fn main() -> eyre::Result<()> { } // Print welcome header - println!("Welcome to Chisel! Type `{}` to show available commands.", Paint::green("!help")); + println!("Welcome to Chisel! Type `{}` to show available commands.", "!help".green()); // Begin Rustyline loop loop { @@ -250,17 +245,17 @@ async fn dispatch_repl_line(dispatcher: &mut ChiselDispatcher, line: &str) -> bo DispatchResult::Success(msg) | DispatchResult::CommandSuccess(msg) => { debug!(%line, ?msg, "dispatch success"); if let Some(msg) = msg { - println!("{}", Paint::green(msg)); + println!("{}", msg.green()); } }, DispatchResult::UnrecognizedCommand(e) => eprintln!("{e}"), DispatchResult::SolangParserFailed(e) => { - eprintln!("{}", Paint::red("Compilation error")); - eprintln!("{}", Paint::red(format!("{e:?}"))); + eprintln!("{}", "Compilation error".red()); + eprintln!("{}", format!("{e:?}").red()); } - DispatchResult::FileIoError(e) => eprintln!("{}", Paint::red(format!("⚒️ Chisel File IO Error - {e}"))), - DispatchResult::CommandFailed(msg) | DispatchResult::Failure(Some(msg)) => eprintln!("{}", Paint::red(msg)), - DispatchResult::Failure(None) => eprintln!("{}\nPlease Report this bug as a github issue if it persists: https://github.com/foundry-rs/foundry/issues/new/choose", Paint::red("⚒️ Unknown Chisel Error ⚒️")), + DispatchResult::FileIoError(e) => eprintln!("{}", format!("⚒️ Chisel File IO Error - {e}").red()), + DispatchResult::CommandFailed(msg) | DispatchResult::Failure(Some(msg)) => eprintln!("{}", msg.red()), + DispatchResult::Failure(None) => eprintln!("{}\nPlease Report this bug as a github issue if it persists: https://github.com/foundry-rs/foundry/issues/new/choose", "⚒️ Unknown Chisel Error ⚒️".red()), } r.is_error() } @@ -273,19 +268,19 @@ async fn evaluate_prelude( ) -> eyre::Result<()> { let Some(prelude_dir) = maybe_prelude else { return Ok(()) }; if prelude_dir.is_file() { - println!("{} {}", Paint::yellow("Loading prelude source file:"), prelude_dir.display(),); + println!("{} {}", "Loading prelude source file:".yellow(), prelude_dir.display(),); load_prelude_file(dispatcher, prelude_dir).await?; - println!("{}\n", Paint::green("Prelude source file loaded successfully!")); + println!("{}\n", "Prelude source file loaded successfully!".green()); } else { let prelude_sources = fs::files_with_ext(prelude_dir, "sol"); let print_success_msg = !prelude_sources.is_empty(); for source_file in prelude_sources { - println!("{} {}", Paint::yellow("Loading prelude source file:"), source_file.display(),); + println!("{} {}", "Loading prelude source file:".yellow(), source_file.display(),); load_prelude_file(dispatcher, source_file).await?; } if print_success_msg { - println!("{}\n", Paint::green("All prelude source files loaded successfully!")); + println!("{}\n", "All prelude source files loaded successfully!".green()); } } Ok(()) diff --git a/crates/chisel/src/dispatcher.rs b/crates/chisel/src/dispatcher.rs index cba66f7aa..ebf4aeb37 100644 --- a/crates/chisel/src/dispatcher.rs +++ b/crates/chisel/src/dispatcher.rs @@ -195,7 +195,7 @@ impl ChiselDispatcher { ChiselCommand::iter().map(CmdDescriptor::from).collect::>(); DispatchResult::CommandSuccess(Some(format!( "{}\n{}", - Paint::cyan(format!("{CHISEL_CHAR} Chisel help\n=============")), + format!("{CHISEL_CHAR} Chisel help\n=============").cyan(), CmdCategory::iter() .map(|cat| { // Get commands in the current category @@ -209,13 +209,13 @@ impl ChiselDispatcher { // Format the help menu for the current category format!( "{}\n{}\n", - Paint::magenta(cat), + cat.magenta(), cat_cmds .iter() .map(|(cmds, desc, _)| format!( "\t{} - {}", cmds.iter() - .map(|cmd| format!("!{}", Paint::green(cmd))) + .map(|cmd| format!("!{}", cmd.green())) .collect::>() .join(" | "), desc @@ -274,7 +274,7 @@ impl ChiselDispatcher { if let Err(e) = self.session.write() { return DispatchResult::FileIoError(e.into()) } - println!("{}", Paint::green("Saved current session!")); + println!("{}", "Saved current session!".green()); } // Parse the arguments @@ -304,11 +304,11 @@ impl ChiselDispatcher { ChiselCommand::ListSessions => match ChiselSession::list_sessions() { Ok(sessions) => DispatchResult::CommandSuccess(Some(format!( "{}\n{}", - Paint::cyan(format!("{CHISEL_CHAR} Chisel Sessions")), + format!("{CHISEL_CHAR} Chisel Sessions").cyan(), sessions .iter() .map(|(time, name)| { - format!("{} - {}", Paint::blue(format!("{time:?}")), name) + format!("{} - {}", format!("{time:?}").blue(), name) }) .collect::>() .join("\n") @@ -372,7 +372,7 @@ impl ChiselDispatcher { } // Create success message before moving the fork_url - let success_msg = format!("Set fork URL to {}", Paint::yellow(&fork_url)); + let success_msg = format!("Set fork URL to {}", &fork_url.yellow()); // Update the fork_url inside of the [SessionSourceConfig]'s [EvmOpts] // field @@ -410,7 +410,7 @@ impl ChiselDispatcher { self.source_mut().config.calldata = Some(calldata); DispatchResult::CommandSuccess(Some(format!( "Set calldata to '{}'", - Paint::yellow(arg) + arg.yellow() ))) } Err(e) => DispatchResult::CommandFailed(Self::make_error(format!( @@ -428,8 +428,8 @@ impl ChiselDispatcher { (0..mem.len()).step_by(32).for_each(|i| { println!( "{}: {}", - Paint::yellow(format!("[0x{:02x}:0x{:02x}]", i, i + 32)), - Paint::cyan(hex::encode_prefixed(&mem[i..i + 32])) + format!("[0x{:02x}:0x{:02x}]", i, i + 32).yellow(), + hex::encode_prefixed(&mem[i..i + 32]).cyan() ); }); } else { @@ -437,8 +437,8 @@ impl ChiselDispatcher { (0..stack.len()).rev().for_each(|i| { println!( "{}: {}", - Paint::yellow(format!("[{}]", stack.len() - i - 1)), - Paint::cyan(format!("0x{:02x}", stack[i])) + format!("[{}]", stack.len() - i - 1).yellow(), + format!("0x{:02x}", stack[i]).cyan() ); }); } @@ -698,7 +698,7 @@ impl ChiselDispatcher { // Show console logs, if there are any let decoded_logs = decode_console_logs(&res.logs); if !decoded_logs.is_empty() { - println!("{}", Paint::green("Logs:")); + println!("{}", "Logs:".green()); for log in decoded_logs { println!(" {log}"); } @@ -842,7 +842,7 @@ impl ChiselDispatcher { // Show console logs, if there are any let decoded_logs = decode_console_logs(&res.logs); if !decoded_logs.is_empty() { - println!("{}", Paint::green("Logs:")); + println!("{}", "Logs:".green()); for log in decoded_logs { println!(" {log}"); } @@ -931,7 +931,7 @@ impl ChiselDispatcher { eyre::bail!("Unexpected error: No traces gathered. Please report this as a bug: https://github.com/foundry-rs/foundry/issues/new?assignees=&labels=T-bug&template=BUG-FORM.yml"); } - println!("{}", Paint::green("Traces:")); + println!("{}", "Traces:".green()); for (kind, trace) in &result.traces { // Display all Setup + Execution traces. if matches!(kind, TraceKind::Setup | TraceKind::Execution) { @@ -952,7 +952,7 @@ impl ChiselDispatcher { /// /// A formatted error [String]. pub fn make_error(msg: T) -> String { - format!("{} {}", Paint::red(format!("{CHISEL_CHAR} Chisel Error:")), Paint::red(msg)) + format!("{} {}", format!("{CHISEL_CHAR} Chisel Error:").red(), msg.red()) } } diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index 6343013b1..424bcda90 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -194,7 +194,7 @@ impl SessionSource { } let decoded_logs = decode_console_logs(&res.logs); if !decoded_logs.is_empty() { - println!("{}", Paint::green("Logs:")); + println!("{}", "Logs:".green()); for log in decoded_logs { println!(" {log}"); } @@ -337,20 +337,20 @@ impl SessionSource { fn format_token(token: DynSolValue) -> String { match token { DynSolValue::Address(a) => { - format!("Type: {}\n└ Data: {}", Paint::red("address"), Paint::cyan(a.to_string())) + format!("Type: {}\n└ Data: {}", "address".red(), a.cyan()) } DynSolValue::FixedBytes(b, byte_len) => { format!( "Type: {}\n└ Data: {}", - Paint::red(format!("bytes{byte_len}")), - Paint::cyan(hex::encode_prefixed(b)) + format!("bytes{byte_len}").red(), + hex::encode_prefixed(b).cyan() ) } DynSolValue::Int(i, bit_len) => { format!( "Type: {}\n├ Hex: {}\n├ Hex (full word): {}\n└ Decimal: {}", - Paint::red(format!("int{}", bit_len)), - Paint::cyan(format!( + format!("int{}", bit_len).red(), + format!( "0x{}", format!("{i:x}") .char_indices() @@ -358,16 +358,17 @@ fn format_token(token: DynSolValue) -> String { .take(bit_len / 4) .map(|(_, c)| c) .collect::() - )), - Paint::cyan(format!("{i:#x}")), - Paint::cyan(i) + ) + .cyan(), + format!("{i:#x}").cyan(), + i.cyan() ) } DynSolValue::Uint(i, bit_len) => { format!( "Type: {}\n├ Hex: {}\n├ Hex (full word): {}\n└ Decimal: {}", - Paint::red(format!("uint{}", bit_len)), - Paint::cyan(format!( + format!("uint{}", bit_len).red(), + format!( "0x{}", format!("{i:x}") .char_indices() @@ -375,50 +376,51 @@ fn format_token(token: DynSolValue) -> String { .take(bit_len / 4) .map(|(_, c)| c) .collect::() - )), - Paint::cyan(format!("{i:#x}")), - Paint::cyan(i) + ) + .cyan(), + format!("{i:#x}").cyan(), + i.cyan() ) } DynSolValue::Bool(b) => { - format!("Type: {}\n└ Value: {}", Paint::red("bool"), Paint::cyan(b)) + format!("Type: {}\n└ Value: {}", "bool".red(), b.cyan()) } DynSolValue::String(_) | DynSolValue::Bytes(_) => { let hex = hex::encode(token.abi_encode()); let s = token.as_str(); format!( "Type: {}\n{}├ Hex (Memory):\n├─ Length ({}): {}\n├─ Contents ({}): {}\n├ Hex (Tuple Encoded):\n├─ Pointer ({}): {}\n├─ Length ({}): {}\n└─ Contents ({}): {}", - Paint::red(if s.is_some() { "string" } else { "dynamic bytes" }), + if s.is_some() { "string" } else { "dynamic bytes" }.red(), if let Some(s) = s { - format!("├ UTF-8: {}\n", Paint::cyan(s)) + format!("├ UTF-8: {}\n", s.cyan()) } else { String::default() }, - Paint::yellow("[0x00:0x20]"), - Paint::cyan(format!("0x{}", &hex[64..128])), - Paint::yellow("[0x20:..]"), - Paint::cyan(format!("0x{}", &hex[128..])), - Paint::yellow("[0x00:0x20]"), - Paint::cyan(format!("0x{}", &hex[..64])), - Paint::yellow("[0x20:0x40]"), - Paint::cyan(format!("0x{}", &hex[64..128])), - Paint::yellow("[0x40:..]"), - Paint::cyan(format!("0x{}", &hex[128..])), + "[0x00:0x20]".yellow(), + format!("0x{}", &hex[64..128]).cyan(), + "[0x20:..]".yellow(), + format!("0x{}", &hex[128..]).cyan(), + "[0x00:0x20]".yellow(), + format!("0x{}", &hex[..64]).cyan(), + "[0x20:0x40]".yellow(), + format!("0x{}", &hex[64..128]).cyan(), + "[0x40:..]".yellow(), + format!("0x{}", &hex[128..]).cyan(), ) } DynSolValue::FixedArray(tokens) | DynSolValue::Array(tokens) => { let mut out = format!( "{}({}) = {}", - Paint::red("array"), - Paint::yellow(format!("{}", tokens.len())), - Paint::red('[') + "array".red(), + format!("{}", tokens.len()).yellow(), + '['.red() ); for token in tokens { out.push_str("\n ├ "); out.push_str(&format_token(token).replace('\n', "\n ")); out.push('\n'); } - out.push_str(&Paint::red(']').to_string()); + out.push_str(&']'.red().to_string()); out } DynSolValue::Tuple(tokens) => { @@ -428,18 +430,14 @@ fn format_token(token: DynSolValue) -> String { .map(|t| t.unwrap_or_default().into_owned()) .collect::>() .join(", "); - let mut out = format!( - "{}({}) = {}", - Paint::red("tuple"), - Paint::yellow(displayed_types), - Paint::red('(') - ); + let mut out = + format!("{}({}) = {}", "tuple".red(), displayed_types.yellow(), '('.red()); for token in tokens { out.push_str("\n ├ "); out.push_str(&format_token(token).replace('\n', "\n ")); out.push('\n'); } - out.push_str(&Paint::red(')').to_string()); + out.push_str(&')'.red().to_string()); out } _ => { @@ -487,7 +485,7 @@ fn format_event_definition(event_definition: &pt::EventDefinition) -> Result Result>() .join(", ") )), - Paint::cyan(event.signature()), - Paint::cyan(event.selector()), + event.signature().cyan(), + event.selector().cyan(), )) } diff --git a/crates/chisel/src/session_source.rs b/crates/chisel/src/session_source.rs index 47660ba09..9deac254b 100644 --- a/crates/chisel/src/session_source.rs +++ b/crates/chisel/src/session_source.rs @@ -97,7 +97,7 @@ impl SessionSourceConfig { SolcReq::Version(version.into()) } else { if !self.foundry_config.offline { - print!("{}", Paint::green("No solidity versions installed! ")); + print!("{}", "No solidity versions installed! ".green()); } // use default SolcReq::Version("0.8.19".parse().unwrap()) @@ -121,10 +121,7 @@ impl SessionSourceConfig { if self.foundry_config.offline { eyre::bail!("can't install missing solc {version} in offline mode") } - println!( - "{}", - Paint::green(format!("Installing solidity version {version}...")) - ); + println!("{}", format!("Installing solidity version {version}...").green()); Solc::blocking_install(&version)?; solc = Solc::find_svm_installed_version(version.to_string())?; } @@ -289,21 +286,21 @@ impl SessionSource { /// Clears global code from the source pub fn drain_global_code(&mut self) -> &mut Self { - self.global_code.clear(); + String::clear(&mut self.global_code); self.generated_output = None; self } /// Clears top-level code from the source pub fn drain_top_level_code(&mut self) -> &mut Self { - self.top_level_code.clear(); + String::clear(&mut self.top_level_code); self.generated_output = None; self } /// Clears the "run()" function's code pub fn drain_run(&mut self) -> &mut Self { - self.run_code.clear(); + String::clear(&mut self.run_code); self.generated_output = None; self } diff --git a/crates/chisel/src/solidity_helper.rs b/crates/chisel/src/solidity_helper.rs index 42641a1f0..f09ad1683 100644 --- a/crates/chisel/src/solidity_helper.rs +++ b/crates/chisel/src/solidity_helper.rs @@ -19,7 +19,7 @@ use solang_parser::{ pt, }; use std::{borrow::Cow, str::FromStr}; -use yansi::{Color, Paint, Style}; +use yansi::{Color, Style}; /// The default pre-allocation for solang parsed comments const DEFAULT_COMMENTS: usize = 5; @@ -71,7 +71,7 @@ impl SolidityHelper { pt::Comment::DocLine(loc, _) | pt::Comment::DocBlock(loc, _) => loc, }; - (loc.start(), Style::default().dimmed(), loc.end()) + (loc.start(), Style::new().dim(), loc.end()) }); out.extend(comments_iter); @@ -102,7 +102,7 @@ impl SolidityHelper { /// Highlights a solidity source string pub fn highlight(input: &str) -> Cow { - if !Paint::is_enabled() { + if !yansi::is_enabled() { return Cow::Borrowed(input) } @@ -119,7 +119,7 @@ impl SolidityHelper { // cmd out.push(COMMAND_LEADER); let cmd_res = ChiselCommand::from_str(cmd); - let style = Style::new(if cmd_res.is_ok() { Color::Green } else { Color::Red }); + let style = (if cmd_res.is_ok() { Color::Green } else { Color::Red }).foreground(); Self::paint_unchecked(cmd, style, &mut out); // rest @@ -186,7 +186,7 @@ impl SolidityHelper { } /// Formats `input` with `style` into `out`, without checking `style.wrapping` or - /// `Paint::is_enabled` + /// `yansi::is_enabled` #[inline] fn paint_unchecked(string: &str, style: Style, out: &mut String) { if style == Style::default() { @@ -220,7 +220,7 @@ impl Highlighter for SolidityHelper { prompt: &'p str, _default: bool, ) -> Cow<'b, str> { - if !Paint::is_enabled() { + if !yansi::is_enabled() { return Cow::Borrowed(prompt) } @@ -231,12 +231,16 @@ impl Highlighter for SolidityHelper { let id_end = prompt.find(')').unwrap(); let id_span = 5..id_end; let id = &prompt[id_span.clone()]; - out.replace_range(id_span, &Self::paint_unchecked_owned(id, Color::Yellow.style())); - out.replace_range(1..=2, &Self::paint_unchecked_owned("ID", Color::Cyan.style())); + out.replace_range( + id_span, + &Self::paint_unchecked_owned(id, Color::Yellow.foreground()), + ); + out.replace_range(1..=2, &Self::paint_unchecked_owned("ID", Color::Cyan.foreground())); } if let Some(i) = out.find(PROMPT_ARROW) { - let style = if self.errored { Color::Red.style() } else { Color::Green.style() }; + let style = + if self.errored { Color::Red.foreground() } else { Color::Green.foreground() }; let mut arrow = String::with_capacity(MAX_ANSI_LEN + 4); @@ -278,7 +282,7 @@ impl<'a> TokenStyle for Token<'a> { fn style(&self) -> Style { use Token::*; match self { - StringLiteral(_, _) => Color::Green.style(), + StringLiteral(_, _) => Color::Green.foreground(), AddressLiteral(_) | HexLiteral(_) | @@ -286,21 +290,21 @@ impl<'a> TokenStyle for Token<'a> { RationalNumber(_, _, _) | HexNumber(_) | True | - False => Color::Yellow.style(), + False => Color::Yellow.foreground(), Memory | Storage | Calldata | Public | Private | Internal | External | Constant | Pure | View | Payable | Anonymous | Indexed | Abstract | Virtual | Override | - Modifier | Immutable | Unchecked => Color::Cyan.style(), + 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.style() + Color::Magenta.foreground() } Uint(_) | Int(_) | Bytes(_) | Byte | DynamicBytes | Bool | Address | String | - Mapping => Color::Blue.style(), + Mapping => Color::Blue.foreground(), Identifier(_) => Style::default(), diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index fcc95d55e..a314ebb60 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -40,7 +40,7 @@ tokio = { version = "1", features = ["macros"] } tracing-error = "0.2" tracing-subscriber = { workspace = true, features = ["registry", "env-filter", "fmt"] } tracing.workspace = true -yansi = "0.5" +yansi.workspace = true hex.workspace = true futures = "0.3" diff --git a/crates/cli/src/handler.rs b/crates/cli/src/handler.rs index 6666686c5..d6e34f3b8 100644 --- a/crates/cli/src/handler.rs +++ b/crates/cli/src/handler.rs @@ -16,7 +16,7 @@ impl EyreHandler for Handler { return core::fmt::Debug::fmt(error, f) } writeln!(f)?; - write!(f, "{}", Paint::red(error))?; + write!(f, "{}", error.red())?; if let Some(cause) = error.source() { write!(f, "\n\nContext:")?; diff --git a/crates/cli/src/utils/cmd.rs b/crates/cli/src/utils/cmd.rs index 475fae2ed..d8c5ab222 100644 --- a/crates/cli/src/utils/cmd.rs +++ b/crates/cli/src/utils/cmd.rs @@ -128,7 +128,7 @@ pub fn needs_setup(abi: &JsonAbi) -> bool { if setup_fn.name != "setUp" { println!( "{} Found invalid setup function \"{}\" did you mean \"setUp()\"?", - Paint::yellow("Warning:").bold(), + "Warning:".yellow().bold(), setup_fn.signature() ); } @@ -437,9 +437,9 @@ pub async fn print_traces(result: &mut TraceResult, decoder: &CallTraceDecoder) println!(); if result.success { - println!("{}", Paint::green("Transaction successfully executed.")); + println!("{}", "Transaction successfully executed.".green()); } else { - println!("{}", Paint::red("Transaction failed.")); + println!("{}", "Transaction failed.".red()); } println!("Gas used: {}", result.gas_used); diff --git a/crates/cli/src/utils/mod.rs b/crates/cli/src/utils/mod.rs index 015ce2145..e5950078e 100644 --- a/crates/cli/src/utils/mod.rs +++ b/crates/cli/src/utils/mod.rs @@ -13,7 +13,6 @@ use std::{ }; use tracing_error::ErrorLayer; use tracing_subscriber::prelude::*; -use yansi::Paint; mod cmd; pub use cmd::*; @@ -208,16 +207,9 @@ pub fn load_dotenv() { }; } -/// Disables terminal colours if either: -/// - Running windows and the terminal does not support colour codes. -/// - Colour has been disabled by some environment variable. -/// - We are running inside a test +/// Sets the default [`yansi`] color output condition. pub fn enable_paint() { - let is_windows = cfg!(windows) && !Paint::enable_windows_ascii(); - let env_colour_disabled = std::env::var("NO_COLOR").is_ok(); - if is_windows || env_colour_disabled { - Paint::disable(); - } + yansi::whenever(yansi::Condition::TTY_AND_COLOR); } /// Useful extensions to [`std::process::Command`]. diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 07f8837ca..8bccf5054 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -60,7 +60,7 @@ tokio = "1" tracing.workspace = true url = "2" walkdir = "2" -yansi = "0.5" +yansi.workspace = true rustc-hash.workspace = true num-format.workspace = true diff --git a/crates/common/src/fmt/mod.rs b/crates/common/src/fmt/mod.rs index fbe1670fc..a43fe7dea 100644 --- a/crates/common/src/fmt/mod.rs +++ b/crates/common/src/fmt/mod.rs @@ -23,7 +23,7 @@ pub use ui::{get_pretty_block_attr, get_pretty_tx_attr, get_pretty_tx_receipt_at /// use alloy_primitives::U256; /// use foundry_common::fmt::format_uint_exp as f; /// -/// # yansi::Paint::disable(); +/// # yansi::disable(); /// assert_eq!(f(U256::from(0)), "0"); /// assert_eq!(f(U256::from(1234)), "1234"); /// assert_eq!(f(U256::from(1234567890)), "1234567890 [1.234e9]"); @@ -36,7 +36,7 @@ pub fn format_uint_exp(num: U256) -> String { } let exp = to_exp_notation(num, 4, true, Sign::Positive); - format!("{num} {}", Paint::default(format!("[{exp}]")).dimmed()) + format!("{num} {}", format!("[{exp}]").dim()) } /// Formats a U256 number to string, adding an exponential notation _hint_. @@ -49,7 +49,7 @@ pub fn format_uint_exp(num: U256) -> String { /// use alloy_primitives::I256; /// use foundry_common::fmt::format_int_exp as f; /// -/// # yansi::Paint::disable(); +/// # yansi::disable(); /// assert_eq!(f(I256::try_from(0).unwrap()), "0"); /// assert_eq!(f(I256::try_from(-1).unwrap()), "-1"); /// assert_eq!(f(I256::try_from(1234).unwrap()), "1234"); @@ -72,5 +72,5 @@ pub fn format_int_exp(num: I256) -> String { } let exp = to_exp_notation(abs, 4, true, sign); - format!("{sign}{abs} {}", Paint::default(format!("[{exp}]")).dimmed()) + format!("{sign}{abs} {}", format!("[{exp}]").dim()) } diff --git a/crates/common/src/term.rs b/crates/common/src/term.rs index afeae0664..5cfb5ef37 100644 --- a/crates/common/src/term.rs +++ b/crates/common/src/term.rs @@ -69,7 +69,7 @@ impl Spinner { return } - let indicator = Paint::green(self.indicator[self.idx % self.indicator.len()]); + let indicator = self.indicator[self.idx % self.indicator.len()].green(); let indicator = Paint::new(format!("[{indicator}]")).bold(); print!("\r\x33[2K\r{indicator} {}", self.message); io::stdout().flush().unwrap(); @@ -194,7 +194,7 @@ impl Reporter for SpinnerReporter { } fn on_solc_installation_error(&self, version: &Version, error: &str) { - self.send_msg(Paint::red(format!("Failed to install Solc {version}: {error}")).to_string()); + self.send_msg(format!("Failed to install Solc {version}: {error}").red().to_string()); } fn on_unresolved_imports(&self, imports: &[(&Path, &Path)], remappings: &[Remapping]) { @@ -221,8 +221,8 @@ macro_rules! cli_warn { ($($arg:tt)*) => { eprintln!( "{}{} {}", - yansi::Paint::yellow("warning").bold(), - yansi::Paint::new(":").bold(), + yansi::Painted::new("warning").yellow().bold(), + yansi::Painted::new(":").bold(), format_args!($($arg)*) ) } diff --git a/crates/evm/traces/Cargo.toml b/crates/evm/traces/Cargo.toml index be3cecb36..fd873609d 100644 --- a/crates/evm/traces/Cargo.toml +++ b/crates/evm/traces/Cargo.toml @@ -31,7 +31,7 @@ once_cell = "1" serde = "1" tokio = { version = "1", features = ["time", "macros"] } tracing = "0.1" -yansi = "0.5" +yansi.workspace = true [dev-dependencies] tempfile = "3" diff --git a/crates/evm/traces/src/lib.rs b/crates/evm/traces/src/lib.rs index a52fd3b42..16cadde10 100644 --- a/crates/evm/traces/src/lib.rs +++ b/crates/evm/traces/src/lib.rs @@ -125,8 +125,8 @@ pub async fn render_trace_arena( write!( s, "{child}{EDGE}{}{}", - color.paint(RETURN), - color.paint(format!("[{:?}] ", node.trace.status)) + RETURN.fg(color), + format!("[{:?}] ", node.trace.status).fg(color) )?; match return_data { Some(val) => write!(s, "{val}"), @@ -164,8 +164,8 @@ pub async fn render_trace( write!( &mut s, "{}{} {}@{}", - Paint::yellow(CALL), - Paint::yellow("new"), + CALL.yellow(), + "new".yellow(), decoded.label.as_deref().unwrap_or(""), address )?; @@ -198,14 +198,14 @@ pub async fn render_trace( write!( &mut s, "{addr}::{func_name}{opt_value}({inputs}){action}", - addr = color.paint(decoded.label.as_deref().unwrap_or(&address)), - func_name = color.paint(func_name), + addr = decoded.label.as_deref().unwrap_or(&address).fg(color), + func_name = func_name.fg(color), opt_value = if trace.value.is_zero() { String::new() } else { format!("{{value: {}}}", trace.value) }, - action = Paint::yellow(action), + action = action.yellow(), )?; } @@ -227,11 +227,11 @@ async fn render_trace_log( s, "{:>13}: {}", if i == 0 { "emit topic 0".to_string() } else { format!("topic {i}") }, - Paint::cyan(format!("{topic:?}")) + format!("{topic:?}").cyan() )?; } - write!(s, " data: {}", Paint::cyan(hex::encode_prefixed(&log.data)))?; + write!(s, " data: {}", hex::encode_prefixed(&log.data).cyan())?; } DecodedCallLog::Decoded(name, params) => { let params = params @@ -240,7 +240,7 @@ async fn render_trace_log( .collect::>() .join(", "); - write!(s, "emit {}({params})", Paint::cyan(name.clone()))?; + write!(s, "emit {}({params})", name.clone().cyan())?; } } diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 7b05ee06e..3f724481c 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -37,7 +37,7 @@ proptest = "1" rayon = "1" serde.workspace = true tracing.workspace = true -yansi = "0.5" +yansi.workspace = true humantime-serde = "1.1.1" # bin diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index 66f45b770..2b5ce85a6 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -112,13 +112,13 @@ impl CoverageArgs { } // print warning message - let msg = Paint::yellow(concat!( + let msg = concat!( "Warning! \"--ir-minimum\" flag 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", "See more: https://github.com/foundry-rs/foundry/issues/3357", - )); + ).yellow(); p_println!(!self.test.build_args().silent => "{msg}"); // Enable viaIR with minimum optimization diff --git a/crates/forge/bin/cmd/fmt.rs b/crates/forge/bin/cmd/fmt.rs index 36c080000..06e05a568 100644 --- a/crates/forge/bin/cmd/fmt.rs +++ b/crates/forge/bin/cmd/fmt.rs @@ -12,7 +12,7 @@ use std::{ io::{Read, Write as _}, path::{Path, PathBuf}, }; -use yansi::Color; +use yansi::{Color, Paint, Style}; /// CLI arguments for `forge fmt`. #[derive(Clone, Debug, Parser)] @@ -217,24 +217,24 @@ where } for op in group { for change in diff.iter_inline_changes(&op) { - let dimmed = Color::Default.style().dimmed(); + let dimmed = Style::new().dim(); let (sign, s) = match change.tag() { - ChangeTag::Delete => ("-", Color::Red.style()), - ChangeTag::Insert => ("+", Color::Green.style()), + ChangeTag::Delete => ("-", Color::Red.foreground()), + ChangeTag::Insert => ("+", Color::Green.foreground()), ChangeTag::Equal => (" ", dimmed), }; let _ = write!( diff_summary, "{}{} |{}", - dimmed.paint(Line(change.old_index())), - dimmed.paint(Line(change.new_index())), - s.bold().paint(sign), + Line(change.old_index()).paint(dimmed), + Line(change.new_index()).paint(dimmed), + sign.paint(s.bold()), ); for (emphasized, value) in change.iter_strings_lossy() { let s = if emphasized { s.underline().bg(Color::Black) } else { s }; - let _ = write!(diff_summary, "{}", s.paint(value)); + let _ = write!(diff_summary, "{}", value.paint(s)); } if change.missing_newline() { diff --git a/crates/forge/bin/cmd/geiger/find.rs b/crates/forge/bin/cmd/geiger/find.rs index ed1d5e691..3ea9c0234 100644 --- a/crates/forge/bin/cmd/geiger/find.rs +++ b/crates/forge/bin/cmd/geiger/find.rs @@ -55,15 +55,15 @@ impl<'a, 'b> fmt::Display for SolFileMetricsPrinter<'a, 'b> { ($($name:literal => $field:ident),*) => {$( let $field = &metrics.cheatcodes.$field[..]; if !$field.is_empty() { - writeln!(f, " {} {}", Paint::red(metrics.cheatcodes.$field.len()), Paint::red($name))?; + 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,"{}", Paint::red(pos))?; + writeln!(f,"{}", pos.red())?; for line in content.lines() { - writeln!(f, " {}", Paint::red(line))?; + writeln!(f, " {}", line.red())?; } } } @@ -71,12 +71,7 @@ impl<'a, 'b> fmt::Display for SolFileMetricsPrinter<'a, 'b> { } if !metrics.cheatcodes.is_empty() { - writeln!( - f, - "{} {}", - Paint::red(metrics.cheatcodes.len()), - Paint::red(file.display()) - )?; + writeln!(f, "{} {}", metrics.cheatcodes.len().red(), file.display().red())?; print_unsafe_fn!( "ffi" => ffi, "readFile" => read_file, diff --git a/crates/forge/bin/cmd/geiger/mod.rs b/crates/forge/bin/cmd/geiger/mod.rs index 8e3bc6fc5..27555ed1a 100644 --- a/crates/forge/bin/cmd/geiger/mod.rs +++ b/crates/forge/bin/cmd/geiger/mod.rs @@ -91,7 +91,7 @@ impl GeigerArgs { let sources = self.sources(&config).wrap_err("Failed to resolve files")?; if config.ffi { - eprintln!("{}\n", Paint::red("ffi enabled")); + eprintln!("{}\n", "ffi enabled".red()); } let root = config.__root.0; diff --git a/crates/forge/bin/cmd/generate/mod.rs b/crates/forge/bin/cmd/generate/mod.rs index 190ea52b7..fd40a78ae 100644 --- a/crates/forge/bin/cmd/generate/mod.rs +++ b/crates/forge/bin/cmd/generate/mod.rs @@ -44,7 +44,7 @@ impl GenerateTestArgs { // Write the test content to the test file. fs::write(&test_file_path, test_content)?; - println!("{} test file: {}", Paint::green("Generated"), test_file_path.to_str().unwrap()); + println!("{} test file: {}", "Generated".green(), test_file_path.to_str().unwrap()); Ok(()) } } diff --git a/crates/forge/bin/cmd/init.rs b/crates/forge/bin/cmd/init.rs index d05c8a7bd..87010244c 100644 --- a/crates/forge/bin/cmd/init.rs +++ b/crates/forge/bin/cmd/init.rs @@ -159,7 +159,7 @@ impl InitArgs { } } - p_println!(!quiet => " {} forge project", Paint::green("Initialized")); + p_println!(!quiet => " {} forge project", "Initialized".green()); Ok(()) } } diff --git a/crates/forge/bin/cmd/install.rs b/crates/forge/bin/cmd/install.rs index 75fdbe3aa..ccec7d5de 100644 --- a/crates/forge/bin/cmd/install.rs +++ b/crates/forge/bin/cmd/install.rs @@ -103,9 +103,7 @@ impl DependencyInstallOpts { if self.install(config, Vec::new()).is_err() && !quiet { eprintln!( "{}", - Paint::yellow( - "Your project has missing dependencies that could not be installed." - ) + "Your project has missing dependencies that could not be installed.".yellow() ) } true @@ -193,7 +191,7 @@ impl DependencyInstallOpts { } if !quiet { - let mut msg = format!(" {} {}", Paint::green("Installed"), dep.name); + let mut msg = format!(" {} {}", "Installed".green(), dep.name); if let Some(tag) = dep.tag.or(installed_tag) { msg.push(' '); msg.push_str(tag.as_str()); diff --git a/crates/forge/bin/cmd/snapshot.rs b/crates/forge/bin/cmd/snapshot.rs index d7147f49b..5f91951d8 100644 --- a/crates/forge/bin/cmd/snapshot.rs +++ b/crates/forge/bin/cmd/snapshot.rs @@ -398,21 +398,21 @@ fn diff(tests: Vec, snaps: Vec) -> Result<()> { fn fmt_pct_change(change: f64) -> String { let change_pct = change * 100.0; match change.partial_cmp(&0.0).unwrap_or(Ordering::Equal) { - Ordering::Less => Paint::green(format!("{change_pct:.3}%")).to_string(), + Ordering::Less => format!("{change_pct:.3}%").green().to_string(), Ordering::Equal => { format!("{change_pct:.3}%") } - Ordering::Greater => Paint::red(format!("{change_pct:.3}%")).to_string(), + Ordering::Greater => format!("{change_pct:.3}%").red().to_string(), } } fn fmt_change(change: i128) -> String { match change.cmp(&0) { - Ordering::Less => Paint::green(format!("{change}")).to_string(), + Ordering::Less => format!("{change}").green().to_string(), Ordering::Equal => { format!("{change}") } - Ordering::Greater => Paint::red(format!("{change}")).to_string(), + Ordering::Greater => format!("{change}").red().to_string(), } } diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 4a120adc7..72835f097 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -408,7 +408,7 @@ impl TestArgs { // Print suite header. println!(); for warning in suite_result.warnings.iter() { - eprintln!("{} {warning}", Paint::yellow("Warning:").bold()); + eprintln!("{} {warning}", "Warning:".yellow().bold()); } if !tests.is_empty() { let len = tests.len(); diff --git a/crates/forge/src/gas_report.rs b/crates/forge/src/gas_report.rs index 2bdd6d4da..058af0587 100644 --- a/crates/forge/src/gas_report.rs +++ b/crates/forge/src/gas_report.rs @@ -12,6 +12,7 @@ use std::{ collections::{BTreeMap, HashSet}, fmt::Display, }; +use yansi::Paint; /// Represents the gas report for a set of contracts. #[derive(Clone, Debug, Default, Serialize, Deserialize)] @@ -51,7 +52,7 @@ impl GasReport { // indicating the "double listing". eprintln!( "{}: {} is listed in both 'gas_reports' and 'gas_reports_ignore'.", - yansi::Paint::yellow("warning").bold(), + "warning".yellow().bold(), contract_name ); } diff --git a/crates/forge/src/result.rs b/crates/forge/src/result.rs index 7cef52a9e..04f397c48 100644 --- a/crates/forge/src/result.rs +++ b/crates/forge/src/result.rs @@ -141,9 +141,9 @@ impl TestOutcome { suites, wall_clock_time, self.total_time(), - Paint::green(total_passed), - Paint::red(total_failed), - Paint::yellow(total_skipped), + total_passed.green(), + total_failed.red(), + total_skipped.yellow(), total_tests ) } @@ -179,8 +179,8 @@ impl TestOutcome { let successes = outcome.passed(); shell::println(format!( "Encountered a total of {} failing tests, {} tests succeeded", - Paint::red(failures.to_string()), - Paint::green(successes.to_string()) + failures.to_string().red(), + successes.to_string().green() ))?; // TODO: Avoid process::exit @@ -271,13 +271,13 @@ impl SuiteResult { /// Returns the summary of a single test suite. pub fn summary(&self) -> String { let failed = self.failed(); - let result = if failed == 0 { Paint::green("ok") } else { Paint::red("FAILED") }; + let result = if failed == 0 { "ok".green() } else { "FAILED".red() }; format!( "Suite result: {}. {} passed; {} failed; {} skipped; finished in {:.2?} ({:.2?} CPU time)", result, - Paint::green(self.passed()), - Paint::red(failed), - Paint::yellow(self.skipped()), + self.passed().green(), + failed.red(), + self.skipped().yellow(), self.duration, self.total_time(), ) @@ -397,8 +397,8 @@ pub struct TestResult { impl fmt::Display for TestResult { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.status { - TestStatus::Success => Paint::green("[PASS]").fmt(f), - TestStatus::Skipped => Paint::yellow("[SKIP]").fmt(f), + TestStatus::Success => "[PASS]".green().fmt(f), + TestStatus::Skipped => "[SKIP]".yellow().fmt(f), TestStatus::Failure => { let mut s = String::from("[FAIL. Reason: "); @@ -421,7 +421,7 @@ impl fmt::Display for TestResult { s.push(']'); } - Paint::red(s).fmt(f) + s.red().fmt(f) } } } diff --git a/crates/script/Cargo.toml b/crates/script/Cargo.toml index e62e054bf..4c14d26b4 100644 --- a/crates/script/Cargo.toml +++ b/crates/script/Cargo.toml @@ -35,7 +35,7 @@ async-recursion = "1.0.5" itertools.workspace = true parking_lot = "0.12" -yansi = "0.5" +yansi.workspace = true revm-inspectors.workspace = true alloy-rpc-types.workspace = true alloy-json-abi.workspace = true diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index 6dc6a9617..66d44da0e 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -267,7 +267,7 @@ For more information, please see https://eips.ethereum.org/EIPS/eip-3855", .map(|(_, chain)| *chain as u64) .format(", ") ); - shell::println(Paint::yellow(msg))?; + shell::println(msg.yellow())?; } Ok(()) } @@ -306,9 +306,7 @@ impl ExecutedState { if rpc_data.is_multi_chain() { shell::eprintln(format!( "{}", - Paint::yellow( - "Multi chain deployment is still under development. Use with caution." - ) + "Multi chain deployment is still under development. Use with caution.".yellow() ))?; if !self.build_data.libraries.is_empty() { eyre::bail!( @@ -453,7 +451,7 @@ impl PreSimulationState { } if result.success { - shell::println(format!("{}", Paint::green("Script ran successfully.")))?; + shell::println(format!("{}", "Script ran successfully.".green()))?; } if self.script_config.evm_opts.fork_url.is_none() { diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 0f9d8ad47..bbdda84ae 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -419,9 +419,9 @@ impl ScriptArgs { prompt_user = self.broadcast; shell::println(format!( "{}", - Paint::red(format!( + format!( "`{name}` is above the contract size limit ({deployment_size} > {max_size})." - )) + ).red() ))?; } } diff --git a/crates/script/src/runner.rs b/crates/script/src/runner.rs index 23c25d133..ff77b8e61 100644 --- a/crates/script/src/runner.rs +++ b/crates/script/src/runner.rs @@ -200,7 +200,7 @@ impl ScriptRunner { Ok(DeployResult { address, raw }) => (address, raw), Err(EvmError::Execution(err)) => { let ExecutionErr { raw, reason } = *err; - println!("{}", Paint::red(format!("\nFailed with `{reason}`:\n"))); + println!("{}", format!("\nFailed with `{reason}`:\n").red()); (Address::ZERO, raw) } Err(e) => eyre::bail!("Failed deploying contract: {e:?}"), diff --git a/crates/script/src/sequence.rs b/crates/script/src/sequence.rs index 2d39c1292..f98326564 100644 --- a/crates/script/src/sequence.rs +++ b/crates/script/src/sequence.rs @@ -324,11 +324,12 @@ impl ScriptSequence { if !unverifiable_contracts.is_empty() { println!( "\n{}", - Paint::yellow(format!( + format!( "We haven't found any matching bytecode for the following contracts: {:?}.\n\n{}", unverifiable_contracts, "This may occur when resuming a verification, but the underlying source code or compiler version has changed." - )) + ) + .yellow() .bold(), ); diff --git a/crates/verify/Cargo.toml b/crates/verify/Cargo.toml index d365388d0..5fa8c761d 100644 --- a/crates/verify/Cargo.toml +++ b/crates/verify/Cargo.toml @@ -35,7 +35,7 @@ futures = "0.3" semver = "1" regex = { version = "1", default-features = false } once_cell = "1" -yansi = "0.5" +yansi.workspace = true [dev-dependencies] tokio = { version = "1", features = ["macros"] } diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index b89fc91f1..6f8d44ff9 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -117,8 +117,8 @@ impl VerifyBytecodeArgs { if !self.json { println!( "Verifying bytecode for contract {} at address {}", - Paint::green(self.contract.name.clone()), - Paint::green(self.address.to_string()) + self.contract.name.clone().green(), + self.address.green() ); } @@ -174,7 +174,7 @@ impl VerifyBytecodeArgs { if provided_constructor_args != constructor_args.to_string() && !self.json { println!( "{}", - Paint::red("The provided constructor args do not match the constructor args from etherscan. This will result in a mismatch - Using the args from etherscan").bold(), + "The provided constructor args do not match the constructor args from etherscan. This will result in a mismatch - Using the args from etherscan".red().bold(), ); } @@ -456,8 +456,8 @@ impl VerifyBytecodeArgs { if !self.json { println!( "{} with status {}", - Paint::green(format!("{:?} code matched", bytecode_type)).bold(), - Paint::green(res.1.unwrap()).bold() + format!("{:?} code matched", bytecode_type).green().bold(), + res.1.unwrap().green().bold() ); } else { let json_res = JsonResult { @@ -471,15 +471,16 @@ impl VerifyBytecodeArgs { } else if !res.0 && !self.json { println!( "{}", - Paint::red(format!( + format!( "{:?} code did not match - this may be due to varying compiler settings", bytecode_type - )) + ) + .red() .bold() ); let mismatches = find_mismatch_in_settings(etherscan_config, config); for mismatch in mismatches { - println!("{}", Paint::red(mismatch).bold()); + println!("{}", mismatch.red().bold()); } } else if !res.0 && self.json { let json_res = JsonResult { From 9f06a4af1dfdf648a9cee1e7540e2af5a83bd8df Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 12:50:23 +0200 Subject: [PATCH 208/430] chore(deps): weekly `cargo update` (#7746) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/alloy-rs/alloy` Updating git repository `https://github.com/paradigmxyz/evm-inspectors` Locking 23 packages to latest compatible versions Updating async-channel v2.2.0 -> v2.2.1 Updating aurora-engine-modexp v1.0.0 -> v1.1.0 Updating aws-smithy-http v0.60.7 -> v0.60.8 Updating aws-smithy-runtime v1.3.0 -> v1.3.1 Updating aws-smithy-xml v0.60.7 -> v0.60.8 Updating cc v1.0.94 -> v1.0.95 Updating chrono v0.4.37 -> v0.4.38 Updating figment v0.10.16 -> v0.10.18 Updating foundry-compilers v0.3.17 -> v0.3.18 Updating hyper v1.2.0 -> v1.3.1 Adding lzma-rs v0.3.0 Updating prettyplease v0.2.17 -> v0.2.19 Updating proc-macro2 v1.0.79 -> v1.0.81 Updating reqwest v0.12.3 -> v0.12.4 Updating serde v1.0.197 -> v1.0.198 Updating serde_derive v1.0.197 -> v1.0.198 Updating serde_json v1.0.115 -> v1.0.116 Updating syn v2.0.58 -> v2.0.60 Updating thiserror v1.0.58 -> v1.0.59 Updating thiserror-impl v1.0.58 -> v1.0.59 Updating toml_edit v0.22.9 -> v0.22.12 Adding zip v1.1.0 Updating zip_next v1.0.1 -> v1.1.1 note: pass `--verbose` to see 169 unchanged dependencies behind latest * tempfile --------- Co-authored-by: mattsse Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- Cargo.lock | 352 ++++++++++++++--------------------- Cargo.toml | 1 + crates/anvil/Cargo.toml | 2 +- crates/cast/Cargo.toml | 8 +- crates/chisel/Cargo.toml | 1 - crates/cli/Cargo.toml | 2 +- crates/cli/src/utils/mod.rs | 3 +- crates/common/Cargo.toml | 2 +- crates/config/Cargo.toml | 2 +- crates/evm/traces/Cargo.toml | 2 +- crates/forge/Cargo.toml | 2 +- crates/script/Cargo.toml | 2 +- crates/verify/Cargo.toml | 2 +- 13 files changed, 155 insertions(+), 226 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1ba16aacc..114b59a2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -245,7 +245,7 @@ dependencies = [ "futures", "futures-utils-wasm", "lru", - "reqwest 0.12.3", + "reqwest 0.12.4", "serde_json", "tokio", "tracing", @@ -289,7 +289,7 @@ checksum = "1a047897373be4bbb0224c1afdabca92648dc57a9c9ef6e7b0be3aff7a859c83" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -306,7 +306,7 @@ dependencies = [ "alloy-transport-ws", "futures", "pin-project", - "reqwest 0.12.3", + "reqwest 0.12.4", "serde", "serde_json", "tokio", @@ -454,7 +454,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", "syn-solidity", "tiny-keccak", ] @@ -472,7 +472,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.58", + "syn 2.0.60", "syn-solidity", ] @@ -523,7 +523,7 @@ source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be9 dependencies = [ "alloy-json-rpc", "alloy-transport", - "reqwest 0.12.3", + "reqwest 0.12.4", "serde_json", "tower", "url", @@ -708,7 +708,7 @@ dependencies = [ "foundry-evm", "foundry-test-utils", "futures", - "hyper 1.2.0", + "hyper 1.3.1", "itertools 0.12.1", "k256", "parking_lot", @@ -948,9 +948,9 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" +checksum = "136d4d23bcc79e27423727b36823d86233aad06dfea531837b038394d11e9928" dependencies = [ "concurrent-queue", "event-listener 5.3.0", @@ -987,7 +987,7 @@ checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -1009,7 +1009,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -1026,7 +1026,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -1063,9 +1063,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "aurora-engine-modexp" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfacad86e9e138fca0670949eb8ed4ffdf73a55bded8887efe0863cd1a3a6f70" +checksum = "0aef7712851e524f35fbbb74fa6599c5cd8692056a1c36f9ca0d2001b670e7e5" dependencies = [ "hex", "num", @@ -1079,7 +1079,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -1279,9 +1279,9 @@ dependencies = [ [[package]] name = "aws-smithy-http" -version = "0.60.7" +version = "0.60.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f10fa66956f01540051b0aa7ad54574640f748f9839e843442d99b970d3aff9" +checksum = "4a7de001a1b9a25601016d8057ea16e31a45fdca3751304c8edf4ad72e706c08" dependencies = [ "aws-smithy-runtime-api", "aws-smithy-types", @@ -1318,9 +1318,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de34bcfa1fb3c82a80e252a753db34a6658e07f23d3a5b3fc96919518fa7a3f5" +checksum = "44e7945379821074549168917e89e60630647e186a69243248f08c6d168b975a" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -1384,9 +1384,9 @@ dependencies = [ [[package]] name = "aws-smithy-xml" -version = "0.60.7" +version = "0.60.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "872c68cf019c0e4afc5de7753c4f7288ce4b71663212771bf5e4542eb9346ca9" +checksum = "d123fbc2a4adc3c301652ba8e149bf4bc1d1725affb9784eb20c953ace06bf55" dependencies = [ "xmlparser", ] @@ -1420,7 +1420,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.0", "http-body-util", - "hyper 1.2.0", + "hyper 1.3.1", "hyper-util", "itoa", "matchit", @@ -1841,12 +1841,13 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7" +checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" dependencies = [ "jobserver", "libc", + "once_cell", ] [[package]] @@ -1882,7 +1883,7 @@ dependencies = [ "foundry-evm", "once_cell", "regex", - "reqwest 0.12.3", + "reqwest 0.12.4", "revm", "rustyline", "semver 1.0.22", @@ -1902,9 +1903,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.37" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", @@ -2002,7 +2003,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -2035,15 +2036,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "cmake" -version = "0.1.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" -dependencies = [ - "cc", -] - [[package]] name = "coins-bip32" version = "0.8.7" @@ -2236,12 +2228,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" -[[package]] -name = "constant_time_eq" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" - [[package]] name = "convert_case" version = "0.4.0" @@ -2471,7 +2457,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -2482,7 +2468,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -2504,12 +2490,6 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" -[[package]] -name = "deflate64" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83ace6c86376be0b6cdcf3fb41882e81d94b31587573d1cfa9d01cd06bba210d" - [[package]] name = "der" version = "0.7.9" @@ -2549,7 +2529,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -2570,7 +2550,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -2580,7 +2560,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" dependencies = [ "derive_builder_core", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -2830,7 +2810,7 @@ checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -3042,7 +3022,7 @@ dependencies = [ "reqwest 0.11.27", "serde", "serde_json", - "syn 2.0.58", + "syn 2.0.60", "toml 0.8.12", "walkdir", ] @@ -3060,7 +3040,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -3086,7 +3066,7 @@ dependencies = [ "serde", "serde_json", "strum", - "syn 2.0.58", + "syn 2.0.60", "tempfile", "thiserror", "tiny-keccak", @@ -3329,7 +3309,7 @@ dependencies = [ "bytes", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -3376,9 +3356,9 @@ dependencies = [ [[package]] name = "figment" -version = "0.10.16" +version = "0.10.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdefe49ed1057d124dc81a0681c30dd07de56ad96e32adc7b64e8f28eaab31c4" +checksum = "d032832d74006f99547004d49410a4b4218e4c33382d56ca3ff89df74f86b953" dependencies = [ "atomic", "parking_lot", @@ -3428,7 +3408,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", - "libz-ng-sys", "miniz_oxide", ] @@ -3500,7 +3479,7 @@ dependencies = [ "futures", "globset", "humantime-serde", - "hyper 1.2.0", + "hyper 1.3.1", "indicatif", "itertools 0.12.1", "mockall", @@ -3513,7 +3492,7 @@ dependencies = [ "proptest", "rayon", "regex", - "reqwest 0.12.3", + "reqwest 0.12.4", "revm-inspectors", "rustc-hash", "semver 1.0.22", @@ -3522,13 +3501,13 @@ dependencies = [ "similar", "solang-parser", "strum", - "svm-rs 0.5.1", + "svm-rs 0.5.2", "tempfile", "thiserror", "tikv-jemallocator", "tokio", "toml 0.8.12", - "toml_edit 0.22.9", + "toml_edit 0.22.12", "tower-http", "tracing", "tracing-subscriber", @@ -3643,7 +3622,7 @@ dependencies = [ "futures", "once_cell", "regex", - "reqwest 0.12.3", + "reqwest 0.12.4", "revm-primitives", "semver 1.0.22", "serde", @@ -3673,7 +3652,7 @@ dependencies = [ "alloy-json-abi", "alloy-primitives", "foundry-compilers", - "reqwest 0.12.3", + "reqwest 0.12.4", "semver 1.0.22", "serde", "serde_json", @@ -3808,7 +3787,7 @@ dependencies = [ "once_cell", "pretty_assertions", "reqwest 0.11.27", - "reqwest 0.12.3", + "reqwest 0.12.4", "rustc-hash", "semver 1.0.22", "serde", @@ -3850,7 +3829,7 @@ dependencies = [ "serde_json", "sha2", "solang-parser", - "svm-rs 0.5.1", + "svm-rs 0.5.2", "svm-rs-builds", "tempfile", "thiserror", @@ -3879,7 +3858,7 @@ dependencies = [ "path-slash", "pretty_assertions", "regex", - "reqwest 0.12.3", + "reqwest 0.12.4", "revm-primitives", "semver 1.0.22", "serde", @@ -3889,7 +3868,7 @@ dependencies = [ "tempfile", "thiserror", "toml 0.8.12", - "toml_edit 0.22.9", + "toml_edit 0.22.12", "tracing", "walkdir", ] @@ -4062,7 +4041,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -4251,7 +4230,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -4866,9 +4845,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186548d73ac615b32a73aafe38fb4f56c0d340e110e5a200bcadbaf2e199263a" +checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" dependencies = [ "bytes", "futures-channel", @@ -4908,7 +4887,7 @@ checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" dependencies = [ "futures-util", "http 1.1.0", - "hyper 1.2.0", + "hyper 1.3.1", "hyper-util", "rustls 0.22.4", "rustls-pki-types", @@ -4925,7 +4904,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.2.0", + "hyper 1.3.1", "hyper-util", "native-tls", "tokio", @@ -4944,7 +4923,7 @@ dependencies = [ "futures-util", "http 1.1.0", "http-body 1.0.0", - "hyper 1.2.0", + "hyper 1.3.1", "pin-project-lite", "socket2", "tokio", @@ -5389,16 +5368,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "libz-ng-sys" -version = "1.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6409efc61b12687963e602df8ecf70e8ddacf95bc6576bcf16e3ac6328083c5" -dependencies = [ - "cmake", - "libc", -] - [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -5562,7 +5531,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -5632,7 +5601,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -5866,7 +5835,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -5977,7 +5946,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -6168,7 +6137,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -6226,7 +6195,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -6329,7 +6298,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -6367,7 +6336,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -6499,12 +6468,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" +checksum = "5ac2cf0f2e4f42b49f5ffd07dae8d746508ef7526c13940e5f524012ae6c6550" dependencies = [ "proc-macro2", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -6584,9 +6553,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.79" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] @@ -6599,7 +6568,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", "version_check", "yansi 1.0.1", ] @@ -6949,9 +6918,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e6cc1e89e689536eb5aeede61520e874df5a4707df811cd5da4aa5fbb2aae19" +checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" dependencies = [ "base64 0.22.0", "bytes", @@ -6961,7 +6930,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.0", "http-body-util", - "hyper 1.2.0", + "hyper 1.3.1", "hyper-rustls 0.26.0", "hyper-tls", "hyper-util", @@ -7242,9 +7211,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.32" +version = "0.38.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +checksum = "e3cc72858054fcff6d7dea32df2aeaee6a7c24227366d7ea429aada2f26b16ad" dependencies = [ "bitflags 2.5.0", "errno", @@ -7439,6 +7408,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "scc" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec96560eea317a9cc4e0bb1f6a2c93c09a19b8c4fc5cb3fcc0ec1c094cd783e2" +dependencies = [ + "sdd", +] + [[package]] name = "schannel" version = "0.1.23" @@ -7500,6 +7478,12 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "sdd" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b84345e4c9bd703274a082fb80caaa99b7612be48dfaa1dd9266577ec412309d" + [[package]] name = "sec1" version = "0.7.3" @@ -7597,22 +7581,22 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.197" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -7628,9 +7612,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.115" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" dependencies = [ "indexmap", "itoa", @@ -7666,7 +7650,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -7692,27 +7676,27 @@ dependencies = [ [[package]] name = "serial_test" -version = "3.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ad9342b3aaca7cb43c45c097dd008d4907070394bd0751a0aa8817e5a018d" +checksum = "adb86f9315df5df6a70eae0cc22395a44e544a0d8897586820770a35ede74449" dependencies = [ - "dashmap", "futures", - "lazy_static", "log", + "once_cell", "parking_lot", + "scc", "serial_test_derive", ] [[package]] name = "serial_test_derive" -version = "3.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b93fb4adc70021ac1b47f7d45e8cc4169baaa7ea58483bc5b721d19a26202212" +checksum = "a9bb72430492e9549b0c4596725c0f82729bff861c45aa8099c0a8e67fc3b721" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -7807,9 +7791,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -7824,12 +7808,6 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - [[package]] name = "similar" version = "2.5.0" @@ -7931,7 +7909,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ff9eaf853dec4c8802325d8b6d3dffa86cc707fd7a1a4cdbf416e13b061787a" dependencies = [ "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -8003,7 +7981,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -8042,40 +8020,40 @@ dependencies = [ "sha2", "thiserror", "url", - "zip", + "zip 0.6.6", ] [[package]] name = "svm-rs" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c912d2f0dfbf9d8ba683b3181c4bd6d042bac9279d5c062346c253c1eadf46e2" +checksum = "f7c7a55b859b986daa02c731cd07758d84b1db852665e45c5cfa6698c41d17cb" dependencies = [ "const-hex", "dirs 5.0.1", "fs4", "once_cell", - "reqwest 0.12.3", + "reqwest 0.12.4", "semver 1.0.22", "serde", "serde_json", "sha2", "thiserror", "url", - "zip_next", + "zip 1.1.1", ] [[package]] name = "svm-rs-builds" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc1e420b6e969cb161e3b1758ea50cbde308c82fbfcb322979eae71c5cc947a" +checksum = "f1bb4806c96207e7cde40fc238f9a1d570f3090f850a742e1e96665440615a31" dependencies = [ "build_const", "const-hex", "semver 1.0.22", "serde_json", - "svm-rs 0.5.1", + "svm-rs 0.5.2", ] [[package]] @@ -8091,9 +8069,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.58" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", @@ -8109,7 +8087,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -8216,22 +8194,22 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.58" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.58" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -8373,7 +8351,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -8480,7 +8458,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.9", + "toml_edit 0.22.12", ] [[package]] @@ -8527,9 +8505,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.9" +version = "0.22.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" +checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" dependencies = [ "indexmap", "serde", @@ -8617,7 +8595,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -8738,12 +8716,6 @@ 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" @@ -8992,7 +8964,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", "wasm-bindgen-shared", ] @@ -9026,7 +8998,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -9480,7 +9452,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -9500,7 +9472,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -9512,7 +9484,7 @@ dependencies = [ "aes", "byteorder", "bzip2", - "constant_time_eq 0.1.5", + "constant_time_eq", "crc32fast", "crossbeam-utils", "flate2", @@ -9520,42 +9492,20 @@ dependencies = [ "pbkdf2 0.11.0", "sha1", "time", - "zstd 0.11.2+zstd.1.5.2", + "zstd", ] [[package]] -name = "zip_next" -version = "1.0.1" +name = "zip" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658758d431446f97e25f129b30c97646db8799b30f00aaf10379b4fa343d4ded" +checksum = "f2655979068a1f8fa91cb9e8e5b9d3ee54d18e0ddc358f2f4a395afc0929a84b" dependencies = [ - "aes", "arbitrary", "byteorder", - "bzip2", - "constant_time_eq 0.3.0", "crc32fast", "crossbeam-utils", - "deflate64", "flate2", - "hmac", - "pbkdf2 0.12.2", - "sha1", - "time", - "zopfli", - "zstd 0.13.1", -] - -[[package]] -name = "zopfli" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c1f48f3508a3a3f2faee01629564400bc12260f6214a056d06a3aaaa6ef0736" -dependencies = [ - "crc32fast", - "log", - "simd-adler32", - "typed-arena", ] [[package]] @@ -9564,16 +9514,7 @@ version = "0.11.2+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" dependencies = [ - "zstd-safe 5.0.2+zstd.1.5.2", -] - -[[package]] -name = "zstd" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a" -dependencies = [ - "zstd-safe 7.1.0", + "zstd-safe", ] [[package]] @@ -9586,15 +9527,6 @@ dependencies = [ "zstd-sys", ] -[[package]] -name = "zstd-safe" -version = "7.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" -dependencies = [ - "zstd-sys", -] - [[package]] name = "zstd-sys" version = "2.0.10+zstd.1.5.6" diff --git a/Cargo.toml b/Cargo.toml index d14288a38..4ef7befcb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -215,6 +215,7 @@ indexmap = "2.2" tikv-jemallocator = "0.5.4" num-format = "0.4.4" yansi = { version = "1.0", features = ["detect-tty", "detect-env"] } +tempfile = "3.10" axum = "0.7" hyper = "1.0" diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index b7619eb5a..b067bfd04 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -71,7 +71,7 @@ serde_json.workspace = true serde.workspace = true thiserror = "1" yansi.workspace = true -tempfile = "3" +tempfile.workspace = true itertools.workspace = true rand = "0.8" eyre.workspace = true diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index e96737ce5..11b304212 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -15,11 +15,7 @@ name = "cast" path = "bin/main.rs" [build-dependencies] -vergen = { workspace = true, default-features = false, features = [ - "build", - "git", - "gitcl", -] } +vergen = { workspace = true, default-features = false, features = ["build", "git", "gitcl"] } [dependencies] # lib @@ -75,7 +71,7 @@ itertools.workspace = true regex = { version = "1", default-features = false } rpassword = "7" semver = "1" -tempfile = "3" +tempfile.workspace = true tokio = { version = "1", features = ["macros", "signal"] } tracing.workspace = true yansi.workspace = true diff --git a/crates/chisel/Cargo.toml b/crates/chisel/Cargo.toml index 05ad5aefc..6ae62b970 100644 --- a/crates/chisel/Cargo.toml +++ b/crates/chisel/Cargo.toml @@ -55,7 +55,6 @@ tikv-jemallocator = { workspace = true, optional = true } [dev-dependencies] criterion = { version = "0.5", features = ["async_tokio"] } -once_cell = "1" serial_test = "3" tracing-subscriber.workspace = true diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index a314ebb60..dd247ac48 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -45,7 +45,7 @@ hex.workspace = true futures = "0.3" [dev-dependencies] -tempfile = "3.7" +tempfile.workspace = true [features] default = ["rustls"] diff --git a/crates/cli/src/utils/mod.rs b/crates/cli/src/utils/mod.rs index e5950078e..cd0716175 100644 --- a/crates/cli/src/utils/mod.rs +++ b/crates/cli/src/utils/mod.rs @@ -209,7 +209,8 @@ pub fn load_dotenv() { /// Sets the default [`yansi`] color output condition. pub fn enable_paint() { - yansi::whenever(yansi::Condition::TTY_AND_COLOR); + let enable = yansi::Condition::os_support() && yansi::Condition::tty_and_color_live(); + yansi::whenever(yansi::Condition::cached(enable)); } /// Useful extensions to [`std::process::Command`]. diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 8bccf5054..29d55fe5f 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -54,7 +54,7 @@ reqwest.workspace = true semver = "1" serde_json.workspace = true serde.workspace = true -tempfile = "3" +tempfile.workspace = true thiserror = "1" tokio = "1" tracing.workspace = true diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml index 1b639ee55..7a65678d4 100644 --- a/crates/config/Cargo.toml +++ b/crates/config/Cargo.toml @@ -46,7 +46,7 @@ path-slash = "0.2.1" [dev-dependencies] pretty_assertions.workspace = true figment = { version = "0.10", features = ["test"] } -tempfile = "3" +tempfile.workspace = true [features] default = ["rustls"] diff --git a/crates/evm/traces/Cargo.toml b/crates/evm/traces/Cargo.toml index fd873609d..a4bcb0a0b 100644 --- a/crates/evm/traces/Cargo.toml +++ b/crates/evm/traces/Cargo.toml @@ -34,4 +34,4 @@ tracing = "0.1" yansi.workspace = true [dev-dependencies] -tempfile = "3" +tempfile.workspace = true diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 3f724481c..05a7f75f5 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -106,7 +106,7 @@ paste = "1.0" path-slash = "0.2" pretty_assertions.workspace = true svm = { package = "svm-rs", version = "0.5", default-features = false, features = ["rustls"] } -tempfile = "3" +tempfile.workspace = true tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] } ethers-core.workspace = true diff --git a/crates/script/Cargo.toml b/crates/script/Cargo.toml index 4c14d26b4..3d41ac795 100644 --- a/crates/script/Cargo.toml +++ b/crates/script/Cargo.toml @@ -52,4 +52,4 @@ alloy-eips.workspace = true alloy-transport.workspace = true [dev-dependencies] -tempfile = "3" \ No newline at end of file +tempfile.workspace = true diff --git a/crates/verify/Cargo.toml b/crates/verify/Cargo.toml index 5fa8c761d..21eb65942 100644 --- a/crates/verify/Cargo.toml +++ b/crates/verify/Cargo.toml @@ -40,4 +40,4 @@ yansi.workspace = true [dev-dependencies] tokio = { version = "1", features = ["macros"] } foundry-test-utils.workspace = true -tempfile = "3" +tempfile.workspace = true From e971af109b8240b8de694b23bf6d1f96ed10ae71 Mon Sep 17 00:00:00 2001 From: sealer3 <125761775+sealer3@users.noreply.github.com> Date: Mon, 22 Apr 2024 12:20:25 +0000 Subject: [PATCH 209/430] fix(anvil): Return transaction hash in ots_getTransactionBySenderAndNonce (#7741) Return transaction hash in ots_getTransactionBySenderAndNonce --- crates/anvil/src/eth/otterscan/api.rs | 8 +++----- crates/anvil/tests/it/otterscan.rs | 4 ++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/crates/anvil/src/eth/otterscan/api.rs b/crates/anvil/src/eth/otterscan/api.rs index 636140f13..400f99193 100644 --- a/crates/anvil/src/eth/otterscan/api.rs +++ b/crates/anvil/src/eth/otterscan/api.rs @@ -8,9 +8,7 @@ use crate::eth::{ EthApi, }; use alloy_primitives::{Address, Bytes, B256, U256}; -use alloy_rpc_types::{ - Block, BlockId, BlockNumberOrTag as BlockNumber, Transaction, WithOtherFields, -}; +use alloy_rpc_types::{Block, BlockId, BlockNumberOrTag as BlockNumber}; use alloy_rpc_types_trace::parity::{ Action, CallAction, CreateAction, CreateOutput, RewardAction, TraceOutput, }; @@ -240,7 +238,7 @@ impl EthApi { &self, address: Address, nonce: U256, - ) -> Result>> { + ) -> Result> { node_info!("ots_getTransactionBySenderAndNonce"); let from = self.get_fork().map(|f| f.block_number() + 1).unwrap_or_default(); @@ -250,7 +248,7 @@ impl EthApi { 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)) + return Ok(Some(tx.hash)) } } } diff --git a/crates/anvil/tests/it/otterscan.rs b/crates/anvil/tests/it/otterscan.rs index 4c3442640..3b27a0f6c 100644 --- a/crates/anvil/tests/it/otterscan.rs +++ b/crates/anvil/tests/it/otterscan.rs @@ -594,8 +594,8 @@ async fn can_call_ots_get_transaction_by_sender_and_nonce() { .await .unwrap(); - assert_eq!(result1.unwrap().hash, receipt1.transaction_hash.to_alloy()); - assert_eq!(result2.unwrap().hash, receipt2.transaction_hash.to_alloy()); + assert_eq!(result1.unwrap(), receipt1.transaction_hash.to_alloy()); + assert_eq!(result2.unwrap(), receipt2.transaction_hash.to_alloy()); } #[tokio::test(flavor = "multi_thread")] From 008922d5165c764859bc540d7298045eebf5bc60 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Mon, 22 Apr 2024 22:33:39 +0300 Subject: [PATCH 210/430] feat(fuzz): ability to declare fuzz test fixtures (#7428) * fix(fuzz): deduplicate fuzz inputs * Fix tests, collect fixtures in test setup, arc fixtures * Cleanup * Use fixture_ prefix * Update tests to reflect that random values are used if no fixtures * Review changes * Group fuzz_calldata and fuzz_calldata_from_state in calldata mod * Review changes: remove unnecessary clones, nicer code to collect fixtures * Add support for bytes and string fixtures, fixture strategy macro. Solidity test * Remove unnecessary clone * Use inline config * More robust invariant assume test - previously rejecting when param was 0 (vm.assume(param != 0)) that is param should have been fuzzed twice with 0 in a run - with fuzz input deduplication is now harder to occur, changed rejected if param is not 0 (vm.assume(param != 0)) and narrow down to one run and just 10 depth * Fixtures as storage arrays, remove inline config * Simplify code * Support fixed size arrays fixtures * Update comment * Use DynSolValue::type_strategy for address and fixed bytes fuzzed params * Add prefix to mark a storage array or a function as fixture * Fix test * Simplify code / fixture strategy macro, panic if configured fixture not of param type * Consistent panic with fixture strategy if uint / int fixture of different type. Keep level of randomness in fixture strategy, at par with uint / int strategies. * Review changes: don't panic when invalid fixture, use prop_filter_map for fixture strategy and raise error --- crates/common/src/traits.rs | 15 +++ crates/config/src/fuzz.rs | 5 - crates/config/src/inline/conf_parser.rs | 4 +- crates/config/src/inline/mod.rs | 2 +- crates/evm/evm/src/executors/fuzz/mod.rs | 7 +- crates/evm/evm/src/executors/invariant/mod.rs | 31 ++--- crates/evm/fuzz/src/lib.rs | 41 +++++- crates/evm/fuzz/src/strategies/calldata.rs | 126 +++++++++--------- crates/evm/fuzz/src/strategies/int.rs | 33 ++++- crates/evm/fuzz/src/strategies/invariants.rs | 23 ++-- crates/evm/fuzz/src/strategies/mod.rs | 36 ++++- crates/evm/fuzz/src/strategies/param.rs | 124 +++++++++++------ crates/evm/fuzz/src/strategies/state.rs | 29 +--- crates/evm/fuzz/src/strategies/uint.rs | 45 +++++-- crates/forge/bin/cmd/test/mod.rs | 2 +- crates/forge/src/result.rs | 8 +- crates/forge/src/runner.rs | 95 +++++++++++-- crates/forge/tests/it/invariant.rs | 61 ++++++--- crates/forge/tests/it/test_helpers.rs | 2 - testdata/default/fuzz/FuzzInt.t.sol | 17 +-- testdata/default/fuzz/FuzzUint.t.sol | 11 +- .../invariant/common/InvariantAssume.t.sol | 2 +- .../common/InvariantCalldataDictionary.t.sol | 11 ++ .../invariant/common/InvariantFixtures.t.sol | 77 +++++++++++ 24 files changed, 568 insertions(+), 239 deletions(-) create mode 100644 testdata/default/fuzz/invariant/common/InvariantFixtures.t.sol diff --git a/crates/common/src/traits.rs b/crates/common/src/traits.rs index 4232fb946..8ed1edbec 100644 --- a/crates/common/src/traits.rs +++ b/crates/common/src/traits.rs @@ -33,6 +33,9 @@ pub trait TestFunctionExt { /// Returns whether this function is a `setUp` function. fn is_setup(&self) -> bool; + + /// Returns whether this function is a fixture function. + fn is_fixture(&self) -> bool; } impl TestFunctionExt for Function { @@ -56,6 +59,10 @@ impl TestFunctionExt for Function { fn is_setup(&self) -> bool { self.name.is_setup() } + + fn is_fixture(&self) -> bool { + self.name.is_fixture() + } } impl TestFunctionExt for String { @@ -78,6 +85,10 @@ impl TestFunctionExt for String { fn is_setup(&self) -> bool { self.as_str().is_setup() } + + fn is_fixture(&self) -> bool { + self.as_str().is_fixture() + } } impl TestFunctionExt for str { @@ -100,6 +111,10 @@ impl TestFunctionExt for str { fn is_setup(&self) -> bool { self.eq_ignore_ascii_case("setup") } + + fn is_fixture(&self) -> bool { + self.starts_with("fixture") + } } /// An extension trait for `std::error::Error` for ABI encoding. diff --git a/crates/config/src/fuzz.rs b/crates/config/src/fuzz.rs index 3b0e13bcd..7049a401a 100644 --- a/crates/config/src/fuzz.rs +++ b/crates/config/src/fuzz.rs @@ -111,10 +111,6 @@ pub struct FuzzDictionaryConfig { /// Once the fuzzer exceeds this limit, it will start evicting random entries #[serde(deserialize_with = "crate::deserialize_usize_or_max")] pub max_fuzz_dictionary_values: usize, - /// How many random addresses to use and to recycle when fuzzing calldata. - /// If not specified then `max_fuzz_dictionary_addresses` value applies. - #[serde(deserialize_with = "crate::deserialize_usize_or_max")] - pub max_calldata_fuzz_dictionary_addresses: usize, } impl Default for FuzzDictionaryConfig { @@ -127,7 +123,6 @@ impl Default for FuzzDictionaryConfig { max_fuzz_dictionary_addresses: (300 * 1024 * 1024) / 20, // limit this to 200MB max_fuzz_dictionary_values: (200 * 1024 * 1024) / 32, - max_calldata_fuzz_dictionary_addresses: 0, } } } diff --git a/crates/config/src/inline/conf_parser.rs b/crates/config/src/inline/conf_parser.rs index acad057ae..1f6fca6c7 100644 --- a/crates/config/src/inline/conf_parser.rs +++ b/crates/config/src/inline/conf_parser.rs @@ -149,7 +149,7 @@ mod tests { function: Default::default(), line: Default::default(), docs: r" - forge-config: ciii.invariant.depth = 1 + forge-config: ciii.invariant.depth = 1 forge-config: default.invariant.depth = 1 " .into(), @@ -167,7 +167,7 @@ mod tests { function: Default::default(), line: Default::default(), docs: r" - forge-config: ci.invariant.depth = 1 + forge-config: ci.invariant.depth = 1 forge-config: default.invariant.depth = 1 " .into(), diff --git a/crates/config/src/inline/mod.rs b/crates/config/src/inline/mod.rs index 9989d5b76..f2222901f 100644 --- a/crates/config/src/inline/mod.rs +++ b/crates/config/src/inline/mod.rs @@ -37,7 +37,7 @@ impl InlineConfig { } /// Inserts an inline configuration, for a test function. - /// Configuration is identified by the pair "contract", "function". + /// Configuration is identified by the pair "contract", "function". pub fn insert(&mut self, contract_id: C, fn_name: F, config: T) where C: Into, diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index ae63d2386..8e3a85bdd 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -11,7 +11,7 @@ use foundry_evm_core::{ use foundry_evm_coverage::HitMaps; use foundry_evm_fuzz::{ strategies::{build_initial_state, fuzz_calldata, fuzz_calldata_from_state, EvmFuzzState}, - BaseCounterExample, CounterExample, FuzzCase, FuzzError, FuzzTestResult, + BaseCounterExample, CounterExample, FuzzCase, FuzzError, FuzzFixtures, FuzzTestResult, }; use foundry_evm_traces::CallTraceArena; use proptest::test_runner::{TestCaseError, TestError, TestRunner}; @@ -55,6 +55,7 @@ impl FuzzedExecutor { pub fn fuzz( &self, func: &Function, + fuzz_fixtures: &FuzzFixtures, address: Address, should_fail: bool, rd: &RevertDecoder, @@ -80,10 +81,12 @@ impl FuzzedExecutor { let state = self.build_fuzz_state(); let dictionary_weight = self.config.dictionary.dictionary_weight.min(100); + let strat = proptest::prop_oneof![ - 100 - dictionary_weight => fuzz_calldata(func.clone()), + 100 - dictionary_weight => fuzz_calldata(func.clone(), fuzz_fixtures), dictionary_weight => fuzz_calldata_from_state(func.clone(), &state), ]; + debug!(func=?func.name, should_fail, "fuzzing"); let run_result = self.runner.clone().run(&strat, |calldata| { let fuzz_res = self.single_fuzz(address, should_fail, calldata)?; diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 2a7000ec1..b5669cb7d 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -18,14 +18,14 @@ use foundry_evm_fuzz::{ }, strategies::{ build_initial_state, collect_created_contracts, invariant_strat, override_call_strat, - CalldataFuzzDictionary, EvmFuzzState, + EvmFuzzState, }, - FuzzCase, FuzzedCases, + FuzzCase, FuzzFixtures, FuzzedCases, }; use foundry_evm_traces::CallTraceArena; use parking_lot::RwLock; use proptest::{ - strategy::{BoxedStrategy, Strategy, ValueTree}, + strategy::{BoxedStrategy, Strategy}, test_runner::{TestCaseError, TestRunner}, }; use revm::{primitives::HashMap, DatabaseCommit}; @@ -88,12 +88,8 @@ sol! { } /// Alias for (Dictionary for fuzzing, initial contracts to fuzz and an InvariantStrategy). -type InvariantPreparation = ( - EvmFuzzState, - FuzzRunIdentifiedContracts, - BoxedStrategy, - CalldataFuzzDictionary, -); +type InvariantPreparation = + (EvmFuzzState, FuzzRunIdentifiedContracts, BoxedStrategy); /// Enriched results of an invariant run check. /// @@ -152,14 +148,15 @@ impl<'a> InvariantExecutor<'a> { pub fn invariant_fuzz( &mut self, invariant_contract: InvariantContract<'_>, + fuzz_fixtures: &FuzzFixtures, ) -> Result { // Throw an error to abort test run if the invariant function accepts input params if !invariant_contract.invariant_function.inputs.is_empty() { return Err(eyre!("Invariant test function should have no inputs")) } - let (fuzz_state, targeted_contracts, strat, calldata_fuzz_dictionary) = - self.prepare_fuzzing(&invariant_contract)?; + let (fuzz_state, targeted_contracts, strat) = + self.prepare_fuzzing(&invariant_contract, fuzz_fixtures)?; // Stores the consumed gas and calldata of every successful fuzz call. let fuzz_cases: RefCell> = RefCell::new(Default::default()); @@ -329,7 +326,7 @@ impl<'a> InvariantExecutor<'a> { Ok(()) }); - trace!(target: "forge::test::invariant::calldata_address_fuzz_dictionary", "{:?}", calldata_fuzz_dictionary.inner.addresses); + trace!(target: "forge::test::invariant::fuzz_fixtures", "{:?}", fuzz_fixtures); trace!(target: "forge::test::invariant::dictionary", "{:?}", fuzz_state.dictionary_read().values().iter().map(hex::encode).collect::>()); let (reverts, error) = failures.into_inner().into_inner(); @@ -350,6 +347,7 @@ impl<'a> InvariantExecutor<'a> { fn prepare_fuzzing( &mut self, invariant_contract: &InvariantContract<'_>, + fuzz_fixtures: &FuzzFixtures, ) -> eyre::Result { // Finds out the chosen deployed contracts and/or senders. self.select_contract_artifacts(invariant_contract.address)?; @@ -360,16 +358,13 @@ impl<'a> InvariantExecutor<'a> { let fuzz_state: EvmFuzzState = build_initial_state(self.executor.backend.mem_db(), self.config.dictionary); - let calldata_fuzz_config = - CalldataFuzzDictionary::new(&self.config.dictionary, &fuzz_state); - // Creates the invariant strategy. let strat = invariant_strat( fuzz_state.clone(), targeted_senders, targeted_contracts.clone(), self.config.dictionary.dictionary_weight, - calldata_fuzz_config.clone(), + fuzz_fixtures.clone(), ) .no_shrink() .boxed(); @@ -387,7 +382,7 @@ impl<'a> InvariantExecutor<'a> { fuzz_state.clone(), targeted_contracts.clone(), target_contract_ref.clone(), - calldata_fuzz_config.clone(), + fuzz_fixtures.clone(), ), target_contract_ref, )); @@ -396,7 +391,7 @@ impl<'a> InvariantExecutor<'a> { self.executor.inspector.fuzzer = Some(Fuzzer { call_generator, fuzz_state: fuzz_state.clone(), collect: true }); - Ok((fuzz_state, targeted_contracts, strat, calldata_fuzz_config)) + Ok((fuzz_state, targeted_contracts, strat)) } /// Fills the `InvariantExecutor` with the artifact identifier filters (in `path:name` string diff --git a/crates/evm/fuzz/src/lib.rs b/crates/evm/fuzz/src/lib.rs index f8928ea3e..b2a058e5b 100644 --- a/crates/evm/fuzz/src/lib.rs +++ b/crates/evm/fuzz/src/lib.rs @@ -14,7 +14,7 @@ use foundry_evm_coverage::HitMaps; use foundry_evm_traces::CallTraceArena; use itertools::Itertools; use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, fmt}; +use std::{collections::HashMap, fmt, sync::Arc}; pub use proptest::test_runner::{Config as FuzzConfig, Reason}; @@ -272,3 +272,42 @@ impl FuzzedCases { self.lowest().map(|c| c.gas).unwrap_or_default() } } + +/// Fixtures to be used for fuzz tests. +/// The key represents name of the fuzzed parameter, value holds possible fuzzed values. +/// For example, for a fixture function declared as +/// `function fixture_sender() external returns (address[] memory senders)` +/// the fuzz fixtures will contain `sender` key with `senders` array as value +#[derive(Clone, Default, Debug)] +pub struct FuzzFixtures { + inner: Arc>, +} + +impl FuzzFixtures { + pub fn new(fixtures: HashMap) -> FuzzFixtures { + Self { inner: Arc::new(fixtures) } + } + + /// Returns configured fixtures for `param_name` fuzzed parameter. + pub fn param_fixtures(&self, param_name: &str) -> Option<&[DynSolValue]> { + if let Some(param_fixtures) = self.inner.get(&normalize_fixture(param_name)) { + match param_fixtures { + DynSolValue::FixedArray(_) => param_fixtures.as_fixed_array(), + _ => param_fixtures.as_array(), + } + } else { + None + } + } +} + +/// Extracts fixture name from a function name. +/// For example: fixtures defined in `fixture_Owner` function will be applied for `owner` parameter. +pub fn fixture_name(function_name: String) -> String { + normalize_fixture(function_name.strip_prefix("fixture").unwrap()) +} + +/// Normalize fixture parameter name, for example `_Owner` to `owner`. +fn normalize_fixture(param_name: &str) -> String { + param_name.trim_matches(&['_']).to_ascii_lowercase() +} diff --git a/crates/evm/fuzz/src/strategies/calldata.rs b/crates/evm/fuzz/src/strategies/calldata.rs index ff3bb5713..760d66175 100644 --- a/crates/evm/fuzz/src/strategies/calldata.rs +++ b/crates/evm/fuzz/src/strategies/calldata.rs @@ -1,71 +1,26 @@ -use crate::strategies::{fuzz_param, EvmFuzzState}; +use crate::{ + strategies::{fuzz_param, fuzz_param_from_state, EvmFuzzState}, + FuzzFixtures, +}; use alloy_dyn_abi::JsonAbiExt; use alloy_json_abi::Function; -use alloy_primitives::{Address, Bytes}; -use foundry_config::FuzzDictionaryConfig; -use proptest::prelude::Strategy; -use std::{collections::HashSet, sync::Arc}; - -/// Clonable wrapper around [CalldataFuzzDictionary]. -#[derive(Clone, Debug)] -pub struct CalldataFuzzDictionary { - pub inner: Arc, -} - -impl CalldataFuzzDictionary { - pub fn new(config: &FuzzDictionaryConfig, state: &EvmFuzzState) -> Self { - Self { inner: Arc::new(CalldataFuzzDictionaryConfig::new(config, state)) } - } -} - -#[derive(Clone, Debug)] -pub struct CalldataFuzzDictionaryConfig { - /// Addresses that can be used for fuzzing calldata. - pub addresses: Vec
, -} - -/// Represents custom configuration for invariant fuzzed calldata strategies. -/// -/// At the moment only the dictionary of addresses to be used for a fuzzed `function(address)` can -/// be configured, but support for other types can be added. -impl CalldataFuzzDictionaryConfig { - /// Creates config with the set of addresses that can be used for fuzzing invariant calldata (if - /// `max_calldata_fuzz_dictionary_addresses` configured). - /// The set of addresses contains a number of `max_calldata_fuzz_dictionary_addresses` random - /// addresses plus all addresses that already had their PUSH bytes collected (retrieved from - /// `EvmFuzzState`, if `include_push_bytes` config enabled). - pub fn new(config: &FuzzDictionaryConfig, state: &EvmFuzzState) -> Self { - let mut addresses = HashSet::
::new(); - - let dict_size = config.max_calldata_fuzz_dictionary_addresses; - if dict_size > 0 { - addresses.extend(std::iter::repeat_with(Address::random).take(dict_size)); - // Add all addresses that already had their PUSH bytes collected. - addresses.extend(state.dictionary_read().addresses()); - } - - Self { addresses: addresses.into_iter().collect() } - } -} +use alloy_primitives::Bytes; +use proptest::prelude::{BoxedStrategy, Strategy}; /// Given a function, it returns a strategy which generates valid calldata -/// for that function's input types. -pub fn fuzz_calldata(func: Function) -> impl Strategy { - fuzz_calldata_with_config(func, None) -} - -/// Given a function, it returns a strategy which generates valid calldata -/// for that function's input types, following custom configuration rules. -pub fn fuzz_calldata_with_config( - func: Function, - config: Option<&CalldataFuzzDictionary>, -) -> impl Strategy { +/// for that function's input types, following declared test fixtures. +pub fn fuzz_calldata(func: Function, fuzz_fixtures: &FuzzFixtures) -> impl Strategy { // We need to compose all the strategies generated for each parameter in all - // possible combinations + // possible combinations, accounting any parameter declared fixture let strats = func .inputs .iter() - .map(|input| fuzz_param(&input.selector_type().parse().unwrap(), config)) + .map(|input| { + fuzz_param( + &input.selector_type().parse().unwrap(), + fuzz_fixtures.param_fixtures(&input.name), + ) + }) .collect::>(); strats.prop_map(move |values| { func.abi_encode_input(&values) @@ -78,3 +33,54 @@ pub fn fuzz_calldata_with_config( .into() }) } + +/// Given a function and some state, it returns a strategy which generated valid calldata for the +/// given function's input types, based on state taken from the EVM. +pub fn fuzz_calldata_from_state(func: Function, state: &EvmFuzzState) -> BoxedStrategy { + let strats = func + .inputs + .iter() + .map(|input| fuzz_param_from_state(&input.selector_type().parse().unwrap(), state)) + .collect::>(); + strats + .prop_map(move |values| { + func.abi_encode_input(&values) + .unwrap_or_else(|_| { + panic!( + "Fuzzer generated invalid arguments for function `{}` with inputs {:?}: {:?}", + func.name, func.inputs, values + ) + }) + .into() + }) + .no_shrink() + .boxed() +} + +#[cfg(test)] +mod tests { + use crate::{strategies::fuzz_calldata, FuzzFixtures}; + use alloy_dyn_abi::{DynSolValue, JsonAbiExt}; + use alloy_json_abi::Function; + use alloy_primitives::Address; + use proptest::prelude::Strategy; + use std::collections::HashMap; + + #[test] + fn can_fuzz_with_fixtures() { + let function = Function::parse("test_fuzzed_address(address addressFixture)").unwrap(); + + let address_fixture = DynSolValue::Address(Address::random()); + let mut fixtures = HashMap::new(); + fixtures.insert( + "addressFixture".to_string(), + DynSolValue::Array(vec![address_fixture.clone()]), + ); + + let expected = function.abi_encode_input(&[address_fixture]).unwrap(); + let strategy = fuzz_calldata(function, &FuzzFixtures::new(fixtures)); + let _ = strategy.prop_map(move |fuzzed| { + assert_eq!(expected, fuzzed); + }); + } +} diff --git a/crates/evm/fuzz/src/strategies/int.rs b/crates/evm/fuzz/src/strategies/int.rs index f772d97c0..b27f62f28 100644 --- a/crates/evm/fuzz/src/strategies/int.rs +++ b/crates/evm/fuzz/src/strategies/int.rs @@ -1,3 +1,4 @@ +use alloy_dyn_abi::{DynSolType, DynSolValue}; use alloy_primitives::{Sign, I256, U256}; use proptest::{ strategy::{NewTree, Strategy, ValueTree}, @@ -83,12 +84,20 @@ impl ValueTree for IntValueTree { /// param). Then generate a value for this bit size. /// 2. Generate a random value around the edges (+/- 3 around min, 0 and max possible value) /// 3. Generate a value from a predefined fixtures set +/// +/// To define int fixtures: +/// - return an array of possible values for a parameter named `amount` declare a function +/// `function fixture_amount() public returns (int32[] memory)`. +/// - use `amount` named parameter in fuzzed test in order to include fixtures in fuzzed values +/// `function testFuzz_int32(int32 amount)`. +/// +/// If fixture is not a valid int type then error is raised and random value generated. #[derive(Debug)] pub struct IntStrategy { /// Bit size of int (e.g. 256) bits: usize, /// A set of fixtures to be generated - fixtures: Vec, + fixtures: Vec, /// The weight for edge cases (+/- 3 around 0 and max possible value) edge_weight: usize, /// The weight for fixtures @@ -102,10 +111,10 @@ impl IntStrategy { /// #Arguments /// * `bits` - Size of uint in bits /// * `fixtures` - A set of fixed values to be generated (according to fixtures weight) - pub fn new(bits: usize, fixtures: Vec) -> Self { + pub fn new(bits: usize, fixtures: Option<&[DynSolValue]>) -> Self { Self { bits, - fixtures, + fixtures: Vec::from(fixtures.unwrap_or_default()), edge_weight: 10usize, fixtures_weight: 40usize, random_weight: 50usize, @@ -132,12 +141,22 @@ impl IntStrategy { } fn generate_fixtures_tree(&self, runner: &mut TestRunner) -> NewTree { - // generate edge cases if there's no fixtures + // generate random cases if there's no fixtures if self.fixtures.is_empty() { - return self.generate_edge_tree(runner) + return self.generate_random_tree(runner) } - let idx = runner.rng().gen_range(0..self.fixtures.len()); - Ok(IntValueTree::new(self.fixtures[idx], false)) + + // Generate value tree from fixture. + let fixture = &self.fixtures[runner.rng().gen_range(0..self.fixtures.len())]; + if let Some(int_fixture) = fixture.as_int() { + if int_fixture.1 == self.bits { + return Ok(IntValueTree::new(int_fixture.0, false)); + } + } + + // If fixture is not a valid type, raise error and generate random value. + error!("{:?} is not a valid {} fixture", fixture, DynSolType::Int(self.bits)); + self.generate_random_tree(runner) } fn generate_random_tree(&self, runner: &mut TestRunner) -> NewTree { diff --git a/crates/evm/fuzz/src/strategies/invariants.rs b/crates/evm/fuzz/src/strategies/invariants.rs index 137e70852..08e53b2a0 100644 --- a/crates/evm/fuzz/src/strategies/invariants.rs +++ b/crates/evm/fuzz/src/strategies/invariants.rs @@ -1,7 +1,8 @@ -use super::{fuzz_calldata_with_config, fuzz_param_from_state, CalldataFuzzDictionary}; +use super::{fuzz_calldata, fuzz_param_from_state}; use crate::{ invariant::{BasicTxDetails, FuzzRunIdentifiedContracts, SenderFilters}, strategies::{fuzz_calldata_from_state, fuzz_param, EvmFuzzState}, + FuzzFixtures, }; use alloy_json_abi::{Function, JsonAbi}; use alloy_primitives::{Address, Bytes}; @@ -14,7 +15,7 @@ pub fn override_call_strat( fuzz_state: EvmFuzzState, contracts: FuzzRunIdentifiedContracts, target: Arc>, - calldata_fuzz_config: CalldataFuzzDictionary, + fuzz_fixtures: FuzzFixtures, ) -> SBoxedStrategy<(Address, Bytes)> { let contracts_ref = contracts.targets.clone(); proptest::prop_oneof![ @@ -24,7 +25,7 @@ pub fn override_call_strat( ] .prop_flat_map(move |target_address| { let fuzz_state = fuzz_state.clone(); - let calldata_fuzz_config = calldata_fuzz_config.clone(); + let fuzz_fixtures = fuzz_fixtures.clone(); let func = { let contracts = contracts.targets.lock(); @@ -40,7 +41,7 @@ pub fn override_call_strat( }; func.prop_flat_map(move |func| { - fuzz_contract_with_calldata(&fuzz_state, &calldata_fuzz_config, target_address, func) + fuzz_contract_with_calldata(&fuzz_state, &fuzz_fixtures, target_address, func) }) }) .sboxed() @@ -61,11 +62,11 @@ pub fn invariant_strat( senders: SenderFilters, contracts: FuzzRunIdentifiedContracts, dictionary_weight: u32, - calldata_fuzz_config: CalldataFuzzDictionary, + fuzz_fixtures: FuzzFixtures, ) -> impl Strategy { // We only want to seed the first value, since we want to generate the rest as we mutate the // state - generate_call(fuzz_state, senders, contracts, dictionary_weight, calldata_fuzz_config) + generate_call(fuzz_state, senders, contracts, dictionary_weight, fuzz_fixtures) } /// Strategy to generate a transaction where the `sender`, `target` and `calldata` are all generated @@ -75,7 +76,7 @@ fn generate_call( senders: SenderFilters, contracts: FuzzRunIdentifiedContracts, dictionary_weight: u32, - calldata_fuzz_config: CalldataFuzzDictionary, + fuzz_fixtures: FuzzFixtures, ) -> BoxedStrategy { let senders = Rc::new(senders); any::() @@ -92,11 +93,11 @@ fn generate_call( let senders = senders.clone(); let fuzz_state = fuzz_state.clone(); - let calldata_fuzz_config = calldata_fuzz_config.clone(); + let fuzz_fixtures = fuzz_fixtures.clone(); func.prop_flat_map(move |func| { let sender = select_random_sender(&fuzz_state, senders.clone(), dictionary_weight); let contract = - fuzz_contract_with_calldata(&fuzz_state, &calldata_fuzz_config, contract, func); + fuzz_contract_with_calldata(&fuzz_state, &fuzz_fixtures, contract, func); (sender, contract) }) }) @@ -164,7 +165,7 @@ fn select_random_function( /// for that function's input types. pub fn fuzz_contract_with_calldata( fuzz_state: &EvmFuzzState, - calldata_fuzz_config: &CalldataFuzzDictionary, + fuzz_fixtures: &FuzzFixtures, contract: Address, func: Function, ) -> impl Strategy { @@ -173,7 +174,7 @@ pub fn fuzz_contract_with_calldata( // `prop_oneof!` / `TupleUnion` `Arc`s for cheap cloning. #[allow(clippy::arc_with_non_send_sync)] prop_oneof![ - 60 => fuzz_calldata_with_config(func.clone(), Some(calldata_fuzz_config)), + 60 => fuzz_calldata(func.clone(), fuzz_fixtures), 40 => fuzz_calldata_from_state(func, fuzz_state), ] .prop_map(move |calldata| { diff --git a/crates/evm/fuzz/src/strategies/mod.rs b/crates/evm/fuzz/src/strategies/mod.rs index 0e82a4d4b..74cefca2a 100644 --- a/crates/evm/fuzz/src/strategies/mod.rs +++ b/crates/evm/fuzz/src/strategies/mod.rs @@ -8,14 +8,38 @@ mod param; pub use param::{fuzz_param, fuzz_param_from_state}; mod calldata; -pub use calldata::{ - fuzz_calldata, fuzz_calldata_with_config, CalldataFuzzDictionary, CalldataFuzzDictionaryConfig, -}; +pub use calldata::{fuzz_calldata, fuzz_calldata_from_state}; mod state; -pub use state::{ - build_initial_state, collect_created_contracts, fuzz_calldata_from_state, EvmFuzzState, -}; +pub use state::{build_initial_state, collect_created_contracts, EvmFuzzState}; mod invariants; pub use invariants::{fuzz_contract_with_calldata, invariant_strat, override_call_strat}; + +/// Macro to create strategy with fixtures. +/// 1. A default strategy if no fixture defined for current parameter. +/// 2. A weighted strategy that use fixtures and default strategy values for current parameter. +/// If fixture is not of the same type as fuzzed parameter then value is rejected and error raised. +macro_rules! fixture_strategy { + ($fixtures:ident, $strategy_value:expr, $default_strategy:expr) => { + if let Some(fixtures) = $fixtures { + proptest::prop_oneof![ + 50 => { + let custom_fixtures: Vec = + fixtures.iter().enumerate().map(|(_, value)| value.to_owned()).collect(); + let custom_fixtures_len = custom_fixtures.len(); + any::() + .prop_filter_map("invalid fixture", move |index| { + let index = index.index(custom_fixtures_len); + $strategy_value(custom_fixtures.get(index)) + }) + }, + 50 => $default_strategy + ].boxed() + } else { + $default_strategy.boxed() + } + }; +} + +pub(crate) use fixture_strategy; diff --git a/crates/evm/fuzz/src/strategies/param.rs b/crates/evm/fuzz/src/strategies/param.rs index 20e69a27e..12904cb00 100644 --- a/crates/evm/fuzz/src/strategies/param.rs +++ b/crates/evm/fuzz/src/strategies/param.rs @@ -1,5 +1,5 @@ use super::state::EvmFuzzState; -use crate::strategies::calldata::CalldataFuzzDictionary; +use crate::strategies::fixture_strategy; use alloy_dyn_abi::{DynSolType, DynSolValue}; use alloy_primitives::{Address, B256, I256, U256}; use proptest::prelude::*; @@ -7,68 +7,105 @@ use proptest::prelude::*; /// The max length of arrays we fuzz for is 256. const MAX_ARRAY_LEN: usize = 256; -/// Given a parameter type, returns a strategy for generating values for that type. +/// Given a parameter type and configured fixtures for param name, returns a strategy for generating +/// values for that type. Fixtures can be currently generated for uint, int, address, bytes and +/// string types and are defined for parameter name. +/// +/// For example, fixtures for parameter `owner` of type `address` can be defined in a function with +/// a `function fixture_owner() public returns (address[] memory)` signature. +/// +/// Fixtures are matched on parameter name, hence fixtures defined in +/// `fixture_owner` function can be used in a fuzzed test function with a signature like +/// `function testFuzz_ownerAddress(address owner, uint amount)`. +/// +/// Fuzzer will reject value and raise error if the fixture type is not of the same type as +/// parameter to fuzz. /// /// Works with ABI Encoder v2 tuples. pub fn fuzz_param( param: &DynSolType, - config: Option<&CalldataFuzzDictionary>, + fuzz_fixtures: Option<&[DynSolValue]>, ) -> BoxedStrategy { match *param { DynSolType::Address => { - if let Some(config) = config { - let len = config.inner.addresses.len(); - if len > 0 { - let dict = config.inner.clone(); - // Create strategy to return random address from configured dictionary. - return any::() - .prop_map(move |index| { - let index = index.index(len); - DynSolValue::Address(dict.addresses[index]) - }) - .boxed(); - } - } - - // If no config for addresses dictionary then create unbounded addresses strategy. - any::
().prop_map(DynSolValue::Address).boxed() - } - DynSolType::Int(n @ 8..=256) => { - super::IntStrategy::new(n, vec![]).prop_map(move |x| DynSolValue::Int(x, n)).boxed() + fixture_strategy!( + fuzz_fixtures, + |fixture: Option<&DynSolValue>| { + if let Some(val @ DynSolValue::Address(_)) = fixture { + Some(val.clone()) + } else { + error!("{:?} is not a valid address fixture", fixture.unwrap()); + None + } + }, + DynSolValue::type_strategy(&DynSolType::Address) + ) } - DynSolType::Uint(n @ 8..=256) => { - super::UintStrategy::new(n, vec![]).prop_map(move |x| DynSolValue::Uint(x, n)).boxed() - } - DynSolType::Function | DynSolType::Bool | DynSolType::Bytes => { - DynSolValue::type_strategy(param).boxed() - } - DynSolType::FixedBytes(size @ 1..=32) => any::() - .prop_map(move |mut v| { - v[size..].fill(0); - DynSolValue::FixedBytes(v, size) - }) + DynSolType::Int(n @ 8..=256) => super::IntStrategy::new(n, fuzz_fixtures) + .prop_map(move |x| DynSolValue::Int(x, n)) .boxed(), - DynSolType::String => DynSolValue::type_strategy(param) - .prop_map(move |value| { + DynSolType::Uint(n @ 8..=256) => super::UintStrategy::new(n, fuzz_fixtures) + .prop_map(move |x| DynSolValue::Uint(x, n)) + .boxed(), + DynSolType::Function | DynSolType::Bool => DynSolValue::type_strategy(param).boxed(), + DynSolType::Bytes => { + fixture_strategy!( + fuzz_fixtures, + |fixture: Option<&DynSolValue>| { + if let Some(val @ DynSolValue::Bytes(_)) = fixture { + Some(val.clone()) + } else { + error!("{:?} is not a valid bytes fixture", fixture.unwrap()); + None + } + }, + DynSolValue::type_strategy(&DynSolType::Bytes) + ) + } + DynSolType::FixedBytes(size @ 1..=32) => fixture_strategy!( + fuzz_fixtures, + |fixture: Option<&DynSolValue>| { + if let Some(val @ DynSolValue::FixedBytes(_, _)) = fixture { + if let Some(val) = val.as_fixed_bytes() { + if val.1 == size { + return Some(DynSolValue::FixedBytes(B256::from_slice(val.0), val.1)) + } + } + } + error!("{:?} is not a valid fixed bytes fixture", fixture.unwrap()); + None + }, + DynSolValue::type_strategy(&DynSolType::FixedBytes(size)) + ), + DynSolType::String => fixture_strategy!( + fuzz_fixtures, + |fixture: Option<&DynSolValue>| { + if let Some(val @ DynSolValue::String(_)) = fixture { + Some(val.clone()) + } else { + error!("{:?} is not a valid string fixture", fixture.unwrap()); + None + } + }, + DynSolValue::type_strategy(&DynSolType::String).prop_map(move |value| { DynSolValue::String( value.as_str().unwrap().trim().trim_end_matches('\0').to_string(), ) }) - .boxed(), - + ), DynSolType::Tuple(ref params) => params .iter() - .map(|p| fuzz_param(p, config)) + .map(|p| fuzz_param(p, None)) .collect::>() .prop_map(DynSolValue::Tuple) .boxed(), DynSolType::FixedArray(ref param, size) => { - proptest::collection::vec(fuzz_param(param, config), size) + proptest::collection::vec(fuzz_param(param, None), size) .prop_map(DynSolValue::FixedArray) .boxed() } DynSolType::Array(ref param) => { - proptest::collection::vec(fuzz_param(param, config), 0..MAX_ARRAY_LEN) + proptest::collection::vec(fuzz_param(param, None), 0..MAX_ARRAY_LEN) .prop_map(DynSolValue::Array) .boxed() } @@ -174,7 +211,10 @@ pub fn fuzz_param_from_state( #[cfg(test)] mod tests { - use crate::strategies::{build_initial_state, fuzz_calldata, fuzz_calldata_from_state}; + use crate::{ + strategies::{build_initial_state, fuzz_calldata, fuzz_calldata_from_state}, + FuzzFixtures, + }; use foundry_common::abi::get_func; use foundry_config::FuzzDictionaryConfig; use revm::db::{CacheDB, EmptyDB}; @@ -186,7 +226,7 @@ mod tests { let db = CacheDB::new(EmptyDB::default()); let state = build_initial_state(&db, FuzzDictionaryConfig::default()); let strat = proptest::prop_oneof![ - 60 => fuzz_calldata(func.clone()), + 60 => fuzz_calldata(func.clone(), &FuzzFixtures::default()), 40 => fuzz_calldata_from_state(func, &state), ]; let cfg = proptest::test_runner::Config { failure_persistence: None, ..Default::default() }; diff --git a/crates/evm/fuzz/src/strategies/state.rs b/crates/evm/fuzz/src/strategies/state.rs index 2c5e98d9c..2ee3f7fca 100644 --- a/crates/evm/fuzz/src/strategies/state.rs +++ b/crates/evm/fuzz/src/strategies/state.rs @@ -1,14 +1,10 @@ -use super::fuzz_param_from_state; use crate::invariant::{ArtifactFilters, FuzzRunIdentifiedContracts}; -use alloy_dyn_abi::JsonAbiExt; -use alloy_json_abi::Function; -use alloy_primitives::{Address, Bytes, Log, B256, U256}; +use alloy_primitives::{Address, Log, B256, U256}; use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; use foundry_config::FuzzDictionaryConfig; use foundry_evm_core::utils::StateChangeset; use indexmap::IndexSet; use parking_lot::{lock_api::RwLockReadGuard, RawRwLock, RwLock}; -use proptest::prelude::{BoxedStrategy, Strategy}; use revm::{ db::{CacheDB, DatabaseRef}, interpreter::opcode::{self, spec_opcode_gas}, @@ -181,29 +177,6 @@ impl FuzzDictionary { } } -/// Given a function and some state, it returns a strategy which generated valid calldata for the -/// given function's input types, based on state taken from the EVM. -pub fn fuzz_calldata_from_state(func: Function, state: &EvmFuzzState) -> BoxedStrategy { - let strats = func - .inputs - .iter() - .map(|input| fuzz_param_from_state(&input.selector_type().parse().unwrap(), state)) - .collect::>(); - strats - .prop_map(move |values| { - func.abi_encode_input(&values) - .unwrap_or_else(|_| { - panic!( - "Fuzzer generated invalid arguments for function `{}` with inputs {:?}: {:?}", - func.name, func.inputs, values - ) - }) - .into() - }) - .no_shrink() - .boxed() -} - /// Builds the initial [EvmFuzzState] from a database. pub fn build_initial_state( db: &CacheDB, diff --git a/crates/evm/fuzz/src/strategies/uint.rs b/crates/evm/fuzz/src/strategies/uint.rs index 7b9aac1d4..1b1eb2540 100644 --- a/crates/evm/fuzz/src/strategies/uint.rs +++ b/crates/evm/fuzz/src/strategies/uint.rs @@ -1,3 +1,4 @@ +use alloy_dyn_abi::{DynSolType, DynSolValue}; use alloy_primitives::U256; use proptest::{ strategy::{NewTree, Strategy, ValueTree}, @@ -71,12 +72,20 @@ impl ValueTree for UintValueTree { /// param). Then generate a value for this bit size. /// 2. Generate a random value around the edges (+/- 3 around 0 and max possible value) /// 3. Generate a value from a predefined fixtures set +/// +/// To define uint fixtures: +/// - return an array of possible values for a parameter named `amount` declare a function +/// `function fixture_amount() public returns (uint32[] memory)`. +/// - use `amount` named parameter in fuzzed test in order to include fixtures in fuzzed values +/// `function testFuzz_uint32(uint32 amount)`. +/// +/// If fixture is not a valid uint type then error is raised and random value generated. #[derive(Debug)] pub struct UintStrategy { /// Bit size of uint (e.g. 256) bits: usize, /// A set of fixtures to be generated - fixtures: Vec, + fixtures: Vec, /// The weight for edge cases (+/- 3 around 0 and max possible value) edge_weight: usize, /// The weight for fixtures @@ -90,10 +99,10 @@ impl UintStrategy { /// #Arguments /// * `bits` - Size of uint in bits /// * `fixtures` - A set of fixed values to be generated (according to fixtures weight) - pub fn new(bits: usize, fixtures: Vec) -> Self { + pub fn new(bits: usize, fixtures: Option<&[DynSolValue]>) -> Self { Self { bits, - fixtures, + fixtures: Vec::from(fixtures.unwrap_or_default()), edge_weight: 10usize, fixtures_weight: 40usize, random_weight: 50usize, @@ -105,19 +114,27 @@ impl UintStrategy { // Choose if we want values around 0 or max let is_min = rng.gen_bool(0.5); let offset = U256::from(rng.gen_range(0..4)); - let max = - if self.bits < 256 { (U256::from(1) << self.bits) - U256::from(1) } else { U256::MAX }; - let start = if is_min { offset } else { max.saturating_sub(offset) }; + let start = if is_min { offset } else { self.type_max().saturating_sub(offset) }; Ok(UintValueTree::new(start, false)) } fn generate_fixtures_tree(&self, runner: &mut TestRunner) -> NewTree { - // generate edge cases if there's no fixtures + // generate random cases if there's no fixtures if self.fixtures.is_empty() { - return self.generate_edge_tree(runner) + return self.generate_random_tree(runner) } - let idx = runner.rng().gen_range(0..self.fixtures.len()); - Ok(UintValueTree::new(self.fixtures[idx], false)) + + // Generate value tree from fixture. + let fixture = &self.fixtures[runner.rng().gen_range(0..self.fixtures.len())]; + if let Some(uint_fixture) = fixture.as_uint() { + if uint_fixture.1 == self.bits { + return Ok(UintValueTree::new(uint_fixture.0, false)); + } + } + + // If fixture is not a valid type, raise error and generate random value. + error!("{:?} is not a valid {} fixture", fixture, DynSolType::Uint(self.bits)); + self.generate_random_tree(runner) } fn generate_random_tree(&self, runner: &mut TestRunner) -> NewTree { @@ -151,6 +168,14 @@ impl UintStrategy { Ok(UintValueTree::new(start, false)) } + + fn type_max(&self) -> U256 { + if self.bits < 256 { + (U256::from(1) << self.bits) - U256::from(1) + } else { + U256::MAX + } + } } impl Strategy for UintStrategy { diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 72835f097..ed0c1305c 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -254,7 +254,7 @@ impl TestArgs { let profiles = get_available_profiles(toml)?; let test_options: TestOptions = TestOptionsBuilder::default() - .fuzz(config.clone().fuzz) + .fuzz(config.fuzz.clone()) .invariant(config.invariant) .profiles(profiles) .build(&output, project_root)?; diff --git a/crates/forge/src/result.rs b/crates/forge/src/result.rs index 04f397c48..2dd5508e8 100644 --- a/crates/forge/src/result.rs +++ b/crates/forge/src/result.rs @@ -9,7 +9,7 @@ use foundry_evm::{ coverage::HitMaps, debug::DebugArena, executors::EvmError, - fuzz::{CounterExample, FuzzCase}, + fuzz::{CounterExample, FuzzCase, FuzzFixtures}, traces::{CallTraceArena, CallTraceDecoder, TraceKind, Traces}, }; use serde::{Deserialize, Serialize}; @@ -534,6 +534,8 @@ pub struct TestSetup { pub reason: Option, /// Coverage info during setup pub coverage: Option, + /// Defined fuzz test fixtures + pub fuzz_fixtures: FuzzFixtures, } impl TestSetup { @@ -566,8 +568,9 @@ impl TestSetup { traces: Traces, labeled_addresses: HashMap, coverage: Option, + fuzz_fixtures: FuzzFixtures, ) -> Self { - Self { address, logs, traces, labeled_addresses, reason: None, coverage } + Self { address, logs, traces, labeled_addresses, reason: None, coverage, fuzz_fixtures } } pub fn failed_with( @@ -583,6 +586,7 @@ impl TestSetup { labeled_addresses, reason: Some(reason), coverage: None, + fuzz_fixtures: FuzzFixtures::default(), } } diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index ad335e767..ed48bdc34 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -5,6 +5,7 @@ use crate::{ result::{SuiteResult, TestKind, TestResult, TestSetup, TestStatus}, TestFilter, TestOptions, }; +use alloy_dyn_abi::DynSolValue; use alloy_json_abi::Function; use alloy_primitives::{Address, U256}; use eyre::Result; @@ -20,9 +21,9 @@ use foundry_evm::{ executors::{ fuzz::{CaseOutcome, CounterExampleOutcome, FuzzOutcome, FuzzedExecutor}, invariant::{replay_run, InvariantExecutor, InvariantFuzzError, InvariantFuzzTestResult}, - EvmError, ExecutionErr, Executor, RawCallResult, + CallResult, EvmError, ExecutionErr, Executor, RawCallResult, }, - fuzz::{invariant::InvariantContract, CounterExample}, + fuzz::{fixture_name, invariant::InvariantContract, CounterExample, FuzzFixtures}, traces::{load_contracts, TraceKind}, }; use proptest::test_runner::TestRunner; @@ -166,14 +167,84 @@ impl<'a> ContractRunner<'a> { traces.extend(setup_traces.map(|traces| (TraceKind::Setup, traces))); logs.extend(setup_logs); - TestSetup { address, logs, traces, labeled_addresses, reason, coverage } + TestSetup { + address, + logs, + traces, + labeled_addresses, + reason, + coverage, + fuzz_fixtures: self.fuzz_fixtures(address), + } } else { - TestSetup::success(address, logs, traces, Default::default(), None) + TestSetup::success( + address, + logs, + traces, + Default::default(), + None, + self.fuzz_fixtures(address), + ) }; Ok(setup) } + /// Collect fixtures from test contract. + /// + /// Fixtures can be defined: + /// - as storage arrays in test contract, prefixed with `fixture` + /// - as functions prefixed with `fixture` and followed by parameter name to be + /// fuzzed + /// + /// Storage array fixtures: + /// `uint256[] public fixture_amount = [1, 2, 3];` + /// define an array of uint256 values to be used for fuzzing `amount` named parameter in scope + /// of the current test. + /// + /// Function fixtures: + /// `function fixture_owner() public returns (address[] memory){}` + /// returns an array of addresses to be used for fuzzing `owner` named parameter in scope of the + /// current test. + fn fuzz_fixtures(&mut self, address: Address) -> FuzzFixtures { + let mut fixtures = HashMap::new(); + self.contract.abi.functions().filter(|func| func.is_fixture()).for_each(|func| { + if func.inputs.is_empty() { + // Read fixtures declared as functions. + if let Ok(CallResult { raw: _, decoded_result }) = + self.executor.call(CALLER, address, func, &[], U256::ZERO, None) + { + fixtures.insert(fixture_name(func.name.clone()), decoded_result); + } + } else { + // For reading fixtures from storage arrays we collect values by calling the + // function with incremented indexes until there's an error. + let mut vals = Vec::new(); + let mut index = 0; + loop { + if let Ok(CallResult { raw: _, decoded_result }) = self.executor.call( + CALLER, + address, + func, + &[DynSolValue::Uint(U256::from(index), 256)], + U256::ZERO, + None, + ) { + vals.push(decoded_result); + } else { + // No result returned for this index, we reached the end of storage + // array or the function is not a valid fixture. + break; + } + index += 1; + } + fixtures.insert(fixture_name(func.name.clone()), DynSolValue::Array(vals)); + }; + }); + + FuzzFixtures::new(fixtures) + } + /// Runs all tests for a contract whose names match the provided regular expression pub fn run_tests( mut self, @@ -442,7 +513,8 @@ impl<'a> ContractRunner<'a> { identified_contracts: &ContractsByAddress, ) -> TestResult { trace!(target: "forge::test::fuzz", "executing invariant test for {:?}", func.name); - let TestSetup { address, logs, traces, labeled_addresses, coverage, .. } = setup; + let TestSetup { address, logs, traces, labeled_addresses, coverage, fuzz_fixtures, .. } = + setup; // First, run the test normally to see if it needs to be skipped. let start = Instant::now(); @@ -479,7 +551,7 @@ impl<'a> ContractRunner<'a> { InvariantContract { address, invariant_function: func, abi: &self.contract.abi }; let InvariantFuzzTestResult { error, cases, reverts, last_run_inputs, gas_report_traces } = - match evm.invariant_fuzz(invariant_contract.clone()) { + match evm.invariant_fuzz(invariant_contract.clone(), &fuzz_fixtures) { Ok(x) => x, Err(e) => { return TestResult { @@ -587,7 +659,13 @@ impl<'a> ContractRunner<'a> { let _guard = span.enter(); let TestSetup { - address, mut logs, mut traces, mut labeled_addresses, mut coverage, .. + address, + mut logs, + mut traces, + mut labeled_addresses, + mut coverage, + fuzz_fixtures, + .. } = setup; // Run fuzz test @@ -598,7 +676,8 @@ impl<'a> ContractRunner<'a> { self.sender, fuzz_config.clone(), ); - let result = fuzzed_executor.fuzz(func, address, should_fail, self.revert_decoder); + let result = + fuzzed_executor.fuzz(func, &fuzz_fixtures, address, should_fail, self.revert_decoder); let mut debug = Default::default(); let mut breakpoints = Default::default(); diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index efbd95d71..e03272ea8 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -139,7 +139,13 @@ async fn test_invariant() { ), ( "default/fuzz/invariant/common/InvariantCalldataDictionary.t.sol:InvariantCalldataDictionary", - vec![("invariant_owner_never_changes()", true, None, None, None)], + vec![( + "invariant_owner_never_changes()", + false, + Some("".into()), + None, + None, + )], ), ( "default/fuzz/invariant/common/InvariantAssume.t.sol:InvariantAssume", @@ -157,6 +163,16 @@ async fn test_invariant() { "default/fuzz/invariant/target/FuzzedTargetContracts.t.sol:DynamicTargetContract", vec![("invariant_dynamic_targets()", true, None, None, None)], ), + ( + "default/fuzz/invariant/common/InvariantFixtures.t.sol:InvariantFixtures", + vec![( + "invariant_target_not_compromised()", + false, + Some("".into()), + None, + None, + )], + ), ]), ); } @@ -348,30 +364,13 @@ async fn test_invariant_preserve_state() { } #[tokio::test(flavor = "multi_thread")] -async fn test_invariant_calldata_fuzz_dictionary_addresses() { - // should not fail with default options (address dict not finite) +async fn test_invariant_with_address_fixture() { let mut runner = TEST_DATA_DEFAULT.runner(); let results = runner.test_collect(&Filter::new( ".*", ".*", ".*fuzz/invariant/common/InvariantCalldataDictionary.t.sol", )); - assert_multiple( - &results, - BTreeMap::from([( - "default/fuzz/invariant/common/InvariantCalldataDictionary.t.sol:InvariantCalldataDictionary", - vec![("invariant_owner_never_changes()", true, None, None, None)], - )]), - ); - - // same test should fail when calldata address dict is bounded - // set address dictionary to single entry to fail fast - runner.test_options.invariant.dictionary.max_calldata_fuzz_dictionary_addresses = 1; - let results = runner.test_collect(&Filter::new( - ".*", - ".*", - ".*fuzz/invariant/common/InvariantCalldataDictionary.t.sol", - )); assert_multiple( &results, BTreeMap::from([( @@ -407,6 +406,8 @@ async fn test_invariant_assume_does_not_revert() { 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 results = runner.test_collect(&filter); assert_multiple( @@ -471,3 +472,25 @@ 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 results = runner.test_collect(&filter); + assert_multiple( + &results, + BTreeMap::from([( + "default/fuzz/invariant/common/InvariantFixtures.t.sol:InvariantFixtures", + vec![( + "invariant_target_not_compromised()", + false, + Some("".into()), + None, + None, + )], + )]), + ); +} diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index f65a3c116..27f574990 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -87,7 +87,6 @@ impl ForgeTestProfile { dictionary_weight: 40, max_fuzz_dictionary_addresses: 10_000, max_fuzz_dictionary_values: 10_000, - max_calldata_fuzz_dictionary_addresses: 0, }, gas_report_samples: 256, failure_persist_dir: Some(tempfile::tempdir().unwrap().into_path()), @@ -104,7 +103,6 @@ impl ForgeTestProfile { include_push_bytes: true, max_fuzz_dictionary_addresses: 10_000, max_fuzz_dictionary_values: 10_000, - max_calldata_fuzz_dictionary_addresses: 0, }, shrink_sequence: true, shrink_run_limit: 2usize.pow(18u32), diff --git a/testdata/default/fuzz/FuzzInt.t.sol b/testdata/default/fuzz/FuzzInt.t.sol index aac0825db..071727f6d 100644 --- a/testdata/default/fuzz/FuzzInt.t.sol +++ b/testdata/default/fuzz/FuzzInt.t.sol @@ -3,7 +3,8 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -// See https://github.com/foundry-rs/foundry/pull/735 for context +// https://github.com/foundry-rs/foundry/pull/735 behavior changed with https://github.com/foundry-rs/foundry/issues/3521 +// random values (instead edge cases) are generated if no fixtures defined contract FuzzNumbersTest is DSTest { function testPositive(int256) public { assertTrue(true); @@ -14,31 +15,31 @@ contract FuzzNumbersTest is DSTest { } function testNegative0(int256 val) public { - assertTrue(val != 0); + assertTrue(val == 0); } function testNegative1(int256 val) public { - assertTrue(val != -1); + assertTrue(val == -1); } function testNegative2(int128 val) public { - assertTrue(val != 1); + assertTrue(val == 1); } function testNegativeMax0(int256 val) public { - assertTrue(val != type(int256).max); + assertTrue(val == type(int256).max); } function testNegativeMax1(int256 val) public { - assertTrue(val != type(int256).max - 2); + assertTrue(val == type(int256).max - 2); } function testNegativeMin0(int256 val) public { - assertTrue(val != type(int256).min); + assertTrue(val == type(int256).min); } function testNegativeMin1(int256 val) public { - assertTrue(val != type(int256).min + 2); + assertTrue(val == type(int256).min + 2); } function testEquality(int256 x, int256 y) public { diff --git a/testdata/default/fuzz/FuzzUint.t.sol b/testdata/default/fuzz/FuzzUint.t.sol index 5ae90a57b..923c2980f 100644 --- a/testdata/default/fuzz/FuzzUint.t.sol +++ b/testdata/default/fuzz/FuzzUint.t.sol @@ -3,7 +3,8 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -// See https://github.com/foundry-rs/foundry/pull/735 for context +// https://github.com/foundry-rs/foundry/pull/735 behavior changed with https://github.com/foundry-rs/foundry/issues/3521 +// random values (instead edge cases) are generated if no fixtures defined contract FuzzNumbersTest is DSTest { function testPositive(uint256) public { assertTrue(true); @@ -14,19 +15,19 @@ contract FuzzNumbersTest is DSTest { } function testNegative0(uint256 val) public { - assertTrue(val != 0); + assertTrue(val == 0); } function testNegative2(uint256 val) public { - assertTrue(val != 2); + assertTrue(val == 2); } function testNegative2Max(uint256 val) public { - assertTrue(val != type(uint256).max - 2); + assertTrue(val == type(uint256).max - 2); } function testNegativeMax(uint256 val) public { - assertTrue(val != type(uint256).max); + assertTrue(val == type(uint256).max); } function testEquality(uint256 x, uint256 y) public { diff --git a/testdata/default/fuzz/invariant/common/InvariantAssume.t.sol b/testdata/default/fuzz/invariant/common/InvariantAssume.t.sol index 4ac0d085c..9808a870f 100644 --- a/testdata/default/fuzz/invariant/common/InvariantAssume.t.sol +++ b/testdata/default/fuzz/invariant/common/InvariantAssume.t.sol @@ -8,7 +8,7 @@ contract Handler is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); function doSomething(uint256 param) public { - vm.assume(param != 0); + vm.assume(param == 0); } } diff --git a/testdata/default/fuzz/invariant/common/InvariantCalldataDictionary.t.sol b/testdata/default/fuzz/invariant/common/InvariantCalldataDictionary.t.sol index e1486f963..5387b020d 100644 --- a/testdata/default/fuzz/invariant/common/InvariantCalldataDictionary.t.sol +++ b/testdata/default/fuzz/invariant/common/InvariantCalldataDictionary.t.sol @@ -62,11 +62,14 @@ contract InvariantCalldataDictionary is DSTest { address owner; Owned owned; Handler handler; + address[] actors; function setUp() public { owner = address(this); owned = new Owned(); handler = new Handler(owned); + actors.push(owner); + actors.push(address(777)); } function targetSelectors() public returns (FuzzSelector[] memory) { @@ -78,6 +81,14 @@ contract InvariantCalldataDictionary is DSTest { return targets; } + function fixtureSender() external returns (address[] memory) { + return actors; + } + + function fixtureCandidate() external returns (address[] memory) { + return actors; + } + function invariant_owner_never_changes() public { assertEq(owned.owner(), owner); } diff --git a/testdata/default/fuzz/invariant/common/InvariantFixtures.t.sol b/testdata/default/fuzz/invariant/common/InvariantFixtures.t.sol new file mode 100644 index 000000000..b3f1e17cb --- /dev/null +++ b/testdata/default/fuzz/invariant/common/InvariantFixtures.t.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.0; + +import "ds-test/test.sol"; + +contract Target { + bool ownerFound; + bool amountFound; + bool magicFound; + bool keyFound; + bool backupFound; + bool extraStringFound; + + function fuzzWithFixtures( + address owner_, + uint256 _amount, + int32 magic, + bytes32 key, + bytes memory backup, + string memory extra + ) external { + if (owner_ == address(0x6B175474E89094C44Da98b954EedeAC495271d0F)) { + ownerFound = true; + } + if (_amount == 1122334455) amountFound = true; + if (magic == -777) magicFound = true; + if (key == "abcd1234") keyFound = true; + if (keccak256(backup) == keccak256("qwerty1234")) backupFound = true; + if (keccak256(abi.encodePacked(extra)) == keccak256(abi.encodePacked("112233aabbccdd"))) { + extraStringFound = true; + } + } + + function isCompromised() public view returns (bool) { + return ownerFound && amountFound && magicFound && keyFound && backupFound && extraStringFound; + } +} + +/// Try to compromise target contract by finding all accepted values using fixtures. +contract InvariantFixtures is DSTest { + Target target; + address[] public fixture_owner_ = [address(0x6B175474E89094C44Da98b954EedeAC495271d0F)]; + uint256[] public fixture_amount = [1, 2, 1122334455]; + + function setUp() public { + target = new Target(); + } + + function fixtureMagic() external returns (int32[2] memory) { + int32[2] memory magic; + magic[0] = -777; + magic[1] = 777; + return magic; + } + + function fixtureKey() external pure returns (bytes32[] memory) { + bytes32[] memory keyFixture = new bytes32[](1); + keyFixture[0] = "abcd1234"; + return keyFixture; + } + + function fixtureBackup() external pure returns (bytes[] memory) { + bytes[] memory backupFixture = new bytes[](1); + backupFixture[0] = "qwerty1234"; + return backupFixture; + } + + function fixtureExtra() external pure returns (string[] memory) { + string[] memory extraFixture = new string[](1); + extraFixture[0] = "112233aabbccdd"; + return extraFixture; + } + + function invariant_target_not_compromised() public { + assertEq(target.isCompromised(), false); + } +} From db64c3e61fc37934a352b4c5ad5a7feb3d2753e7 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 23 Apr 2024 11:44:15 +0200 Subject: [PATCH 211/430] chore: bump alloy chains (#7763) --- Cargo.lock | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 114b59a2a..0082e8412 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,9 +67,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "alloy-chains" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40646aa7f01e396139cf0d6c3a7475eeb8094a0f41d8199f10860c8aef09d2f1" +checksum = "fe6c2674230e94ea98767550b02853bf7024b46f784827be95acfc5f5f1a445f" dependencies = [ "num_enum", "serde", @@ -5832,7 +5832,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ - "proc-macro-crate 3.1.0", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 2.0.60", @@ -6518,15 +6518,6 @@ dependencies = [ "toml_edit 0.20.7", ] -[[package]] -name = "proc-macro-crate" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" -dependencies = [ - "toml_edit 0.21.1", -] - [[package]] name = "proc-macro-error" version = "1.0.4" @@ -8492,17 +8483,6 @@ dependencies = [ "winnow 0.5.40", ] -[[package]] -name = "toml_edit" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" -dependencies = [ - "indexmap", - "toml_datetime", - "winnow 0.5.40", -] - [[package]] name = "toml_edit" version = "0.22.12" From bf853afe4be085f06e5ff673c33fb09e91390945 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 23 Apr 2024 18:27:51 +0200 Subject: [PATCH 212/430] fix(cast): list all files in the keystore directory (#7766) --- crates/cast/bin/cmd/wallet/list.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/cast/bin/cmd/wallet/list.rs b/crates/cast/bin/cmd/wallet/list.rs index d4ca3f013..e534d5713 100644 --- a/crates/cast/bin/cmd/wallet/list.rs +++ b/crates/cast/bin/cmd/wallet/list.rs @@ -91,17 +91,17 @@ impl ListArgs { dunce::canonicalize(keystore_path)? }; - // list files within keystore dir - std::fs::read_dir(keystore_dir)?.flatten().for_each(|entry| { - let path = entry.path(); - if path.is_file() && path.extension().is_none() { + // List all files within the keystore directory. + for entry in std::fs::read_dir(keystore_dir)? { + let path = entry?.path(); + if path.is_file() { if let Some(file_name) = path.file_name() { if let Some(name) = file_name.to_str() { - println!("{} (Local)", name); + println!("{name} (Local)"); } } } - }); + } Ok(()) } From 6ff43857bbb38c1f0c7f8dec5db1fda802362c10 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 23 Apr 2024 18:29:13 +0200 Subject: [PATCH 213/430] fix(cast): ENS commands (#7765) --- crates/cast/bin/main.rs | 18 ++--- crates/cast/src/lib.rs | 47 ----------- crates/cast/tests/cli/main.rs | 34 ++++++++ crates/common/src/ens.rs | 147 ++++++++++++++++++++++------------ 4 files changed, 138 insertions(+), 108 deletions(-) diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index 8c0181367..565cd6eda 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -11,7 +11,7 @@ use eyre::Result; use foundry_cli::{handler, prompt, stdin, utils}; use foundry_common::{ abi::get_event, - ens::ProviderEnsExt, + ens::{namehash, ProviderEnsExt}, fmt::{format_tokens, format_uint_exp}, fs, selectors::{ @@ -446,19 +446,19 @@ async fn main() -> Result<()> { // ENS CastSubcommand::Namehash { name } => { let name = stdin::unwrap_line(name)?; - println!("{}", SimpleCast::namehash(&name)?); + println!("{}", namehash(&name)); } CastSubcommand::LookupAddress { who, rpc, verify } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; let who = stdin::unwrap_line(who)?; - let name = provider.lookup_address(who).await?; + let name = provider.lookup_address(&who).await?; if verify { let address = provider.resolve_name(&name).await?; eyre::ensure!( address == who, - "Forward lookup verification failed: got `{name:?}`, expected `{who:?}`" + "Reverse lookup verification failed: got `{address}`, expected `{who}`" ); } println!("{name}"); @@ -470,13 +470,13 @@ async fn main() -> Result<()> { let who = stdin::unwrap_line(who)?; let address = provider.resolve_name(&who).await?; if verify { - let name = provider.lookup_address(address).await?; - assert_eq!( - name, who, - "forward lookup verification failed. got {name}, expected {who}" + let name = provider.lookup_address(&address).await?; + eyre::ensure!( + name == who, + "Forward lookup verification failed: got `{name}`, expected `{who}`" ); } - println!("{}", address.to_checksum(None)); + println!("{address}"); } // Misc diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index 338510eb9..beb5e4dc3 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -1676,53 +1676,6 @@ impl SimpleCast { Ok(location.to_string()) } - /// Converts ENS names to their namehash representation - /// [Namehash reference](https://docs.ens.domains/contract-api-reference/name-processing#hashing-names) - /// [namehash-rust reference](https://github.com/InstateDev/namehash-rust/blob/master/src/lib.rs) - /// - /// # Example - /// - /// ``` - /// use cast::SimpleCast as Cast; - /// - /// assert_eq!( - /// Cast::namehash("")?, - /// "0x0000000000000000000000000000000000000000000000000000000000000000" - /// ); - /// assert_eq!( - /// Cast::namehash("eth")?, - /// "0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae" - /// ); - /// assert_eq!( - /// Cast::namehash("foo.eth")?, - /// "0xde9b09fd7c5f901e23a3f19fecc54828e9c848539801e86591bd9801b019f84f" - /// ); - /// assert_eq!( - /// Cast::namehash("sub.foo.eth")?, - /// "0x500d86f9e663479e5aaa6e99276e55fc139c597211ee47d17e1e92da16a83402" - /// ); - /// # Ok::<_, eyre::Report>(()) - /// ``` - pub fn namehash(ens: &str) -> Result { - let mut node = vec![0u8; 32]; - - if !ens.is_empty() { - let ens_lower = ens.to_lowercase(); - let mut labels: Vec<&str> = ens_lower.split('.').collect(); - labels.reverse(); - - for label in labels { - let mut label_hash = keccak256(label.as_bytes()); - node.append(&mut label_hash.to_vec()); - - label_hash = keccak256(node.as_slice()); - node = label_hash.to_vec(); - } - } - - Ok(hex::encode_prefixed(node)) - } - /// Keccak-256 hashes arbitrary data /// /// # Example diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index bdc17a2fa..c6f2b0450 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -1,5 +1,6 @@ //! Contains various tests for checking cast commands +use alloy_primitives::{address, b256, Address, B256}; use foundry_test_utils::{ casttest, rpc::{next_http_rpc_endpoint, next_ws_rpc_endpoint}, @@ -794,3 +795,36 @@ interface Interface { }"#; assert_eq!(output.trim(), s); }); + +const ENS_NAME: &str = "emo.eth"; +const ENS_NAMEHASH: B256 = + b256!("0a21aaf2f6414aa664deb341d1114351fdb023cad07bf53b28e57c26db681910"); +const ENS_ADDRESS: Address = address!("28679A1a632125fbBf7A68d850E50623194A709E"); + +casttest!(ens_namehash, |_prj, cmd| { + cmd.args(["namehash", ENS_NAME]); + let out = cmd.stdout_lossy().trim().parse::(); + assert_eq!(out, Ok(ENS_NAMEHASH)); +}); + +casttest!(ens_lookup, |_prj, cmd| { + let eth_rpc_url = next_http_rpc_endpoint(); + cmd.args(["lookup-address", &ENS_ADDRESS.to_string(), "--rpc-url", ð_rpc_url, "--verify"]); + let out = cmd.stdout_lossy(); + assert_eq!(out.trim(), ENS_NAME); +}); + +casttest!(ens_resolve, |_prj, cmd| { + let eth_rpc_url = next_http_rpc_endpoint(); + cmd.args(["resolve-name", ENS_NAME, "--rpc-url", ð_rpc_url, "--verify"]); + let out = cmd.stdout_lossy().trim().parse::
(); + assert_eq!(out, Ok(ENS_ADDRESS)); +}); + +casttest!(ens_resolve_no_dot_eth, |_prj, cmd| { + let eth_rpc_url = next_http_rpc_endpoint(); + let name = ENS_NAME.strip_suffix(".eth").unwrap(); + cmd.args(["resolve-name", name, "--rpc-url", ð_rpc_url, "--verify"]); + let (_out, err) = cmd.unchecked_output_lossy(); + assert!(err.contains("not found"), "{err:?}"); +}); diff --git a/crates/common/src/ens.rs b/crates/common/src/ens.rs index cb1058384..131980f55 100644 --- a/crates/common/src/ens.rs +++ b/crates/common/src/ens.rs @@ -1,30 +1,31 @@ //! ENS Name resolving utilities. + #![allow(missing_docs)] -use alloy_primitives::{address, keccak256, Address, B256}; + +use self::EnsResolver::EnsResolverInstance; +use alloy_primitives::{address, Address, Keccak256, B256}; use alloy_provider::{Network, Provider}; use alloy_sol_types::sol; use alloy_transport::Transport; use async_trait::async_trait; -use std::str::FromStr; - -use self::EnsResolver::EnsResolverInstance; +use std::{borrow::Cow, str::FromStr}; // ENS Registry and Resolver contracts. sol! { + /// ENS Registry contract. #[sol(rpc)] - // ENS Registry contract. contract EnsRegistry { /// Returns the resolver for the specified node. function resolver(bytes32 node) view returns (address); } + /// ENS Resolver interface. #[sol(rpc)] - // ENS Resolver interface. contract EnsResolver { - // Returns the address associated with the specified node. + /// Returns the address associated with the specified node. function addr(bytes32 node) view returns (address); - // Returns the name associated with an ENS node, for reverse records. + /// Returns the name associated with an ENS node, for reverse records. function name(bytes32 node) view returns (string); } } @@ -36,13 +37,19 @@ pub const ENS_REVERSE_REGISTRAR_DOMAIN: &str = "addr.reverse"; /// Error type for ENS resolution. #[derive(Debug, thiserror::Error)] -pub enum EnsResolutionError { - /// Failed to resolve ENS registry. - #[error("Failed to get resolver from ENS registry: {0}")] - EnsRegistryResolutionFailed(String), +pub enum EnsError { + /// Failed to get resolver from the ENS registry. + #[error("Failed to get resolver from the ENS registry: {0}")] + Resolver(alloy_contract::Error), + /// Failed to get resolver from the ENS registry. + #[error("ENS resolver not found for name {0:?}")] + ResolverNotFound(String), + /// Failed to lookup ENS name from an address. + #[error("Failed to lookup ENS name from an address: {0}")] + Lookup(alloy_contract::Error), /// Failed to resolve ENS name to an address. #[error("Failed to resolve ENS name to an address: {0}")] - EnsResolutionFailed(String), + Resolve(alloy_contract::Error), } /// ENS name or Ethereum Address. @@ -59,7 +66,7 @@ impl NameOrAddress { pub async fn resolve>( &self, provider: &P, - ) -> Result { + ) -> Result { match self { NameOrAddress::Name(name) => provider.resolve_name(name).await, NameOrAddress::Address(addr) => Ok(*addr), @@ -97,35 +104,36 @@ impl FromStr for NameOrAddress { } } +/// Extension trait for ENS contract calls. #[async_trait] pub trait ProviderEnsExt> { - async fn get_resolver(&self) -> Result, EnsResolutionError>; + /// Returns the resolver for the specified node. The `&str` is only used for error messages. + async fn get_resolver( + &self, + node: B256, + error_name: &str, + ) -> Result, EnsError>; - async fn resolve_name(&self, name: &str) -> Result { + /// Performs a forward lookup of an ENS name to an address. + async fn resolve_name(&self, name: &str) -> Result { let node = namehash(name); - let addr = self - .get_resolver() - .await? + let resolver = self.get_resolver(node, name).await?; + let addr = resolver .addr(node) .call() .await - .map_err(|err| EnsResolutionError::EnsResolutionFailed(err.to_string()))? + .map_err(EnsError::Resolve) + .inspect_err(|e| eprintln!("{e:?}"))? ._0; - Ok(addr) } - async fn lookup_address(&self, address: Address) -> Result { - let node = namehash(&reverse_address(address)); - let name = self - .get_resolver() - .await? - .name(node) - .call() - .await - .map_err(|err| EnsResolutionError::EnsResolutionFailed(err.to_string()))? - ._0; - + /// Performs a reverse lookup of an address to an ENS name. + async fn lookup_address(&self, address: &Address) -> Result { + let name = reverse_address(address); + let node = namehash(&name); + let resolver = self.get_resolver(node, &name).await?; + let name = resolver.name(node).call().await.map_err(EnsError::Lookup)?._0; Ok(name) } } @@ -137,15 +145,16 @@ where N: Network, T: Transport + Clone, { - async fn get_resolver(&self) -> Result, EnsResolutionError> { + async fn get_resolver( + &self, + node: B256, + error_name: &str, + ) -> Result, EnsError> { let registry = EnsRegistry::new(ENS_ADDRESS, self); - let address = registry - .resolver(namehash("eth")) - .call() - .await - .map_err(|err| EnsResolutionError::EnsRegistryResolutionFailed(err.to_string()))? - ._0; - + let address = registry.resolver(node).call().await.map_err(EnsError::Resolver)?._0; + if address == Address::ZERO { + return Err(EnsError::ResolverNotFound(error_name.to_string())); + } Ok(EnsResolverInstance::new(address, self)) } } @@ -156,18 +165,36 @@ pub fn namehash(name: &str) -> B256 { return B256::ZERO } - // Remove the variation selector U+FE0F - let name = name.replace('\u{fe0f}', ""); - - // Generate the node starting from the right - name.rsplit('.') - .fold([0u8; 32], |node, label| *keccak256([node, *keccak256(label.as_bytes())].concat())) - .into() + // Remove the variation selector `U+FE0F` if present. + const VARIATION_SELECTOR: char = '\u{fe0f}'; + let name = if name.contains(VARIATION_SELECTOR) { + Cow::Owned(name.replace(VARIATION_SELECTOR, "")) + } else { + Cow::Borrowed(name) + }; + + // Generate the node starting from the right. + // This buffer is `[node @ [u8; 32], label_hash @ [u8; 32]]`. + let mut buffer = [0u8; 64]; + for label in name.rsplit('.') { + // node = keccak256([node, keccak256(label)]) + + // Hash the label. + let mut label_hasher = Keccak256::new(); + label_hasher.update(label.as_bytes()); + label_hasher.finalize_into(&mut buffer[32..]); + + // Hash both the node and the label hash, writing into the node. + let mut buffer_hasher = Keccak256::new(); + buffer_hasher.update(buffer.as_slice()); + buffer_hasher.finalize_into(&mut buffer[..32]); + } + buffer[..32].try_into().unwrap() } /// Returns the reverse-registrar name of an address. -pub fn reverse_address(addr: Address) -> String { - format!("{addr:?}.{ENS_REVERSE_REGISTRAR_DOMAIN}")[2..].to_string() +pub fn reverse_address(addr: &Address) -> String { + format!("{addr:x}.{ENS_REVERSE_REGISTRAR_DOMAIN}") } #[cfg(test)] @@ -175,19 +202,35 @@ mod test { use super::*; fn assert_hex(hash: B256, val: &str) { - assert_eq!(hash.0.to_vec(), hex::decode(val).unwrap()); + assert_eq!(hash.0[..], hex::decode(val).unwrap()[..]); } #[test] fn test_namehash() { for (name, expected) in &[ - ("", "0000000000000000000000000000000000000000000000000000000000000000"), - ("foo.eth", "de9b09fd7c5f901e23a3f19fecc54828e9c848539801e86591bd9801b019f84f"), + ("", "0x0000000000000000000000000000000000000000000000000000000000000000"), ("eth", "0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae"), + ("foo.eth", "0xde9b09fd7c5f901e23a3f19fecc54828e9c848539801e86591bd9801b019f84f"), ("alice.eth", "0x787192fc5378cc32aa956ddfdedbf26b24e8d78e40109add0eea2c1a012c3dec"), ("ret↩️rn.eth", "0x3de5f4c02db61b221e7de7f1c40e29b6e2f07eb48d65bf7e304715cd9ed33b24"), ] { assert_hex(namehash(name), expected); } } + + #[test] + fn test_reverse_address() { + for (addr, expected) in [ + ( + "0x314159265dd8dbb310642f98f50c066173c1259b", + "314159265dd8dbb310642f98f50c066173c1259b.addr.reverse", + ), + ( + "0x28679A1a632125fbBf7A68d850E50623194A709E", + "28679a1a632125fbbf7a68d850e50623194a709e.addr.reverse", + ), + ] { + assert_eq!(reverse_address(&addr.parse().unwrap()), expected, "{addr}"); + } + } } From 21dfde4df13fc2be8fe9ce2f1d6fd63023c2ed40 Mon Sep 17 00:00:00 2001 From: Krishang <93703995+kamuik16@users.noreply.github.com> Date: Tue, 23 Apr 2024 23:23:10 +0530 Subject: [PATCH 214/430] feat(cheatcode): UintToHex cheatcode (#7767) uintToHex cheatcode --- crates/cheatcodes/assets/cheatcodes.json | 20 ++++++++++++++++++++ crates/cheatcodes/spec/src/vm.rs | 5 +++++ crates/cheatcodes/src/json.rs | 8 ++++++++ crates/evm/traces/src/decoder/mod.rs | 1 + testdata/cheats/Vm.sol | 1 + testdata/default/cheats/Json.t.sol | 1 + 6 files changed, 36 insertions(+) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 9ce73e931..1c7781591 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -7191,6 +7191,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "serializeUintToHex", + "description": "See `serializeJson`.", + "declaration": "function serializeUintToHex(string calldata objectKey, string calldata valueKey, uint256 value) external returns (string memory json);", + "visibility": "external", + "mutability": "", + "signature": "serializeUintToHex(string,string,uint256)", + "selector": "0xae5a2ae8", + "selectorBytes": [ + 174, + 90, + 42, + 232 + ] + }, + "group": "json", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "serializeUint_0", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 39f0fd8b0..44ee2534f 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1865,6 +1865,11 @@ interface Vm { returns (string memory json); /// See `serializeJson`. #[cheatcode(group = Json)] + function serializeUintToHex(string calldata objectKey, string calldata valueKey, uint256 value) + external + returns (string memory json); + /// See `serializeJson`. + #[cheatcode(group = Json)] function serializeInt(string calldata objectKey, string calldata valueKey, int256 value) external returns (string memory json); diff --git a/crates/cheatcodes/src/json.rs b/crates/cheatcodes/src/json.rs index b837c8425..e493078a3 100644 --- a/crates/cheatcodes/src/json.rs +++ b/crates/cheatcodes/src/json.rs @@ -248,6 +248,14 @@ impl Cheatcode for serializeBytes_1Call { } } +impl Cheatcode for serializeUintToHexCall { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let Self { objectKey, valueKey, value } = self; + let hex = format!("0x{:x}", value); + serialize_json(state, objectKey, Some(valueKey), &hex) + } +} + impl Cheatcode for writeJson_0Call { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { json, path } = self; diff --git a/crates/evm/traces/src/decoder/mod.rs b/crates/evm/traces/src/decoder/mod.rs index ad4e161cf..70720dc6c 100644 --- a/crates/evm/traces/src/decoder/mod.rs +++ b/crates/evm/traces/src/decoder/mod.rs @@ -441,6 +441,7 @@ impl CallTraceDecoder { "keyExistsJson" | "serializeBool" | "serializeUint" | + "serializeUintToHex" | "serializeInt" | "serializeAddress" | "serializeBytes32" | diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 02eeac480..01d2bd431 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -355,6 +355,7 @@ interface Vm { function serializeJson(string calldata objectKey, string calldata value) external returns (string memory json); function serializeString(string calldata objectKey, string calldata valueKey, string calldata value) external returns (string memory json); function serializeString(string calldata objectKey, string calldata valueKey, string[] calldata values) external returns (string memory json); + function serializeUintToHex(string calldata objectKey, string calldata valueKey, uint256 value) external returns (string memory json); function serializeUint(string calldata objectKey, string calldata valueKey, uint256 value) external returns (string memory json); function serializeUint(string calldata objectKey, string calldata valueKey, uint256[] calldata values) external returns (string memory json); function setEnv(string calldata name, string calldata value) external; diff --git a/testdata/default/cheats/Json.t.sol b/testdata/default/cheats/Json.t.sol index b15292e15..ca53b1801 100644 --- a/testdata/default/cheats/Json.t.sol +++ b/testdata/default/cheats/Json.t.sol @@ -224,6 +224,7 @@ contract WriteJsonTest is DSTest { vm.serializeBool(json1, "boolean", true); vm.serializeInt(json2, "key2", -234); vm.serializeUint(json2, "deploy", uint256(254)); + vm.serializeUintToHex(json2, "hexUint", uint256(255)); string memory data = vm.serializeBool(json2, "boolean", true); vm.serializeString(json2, "json1", data); emit log(data); From 7dfa26392c2e0f23270c965c39126370fc642941 Mon Sep 17 00:00:00 2001 From: "Du, Chengbin" Date: Wed, 24 Apr 2024 06:17:17 +0800 Subject: [PATCH 215/430] Fix contract create-and-verify with libraries (#7750) pass libraries to verify args --- crates/forge/bin/cmd/create.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index 2212c4e98..68b303362 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -185,7 +185,7 @@ impl CreateArgs { skip_is_verified_check: true, watch: true, retry: self.retry, - libraries: vec![], + libraries: self.opts.libraries.clone(), root: None, verifier: self.verifier.clone(), via_ir: self.opts.via_ir, @@ -330,7 +330,7 @@ impl CreateArgs { skip_is_verified_check: false, watch: true, retry: self.retry, - libraries: vec![], + libraries: self.opts.libraries.clone(), root: None, verifier: self.verifier, via_ir: self.opts.via_ir, From e0ea59cae26d945445d9cf21fdf22f4a18ac5bb2 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Tue, 23 Apr 2024 15:56:29 -0700 Subject: [PATCH 216/430] refactor: migrate cast doctests to alloy (#7768) * migrate: cast doctests to alloy * migrated: cast doctests to alloy * nits * use alloy_provider not helper fn --- crates/cast/src/lib.rs | 244 +++++++++++++++++++++++------------------ 1 file changed, 138 insertions(+), 106 deletions(-) diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index beb5e4dc3..d63bbc33e 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -71,11 +71,12 @@ where /// # Example /// /// ``` + /// use alloy_provider::{network::AnyNetwork, ProviderBuilder, RootProvider}; /// use cast::Cast; - /// use foundry_common::provider::alloy::get_http_provider; /// /// # async fn foo() -> eyre::Result<()> { - /// let provider = get_http_provider("http://localhost:8545"); + /// let provider = + /// ProviderBuilder::<_, _, AnyNetwork>::default().on_builtin("http://localhost:8545").await?; /// let cast = Cast::new(provider); /// # Ok(()) /// # } @@ -88,25 +89,27 @@ where /// /// # Example /// - /// ```ignore - /// use cast::{Cast, TxBuilder}; - /// use ethers_core::types::Address; - /// use ethers_providers::{Http, Provider}; - /// use foundry_common::provider::alloy::get_http_provider; + /// ``` + /// use alloy_primitives::{Address, U256, Bytes}; + /// use alloy_rpc_types::{TransactionRequest, WithOtherFields}; + /// use cast::Cast; + /// use alloy_provider::{RootProvider, ProviderBuilder, network::AnyNetwork}; /// use std::str::FromStr; + /// use alloy_sol_types::{sol, SolCall}; + /// + /// sol!( + /// function greeting(uint256 i) public returns (string); + /// ); /// /// # async fn foo() -> eyre::Result<()> { - /// let provider = Provider::::try_from("http://localhost:8545")?; - /// let alloy_provider = get_http_provider("http://localhost:8545"); + /// let alloy_provider = ProviderBuilder::<_,_, AnyNetwork>::default().on_builtin("http://localhost:8545").await?;; /// let to = Address::from_str("0xB3C95ff08316fb2F2e3E52Ee82F8e7b605Aa1304")?; - /// let sig = "function greeting(uint256 i) public returns (string)"; - /// let args = vec!["5".to_owned()]; - /// let mut builder = - /// TxBuilder::new(&provider, Address::zero(), Some(to), Chain::Mainnet, false).await?; - /// builder.set_args(sig, args).await?; - /// let builder_output = builder.build(); - /// let cast = Cast::new(provider, alloy_provider); - /// let data = cast.call(builder_output, None).await?; + /// let greeting = greetingCall { i: U256::from(5) }.abi_encode(); + /// let bytes = Bytes::from_iter(greeting.iter()); + /// let tx = TransactionRequest::default().to(Some(to)).input(bytes.into()); + /// let tx = WithOtherFields::new(tx); + /// let cast = Cast::new(alloy_provider); + /// let data = cast.call(&tx, None, None).await?; /// println!("{}", data); /// # Ok(()) /// # } @@ -159,23 +162,27 @@ where /// /// # Example /// - /// ```ignore - /// use cast::{Cast, TxBuilder}; - /// use ethers_core::types::Address; - /// use ethers_providers::{Http, Provider}; + /// ``` + /// use cast::{Cast}; + /// use alloy_primitives::{Address, U256, Bytes}; + /// use alloy_rpc_types::{TransactionRequest, WithOtherFields}; + /// use alloy_provider::{RootProvider, ProviderBuilder, network::AnyNetwork}; /// use std::str::FromStr; + /// use alloy_sol_types::{sol, SolCall}; + /// + /// sol!( + /// function greeting(uint256 i) public returns (string); + /// ); /// /// # async fn foo() -> eyre::Result<()> { - /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let provider = ProviderBuilder::<_,_, AnyNetwork>::default().on_builtin("http://localhost:8545").await?;; /// let to = Address::from_str("0xB3C95ff08316fb2F2e3E52Ee82F8e7b605Aa1304")?; - /// let sig = "greeting(uint256)(string)"; - /// let args = vec!["5".to_owned()]; - /// let mut builder = - /// TxBuilder::new(&provider, Address::zero(), Some(to), Chain::Mainnet, false).await?; - /// builder.set_args(sig, args).await?; - /// let builder_output = builder.peek(); + /// let greeting = greetingCall { i: U256::from(5) }.abi_encode(); + /// let bytes = Bytes::from_iter(greeting.iter()); + /// let tx = TransactionRequest::default().to(Some(to)).input(bytes.into()); + /// let tx = WithOtherFields::new(tx); /// let cast = Cast::new(&provider); - /// let access_list = cast.access_list(builder_output, None, false).await?; + /// let access_list = cast.access_list(&tx, None, false).await?; /// println!("{}", access_list); /// # Ok(()) /// # } @@ -215,27 +222,32 @@ where /// /// # Example /// - /// ```ignore - /// use cast::{Cast, TxBuilder}; - /// use ethers_core::types::{Address, U256}; - /// use ethers_providers::{Http, Provider}; + /// ``` + /// use cast::{Cast}; + /// use alloy_primitives::{Address, U256, Bytes}; + /// use alloy_rpc_types::{TransactionRequest, WithOtherFields}; + /// use alloy_provider::{RootProvider, ProviderBuilder, network::AnyNetwork}; /// use std::str::FromStr; + /// use alloy_sol_types::{sol, SolCall}; + /// + /// sol!( + /// function greet(string greeting) public; + /// ); /// /// # async fn foo() -> eyre::Result<()> { - /// let provider = Provider::::try_from("http://localhost:8545")?; - /// let from = "vitalik.eth"; - /// let to = eAddress::from_str("0xB3C95ff08316fb2F2e3E52Ee82F8e7b605Aa1304")?; - /// let sig = "greet(string)()"; - /// let args = vec!["hello".to_owned()]; + /// let provider = ProviderBuilder::<_,_, AnyNetwork>::default().on_builtin("http://localhost:8545").await?;; + /// let from = Address::from_str("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")?; + /// let to = Address::from_str("0xB3C95ff08316fb2F2e3E52Ee82F8e7b605Aa1304")?; + /// let greeting = greetCall { greeting: "hello".to_string() }.abi_encode(); + /// let bytes = Bytes::from_iter(greeting.iter()); /// let gas = U256::from_str("200000").unwrap(); /// let value = U256::from_str("1").unwrap(); /// let nonce = U256::from_str("1").unwrap(); - /// let mut builder = TxBuilder::new(&provider, from, Some(to), Chain::Mainnet, false).await?; - /// builder.set_args(sig, args).await?.set_gas(gas).set_value(value).set_nonce(nonce); - /// let builder_output = builder.build(); + /// let tx = TransactionRequest::default().to(Some(to)).input(bytes.into()).from(from); + /// let tx = WithOtherFields::new(tx); /// let cast = Cast::new(provider); - /// let data = cast.send(builder_output).await?; - /// println!("{}", *data); + /// let data = cast.send(tx).await?; + /// println!("{:#?}", data); /// # Ok(()) /// # } /// ``` @@ -252,12 +264,13 @@ where /// /// # Example /// - /// ```ignore + /// ``` + /// use alloy_provider::{network::AnyNetwork, ProviderBuilder, RootProvider}; /// use cast::Cast; - /// use ethers_providers::{Http, Provider}; /// /// # async fn foo() -> eyre::Result<()> { - /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let provider = + /// ProviderBuilder::<_, _, AnyNetwork>::default().on_builtin("http://localhost:8545").await?; /// let cast = Cast::new(provider); /// let res = cast.publish("0x1234".to_string()).await?; /// println!("{:?}", res); @@ -280,12 +293,13 @@ where /// # Example /// - /// ```ignore + /// ``` + /// use alloy_provider::{network::AnyNetwork, ProviderBuilder, RootProvider}; /// use cast::Cast; - /// use ethers_providers::{Http, Provider}; /// /// # async fn foo() -> eyre::Result<()> { - /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let provider = + /// ProviderBuilder::<_, _, AnyNetwork>::default().on_builtin("http://localhost:8545").await?; /// let cast = Cast::new(provider); /// let block = cast.block(5, true, None, false).await?; /// println!("{}", block); @@ -436,14 +450,15 @@ where /// # Example /// - /// ```ignore + /// ``` + /// use alloy_primitives::Address; + /// use alloy_provider::{network::AnyNetwork, ProviderBuilder, RootProvider}; /// use cast::Cast; - /// use ethers_core::types::Address; - /// use ethers_providers::{Http, Provider}; /// use std::str::FromStr; /// /// # async fn foo() -> eyre::Result<()> { - /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let provider = + /// ProviderBuilder::<_, _, AnyNetwork>::default().on_builtin("http://localhost:8545").await?; /// let cast = Cast::new(provider); /// let addr = Address::from_str("0x7eD52863829AB99354F3a0503A622e82AcD5F7d3")?; /// let nonce = cast.nonce(addr, None).await?; @@ -457,14 +472,15 @@ where /// # Example /// - /// ```ignore + /// ``` + /// use alloy_primitives::Address; + /// use alloy_provider::{network::AnyNetwork, ProviderBuilder, RootProvider}; /// use cast::Cast; - /// use ethers_core::types::Address; - /// use ethers_providers::{Http, Provider}; /// use std::str::FromStr; /// /// # async fn foo() -> eyre::Result<()> { - /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let provider = + /// ProviderBuilder::<_, _, AnyNetwork>::default().on_builtin("http://localhost:8545").await?; /// let cast = Cast::new(provider); /// let addr = Address::from_str("0x7eD52863829AB99354F3a0503A622e82AcD5F7d3")?; /// let implementation = cast.implementation(addr, None).await?; @@ -482,14 +498,15 @@ where /// # Example /// - /// ```ignore + /// ``` + /// use alloy_primitives::Address; + /// use alloy_provider::{network::AnyNetwork, ProviderBuilder, RootProvider}; /// use cast::Cast; - /// use ethers_core::types::Address; - /// use ethers_providers::{Http, Provider}; /// use std::str::FromStr; /// /// # async fn foo() -> eyre::Result<()> { - /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let provider = + /// ProviderBuilder::<_, _, AnyNetwork>::default().on_builtin("http://localhost:8545").await?; /// let cast = Cast::new(provider); /// let addr = Address::from_str("0x7eD52863829AB99354F3a0503A622e82AcD5F7d3")?; /// let admin = cast.admin(addr, None).await?; @@ -507,14 +524,15 @@ where /// # Example /// - /// ```ignore + /// ``` /// use alloy_primitives::{Address, U256}; + /// use alloy_provider::{network::AnyNetwork, ProviderBuilder, RootProvider}; /// use cast::Cast; - /// use ethers_providers::{Http, Provider}; /// use std::str::FromStr; /// /// # async fn foo() -> eyre::Result<()> { - /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let provider = + /// ProviderBuilder::<_, _, AnyNetwork>::default().on_builtin("http://localhost:8545").await?; /// let cast = Cast::new(provider); /// let addr = Address::from_str("7eD52863829AB99354F3a0503A622e82AcD5F7d3")?; /// let computed_address = cast.compute_address(addr, None).await?; @@ -529,14 +547,15 @@ where /// # Example /// - /// ```ignore + /// ``` + /// use alloy_primitives::Address; + /// use alloy_provider::{network::AnyNetwork, ProviderBuilder, RootProvider}; /// use cast::Cast; - /// use ethers_core::types::Address; - /// use ethers_providers::{Http, Provider}; /// use std::str::FromStr; /// /// # async fn foo() -> eyre::Result<()> { - /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let provider = + /// ProviderBuilder::<_, _, AnyNetwork>::default().on_builtin("http://localhost:8545").await?; /// let cast = Cast::new(provider); /// let addr = Address::from_str("0x00000000219ab540356cbb839cbe05303d7705fa")?; /// let code = cast.code(addr, None, false).await?; @@ -560,14 +579,15 @@ where /// Example /// - /// ```ignore + /// ``` + /// use alloy_primitives::Address; + /// use alloy_provider::{network::AnyNetwork, ProviderBuilder, RootProvider}; /// use cast::Cast; - /// use ethers_core::types::Address; - /// use ethers_providers::{Http, Provider}; /// use std::str::FromStr; /// /// # async fn foo() -> eyre::Result<()> { - /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let provider = + /// ProviderBuilder::<_, _, AnyNetwork>::default().on_builtin("http://localhost:8545").await?; /// let cast = Cast::new(provider); /// let addr = Address::from_str("0x00000000219ab540356cbb839cbe05303d7705fa")?; /// let codesize = cast.codesize(addr, None).await?; @@ -582,12 +602,13 @@ where /// # Example /// - /// ```ignore + /// ``` + /// use alloy_provider::{network::AnyNetwork, ProviderBuilder, RootProvider}; /// use cast::Cast; - /// use ethers_providers::{Http, Provider}; /// /// # async fn foo() -> eyre::Result<()> { - /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let provider = + /// ProviderBuilder::<_, _, AnyNetwork>::default().on_builtin("http://localhost:8545").await?; /// let cast = Cast::new(provider); /// let tx_hash = "0xf8d1713ea15a81482958fb7ddf884baee8d3bcc478c5f2f604e008dc788ee4fc"; /// let tx = cast.transaction(tx_hash.to_string(), None, false, false).await?; @@ -620,12 +641,13 @@ where /// # Example /// - /// ```ignore + /// ``` + /// use alloy_provider::{network::AnyNetwork, ProviderBuilder, RootProvider}; /// use cast::Cast; - /// use ethers_providers::{Http, Provider}; /// /// # async fn foo() -> eyre::Result<()> { - /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let provider = + /// ProviderBuilder::<_, _, AnyNetwork>::default().on_builtin("http://localhost:8545").await?; /// let cast = Cast::new(provider); /// let tx_hash = "0xf8d1713ea15a81482958fb7ddf884baee8d3bcc478c5f2f604e008dc788ee4fc"; /// let receipt = cast.receipt(tx_hash.to_string(), None, 1, false, false).await?; @@ -679,12 +701,13 @@ where /// /// # Example /// - /// ```ignore + /// ``` + /// use alloy_provider::{network::AnyNetwork, ProviderBuilder, RootProvider}; /// use cast::Cast; - /// use ethers_providers::{Http, Provider}; /// /// # async fn foo() -> eyre::Result<()> { - /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let provider = + /// ProviderBuilder::<_, _, AnyNetwork>::default().on_builtin("http://localhost:8545").await?; /// let cast = Cast::new(provider); /// let result = cast /// .rpc("eth_getBalance", &["0xc94770007dda54cF92009BFF0dE90c06F603a09f", "latest"]) @@ -708,17 +731,18 @@ where /// /// # Example /// - /// ```ignore + /// ``` + /// use alloy_primitives::{Address, B256}; + /// use alloy_provider::{network::AnyNetwork, ProviderBuilder, RootProvider}; /// use cast::Cast; - /// use ethers_core::types::{Address, H256}; - /// use ethers_providers::{Http, Provider}; /// use std::str::FromStr; /// /// # async fn foo() -> eyre::Result<()> { - /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let provider = + /// ProviderBuilder::<_, _, AnyNetwork>::default().on_builtin("http://localhost:8545").await?; /// let cast = Cast::new(provider); /// let addr = Address::from_str("0x00000000006c3852cbEf3e08E8dF289169EdE581")?; - /// let slot = H256::zero(); + /// let slot = B256::ZERO; /// let storage = cast.storage(addr, slot, None).await?; /// println!("{}", storage); /// # Ok(()) @@ -763,23 +787,27 @@ where /// /// # Example /// - /// ```ignore + /// ``` + /// use alloy_primitives::fixed_bytes; + /// use alloy_provider::{network::AnyNetwork, ProviderBuilder, RootProvider}; + /// use alloy_rpc_types::{BlockId, BlockNumberOrTag}; /// use cast::Cast; - /// use ethers_core::types::{BlockId, BlockNumber}; - /// use ethers_providers::{Http, Provider}; - /// use std::convert::TryFrom; + /// use std::{convert::TryFrom, str::FromStr}; /// /// # async fn foo() -> eyre::Result<()> { - /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let provider = + /// ProviderBuilder::<_, _, AnyNetwork>::default().on_builtin("http://localhost:8545").await?; /// let cast = Cast::new(provider); /// - /// let block_number = - /// cast.convert_block_number(Some(BlockId::Number(BlockNumber::from(5)))).await?; - /// assert_eq!(block_number, Some(BlockNumber::from(5))); + /// let block_number = cast.convert_block_number(Some(BlockId::number(5))).await?; + /// assert_eq!(block_number, Some(BlockNumberOrTag::Number(5))); /// - /// let block_number = - /// cast.convert_block_number(Some(BlockId::Hash("0x1234".parse().unwrap()))).await?; - /// assert_eq!(block_number, Some(BlockNumber::from(1234))); + /// let block_number = cast + /// .convert_block_number(Some(BlockId::hash(fixed_bytes!( + /// "0000000000000000000000000000000000000000000000000000000000001234" + /// )))) + /// .await?; + /// assert_eq!(block_number, Some(BlockNumberOrTag::Number(4660))); /// /// let block_number = cast.convert_block_number(None).await?; /// assert_eq!(block_number, None); @@ -806,14 +834,18 @@ where /// /// # Example /// - /// ```ignore + /// ``` + /// use alloy_primitives::Address; + /// use alloy_provider::{network::AnyNetwork, ProviderBuilder, RootProvider}; + /// use alloy_rpc_types::Filter; + /// use alloy_transport::BoxTransport; /// use cast::Cast; - /// use ethers_core::{abi::Address, types::Filter}; - /// use ethers_providers::{Provider, Ws}; + /// use foundry_common::provider::alloy::get_http_provider as get_ws_provider; /// use std::{io, str::FromStr}; /// /// # async fn foo() -> eyre::Result<()> { - /// let provider = Provider::new(Ws::connect("wss://localhost:8545").await?); + /// let provider = + /// ProviderBuilder::<_, _, AnyNetwork>::default().on_builtin("wss://localhost:8545").await?; /// let cast = Cast::new(provider); /// /// let filter = @@ -930,8 +962,8 @@ impl SimpleCast { /// # Example /// /// ``` + /// use alloy_primitives::{I256, U256}; /// use cast::SimpleCast; - /// use ethers_core::types::{I256, U256}; /// /// assert_eq!(SimpleCast::max_int("uint256")?, U256::MAX.to_string()); /// assert_eq!(SimpleCast::max_int("int256")?, I256::MAX.to_string()); @@ -947,8 +979,8 @@ impl SimpleCast { /// # Example /// /// ``` + /// use alloy_primitives::{I256, U256}; /// use cast::SimpleCast; - /// use ethers_core::types::{I256, U256}; /// /// assert_eq!(SimpleCast::min_int("uint256")?, "0"); /// assert_eq!(SimpleCast::min_int("int256")?, I256::MIN.to_string()); @@ -1025,8 +1057,8 @@ impl SimpleCast { /// Converts fixed point number into specified number of decimals /// ``` + /// use alloy_primitives::U256; /// use cast::SimpleCast as Cast; - /// use ethers_core::types::U256; /// /// assert_eq!(Cast::from_fixed_point("10", "0")?, "10"); /// assert_eq!(Cast::from_fixed_point("1.0", "1")?, "10"); @@ -1050,8 +1082,8 @@ impl SimpleCast { /// # Example /// /// ``` + /// use alloy_primitives::U256; /// use cast::SimpleCast as Cast; - /// use ethers_core::types::U256; /// /// assert_eq!(Cast::to_fixed_point("10", "0")?, "10."); /// assert_eq!(Cast::to_fixed_point("10", "1")?, "1.0"); @@ -1833,7 +1865,7 @@ impl SimpleCast { /// /// # Example /// - /// ```ignore + /// ``` /// use cast::SimpleCast as Cast; /// /// # async fn foo() -> eyre::Result<()> { From ad04f2392a76220abe288b4af7d5230f817be9b3 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Wed, 24 Apr 2024 15:15:45 -0700 Subject: [PATCH 217/430] fix: change envExists return sig (#7774) * fix: change envExists return sig * update: cheats testdata --- 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 1c7781591..f769b2f06 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -3815,7 +3815,7 @@ "func": { "id": "envExists", "description": "Gets the environment variable `name` and returns true if it exists, else returns false.", - "declaration": "function envExists(string calldata name) external view returns (bool exists);", + "declaration": "function envExists(string calldata name) external view returns (bool result);", "visibility": "external", "mutability": "view", "signature": "envExists(string)", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 44ee2534f..e838298c7 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1503,7 +1503,7 @@ interface Vm { /// Gets the environment variable `name` and returns true if it exists, else returns false. #[cheatcode(group = Environment)] - function envExists(string calldata name) external view returns (bool exists); + function envExists(string calldata name) external view returns (bool result); /// Gets the environment variable `name` and parses it as `bool`. /// Reverts if the variable was not found or could not be parsed. diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 01d2bd431..854f57d23 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -186,7 +186,7 @@ interface Vm { function envBytes32(string calldata name, string calldata delim) external view returns (bytes32[] memory value); function envBytes(string calldata name) external view returns (bytes memory value); function envBytes(string calldata name, string calldata delim) external view returns (bytes[] memory value); - function envExists(string calldata name) external view returns (bool exists); + function envExists(string calldata name) external view returns (bool result); function envInt(string calldata name) external view returns (int256 value); function envInt(string calldata name, string calldata delim) external view returns (int256[] memory value); function envOr(string calldata name, bool defaultValue) external view returns (bool value); From e01038f35750b4a41b1eda5a4ea3da976027eee2 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Wed, 24 Apr 2024 17:05:14 -0700 Subject: [PATCH 218/430] Bump alloy to use `get_receipt` hotfix (#7772) * bump alloy to include `get_receipt` hotfix * refactor: alloy bump breaking changes * fix(cast): doctests * fix(forge/tests): can_broadcast_script_skipping_simulation * fix(forge): create tests --- Cargo.lock | 80 ++++++++++---------- Cargo.toml | 51 +++++++------ crates/anvil/core/src/eth/transaction/mod.rs | 20 ++--- crates/anvil/src/eth/api.rs | 7 +- crates/anvil/src/eth/backend/fork.rs | 24 +++--- crates/anvil/src/eth/backend/mem/mod.rs | 8 +- crates/anvil/src/eth/pool/mod.rs | 4 +- crates/anvil/tests/it/api.rs | 7 +- crates/anvil/tests/it/fork.rs | 80 ++++++++++++-------- crates/anvil/tests/it/genesis.rs | 3 +- crates/anvil/tests/it/transaction.rs | 4 +- crates/anvil/tests/it/wsapi.rs | 3 +- crates/cast/bin/cmd/access_list.rs | 6 +- crates/cast/bin/cmd/call.rs | 9 ++- crates/cast/bin/cmd/estimate.rs | 10 +-- crates/cast/bin/cmd/mktx.rs | 4 +- crates/cast/bin/cmd/send.rs | 9 ++- crates/cast/bin/cmd/storage.rs | 3 +- crates/cast/bin/main.rs | 4 +- crates/cast/bin/tx.rs | 16 ++-- crates/cast/src/lib.rs | 39 +++++++--- crates/cheatcodes/src/inspector.rs | 4 +- crates/common/src/fmt/ui.rs | 2 +- crates/common/src/transactions.rs | 2 +- crates/evm/core/src/fork/backend.rs | 6 +- crates/forge/bin/cmd/create.rs | 13 ++-- crates/script/src/broadcast.rs | 16 +++- crates/script/src/lib.rs | 10 ++- crates/script/src/simulate.rs | 5 +- crates/script/src/transaction.rs | 4 +- crates/test-utils/Cargo.toml | 1 + crates/test-utils/src/script.rs | 23 ++++-- crates/verify/src/bytecode.rs | 5 +- crates/wallets/src/wallet_signer.rs | 8 +- 34 files changed, 284 insertions(+), 206 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0082e8412..a4ab4783e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,7 +79,7 @@ dependencies = [ [[package]] name = "alloy-consensus" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" +source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" dependencies = [ "alloy-eips", "alloy-primitives", @@ -94,7 +94,7 @@ dependencies = [ [[package]] name = "alloy-contract" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" +source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -111,9 +111,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "872f239c15befa27cc4f0d3d82a70b3365c2d0202562bf906eb93b299fa31882" +checksum = "22ab339ca7b4ea9115f0578c941abc80a171edf8e5eadd01e6c4237b68db8083" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -133,7 +133,7 @@ dependencies = [ [[package]] name = "alloy-eips" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" +source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -147,7 +147,7 @@ dependencies = [ [[package]] name = "alloy-genesis" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" +source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" dependencies = [ "alloy-primitives", "alloy-serde", @@ -156,9 +156,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a35ddfd27576474322a5869e4c123e5f3e7b2177297c18e4e82ea501cb125b" +checksum = "44294729c145cf7ae65feab544b5b81fb2bb7e2fd060214842eb3989a1e9d882" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -169,7 +169,7 @@ dependencies = [ [[package]] name = "alloy-json-rpc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" +source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" dependencies = [ "alloy-primitives", "serde", @@ -181,7 +181,7 @@ dependencies = [ [[package]] name = "alloy-network" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" +source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" dependencies = [ "alloy-consensus", "alloy-eips", @@ -189,6 +189,7 @@ dependencies = [ "alloy-primitives", "alloy-rpc-types", "alloy-signer", + "alloy-sol-types", "async-trait", "futures-utils-wasm", "thiserror", @@ -196,9 +197,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99bbad0a6b588ef4aec1b5ddbbfdacd9ef04e00b979617765b03174318ee1f3a" +checksum = "50c715249705afa1e32be79dabfd35e2ef0f1cc02ad2cf48c9d1e20026ee637b" dependencies = [ "alloy-rlp", "arbitrary", @@ -224,7 +225,7 @@ dependencies = [ [[package]] name = "alloy-provider" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" +source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" dependencies = [ "alloy-eips", "alloy-json-rpc", @@ -255,7 +256,7 @@ dependencies = [ [[package]] name = "alloy-pubsub" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" +source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -295,7 +296,7 @@ dependencies = [ [[package]] name = "alloy-rpc-client" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" +source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -319,7 +320,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" +source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" dependencies = [ "alloy-consensus", "alloy-eips", @@ -337,7 +338,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" +source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -349,7 +350,7 @@ dependencies = [ [[package]] name = "alloy-serde" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" +source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" dependencies = [ "alloy-primitives", "serde", @@ -359,7 +360,7 @@ dependencies = [ [[package]] name = "alloy-signer" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" +source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -374,7 +375,7 @@ dependencies = [ [[package]] name = "alloy-signer-aws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" +source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" dependencies = [ "alloy-consensus", "alloy-network", @@ -391,7 +392,7 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" +source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" dependencies = [ "alloy-consensus", "alloy-network", @@ -408,7 +409,7 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" +source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" dependencies = [ "alloy-consensus", "alloy-network", @@ -424,7 +425,7 @@ dependencies = [ [[package]] name = "alloy-signer-wallet" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" +source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" dependencies = [ "alloy-consensus", "alloy-network", @@ -442,9 +443,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "452d929748ac948a10481fff4123affead32c553cf362841c5103dd508bdfc16" +checksum = "bef9a94a27345fb31e3fcb5f5e9f592bb4847493b07fa1e47dd9fde2222f2e28" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", @@ -461,9 +462,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df64e094f6d2099339f9e82b5b38440b159757b6920878f28316243f8166c8d1" +checksum = "c31fe73cd259527e24dc2dbfe64bc95e5ddfcd2b2731f670a11ff72b2be2c25b" dependencies = [ "alloy-json-abi", "const-hex", @@ -478,18 +479,18 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715f4d09a330cc181fc7c361b5c5c2766408fa59a0bac60349dcb7baabd404cc" +checksum = "8c8d6e74e4feeaa2bcfdecfd3da247ab53c67bd654ba1907270c32e02b142331" dependencies = [ "winnow 0.6.6", ] [[package]] name = "alloy-sol-types" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43bc2d6dfc2a19fd56644494479510f98b1ee929e04cf0d4aa45e98baa3e545b" +checksum = "afaffed78bfb17526375754931e045f96018aa810844b29c7aef823266dd4b4b" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -501,7 +502,7 @@ dependencies = [ [[package]] name = "alloy-transport" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" +source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" dependencies = [ "alloy-json-rpc", "base64 0.22.0", @@ -519,7 +520,7 @@ dependencies = [ [[package]] name = "alloy-transport-http" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" +source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -532,7 +533,7 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" +source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -550,7 +551,7 @@ dependencies = [ [[package]] name = "alloy-transport-ws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=7629f79#7629f79e3ffb6abd0be901a06deed2ab9f695e4e" +source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -4050,6 +4051,7 @@ version = "0.2.0" dependencies = [ "alloy-primitives", "alloy-provider", + "alloy-rpc-types", "eyre", "fd-lock 4.0.2", "foundry-common", @@ -5832,7 +5834,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate 2.0.0", "proc-macro2", "quote", "syn 2.0.60", @@ -6971,7 +6973,7 @@ dependencies = [ [[package]] name = "revm-inspectors" version = "0.1.0" -source = "git+https://github.com/paradigmxyz/evm-inspectors?rev=413b892#413b892dd936d117c52d47ba07d195b09a7f1216" +source = "git+https://github.com/paradigmxyz/evm-inspectors?rev=813d7e7#813d7e73a090d426935e63d9308bdd2c945a58c4" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -8071,9 +8073,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4497156948bd342b52038035a6fa514a89626e37af9d2c52a5e8d8ebcc7ee479" +checksum = "70aba06097b6eda3c15f6eebab8a6339e121475bcf08bbe6758807e716c372a1" dependencies = [ "paste", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index 4ef7befcb..08b4080bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -144,7 +144,7 @@ foundry-compilers = { version = "0.3.18", default-features = false } # no default features to avoid c-kzg revm = { version = "8", default-features = false } revm-primitives = { version = "3", default-features = false } -revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "413b892", features = [ +revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "813d7e7", features = [ "serde", ] } @@ -158,28 +158,28 @@ ethers-signers = { version = "2.0.14", default-features = false } ethers-middleware = { version = "2.0.14", default-features = false } ## alloy -alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } -alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } -alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } -alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } -alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } -alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } -alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } -alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } -alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } -alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } -alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } -alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } -alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } -alloy-signer-wallet = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } -alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } -alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } -alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } -alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } -alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } -alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } -alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "7629f79", default-features = false } -alloy-primitives = { version = "0.7.0", features = ["getrandom"] } +alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } +alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } +alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } +alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } +alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } +alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } +alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } +alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } +alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } +alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } +alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } +alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } +alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } +alloy-signer-wallet = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } +alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } +alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } +alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } +alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } +alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } +alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } +alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } +alloy-primitives = { version = "0.7.1", features = ["getrandom"] } alloy-dyn-abi = "0.7.0" alloy-json-abi = "0.7.0" alloy-sol-types = "0.7.0" @@ -192,7 +192,10 @@ solang-parser = "=0.3.3" ## misc arrayvec = "0.7" base64 = "0.22" -chrono = { version = "0.4", default-features = false, features = ["clock", "std"] } +chrono = { version = "0.4", default-features = false, features = [ + "clock", + "std", +] } color-eyre = "0.6" derive_more = "0.99" evm-disassembler = "0.5" diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 70f887f1e..261726636 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -58,12 +58,13 @@ pub fn transaction_request_to_typed( other, } = tx; + let to = if let Some(TxKind::Call(to)) = to { TxKind::Call(to) } else { TxKind::Create }; // Special case: OP-stack deposit tx if transaction_type == Some(126) { return Some(TypedTransactionRequest::Deposit(DepositTransactionRequest { from: from.unwrap_or_default(), source_hash: other.get_deserialized::("sourceHash")?.ok()?, - kind: to.into(), + kind: to, mint: other.get_deserialized::("mint")?.ok()?, value: value.unwrap_or_default(), gas_limit: gas.unwrap_or_default(), @@ -92,10 +93,7 @@ pub fn transaction_request_to_typed( gas_limit: gas.unwrap_or_default(), value: value.unwrap_or(U256::ZERO), input: input.into_input().unwrap_or_default(), - to: match to { - Some(to) => TxKind::Call(to), - None => TxKind::Create, - }, + to, chain_id: None, })) } @@ -108,10 +106,7 @@ pub fn transaction_request_to_typed( gas_limit: gas.unwrap_or_default(), value: value.unwrap_or(U256::ZERO), input: input.into_input().unwrap_or_default(), - to: match to { - Some(to) => TxKind::Call(to), - None => TxKind::Create, - }, + to, chain_id: 0, access_list: access_list.unwrap_or_default(), })) @@ -129,16 +124,13 @@ pub fn transaction_request_to_typed( gas_limit: gas.unwrap_or_default(), value: value.unwrap_or(U256::ZERO), input: input.into_input().unwrap_or_default(), - to: match to { - Some(to) => TxKind::Call(to), - None => TxKind::Create, - }, + to, chain_id: 0, access_list: access_list.unwrap_or_default(), })) } // EIP4844 - (Some(3), None, _, _, _, Some(_), Some(_), Some(sidecar), Some(to)) => { + (Some(3), None, _, _, _, Some(_), Some(_), Some(sidecar), TxKind::Call(to)) => { let tx = TxEip4844 { nonce: nonce.unwrap_or_default(), max_fee_per_gas: max_fee_per_gas.unwrap_or_default(), diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 1ed664a34..10689de90 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -2025,9 +2025,9 @@ impl EthApi { fn convert(tx: Arc) -> TxpoolInspectSummary { let tx = &tx.pending_transaction.transaction; let to = tx.to(); - let gas_price = U256::from(tx.gas_price()); + let gas_price = tx.gas_price(); let value = tx.value(); - let gas = U256::from(tx.gas_limit()); + let gas = tx.gas_limit(); TxpoolInspectSummary { to, value, gas, gas_price } } @@ -2188,9 +2188,10 @@ impl EthApi { { // If the request is a simple native token transfer we can optimize // We assume it's a transfer if we have no input data. + let to = if let Some(TxKind::Call(to)) = request.to { Some(to) } else { None }; let likely_transfer = request.input.clone().into_input().is_none(); if likely_transfer { - if let Some(to) = request.to { + if let Some(to) = to { if let Ok(target_code) = self.backend.get_code_with_state(&state, to) { if target_code.as_ref().is_empty() { return Ok(MIN_TRANSACTION_GAS); diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index 3727740cd..0f4a04f86 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -1,8 +1,8 @@ //! Support for forking off another client use crate::eth::{backend::db::Db, error::BlockchainError}; -use alloy_primitives::{Address, Bytes, StorageValue, B256, U256, U64}; -use alloy_provider::Provider; +use alloy_primitives::{Address, Bytes, StorageValue, B256, U256}; +use alloy_provider::{debug::DebugApi, Provider}; use alloy_rpc_types::{ request::TransactionRequest, AccessListWithGasUsed, Block, BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, EIP1186AccountProofResponse, FeeHistory, @@ -162,7 +162,7 @@ impl ClientFork { keys: Vec, block_number: Option, ) -> Result { - self.provider().get_proof(address, keys, block_number).await + self.provider().get_proof(address, keys, block_number.unwrap_or(BlockId::latest())).await } /// Sends `eth_call` @@ -172,7 +172,7 @@ impl ClientFork { block: Option, ) -> Result { let block = block.unwrap_or(BlockNumber::Latest); - let res = self.provider().call(request, Some(block.into())).await?; + let res = self.provider().call(request, block.into()).await?; Ok(res) } @@ -184,7 +184,7 @@ impl ClientFork { block: Option, ) -> Result { let block = block.unwrap_or(BlockNumber::Latest); - let res = self.provider().estimate_gas(request, Some(block.into())).await?; + let res = self.provider().estimate_gas(request, block.into()).await?; Ok(res) } @@ -195,7 +195,9 @@ impl ClientFork { request: &WithOtherFields, block: Option, ) -> Result { - self.provider().create_access_list(request, block.map(|b| b.into())).await + self.provider() + .create_access_list(request, block.unwrap_or(BlockNumber::Latest).into()) + .await } pub async fn storage_at( @@ -204,7 +206,9 @@ impl ClientFork { index: U256, number: Option, ) -> Result { - self.provider().get_storage_at(address, index, number.map(Into::into)).await + self.provider() + .get_storage_at(address, index, number.unwrap_or(BlockNumber::Latest).into()) + .await } pub async fn logs(&self, filter: &Filter) -> Result, TransportError> { @@ -245,12 +249,12 @@ impl ClientFork { blocknumber: u64, ) -> Result { trace!(target: "backend::fork", "get_balance={:?}", address); - self.provider().get_balance(address, Some(blocknumber.into())).await + self.provider().get_balance(address, blocknumber.into()).await } pub async fn get_nonce(&self, address: Address, block: u64) -> Result { trace!(target: "backend::fork", "get_nonce={:?}", address); - self.provider().get_transaction_count(address, Some(block.into())).await + self.provider().get_transaction_count(address, block.into()).await } pub async fn transaction_by_block_number_and_index( @@ -532,7 +536,7 @@ impl ClientFork { let mut uncles = Vec::with_capacity(block.uncles.len()); for (uncle_idx, _) in block.uncles.iter().enumerate() { let uncle = - match self.provider().get_uncle(block_number.into(), U64::from(uncle_idx)).await? { + match self.provider().get_uncle(block_number.into(), uncle_idx as u64).await? { Some(u) => u, None => return Ok(None), }; diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 3b5fdc73f..1e82700ed 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -35,7 +35,7 @@ use crate::{ NodeConfig, PrecompileFactory, }; use alloy_consensus::{Header, Receipt, ReceiptWithBloom}; -use alloy_primitives::{keccak256, Address, Bytes, TxHash, B256, U256, U64}; +use alloy_primitives::{keccak256, Address, Bytes, TxHash, TxKind, B256, U256, U64}; use alloy_rpc_types::{ request::TransactionRequest, serde_helpers::JsonStorageKey, state::StateOverride, AccessList, Block as AlloyBlock, BlockId, BlockNumberOrTag as BlockNumber, @@ -1120,7 +1120,7 @@ impl Backend { let gas_price = gas_price.or(max_fee_per_gas).unwrap_or_else(|| self.gas_price()); let caller = from.unwrap_or_default(); - + let to = if let Some(TxKind::Call(addr)) = to { Some(addr) } else { None }; env.tx = TxEnv { caller, gas_limit: gas_limit as u64, @@ -1223,7 +1223,7 @@ impl Backend { D: DatabaseRef, { let from = request.from.unwrap_or_default(); - let to = if let Some(to) = request.to { + let to = if let Some(TxKind::Call(to)) = request.to { to } else { let nonce = state.basic_ref(from)?.unwrap_or_default().nonce; @@ -2038,7 +2038,7 @@ impl Backend { let inner = TransactionReceipt { inner, transaction_hash: info.transaction_hash, - transaction_index: info.transaction_index, + transaction_index: Some(info.transaction_index), block_number: Some(block.header.number), gas_used: info.gas_used, contract_address: info.contract_address, diff --git a/crates/anvil/src/eth/pool/mod.rs b/crates/anvil/src/eth/pool/mod.rs index 674885500..ace226fe5 100644 --- a/crates/anvil/src/eth/pool/mod.rs +++ b/crates/anvil/src/eth/pool/mod.rs @@ -75,8 +75,8 @@ impl Pool { /// Returns the number of tx that are ready and queued for further execution pub fn txpool_status(&self) -> TxpoolStatus { // Note: naming differs here compared to geth's `TxpoolStatus` - let pending = U64::from(self.ready_transactions().count()); - let queued = U64::from(self.inner.read().pending_transactions.len()); + let pending = self.ready_transactions().count() as u64; + let queued = self.inner.read().pending_transactions.len() as u64; TxpoolStatus { pending, queued } } diff --git a/crates/anvil/tests/it/api.rs b/crates/anvil/tests/it/api.rs index acf9a0f98..1afa76df1 100644 --- a/crates/anvil/tests/it/api.rs +++ b/crates/anvil/tests/it/api.rs @@ -4,7 +4,8 @@ use crate::{ abi::{MulticallContract, SimpleStorage}, utils::ethers_http_provider, }; -use alloy_primitives::{Address as rAddress, B256, U256 as rU256}; +use alloy_eips::BlockId; +use alloy_primitives::{Address as rAddress, TxKind, B256, U256 as rU256}; use alloy_provider::Provider; use alloy_rpc_types::{ request::{TransactionInput as CallInput, TransactionRequest as CallRequest}, @@ -45,7 +46,7 @@ async fn can_dev_get_balance() { let genesis_balance = handle.genesis_balance(); for acc in handle.genesis_accounts() { - let balance = provider.get_balance(acc, None).await.unwrap(); + let balance = provider.get_balance(acc, BlockId::latest()).await.unwrap(); assert_eq!(balance, genesis_balance); } } @@ -246,7 +247,7 @@ where .call( WithOtherFields::new(CallRequest { input: CallInput::maybe_input(call.tx.data().cloned().map(|b| b.0.into())), - to: Some(to.to_alloy()), + to: Some(TxKind::Call(to.to_alloy())), ..Default::default() }), None, diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index 717e0c578..f22d22a91 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -4,11 +4,11 @@ use crate::{ abi::*, utils::{self, ethers_http_provider}, }; -use alloy_primitives::{address, U256 as rU256}; +use alloy_primitives::{address, TxKind, U256 as rU256}; use alloy_provider::Provider as AlloyProvider; use alloy_rpc_types::{ request::{TransactionInput, TransactionRequest as CallRequest}, - BlockNumberOrTag, WithOtherFields, + BlockId, BlockNumberOrTag, WithOtherFields, }; use anvil::{eth::EthApi, spawn, NodeConfig, NodeHandle}; use anvil_core::types::Forking; @@ -273,8 +273,8 @@ async fn test_fork_snapshotting() { let to = accounts[1].address(); let block_number = provider.get_block_number().await.unwrap(); - let initial_nonce = provider.get_transaction_count(from, None).await.unwrap(); - let balance_before = provider.get_balance(to, None).await.unwrap(); + let initial_nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); + let balance_before = provider.get_balance(to, BlockId::latest()).await.unwrap(); let amount = handle.genesis_balance().checked_div(rU256::from(2u64)).unwrap(); let provider = ethers_http_provider(&handle.http_endpoint()); @@ -287,18 +287,18 @@ async fn test_fork_snapshotting() { let provider = handle.http_provider(); - let nonce = provider.get_transaction_count(from, None).await.unwrap(); + let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); assert_eq!(nonce, initial_nonce + 1); - let to_balance = provider.get_balance(to, None).await.unwrap(); + let to_balance = provider.get_balance(to, BlockId::latest()).await.unwrap(); assert_eq!(balance_before.saturating_add(amount), to_balance); assert!(api.evm_revert(snapshot).await.unwrap()); - let nonce = provider.get_transaction_count(from, None).await.unwrap(); + let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); assert_eq!(nonce, initial_nonce); - let balance = provider.get_balance(from, None).await.unwrap(); + let balance = provider.get_balance(from, BlockId::latest()).await.unwrap(); assert_eq!(balance, handle.genesis_balance()); - let balance = provider.get_balance(to, None).await.unwrap(); + let balance = provider.get_balance(to, BlockId::latest()).await.unwrap(); assert_eq!(balance, handle.genesis_balance()); assert_eq!(block_number, provider.get_block_number().await.unwrap()); } @@ -315,8 +315,8 @@ async fn test_fork_snapshotting_repeated() { let to = accounts[1].address(); let block_number = provider.get_block_number().await.unwrap(); - let initial_nonce = provider.get_transaction_count(from, None).await.unwrap(); - let balance_before = provider.get_balance(to, None).await.unwrap(); + let initial_nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); + let balance_before = provider.get_balance(to, BlockId::latest()).await.unwrap(); let amount = handle.genesis_balance().checked_div(rU256::from(92u64)).unwrap(); let tx = TransactionRequest::new() @@ -326,20 +326,20 @@ async fn test_fork_snapshotting_repeated() { let tx_provider = ethers_http_provider(&handle.http_endpoint()); let _ = tx_provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); - let nonce = provider.get_transaction_count(from, None).await.unwrap(); + let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); assert_eq!(nonce, initial_nonce + 1); - let to_balance = provider.get_balance(to, None).await.unwrap(); + let to_balance = provider.get_balance(to, BlockId::latest()).await.unwrap(); assert_eq!(balance_before.saturating_add(amount), to_balance); let _second_snapshot = api.evm_snapshot().await.unwrap(); assert!(api.evm_revert(snapshot).await.unwrap()); - let nonce = provider.get_transaction_count(from, None).await.unwrap(); + let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); assert_eq!(nonce, initial_nonce); - let balance = provider.get_balance(from, None).await.unwrap(); + let balance = provider.get_balance(from, BlockId::latest()).await.unwrap(); assert_eq!(balance, handle.genesis_balance()); - let balance = provider.get_balance(to, None).await.unwrap(); + let balance = provider.get_balance(to, BlockId::latest()).await.unwrap(); assert_eq!(balance, handle.genesis_balance()); assert_eq!(block_number, provider.get_block_number().await.unwrap()); @@ -366,8 +366,8 @@ async fn test_fork_snapshotting_blocks() { let to = accounts[1].address(); let block_number = provider.get_block_number().await.unwrap(); - let initial_nonce = provider.get_transaction_count(from, None).await.unwrap(); - let balance_before = provider.get_balance(to, None).await.unwrap(); + let initial_nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); + let balance_before = provider.get_balance(to, BlockId::latest()).await.unwrap(); let amount = handle.genesis_balance().checked_div(rU256::from(2u64)).unwrap(); // send the transaction @@ -380,26 +380,29 @@ async fn test_fork_snapshotting_blocks() { let block_number_after = provider.get_block_number().await.unwrap(); assert_eq!(block_number_after, block_number + 1); - let nonce = provider.get_transaction_count(from, None).await.unwrap(); + let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); assert_eq!(nonce, initial_nonce + 1); - let to_balance = provider.get_balance(to, None).await.unwrap(); + let to_balance = provider.get_balance(to, BlockId::latest()).await.unwrap(); assert_eq!(balance_before.saturating_add(amount), to_balance); // revert snapshot assert!(api.evm_revert(snapshot).await.unwrap()); - assert_eq!(initial_nonce, provider.get_transaction_count(from, None).await.unwrap()); + assert_eq!( + initial_nonce, + provider.get_transaction_count(from, BlockId::latest()).await.unwrap() + ); let block_number_after = provider.get_block_number().await.unwrap(); assert_eq!(block_number_after, block_number); // repeat transaction let _ = tx_provider.send_transaction(tx.clone(), None).await.unwrap().await.unwrap().unwrap(); - let nonce = provider.get_transaction_count(from, None).await.unwrap(); + let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); assert_eq!(nonce, initial_nonce + 1); // revert again: nothing to revert since snapshot gone assert!(!api.evm_revert(snapshot).await.unwrap()); - let nonce = provider.get_transaction_count(from, None).await.unwrap(); + let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); assert_eq!(nonce, initial_nonce + 1); let block_number_after = provider.get_block_number().await.unwrap(); assert_eq!(block_number_after, block_number + 1); @@ -469,31 +472,46 @@ async fn can_reset_properly() { let origin_nonce = 1u64; origin_api.anvil_set_nonce(account, rU256::from(origin_nonce)).await.unwrap(); - assert_eq!(origin_nonce, origin_provider.get_transaction_count(account, None).await.unwrap()); + assert_eq!( + origin_nonce, + origin_provider.get_transaction_count(account, BlockId::latest()).await.unwrap() + ); let (fork_api, fork_handle) = spawn(NodeConfig::test().with_eth_rpc_url(Some(origin_handle.http_endpoint()))).await; let fork_provider = fork_handle.http_provider(); let fork_tx_provider = ethers_http_provider(&fork_handle.http_endpoint()); - assert_eq!(origin_nonce, fork_provider.get_transaction_count(account, None).await.unwrap()); + assert_eq!( + origin_nonce, + fork_provider.get_transaction_count(account, BlockId::latest()).await.unwrap() + ); let to = Address::random(); - let to_balance = fork_provider.get_balance(to.to_alloy(), None).await.unwrap(); + let to_balance = fork_provider.get_balance(to.to_alloy(), BlockId::latest()).await.unwrap(); let tx = TransactionRequest::new().from(account.to_ethers()).to(to).value(1337u64); let tx = fork_tx_provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); // nonce incremented by 1 - assert_eq!(origin_nonce + 1, fork_provider.get_transaction_count(account, None).await.unwrap()); + assert_eq!( + origin_nonce + 1, + fork_provider.get_transaction_count(account, BlockId::latest()).await.unwrap() + ); // resetting to origin state fork_api.anvil_reset(Some(Forking::default())).await.unwrap(); // nonce reset to origin - assert_eq!(origin_nonce, fork_provider.get_transaction_count(account, None).await.unwrap()); + assert_eq!( + origin_nonce, + fork_provider.get_transaction_count(account, BlockId::latest()).await.unwrap() + ); // balance is reset - assert_eq!(to_balance, fork_provider.get_balance(to.to_alloy(), None).await.unwrap()); + assert_eq!( + to_balance, + fork_provider.get_balance(to.to_alloy(), BlockId::latest()).await.unwrap() + ); // tx does not exist anymore assert!(fork_tx_provider.get_transaction(tx.transaction_hash).await.is_err()) @@ -790,7 +808,7 @@ async fn test_fork_call() { let res1 = api .call( WithOtherFields::new(CallRequest { - to: Some(to.to_alloy()), + to: Some(TxKind::Call(to.to_alloy())), input: input.to_alloy().into(), ..Default::default() }), @@ -1179,7 +1197,7 @@ async fn test_fork_execution_reverted() { let resp = api .call( WithOtherFields::new(CallRequest { - to: Some(address!("Fd6CC4F251eaE6d02f9F7B41D1e80464D3d2F377")), + to: Some(TxKind::Call(address!("Fd6CC4F251eaE6d02f9F7B41D1e80464D3d2F377"))), input: TransactionInput::new("0x8f283b3c".as_bytes().into()), ..Default::default() }), diff --git a/crates/anvil/tests/it/genesis.rs b/crates/anvil/tests/it/genesis.rs index 0b243db3e..c8157e160 100644 --- a/crates/anvil/tests/it/genesis.rs +++ b/crates/anvil/tests/it/genesis.rs @@ -2,6 +2,7 @@ use std::str::FromStr; +use alloy_eips::BlockId; use alloy_genesis::Genesis; use alloy_primitives::{Address, U256}; use alloy_provider::Provider; @@ -44,7 +45,7 @@ async fn can_apply_genesis() { assert_eq!(provider.get_chain_id().await.unwrap(), 19763u64); let addr: Address = Address::from_str("71562b71999873db5b286df957af199ec94617f7").unwrap(); - let balance = provider.get_balance(addr, None).await.unwrap(); + let balance = provider.get_balance(addr, BlockId::latest()).await.unwrap(); let expected: U256 = U256::from_str_radix("ffffffffffffffffffffffffff", 16).unwrap(); assert_eq!(balance, expected); diff --git a/crates/anvil/tests/it/transaction.rs b/crates/anvil/tests/it/transaction.rs index 46c059374..6dfae6a8d 100644 --- a/crates/anvil/tests/it/transaction.rs +++ b/crates/anvil/tests/it/transaction.rs @@ -963,7 +963,7 @@ async fn estimates_gas_on_pending_by_default() { let tx = AlloyTransactionRequest::default() .from(recipient.to_alloy()) - .to(Some(sender.to_alloy())) + .to(sender.to_alloy()) .value(rU256::from(1e10)) .input(Bytes::from(vec![0x42]).into()); api.estimate_gas(WithOtherFields::new(tx), None, None).await.unwrap(); @@ -979,7 +979,7 @@ async fn test_estimate_gas() { let tx = AlloyTransactionRequest::default() .from(recipient.to_alloy()) - .to(Some(sender.to_alloy())) + .to(sender.to_alloy()) .value(rU256::from(1e10)) .input(Bytes::from(vec![0x42]).into()); // Expect the gas estimation to fail due to insufficient funds. diff --git a/crates/anvil/tests/it/wsapi.rs b/crates/anvil/tests/it/wsapi.rs index ce0c4d6a4..58bad12eb 100644 --- a/crates/anvil/tests/it/wsapi.rs +++ b/crates/anvil/tests/it/wsapi.rs @@ -1,5 +1,6 @@ //! general eth api tests with websocket provider +use alloy_eips::BlockId; use alloy_provider::Provider; use anvil::{spawn, NodeConfig}; use ethers::types::U256; @@ -24,7 +25,7 @@ async fn can_dev_get_balance_ws() { let genesis_balance = handle.genesis_balance(); for acc in handle.genesis_accounts() { - let balance = provider.get_balance(acc, None).await.unwrap(); + let balance = provider.get_balance(acc, BlockId::latest()).await.unwrap(); assert_eq!(balance, genesis_balance); } } diff --git a/crates/cast/bin/cmd/access_list.rs b/crates/cast/bin/cmd/access_list.rs index ed572628d..d344017f6 100644 --- a/crates/cast/bin/cmd/access_list.rs +++ b/crates/cast/bin/cmd/access_list.rs @@ -1,5 +1,5 @@ use alloy_network::{AnyNetwork, TransactionBuilder}; -use alloy_primitives::Address; +use alloy_primitives::{Address, Bytes}; use alloy_provider::Provider; use alloy_rpc_types::{BlockId, TransactionRequest, WithOtherFields}; use alloy_transport::Transport; @@ -105,7 +105,7 @@ async fn access_list, T: Transport + Clone>( to_json: bool, ) -> Result<()> { let mut req = WithOtherFields::::default() - .with_to(to.into()) + .with_to(to.unwrap_or_default()) .with_from(from) .with_value(tx.value.unwrap_or_default()) .with_chain_id(chain.id()); @@ -127,7 +127,7 @@ async fn access_list, T: Transport + Clone>( Vec::new() }; - req.set_input(data.into()); + req.set_input::(data.into()); let cast = Cast::new(&provider); diff --git a/crates/cast/bin/cmd/call.rs b/crates/cast/bin/cmd/call.rs index eeb09d5c0..32b4ec794 100644 --- a/crates/cast/bin/cmd/call.rs +++ b/crates/cast/bin/cmd/call.rs @@ -1,5 +1,5 @@ use alloy_network::TransactionBuilder; -use alloy_primitives::U256; +use alloy_primitives::{Bytes, TxKind, U256}; use alloy_rpc_types::{BlockId, TransactionRequest, WithOtherFields}; use cast::Cast; use clap::Parser; @@ -122,7 +122,7 @@ impl CallArgs { }; let mut req = WithOtherFields::::default() - .with_to(to.into()) + .with_to(to.unwrap_or_default()) .with_from(sender) .with_value(tx.value.unwrap_or_default()); @@ -209,9 +209,10 @@ impl CallArgs { let mut executor = TracingExecutor::new(env, fork, evm_version, debug); + let to = if let Some(TxKind::Call(to)) = req.to { Some(to) } else { None }; let trace = TraceResult::from(executor.call_raw_committing( sender, - req.to.expect("an address to be here"), + to.expect("an address to be here"), data.into(), req.value.unwrap_or_default(), )?); @@ -225,7 +226,7 @@ impl CallArgs { } }; - req.set_input(data.into()); + req.set_input::(data.into()); println!("{}", Cast::new(provider).call(&req, func.as_ref(), block).await?); diff --git a/crates/cast/bin/cmd/estimate.rs b/crates/cast/bin/cmd/estimate.rs index 55149937b..6ee33ed26 100644 --- a/crates/cast/bin/cmd/estimate.rs +++ b/crates/cast/bin/cmd/estimate.rs @@ -1,7 +1,7 @@ use alloy_network::TransactionBuilder; -use alloy_primitives::U256; +use alloy_primitives::{Bytes, U256}; use alloy_provider::Provider; -use alloy_rpc_types::{TransactionRequest, WithOtherFields}; +use alloy_rpc_types::{BlockId, TransactionRequest, WithOtherFields}; use clap::Parser; use eyre::Result; use foundry_cli::{ @@ -94,7 +94,7 @@ impl EstimateArgs { }; let mut req = WithOtherFields::::default() - .with_to(to.into()) + .with_to(to.unwrap_or_default()) .with_from(from) .with_value(value.unwrap_or_default()); @@ -121,9 +121,9 @@ impl EstimateArgs { } }; - req.set_input(data.into()); + req.set_input::(data.into()); - let gas = provider.estimate_gas(&req, None).await?; + let gas = provider.estimate_gas(&req, BlockId::latest()).await?; println!("{gas}"); Ok(()) } diff --git a/crates/cast/bin/cmd/mktx.rs b/crates/cast/bin/cmd/mktx.rs index def6d46f4..e26115da3 100644 --- a/crates/cast/bin/cmd/mktx.rs +++ b/crates/cast/bin/cmd/mktx.rs @@ -2,6 +2,7 @@ use crate::tx; use alloy_network::{eip2718::Encodable2718, EthereumSigner, TransactionBuilder}; use alloy_primitives::U64; use alloy_provider::Provider; +use alloy_rpc_types::BlockId; use alloy_signer::Signer; use clap::Parser; use eyre::Result; @@ -89,7 +90,8 @@ impl MakeTxArgs { tx::validate_from_address(eth.wallet.from, from)?; if resend { - tx.nonce = Some(U64::from(provider.get_transaction_count(from, None).await?)); + tx.nonce = + Some(U64::from(provider.get_transaction_count(from, BlockId::latest()).await?)); } let provider = get_provider(&config)?; diff --git a/crates/cast/bin/cmd/send.rs b/crates/cast/bin/cmd/send.rs index b24de5fba..7799f5226 100644 --- a/crates/cast/bin/cmd/send.rs +++ b/crates/cast/bin/cmd/send.rs @@ -2,6 +2,7 @@ use crate::tx; use alloy_network::{AnyNetwork, EthereumSigner}; use alloy_primitives::{Address, U64}; use alloy_provider::{Provider, ProviderBuilder}; +use alloy_rpc_types::BlockId; use alloy_signer::Signer; use alloy_transport::Transport; use cast::Cast; @@ -142,8 +143,9 @@ impl SendTxArgs { } if resend { - tx.nonce = - Some(U64::from(provider.get_transaction_count(config.sender, None).await?)); + tx.nonce = Some(U64::from( + provider.get_transaction_count(config.sender, BlockId::latest()).await?, + )); } cast_send( @@ -173,7 +175,8 @@ impl SendTxArgs { tx::validate_from_address(eth.wallet.from, from)?; if resend { - tx.nonce = Some(U64::from(provider.get_transaction_count(from, None).await?)); + tx.nonce = + Some(U64::from(provider.get_transaction_count(from, BlockId::latest()).await?)); } let signer = EthereumSigner::from(signer); diff --git a/crates/cast/bin/cmd/storage.rs b/crates/cast/bin/cmd/storage.rs index 0bf041b8a..18411165e 100644 --- a/crates/cast/bin/cmd/storage.rs +++ b/crates/cast/bin/cmd/storage.rs @@ -230,7 +230,8 @@ async fn fetch_storage_slots, T: Transport + Clone>( ) -> Result> { let requests = layout.storage.iter().map(|storage_slot| async { let slot = B256::from(U256::from_str(&storage_slot.slot)?); - let raw_slot_value = provider.get_storage_at(address, slot.into(), block).await?; + let raw_slot_value = + provider.get_storage_at(address, slot.into(), block.unwrap_or_default()).await?; let value = StorageValue { slot, raw_slot_value: raw_slot_value.into() }; diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index 565cd6eda..e1b411864 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -346,7 +346,9 @@ async fn main() -> Result<()> { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; let address = address.resolve(&provider).await?; - let value = provider.get_proof(address, slots.into_iter().collect(), block).await?; + let value = provider + .get_proof(address, slots.into_iter().collect(), block.unwrap_or(BlockId::latest())) + .await?; println!("{}", serde_json::to_string(&value)?); } CastSubcommand::Rpc(cmd) => cmd.run().await?, diff --git a/crates/cast/bin/tx.rs b/crates/cast/bin/tx.rs index 98f7bf67e..184da16d6 100644 --- a/crates/cast/bin/tx.rs +++ b/crates/cast/bin/tx.rs @@ -1,8 +1,8 @@ use alloy_json_abi::Function; use alloy_network::{AnyNetwork, TransactionBuilder}; -use alloy_primitives::{Address, U256}; +use alloy_primitives::{Address, Bytes, U256}; use alloy_provider::Provider; -use alloy_rpc_types::{TransactionRequest, WithOtherFields}; +use alloy_rpc_types::{BlockId, TransactionRequest, WithOtherFields}; use alloy_transport::Transport; use eyre::Result; use foundry_cli::{opts::TransactionOpts, utils::parse_function_args}; @@ -56,10 +56,12 @@ pub async fn build_tx< let chain = chain.into(); let from = from.into().resolve(provider).await?; - let to = if let Some(to) = to { Some(to.into().resolve(provider).await?) } else { None }; + // TODO: Possible bug here? + let to: Option
= + if let Some(to) = to { Some(to.into().resolve(provider).await?) } else { None }; let mut req = WithOtherFields::new(TransactionRequest::default()) - .with_to(to.into()) + .with_to(to.unwrap_or_default()) .with_from(from) .with_value(tx.value.unwrap_or_default()) .with_chain_id(chain.id()); @@ -67,7 +69,7 @@ pub async fn build_tx< req.set_nonce(if let Some(nonce) = tx.nonce { nonce.to() } else { - provider.get_transaction_count(from, None).await? + provider.get_transaction_count(from, BlockId::latest()).await? }); if tx.legacy || chain.is_legacy() { @@ -110,12 +112,12 @@ pub async fn build_tx< (Vec::new(), None) }; - req.set_input(data.into()); + req.set_input::(data.into()); req.set_gas_limit(if let Some(gas_limit) = tx.gas_limit { gas_limit.to() } else { - provider.estimate_gas(&req, None).await? + provider.estimate_gas(&req, BlockId::latest()).await? }); Ok((req, func)) diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index d63bbc33e..1f26acbee 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -4,7 +4,7 @@ use alloy_json_abi::{ContractObject, Function}; use alloy_network::AnyNetwork; use alloy_primitives::{ utils::{keccak256, ParseUnits, Unit}, - Address, Keccak256, TxHash, B256, I256, U256, + Address, Keccak256, TxHash, TxKind, B256, I256, U256, }; use alloy_provider::{ network::eip2718::{Decodable2718, Encodable2718}, @@ -106,7 +106,7 @@ where /// let to = Address::from_str("0xB3C95ff08316fb2F2e3E52Ee82F8e7b605Aa1304")?; /// let greeting = greetingCall { i: U256::from(5) }.abi_encode(); /// let bytes = Bytes::from_iter(greeting.iter()); - /// let tx = TransactionRequest::default().to(Some(to)).input(bytes.into()); + /// let tx = TransactionRequest::default().to(to).input(bytes.into()); /// let tx = WithOtherFields::new(tx); /// let cast = Cast::new(alloy_provider); /// let data = cast.call(&tx, None, None).await?; @@ -120,7 +120,7 @@ where func: Option<&Function>, block: Option, ) -> Result { - let res = self.provider.call(req, block).await?; + let res = self.provider.call(req, block.unwrap_or(BlockId::latest())).await?; let mut decoded = vec![]; @@ -132,7 +132,7 @@ where // ensure the address is a contract if res.is_empty() { // check that the recipient is a contract that can be called - if let Some(addr) = req.to { + if let Some(TxKind::Call(addr)) = req.to { if let Ok(code) = self.provider.get_code_at(addr, block.unwrap_or_default()).await { @@ -140,6 +140,10 @@ where eyre::bail!("contract {addr:?} does not have any code") } } + } else if Some(TxKind::Create) == req.to { + eyre::bail!("tx req is a contract deployment"); + } else { + eyre::bail!("recipient is None"); } } return Err(err).wrap_err( @@ -179,7 +183,7 @@ where /// let to = Address::from_str("0xB3C95ff08316fb2F2e3E52Ee82F8e7b605Aa1304")?; /// let greeting = greetingCall { i: U256::from(5) }.abi_encode(); /// let bytes = Bytes::from_iter(greeting.iter()); - /// let tx = TransactionRequest::default().to(Some(to)).input(bytes.into()); + /// let tx = TransactionRequest::default().to(to).input(bytes.into()); /// let tx = WithOtherFields::new(tx); /// let cast = Cast::new(&provider); /// let access_list = cast.access_list(&tx, None, false).await?; @@ -193,7 +197,8 @@ where block: Option, to_json: bool, ) -> Result { - let access_list = self.provider.create_access_list(req, block).await?; + let access_list = + self.provider.create_access_list(req, block.unwrap_or(BlockId::latest())).await?; let res = if to_json { serde_json::to_string(&access_list)? } else { @@ -215,7 +220,7 @@ where } pub async fn balance(&self, who: Address, block: Option) -> Result { - Ok(self.provider.get_balance(who, block).await?) + Ok(self.provider.get_balance(who, block.unwrap_or(BlockId::latest())).await?) } /// Sends a transaction to the specified address @@ -243,7 +248,7 @@ where /// let gas = U256::from_str("200000").unwrap(); /// let value = U256::from_str("1").unwrap(); /// let nonce = U256::from_str("1").unwrap(); - /// let tx = TransactionRequest::default().to(Some(to)).input(bytes.into()).from(from); + /// let tx = TransactionRequest::default().to(to).input(bytes.into()).from(from); /// let tx = WithOtherFields::new(tx); /// let cast = Cast::new(provider); /// let data = cast.send(tx).await?; @@ -467,7 +472,7 @@ where /// # } /// ``` pub async fn nonce(&self, who: Address, block: Option) -> Result { - Ok(self.provider.get_transaction_count(who, block).await?) + Ok(self.provider.get_transaction_count(who, block.unwrap_or(BlockId::latest())).await?) } /// # Example @@ -491,7 +496,10 @@ where pub async fn implementation(&self, who: Address, block: Option) -> Result { let slot = B256::from_str("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc")?; - let value = self.provider.get_storage_at(who, slot.into(), block).await?; + let value = self + .provider + .get_storage_at(who, slot.into(), block.unwrap_or(BlockId::latest())) + .await?; let addr = Address::from_word(value.into()); Ok(format!("{addr:?}")) } @@ -517,7 +525,10 @@ where pub async fn admin(&self, who: Address, block: Option) -> Result { let slot = B256::from_str("0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103")?; - let value = self.provider.get_storage_at(who, slot.into(), block).await?; + let value = self + .provider + .get_storage_at(who, slot.into(), block.unwrap_or(BlockId::latest())) + .await?; let addr = Address::from_word(value.into()); Ok(format!("{addr:?}")) } @@ -756,7 +767,11 @@ where ) -> Result { Ok(format!( "{:?}", - B256::from(self.provider.get_storage_at(from, slot.into(), block).await?) + B256::from( + self.provider + .get_storage_at(from, slot.into(), block.unwrap_or(BlockId::latest())) + .await? + ) )) } diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 64d2e70a4..68418e282 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -15,7 +15,7 @@ use crate::{ CheatsConfig, CheatsCtxt, DynCheatcode, Error, Result, Vm, Vm::AccountAccess, }; -use alloy_primitives::{Address, Bytes, Log, B256, U256}; +use alloy_primitives::{Address, Bytes, Log, TxKind, B256, U256}; use alloy_rpc_types::request::{TransactionInput, TransactionRequest}; use alloy_sol_types::{SolInterface, SolValue}; use foundry_common::{evm::Breakpoints, provider::alloy::RpcUrl, SELECTOR_LEN}; @@ -915,7 +915,7 @@ impl Inspector for Cheatcodes { rpc: ecx.db.active_fork_url(), transaction: TransactionRequest { from: Some(broadcast.new_origin), - to: Some(call.contract), + to: Some(TxKind::Call(call.contract)), value: Some(call.transfer.value), input: TransactionInput::new(call.input.clone()), nonce: Some(account.info.nonce), diff --git a/crates/common/src/fmt/ui.rs b/crates/common/src/fmt/ui.rs index 1607b6d10..26db7038c 100644 --- a/crates/common/src/fmt/ui.rs +++ b/crates/common/src/fmt/ui.rs @@ -453,7 +453,7 @@ pub fn get_pretty_tx_receipt_attr( } "transactionHash" | "transaction_hash" => Some(receipt.receipt.transaction_hash.pretty()), "transactionIndex" | "transaction_index" => { - Some(receipt.receipt.transaction_index.to_string()) + Some(receipt.receipt.transaction_index.pretty()) } "type" | "transaction_type" => Some(receipt.receipt.inner.inner.r#type.to_string()), "revertReason" | "revert_reason" => Some(receipt.revert_reason.pretty()), diff --git a/crates/common/src/transactions.rs b/crates/common/src/transactions.rs index cf3f5a89a..1f7d6228e 100644 --- a/crates/common/src/transactions.rs +++ b/crates/common/src/transactions.rs @@ -50,7 +50,7 @@ impl TransactionReceiptWithRevertReason { match provider .call( &WithOtherFields::new(transaction.inner.into()), - Some(BlockId::Hash(block_hash.into())), + BlockId::Hash(block_hash.into()), ) .await { diff --git a/crates/evm/core/src/fork/backend.rs b/crates/evm/core/src/fork/backend.rs index f6da76fc5..5daa4e96c 100644 --- a/crates/evm/core/src/fork/backend.rs +++ b/crates/evm/core/src/fork/backend.rs @@ -187,7 +187,7 @@ where trace!(target: "backendhandler", %address, %idx, "preparing storage request"); entry.insert(vec![listener]); let provider = self.provider.clone(); - let block_id = self.block_id; + let block_id = self.block_id.unwrap_or(BlockId::latest()); let fut = Box::pin(async move { let storage = provider.get_storage_at(address, idx, block_id).await.map_err(Into::into); @@ -202,11 +202,11 @@ where fn get_account_req(&self, address: Address) -> ProviderRequest { trace!(target: "backendhandler", "preparing account request, address={:?}", address); let provider = self.provider.clone(); - let block_id = self.block_id; + let block_id = self.block_id.unwrap_or(BlockId::latest()); let fut = Box::pin(async move { let balance = provider.get_balance(address, block_id); let nonce = provider.get_transaction_count(address, block_id); - let code = provider.get_code_at(address, block_id.unwrap_or_default()); + let code = provider.get_code_at(address, block_id); let resp = tokio::try_join!(balance, nonce, code).map_err(Into::into); (resp, address) }); diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index 68b303362..dfa1c003e 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -4,7 +4,7 @@ use alloy_json_abi::{Constructor, JsonAbi}; use alloy_network::{AnyNetwork, EthereumSigner, TransactionBuilder}; use alloy_primitives::{Address, Bytes}; use alloy_provider::{Provider, ProviderBuilder}; -use alloy_rpc_types::{AnyTransactionReceipt, TransactionRequest, WithOtherFields}; +use alloy_rpc_types::{AnyTransactionReceipt, BlockId, TransactionRequest, WithOtherFields}; use alloy_signer::Signer; use alloy_transport::{Transport, TransportError}; use clap::{Parser, ValueHint}; @@ -233,17 +233,20 @@ impl CreateArgs { deployer.tx.set_from(deployer_address); deployer.tx.set_chain_id(chain); - + // `to` field must be set explicitly, cannot be None. + if deployer.tx.to.is_none() { + deployer.tx.set_create(); + } deployer.tx.set_nonce(if let Some(nonce) = self.tx.nonce { Ok(nonce.to()) } else { - provider.get_transaction_count(deployer_address, None).await + provider.get_transaction_count(deployer_address, BlockId::latest()).await }?); deployer.tx.set_gas_limit(if let Some(gas_limit) = self.tx.gas_limit { Ok(gas_limit.to()) } else { - provider.estimate_gas(&deployer.tx, None).await + provider.estimate_gas(&deployer.tx, BlockId::latest()).await }?); // set tx value if specified @@ -557,7 +560,7 @@ where }; // create the tx object. Since we're deploying a contract, `to` is `None` - let tx = WithOtherFields::new(TransactionRequest::default().input(data.into()).to(None)); + let tx = WithOtherFields::new(TransactionRequest::default().input(data.into())); Ok(Deployer { client: self.client.clone(), diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index 0cb5b0c9c..cd4decfcb 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -4,7 +4,7 @@ use crate::{ ScriptConfig, }; use alloy_chains::Chain; -use alloy_eips::eip2718::Encodable2718; +use alloy_eips::{eip2718::Encodable2718, BlockId}; use alloy_network::{AnyNetwork, EthereumSigner, TransactionBuilder}; use alloy_primitives::{utils::format_units, Address, TxHash}; use alloy_provider::{utils::Eip1559Estimation, Provider}; @@ -45,7 +45,10 @@ where tx.gas = None; tx.set_gas_limit( - provider.estimate_gas(tx, None).await.wrap_err("Failed to estimate gas for tx")? * + provider + .estimate_gas(tx, BlockId::latest()) + .await + .wrap_err("Failed to estimate gas for tx")? * estimate_multiplier as u128 / 100, ); @@ -55,7 +58,7 @@ where pub async fn next_nonce(caller: Address, provider_url: &str) -> eyre::Result { let provider = try_get_http_provider(provider_url) .wrap_err_with(|| format!("bad fork_url provider: {provider_url}"))?; - Ok(provider.get_transaction_count(caller, None).await?) + Ok(provider.get_transaction_count(caller, BlockId::latest()).await?) } pub async fn send_transaction( @@ -70,7 +73,7 @@ pub async fn send_transaction( let from = tx.from.expect("no sender"); if sequential_broadcast { - let nonce = provider.get_transaction_count(from, None).await?; + let nonce = provider.get_transaction_count(from, BlockId::latest()).await?; let tx_nonce = tx.nonce.expect("no nonce"); if nonce != tx_nonce { @@ -287,6 +290,11 @@ impl BundledState { let mut tx = tx.clone(); tx.set_chain_id(sequence.chain); + // Set TxKind::Create explicityly to satify `check_reqd_fields` in alloy + if tx.to().is_none() { + tx.set_create(); + } + if let Some(gas_price) = gas_price { tx.set_gas_price(gas_price); } else { diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index bbdda84ae..9500c4d71 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -6,7 +6,7 @@ extern crate tracing; use self::transaction::AdditionalContract; use crate::runner::ScriptRunner; use alloy_json_abi::{Function, JsonAbi}; -use alloy_primitives::{Address, Bytes, Log, U256}; +use alloy_primitives::{Address, Bytes, Log, TxKind, U256}; use alloy_signer::Signer; use broadcast::next_nonce; use build::PreprocessedState; @@ -400,13 +400,15 @@ impl ScriptArgs { let mut offset = 0; // Find if it's a CREATE or CREATE2. Otherwise, skip transaction. - if let Some(to) = to { + if let Some(TxKind::Call(to)) = to { if to == DEFAULT_CREATE2_DEPLOYER { // Size of the salt prefix. offset = 32; + } else { + continue; } - } else if to.is_some() { - continue; + } else if let Some(TxKind::Create) = to { + // Pass } // Find artifact with a deployment code same as the data. diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index 87f9b3515..37926e308 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -13,7 +13,7 @@ use crate::{ ScriptArgs, ScriptConfig, ScriptResult, }; use alloy_network::TransactionBuilder; -use alloy_primitives::{utils::format_units, Address, U256}; +use alloy_primitives::{utils::format_units, Address, TxKind, U256}; use eyre::{Context, Result}; use foundry_cheatcodes::{BroadcastableTransactions, ScriptWallets}; use foundry_cli::utils::{has_different_gas_calc, now}; @@ -99,11 +99,12 @@ impl PreSimulationState { let mut runner = runners.get(&rpc).expect("invalid rpc url").write(); let mut tx = transaction.transaction; + let to = if let Some(TxKind::Call(to)) = tx.to { Some(to) } else { None }; let result = runner .simulate( tx.from .expect("transaction doesn't have a `from` address at execution time"), - tx.to, + to, tx.input.clone().into_input(), tx.value, ) diff --git a/crates/script/src/transaction.rs b/crates/script/src/transaction.rs index 3f5405db6..534b49641 100644 --- a/crates/script/src/transaction.rs +++ b/crates/script/src/transaction.rs @@ -1,6 +1,6 @@ use super::ScriptResult; use alloy_dyn_abi::JsonAbiExt; -use alloy_primitives::{Address, Bytes, B256}; +use alloy_primitives::{Address, Bytes, TxKind, B256}; use alloy_rpc_types::{request::TransactionRequest, WithOtherFields}; use eyre::{ContextCompat, Result, WrapErr}; use foundry_common::{fmt::format_token_raw, provider::alloy::RpcUrl, ContractData, SELECTOR_LEN}; @@ -71,7 +71,7 @@ impl TransactionWithMetadata { metadata.is_fixed_gas_limit = is_fixed_gas_limit; // Specify if any contract was directly created with this transaction - if let Some(to) = metadata.transaction.to { + if let Some(TxKind::Call(to)) = metadata.transaction.to { if to == DEFAULT_CREATE2_DEPLOYER { metadata.set_create( true, diff --git a/crates/test-utils/Cargo.toml b/crates/test-utils/Cargo.toml index 195048711..a73c53a39 100644 --- a/crates/test-utils/Cargo.toml +++ b/crates/test-utils/Cargo.toml @@ -18,6 +18,7 @@ foundry-config.workspace = true alloy-primitives.workspace = true alloy-provider.workspace = true +alloy-rpc-types.workspace = true eyre.workspace = true fd-lock = "4.0.0" diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index fecbc9f5f..3fa5f07f8 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -1,6 +1,7 @@ use crate::{init_tracing, TestCommand}; use alloy_primitives::Address; use alloy_provider::Provider; +use alloy_rpc_types::BlockId; use eyre::Result; use foundry_common::provider::alloy::{get_http_provider, RetryProvider}; use std::{collections::BTreeMap, fs, path::Path, str::FromStr}; @@ -117,7 +118,7 @@ impl ScriptTester { if let Some(provider) = &self.provider { let nonce = provider - .get_transaction_count(self.accounts_pub[index as usize], None) + .get_transaction_count(self.accounts_pub[index as usize], BlockId::latest()) .await .unwrap(); self.nonces.insert(index, nonce); @@ -128,8 +129,13 @@ impl ScriptTester { pub async fn load_addresses(&mut self, addresses: &[Address]) -> &mut Self { for &address in addresses { - let nonce = - self.provider.as_ref().unwrap().get_transaction_count(address, None).await.unwrap(); + let nonce = self + .provider + .as_ref() + .unwrap() + .get_transaction_count(address, BlockId::latest()) + .await + .unwrap(); self.address_nonces.insert(address, nonce); } self @@ -169,8 +175,13 @@ impl ScriptTester { pub async fn assert_nonce_increment(&mut self, keys_indexes: &[(u32, u32)]) -> &mut Self { for &(private_key_slot, expected_increment) in keys_indexes { let addr = self.accounts_pub[private_key_slot as usize]; - let nonce = - self.provider.as_ref().unwrap().get_transaction_count(addr, None).await.unwrap(); + let nonce = self + .provider + .as_ref() + .unwrap() + .get_transaction_count(addr, BlockId::latest()) + .await + .unwrap(); let prev_nonce = self.nonces.get(&private_key_slot).unwrap(); assert_eq!( @@ -193,7 +204,7 @@ impl ScriptTester { .provider .as_ref() .unwrap() - .get_transaction_count(*address, None) + .get_transaction_count(*address, BlockId::latest()) .await .unwrap(); let prev_nonce = self.address_nonces.get(address).unwrap(); diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index 6f8d44ff9..91963752d 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -286,9 +286,8 @@ impl VerifyBytecodeArgs { // Workaround for the NonceTooHigh issue as we're not simulating prior txs of the same // block. let prev_block_id = BlockId::Number(BlockNumberOrTag::Number(simulation_block - 1)); - let prev_block_nonce = provider - .get_transaction_count(creation_data.contract_creator, Some(prev_block_id)) - .await?; + let prev_block_nonce = + provider.get_transaction_count(creation_data.contract_creator, prev_block_id).await?; transaction.nonce = prev_block_nonce; if let Some(ref block) = block { diff --git a/crates/wallets/src/wallet_signer.rs b/crates/wallets/src/wallet_signer.rs index 9cf4478e2..37526559c 100644 --- a/crates/wallets/src/wallet_signer.rs +++ b/crates/wallets/src/wallet_signer.rs @@ -91,7 +91,7 @@ impl WalletSigner { } } WalletSigner::Aws(aws) => { - senders.push(aws.address()); + senders.push(alloy_signer::Signer::address(aws)); } } Ok(senders) @@ -142,7 +142,7 @@ impl Signer for WalletSigner { } fn address(&self) -> Address { - delegate!(self, inner => inner.address()) + delegate!(self, inner => alloy_signer::Signer::address(inner)) } fn chain_id(&self) -> Option { @@ -180,6 +180,10 @@ impl TxSigner for WalletSigner { ) -> alloy_signer::Result { delegate!(self, inner => inner.sign_transaction(tx)).await } + + fn address(&self) -> Address { + delegate!(self, inner => alloy_signer::Signer::address(inner)) + } } /// Signers that require user action to be obtained. From 651cec1665d785a2671d666a7bdfe55dd3eeebfb Mon Sep 17 00:00:00 2001 From: Danil Menkin <83108115+golden-expiriensu@users.noreply.github.com> Date: Thu, 25 Apr 2024 20:05:11 +0700 Subject: [PATCH 219/430] fix(anvil): take block count limit in fee history into account (#7780) --- crates/anvil/src/eth/api.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 10689de90..ac1760c6d 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -1264,7 +1264,7 @@ impl EthApi { } const MAX_BLOCK_COUNT: u64 = 1024u64; - let block_count = block_count.to::().max(MAX_BLOCK_COUNT); + let block_count = block_count.to::().min(MAX_BLOCK_COUNT); // highest and lowest block num in the requested range let highest = number; From 1fc4aa34c4331c12aaa8ee831b16b80b7d5a446f Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Thu, 25 Apr 2024 19:38:40 +0300 Subject: [PATCH 220/430] perf(invariant): sequentially shrink failed sequence (#7756) * perf(invariant): sequentially shrink failed sequence * If invariant function to test sequence is not set then return true (meaning original sequence is the smallest sequence we can identify) Follow ups: always set invariant function so we can shrink / test sequence, figure out why sometimes a failed sequence (reproducible with a regression test) doesn't fail when it's replayed * Reduce number of calls by trying to simplify in same step as complicate and avoid duplicate tests * Changes after review - for loop to shrink run limit - store only call seq len in shrinker - clone call seq only once * Nit --- Cargo.lock | 3 - crates/config/src/invariant.rs | 1 + crates/evm/evm/Cargo.toml | 3 - .../evm/evm/src/executors/invariant/error.rs | 249 ++++-------------- crates/evm/evm/src/executors/invariant/mod.rs | 2 + .../evm/evm/src/executors/invariant/shrink.rs | 68 +++++ crates/forge/tests/it/invariant.rs | 42 +++ .../common/InvariantShrinkBigSequence.t.sol | 31 +++ 8 files changed, 188 insertions(+), 211 deletions(-) create mode 100644 crates/evm/evm/src/executors/invariant/shrink.rs create mode 100644 testdata/default/fuzz/invariant/common/InvariantShrinkBigSequence.t.sol diff --git a/Cargo.lock b/Cargo.lock index a4ab4783e..1c081fb29 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3910,11 +3910,8 @@ dependencies = [ "foundry-evm-coverage", "foundry-evm-fuzz", "foundry-evm-traces", - "itertools 0.12.1", "parking_lot", "proptest", - "rand 0.8.5", - "rayon", "revm", "revm-inspectors", "thiserror", diff --git a/crates/config/src/invariant.rs b/crates/config/src/invariant.rs index 13c203d39..c7462d539 100644 --- a/crates/config/src/invariant.rs +++ b/crates/config/src/invariant.rs @@ -25,6 +25,7 @@ pub struct InvariantConfig { #[serde(flatten)] pub dictionary: FuzzDictionaryConfig, /// Attempt to shrink the failure case to its smallest sequence of calls + /// TODO: remove this setting as it is now redundant with shrink_run_limit = 0 pub shrink_sequence: bool, /// The maximum number of attempts to shrink the sequence pub shrink_run_limit: usize, diff --git a/crates/evm/evm/Cargo.toml b/crates/evm/evm/Cargo.toml index 9336ffff7..8a5d40db5 100644 --- a/crates/evm/evm/Cargo.toml +++ b/crates/evm/evm/Cargo.toml @@ -38,10 +38,7 @@ revm-inspectors.workspace = true arrayvec.workspace = true eyre = "0.6" hex.workspace = true -itertools.workspace = true parking_lot = "0.12" proptest = "1" -rand.workspace = true -rayon = "1" thiserror = "1" tracing = "0.1" diff --git a/crates/evm/evm/src/executors/invariant/error.rs b/crates/evm/evm/src/executors/invariant/error.rs index ae122d0fa..5b3ebad91 100644 --- a/crates/evm/evm/src/executors/invariant/error.rs +++ b/crates/evm/evm/src/executors/invariant/error.rs @@ -1,5 +1,5 @@ use super::{BasicTxDetails, InvariantContract}; -use crate::executors::{Executor, RawCallResult}; +use crate::executors::{invariant::shrink::CallSequenceShrinker, Executor, RawCallResult}; use alloy_json_abi::Function; use alloy_primitives::{Address, Bytes, Log}; use eyre::Result; @@ -9,11 +9,8 @@ use foundry_evm_fuzz::{ invariant::FuzzRunIdentifiedContracts, BaseCounterExample, CounterExample, FuzzedCases, Reason, }; use foundry_evm_traces::{load_contracts, CallTraceArena, TraceKind, Traces}; -use itertools::Itertools; use parking_lot::RwLock; use proptest::test_runner::TestError; -use rand::{seq, thread_rng, Rng}; -use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use revm::primitives::U256; use std::{borrow::Cow, sync::Arc}; @@ -162,7 +159,7 @@ impl FailedInvariantCaseData { }; if self.shrink { - calls = self.try_shrinking(&calls, &executor)?.into_iter().cloned().collect(); + calls = self.shrink_sequence(&calls, &executor)?; } else { trace!(target: "forge::test", "Shrinking disabled."); } @@ -212,69 +209,14 @@ impl FailedInvariantCaseData { .then_some(CounterExample::Sequence(counterexample_sequence))) } - /// Checks that a subsequence of the provided calls fails the provided invariant test - /// and updates an Arc Mutex of the indices of the shortest sequence - fn set_fails_successfully( - &self, - mut executor: Executor, - calls: &[BasicTxDetails], - use_calls: &[usize], - curr_seq: Arc>>, - ) -> eyre::Result<()> { - if curr_seq.read().len() == 1 { - // if current sequence is already the smallest possible, just return - return Ok(()); - } - - let mut new_sequence = Vec::with_capacity(calls.len()); - for index in 0..calls.len() { - if !use_calls.contains(&index) { - continue - } - - new_sequence.push(index); - - // If the new sequence is already longer than the known best, skip execution - if new_sequence.len() >= curr_seq.read().len() { - return Ok(()); - } - } - - for (seq_idx, call_index) in new_sequence.iter().enumerate() { - let (sender, (addr, bytes)) = &calls[*call_index]; - - executor.call_raw_committing(*sender, *addr, bytes.clone(), U256::ZERO)?; - - // Checks the invariant. If we revert or fail before the last call, all the better. - if let Some(func) = &self.func { - let mut call_result = - executor.call_raw(CALLER, self.addr, func.clone(), U256::ZERO)?; - let is_success = executor.is_raw_call_success( - self.addr, - Cow::Owned(call_result.state_changeset.take().unwrap()), - &call_result, - false, - ); - if !is_success { - let mut locked = curr_seq.write(); - if new_sequence[..=seq_idx].len() < locked.len() { - // update the curr_sequence if the new sequence is lower than - *locked = new_sequence[..=seq_idx].to_vec(); - } - } - } - } - Ok(()) - } - /// Tries to shrink the failure case to its smallest sequence of calls. /// /// If the number of calls is small enough, we can guarantee maximal shrinkage - fn try_shrinking<'a>( + fn shrink_sequence( &self, - calls: &'a [BasicTxDetails], + calls: &[BasicTxDetails], executor: &Executor, - ) -> eyre::Result> { + ) -> Result> { trace!(target: "forge::test", "Shrinking."); // Special case test: the invariant is *unsatisfiable* - it took 0 calls to @@ -287,7 +229,18 @@ impl FailedInvariantCaseData { } } - let shrunk_call_indices = self.try_shrinking_recurse(calls, executor, 0, 0)?; + let mut shrinker = CallSequenceShrinker::new(calls.len()); + for _ in 0..self.shrink_run_limit { + // Check candidate sequence result. + match self.check_sequence(executor.clone(), calls, shrinker.current().collect()) { + // If candidate sequence still fails then shrink more if possible. + false if !shrinker.simplify() => break, + // If candidate sequence pass then restore last removed call and shrink other + // calls if possible. + true if !shrinker.complicate() => break, + _ => {} + } + } // We recreate the call sequence in the same order as they reproduce the failure, // otherwise we could end up with inverted sequence. @@ -296,153 +249,39 @@ impl FailedInvariantCaseData { // 2. Bob calls transferOwnership to Alice // 3. Alice calls acceptOwnership and test fails // we shrink to indices of [2, 1] and we recreate call sequence in same order. - Ok(shrunk_call_indices.iter().map(|idx| &calls[*idx]).collect()) + Ok(shrinker.current().map(|idx| &calls[idx]).cloned().collect()) } - /// We try to construct a [powerset](https://en.wikipedia.org/wiki/Power_set) of the sequence if - /// the configuration allows for it and the length of the sequence is small enough. If we - /// do construct the powerset, we run all of the sequences in parallel, finding the smallest - /// one. If we have ran the powerset, we are guaranteed to find the smallest sequence for a - /// given set of calls (however, not guaranteed *global* smallest if a random sequence was - /// used at any point during recursion). - /// - /// If we were unable to construct a powerset, we construct a random sample over the sequence - /// and run these sequences in parallel instead. - /// - /// After running either the powerset or the random sequences, we check if we successfully - /// shrunk the call sequence. - fn try_shrinking_recurse( + fn check_sequence( &self, + mut executor: Executor, calls: &[BasicTxDetails], - executor: &Executor, - runs: usize, - retries: usize, - ) -> eyre::Result> { - // Construct a ArcRwLock vector of indices of `calls` - let shrunk_call_indices = Arc::new(RwLock::new((0..calls.len()).collect())); - let shrink_limit = self.shrink_run_limit - runs; - - let upper_bound = calls.len().saturating_sub(1); - // We construct either a full powerset (this guarantees we maximally shrunk for the given - // calls) or a random subset - let (set_of_indices, is_powerset): (Vec<_>, bool) = if calls.len() <= 64 && - (1 << calls.len() as u32) <= shrink_limit - { - // We add the last tx always because thats ultimately what broke the invariant - let powerset = (0..upper_bound) - .powerset() - .map(|mut subset| { - subset.push(upper_bound); - subset - }) - .collect(); - (powerset, true) - } else { - // construct a random set of subsequences - let mut rng = thread_rng(); - ( - (0..shrink_limit / 3) - .map(|_| { - // Select between 1 and calls.len() - 2 number of indices - let amt: usize = rng.gen_range(1..upper_bound); - // Construct a random sequence of indices, up to calls.len() - 1 (sample is - // exclusive range and we dont include the last tx - // because its always included), and amt number of indices - let mut seq = seq::index::sample(&mut rng, upper_bound, amt).into_vec(); - // Sort the indices because seq::index::sample is unordered - seq.sort(); - // We add the last tx always because thats what ultimately broke the - // invariant - seq.push(upper_bound); - seq - }) - .collect(), + sequence: Vec, + ) -> bool { + // Apply the shrinked candidate sequence. + sequence.iter().for_each(|call_index| { + let (sender, (addr, bytes)) = &calls[*call_index]; + executor.call_raw_committing(*sender, *addr, bytes.clone(), U256::ZERO).unwrap(); + }); + + // Check the invariant for candidate sequence. + // If sequence fails then we can continue with shrinking - the removed call does not affect + // failure. + // + // If sequence doesn't fail then we have to restore last removed call and continue with next + // call - removed call is a required step for reproducing the failure. + if let Some(func) = &self.func { + let mut call_result = + executor.call_raw(CALLER, self.addr, func.clone(), U256::ZERO).unwrap(); + executor.is_raw_call_success( + self.addr, + Cow::Owned(call_result.state_changeset.take().unwrap()), + &call_result, false, ) - }; - - let new_runs = set_of_indices.len(); - - // just try all of them in parallel - set_of_indices - .par_iter() - .map(|use_calls| { - self.set_fails_successfully( - executor.clone(), - calls, - use_calls, - Arc::clone(&shrunk_call_indices), - ) - }) - .collect::>()?; - - // There are no more live references to shrunk_call_indices as the parallel execution is - // finished, so it is fine to get the inner value via `Arc::unwrap`. - let shrunk_call_indices = Arc::try_unwrap(shrunk_call_indices).unwrap().into_inner(); - - if is_powerset { - // A powerset is guaranteed to be smallest local subset, so we return early. - return Ok(shrunk_call_indices); - } - - let computation_budget_not_hit = new_runs + runs < self.shrink_run_limit; - // If the new shrunk_call_indices is less than the input calls length, - // we found a subsequence that is shorter. So we can measure if we made progress by - // comparing them - let made_progress = shrunk_call_indices.len() < calls.len(); - // We limit the number of times we can iterate without making progress - let has_remaining_retries = retries <= 3; - - match (computation_budget_not_hit, made_progress) { - (true, false) if has_remaining_retries => { - // we havent hit the computation budget and we have retries remaining - // - // use the same call set but increase retries which should select a different random - // subset we dont need to do the mapping stuff like above because we dont - // take a subset of the input - self.try_shrinking_recurse(calls, executor, runs + new_runs, retries + 1) - } - (true, true) => { - // We construct a *new* subset of calls using the `shrunk_call_indices` of the - // passed in calls i.e. if shrunk_call_indices == [1, 3], and calls - // is: [call0, call1, call2, call3] then new_calls == [call1, call3] - let new_calls: Vec<_> = calls - .iter() - .enumerate() - .filter(|(i, _)| shrunk_call_indices.contains(i)) - .map(|(_, call)| call.clone()) - .collect(); - - // We rerun this algorithm as if the new smaller subset above were the original - // calls. i.e. if [call0, call1, call2, call3] got reduced to - // [call1, call3] (in the above line) and we still have progress - // to make, we recall this function with [call1, call3]. Then after this call say it - // returns [1]. This means `call3` is all that is required to break - // the invariant. - let new_calls_idxs = - self.try_shrinking_recurse(&new_calls, executor, runs + new_runs, 0)?; - - // Notably, the indices returned above are relative to `new_calls`, *not* the - // originally passed in `calls`. So we map back by filtering - // `new_calls` by index if the index was returned above, and finding the position - // of the `new_call` in the passed in `call` - Ok(new_calls - .iter() - .enumerate() - .filter_map(|(idx, new_call)| { - if !new_calls_idxs.contains(&idx) { - None - } else { - calls.iter().position(|r| r == new_call) - } - }) - .collect()) - } - _ => { - // The computation budget has been hit or no retries remaining, stop trying to make - // progress - Ok(shrunk_call_indices) - } + } else { + // Invariant function is not set, return true as we cannot test the sequence. + true } } } diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index b5669cb7d..d0593d919 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -36,6 +36,8 @@ use self::error::FailedInvariantCaseData; pub use error::{InvariantFailures, InvariantFuzzError, InvariantFuzzTestResult}; mod funcs; +mod shrink; + pub use funcs::{assert_invariants, replay_run}; sol! { diff --git a/crates/evm/evm/src/executors/invariant/shrink.rs b/crates/evm/evm/src/executors/invariant/shrink.rs new file mode 100644 index 000000000..09f890c65 --- /dev/null +++ b/crates/evm/evm/src/executors/invariant/shrink.rs @@ -0,0 +1,68 @@ +use proptest::bits::{BitSetLike, VarBitSet}; + +#[derive(Clone, Copy, Debug)] +struct Shrink { + call_index: usize, +} + +/// Shrinker for a call sequence failure. +/// Iterates sequence call sequence top down and removes calls one by one. +/// If the failure is still reproducible with removed call then moves to the next one. +/// If the failure is not reproducible then restore removed call and moves to next one. +#[derive(Debug)] +pub(crate) struct CallSequenceShrinker { + /// Length of call sequence to be shrinked. + call_sequence_len: usize, + /// Call ids contained in current shrinked sequence. + included_calls: VarBitSet, + /// Current shrinked call id. + shrink: Shrink, + /// Previous shrinked call id. + prev_shrink: Option, +} + +impl CallSequenceShrinker { + pub(crate) fn new(call_sequence_len: usize) -> Self { + Self { + call_sequence_len, + included_calls: VarBitSet::saturated(call_sequence_len), + shrink: Shrink { call_index: 0 }, + prev_shrink: None, + } + } + + /// Return candidate shrink sequence to be tested, by removing ids from original sequence. + pub(crate) fn current(&self) -> impl Iterator + '_ { + (0..self.call_sequence_len).filter(|&call_id| self.included_calls.test(call_id)) + } + + /// Removes next call from sequence. + pub(crate) fn simplify(&mut self) -> bool { + if self.shrink.call_index >= self.call_sequence_len { + // We reached the end of call sequence, nothing left to simplify. + false + } else { + // Remove current call. + self.included_calls.clear(self.shrink.call_index); + // Record current call as previous call. + self.prev_shrink = Some(self.shrink); + // Remove next call index + self.shrink = Shrink { call_index: self.shrink.call_index + 1 }; + true + } + } + + /// Reverts removed call from sequence and tries to simplify next call. + pub(crate) fn complicate(&mut self) -> bool { + match self.prev_shrink { + Some(shrink) => { + // Undo the last call removed. + self.included_calls.set(shrink.call_index); + self.prev_shrink = None; + // Try to simplify next call. + self.simplify() + } + None => false, + } + } +} diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index e03272ea8..ee3f0edc3 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -173,6 +173,10 @@ async fn test_invariant() { None, )], ), + ( + "default/fuzz/invariant/common/InvariantShrinkBigSequence.t.sol:ShrinkBigSequenceTest", + vec![("invariant_shrink_big_sequence()", true, None, None, None)], + ), ]), ); } @@ -329,6 +333,44 @@ async fn test_shrink(opts: TestOptions, contract_pattern: &str) { }; } +#[tokio::test(flavor = "multi_thread")] +#[cfg_attr(windows, ignore = "for some reason there's different rng")] +async fn test_shrink_big_sequence() { + let mut opts = TEST_DATA_DEFAULT.test_opts.clone(); + opts.fuzz.seed = Some(U256::from(119u32)); + + let filter = + Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantShrinkBigSequence.t.sol"); + let mut runner = TEST_DATA_DEFAULT.runner(); + runner.test_options = opts.clone(); + runner.test_options.invariant.runs = 1; + runner.test_options.invariant.depth = 500; + let results = runner.test_collect(&filter); + let results = + results.values().last().expect("`InvariantShrinkBigSequence` should be testable."); + + let result = results + .test_results + .values() + .last() + .expect("`InvariantShrinkBigSequence` should be testable."); + + assert_eq!(result.status, TestStatus::Failure); + + let counter = result + .counterexample + .as_ref() + .expect("`InvariantShrinkBigSequence` should have failed with a counterexample."); + + match counter { + CounterExample::Single(_) => panic!("CounterExample should be a sequence."), + CounterExample::Sequence(sequence) => { + // ensure shrinks to same sequence of 77 + assert_eq!(sequence.len(), 77); + } + }; +} + #[tokio::test(flavor = "multi_thread")] async fn test_invariant_preserve_state() { let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantPreserveState.t.sol"); diff --git a/testdata/default/fuzz/invariant/common/InvariantShrinkBigSequence.t.sol b/testdata/default/fuzz/invariant/common/InvariantShrinkBigSequence.t.sol new file mode 100644 index 000000000..5699d9c45 --- /dev/null +++ b/testdata/default/fuzz/invariant/common/InvariantShrinkBigSequence.t.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract ShrinkBigSequence { + uint256 cond; + + function work(uint256 x) public { + if (x % 2 != 0 && x < 9000) { + cond++; + } + } + + function checkCond() public view { + require(cond < 77, "condition met"); + } +} + +contract ShrinkBigSequenceTest is DSTest { + ShrinkBigSequence target; + + function setUp() public { + target = new ShrinkBigSequence(); + } + + function invariant_shrink_big_sequence() public view { + target.checkCond(); + } +} From 19d69f277de96f621d930cdb767a9693c55ae8e1 Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Fri, 26 Apr 2024 00:28:34 +0200 Subject: [PATCH 221/430] feat: `vm.blobhashes` (#7001) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: `vm.blobhashes` * chore: rename * test: add `vm.blobhashes` test * fix: add missing vm fn * fix: `cargo cheat` * fix: specify blobhashes loc * fix: use solidity 0.8.24 for blobhash test * chore: reinsert noop line ¯\_(ツ)_/¯ * refactor: move cancun cheats to sep dir * temp * temp * feat(cheatcodes): getBlobhashes --------- Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> --- crates/cheatcodes/assets/cheatcodes.json | 40 ++++++++++++++++++++++++ crates/cheatcodes/spec/src/vm.rs | 12 +++++++ crates/cheatcodes/src/evm.rs | 25 +++++++++++++++ testdata/cancun/cheats/Blobhashes.t.sol | 20 ++++++++++++ testdata/cheats/Vm.sol | 2 ++ 5 files changed, 99 insertions(+) create mode 100644 testdata/cancun/cheats/Blobhashes.t.sol diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index f769b2f06..1d34bf578 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -2991,6 +2991,26 @@ "status": "stable", "safety": "unsafe" }, + { + "func": { + "id": "blobhashes", + "description": "Sets the blobhashes in the transaction.\nNot available on EVM versions before Cancun.\nIf used on unsupported EVM versions it will revert.", + "declaration": "function blobhashes(bytes32[] calldata blobhashes) external;", + "visibility": "external", + "mutability": "", + "signature": "blobhashes(bytes32[])", + "selector": "0x129de7eb", + "selectorBytes": [ + 18, + 157, + 231, + 235 + ] + }, + "group": "evm", + "status": "stable", + "safety": "unsafe" + }, { "func": { "id": "breakpoint_0", @@ -4711,6 +4731,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "getBlobhashes", + "description": "Gets the blockhashes from the current transaction.\nNot available on EVM versions before Cancun.\nIf used on unsupported EVM versions it will revert.", + "declaration": "function getBlobhashes() external view returns (bytes32[] memory blobhashes);", + "visibility": "external", + "mutability": "view", + "signature": "getBlobhashes()", + "selector": "0xf56ff18b", + "selectorBytes": [ + 245, + 111, + 241, + 139 + ] + }, + "group": "evm", + "status": "stable", + "safety": "unsafe" + }, { "func": { "id": "getBlockNumber", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index e838298c7..a62a05880 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -381,6 +381,18 @@ interface Vm { #[cheatcode(group = Evm, safety = Unsafe)] function prevrandao(uint256 newPrevrandao) external; + /// Sets the blobhashes in the transaction. + /// Not available on EVM versions before Cancun. + /// If used on unsupported EVM versions it will revert. + #[cheatcode(group = Evm, safety = Unsafe)] + function blobhashes(bytes32[] calldata blobhashes) external; + + /// Gets the blockhashes from the current transaction. + /// Not available on EVM versions before Cancun. + /// If used on unsupported EVM versions it will revert. + #[cheatcode(group = Evm, safety = Unsafe)] + function getBlobhashes() external view returns (bytes32[] memory blobhashes); + /// Sets `block.height`. #[cheatcode(group = Evm, safety = Unsafe)] function roll(uint256 newHeight) external; diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index bfe43d684..c924c2436 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -303,6 +303,31 @@ impl Cheatcode for prevrandao_1Call { } } +impl Cheatcode for blobhashesCall { + fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + let Self { blobhashes } = self; + ensure!( + ccx.ecx.spec_id() >= SpecId::CANCUN, + "`blobhash` is not supported before the Cancun hard fork; \ + see EIP-4844: https://eips.ethereum.org/EIPS/eip-4844" + ); + ccx.ecx.env.tx.blob_hashes.clone_from(blobhashes); + Ok(Default::default()) + } +} + +impl Cheatcode for getBlobhashesCall { + fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + let Self {} = self; + ensure!( + ccx.ecx.spec_id() >= SpecId::CANCUN, + "`blobhash` is not supported before the Cancun hard fork; \ + see EIP-4844: https://eips.ethereum.org/EIPS/eip-4844" + ); + Ok(ccx.ecx.env.tx.blob_hashes.clone().abi_encode()) + } +} + impl Cheatcode for rollCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newHeight } = self; diff --git a/testdata/cancun/cheats/Blobhashes.t.sol b/testdata/cancun/cheats/Blobhashes.t.sol new file mode 100644 index 000000000..4a589b45a --- /dev/null +++ b/testdata/cancun/cheats/Blobhashes.t.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.25; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract BlobhashesTest is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + function testSetAndGetBlobhashes() public { + bytes32[] memory blobhashes = new bytes32[](2); + blobhashes[0] = bytes32(0x0000000000000000000000000000000000000000000000000000000000000001); + blobhashes[1] = bytes32(0x0000000000000000000000000000000000000000000000000000000000000002); + vm.blobhashes(blobhashes); + + bytes32[] memory gotBlobhashes = vm.getBlobhashes(); + assertEq(gotBlobhashes[0], blobhashes[0]); + assertEq(gotBlobhashes[1], blobhashes[1]); + } +} diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 854f57d23..e70049cbf 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -145,6 +145,7 @@ interface Vm { function assertTrue(bool condition, string calldata error) external pure; function assume(bool condition) external pure; function blobBaseFee(uint256 newBlobBaseFee) external; + function blobhashes(bytes32[] calldata blobhashes) external; function breakpoint(string calldata char) external; function breakpoint(string calldata char, bool value) external; function broadcast() external; @@ -231,6 +232,7 @@ interface Vm { function ffi(string[] calldata commandInput) external returns (bytes memory result); function fsMetadata(string calldata path) external view returns (FsMetadata memory metadata); function getBlobBaseFee() external view returns (uint256 blobBaseFee); + function getBlobhashes() external view returns (bytes32[] memory blobhashes); function getBlockNumber() external view returns (uint256 height); function getBlockTimestamp() external view returns (uint256 timestamp); function getCode(string calldata artifactPath) external view returns (bytes memory creationBytecode); From d21c3e7e1864d909cbece8ad650f91770193f128 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Thu, 25 Apr 2024 17:16:59 -0700 Subject: [PATCH 222/430] fix: blob cheats shadowing (#7787) * fix: blob cheats shadowing * fix: cargo cheats --- crates/cheatcodes/assets/cheatcodes.json | 4 ++-- crates/cheatcodes/spec/src/vm.rs | 4 ++-- crates/cheatcodes/src/evm.rs | 4 ++-- testdata/cheats/Vm.sol | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 1d34bf578..39b58ff5e 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -2995,7 +2995,7 @@ "func": { "id": "blobhashes", "description": "Sets the blobhashes in the transaction.\nNot available on EVM versions before Cancun.\nIf used on unsupported EVM versions it will revert.", - "declaration": "function blobhashes(bytes32[] calldata blobhashes) external;", + "declaration": "function blobhashes(bytes32[] calldata hashes) external;", "visibility": "external", "mutability": "", "signature": "blobhashes(bytes32[])", @@ -4735,7 +4735,7 @@ "func": { "id": "getBlobhashes", "description": "Gets the blockhashes from the current transaction.\nNot available on EVM versions before Cancun.\nIf used on unsupported EVM versions it will revert.", - "declaration": "function getBlobhashes() external view returns (bytes32[] memory blobhashes);", + "declaration": "function getBlobhashes() external view returns (bytes32[] memory hashes);", "visibility": "external", "mutability": "view", "signature": "getBlobhashes()", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index a62a05880..4bbf63169 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -385,13 +385,13 @@ interface Vm { /// Not available on EVM versions before Cancun. /// If used on unsupported EVM versions it will revert. #[cheatcode(group = Evm, safety = Unsafe)] - function blobhashes(bytes32[] calldata blobhashes) external; + function blobhashes(bytes32[] calldata hashes) external; /// Gets the blockhashes from the current transaction. /// Not available on EVM versions before Cancun. /// If used on unsupported EVM versions it will revert. #[cheatcode(group = Evm, safety = Unsafe)] - function getBlobhashes() external view returns (bytes32[] memory blobhashes); + function getBlobhashes() external view returns (bytes32[] memory hashes); /// Sets `block.height`. #[cheatcode(group = Evm, safety = Unsafe)] diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index c924c2436..618998071 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -305,13 +305,13 @@ impl Cheatcode for prevrandao_1Call { impl Cheatcode for blobhashesCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { - let Self { blobhashes } = self; + let Self { hashes } = self; ensure!( ccx.ecx.spec_id() >= SpecId::CANCUN, "`blobhash` is not supported before the Cancun hard fork; \ see EIP-4844: https://eips.ethereum.org/EIPS/eip-4844" ); - ccx.ecx.env.tx.blob_hashes.clone_from(blobhashes); + ccx.ecx.env.tx.blob_hashes.clone_from(hashes); Ok(Default::default()) } } diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index e70049cbf..db8e45ada 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -145,7 +145,7 @@ interface Vm { function assertTrue(bool condition, string calldata error) external pure; function assume(bool condition) external pure; function blobBaseFee(uint256 newBlobBaseFee) external; - function blobhashes(bytes32[] calldata blobhashes) external; + function blobhashes(bytes32[] calldata hashes) external; function breakpoint(string calldata char) external; function breakpoint(string calldata char, bool value) external; function broadcast() external; @@ -232,7 +232,7 @@ interface Vm { function ffi(string[] calldata commandInput) external returns (bytes memory result); function fsMetadata(string calldata path) external view returns (FsMetadata memory metadata); function getBlobBaseFee() external view returns (uint256 blobBaseFee); - function getBlobhashes() external view returns (bytes32[] memory blobhashes); + function getBlobhashes() external view returns (bytes32[] memory hashes); function getBlockNumber() external view returns (uint256 height); function getBlockTimestamp() external view returns (uint256 timestamp); function getCode(string calldata artifactPath) external view returns (bytes memory creationBytecode); From d431f74f78eb52e4a0c528cd728aad5e4270367d Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Fri, 26 Apr 2024 19:04:08 +0200 Subject: [PATCH 223/430] Upgrade to latest version of Alloy and port Anvil tests (#7701) * add: alloy utils and fix anvil tests * fix: clippy * migrate`can_order_transactions` test * migrate(anvil/tests): transactions - `can_respect_nonces`, `can_replace_transaction`, `can_reject_too_high_gas_limits`, `can_reject_underpriced_replacement` * add: provider with signer utils * use: sol! in abi * start porting anvil_api tests * additional tests * add: can_impersonate_gnosis_safe * add: can_impersonate_multiple_account * add: can_mine_manually + test_set_next_timestamp * more tests * add: test_can_set_storage_bsc_fork * port the rest of the tests, final test is blocked on lack of txpool_inspect methods on the provider, see https://github.com/alloy-rs/alloy/issues/502 * simplify types * use provider_with_signer, unclear exactly if it will interact as expected in relation to impersonation * migrate(anvil/tests): `can_deploy_greeter_http`, `can_deploy_and_mine_manually` tx tests migrated to alloy * migrate(anvil/tests): `can_mine_automatically`, `can_call_greeter_historic` tx tests to alloy * migrate(anvil/test): tx tests migrated to alloy - TODOs remaining * migrate transaction::test_tx_access_list to alloy * nit * migrate(anvil/tests): transactions::call_past_state * migrate(anvil/tests): can_handle_multiple_concurrent_deploys_with_same_nonce & can_handle_multiple_concurrent_transactions_with_same_nonce tx tests * migrate: tx test stream_pending_txs - fix TODO * start on api * finish api examples, softly blocked on simulated call overrides - needs some more investigation * clean up imports * specify from on contract builder * finish ganache tests * wrap up ganache, start on gas * add gas tests * considering these tests are ignored is it necessary to keep them around? * add back ganache and geth * port geth * add ipc * add txpool, missing methods * migrates(anvil/tests): `fork` tests to alloy - fix TODOs * migrate(anvil/tests): trace tests to alloy - fix `debug_*` TODO * bump alloy - satisfy clippy * bump alloy & migrate sign examples * fix revm-inspectors * use latest evm-inspectors version * start fixing broken test ports * fix test_tip_above_fee_cap * fix broken tests, long running websocket / ipc tests still have issues * add can_call_with_state_override test * re-enable txpool test body * add logs:get_past_events test * add logs:get_all_events * add logs:watch_events * pubsub utils * yash/anvil-to-alloy (#7705) * migrate(anvil/tests): pubsub * pubsub tests to alloy * nit * nits * nit:test_sub_new_heads_fast * fix api:can_get_pending_block * temporarily change ipc_provider to connect_pubsub, add ignores to breaking tests relying on #389 * fix gas:test_respect_base_fee * fix api:can_call_on_pending_block * add note on broken test to revisit, all tests should run now * add temp attempt at optimism port, not behaving an expected and a lot of conversions * revert for now * start porting otterscan * continue adding otterscan tests * another otterscan test case * finish otterscan tests * clean up imports * start porting revert tests * fix(anvil/tests): TODOs * bump alloy * nit * nits * bump alloy to fix test_fork_uncles_fetch * fmt nits * nit * rm abigen from abi * nit * rm unused ethers utils * finish revert examples * clean up imports and commits, use dynamic complilation where previously implemented * port optimism * lift comment to todo * clean up imports, start porting leftover ethers references * inline alloy namespace in foundry-common * remove runtime_client, unnecessary imports * fix: test_sub_new_heads_fast using workaround * port jwt * update alloy / alloy-core / evm-inspectors * remove hex dep * add missing hex * implement txkind change, issues around test running - spotty * cast differently, still not working * rm ignore fork tests * fix: clippy * nits * fix flaky test, make sure block is available by mining it * fix cargo hack check * ignore specific ipc error on windows * append to previous commit, same ipc issue * http_provider(&handle.http_endpoint()) -> handle.http_provider() * apply for ws_provider and ipc_provider as well, re-enable can_remove_pool_transactions * refactor test_sub_new_heads_fast * remove redundant RpcUrl alias * temp enable ipc tests for windows with debug * attempt fix of ipc issue with tempfile, as used in Alloy test suite * fix(anvil/tests): can_replace_transaction * explicitly enable mocking for tests * attempt ipc prefix * enhance error, ignore failing ipc tests on windows for now --------- Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> --- Cargo.lock | 79 +- Cargo.toml | 10 +- crates/anvil/Cargo.toml | 19 +- crates/anvil/core/src/eth/transaction/mod.rs | 16 +- crates/anvil/src/config.rs | 3 +- crates/anvil/src/eth/api.rs | 6 +- crates/anvil/src/eth/backend/fork.rs | 2 +- crates/anvil/src/eth/backend/mem/mod.rs | 30 +- crates/anvil/src/eth/error.rs | 14 +- crates/anvil/src/eth/pool/mod.rs | 4 +- crates/anvil/src/filter.rs | 6 +- crates/anvil/src/lib.rs | 2 +- crates/anvil/tests/it/abi.rs | 96 +- crates/anvil/tests/it/anvil.rs | 41 +- crates/anvil/tests/it/anvil_api.rs | 434 ++++---- crates/anvil/tests/it/api.rs | 333 +++--- crates/anvil/tests/it/fork.rs | 608 +++++------ crates/anvil/tests/it/ganache.rs | 216 ++-- crates/anvil/tests/it/gas.rs | 157 ++- crates/anvil/tests/it/genesis.rs | 5 +- crates/anvil/tests/it/geth.rs | 105 +- crates/anvil/tests/it/ipc.rs | 16 +- crates/anvil/tests/it/logs.rs | 252 +++-- crates/anvil/tests/it/main.rs | 4 +- crates/anvil/tests/it/optimism.rs | 215 ++-- crates/anvil/tests/it/otterscan.rs | 509 +++++---- crates/anvil/tests/it/proof.rs | 3 +- crates/anvil/tests/it/pubsub.rs | 255 +++-- crates/anvil/tests/it/revert.rs | 247 +++-- crates/anvil/tests/it/sign.rs | 39 +- crates/anvil/tests/it/traces.rs | 245 ++--- crates/anvil/tests/it/transaction.rs | 975 ++++++++++-------- crates/anvil/tests/it/txpool.rs | 36 +- crates/anvil/tests/it/utils.rs | 124 +-- crates/anvil/tests/it/wsapi.rs | 7 +- crates/cast/Cargo.toml | 8 +- crates/cast/bin/cmd/call.rs | 4 +- crates/cast/bin/cmd/estimate.rs | 4 +- crates/cast/bin/cmd/run.rs | 2 +- crates/cast/bin/tx.rs | 2 +- crates/cast/src/lib.rs | 3 +- crates/cheatcodes/Cargo.toml | 5 +- crates/cheatcodes/src/evm/fork.rs | 2 +- crates/cheatcodes/src/inspector.rs | 6 +- crates/cli/src/utils/mod.rs | 12 +- crates/common/Cargo.toml | 26 +- crates/common/src/ens.rs | 1 + crates/common/src/fmt/ui.rs | 25 - crates/common/src/lib.rs | 2 - crates/common/src/provider/alloy.rs | 324 ------ crates/common/src/provider/ethers.rs | 283 ----- crates/common/src/provider/mod.rs | 325 +++++- .../common/src/provider/runtime_transport.rs | 22 +- crates/common/src/runtime_client.rs | 337 ------ crates/common/src/types.rs | 210 ---- crates/evm/core/src/fork/backend.rs | 2 +- crates/evm/core/src/fork/database.rs | 2 +- crates/evm/core/src/fork/multi.rs | 4 +- crates/evm/core/src/opts.rs | 7 +- crates/forge/Cargo.toml | 13 +- crates/forge/bin/cmd/create.rs | 5 +- crates/forge/tests/cli/utils.rs | 39 +- crates/script/src/broadcast.rs | 12 +- crates/script/src/build.rs | 2 +- crates/script/src/execute.rs | 4 +- crates/script/src/lib.rs | 3 +- crates/script/src/providers.rs | 6 +- crates/script/src/receipts.rs | 2 +- crates/script/src/simulate.rs | 6 +- crates/script/src/transaction.rs | 6 +- crates/test-utils/src/script.rs | 2 +- crates/verify/src/bytecode.rs | 2 +- crates/wallets/src/wallet_signer.rs | 8 +- 73 files changed, 3133 insertions(+), 3708 deletions(-) delete mode 100644 crates/common/src/provider/alloy.rs delete mode 100644 crates/common/src/provider/ethers.rs delete mode 100644 crates/common/src/runtime_client.rs delete mode 100644 crates/common/src/types.rs diff --git a/Cargo.lock b/Cargo.lock index 1c081fb29..0076ecc42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,6 +101,7 @@ dependencies = [ "alloy-network", "alloy-primitives", "alloy-provider", + "alloy-pubsub", "alloy-rpc-types", "alloy-sol-types", "alloy-transport", @@ -335,6 +336,23 @@ dependencies = [ "thiserror", ] +[[package]] +name = "alloy-rpc-types-engine" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-rpc-types", + "alloy-serde", + "jsonwebtoken 9.3.0", + "rand 0.8.5", + "serde", + "thiserror", +] + [[package]] name = "alloy-rpc-types-trace" version = "0.1.0" @@ -542,7 +560,9 @@ dependencies = [ "futures", "interprocess", "pin-project", + "serde", "serde_json", + "tempfile", "tokio", "tokio-util", "tracing", @@ -668,20 +688,26 @@ version = "0.2.0" dependencies = [ "alloy-chains", "alloy-consensus", + "alloy-contract", "alloy-dyn-abi", "alloy-eips", "alloy-genesis", "alloy-json-abi", + "alloy-json-rpc", "alloy-network", "alloy-primitives", "alloy-provider", + "alloy-pubsub", "alloy-rlp", + "alloy-rpc-client", "alloy-rpc-types", "alloy-rpc-types-trace", "alloy-signer", "alloy-signer-wallet", "alloy-sol-types", "alloy-transport", + "alloy-transport-ipc", + "alloy-transport-ws", "alloy-trie", "anvil-core", "anvil-rpc", @@ -3136,7 +3162,7 @@ dependencies = [ "hashers", "http 0.2.12", "instant", - "jsonwebtoken", + "jsonwebtoken 8.3.0", "once_cell", "pin-project", "reqwest 0.11.27", @@ -3460,7 +3486,6 @@ dependencies = [ "dialoguer", "dunce", "ethers-contract", - "ethers-core", "evm-disassembler", "eyre", "forge-doc", @@ -3761,7 +3786,7 @@ dependencies = [ "alloy-pubsub", "alloy-rpc-client", "alloy-rpc-types", - "alloy-signer-wallet", + "alloy-rpc-types-engine", "alloy-sol-types", "alloy-transport", "alloy-transport-http", @@ -3770,12 +3795,7 @@ dependencies = [ "async-trait", "clap", "comfy-table", - "const-hex", "dunce", - "ethers-core", - "ethers-middleware", - "ethers-providers", - "ethers-signers", "eyre", "foundry-block-explorers", "foundry-compilers", @@ -3787,7 +3807,6 @@ dependencies = [ "num-format", "once_cell", "pretty_assertions", - "reqwest 0.11.27", "reqwest 0.12.4", "rustc-hash", "semver 1.0.22", @@ -3805,9 +3824,9 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a73252c7753488cf7779e2788e05088b3b3d9198fbbc5d4da94b5afd0f751d" +checksum = "e5421772f768d43f81052159c5175e7d10941e0f0416dafd768f353ed9c554fd" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -5200,9 +5219,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685a7d121ee3f65ae4fddd72b25a04bb36b6af81bc0828f7d5434c0fe60fa3a2" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" dependencies = [ "libc", ] @@ -5234,13 +5253,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" dependencies = [ "base64 0.21.7", - "pem", + "pem 1.1.1", "ring 0.16.20", "serde", "serde_json", "simple_asn1", ] +[[package]] +name = "jsonwebtoken" +version = "9.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ae10193d25051e74945f1ea2d0b42e03cc3b890f7e4cc5faa44997d808193f" +dependencies = [ + "base64 0.21.7", + "js-sys", + "pem 3.0.4", + "ring 0.17.8", + "serde", + "serde_json", + "simple_asn1", +] + [[package]] name = "k256" version = "0.13.3" @@ -6148,6 +6182,16 @@ dependencies = [ "base64 0.13.1", ] +[[package]] +name = "pem" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +dependencies = [ + "base64 0.22.0", + "serde", +] + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -6888,7 +6932,6 @@ dependencies = [ "percent-encoding", "pin-project-lite", "rustls 0.21.11", - "rustls-native-certs 0.6.3", "rustls-pemfile 1.0.4", "serde", "serde_json", @@ -7233,7 +7276,7 @@ dependencies = [ "log", "ring 0.17.8", "rustls-pki-types", - "rustls-webpki 0.102.2", + "rustls-webpki 0.102.3", "subtle", "zeroize", ] @@ -7300,9 +7343,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.2" +version = "0.102.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" +checksum = "f3bce581c0dd41bce533ce695a1437fa16a7ab5ac3ccfa99fe1a620a7885eabf" dependencies = [ "ring 0.17.8", "rustls-pki-types", diff --git a/Cargo.toml b/Cargo.toml index 08b4080bf..3513cf6c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -155,7 +155,6 @@ ethers-contract = { version = "2.0.14", default-features = false } ethers-contract-abigen = { version = "2.0.14", default-features = false } ethers-providers = { version = "2.0.14", default-features = false } ethers-signers = { version = "2.0.14", default-features = false } -ethers-middleware = { version = "2.0.14", default-features = false } ## alloy alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } @@ -168,6 +167,7 @@ alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "8808d2 alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } +alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } @@ -180,10 +180,10 @@ alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "8808d alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } alloy-primitives = { version = "0.7.1", features = ["getrandom"] } -alloy-dyn-abi = "0.7.0" -alloy-json-abi = "0.7.0" -alloy-sol-types = "0.7.0" -syn-solidity = "0.7.0" +alloy-dyn-abi = "0.7.1" +alloy-json-abi = "0.7.1" +alloy-sol-types = "0.7.1" +syn-solidity = "0.7.1" alloy-chains = "0.1" alloy-trie = "0.3.1" alloy-rlp = "0.3.3" diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index b067bfd04..afbf940fb 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -16,7 +16,11 @@ path = "src/anvil.rs" required-features = ["cli"] [build-dependencies] -vergen = { workspace = true, default-features = false, features = ["build", "git", "gitcl"] } +vergen = { workspace = true, default-features = false, features = [ + "build", + "git", + "gitcl", +] } [dependencies] # foundry internal @@ -34,6 +38,7 @@ k256.workspace = true ethers = { workspace = true, features = ["rustls", "ws", "ipc", "optimism"] } alloy-primitives = { workspace = true, features = ["serde"] } alloy-consensus = { workspace = true, features = ["k256", "kzg"] } +alloy-contract = { workspace = true, features = ["pubsub"] } alloy-network.workspace = true alloy-eips.workspace = true alloy-rlp.workspace = true @@ -77,7 +82,11 @@ rand = "0.8" eyre.workspace = true # cli -clap = { version = "4", features = ["derive", "env", "wrap_help"], optional = true } +clap = { version = "4", features = [ + "derive", + "env", + "wrap_help", +], optional = true } clap_complete = { version = "4", optional = true } chrono.workspace = true auto_impl = "1" @@ -94,8 +103,12 @@ alloy-json-abi.workspace = true ethers = { workspace = true, features = ["abigen", "optimism"] } ethers-core = { workspace = true, features = ["optimism"] } foundry-compilers = { workspace = true, features = ["project-util", "full"] } +alloy-rpc-client = { workspace = true, features = ["pubsub"] } +alloy-transport-ipc = { workspace = true, features = ["mock"] } +alloy-transport-ws.workspace = true +alloy-json-rpc.workspace = true +alloy-pubsub.workspace = true foundry-test-utils.workspace = true - pretty_assertions.workspace = true tokio = { version = "1", features = ["full"] } crc = "3.0.1" diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 261726636..3c60bb3a6 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -58,13 +58,12 @@ pub fn transaction_request_to_typed( other, } = tx; - let to = if let Some(TxKind::Call(to)) = to { TxKind::Call(to) } else { TxKind::Create }; // Special case: OP-stack deposit tx if transaction_type == Some(126) { return Some(TypedTransactionRequest::Deposit(DepositTransactionRequest { from: from.unwrap_or_default(), source_hash: other.get_deserialized::("sourceHash")?.ok()?, - kind: to, + kind: to.unwrap_or_default(), mint: other.get_deserialized::("mint")?.ok()?, value: value.unwrap_or_default(), gas_limit: gas.unwrap_or_default(), @@ -93,7 +92,7 @@ pub fn transaction_request_to_typed( gas_limit: gas.unwrap_or_default(), value: value.unwrap_or(U256::ZERO), input: input.into_input().unwrap_or_default(), - to, + to: to.unwrap_or_default(), chain_id: None, })) } @@ -106,7 +105,7 @@ pub fn transaction_request_to_typed( gas_limit: gas.unwrap_or_default(), value: value.unwrap_or(U256::ZERO), input: input.into_input().unwrap_or_default(), - to, + to: to.unwrap_or_default(), chain_id: 0, access_list: access_list.unwrap_or_default(), })) @@ -124,13 +123,13 @@ pub fn transaction_request_to_typed( gas_limit: gas.unwrap_or_default(), value: value.unwrap_or(U256::ZERO), input: input.into_input().unwrap_or_default(), - to, + to: to.unwrap_or_default(), chain_id: 0, access_list: access_list.unwrap_or_default(), })) } // EIP4844 - (Some(3), None, _, _, _, Some(_), Some(_), Some(sidecar), TxKind::Call(to)) => { + (Some(3), None, _, _, _, Some(_), Some(_), Some(sidecar), Some(to)) => { let tx = TxEip4844 { nonce: nonce.unwrap_or_default(), max_fee_per_gas: max_fee_per_gas.unwrap_or_default(), @@ -139,7 +138,10 @@ pub fn transaction_request_to_typed( gas_limit: gas.unwrap_or_default(), value: value.unwrap_or(U256::ZERO), input: input.into_input().unwrap_or_default(), - to, + to: match to { + TxKind::Call(to) => to, + TxKind::Create => Address::ZERO, + }, chain_id: 0, access_list: access_list.unwrap_or_default(), blob_versioned_hashes: blob_versioned_hashes.unwrap_or_default(), diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 883e1dd44..a5eb3237f 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -27,8 +27,7 @@ use alloy_signer_wallet::{ use alloy_transport::{Transport, TransportError}; use anvil_server::ServerConfig; use foundry_common::{ - provider::alloy::ProviderBuilder, ALCHEMY_FREE_TIER_CUPS, NON_ARCHIVE_NODE_WARNING, - REQUEST_TIMEOUT, + provider::ProviderBuilder, ALCHEMY_FREE_TIER_CUPS, NON_ARCHIVE_NODE_WARNING, REQUEST_TIMEOUT, }; use foundry_config::Config; use foundry_evm::{ diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index ac1760c6d..ba9965faa 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -62,7 +62,7 @@ use anvil_core::{ }, }; use anvil_rpc::{error::RpcError, response::ResponseResult}; -use foundry_common::provider::alloy::ProviderBuilder; +use foundry_common::provider::ProviderBuilder; use foundry_evm::{ backend::DatabaseError, decode::RevertDecoder, @@ -2188,11 +2188,11 @@ impl EthApi { { // If the request is a simple native token transfer we can optimize // We assume it's a transfer if we have no input data. - let to = if let Some(TxKind::Call(to)) = request.to { Some(to) } else { None }; + let to = request.to.as_ref().and_then(TxKind::to); let likely_transfer = request.input.clone().into_input().is_none(); if likely_transfer { if let Some(to) = to { - if let Ok(target_code) = self.backend.get_code_with_state(&state, to) { + if let Ok(target_code) = self.backend.get_code_with_state(&state, *to) { if target_code.as_ref().is_empty() { return Ok(MIN_TRANSACTION_GAS); } diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index 0f4a04f86..0d456b3c6 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -14,7 +14,7 @@ use alloy_rpc_types_trace::{ }; use alloy_transport::TransportError; use anvil_core::eth::transaction::{convert_to_anvil_receipt, ReceiptResponse}; -use foundry_common::provider::alloy::{ProviderBuilder, RetryProvider}; +use foundry_common::provider::{ProviderBuilder, RetryProvider}; use parking_lot::{ lock_api::{RwLockReadGuard, RwLockWriteGuard}, RawRwLock, RwLock, diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 1e82700ed..994dde5fd 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -28,10 +28,7 @@ use crate::{ inspector::Inspector, storage::{BlockchainStorage, InMemoryBlockStates, MinedBlockOutcome}, }, - revm::{ - db::DatabaseRef, - primitives::{AccountInfo, U256 as rU256}, - }, + revm::{db::DatabaseRef, primitives::AccountInfo}, NodeConfig, PrecompileFactory, }; use alloy_consensus::{Header, Receipt, ReceiptWithBloom}; @@ -60,7 +57,6 @@ use anvil_core::{ }; use anvil_rpc::error::RpcError; use flate2::{read::GzDecoder, write::GzEncoder, Compression}; -use foundry_common::types::ToAlloy; use foundry_evm::{ backend::{DatabaseError, DatabaseResult, RevertSnapshotAction}, constants::DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE, @@ -419,7 +415,7 @@ impl Backend { env.cfg.chain_id = fork.chain_id(); env.block = BlockEnv { - number: rU256::from(fork_block_number), + number: U256::from(fork_block_number), timestamp: U256::from(fork_block.header.timestamp), gas_limit: U256::from(fork_block.header.gas_limit), difficulty: fork_block.header.difficulty, @@ -720,7 +716,7 @@ impl Backend { let mut env = self.env.write(); env.block = BlockEnv { - number: rU256::from(num), + number: U256::from(num), timestamp: U256::from(block.header.timestamp), difficulty: block.header.difficulty, // ensures prevrandao is set @@ -937,9 +933,9 @@ impl Backend { } // increase block number for this block - env.block.number = env.block.number.saturating_add(rU256::from(1)); + env.block.number = env.block.number.saturating_add(U256::from(1)); env.block.basefee = U256::from(current_base_fee); - env.block.timestamp = rU256::from(self.time.next_timestamp()); + env.block.timestamp = U256::from(self.time.next_timestamp()); let best_hash = self.blockchain.storage.read().best_hash; @@ -1038,7 +1034,7 @@ impl Backend { } // we intentionally set the difficulty to `0` for newer blocks - env.block.difficulty = rU256::from(0); + env.block.difficulty = U256::from(0); // update env with new values *self.env.write() = env; @@ -1120,14 +1116,14 @@ impl Backend { let gas_price = gas_price.or(max_fee_per_gas).unwrap_or_else(|| self.gas_price()); let caller = from.unwrap_or_default(); - let to = if let Some(TxKind::Call(addr)) = to { Some(addr) } else { None }; + let to = to.as_ref().and_then(TxKind::to); env.tx = TxEnv { caller, gas_limit: gas_limit as u64, gas_price: U256::from(gas_price), gas_priority_fee: max_priority_fee_per_gas.map(U256::from), transact_to: match to { - Some(addr) => TransactTo::Call(addr), + Some(addr) => TransactTo::Call(*addr), None => TransactTo::Create(CreateScheme::Create), }, value: value.unwrap_or_default(), @@ -1669,7 +1665,7 @@ impl Backend { .with_pending_block(pool_transactions, |state, block| { let block = block.block; let block = BlockEnv { - number: block.header.number.to_alloy(), + number: U256::from(block.header.number), coinbase: block.header.beneficiary, timestamp: U256::from(block.header.timestamp), difficulty: block.header.difficulty, @@ -1697,9 +1693,9 @@ impl Backend { .and_then(|block| Some((states.get(&block.header.hash_slow())?, block))) { let block = BlockEnv { - number: block.header.number.to_alloy(), + number: U256::from(block.header.number), coinbase: block.header.beneficiary, - timestamp: rU256::from(block.header.timestamp), + timestamp: U256::from(block.header.timestamp), difficulty: block.header.difficulty, prevrandao: Some(block.header.mix_hash), basefee: U256::from(block.header.base_fee_per_gas.unwrap_or_default()), @@ -1722,8 +1718,8 @@ impl Backend { let gen_db = self.genesis.state_db_at_genesis(Box::new(&*db)); block.number = block_number; - block.timestamp = rU256::from(fork.timestamp()); - block.basefee = rU256::from(fork.base_fee().unwrap_or_default()); + block.timestamp = U256::from(fork.timestamp()); + block.basefee = U256::from(fork.base_fee().unwrap_or_default()); return Ok(f(Box::new(&gen_db), block)); } diff --git a/crates/anvil/src/eth/error.rs b/crates/anvil/src/eth/error.rs index 9bd3d64a4..cc7517b42 100644 --- a/crates/anvil/src/eth/error.rs +++ b/crates/anvil/src/eth/error.rs @@ -1,8 +1,8 @@ //! Aggregated error type for this module use crate::eth::pool::transactions::PoolTransaction; -use alloy_primitives::{Bytes, SignatureError as AlloySignatureError}; -use alloy_signer::Error as AlloySignerError; +use alloy_primitives::{Bytes, SignatureError}; +use alloy_signer::Error as SignerError; use alloy_transport::TransportError; use anvil_rpc::{ error::{ErrorCode, RpcError}, @@ -44,9 +44,9 @@ pub enum BlockchainError { #[error("Prevrandao not in th EVM's environment after merge")] PrevrandaoNotSet, #[error(transparent)] - AlloySignatureError(#[from] AlloySignatureError), + SignatureError(#[from] SignatureError), #[error(transparent)] - AlloySignerError(#[from] AlloySignerError), + SignerError(#[from] SignerError), #[error("Rpc Endpoint not implemented")] RpcUnimplemented, #[error("Rpc error {0:?}")] @@ -356,10 +356,8 @@ impl ToRpcResponseResult for Result { BlockchainError::FailedToDecodeStateDump => { RpcError::invalid_params("Failed to decode state dump") } - BlockchainError::AlloySignerError(err) => RpcError::invalid_params(err.to_string()), - BlockchainError::AlloySignatureError(err) => { - RpcError::invalid_params(err.to_string()) - } + BlockchainError::SignerError(err) => RpcError::invalid_params(err.to_string()), + BlockchainError::SignatureError(err) => RpcError::invalid_params(err.to_string()), BlockchainError::RpcUnimplemented => { RpcError::internal_error_with("Not implemented") } diff --git a/crates/anvil/src/eth/pool/mod.rs b/crates/anvil/src/eth/pool/mod.rs index ace226fe5..c94fafaca 100644 --- a/crates/anvil/src/eth/pool/mod.rs +++ b/crates/anvil/src/eth/pool/mod.rs @@ -75,8 +75,8 @@ impl Pool { /// Returns the number of tx that are ready and queued for further execution pub fn txpool_status(&self) -> TxpoolStatus { // Note: naming differs here compared to geth's `TxpoolStatus` - let pending = self.ready_transactions().count() as u64; - let queued = self.inner.read().pending_transactions.len() as u64; + let pending: u64 = self.ready_transactions().count().try_into().unwrap_or(0); + let queued: u64 = self.inner.read().pending_transactions.len().try_into().unwrap_or(0); TxpoolStatus { pending, queued } } diff --git a/crates/anvil/src/filter.rs b/crates/anvil/src/filter.rs index 60dce6b66..268a8f46e 100644 --- a/crates/anvil/src/filter.rs +++ b/crates/anvil/src/filter.rs @@ -5,7 +5,7 @@ use crate::{ StorageInfo, }; use alloy_primitives::TxHash; -use alloy_rpc_types::{Filter, FilteredParams, Log as AlloyLog}; +use alloy_rpc_types::{Filter, FilteredParams, Log}; use anvil_core::eth::subscription::SubscriptionId; use anvil_rpc::response::ResponseResult; use futures::{channel::mpsc::Receiver, Stream, StreamExt}; @@ -162,14 +162,14 @@ pub struct LogsFilter { /// existing logs that matched the filter when the listener was installed /// /// They'll be returned on the first pill - pub historic: Option>, + pub historic: Option>, } // === impl LogsFilter === impl LogsFilter { /// Returns all the logs since the last time this filter was polled - pub fn poll(&mut self, cx: &mut Context<'_>) -> Vec { + pub fn poll(&mut self, cx: &mut Context<'_>) -> Vec { let mut logs = self.historic.take().unwrap_or_default(); while let Poll::Ready(Some(block)) = self.blocks.poll_next_unpin(cx) { let b = self.storage.block(block.hash); diff --git a/crates/anvil/src/lib.rs b/crates/anvil/src/lib.rs index b08fd7e9c..ce6a85411 100644 --- a/crates/anvil/src/lib.rs +++ b/crates/anvil/src/lib.rs @@ -19,7 +19,7 @@ use crate::{ use alloy_primitives::{Address, U256}; use alloy_signer_wallet::LocalWallet; use eth::backend::fork::ClientFork; -use foundry_common::provider::alloy::{ProviderBuilder, RetryProvider}; +use foundry_common::provider::{ProviderBuilder, RetryProvider}; use foundry_evm::revm; use futures::{FutureExt, TryFutureExt}; use parking_lot::Mutex; diff --git a/crates/anvil/tests/it/abi.rs b/crates/anvil/tests/it/abi.rs index c9cb0d7b1..fda13dc46 100644 --- a/crates/anvil/tests/it/abi.rs +++ b/crates/anvil/tests/it/abi.rs @@ -1,49 +1,52 @@ -//! commonly used abigen generated types +//! commonly used sol generated types +use alloy_sol_types::sol; -use ethers::{ - contract::{abigen, EthEvent}, - types::Address, -}; +sol!( + #[sol(rpc)] + Greeter, + "test-data/greeter.json" +); -#[derive(Clone, Debug, EthEvent)] -pub struct ValueChanged { - #[ethevent(indexed)] - pub old_author: Address, - #[ethevent(indexed)] - pub new_author: Address, - pub old_value: String, - pub new_value: String, -} +sol!( + #[derive(Debug)] + #[sol(rpc)] + SimpleStorage, + "test-data/SimpleStorage.json" +); -abigen!(Greeter, "test-data/greeter.json"); -abigen!(SimpleStorage, "test-data/SimpleStorage.json"); -abigen!(MulticallContract, "test-data/multicall.json"); -abigen!( - Erc721, - r#"[ - balanceOf(address)(uint256) - ownerOf(uint256)(address) - name()(string) - symbol()(string) - tokenURI(uint256)(string) - getApproved(uint256)(address) - setApprovalForAll(address,bool) - isApprovedForAll(address,address) - transferFrom(address,address,uint256) - safeTransferFrom(address,address,uint256,bytes) - _transfer(address,address,uint256) - _approve(address, uint256) - _burn(uint256) - _safeMint(address,uint256,bytes) - _mint(address,uint256) - _exists(uint256)(bool) -]"# +sol!( + #[sol(rpc)] + MulticallContract, + "test-data/multicall.json" +); + +sol!( + #[sol(rpc)] + contract BUSD { + function balanceOf(address) external view returns (uint256); + } ); -abigen!( - BUSD, - r#"[ - balanceOf(address)(uint256) -]"# + +sol!( + #[sol(rpc)] + interface ERC721 { + function balanceOf(address owner) public view virtual returns (uint256); + function ownerOf(uint256 tokenId) public view virtual returns (address); + function name() public view virtual returns (string memory); + function symbol() public view virtual returns (string memory); + function tokenURI(uint256 tokenId) public view virtual returns (string memory); + function getApproved(uint256 tokenId) public view virtual returns (address); + function setApprovalForAll(address operator, bool approved) public virtual; + function isApprovedForAll(address owner, address operator) public view virtual returns (bool); + function transferFrom(address from, address to, uint256 tokenId) public virtual; + function safeTransferFrom(address from, address to, uint256 tokenId) public; + function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual; + function _mint(address to, uint256 tokenId) internal; + function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual; + function _burn(uint256 tokenId) internal; + function _transfer(address from, address to, uint256 tokenId) internal; + function _approve(address to, uint256 tokenId, address auth) internal; + } ); // @@ -70,3 +73,12 @@ contract VendingMachine { payable(msg.sender).transfer(address(this).balance); } }"#; + +sol!( + #[sol(rpc)] + contract VendingMachine { + function buyRevert(uint amount) external payable; + function buyRequire(uint amount) external payable; + function withdraw() external; + } +); diff --git a/crates/anvil/tests/it/anvil.rs b/crates/anvil/tests/it/anvil.rs index 3a39f7e74..7aff5660e 100644 --- a/crates/anvil/tests/it/anvil.rs +++ b/crates/anvil/tests/it/anvil.rs @@ -1,52 +1,46 @@ //! tests for anvil specific logic -use crate::utils::ethers_http_provider; +use alloy_primitives::Address; +use alloy_provider::Provider; use anvil::{spawn, NodeConfig}; -use ethers::{prelude::Middleware, types::Address}; -use foundry_common::types::ToAlloy; #[tokio::test(flavor = "multi_thread")] async fn test_can_change_mining_mode() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); assert!(api.anvil_get_auto_mine().unwrap()); let num = provider.get_block_number().await.unwrap(); - assert_eq!(num.as_u64(), 0); + assert_eq!(num, 0); api.anvil_set_interval_mining(1).unwrap(); assert!(!api.anvil_get_auto_mine().unwrap()); // changing the mining mode will instantly mine a new block tokio::time::sleep(std::time::Duration::from_millis(500)).await; let num = provider.get_block_number().await.unwrap(); - assert_eq!(num.as_u64(), 0); + assert_eq!(num, 0); tokio::time::sleep(std::time::Duration::from_millis(700)).await; let num = provider.get_block_number().await.unwrap(); - assert_eq!(num.as_u64(), 1); + assert_eq!(num, 1); // assert that no block is mined when the interval is set to 0 api.anvil_set_interval_mining(0).unwrap(); assert!(!api.anvil_get_auto_mine().unwrap()); tokio::time::sleep(std::time::Duration::from_millis(1000)).await; let num = provider.get_block_number().await.unwrap(); - assert_eq!(num.as_u64(), 1); + assert_eq!(num, 1); } #[tokio::test(flavor = "multi_thread")] async fn can_get_default_dev_keys() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); let dev_accounts = handle.dev_accounts().collect::>(); - let accounts = provider - .get_accounts() - .await - .unwrap() - .into_iter() - .map(ToAlloy::to_alloy) - .collect::>(); + let accounts = provider.get_accounts().await.unwrap(); + assert_eq!(dev_accounts, accounts); } @@ -54,8 +48,8 @@ async fn can_get_default_dev_keys() { async fn can_set_empty_code() { let (api, _handle) = spawn(NodeConfig::test()).await; let addr = Address::random(); - api.anvil_set_code(addr.to_alloy(), Vec::new().into()).await.unwrap(); - let code = api.get_code(addr.to_alloy(), None).await.unwrap(); + api.anvil_set_code(addr, Vec::new().into()).await.unwrap(); + let code = api.get_code(addr, None).await.unwrap(); assert!(code.as_ref().is_empty()); } @@ -64,15 +58,18 @@ async fn test_can_set_genesis_timestamp() { let genesis_timestamp = 1000u64; let (_api, handle) = spawn(NodeConfig::test().with_genesis_timestamp(genesis_timestamp.into())).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); - assert_eq!(genesis_timestamp, provider.get_block(0).await.unwrap().unwrap().timestamp.as_u64()); + assert_eq!( + genesis_timestamp, + provider.get_block(0.into(), false).await.unwrap().unwrap().header.timestamp + ); } #[tokio::test(flavor = "multi_thread")] async fn test_can_use_default_genesis_timestamp() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); - assert_ne!(0u64, provider.get_block(0).await.unwrap().unwrap().timestamp.as_u64()); + assert_ne!(0u64, provider.get_block(0.into(), false).await.unwrap().unwrap().header.timestamp); } diff --git a/crates/anvil/tests/it/anvil_api.rs b/crates/anvil/tests/it/anvil_api.rs index d2826b9ed..01247fb28 100644 --- a/crates/anvil/tests/it/anvil_api.rs +++ b/crates/anvil/tests/it/anvil_api.rs @@ -1,50 +1,45 @@ //! tests for custom anvil endpoints -use crate::{abi::*, fork::fork_config, utils::ethers_http_provider}; -use alloy_rpc_types::BlockNumberOrTag; + +use crate::{ + abi::{Greeter, MulticallContract, BUSD}, + fork::fork_config, + utils::http_provider_with_signer, +}; +use alloy_network::{EthereumSigner, TransactionBuilder}; +use alloy_primitives::{address, fixed_bytes, Address, U256, U64}; +use alloy_provider::{txpool::TxPoolApi, Provider}; +use alloy_rpc_types::{BlockId, BlockNumberOrTag, TransactionRequest, WithOtherFields}; use anvil::{eth::api::CLIENT_VERSION, spawn, Hardfork, NodeConfig}; use anvil_core::{ eth::EthRequest, types::{AnvilMetadata, ForkedNetwork, Forking, NodeEnvironment, NodeForkConfig, NodeInfo}, }; -use ethers::{ - abi::{ethereum_types::BigEndianHash, AbiDecode}, - prelude::{Middleware, SignerMiddleware}, - signers::Signer, - types::{ - transaction::eip2718::TypedTransaction, Address, BlockNumber, Eip1559TransactionRequest, - TransactionRequest, H256, U256, U64, - }, - utils::hex, -}; -use foundry_common::types::{ToAlloy, ToEthers}; use foundry_evm::revm::primitives::SpecId; use std::{ str::FromStr, - sync::Arc, time::{Duration, SystemTime}, }; #[tokio::test(flavor = "multi_thread")] async fn can_set_gas_price() { let (api, handle) = spawn(NodeConfig::test().with_hardfork(Some(Hardfork::Berlin))).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); - let gas_price: U256 = 1337u64.into(); - api.anvil_set_min_gas_price(gas_price.to_alloy()).await.unwrap(); - assert_eq!(gas_price, provider.get_gas_price().await.unwrap()); + let gas_price = U256::from(1337); + api.anvil_set_min_gas_price(gas_price).await.unwrap(); + assert_eq!(gas_price.to::(), provider.get_gas_price().await.unwrap()); } #[tokio::test(flavor = "multi_thread")] async fn can_set_block_gas_limit() { let (api, _) = spawn(NodeConfig::test().with_hardfork(Some(Hardfork::Berlin))).await; - let block_gas_limit: U256 = 1337u64.into(); - assert!(api.evm_set_block_gas_limit(block_gas_limit.to_alloy()).unwrap()); + let block_gas_limit = U256::from(1337); + assert!(api.evm_set_block_gas_limit(block_gas_limit).unwrap()); // Mine a new block, and check the new block gas limit api.mine_one().await; - let latest_block = - api.block_by_number(alloy_rpc_types::BlockNumberOrTag::Latest).await.unwrap().unwrap(); - assert_eq!(block_gas_limit.as_u128(), latest_block.header.gas_limit); + let latest_block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); + assert_eq!(block_gas_limit.to::(), latest_block.header.gas_limit); } // Ref @@ -62,227 +57,226 @@ async fn can_set_storage() { let storage_value = api.storage_at(addr, slot, None).await.unwrap(); assert_eq!(val, storage_value); - assert_eq!(val.to_ethers(), H256::from_uint(&U256::from(12345))); } #[tokio::test(flavor = "multi_thread")] async fn can_impersonate_account() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + + let provider = handle.http_provider(); let impersonate = Address::random(); let to = Address::random(); - let val = 1337u64; + let val = U256::from(1337); let funding = U256::from(1e18 as u64); // fund the impersonated account - api.anvil_set_balance(impersonate.to_alloy(), funding.to_alloy()).await.unwrap(); + api.anvil_set_balance(impersonate, funding).await.unwrap(); - let balance = api.balance(impersonate.to_alloy(), None).await.unwrap(); - assert_eq!(balance, funding.to_alloy()); + let balance = api.balance(impersonate, None).await.unwrap(); + assert_eq!(balance, funding); - let tx = TransactionRequest::new().from(impersonate).to(to).value(val); + let tx = TransactionRequest::default().with_from(impersonate).with_to(to).with_value(val); + let tx = WithOtherFields::new(tx); - let res = provider.send_transaction(tx.clone(), None).await; + let res = provider.send_transaction(tx.clone()).await; res.unwrap_err(); - api.anvil_impersonate_account(impersonate.to_alloy()).await.unwrap(); - assert!(api.accounts().unwrap().contains(&impersonate.to_alloy())); + api.anvil_impersonate_account(impersonate).await.unwrap(); + assert!(api.accounts().unwrap().contains(&impersonate)); - let res = provider.send_transaction(tx.clone(), None).await.unwrap().await.unwrap().unwrap(); + let res = provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); assert_eq!(res.from, impersonate); - let nonce = provider.get_transaction_count(impersonate, None).await.unwrap(); - assert_eq!(nonce, 1u64.into()); + let nonce = provider.get_transaction_count(impersonate, BlockId::latest()).await.unwrap(); + assert_eq!(nonce, 1); - let balance = provider.get_balance(to, None).await.unwrap(); - assert_eq!(balance, val.into()); + let balance = provider.get_balance(to, BlockId::latest()).await.unwrap(); + assert_eq!(balance, val); - api.anvil_stop_impersonating_account(impersonate.to_alloy()).await.unwrap(); - let res = provider.send_transaction(tx, None).await; + api.anvil_stop_impersonating_account(impersonate).await.unwrap(); + let res = provider.send_transaction(tx).await; res.unwrap_err(); } #[tokio::test(flavor = "multi_thread")] async fn can_auto_impersonate_account() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + + let provider = handle.http_provider(); let impersonate = Address::random(); let to = Address::random(); - let val = 1337u64; + let val = U256::from(1337); let funding = U256::from(1e18 as u64); // fund the impersonated account - api.anvil_set_balance(impersonate.to_alloy(), funding.to_alloy()).await.unwrap(); + api.anvil_set_balance(impersonate, funding).await.unwrap(); - let balance = api.balance(impersonate.to_alloy(), None).await.unwrap(); - assert_eq!(balance, funding.to_alloy()); + let balance = api.balance(impersonate, None).await.unwrap(); + assert_eq!(balance, funding); - let tx = TransactionRequest::new().from(impersonate).to(to).value(val); + let tx = TransactionRequest::default().with_from(impersonate).with_to(to).with_value(val); + let tx = WithOtherFields::new(tx); - let res = provider.send_transaction(tx.clone(), None).await; + let res = provider.send_transaction(tx.clone()).await; res.unwrap_err(); api.anvil_auto_impersonate_account(true).await.unwrap(); - let res = provider.send_transaction(tx.clone(), None).await.unwrap().await.unwrap().unwrap(); + let res = provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); assert_eq!(res.from, impersonate); - let nonce = provider.get_transaction_count(impersonate, None).await.unwrap(); - assert_eq!(nonce, 1u64.into()); + let nonce = provider.get_transaction_count(impersonate, BlockId::latest()).await.unwrap(); + assert_eq!(nonce, 1); - let balance = provider.get_balance(to, None).await.unwrap(); - assert_eq!(balance, val.into()); + let balance = provider.get_balance(to, BlockId::latest()).await.unwrap(); + assert_eq!(balance, val); api.anvil_auto_impersonate_account(false).await.unwrap(); - let res = provider.send_transaction(tx, None).await; + let res = provider.send_transaction(tx).await; res.unwrap_err(); // explicitly impersonated accounts get returned by `eth_accounts` - api.anvil_impersonate_account(impersonate.to_alloy()).await.unwrap(); - assert!(api.accounts().unwrap().contains(&impersonate.to_alloy())); + api.anvil_impersonate_account(impersonate).await.unwrap(); + assert!(api.accounts().unwrap().contains(&impersonate)); } #[tokio::test(flavor = "multi_thread")] async fn can_impersonate_contract() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let provider = Arc::new(SignerMiddleware::new(provider, wallet)); + let provider = handle.http_provider(); - let greeter_contract = - Greeter::deploy(provider, "Hello World!".to_string()).unwrap().send().await.unwrap(); - let impersonate = greeter_contract.address(); + let greeter_contract = Greeter::deploy(&provider, "Hello World!".to_string()).await.unwrap(); + let impersonate = greeter_contract.address().to_owned(); let to = Address::random(); - let val = 1337u64; - - let provider = ethers_http_provider(&handle.http_endpoint()); + let val = U256::from(1337); - // fund the impersonated account - api.anvil_set_balance(impersonate.to_alloy(), U256::from(1e18 as u64).to_alloy()) - .await - .unwrap(); + // // fund the impersonated account + api.anvil_set_balance(impersonate, U256::from(1e18 as u64)).await.unwrap(); - let tx = TransactionRequest::new().from(impersonate).to(to).value(val); + let tx = TransactionRequest::default().with_from(impersonate).to(to).with_value(val); + let tx = WithOtherFields::new(tx); - let res = provider.send_transaction(tx.clone(), None).await; + let res = provider.send_transaction(tx.clone()).await; res.unwrap_err(); - let greeting = greeter_contract.greet().call().await.unwrap(); + let greeting = greeter_contract.greet().call().await.unwrap()._0; assert_eq!("Hello World!", greeting); - api.anvil_impersonate_account(impersonate.to_alloy()).await.unwrap(); + api.anvil_impersonate_account(impersonate).await.unwrap(); - let res = provider.send_transaction(tx.clone(), None).await.unwrap().await.unwrap().unwrap(); + let res = provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); assert_eq!(res.from, impersonate); - let balance = provider.get_balance(to, None).await.unwrap(); - assert_eq!(balance, val.into()); + let balance = provider.get_balance(to, BlockId::latest()).await.unwrap(); + assert_eq!(balance, val); - api.anvil_stop_impersonating_account(impersonate.to_alloy()).await.unwrap(); - let res = provider.send_transaction(tx, None).await; + api.anvil_stop_impersonating_account(impersonate).await.unwrap(); + let res = provider.send_transaction(tx).await; res.unwrap_err(); - let greeting = greeter_contract.greet().call().await.unwrap(); + let greeting = greeter_contract.greet().call().await.unwrap()._0; assert_eq!("Hello World!", greeting); } #[tokio::test(flavor = "multi_thread")] async fn can_impersonate_gnosis_safe() { let (api, handle) = spawn(fork_config()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); // - let safe: Address = "0xA063Cb7CFd8E57c30c788A0572CBbf2129ae56B6".parse().unwrap(); + let safe = address!("A063Cb7CFd8E57c30c788A0572CBbf2129ae56B6"); - let code = provider.get_code(safe, None).await.unwrap(); + let code = provider.get_code_at(safe, BlockId::default()).await.unwrap(); assert!(!code.is_empty()); - api.anvil_impersonate_account(safe.to_alloy()).await.unwrap(); + api.anvil_impersonate_account(safe).await.unwrap(); - let code = provider.get_code(safe, None).await.unwrap(); + let code = provider.get_code_at(safe, BlockId::default()).await.unwrap(); assert!(!code.is_empty()); let balance = U256::from(1e18 as u64); // fund the impersonated account - api.anvil_set_balance(safe.to_alloy(), balance.to_alloy()).await.unwrap(); + api.anvil_set_balance(safe, balance).await.unwrap(); - let on_chain_balance = provider.get_balance(safe, None).await.unwrap(); + let on_chain_balance = provider.get_balance(safe, BlockId::latest()).await.unwrap(); assert_eq!(on_chain_balance, balance); - api.anvil_stop_impersonating_account(safe.to_alloy()).await.unwrap(); + api.anvil_stop_impersonating_account(safe).await.unwrap(); - let code = provider.get_code(safe, None).await.unwrap(); + let code = provider.get_code_at(safe, BlockId::default()).await.unwrap(); // code is added back after stop impersonating assert!(!code.is_empty()); } #[tokio::test(flavor = "multi_thread")] -async fn can_impersonate_multiple_account() { +async fn can_impersonate_multiple_accounts() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); let impersonate0 = Address::random(); let impersonate1 = Address::random(); let to = Address::random(); - let val = 1337u64; + let val = U256::from(1337); let funding = U256::from(1e18 as u64); // fund the impersonated accounts - api.anvil_set_balance(impersonate0.to_alloy(), funding.to_alloy()).await.unwrap(); - api.anvil_set_balance(impersonate1.to_alloy(), funding.to_alloy()).await.unwrap(); + api.anvil_set_balance(impersonate0, funding).await.unwrap(); + api.anvil_set_balance(impersonate1, funding).await.unwrap(); - let tx = TransactionRequest::new().from(impersonate0).to(to).value(val); + let tx = TransactionRequest::default().with_from(impersonate0).to(to).with_value(val); + let tx = WithOtherFields::new(tx); - api.anvil_impersonate_account(impersonate0.to_alloy()).await.unwrap(); - api.anvil_impersonate_account(impersonate1.to_alloy()).await.unwrap(); + api.anvil_impersonate_account(impersonate0).await.unwrap(); + api.anvil_impersonate_account(impersonate1).await.unwrap(); - let res0 = provider.send_transaction(tx.clone(), None).await.unwrap().await.unwrap().unwrap(); + let res0 = provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); assert_eq!(res0.from, impersonate0); - let nonce = provider.get_transaction_count(impersonate0, None).await.unwrap(); - assert_eq!(nonce, 1u64.into()); + let nonce = provider.get_transaction_count(impersonate0, BlockId::latest()).await.unwrap(); + assert_eq!(nonce, 1); let receipt = provider.get_transaction_receipt(res0.transaction_hash).await.unwrap().unwrap(); - assert_eq!(res0, receipt); + assert_eq!(res0.inner, receipt.inner); let res1 = provider - .send_transaction(tx.from(impersonate1), None) + .send_transaction(tx.with_from(impersonate1)) .await .unwrap() + .get_receipt() .await - .unwrap() .unwrap(); + assert_eq!(res1.from, impersonate1); - let nonce = provider.get_transaction_count(impersonate1, None).await.unwrap(); - assert_eq!(nonce, 1u64.into()); + let nonce = provider.get_transaction_count(impersonate1, BlockId::latest()).await.unwrap(); + assert_eq!(nonce, 1); let receipt = provider.get_transaction_receipt(res1.transaction_hash).await.unwrap().unwrap(); - assert_eq!(res1, receipt); + assert_eq!(res1.inner, receipt.inner); - assert_ne!(res0, res1); + assert_ne!(res0.inner, res1.inner); } #[tokio::test(flavor = "multi_thread")] async fn can_mine_manually() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); let start_num = provider.get_block_number().await.unwrap(); for (idx, _) in std::iter::repeat(()).take(10).enumerate() { api.evm_mine(None).await.unwrap(); let num = provider.get_block_number().await.unwrap(); - assert_eq!(num, start_num + idx + 1); + assert_eq!(num, start_num + idx as u64 + 1); } } #[tokio::test(flavor = "multi_thread")] async fn test_set_next_timestamp() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); @@ -293,23 +287,23 @@ async fn test_set_next_timestamp() { api.evm_mine(None).await.unwrap(); - let block = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::default(), false).await.unwrap().unwrap(); - assert_eq!(block.number.unwrap().as_u64(), 1); - assert_eq!(block.timestamp.as_u64(), next_timestamp.as_secs()); + assert_eq!(block.header.number.unwrap(), 1); + assert_eq!(block.header.timestamp, next_timestamp.as_secs()); api.evm_mine(None).await.unwrap(); - let next = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); - assert_eq!(next.number.unwrap().as_u64(), 2); + let next = provider.get_block(BlockId::default(), false).await.unwrap().unwrap(); + assert_eq!(next.header.number.unwrap(), 2); - assert!(next.timestamp > block.timestamp); + assert!(next.header.timestamp > block.header.timestamp); } #[tokio::test(flavor = "multi_thread")] async fn test_evm_set_time() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); @@ -320,20 +314,20 @@ async fn test_evm_set_time() { // mine a block api.evm_mine(None).await.unwrap(); - let block = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::default(), false).await.unwrap().unwrap(); - assert!(block.timestamp.as_u64() >= timestamp.as_secs()); + assert!(block.header.timestamp >= timestamp.as_secs()); api.evm_mine(None).await.unwrap(); - let next = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); + let next = provider.get_block(BlockId::default(), false).await.unwrap().unwrap(); - assert!(next.timestamp > block.timestamp); + assert!(next.header.timestamp > block.header.timestamp); } #[tokio::test(flavor = "multi_thread")] async fn test_evm_set_time_in_past() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); @@ -344,59 +338,59 @@ async fn test_evm_set_time_in_past() { // mine a block api.evm_mine(None).await.unwrap(); - let block = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::default(), false).await.unwrap().unwrap(); - assert!(block.timestamp.as_u64() >= timestamp.as_secs()); - assert!(block.timestamp.as_u64() < now.as_secs()); + assert!(block.header.timestamp >= timestamp.as_secs()); + assert!(block.header.timestamp < now.as_secs()); } #[tokio::test(flavor = "multi_thread")] async fn test_timestamp_interval() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); api.evm_mine(None).await.unwrap(); let interval = 10; for _ in 0..5 { - let block = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::default(), false).await.unwrap().unwrap(); // mock timestamp api.evm_set_block_timestamp_interval(interval).unwrap(); api.evm_mine(None).await.unwrap(); - let new_block = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); + let new_block = provider.get_block(BlockId::default(), false).await.unwrap().unwrap(); - assert_eq!(new_block.timestamp, block.timestamp + interval); + assert_eq!(new_block.header.timestamp, block.header.timestamp + interval); } - let block = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::default(), false).await.unwrap().unwrap(); - let next_timestamp = block.timestamp + 50; - api.evm_set_next_block_timestamp(next_timestamp.as_u64()).unwrap(); + let next_timestamp = block.header.timestamp + 50; + api.evm_set_next_block_timestamp(next_timestamp).unwrap(); api.evm_mine(None).await.unwrap(); - let block = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); - assert_eq!(block.timestamp, next_timestamp); + let block = provider.get_block(BlockId::default(), false).await.unwrap().unwrap(); + assert_eq!(block.header.timestamp, next_timestamp); api.evm_mine(None).await.unwrap(); - let block = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::default(), false).await.unwrap().unwrap(); // interval also works after setting the next timestamp manually - assert_eq!(block.timestamp, next_timestamp + interval); + assert_eq!(block.header.timestamp, next_timestamp + interval); assert!(api.evm_remove_block_timestamp_interval().unwrap()); api.evm_mine(None).await.unwrap(); - let new_block = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); + let new_block = provider.get_block(BlockId::default(), false).await.unwrap().unwrap(); // offset is applied correctly after resetting the interval - assert!(new_block.timestamp > block.timestamp); + assert!(new_block.header.timestamp > block.header.timestamp); api.evm_mine(None).await.unwrap(); - let another_block = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); + let another_block = provider.get_block(BlockId::default(), false).await.unwrap().unwrap(); // check interval is disabled - assert!(another_block.timestamp - new_block.timestamp < U256::from(interval)); + assert!(another_block.header.timestamp - new_block.header.timestamp < interval); } // @@ -404,26 +398,25 @@ async fn test_timestamp_interval() { async fn test_can_set_storage_bsc_fork() { let (api, handle) = spawn(NodeConfig::test().with_eth_rpc_url(Some("https://bsc-dataseed.binance.org/"))).await; - let provider = Arc::new(ethers_http_provider(&handle.http_endpoint())); - - let busd_addr: Address = "0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56".parse().unwrap(); - let idx: U256 = - "0xa6eef7e35abe7026729641147f7915573c7e97b47efa546f5f6e3230263bcb49".parse().unwrap(); - let value: H256 = - "0x0000000000000000000000000000000000000000000000000000000000003039".parse().unwrap(); + let provider = handle.http_provider(); - api.anvil_set_storage_at(busd_addr.to_alloy(), idx.to_alloy(), value.to_alloy()).await.unwrap(); - let storage = api.storage_at(busd_addr.to_alloy(), idx.to_alloy(), None).await.unwrap(); - assert_eq!(storage.to_ethers(), value); + let busd_addr = address!("e9e7CEA3DedcA5984780Bafc599bD69ADd087D56"); + let idx = U256::from_str("0xa6eef7e35abe7026729641147f7915573c7e97b47efa546f5f6e3230263bcb49") + .unwrap(); + let value = fixed_bytes!("0000000000000000000000000000000000000000000000000000000000003039"); - let input = - hex::decode("70a082310000000000000000000000000000000000000000000000000000000000000000") - .unwrap(); + api.anvil_set_storage_at(busd_addr, idx, value).await.unwrap(); + let storage = api.storage_at(busd_addr, idx, None).await.unwrap(); + assert_eq!(storage, value); - let busd = BUSD::new(busd_addr, provider); - let call = busd::BalanceOfCall::decode(&input).unwrap(); + let busd_contract = BUSD::new(busd_addr, &provider); - let balance = busd.balance_of(call.0).call().await.unwrap(); + let BUSD::balanceOfReturn { _0 } = busd_contract + .balanceOf(address!("0000000000000000000000000000000000000000")) + .call() + .await + .unwrap(); + let balance = _0; assert_eq!(balance, U256::from(12345u64)); } @@ -433,22 +426,22 @@ async fn can_get_node_info() { let node_info = api.anvil_node_info().await.unwrap(); - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); let block_number = provider.get_block_number().await.unwrap(); - let block = provider.get_block(block_number).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::from(block_number), false).await.unwrap().unwrap(); let expected_node_info = NodeInfo { - current_block_number: U64([0]).to_alloy(), + current_block_number: U64::from(0), current_block_timestamp: 1, - current_block_hash: block.hash.unwrap().to_alloy(), + current_block_hash: block.header.hash.unwrap(), hard_fork: SpecId::CANCUN, transaction_order: "fees".to_owned(), environment: NodeEnvironment { - base_fee: alloy_primitives::U256::from_str("0x3b9aca00").unwrap().to(), + base_fee: U256::from_str("0x3b9aca00").unwrap().to(), chain_id: 0x7a69, - gas_limit: alloy_primitives::U256::from_str("0x1c9c380").unwrap().to(), - gas_price: alloy_primitives::U256::from_str("0x77359400").unwrap().to(), + gas_limit: U256::from_str("0x1c9c380").unwrap().to(), + gas_price: U256::from_str("0x77359400").unwrap().to(), }, fork_config: NodeForkConfig { fork_url: None, @@ -466,14 +459,14 @@ async fn can_get_metadata() { let metadata = api.anvil_metadata().await.unwrap(); - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); - let block_number = provider.get_block_number().await.unwrap().as_u64(); - let chain_id = provider.get_chainid().await.unwrap().as_u64(); - let block = provider.get_block(block_number).await.unwrap().unwrap(); + let block_number = provider.get_block_number().await.unwrap(); + let chain_id = provider.get_chain_id().await.unwrap(); + let block = provider.get_block(BlockId::from(block_number), false).await.unwrap().unwrap(); let expected_metadata = AnvilMetadata { - latest_block_hash: block.hash.unwrap().to_alloy(), + latest_block_hash: block.header.hash.unwrap(), latest_block_number: block_number, chain_id, client_version: CLIENT_VERSION, @@ -489,16 +482,16 @@ async fn can_get_metadata() { async fn can_get_metadata_on_fork() { let (api, handle) = spawn(NodeConfig::test().with_eth_rpc_url(Some("https://bsc-dataseed.binance.org/"))).await; - let provider = Arc::new(ethers_http_provider(&handle.http_endpoint())); + let provider = handle.http_provider(); let metadata = api.anvil_metadata().await.unwrap(); - let block_number = provider.get_block_number().await.unwrap().as_u64(); - let chain_id = provider.get_chainid().await.unwrap().as_u64(); - let block = provider.get_block(block_number).await.unwrap().unwrap(); + let block_number = provider.get_block_number().await.unwrap(); + let chain_id = provider.get_chain_id().await.unwrap(); + let block = provider.get_block(BlockId::from(block_number), false).await.unwrap().unwrap(); let expected_metadata = AnvilMetadata { - latest_block_hash: block.hash.unwrap().to_alloy(), + latest_block_hash: block.header.hash.unwrap(), latest_block_number: block_number, chain_id, client_version: CLIENT_VERSION, @@ -506,7 +499,7 @@ async fn can_get_metadata_on_fork() { forked_network: Some(ForkedNetwork { chain_id, fork_block_number: block_number, - fork_block_hash: block.hash.unwrap().to_alloy(), + fork_block_hash: block.header.hash.unwrap(), }), snapshots: Default::default(), }; @@ -533,46 +526,45 @@ async fn metadata_changes_on_reset() { #[tokio::test(flavor = "multi_thread")] async fn test_get_transaction_receipt() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); // set the base fee - let new_base_fee = U256::from(1_000); - api.anvil_set_next_block_base_fee_per_gas(new_base_fee.to_alloy()).await.unwrap(); + let new_base_fee = U256::from(1000); + api.anvil_set_next_block_base_fee_per_gas(new_base_fee).await.unwrap(); // send a EIP-1559 transaction - let tx = - TypedTransaction::Eip1559(Eip1559TransactionRequest::new().gas(U256::from(30_000_000))); - let receipt = - provider.send_transaction(tx.clone(), None).await.unwrap().await.unwrap().unwrap(); + let to = Address::random(); + let val = U256::from(1337); + let tx = TransactionRequest::default().with_to(to).with_value(val); + let tx = WithOtherFields::new(tx); + + let receipt = provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); // the block should have the new base fee - let block = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); - assert_eq!(block.base_fee_per_gas.unwrap().as_u64(), new_base_fee.as_u64()); + let block = provider.get_block(BlockId::default(), false).await.unwrap().unwrap(); + assert_eq!(block.header.base_fee_per_gas.unwrap(), new_base_fee.to::()); - // mine block + // mine blocks api.evm_mine(None).await.unwrap(); // the transaction receipt should have the original effective gas price let new_receipt = provider.get_transaction_receipt(receipt.transaction_hash).await.unwrap(); - assert_eq!( - receipt.effective_gas_price.unwrap().as_u64(), - new_receipt.unwrap().effective_gas_price.unwrap().as_u64() - ); + assert_eq!(receipt.effective_gas_price, new_receipt.unwrap().effective_gas_price); } // test can set chain id #[tokio::test(flavor = "multi_thread")] async fn test_set_chain_id() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); - let chain_id = provider.get_chainid().await.unwrap(); - assert_eq!(chain_id, U256::from(31337)); + let provider = handle.http_provider(); + let chain_id = provider.get_chain_id().await.unwrap(); + assert_eq!(chain_id, 31337); let chain_id = 1234; api.anvil_set_chain_id(chain_id).await.unwrap(); - let chain_id = provider.get_chainid().await.unwrap(); - assert_eq!(chain_id, U256::from(1234)); + let chain_id = provider.get_chain_id().await.unwrap(); + assert_eq!(chain_id, 1234); } // @@ -600,7 +592,7 @@ async fn test_fork_revert_next_block_timestamp() { #[tokio::test(flavor = "multi_thread")] async fn test_fork_revert_call_latest_block_timestamp() { let (api, handle) = spawn(fork_config()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); // Mine a new block, and check the new block gas limit api.mine_one().await; @@ -610,48 +602,48 @@ async fn test_fork_revert_call_latest_block_timestamp() { api.mine_one().await; api.evm_revert(snapshot_id).await.unwrap(); - let multicall = MulticallContract::new( - Address::from_str("0xeefba1e63905ef1d7acba5a8513c70307c1ce441").unwrap(), - provider.into(), - ); - - assert_eq!( - multicall.get_current_block_timestamp().await.unwrap().as_u64(), - latest_block.header.timestamp - ); - assert_eq!( - multicall.get_current_block_difficulty().await.unwrap(), - latest_block.header.difficulty.to_ethers() - ); - assert_eq!( - multicall.get_current_block_gas_limit().await.unwrap().as_u128(), - latest_block.header.gas_limit - ); - assert_eq!( - multicall.get_current_block_coinbase().await.unwrap(), - latest_block.header.miner.to_ethers() - ); + let multicall_contract = + MulticallContract::new(address!("eefba1e63905ef1d7acba5a8513c70307c1ce441"), &provider); + + let MulticallContract::getCurrentBlockTimestampReturn { timestamp } = + multicall_contract.getCurrentBlockTimestamp().call().await.unwrap(); + assert_eq!(timestamp, U256::from(latest_block.header.timestamp)); + + let MulticallContract::getCurrentBlockDifficultyReturn { difficulty } = + multicall_contract.getCurrentBlockDifficulty().call().await.unwrap(); + assert_eq!(difficulty, U256::from(latest_block.header.difficulty)); + + let MulticallContract::getCurrentBlockGasLimitReturn { gaslimit } = + multicall_contract.getCurrentBlockGasLimit().call().await.unwrap(); + assert_eq!(gaslimit, U256::from(latest_block.header.gas_limit)); + + let MulticallContract::getCurrentBlockCoinbaseReturn { coinbase } = + multicall_contract.getCurrentBlockCoinbase().call().await.unwrap(); + assert_eq!(coinbase, latest_block.header.miner); } #[tokio::test(flavor = "multi_thread")] async fn can_remove_pool_transactions() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let provider = Arc::new(SignerMiddleware::new(provider, wallet.clone())); + + let wallet = handle.dev_wallets().next().unwrap(); + let signer: EthereumSigner = wallet.clone().into(); + let from = wallet.address(); + + let provider = http_provider_with_signer(&handle.http_endpoint(), signer); let sender = Address::random(); let to = Address::random(); - let val = 1337u64; - - let tx = TransactionRequest::new().from(sender).to(to).value(val); + let val = U256::from(1337); + let tx = TransactionRequest::default().with_from(sender).with_to(to).with_value(val); + let tx = WithOtherFields::new(tx); - provider.send_transaction(tx.from(wallet.address()), None).await.unwrap(); + provider.send_transaction(tx.with_from(from)).await.unwrap().register().await.unwrap(); let initial_txs = provider.txpool_inspect().await.unwrap(); assert_eq!(initial_txs.pending.len(), 1); - api.anvil_remove_pool_transactions(wallet.address().to_alloy()).await.unwrap(); + api.anvil_remove_pool_transactions(wallet.address()).await.unwrap(); let final_txs = provider.txpool_inspect().await.unwrap(); assert_eq!(final_txs.pending.len(), 0); diff --git a/crates/anvil/tests/it/api.rs b/crates/anvil/tests/it/api.rs index 1afa76df1..ec8335562 100644 --- a/crates/anvil/tests/it/api.rs +++ b/crates/anvil/tests/it/api.rs @@ -2,41 +2,29 @@ use crate::{ abi::{MulticallContract, SimpleStorage}, - utils::ethers_http_provider, + utils::{connect_pubsub_with_signer, http_provider_with_signer}, }; -use alloy_eips::BlockId; -use alloy_primitives::{Address as rAddress, TxKind, B256, U256 as rU256}; +use alloy_network::{EthereumSigner, TransactionBuilder}; +use alloy_primitives::{Address, ChainId, B256, U256}; use alloy_provider::Provider; use alloy_rpc_types::{ - request::{TransactionInput as CallInput, TransactionRequest as CallRequest}, - state::{AccountOverride, StateOverride}, - WithOtherFields, + request::TransactionRequest, state::AccountOverride, BlockId, BlockNumberOrTag, + BlockTransactions, WithOtherFields, }; -use anvil::{ - eth::{api::CLIENT_VERSION, EthApi}, - spawn, NodeConfig, CHAIN_ID, -}; -use ethers::{ - abi::{Address, Tokenizable}, - prelude::{builders::ContractCall, decode_function_data, Middleware, SignerMiddleware}, - signers::Signer, - types::{Block, BlockNumber, Chain, Transaction, TransactionRequest, U256}, - utils::get_contract_address, -}; -use foundry_common::types::{ToAlloy, ToEthers}; -use std::{collections::HashMap, sync::Arc, time::Duration}; +use anvil::{eth::api::CLIENT_VERSION, spawn, NodeConfig, CHAIN_ID}; +use std::{collections::HashMap, time::Duration}; #[tokio::test(flavor = "multi_thread")] async fn can_get_block_number() { let (api, handle) = spawn(NodeConfig::test()).await; let block_num = api.block_number().unwrap(); - assert_eq!(block_num, U256::zero().to_alloy()); + assert_eq!(block_num, U256::from(0)); - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); let num = provider.get_block_number().await.unwrap(); - assert_eq!(num, block_num.to::().into()); + assert_eq!(num, block_num.to::()); } #[tokio::test(flavor = "multi_thread")] @@ -54,7 +42,7 @@ async fn can_dev_get_balance() { #[tokio::test(flavor = "multi_thread")] async fn can_get_price() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); let _ = provider.get_gas_price().await.unwrap(); } @@ -62,7 +50,7 @@ async fn can_get_price() { #[tokio::test(flavor = "multi_thread")] async fn can_get_accounts() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); let _ = provider.get_accounts().await.unwrap(); } @@ -70,31 +58,32 @@ async fn can_get_accounts() { #[tokio::test(flavor = "multi_thread")] async fn can_get_client_version() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); - let version = provider.client_version().await.unwrap(); + let version = provider.get_client_version().await.unwrap(); assert_eq!(CLIENT_VERSION, version); } #[tokio::test(flavor = "multi_thread")] async fn can_get_chain_id() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); - let chain_id = provider.get_chainid().await.unwrap(); - assert_eq!(chain_id, CHAIN_ID.into()); + let chain_id = provider.get_chain_id().await.unwrap(); + assert_eq!(chain_id, CHAIN_ID); } #[tokio::test(flavor = "multi_thread")] async fn can_modify_chain_id() { - let (_api, handle) = spawn(NodeConfig::test().with_chain_id(Some(Chain::Goerli))).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let (_api, handle) = + spawn(NodeConfig::test().with_chain_id(Some(ChainId::from(777_u64)))).await; + let provider = handle.http_provider(); - let chain_id = provider.get_chainid().await.unwrap(); - assert_eq!(chain_id, Chain::Goerli.into()); + let chain_id = provider.get_chain_id().await.unwrap(); + assert_eq!(chain_id, 777); let chain_id = provider.get_net_version().await.unwrap(); - assert_eq!(chain_id, (Chain::Goerli as u64).to_string()); + assert_eq!(chain_id, 777); } #[tokio::test(flavor = "multi_thread")] @@ -108,252 +97,218 @@ async fn can_get_network_id() { #[tokio::test(flavor = "multi_thread")] async fn can_get_block_by_number() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let accounts: Vec<_> = handle.dev_wallets().collect(); + let signer: EthereumSigner = accounts[0].clone().into(); let from = accounts[0].address(); let to = accounts[1].address(); - let amount = handle.genesis_balance().checked_div(rU256::from(2u64)).unwrap(); - // send a dummy transactions - let tx = TransactionRequest::new() - .to(to.to_ethers()) - .value(amount.to_ethers()) - .from(from.to_ethers()); - let _ = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); - - let block: Block = provider.get_block_with_txs(1u64).await.unwrap().unwrap(); - assert_eq!(block.transactions.len(), 1); - let block = provider.get_block(1u64).await.unwrap().unwrap(); + let provider = http_provider_with_signer(&handle.http_endpoint(), signer); + + let val = handle.genesis_balance().checked_div(U256::from(2)).unwrap(); + + // send a dummy transaction + let tx = TransactionRequest::default().with_from(from).with_to(to).with_value(val); + let tx = WithOtherFields::new(tx); + + provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); + + let block = provider.get_block(BlockId::number(1), true).await.unwrap().unwrap(); assert_eq!(block.transactions.len(), 1); - let block = provider.get_block(block.hash.unwrap()).await.unwrap().unwrap(); + let block = + provider.get_block(BlockId::hash(block.header.hash.unwrap()), true).await.unwrap().unwrap(); assert_eq!(block.transactions.len(), 1); } #[tokio::test(flavor = "multi_thread")] async fn can_get_pending_block() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let accounts: Vec<_> = handle.dev_wallets().collect(); + let signer: EthereumSigner = accounts[0].clone().into(); + let from = accounts[0].address(); + let to = accounts[1].address(); - let block = provider.get_block(BlockNumber::Pending).await.unwrap().unwrap(); + let provider = connect_pubsub_with_signer(&handle.http_endpoint(), signer).await; - assert_eq!(block.number.unwrap().as_u64(), 1u64); + let block = provider.get_block(BlockId::pending(), false).await.unwrap().unwrap(); + assert_eq!(block.header.number.unwrap(), 1); let num = provider.get_block_number().await.unwrap(); - assert_eq!(num.as_u64(), 0u64); + assert_eq!(num, 0); api.anvil_set_auto_mine(false).await.unwrap(); - let from = accounts[0].address(); - let to = accounts[1].address(); - let tx = TransactionRequest::new().to(to.to_ethers()).value(100u64).from(from.to_ethers()); + let tx = TransactionRequest::default().with_from(from).with_to(to).with_value(U256::from(100)); - let tx = provider.send_transaction(tx, None).await.unwrap(); + let pending = provider.send_transaction(tx.clone()).await.unwrap().register().await.unwrap(); let num = provider.get_block_number().await.unwrap(); - assert_eq!(num.as_u64(), 0u64); + assert_eq!(num, 0); - let block = provider.get_block(BlockNumber::Pending).await.unwrap().unwrap(); - assert_eq!(block.number.unwrap().as_u64(), 1u64); + let block = provider.get_block(BlockId::pending(), false).await.unwrap().unwrap(); + assert_eq!(block.header.number.unwrap(), 1); assert_eq!(block.transactions.len(), 1); - assert_eq!(block.transactions, vec![tx.tx_hash()]); + assert_eq!(block.transactions, BlockTransactions::Hashes(vec![*pending.tx_hash()])); - let block = provider.get_block_with_txs(BlockNumber::Pending).await.unwrap().unwrap(); - assert_eq!(block.number.unwrap().as_u64(), 1u64); + let block = provider.get_block(BlockId::pending(), true).await.unwrap().unwrap(); + assert_eq!(block.header.number.unwrap(), 1); assert_eq!(block.transactions.len(), 1); } #[tokio::test(flavor = "multi_thread")] async fn can_call_on_pending_block() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); - let num = provider.get_block_number().await.unwrap(); - assert_eq!(num.as_u64(), 0u64); - - api.anvil_set_auto_mine(false).await.unwrap(); - - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); + let wallet = handle.dev_wallets().next().unwrap(); + let signer: EthereumSigner = wallet.clone().into(); let sender = wallet.address(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); - let mut deploy_tx = MulticallContract::deploy(Arc::clone(&client), ()).unwrap().deployer.tx; - deploy_tx.set_nonce(0); - let pending_contract_address = get_contract_address(sender, deploy_tx.nonce().unwrap()); + let provider = http_provider_with_signer(&handle.http_endpoint(), signer); - client.send_transaction(deploy_tx, None).await.unwrap(); + let num = provider.get_block_number().await.unwrap(); + assert_eq!(num, 0); - let pending_contract = MulticallContract::new(pending_contract_address, client.clone()); + api.anvil_set_auto_mine(false).await.unwrap(); - let num = client.get_block_number().await.unwrap(); - assert_eq!(num.as_u64(), 0u64); + let _contract_pending = MulticallContract::deploy_builder(&provider) + .from(wallet.address()) + .send() + .await + .unwrap() + .register() + .await + .unwrap(); + let contract_address = sender.create(0); + let contract = MulticallContract::new(contract_address, &provider); + + let num = provider.get_block_number().await.unwrap(); + assert_eq!(num, 0); // Ensure that we can get the block_number from the pending contract - let (ret_block_number, _) = - pending_contract.aggregate(vec![]).block(BlockNumber::Pending).call().await.unwrap(); - assert_eq!(ret_block_number.as_u64(), 1u64); + let MulticallContract::aggregateReturn { blockNumber: ret_block_number, .. } = + contract.aggregate(vec![]).block(BlockId::pending()).call().await.unwrap(); + assert_eq!(ret_block_number, U256::from(1)); + + let accounts: Vec
= handle.dev_wallets().map(|w| w.address()).collect(); - let accounts: Vec = handle.dev_wallets().map(|w| w.address()).collect(); for i in 1..10 { api.anvil_set_coinbase(accounts[i % accounts.len()]).await.unwrap(); - api.evm_set_block_gas_limit(rU256::from(30_000_000 + i)).unwrap(); + api.evm_set_block_gas_limit(U256::from(30_000_000 + i)).unwrap(); - api.anvil_mine(Some(rU256::from(1)), None).await.unwrap(); + api.anvil_mine(Some(U256::from(1)), None).await.unwrap(); tokio::time::sleep(Duration::from_secs(1)).await; } + // Ensure that the right header values are set when calling a past block - for block_number in 1..(api.block_number().unwrap().to::() + 1) { - let block_number_alloy = alloy_rpc_types::BlockNumberOrTag::Number(block_number as u64); - let block_number_ethers = BlockNumber::Number((block_number as u64).into()); - let block = api.block_by_number(block_number_alloy).await.unwrap().unwrap(); - - let block_timestamp = pending_contract - .get_current_block_timestamp() - .block(block_number_ethers) - .call() - .await - .unwrap(); - assert_eq!(block.header.timestamp, block_timestamp.as_u64()); - - let block_gas_limit = pending_contract - .get_current_block_gas_limit() - .block(block_number_ethers) - .call() - .await - .unwrap(); - assert_eq!(block.header.gas_limit, block_gas_limit.as_u128()); - - let block_coinbase = pending_contract - .get_current_block_coinbase() - .block(block_number_ethers) - .call() - .await - .unwrap(); - assert_eq!(block.header.miner, block_coinbase.to_alloy()); + for anvil_block_number in 1..(api.block_number().unwrap().to::() + 1) { + let block_number = BlockNumberOrTag::Number(anvil_block_number as u64); + let block = api.block_by_number(block_number).await.unwrap().unwrap(); + + let MulticallContract::getCurrentBlockTimestampReturn { timestamp: ret_timestamp, .. } = + contract + .getCurrentBlockTimestamp() + .block(BlockId::number(anvil_block_number as u64)) + .call() + .await + .unwrap(); + assert_eq!(block.header.timestamp, ret_timestamp.to::()); + + let MulticallContract::getCurrentBlockGasLimitReturn { gaslimit: ret_gas_limit, .. } = + contract + .getCurrentBlockGasLimit() + .block(BlockId::number(anvil_block_number as u64)) + .call() + .await + .unwrap(); + assert_eq!(block.header.gas_limit, ret_gas_limit.to::()); + + let MulticallContract::getCurrentBlockCoinbaseReturn { coinbase: ret_coinbase, .. } = + contract + .getCurrentBlockCoinbase() + .block(BlockId::number(anvil_block_number as u64)) + .call() + .await + .unwrap(); + assert_eq!(block.header.miner, ret_coinbase); } } -async fn call_with_override( - api: &EthApi, - call: ContractCall, - to: Address, - overrides: StateOverride, -) -> D -where - D: Tokenizable, -{ - let result = api - .call( - WithOtherFields::new(CallRequest { - input: CallInput::maybe_input(call.tx.data().cloned().map(|b| b.0.into())), - to: Some(TxKind::Call(to.to_alloy())), - ..Default::default() - }), - None, - Some(overrides), - ) - .await - .unwrap(); - decode_function_data(&call.function, result.as_ref(), false).unwrap() -} - #[tokio::test(flavor = "multi_thread")] async fn can_call_with_state_override() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let wallet = handle.dev_wallets().next().unwrap(); + let signer: EthereumSigner = wallet.clone().into(); + let account = wallet.address(); + + let provider = http_provider_with_signer(&handle.http_endpoint(), signer); api.anvil_set_auto_mine(true).await.unwrap(); - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let account = wallet.address(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); + let multicall_contract = MulticallContract::deploy(&provider).await.unwrap(); let init_value = "toto".to_string(); - let multicall = - MulticallContract::deploy(Arc::clone(&client), ()).unwrap().send().await.unwrap(); - let simple_storage = SimpleStorage::deploy(Arc::clone(&client), init_value.clone()) - .unwrap() - .send() - .await - .unwrap(); + + let simple_storage_contract = + SimpleStorage::deploy(&provider, init_value.clone()).await.unwrap(); // Test the `balance` account override - let balance = rU256::from(42u64); - let result = call_with_override( - &api, - multicall.get_eth_balance(account), - multicall.address(), - HashMap::from([( - account.to_alloy(), - AccountOverride { balance: Some(balance), ..Default::default() }, - )]), - ) - .await; - assert_eq!(result, balance.to_ethers()); + let balance = U256::from(42u64); + let overrides = HashMap::from([( + account, + AccountOverride { balance: Some(balance), ..Default::default() }, + )]); + let result = + multicall_contract.getEthBalance(account).state(overrides).call().await.unwrap().balance; + assert_eq!(result, balance); // Test the `state_diff` account override let overrides = HashMap::from([( - simple_storage.address().to_alloy(), + *simple_storage_contract.address(), AccountOverride { // The `lastSender` is in the first storage slot state_diff: Some(HashMap::from([( B256::ZERO, - rU256::from_be_slice(B256::from(account.to_alloy().into_word()).as_slice()), + U256::from_be_slice(B256::from(account.into_word()).as_slice()), )])), ..Default::default() }, )]); - let last_sender = call_with_override( - &api, - simple_storage.last_sender(), - simple_storage.address(), - Default::default(), - ) - .await; + let last_sender = + simple_storage_contract.lastSender().state(HashMap::default()).call().await.unwrap()._0; // No `sender` set without override - assert_eq!(last_sender, Address::zero()); - let last_sender = call_with_override( - &api, - simple_storage.last_sender(), - simple_storage.address(), - overrides.clone(), - ) - .await; + assert_eq!(last_sender, Address::ZERO); + + let last_sender = + simple_storage_contract.lastSender().state(overrides.clone()).call().await.unwrap()._0; // `sender` *is* set with override assert_eq!(last_sender, account); - let value = - call_with_override(&api, simple_storage.get_value(), simple_storage.address(), overrides) - .await; + + let value = simple_storage_contract.getValue().state(overrides).call().await.unwrap()._0; // `value` *is not* changed with state-diff assert_eq!(value, init_value); // Test the `state` account override let overrides = HashMap::from([( - simple_storage.address().to_alloy(), + *simple_storage_contract.address(), AccountOverride { // The `lastSender` is in the first storage slot state: Some(HashMap::from([( B256::ZERO, - rU256::from_be_slice(B256::from(account.to_alloy().into_word()).as_slice()), + U256::from_be_slice(B256::from(account.into_word()).as_slice()), )])), ..Default::default() }, )]); - let last_sender = call_with_override( - &api, - simple_storage.last_sender(), - simple_storage.address(), - overrides.clone(), - ) - .await; + let last_sender = + simple_storage_contract.lastSender().state(overrides.clone()).call().await.unwrap()._0; // `sender` *is* set with override assert_eq!(last_sender, account); - let value = - call_with_override(&api, simple_storage.get_value(), simple_storage.address(), overrides) - .await; + + let value = simple_storage_contract.getValue().state(overrides).call().await.unwrap()._0; // `value` *is* changed with state assert_eq!(value, ""); } diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index f22d22a91..2ef1e7078 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -1,30 +1,20 @@ //! various fork related test use crate::{ - abi::*, - utils::{self, ethers_http_provider}, + abi::{Greeter, ERC721}, + utils::{http_provider, http_provider_with_signer}, }; -use alloy_primitives::{address, TxKind, U256 as rU256}; -use alloy_provider::Provider as AlloyProvider; +use alloy_network::{EthereumSigner, TransactionBuilder}; +use alloy_primitives::{address, Address, Bytes, TxKind, U256}; +use alloy_provider::Provider; use alloy_rpc_types::{ - request::{TransactionInput, TransactionRequest as CallRequest}, + request::{TransactionInput, TransactionRequest}, BlockId, BlockNumberOrTag, WithOtherFields, }; +use alloy_signer_wallet::LocalWallet; use anvil::{eth::EthApi, spawn, NodeConfig, NodeHandle}; use anvil_core::types::Forking; -use ethers::{ - prelude::{Bytes, LocalWallet, Middleware, SignerMiddleware}, - providers::{Http, Provider}, - signers::Signer, - types::{ - transaction::eip2718::TypedTransaction, Address, BlockNumber, Chain, TransactionRequest, - U256, - }, -}; -use foundry_common::{ - provider::alloy::get_http_provider, - types::{ToAlloy, ToEthers}, -}; +use foundry_common::provider::get_http_provider; use foundry_config::Config; use foundry_test_utils::rpc::{self, next_http_rpc_endpoint}; use futures::StreamExt; @@ -75,18 +65,18 @@ async fn test_spawn_fork() { assert!(api.is_fork()); let head = api.block_number().unwrap(); - assert_eq!(head, rU256::from(BLOCK_NUMBER)) + assert_eq!(head, U256::from(BLOCK_NUMBER)) } #[tokio::test(flavor = "multi_thread")] async fn test_fork_eth_get_balance() { let (api, handle) = spawn(fork_config()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); for _ in 0..10 { let addr = Address::random(); - let balance = api.balance(addr.to_alloy(), None).await.unwrap(); - let provider_balance = provider.get_balance(addr, None).await.unwrap(); - assert_eq!(balance, provider_balance.to_alloy()) + let balance = api.balance(addr, None).await.unwrap(); + let provider_balance = provider.get_balance(addr, BlockId::latest()).await.unwrap(); + assert_eq!(balance, provider_balance) } } @@ -94,66 +84,65 @@ async fn test_fork_eth_get_balance() { #[tokio::test(flavor = "multi_thread")] async fn test_fork_eth_get_balance_after_mine() { let (api, handle) = spawn(fork_config()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); let info = api.anvil_node_info().await.unwrap(); let number = info.fork_config.fork_block_number.unwrap(); assert_eq!(number, BLOCK_NUMBER); let address = Address::random(); - let _balance = provider - .get_balance(address, Some(BlockNumber::Number(number.into()).into())) - .await - .unwrap(); + let _balance = provider.get_balance(address, BlockId::Number(number.into())).await.unwrap(); api.evm_mine(None).await.unwrap(); - let _balance = provider - .get_balance(address, Some(BlockNumber::Number(number.into()).into())) - .await - .unwrap(); + let _balance = provider.get_balance(address, BlockId::Number(number.into())).await.unwrap(); } // #[tokio::test(flavor = "multi_thread")] async fn test_fork_eth_get_code_after_mine() { let (api, handle) = spawn(fork_config()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); let info = api.anvil_node_info().await.unwrap(); let number = info.fork_config.fork_block_number.unwrap(); assert_eq!(number, BLOCK_NUMBER); let address = Address::random(); - let _code = - provider.get_code(address, Some(BlockNumber::Number(number.into()).into())).await.unwrap(); + let _code = provider.get_code_at(address, BlockId::Number(1.into())).await.unwrap(); api.evm_mine(None).await.unwrap(); - let _code = - provider.get_code(address, Some(BlockNumber::Number(number.into()).into())).await.unwrap(); + let _code = provider.get_code_at(address, BlockId::Number(1.into())).await.unwrap(); } #[tokio::test(flavor = "multi_thread")] async fn test_fork_eth_get_code() { let (api, handle) = spawn(fork_config()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); for _ in 0..10 { let addr = Address::random(); - let code = api.get_code(addr.to_alloy(), None).await.unwrap(); - let provider_code = provider.get_code(addr, None).await.unwrap(); - assert_eq!(code, provider_code.to_alloy()) + let code = api.get_code(addr, None).await.unwrap(); + let provider_code = provider.get_code_at(addr, BlockId::latest()).await.unwrap(); + assert_eq!(code, provider_code) } - for address in utils::contract_addresses(Chain::Mainnet) { + let addresses: Vec
= vec![ + "0x6b175474e89094c44da98b954eedeac495271d0f".parse().unwrap(), + "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48".parse().unwrap(), + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2".parse().unwrap(), + "0x1F98431c8aD98523631AE4a59f267346ea31F984".parse().unwrap(), + "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45".parse().unwrap(), + ]; + for address in addresses { let prev_code = api - .get_code(address.to_alloy(), Some(BlockNumberOrTag::Number(BLOCK_NUMBER - 10).into())) + .get_code(address, Some(BlockNumberOrTag::Number(BLOCK_NUMBER - 10).into())) .await .unwrap(); - let code = api.get_code(address.to_alloy(), None).await.unwrap(); - let provider_code = provider.get_code(address, None).await.unwrap(); + let code = api.get_code(address, None).await.unwrap(); + let provider_code = provider.get_code_at(address, BlockId::latest()).await.unwrap(); assert_eq!(code, prev_code); - assert_eq!(code, provider_code.to_alloy()); + assert_eq!(code, provider_code); assert!(!code.as_ref().is_empty()); } } @@ -161,72 +150,70 @@ async fn test_fork_eth_get_code() { #[tokio::test(flavor = "multi_thread")] async fn test_fork_eth_get_nonce() { let (api, handle) = spawn(fork_config()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); for _ in 0..10 { let addr = Address::random(); - let api_nonce = api.transaction_count(addr.to_alloy(), None).await.unwrap(); - let provider_nonce = provider.get_transaction_count(addr, None).await.unwrap(); - assert_eq!(api_nonce, provider_nonce.to_alloy()); + let api_nonce = api.transaction_count(addr, None).await.unwrap().to::(); + let provider_nonce = provider.get_transaction_count(addr, BlockId::latest()).await.unwrap(); + assert_eq!(api_nonce, provider_nonce); } let addr = Config::DEFAULT_SENDER; - let api_nonce = api.transaction_count(addr, None).await.unwrap(); - let provider_nonce = provider.get_transaction_count(addr.to_ethers(), None).await.unwrap(); - assert_eq!(api_nonce, provider_nonce.to_alloy()); + let api_nonce = api.transaction_count(addr, None).await.unwrap().to::(); + let provider_nonce = provider.get_transaction_count(addr, BlockId::latest()).await.unwrap(); + assert_eq!(api_nonce, provider_nonce); } #[tokio::test(flavor = "multi_thread")] async fn test_fork_eth_fee_history() { let (api, handle) = spawn(fork_config()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); let count = 10u64; let _history = - api.fee_history(rU256::from(count), BlockNumberOrTag::Latest, vec![]).await.unwrap(); - let _provider_history = provider.fee_history(count, BlockNumber::Latest, &[]).await.unwrap(); + api.fee_history(U256::from(count), BlockNumberOrTag::Latest, vec![]).await.unwrap(); + let _provider_history = + provider.get_fee_history(count, BlockNumberOrTag::Latest, &[]).await.unwrap(); } #[tokio::test(flavor = "multi_thread")] async fn test_fork_reset() { let (api, handle) = spawn(fork_config()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); let accounts: Vec<_> = handle.dev_wallets().collect(); - let from = accounts[0].address().to_ethers(); - let to = accounts[1].address().to_ethers(); + let from = accounts[0].address(); + let to = accounts[1].address(); let block_number = provider.get_block_number().await.unwrap(); - let balance_before = provider.get_balance(to, None).await.unwrap(); - let amount = handle.genesis_balance().checked_div(rU256::from(2u64)).unwrap(); - - let initial_nonce = provider.get_transaction_count(from, None).await.unwrap(); + let balance_before = provider.get_balance(to, BlockId::latest()).await.unwrap(); + let amount = handle.genesis_balance().checked_div(U256::from(2u64)).unwrap(); - let tx = TransactionRequest::new().to(to).value(amount.to_ethers()).from(from); + let initial_nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); - let tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); - assert_eq!(tx.transaction_index, 0u64.into()); + let tx = TransactionRequest::default().to(to).value(amount).from(from); + let tx = WithOtherFields::new(tx); + let tx = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); + assert_eq!(tx.transaction_index, Some(0)); - let nonce = provider.get_transaction_count(from, None).await.unwrap(); + let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); assert_eq!(nonce, initial_nonce + 1); - let to_balance = provider.get_balance(to, None).await.unwrap(); - assert_eq!(balance_before.saturating_add(amount.to_ethers()), to_balance); - api.anvil_reset(Some(Forking { - json_rpc_url: None, - block_number: Some(block_number.as_u64()), - })) - .await - .unwrap(); + let to_balance = provider.get_balance(to, BlockId::latest()).await.unwrap(); + assert_eq!(balance_before.saturating_add(amount), to_balance); + api.anvil_reset(Some(Forking { json_rpc_url: None, block_number: Some(block_number) })) + .await + .unwrap(); // reset block number assert_eq!(block_number, provider.get_block_number().await.unwrap()); - let nonce = provider.get_transaction_count(from, None).await.unwrap(); + let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); assert_eq!(nonce, initial_nonce); - let balance = provider.get_balance(from, None).await.unwrap(); - assert_eq!(balance, handle.genesis_balance().to_ethers()); - let balance = provider.get_balance(to, None).await.unwrap(); - assert_eq!(balance, handle.genesis_balance().to_ethers()); + let balance = provider.get_balance(from, BlockId::latest()).await.unwrap(); + assert_eq!(balance, handle.genesis_balance()); + let balance = provider.get_balance(to, BlockId::latest()).await.unwrap(); + assert_eq!(balance, handle.genesis_balance()); // reset to latest api.anvil_reset(Some(Forking::default())).await.unwrap(); @@ -238,15 +225,15 @@ async fn test_fork_reset() { #[tokio::test(flavor = "multi_thread")] async fn test_fork_reset_setup() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); let dead_addr: Address = "000000000000000000000000000000000000dEaD".parse().unwrap(); let block_number = provider.get_block_number().await.unwrap(); - assert_eq!(block_number, 0.into()); + assert_eq!(block_number, 0); - let local_balance = provider.get_balance(dead_addr, None).await.unwrap(); - assert_eq!(local_balance, 0.into()); + let local_balance = provider.get_balance(dead_addr, BlockId::latest()).await.unwrap(); + assert_eq!(local_balance, U256::ZERO); api.anvil_reset(Some(Forking { json_rpc_url: Some(rpc::next_http_archive_rpc_endpoint()), @@ -256,10 +243,10 @@ async fn test_fork_reset_setup() { .unwrap(); let block_number = provider.get_block_number().await.unwrap(); - assert_eq!(block_number, BLOCK_NUMBER.into()); + assert_eq!(block_number, BLOCK_NUMBER); - let remote_balance = provider.get_balance(dead_addr, None).await.unwrap(); - assert_eq!(remote_balance, DEAD_BALANCE_AT_BLOCK_NUMBER.into()); + let remote_balance = provider.get_balance(dead_addr, BlockId::latest()).await.unwrap(); + assert_eq!(remote_balance, U256::from(DEAD_BALANCE_AT_BLOCK_NUMBER)); } #[tokio::test(flavor = "multi_thread")] @@ -275,15 +262,13 @@ async fn test_fork_snapshotting() { let initial_nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); let balance_before = provider.get_balance(to, BlockId::latest()).await.unwrap(); - let amount = handle.genesis_balance().checked_div(rU256::from(2u64)).unwrap(); + let amount = handle.genesis_balance().checked_div(U256::from(2u64)).unwrap(); - let provider = ethers_http_provider(&handle.http_endpoint()); - let tx = TransactionRequest::new() - .to(to.to_ethers()) - .value(amount.to_ethers()) - .from(from.to_ethers()); + let provider = handle.http_provider(); + let tx = TransactionRequest::default().to(to).value(amount).from(from); + let tx = WithOtherFields::new(tx); - let _ = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); + let _ = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); let provider = handle.http_provider(); @@ -317,14 +302,12 @@ async fn test_fork_snapshotting_repeated() { let initial_nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); let balance_before = provider.get_balance(to, BlockId::latest()).await.unwrap(); - let amount = handle.genesis_balance().checked_div(rU256::from(92u64)).unwrap(); + let amount = handle.genesis_balance().checked_div(U256::from(92u64)).unwrap(); - let tx = TransactionRequest::new() - .to(to.to_ethers()) - .value(amount.to_ethers()) - .from(from.to_ethers()); - let tx_provider = ethers_http_provider(&handle.http_endpoint()); - let _ = tx_provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); + let tx = TransactionRequest::default().to(to).value(amount).from(from); + let tx = WithOtherFields::new(tx); + let tx_provider = handle.http_provider(); + let _ = tx_provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); assert_eq!(nonce, initial_nonce + 1); @@ -356,7 +339,6 @@ async fn test_fork_snapshotting_repeated() { async fn test_fork_snapshotting_blocks() { let (api, handle) = spawn(fork_config()).await; let provider = handle.http_provider(); - let tx_provider = ethers_http_provider(&handle.http_endpoint()); // create a snapshot let snapshot = api.evm_snapshot().await.unwrap(); @@ -368,14 +350,12 @@ async fn test_fork_snapshotting_blocks() { let initial_nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); let balance_before = provider.get_balance(to, BlockId::latest()).await.unwrap(); - let amount = handle.genesis_balance().checked_div(rU256::from(2u64)).unwrap(); + let amount = handle.genesis_balance().checked_div(U256::from(2u64)).unwrap(); // send the transaction - let tx = TransactionRequest::new() - .to(to.to_ethers()) - .value(amount.to_ethers()) - .from(from.to_ethers()); - let _ = tx_provider.send_transaction(tx.clone(), None).await.unwrap().await.unwrap().unwrap(); + let tx = TransactionRequest::default().to(to).value(amount).from(from); + let tx = WithOtherFields::new(tx); + let _ = provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); let block_number_after = provider.get_block_number().await.unwrap(); assert_eq!(block_number_after, block_number + 1); @@ -396,7 +376,7 @@ async fn test_fork_snapshotting_blocks() { assert_eq!(block_number_after, block_number); // repeat transaction - let _ = tx_provider.send_transaction(tx.clone(), None).await.unwrap().await.unwrap().unwrap(); + let _ = provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); assert_eq!(nonce, initial_nonce + 1); @@ -414,16 +394,16 @@ async fn test_fork_snapshotting_blocks() { #[tokio::test(flavor = "multi_thread")] async fn test_separate_states() { let (api, handle) = spawn(fork_config().with_fork_block_number(Some(14723772u64))).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); let addr: Address = "000000000000000000000000000000000000dEaD".parse().unwrap(); - let remote_balance = provider.get_balance(addr, None).await.unwrap(); - assert_eq!(remote_balance, 12556104082473169733500u128.into()); + let remote_balance = provider.get_balance(addr, BlockId::latest()).await.unwrap(); + assert_eq!(remote_balance, U256::from(12556104082473169733500u128)); - api.anvil_set_balance(addr.to_alloy(), rU256::from(1337u64)).await.unwrap(); - let balance = provider.get_balance(addr, None).await.unwrap(); - assert_eq!(balance, 1337u64.into()); + api.anvil_set_balance(addr, U256::from(1337u64)).await.unwrap(); + let balance = provider.get_balance(addr, BlockId::latest()).await.unwrap(); + assert_eq!(balance, U256::from(1337u64)); let fork = api.get_fork().unwrap(); let fork_db = fork.database.read().await; @@ -433,35 +413,31 @@ async fn test_separate_states() { .db() .accounts .read() - .get(&addr.to_alloy()) + .get(&addr) .cloned() .unwrap(); - assert_eq!(acc.balance, remote_balance.to_alloy()) + assert_eq!(acc.balance, remote_balance); } #[tokio::test(flavor = "multi_thread")] async fn can_deploy_greeter_on_fork() { let (_api, handle) = spawn(fork_config().with_fork_block_number(Some(14723772u64))).await; - let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); + let wallet = handle.dev_wallets().next().unwrap(); + let signer: EthereumSigner = wallet.into(); - let greeter_contract = Greeter::deploy(Arc::clone(&client), "Hello World!".to_string()) - .unwrap() - .send() - .await - .unwrap(); + let provider = http_provider_with_signer(&handle.http_endpoint(), signer); + + let greeter_contract = Greeter::deploy(&provider, "Hello World!".to_string()).await.unwrap(); let greeting = greeter_contract.greet().call().await.unwrap(); - assert_eq!("Hello World!", greeting); + assert_eq!("Hello World!", greeting._0); - let greeter_contract = - Greeter::deploy(client, "Hello World!".to_string()).unwrap().send().await.unwrap(); + let greeter_contract = Greeter::deploy(&provider, "Hello World!".to_string()).await.unwrap(); let greeting = greeter_contract.greet().call().await.unwrap(); - assert_eq!("Hello World!", greeting); + assert_eq!("Hello World!", greeting._0); } #[tokio::test(flavor = "multi_thread")] @@ -470,7 +446,7 @@ async fn can_reset_properly() { let account = origin_handle.dev_accounts().next().unwrap(); let origin_provider = origin_handle.http_provider(); let origin_nonce = 1u64; - origin_api.anvil_set_nonce(account, rU256::from(origin_nonce)).await.unwrap(); + origin_api.anvil_set_nonce(account, U256::from(origin_nonce)).await.unwrap(); assert_eq!( origin_nonce, @@ -481,16 +457,17 @@ async fn can_reset_properly() { spawn(NodeConfig::test().with_eth_rpc_url(Some(origin_handle.http_endpoint()))).await; let fork_provider = fork_handle.http_provider(); - let fork_tx_provider = ethers_http_provider(&fork_handle.http_endpoint()); + let fork_tx_provider = http_provider(&fork_handle.http_endpoint()); assert_eq!( origin_nonce, fork_provider.get_transaction_count(account, BlockId::latest()).await.unwrap() ); let to = Address::random(); - let to_balance = fork_provider.get_balance(to.to_alloy(), BlockId::latest()).await.unwrap(); - let tx = TransactionRequest::new().from(account.to_ethers()).to(to).value(1337u64); - let tx = fork_tx_provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); + let to_balance = fork_provider.get_balance(to, BlockId::latest()).await.unwrap(); + let tx = TransactionRequest::default().from(account).to(to).value(U256::from(1337u64)); + let tx = WithOtherFields::new(tx); + let tx = fork_tx_provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); // nonce incremented by 1 assert_eq!( @@ -508,13 +485,10 @@ async fn can_reset_properly() { ); // balance is reset - assert_eq!( - to_balance, - fork_provider.get_balance(to.to_alloy(), BlockId::latest()).await.unwrap() - ); + assert_eq!(to_balance, fork_provider.get_balance(to, BlockId::latest()).await.unwrap()); // tx does not exist anymore - assert!(fork_tx_provider.get_transaction(tx.transaction_hash).await.is_err()) + assert!(fork_tx_provider.get_transaction_by_hash(tx.transaction_hash).await.is_err()) } #[tokio::test(flavor = "multi_thread")] @@ -522,41 +496,48 @@ async fn test_fork_timestamp() { let start = std::time::Instant::now(); let (api, handle) = spawn(fork_config()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); - let block = provider.get_block(BLOCK_NUMBER).await.unwrap().unwrap(); - assert_eq!(block.timestamp.as_u64(), BLOCK_TIMESTAMP); + let block = + provider.get_block(BlockId::Number(BLOCK_NUMBER.into()), false).await.unwrap().unwrap(); + assert_eq!(block.header.timestamp, BLOCK_TIMESTAMP); let accounts: Vec<_> = handle.dev_wallets().collect(); let from = accounts[0].address(); - let tx = TransactionRequest::new().to(Address::random()).value(1337u64).from(from.to_ethers()); - let tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); - assert_eq!(tx.status, Some(1u64.into())); + let tx = + TransactionRequest::default().to(Address::random()).value(U256::from(1337u64)).from(from); + let tx = WithOtherFields::new(tx); + let tx = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); + let status = tx.inner.inner.inner.receipt.status; + assert!(status); - let block = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::latest(), false).await.unwrap().unwrap(); let elapsed = start.elapsed().as_secs() + 1; // ensure the diff between the new mined block and the original block is within the elapsed time - let diff = block.timestamp - BLOCK_TIMESTAMP; - assert!(diff <= elapsed.into(), "diff={diff}, elapsed={elapsed}"); + let diff = block.header.timestamp - BLOCK_TIMESTAMP; + assert!(diff <= elapsed, "diff={diff}, elapsed={elapsed}"); let start = std::time::Instant::now(); // reset to check timestamp works after resetting api.anvil_reset(Some(Forking { json_rpc_url: None, block_number: Some(BLOCK_NUMBER) })) .await .unwrap(); - let block = provider.get_block(BLOCK_NUMBER).await.unwrap().unwrap(); - assert_eq!(block.timestamp.as_u64(), BLOCK_TIMESTAMP); + let block = + provider.get_block(BlockId::Number(BLOCK_NUMBER.into()), false).await.unwrap().unwrap(); + assert_eq!(block.header.timestamp, BLOCK_TIMESTAMP); - let tx = TransactionRequest::new().to(Address::random()).value(1337u64).from(from.to_ethers()); - let _tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); + let tx = + TransactionRequest::default().to(Address::random()).value(U256::from(1337u64)).from(from); + let tx = WithOtherFields::new(tx); + let _ = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); // FIXME: Awaits endlessly here. - let block = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::latest(), false).await.unwrap().unwrap(); let elapsed = start.elapsed().as_secs() + 1; - let diff = block.timestamp - BLOCK_TIMESTAMP; - assert!(diff <= elapsed.into()); + let diff = block.header.timestamp - BLOCK_TIMESTAMP; + assert!(diff <= elapsed); // ensure that after setting a timestamp manually, then next block time is correct let start = std::time::Instant::now(); @@ -564,19 +545,23 @@ async fn test_fork_timestamp() { .await .unwrap(); api.evm_set_next_block_timestamp(BLOCK_TIMESTAMP + 1).unwrap(); - let tx = TransactionRequest::new().to(Address::random()).value(1337u64).from(from.to_ethers()); - let _tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); + let tx = + TransactionRequest::default().to(Address::random()).value(U256::from(1337u64)).from(from); + let tx = WithOtherFields::new(tx); + let _tx = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); - let block = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); - assert_eq!(block.timestamp.as_u64(), BLOCK_TIMESTAMP + 1); + let block = provider.get_block(BlockId::latest(), false).await.unwrap().unwrap(); + assert_eq!(block.header.timestamp, BLOCK_TIMESTAMP + 1); - let tx = TransactionRequest::new().to(Address::random()).value(1337u64).from(from.to_ethers()); - let _tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); + let tx = + TransactionRequest::default().to(Address::random()).value(U256::from(1337u64)).from(from); + let tx = WithOtherFields::new(tx); + let _ = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); - let block = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::latest(), false).await.unwrap().unwrap(); let elapsed = start.elapsed().as_secs() + 1; - let diff = block.timestamp - (BLOCK_TIMESTAMP + 1); - assert!(diff <= elapsed.into()); + let diff = block.header.timestamp - (BLOCK_TIMESTAMP + 1); + assert!(diff <= elapsed); } #[tokio::test(flavor = "multi_thread")] @@ -595,22 +580,25 @@ async fn test_fork_can_send_tx() { let (api, handle) = spawn(fork_config().with_blocktime(Some(std::time::Duration::from_millis(800)))).await; - let wallet = LocalWallet::new(&mut rand::thread_rng()); - let provider = ethers_http_provider(&handle.http_endpoint()); - let provider = SignerMiddleware::new(provider, wallet); + let wallet = LocalWallet::random(); + let signer = wallet.address(); + let provider = handle.http_provider(); + // let provider = SignerMiddleware::new(provider, wallet); - api.anvil_set_balance(provider.address().to_alloy(), rU256::MAX).await.unwrap(); - let balance = provider.get_balance(provider.address(), None).await.unwrap(); - assert_eq!(balance, rU256::MAX.to_ethers()); + api.anvil_set_balance(signer, U256::MAX).await.unwrap(); + api.anvil_impersonate_account(signer).await.unwrap(); // Added until SignerFiller for alloy-provider is fixed. + let balance = provider.get_balance(signer, BlockId::latest()).await.unwrap(); + assert_eq!(balance, U256::MAX); let addr = Address::random(); - let val = 1337u64; - let tx = TransactionRequest::new().to(addr).value(val); + let val = U256::from(1337u64); + let tx = TransactionRequest::default().to(addr).value(val).from(signer); + let tx = WithOtherFields::new(tx); // broadcast it via the eth_sendTransaction API - let _ = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); + let _ = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); - let balance = provider.get_balance(addr, None).await.unwrap(); - assert_eq!(balance, val.into()); + let balance = provider.get_balance(addr, BlockId::latest()).await.unwrap(); + assert_eq!(balance, val); } // @@ -625,42 +613,50 @@ async fn test_fork_nft_set_approve_all() { .await; // create and fund a random wallet - let wallet = LocalWallet::new(&mut rand::thread_rng()); - api.anvil_set_balance(wallet.address().to_alloy(), rU256::from(1000e18 as u64)).await.unwrap(); + let wallet = LocalWallet::random(); + let signer = wallet.address(); + api.anvil_set_balance(signer, U256::from(1000e18)).await.unwrap(); - let provider = ethers_http_provider(&handle.http_endpoint()); - let provider = Arc::new(SignerMiddleware::new(provider, wallet.clone())); + let provider = handle.http_provider(); // pick a random nft let nouns_addr: Address = "0x9c8ff314c9bc7f6e59a9d9225fb22946427edc03".parse().unwrap(); let owner: Address = "0x052564eb0fd8b340803df55def89c25c432f43f4".parse().unwrap(); - let token_id: U256 = 154u64.into(); - - let nouns = Erc721::new(nouns_addr, Arc::clone(&provider)); - - let real_owner = nouns.owner_of(token_id).call().await.unwrap(); - assert_eq!(real_owner, owner); - let approval = nouns.set_approval_for_all(nouns_addr, true); - let tx = approval.send().await.unwrap().await.unwrap().unwrap(); - assert_eq!(tx.status, Some(1u64.into())); - - let real_owner = real_owner.to_alloy(); + let token_id: U256 = U256::from(154u64); + + let nouns = ERC721::new(nouns_addr, provider.clone()); + + let real_owner = nouns.ownerOf(token_id).call().await.unwrap(); + assert_eq!(real_owner._0, owner); + let approval = nouns.setApprovalForAll(nouns_addr, true); + let tx = TransactionRequest::default() + .from(owner) + .to(nouns_addr) + .with_input(approval.calldata().to_owned()); + let tx = WithOtherFields::new(tx); + api.anvil_impersonate_account(owner).await.unwrap(); + let tx = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); + let status = tx.inner.inner.inner.receipt.status; + assert!(status); // transfer: impersonate real owner and transfer nft - api.anvil_impersonate_account(real_owner).await.unwrap(); - - api.anvil_set_balance(real_owner, rU256::from(10000e18 as u64)).await.unwrap(); - - let call = nouns.transfer_from(real_owner.to_ethers(), wallet.address(), token_id); - let mut tx: TypedTransaction = call.tx; - tx.set_from(real_owner.to_ethers()); - provider.fill_transaction(&mut tx, None).await.unwrap(); - let tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); - assert_eq!(tx.status, Some(1u64.into())); - - let real_owner = nouns.owner_of(token_id).call().await.unwrap(); - assert_eq!(real_owner, wallet.address()); + api.anvil_impersonate_account(real_owner._0).await.unwrap(); + + api.anvil_set_balance(real_owner._0, U256::from(10000e18 as u64)).await.unwrap(); + + let call = nouns.transferFrom(real_owner._0, signer, token_id); + let tx = TransactionRequest::default() + .from(real_owner._0) + .to(nouns_addr) + .with_input(call.calldata().to_owned()); + let tx = WithOtherFields::new(tx); + let tx = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); + let status = tx.inner.inner.inner.receipt.status; + assert!(status); + + let real_owner = nouns.ownerOf(token_id).call().await.unwrap(); + assert_eq!(real_owner._0, wallet.address()); } // @@ -701,22 +697,24 @@ async fn test_fork_can_send_opensea_tx() { let sender: Address = "0x8fdbae54b6d9f3fc2c649e3dd4602961967fd42f".parse().unwrap(); // transfer: impersonate real sender - api.anvil_impersonate_account(sender.to_alloy()).await.unwrap(); + api.anvil_impersonate_account(sender).await.unwrap(); - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); let input: Bytes = "0xfb0f3ee1000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ff2e795f5000000000000000000000000000023f28ae3e9756ba982a6290f9081b6a84900b758000000000000000000000000004c00500000ad104d7dbd00e3ae0a5c00560c0000000000000000000000000003235b597a78eabcb08ffcb4d97411073211dbcb0000000000000000000000000000000000000000000000000000000000000e72000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000062ad47c20000000000000000000000000000000000000000000000000000000062d43104000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000df44e65d2a2cf40000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f00000000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f00000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000001c6bf526340000000000000000000000000008de9c5a032463c561423387a9648c5c7bcc5bc900000000000000000000000000000000000000000000000000005543df729c0000000000000000000000000006eb234847a9e3a546539aac57a071c01dc3f398600000000000000000000000000000000000000000000000000000000000000416d39b5352353a22cf2d44faa696c2089b03137a13b5acfee0366306f2678fede043bc8c7e422f6f13a3453295a4a063dac7ee6216ab7bade299690afc77397a51c00000000000000000000000000000000000000000000000000000000000000".parse().unwrap(); let to: Address = "0x00000000006c3852cbef3e08e8df289169ede581".parse().unwrap(); - let tx = TransactionRequest::new() + let tx = TransactionRequest::default() .from(sender) .to(to) - .value(20000000000000000u64) - .data(input) - .gas_price(22180711707u64) - .gas(150_000u64); - - let tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); - assert_eq!(tx.status, Some(1u64.into())); + .value(U256::from(20000000000000000u64)) + .with_input(input) + .with_gas_price(22180711707u128) + .with_gas_limit(150_000u128); + let tx = WithOtherFields::new(tx); + + let tx = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); + let status = tx.inner.inner.inner.receipt.status; + assert!(status); } #[tokio::test(flavor = "multi_thread")] @@ -726,34 +724,34 @@ async fn test_fork_base_fee() { let accounts: Vec<_> = handle.dev_wallets().collect(); let from = accounts[0].address(); - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); - api.anvil_set_next_block_base_fee_per_gas(rU256::ZERO).await.unwrap(); + api.anvil_set_next_block_base_fee_per_gas(U256::ZERO).await.unwrap(); let addr = Address::random(); - let val = 1337u64; - let tx = TransactionRequest::new().from(from.to_ethers()).to(addr).value(val); - - let _res = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); + let val = U256::from(1337u64); + let tx = TransactionRequest::default().from(from).to(addr).value(val); + let tx = WithOtherFields::new(tx); + let _res = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); } #[tokio::test(flavor = "multi_thread")] async fn test_fork_init_base_fee() { let (api, handle) = spawn(fork_config().with_fork_block_number(Some(13184859u64))).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); - let block = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::latest(), false).await.unwrap().unwrap(); // - assert_eq!(block.number.unwrap().as_u64(), 13184859u64); - let init_base_fee = block.base_fee_per_gas.unwrap(); - assert_eq!(init_base_fee, 63739886069u64.into()); + assert_eq!(block.header.number.unwrap(), 13184859u64); + let init_base_fee = block.header.base_fee_per_gas.unwrap(); + assert_eq!(init_base_fee, 63739886069u128); api.mine_one().await; - let block = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::latest(), false).await.unwrap().unwrap(); - let next_base_fee = block.base_fee_per_gas.unwrap(); + let next_base_fee = block.header.base_fee_per_gas.unwrap(); assert!(next_base_fee < init_base_fee); } @@ -764,8 +762,7 @@ async fn test_reset_fork_on_new_blocks() { ) .await; - let anvil_provider = ethers_http_provider(&handle.http_endpoint()); - + let anvil_provider = handle.http_provider(); let endpoint = next_http_rpc_endpoint(); let provider = Arc::new(get_http_provider(&endpoint)); @@ -782,7 +779,7 @@ async fn test_reset_fork_on_new_blocks() { .flat_map(futures::stream::iter); // the http watcher may fetch multiple blocks at once, so we set a timeout here to offset edge // cases where the stream immediately returns a block - tokio::time::sleep(Chain::Mainnet.average_blocktime_hint().unwrap()).await; + tokio::time::sleep(Duration::from_secs(12)).await; stream.next().await.unwrap(); stream.next().await.unwrap(); @@ -797,19 +794,18 @@ async fn test_fork_call() { let to: Address = "0x99d1Fa417f94dcD62BfE781a1213c092a47041Bc".parse().unwrap(); let block_number = 14746300u64; - let provider = Provider::::try_from(rpc::next_http_archive_rpc_endpoint()).unwrap(); - let mut tx = TypedTransaction::default(); - tx.set_to(to).set_data(input.clone()); - let res0 = - provider.call(&tx, Some(BlockNumber::Number(block_number.into()).into())).await.unwrap(); + let provider = http_provider(rpc::next_http_archive_rpc_endpoint().as_str()); + let tx = TransactionRequest::default().to(to).with_input(input.clone()); + let tx = WithOtherFields::new(tx); + let res0 = provider.call(&tx, BlockId::Number(block_number.into())).await.unwrap(); let (api, _) = spawn(fork_config().with_fork_block_number(Some(block_number))).await; let res1 = api .call( - WithOtherFields::new(CallRequest { - to: Some(TxKind::Call(to.to_alloy())), - input: input.to_alloy().into(), + WithOtherFields::new(TransactionRequest { + to: Some(TxKind::from(to)), + input: input.into(), ..Default::default() }), None, @@ -818,7 +814,7 @@ async fn test_fork_call() { .await .unwrap(); - assert_eq!(res0, res1.to_ethers()); + assert_eq!(res0, res1); } #[tokio::test(flavor = "multi_thread")] @@ -826,7 +822,7 @@ async fn test_fork_block_timestamp() { let (api, _) = spawn(fork_config()).await; let initial_block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); - api.anvil_mine(Some(rU256::from(1)), None).await.unwrap(); + api.anvil_mine(Some(U256::from(1)), None).await.unwrap(); let latest_block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); assert!(initial_block.header.timestamp < latest_block.header.timestamp); @@ -837,11 +833,11 @@ async fn test_fork_snapshot_block_timestamp() { let (api, _) = spawn(fork_config()).await; let snapshot_id = api.evm_snapshot().await.unwrap(); - api.anvil_mine(Some(rU256::from(1)), None).await.unwrap(); + api.anvil_mine(Some(U256::from(1)), None).await.unwrap(); let initial_block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); api.evm_revert(snapshot_id).await.unwrap(); api.evm_set_next_block_timestamp(initial_block.header.timestamp).unwrap(); - api.anvil_mine(Some(rU256::from(1)), None).await.unwrap(); + api.anvil_mine(Some(U256::from(1)), None).await.unwrap(); let latest_block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); assert_eq!(initial_block.header.timestamp, latest_block.header.timestamp); @@ -850,7 +846,7 @@ async fn test_fork_snapshot_block_timestamp() { #[tokio::test(flavor = "multi_thread")] async fn test_fork_uncles_fetch() { let (api, handle) = spawn(fork_config()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); // Block on ETH mainnet with 2 uncles let block_with_uncles = 190u64; @@ -860,35 +856,36 @@ async fn test_fork_uncles_fetch() { assert_eq!(block.uncles.len(), 2); - let count = provider.get_uncle_count(block_with_uncles).await.unwrap(); - assert_eq!(count.as_usize(), block.uncles.len()); + let count = provider.get_uncle_count(block_with_uncles.into()).await.unwrap(); + assert_eq!(count as usize, block.uncles.len()); - let count = provider.get_uncle_count(block.header.hash.unwrap().to_ethers()).await.unwrap(); - assert_eq!(count.as_usize(), block.uncles.len()); + let hash = BlockId::hash(block.header.hash.unwrap()); + let count = provider.get_uncle_count(hash).await.unwrap(); + assert_eq!(count as usize, block.uncles.len()); for (uncle_idx, uncle_hash) in block.uncles.iter().enumerate() { // Try with block number let uncle = provider - .get_uncle(block_with_uncles, (uncle_idx as u64).into()) + .get_uncle(BlockId::number(block_with_uncles), uncle_idx as u64) .await .unwrap() .unwrap(); - assert_eq!(*uncle_hash, uncle.hash.unwrap().to_alloy()); + assert_eq!(*uncle_hash, uncle.header.hash.unwrap()); // Try with block hash let uncle = provider - .get_uncle(block.header.hash.unwrap().to_ethers(), (uncle_idx as u64).into()) + .get_uncle(BlockId::hash(block.header.hash.unwrap()), uncle_idx as u64) .await .unwrap() .unwrap(); - assert_eq!(*uncle_hash, uncle.hash.unwrap().to_alloy()); + assert_eq!(*uncle_hash, uncle.header.hash.unwrap()); } } #[tokio::test(flavor = "multi_thread")] async fn test_fork_block_transaction_count() { let (api, handle) = spawn(fork_config()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); let accounts: Vec<_> = handle.dev_wallets().collect(); let sender = accounts[0].address(); @@ -898,8 +895,10 @@ async fn test_fork_block_transaction_count() { // transfer: impersonate real sender api.anvil_impersonate_account(sender).await.unwrap(); - let tx = TransactionRequest::new().from(sender.to_ethers()).value(42u64).gas(100_000); - provider.send_transaction(tx, None).await.unwrap(); + let tx = + TransactionRequest::default().from(sender).value(U256::from(42u64)).with_gas_limit(100_000); + let tx = WithOtherFields::new(tx); + let _ = provider.send_transaction(tx).await.unwrap(); let pending_txs = api.block_transaction_count_by_number(BlockNumberOrTag::Pending).await.unwrap().unwrap(); @@ -943,31 +942,32 @@ async fn test_fork_block_transaction_count() { #[tokio::test(flavor = "multi_thread")] async fn can_impersonate_in_fork() { let (api, handle) = spawn(fork_config().with_fork_block_number(Some(15347924u64))).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); let token_holder: Address = "0x2f0b23f53734252bda2277357e97e1517d6b042a".parse().unwrap(); let to = Address::random(); - let val = 1337u64; + let val = U256::from(1337u64); // fund the impersonated account - api.anvil_set_balance(token_holder.to_alloy(), rU256::from(1e18 as u64)).await.unwrap(); - - let tx = TransactionRequest::new().from(token_holder).to(to).value(val); + api.anvil_set_balance(token_holder, U256::from(1e18)).await.unwrap(); - let res = provider.send_transaction(tx.clone(), None).await; + let tx = TransactionRequest::default().from(token_holder).to(to).value(val); + let tx = WithOtherFields::new(tx); + let res = provider.send_transaction(tx.clone()).await; res.unwrap_err(); - api.anvil_impersonate_account(token_holder.to_alloy()).await.unwrap(); + api.anvil_impersonate_account(token_holder).await.unwrap(); - let res = provider.send_transaction(tx.clone(), None).await.unwrap().await.unwrap().unwrap(); + let res = provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); assert_eq!(res.from, token_holder); - assert_eq!(res.status, Some(1u64.into())); + let status = res.inner.inner.inner.receipt.status; + assert!(status); - let balance = provider.get_balance(to, None).await.unwrap(); - assert_eq!(balance, val.into()); + let balance = provider.get_balance(to, BlockId::latest()).await.unwrap(); + assert_eq!(balance, val); - api.anvil_stop_impersonating_account(token_holder.to_alloy()).await.unwrap(); - let res = provider.send_transaction(tx, None).await; + api.anvil_stop_impersonating_account(token_holder).await.unwrap(); + let res = provider.send_transaction(tx).await; res.unwrap_err(); } @@ -976,22 +976,22 @@ async fn can_impersonate_in_fork() { async fn test_total_difficulty_fork() { let (api, handle) = spawn(fork_config()).await; - let total_difficulty: U256 = 46_673_965_560_973_856_260_636u128.into(); - let difficulty: U256 = 13_680_435_288_526_144u128.into(); + let total_difficulty = U256::from(46_673_965_560_973_856_260_636u128); + let difficulty = U256::from(13_680_435_288_526_144u128); - let provider = ethers_http_provider(&handle.http_endpoint()); - let block = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); - assert_eq!(block.total_difficulty, Some(total_difficulty)); - assert_eq!(block.difficulty, difficulty); + let provider = handle.http_provider(); + let block = provider.get_block(BlockId::latest(), false).await.unwrap().unwrap(); + assert_eq!(block.header.total_difficulty, Some(total_difficulty)); + assert_eq!(block.header.difficulty, difficulty); api.mine_one().await; api.mine_one().await; let next_total_difficulty = total_difficulty + difficulty; - let block = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); - assert_eq!(block.total_difficulty, Some(next_total_difficulty)); - assert_eq!(block.difficulty, U256::zero()); + let block = provider.get_block(BlockId::latest(), false).await.unwrap().unwrap(); + assert_eq!(block.header.total_difficulty, Some(next_total_difficulty)); + assert_eq!(block.header.difficulty, U256::ZERO); } // @@ -1041,29 +1041,24 @@ async fn can_override_fork_chain_id() { .with_chain_id(Some(chain_id_override)), ) .await; - let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); - - let greeter_contract = Greeter::deploy(Arc::clone(&client), "Hello World!".to_string()) - .unwrap() - .send() - .await - .unwrap(); + let wallet = handle.dev_wallets().next().unwrap(); + let signer: EthereumSigner = wallet.into(); + let provider = http_provider_with_signer(&handle.http_endpoint(), signer); + let greeter_contract = + Greeter::deploy(provider.clone(), "Hello World!".to_string()).await.unwrap(); let greeting = greeter_contract.greet().call().await.unwrap(); - assert_eq!("Hello World!", greeting); + assert_eq!("Hello World!", greeting._0); let greeter_contract = - Greeter::deploy(client, "Hello World!".to_string()).unwrap().send().await.unwrap(); - + Greeter::deploy(provider.clone(), "Hello World!".to_string()).await.unwrap(); let greeting = greeter_contract.greet().call().await.unwrap(); - assert_eq!("Hello World!", greeting); + assert_eq!("Hello World!", greeting._0); - let provider = ethers_http_provider(&handle.http_endpoint()); - let chain_id = provider.get_chainid().await.unwrap(); - assert_eq!(chain_id.as_u64(), chain_id_override); + let provider = handle.http_provider(); + let chain_id = provider.get_chain_id().await.unwrap(); + assert_eq!(chain_id, chain_id_override); } // @@ -1076,14 +1071,18 @@ async fn test_fork_reset_moonbeam() { .with_fork_block_number(None::), ) .await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); let accounts: Vec<_> = handle.dev_wallets().collect(); let from = accounts[0].address(); - let tx = TransactionRequest::new().to(Address::random()).value(1337u64).from(from.to_ethers()); - let tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); - assert_eq!(tx.status, Some(1u64.into())); + let tx = + TransactionRequest::default().to(Address::random()).value(U256::from(1337u64)).from(from); + let tx = WithOtherFields::new(tx); + api.anvil_impersonate_account(from).await.unwrap(); + let tx = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); + let status = tx.inner.inner.inner.receipt.status; + assert!(status); // reset to check timestamp works after resetting api.anvil_reset(Some(Forking { @@ -1093,9 +1092,12 @@ async fn test_fork_reset_moonbeam() { .await .unwrap(); - let tx = TransactionRequest::new().to(Address::random()).value(1337u64).from(from.to_ethers()); - let tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); - assert_eq!(tx.status, Some(1u64.into())); + let tx = + TransactionRequest::default().to(Address::random()).value(U256::from(1337u64)).from(from); + let tx = WithOtherFields::new(tx); + let tx = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); + let status = tx.inner.inner.inner.receipt.status; + assert!(status); } // = handle.dev_wallets().collect(); for acc in accounts { let balance = api.balance(acc.address(), Some(Default::default())).await.unwrap(); - assert_eq!(balance, rU256::from(100000000000000000000u128)); + assert_eq!(balance, U256::from(100000000000000000000u128)); } } @@ -1149,8 +1151,8 @@ async fn test_arbitrum_fork_block_number() { .with_eth_rpc_url(Some("https://arb1.arbitrum.io/rpc".to_string())), ) .await; - let provider = ethers_http_provider(&handle.http_endpoint()); - let initial_block_number = provider.get_block_number().await.unwrap().as_u64(); + let provider = handle.http_provider(); + let initial_block_number = provider.get_block_number().await.unwrap(); // fork again at block number returned by `eth_blockNumber` // if wrong block number returned (e.g. L1) then fork will fail with error code -32000: missing @@ -1196,8 +1198,8 @@ async fn test_fork_execution_reverted() { let resp = api .call( - WithOtherFields::new(CallRequest { - to: Some(TxKind::Call(address!("Fd6CC4F251eaE6d02f9F7B41D1e80464D3d2F377"))), + WithOtherFields::new(TransactionRequest { + to: Some(TxKind::from(address!("Fd6CC4F251eaE6d02f9F7B41D1e80464D3d2F377"))), input: TransactionInput::new("0x8f283b3c".as_bytes().into()), ..Default::default() }), diff --git a/crates/anvil/tests/it/ganache.rs b/crates/anvil/tests/it/ganache.rs index b95b55433..ca8589e21 100644 --- a/crates/anvil/tests/it/ganache.rs +++ b/crates/anvil/tests/it/ganache.rs @@ -1,101 +1,93 @@ //! tests against local ganache for local debug purposes #![allow(unused)] use crate::{ + abi::Greeter, init_tracing, - utils::{ContractInstanceCompat, DeploymentTxFactoryCompat}, -}; -use ethers::{ - abi::Address, - contract::{Contract, ContractFactory, ContractInstance}, - core::k256::SecretKey, - prelude::{abigen, Middleware, Signer, SignerMiddleware, TransactionRequest, Ws}, - providers::{Http, Provider}, - signers::LocalWallet, - types::{BlockNumber, U256}, - utils::hex, + utils::{http_provider, http_provider_with_signer, ws_provider_with_signer}, }; +use alloy_contract::ContractInstance; +use alloy_network::EthereumSigner; +use alloy_primitives::{address, Address, TxKind}; +use alloy_provider::Provider; +use alloy_rpc_types::{BlockId, TransactionRequest, WithOtherFields}; +use alloy_signer_wallet::{LocalWallet, MnemonicBuilder}; +use alloy_sol_types::{sol, Revert}; use foundry_compilers::{project_util::TempProject, Artifact}; -use std::sync::Arc; +use std::{str::FromStr, sync::Arc}; // the mnemonic used to start the local ganache instance const MNEMONIC: &str = "amazing discover palace once resource choice flush horn wink shift planet relief"; fn ganache_wallet() -> LocalWallet { - wallet("552dd2534c4984f892191997d6b1dd9e6a23c7e07b908a6cebfad1d3f2af4c4c") + LocalWallet::from_str("552dd2534c4984f892191997d6b1dd9e6a23c7e07b908a6cebfad1d3f2af4c4c") + .unwrap() } fn ganache_wallet2() -> LocalWallet { - wallet("305b526d493844b63466be6d48a424ab83f5216011eef860acc6db4c1821adc9") + LocalWallet::from_str("305b526d493844b63466be6d48a424ab83f5216011eef860acc6db4c1821adc9") + .unwrap() } fn wallet(key_str: &str) -> LocalWallet { - let key_hex = hex::decode(key_str).expect("could not parse as hex"); - let key = SecretKey::from_bytes(key_hex.as_slice().into()).expect("did not get private key"); - key.into() -} - -fn http_client() -> Arc, LocalWallet>> { - let provider = Provider::::try_from("http://127.0.0.1:8545").unwrap(); - Arc::new(SignerMiddleware::new(provider, ganache_wallet())) -} - -async fn ws_client() -> Arc, LocalWallet>> { - let provider = Provider::::connect("ws://127.0.0.1:8545").await.unwrap(); - Arc::new(SignerMiddleware::new(provider, ganache_wallet())) + LocalWallet::from_str(key_str).unwrap() } #[tokio::test(flavor = "multi_thread")] #[ignore] async fn test_ganache_block_number() { - let client = http_client(); - let balance = client - .get_balance(Address::random(), Some(BlockNumber::Number(100u64.into()).into())) - .await; + let provider = http_provider("http://127.0.0.1:8545"); + + let balance = provider.get_balance(Address::random(), BlockId::latest()).await; } #[tokio::test(flavor = "multi_thread")] #[ignore] async fn test_ganache_deploy() { - abigen!(Greeter, "test-data/greeter.json"); - let client = http_client(); + let signer: EthereumSigner = ganache_wallet().into(); + let provider = http_provider_with_signer("http://127.0.0.1:8545", signer); - let greeter_contract = - Greeter::deploy(client, "Hello World!".to_string()).unwrap().legacy().send().await.unwrap(); + let greeter_contract_builder = Greeter::deploy_builder(&provider, "Hello World!".to_string()); + let greeter_contract_address = greeter_contract_builder.deploy().await.unwrap(); + let greeter_contract = Greeter::new(greeter_contract_address, &provider); - let greeting = greeter_contract.greet().call().await.unwrap(); + let Greeter::greetReturn { _0 } = greeter_contract.greet().call().await.unwrap(); + let greeting = _0; assert_eq!("Hello World!", greeting); } #[tokio::test(flavor = "multi_thread")] #[ignore] async fn test_ganache_emit_logs() { - abigen!(EmitLogs, "test-data/emit_logs.json"); - let client = ws_client().await; + sol!( + #[sol(rpc)] + EmitLogs, + "test-data/emit_logs.json" + ); - let msg = "First Message".to_string(); - let contract = - EmitLogs::deploy(Arc::clone(&client), msg.clone()).unwrap().legacy().send().await.unwrap(); + let signer: EthereumSigner = ganache_wallet().into(); + let provider = ws_provider_with_signer("ws://127.0.0.1:8545", signer); - let val = contract.get_value().call().await.unwrap(); - assert_eq!(val, msg); + let first_msg = "First Message".to_string(); + let next_msg = "Next Message".to_string(); + let emit_logs_contract_builder = EmitLogs::deploy_builder(&provider, first_msg.clone()); + let emit_logs_contract_address = emit_logs_contract_builder.deploy().await.unwrap(); + let emit_logs_contract = EmitLogs::new(emit_logs_contract_address, &provider); - let val = contract - .set_value("Next Message".to_string()) - .legacy() - .send() - .await - .unwrap() - .await - .unwrap() - .unwrap(); + let EmitLogs::getValueReturn { _0 } = emit_logs_contract.getValue().call().await.unwrap(); + let val = _0; + assert_eq!(val, first_msg); + + emit_logs_contract.setValue(next_msg.clone()).send().await.unwrap(); + + let EmitLogs::getValueReturn { _0 } = emit_logs_contract.getValue().call().await.unwrap(); + let val = _0; + assert_eq!(val, next_msg); } #[tokio::test(flavor = "multi_thread")] #[ignore] async fn test_ganache_deploy_reverting() { - let client = http_client(); - let prj = TempProject::dapptools().unwrap(); prj.add_source( "Contract", @@ -110,17 +102,33 @@ contract Contract { ) .unwrap(); + sol!( + #[sol(rpc)] + contract Contract {} + ); + let mut compiled = prj.compile().unwrap(); - println!("{compiled}"); assert!(!compiled.has_compiler_errors()); - let contract = compiled.remove_first("Contract").unwrap(); - - let (abi, bytecode, _) = contract.into_contract_bytecode().into_parts(); - - let factory = ContractFactory::new_compat(abi.unwrap(), bytecode.unwrap(), Arc::clone(&client)); - let contract = factory.deploy(()).unwrap().legacy().send().await; - contract.unwrap_err(); + let bytecode = contract.into_bytecode_bytes().unwrap(); + + let wallet = ganache_wallet(); + let signer: EthereumSigner = wallet.clone().into(); + let sender = wallet.address(); + + let provider = http_provider_with_signer("http://127.0.0.1:8545", signer); + + // should catch the revert during estimation which results in an err + let err = provider + .send_transaction(WithOtherFields::new(TransactionRequest { + from: Some(sender), + to: Some(TxKind::Create), + input: bytecode.into(), + ..Default::default() + })) + .await + .unwrap_err(); + assert!(err.to_string().contains("execution reverted")); } #[tokio::test(flavor = "multi_thread")] @@ -148,41 +156,63 @@ contract Contract { ) .unwrap(); + sol!( + #[sol(rpc)] + contract Contract { + function getSecret() public view returns (uint256); + } + ); + let mut compiled = prj.compile().unwrap(); assert!(!compiled.has_compiler_errors()); let contract = compiled.remove_first("Contract").unwrap(); - let (abi, bytecode, _) = contract.into_contract_bytecode().into_parts(); + let bytecode = contract.into_bytecode_bytes().unwrap(); + + let wallet = ganache_wallet(); + let signer: EthereumSigner = wallet.clone().into(); + let sender = wallet.address(); - let client = Arc::new(http_client()); + let provider = http_provider_with_signer("http://127.0.0.1:8545", signer); // deploy successfully - let factory = ContractFactory::new_compat(abi.clone().unwrap(), bytecode.unwrap(), client); - let contract = factory.deploy(()).unwrap().legacy().send().await.unwrap(); - let provider = SignerMiddleware::new( - Provider::::try_from("http://127.0.0.1:8545").unwrap(), - ganache_wallet2(), - ); - let contract = ContractInstance::new_compat(contract.address(), abi.unwrap(), provider); - let resp = contract.method::<_, U256>("getSecret", ()).unwrap().legacy().call().await; - resp.unwrap_err(); - - /* Ganache rpc errors look like: - < { - < "id": 1627277502538, - < "jsonrpc": "2.0", - < "error": { - < "message": "VM Exception while processing transaction: revert !authorized", - < "code": -32000, - < "data": { - < "0x90264de254689f1d4e7f8670cd97f60d9bc803874fdecb34d249bd1cc3ca823a": { - < "error": "revert", - < "program_counter": 223, - < "return": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b21617574686f72697a6564000000000000000000000000000000000000000000", - < "reason": "!authorized" - < }, - < "stack": "c: VM Exception while processing transaction: revert !authorized\n at Function.c.fromResults (/usr/local/lib/node_modules/ganache-cli/build/ganache-core.node.cli.js:4:192416)\n at /usr/local/lib/node_modules/ganache-cli/build/ganache-core.node.cli.js:42:50402", - < "name": "c" - < } - < } - */ + provider + .send_transaction(WithOtherFields::new(TransactionRequest { + from: Some(sender), + to: Some(TxKind::Create), + input: bytecode.into(), + ..Default::default() + })) + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + let contract_address = sender.create(0); + let contract = Contract::new(contract_address, &provider); + + // should catch the revert during the call which results in an err + contract.getSecret().send().await.unwrap_err(); + + // /* Ganache rpc errors look like: + // < { + // < "id": 1627277502538, + // < "jsonrpc": "2.0", + // < "error": { + // < "message": "VM Exception while processing transaction: revert !authorized", + // < "code": -32000, + // < "data": { + // < "0x90264de254689f1d4e7f8670cd97f60d9bc803874fdecb34d249bd1cc3ca823a": { + // < "error": "revert", + // < "program_counter": 223, + // < "return": + // "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b21617574686f72697a6564000000000000000000000000000000000000000000" + // , < "reason": "!authorized" + // < }, + // < "stack": "c: VM Exception while processing transaction: revert !authorized\n at + // Function.c.fromResults + // (/usr/local/lib/node_modules/ganache-cli/build/ganache-core.node.cli.js:4:192416)\n at + // /usr/local/lib/node_modules/ganache-cli/build/ganache-core.node.cli.js:42:50402", < + // "name": "c" < } + // < } + // */ } diff --git a/crates/anvil/tests/it/gas.rs b/crates/anvil/tests/it/gas.rs index e2d194340..93cebb90c 100644 --- a/crates/anvil/tests/it/gas.rs +++ b/crates/anvil/tests/it/gas.rs @@ -1,14 +1,11 @@ //! Gas related tests -use crate::utils::ethers_http_provider; +use crate::utils::http_provider_with_signer; +use alloy_network::{EthereumSigner, TransactionBuilder}; +use alloy_primitives::{Address, U256}; +use alloy_provider::Provider; +use alloy_rpc_types::{BlockId, TransactionRequest, WithOtherFields}; use anvil::{eth::fees::INITIAL_BASE_FEE, spawn, NodeConfig}; -use ethers::{ - prelude::Middleware, - types::{ - transaction::eip2718::TypedTransaction, Address, BlockNumber, Eip1559TransactionRequest, - TransactionRequest, - }, -}; const GAS_TRANSFER: u128 = 21_000; @@ -18,19 +15,41 @@ async fn test_basefee_full_block() { NodeConfig::test().with_base_fee(Some(INITIAL_BASE_FEE)).with_gas_limit(Some(GAS_TRANSFER)), ) .await; - let provider = ethers_http_provider(&handle.http_endpoint()); - let tx = TransactionRequest::new().to(Address::random()).value(1337u64); - provider.send_transaction(tx.clone(), None).await.unwrap().await.unwrap().unwrap(); - let base_fee = - provider.get_block(BlockNumber::Latest).await.unwrap().unwrap().base_fee_per_gas.unwrap(); - let tx = TransactionRequest::new().to(Address::random()).value(1337u64); - provider.send_transaction(tx.clone(), None).await.unwrap().await.unwrap().unwrap(); - let next_base_fee = - provider.get_block(BlockNumber::Latest).await.unwrap().unwrap().base_fee_per_gas.unwrap(); + + let wallet = handle.dev_wallets().next().unwrap(); + let signer: EthereumSigner = wallet.clone().into(); + + let provider = http_provider_with_signer(&handle.http_endpoint(), signer); + + let tx = TransactionRequest::default().to(Address::random()).with_value(U256::from(1337)); + let tx = WithOtherFields::new(tx); + + provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); + + let base_fee = provider + .get_block(BlockId::latest(), false) + .await + .unwrap() + .unwrap() + .header + .base_fee_per_gas + .unwrap(); + + provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); + + let next_base_fee = provider + .get_block(BlockId::latest(), false) + .await + .unwrap() + .unwrap() + .header + .base_fee_per_gas + .unwrap(); assert!(next_base_fee > base_fee); + // max increase, full block - assert_eq!(next_base_fee.as_u128(), INITIAL_BASE_FEE + 125_000_000); + assert_eq!(next_base_fee, INITIAL_BASE_FEE + 125_000_000); } #[tokio::test(flavor = "multi_thread")] @@ -41,32 +60,69 @@ async fn test_basefee_half_block() { .with_gas_limit(Some(GAS_TRANSFER * 2)), ) .await; - let provider = ethers_http_provider(&handle.http_endpoint()); - let tx = TransactionRequest::new().to(Address::random()).value(1337u64); - provider.send_transaction(tx.clone(), None).await.unwrap().await.unwrap().unwrap(); - let tx = TransactionRequest::new().to(Address::random()).value(1337u64); - provider.send_transaction(tx.clone(), None).await.unwrap().await.unwrap().unwrap(); - let next_base_fee = - provider.get_block(BlockNumber::Latest).await.unwrap().unwrap().base_fee_per_gas.unwrap(); + + let wallet = handle.dev_wallets().next().unwrap(); + let signer: EthereumSigner = wallet.clone().into(); + + let provider = http_provider_with_signer(&handle.http_endpoint(), signer); + + let tx = TransactionRequest::default().to(Address::random()).with_value(U256::from(1337)); + let tx = WithOtherFields::new(tx); + + provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); + + let tx = TransactionRequest::default().to(Address::random()).with_value(U256::from(1337)); + let tx = WithOtherFields::new(tx); + + provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); + + let next_base_fee = provider + .get_block(BlockId::latest(), false) + .await + .unwrap() + .unwrap() + .header + .base_fee_per_gas + .unwrap(); // unchanged, half block - assert_eq!(next_base_fee.as_u128(), INITIAL_BASE_FEE); + assert_eq!(next_base_fee, INITIAL_BASE_FEE); } + #[tokio::test(flavor = "multi_thread")] async fn test_basefee_empty_block() { let (api, handle) = spawn(NodeConfig::test().with_base_fee(Some(INITIAL_BASE_FEE))).await; - let provider = ethers_http_provider(&handle.http_endpoint()); - let tx = TransactionRequest::new().to(Address::random()).value(1337u64); - provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); - let base_fee = - provider.get_block(BlockNumber::Latest).await.unwrap().unwrap().base_fee_per_gas.unwrap(); + let wallet = handle.dev_wallets().next().unwrap(); + let signer: EthereumSigner = wallet.clone().into(); + + let provider = http_provider_with_signer(&handle.http_endpoint(), signer); + + let tx = TransactionRequest::default().with_to(Address::random()).with_value(U256::from(1337)); + let tx = WithOtherFields::new(tx); + + provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); + + let base_fee = provider + .get_block(BlockId::latest(), false) + .await + .unwrap() + .unwrap() + .header + .base_fee_per_gas + .unwrap(); // mine empty block api.mine_one().await; - let next_base_fee = - provider.get_block(BlockNumber::Latest).await.unwrap().unwrap().base_fee_per_gas.unwrap(); + let next_base_fee = provider + .get_block(BlockId::latest(), false) + .await + .unwrap() + .unwrap() + .header + .base_fee_per_gas + .unwrap(); // empty block, decreased base fee assert!(next_base_fee < base_fee); @@ -76,35 +132,38 @@ async fn test_basefee_empty_block() { async fn test_respect_base_fee() { let base_fee = 50u128; let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee))).await; - let provider = ethers_http_provider(&handle.http_endpoint()); - let mut tx = TypedTransaction::default(); - tx.set_value(100u64); - tx.set_to(Address::random()); + + let provider = handle.http_provider(); + + let tx = TransactionRequest::default().with_to(Address::random()).with_value(U256::from(100)); + let mut tx = WithOtherFields::new(tx); let mut underpriced = tx.clone(); underpriced.set_gas_price(base_fee - 1); - let res = provider.send_transaction(underpriced, None).await; + + let res = provider.send_transaction(underpriced).await; assert!(res.is_err()); assert!(res.unwrap_err().to_string().contains("max fee per gas less than block base fee")); tx.set_gas_price(base_fee); - let tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); - assert_eq!(tx.status, Some(1u64.into())); + provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); } #[tokio::test(flavor = "multi_thread")] async fn test_tip_above_fee_cap() { let base_fee = 50u128; let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee))).await; - let provider = ethers_http_provider(&handle.http_endpoint()); - let tx = TypedTransaction::Eip1559( - Eip1559TransactionRequest::new() - .max_fee_per_gas(base_fee) - .max_priority_fee_per_gas(base_fee + 1) - .to(Address::random()) - .value(100u64), - ); - let res = provider.send_transaction(tx, None).await; + + let provider = handle.http_provider(); + + let tx = TransactionRequest::default() + .max_fee_per_gas(base_fee) + .max_priority_fee_per_gas(base_fee + 1) + .with_to(Address::random()) + .with_value(U256::from(100)); + let tx = WithOtherFields::new(tx); + + let res = provider.send_transaction(tx.clone()).await; assert!(res.is_err()); assert!(res .unwrap_err() diff --git a/crates/anvil/tests/it/genesis.rs b/crates/anvil/tests/it/genesis.rs index c8157e160..bb4e2d24b 100644 --- a/crates/anvil/tests/it/genesis.rs +++ b/crates/anvil/tests/it/genesis.rs @@ -1,12 +1,11 @@ //! genesis.json tests -use std::str::FromStr; - -use alloy_eips::BlockId; use alloy_genesis::Genesis; use alloy_primitives::{Address, U256}; use alloy_provider::Provider; +use alloy_rpc_types::BlockId; use anvil::{spawn, NodeConfig}; +use std::str::FromStr; #[tokio::test(flavor = "multi_thread")] async fn can_apply_genesis() { diff --git a/crates/anvil/tests/it/geth.rs b/crates/anvil/tests/it/geth.rs index e1c9bc7b8..7879f4911 100644 --- a/crates/anvil/tests/it/geth.rs +++ b/crates/anvil/tests/it/geth.rs @@ -1,37 +1,35 @@ //! tests against local geth for local debug purposes use crate::{ - abi::VENDING_MACHINE_CONTRACT, - utils::{ContractInstanceCompat, DeploymentTxFactoryCompat}, -}; -use ethers::{ - abi::Address, - contract::{Contract, ContractFactory}, - prelude::{Middleware, TransactionRequest}, - providers::Provider, - types::U256, - utils::WEI_IN_ETHER, + abi::{VendingMachine, VENDING_MACHINE_CONTRACT}, + utils::{http_provider, http_provider_with_signer}, }; +use alloy_network::{EthereumSigner, TransactionBuilder}; +use alloy_primitives::{Address, TxKind, U256}; +use alloy_provider::Provider; +use alloy_rpc_types::{TransactionRequest, WithOtherFields}; +use anvil::{spawn, NodeConfig}; use foundry_compilers::{project_util::TempProject, Artifact}; use futures::StreamExt; -use std::sync::Arc; use tokio::time::timeout; #[tokio::test(flavor = "multi_thread")] #[ignore] async fn test_geth_pending_transaction() { - let client = Provider::try_from("http://127.0.0.1:8545").unwrap(); - let accounts = client.get_accounts().await.unwrap(); - let tx = TransactionRequest::new() - .from(accounts[0]) - .to(Address::random()) - .value(1337u64) - .nonce(2u64); + let provider = http_provider("http://127.0.0.1:8545"); + + let account = provider.get_accounts().await.unwrap().remove(0); - let mut watch_tx_stream = - client.watch_pending_transactions().await.unwrap().transactions_unordered(1).fuse(); + let tx = TransactionRequest::default() + .with_from(account) + .with_to(Address::random()) + .with_value(U256::from(1337u64)) + .with_nonce(2u64); + let tx = WithOtherFields::new(tx); - let _res = client.send_transaction(tx, None).await.unwrap(); + let mut watch_tx_stream = provider.watch_pending_transactions().await.unwrap().into_stream(); + + provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); let pending = timeout(std::time::Duration::from_secs(3), watch_tx_stream.next()).await; pending.unwrap_err(); @@ -47,48 +45,59 @@ async fn test_geth_revert_transaction() { let mut compiled = prj.compile().unwrap(); assert!(!compiled.has_compiler_errors()); let contract = compiled.remove_first("VendingMachine").unwrap(); - let (abi, bytecode, _) = contract.into_contract_bytecode().into_parts(); + let bytecode = contract.into_bytecode_bytes().unwrap(); - let client = Arc::new(Provider::try_from("http://127.0.0.1:8545").unwrap()); + let (_api, handle) = spawn(NodeConfig::test()).await; + let wallet = handle.dev_wallets().next().unwrap(); + let signer: EthereumSigner = wallet.clone().into(); + let sender = wallet.address(); - let account = client.get_accounts().await.unwrap().remove(0); + let provider = http_provider_with_signer("http://127.0.0.1:8545", signer); // deploy successfully - let factory = - ContractFactory::new_compat(abi.clone().unwrap(), bytecode.unwrap(), Arc::clone(&client)); - - let mut tx = factory.deploy(()).unwrap().tx; - tx.set_from(account); - - let resp = client.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); - - let contract = - Contract::>::new_compat(resp.contract_address.unwrap(), abi.unwrap(), client); - - let ten = WEI_IN_ETHER.saturating_mul(10u64.into()); - let call = contract.method::<_, ()>("buyRevert", ten).unwrap().value(ten).from(account); - let resp = call.call().await; - let err = resp.unwrap_err().to_string(); - assert!(err.contains("execution reverted: Not enough Ether provided.")); - assert!(err.contains("code: 3")); + provider + .send_transaction(WithOtherFields::new(TransactionRequest { + from: Some(sender), + to: Some(TxKind::Create), + input: bytecode.into(), + ..Default::default() + })) + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + let contract_address = sender.create(0); + let contract = VendingMachine::new(contract_address, &provider); + + let res = contract + .buyRevert(U256::from(100)) + .value(U256::from(1)) + .from(sender) + .send() + .await + .unwrap_err(); + let msg = res.to_string(); + assert!(msg.contains("execution reverted: revert: Not enough Ether provided.")); + assert!(msg.contains("code: 3")); } #[tokio::test(flavor = "multi_thread")] #[ignore] async fn test_geth_low_gas_limit() { - let provider = Arc::new(Provider::try_from("http://127.0.0.1:8545").unwrap()); + let provider = http_provider("http://127.0.0.1:8545"); let account = provider.get_accounts().await.unwrap().remove(0); - let gas = 21_000u64 - 1; - let tx = TransactionRequest::new() + let gas_limit = 21_000u128 - 1; + let tx = TransactionRequest::default() + .from(account) .to(Address::random()) .value(U256::from(1337u64)) - .from(account) - .gas(gas); - - let resp = provider.send_transaction(tx, None).await; + .gas_limit(gas_limit); + let tx = WithOtherFields::new(tx); + let resp = provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await; let err = resp.unwrap_err().to_string(); assert!(err.contains("intrinsic gas too low")); } diff --git a/crates/anvil/tests/it/ipc.rs b/crates/anvil/tests/it/ipc.rs index 8b7de0884..676a0f61b 100644 --- a/crates/anvil/tests/it/ipc.rs +++ b/crates/anvil/tests/it/ipc.rs @@ -1,9 +1,9 @@ //! IPC tests -use crate::utils::ethers_ipc_provider; +use crate::utils::connect_pubsub; use alloy_primitives::U256; +use alloy_provider::Provider; use anvil::{spawn, NodeConfig}; -use ethers::prelude::Middleware; use futures::StreamExt; pub fn rand_ipc_endpoint() -> String { @@ -20,31 +20,33 @@ fn ipc_config() -> NodeConfig { } #[tokio::test(flavor = "multi_thread")] +#[cfg_attr(target_os = "windows", ignore)] async fn can_get_block_number_ipc() { let (api, handle) = spawn(ipc_config()).await; let block_num = api.block_number().unwrap(); assert_eq!(block_num, U256::ZERO); - let provider = ethers_ipc_provider(handle.ipc_path()).unwrap(); + let provider = handle.ipc_provider().unwrap(); let num = provider.get_block_number().await.unwrap(); - assert_eq!(num.as_u64(), block_num.to::()); + assert_eq!(num, block_num.to::()); } #[tokio::test(flavor = "multi_thread")] +#[cfg_attr(target_os = "windows", ignore)] async fn test_sub_new_heads_ipc() { let (api, handle) = spawn(ipc_config()).await; - let provider = ethers_ipc_provider(handle.ipc_path()).unwrap(); + let provider = connect_pubsub(handle.ipc_path().unwrap().as_str()).await; - let blocks = provider.subscribe_blocks().await.unwrap(); + let blocks = provider.subscribe_blocks().await.unwrap().into_stream(); // mine a block every 1 seconds api.anvil_set_interval_mining(1).unwrap(); let blocks = blocks.take(3).collect::>().await; - let block_numbers = blocks.into_iter().map(|b| b.number.unwrap().as_u64()).collect::>(); + let block_numbers = blocks.into_iter().map(|b| b.header.number.unwrap()).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 2f9290f3e..afcb34019 100644 --- a/crates/anvil/tests/it/logs.rs +++ b/crates/anvil/tests/it/logs.rs @@ -1,88 +1,119 @@ //! log/event related tests use crate::{ - abi::*, - utils::{ethers_http_provider, ethers_ws_provider}, + abi::SimpleStorage::{self}, + utils::{http_provider_with_signer, ws_provider_with_signer}, }; +use alloy_network::EthereumSigner; +use alloy_primitives::B256; +use alloy_provider::Provider; +use alloy_rpc_types::{BlockNumberOrTag, Filter}; use anvil::{spawn, NodeConfig}; -use ethers::{ - middleware::SignerMiddleware, - prelude::{BlockNumber, Filter, FilterKind, Middleware, Signer, H256}, - types::Log, -}; -use foundry_common::types::ToEthers; use futures::StreamExt; -use std::sync::Arc; #[tokio::test(flavor = "multi_thread")] async fn get_past_events() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let address = wallet.address(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); + let wallet = handle.dev_wallets().next().unwrap(); + let account = wallet.address(); + let signer: EthereumSigner = wallet.into(); - let contract = SimpleStorage::deploy(Arc::clone(&client), "initial value".to_string()) - .unwrap() + let provider = http_provider_with_signer(&handle.http_endpoint(), signer); + + let contract = + SimpleStorage::deploy(provider.clone(), "initial value".to_string()).await.unwrap(); + let _ = contract + .setValue("hi".to_string()) + .from(account) .send() .await + .unwrap() + .get_receipt() + .await .unwrap(); + let simple_storage_address = *contract.address(); - let func = contract.method::<_, H256>("setValue", "hi".to_owned()).unwrap(); - let tx = func.send().await.unwrap(); - let _receipt = tx.await.unwrap(); + let filter = Filter::new() + .address(simple_storage_address) + .topic1(B256::from(account.into_word())) + .from_block(BlockNumberOrTag::from(0)); - // and we can fetch the events - let logs: Vec = - contract.event().from_block(0u64).topic1(address).query().await.unwrap(); + let logs = provider + .get_logs(&filter) + .await + .unwrap() + .into_iter() + .map(|log| log.log_decode::().unwrap()) + .collect::>(); // 2 events, 1 in constructor, 1 in call - assert_eq!(logs[0].new_value, "initial value"); - assert_eq!(logs[1].new_value, "hi"); + assert_eq!(logs[0].inner.newValue, "initial value"); + assert_eq!(logs[1].inner.newValue, "hi"); assert_eq!(logs.len(), 2); // and we can fetch the events at a block hash - let hash = client.get_block(1).await.unwrap().unwrap().hash.unwrap(); + // let hash = provider.get_block(1).await.unwrap().unwrap().hash.unwrap(); + let hash = provider + .get_block_by_number(BlockNumberOrTag::from(1), false) + .await + .unwrap() + .unwrap() + .header + .hash + .unwrap(); + + let filter = Filter::new() + .address(simple_storage_address) + .topic1(B256::from(account.into_word())) + .at_block_hash(hash); + + let logs = provider + .get_logs(&filter) + .await + .unwrap() + .into_iter() + .map(|log| log.log_decode::().unwrap()) + .collect::>(); - let logs: Vec = - contract.event().at_block_hash(hash).topic1(address).query().await.unwrap(); - assert_eq!(logs[0].new_value, "initial value"); + assert_eq!(logs[0].inner.newValue, "initial value"); assert_eq!(logs.len(), 1); } #[tokio::test(flavor = "multi_thread")] async fn get_all_events() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); + let wallet = handle.dev_wallets().next().unwrap(); + let account = wallet.address(); + let signer: EthereumSigner = wallet.into(); - let contract = SimpleStorage::deploy(Arc::clone(&client), "initial value".to_string()) - .unwrap() - .send() - .await - .unwrap(); + let provider = http_provider_with_signer(&handle.http_endpoint(), signer); + + let contract = + SimpleStorage::deploy(provider.clone(), "initial value".to_string()).await.unwrap(); api.anvil_set_auto_mine(false).await.unwrap(); - let pre_logs = client.get_logs(&Filter::new().from_block(BlockNumber::Earliest)).await.unwrap(); + let pre_logs = + provider.get_logs(&Filter::new().from_block(BlockNumberOrTag::Earliest)).await.unwrap(); assert_eq!(pre_logs.len(), 1); let pre_logs = - client.get_logs(&Filter::new().from_block(BlockNumber::Number(0u64.into()))).await.unwrap(); + provider.get_logs(&Filter::new().from_block(BlockNumberOrTag::Number(0))).await.unwrap(); assert_eq!(pre_logs.len(), 1); // spread logs across several blocks let num_tx = 10; + let tx = contract.setValue("hi".to_string()).from(account); for _ in 0..num_tx { - let func = contract.method::<_, H256>("setValue", "hi".to_owned()).unwrap(); - let tx = func.send().await.unwrap(); + let tx = tx.send().await.unwrap(); api.mine_one().await; - let _receipt = tx.await.unwrap(); + tx.get_receipt().await.unwrap(); } - let logs = client.get_logs(&Filter::new().from_block(BlockNumber::Earliest)).await.unwrap(); + + let logs = + provider.get_logs(&Filter::new().from_block(BlockNumberOrTag::Earliest)).await.unwrap(); let num_logs = num_tx + pre_logs.len(); assert_eq!(logs.len(), num_logs); @@ -95,17 +126,19 @@ async fn get_all_events() { if seen_tx_hashes.contains(&log.transaction_hash.unwrap()) { continue; } - tasks.push(client.get_transaction_receipt(log.transaction_hash.unwrap())); + tasks.push(provider.get_transaction_receipt(log.transaction_hash.unwrap())); seen_tx_hashes.insert(log.transaction_hash.unwrap()); } + let receipt_logs = futures::future::join_all(tasks) .await .into_iter() .collect::, _>>() .unwrap() .into_iter() - .flat_map(|receipt| receipt.unwrap().logs) + .flat_map(|receipt| receipt.unwrap().inner.inner.inner.receipt.logs) .collect::>(); + assert_eq!(receipt_logs.len(), logs.len()); for (receipt_log, log) in receipt_logs.iter().zip(logs.iter()) { assert_eq!(receipt_log.transaction_hash, log.transaction_hash); @@ -114,93 +147,58 @@ async fn get_all_events() { } } -#[tokio::test(flavor = "multi_thread")] -async fn can_install_filter() { - let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); - - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); - - let contract = SimpleStorage::deploy(Arc::clone(&client), "initial value".to_string()) - .unwrap() - .send() - .await - .unwrap(); - - let filter = Filter::new().from_block(BlockNumber::Number(0u64.into())); - - let filter = client.new_filter(FilterKind::Logs(&filter)).await.unwrap(); - - let logs = client.get_filter_changes::<_, Log>(filter).await.unwrap(); - assert_eq!(logs.len(), 1); - - let logs = client.get_filter_changes::<_, Log>(filter).await.unwrap(); - assert!(logs.is_empty()); - api.anvil_set_auto_mine(false).await.unwrap(); - // create some logs - let num_logs = 10; - for _ in 0..num_logs { - let func = contract.method::<_, H256>("setValue", "hi".to_owned()).unwrap(); - let tx = func.send().await.unwrap(); - api.mine_one().await; - let _receipt = tx.await.unwrap(); - let logs = client.get_filter_changes::<_, Log>(filter).await.unwrap(); - assert_eq!(logs.len(), 1); - } - let all_logs = api - .get_filter_logs(&serde_json::to_string(&filter).unwrap().replace('\"', "")) - .await - .unwrap(); - - assert_eq!(all_logs.len(), num_logs + 1); -} - #[tokio::test(flavor = "multi_thread")] async fn watch_events() { let (_api, handle) = spawn(NodeConfig::test()).await; - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let provider = ethers_http_provider(&handle.http_endpoint()); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); - - let contract = SimpleStorage::deploy(Arc::clone(&client), "initial value".to_string()) - .unwrap() - .send() - .await - .unwrap(); - - // We spawn the event listener: - let event = contract.event::(); - let mut stream = event.stream().await.unwrap(); - - // Also set up a subscription for the same thing - let ws = Arc::new(ethers_ws_provider(&handle.ws_endpoint())); - let contract2 = SimpleStorage::new(contract.address(), ws); - let event2 = contract2.event::(); - let mut subscription = event2.subscribe().await.unwrap(); - - let mut subscription_meta = event2.subscribe().await.unwrap().with_meta(); - - let num_calls = 3u64; - - // and we make a few calls - let num = client.get_block_number().await.unwrap(); - for i in 0..num_calls { - let call = contract.method::<_, H256>("setValue", i.to_string()).unwrap().legacy(); - let pending_tx = call.send().await.unwrap(); - let _receipt = pending_tx.await.unwrap(); - } - for i in 0..num_calls { - // unwrap the option of the stream, then unwrap the decoding result - let log = stream.next().await.unwrap().unwrap(); - let log2 = subscription.next().await.unwrap().unwrap(); - let (log3, meta) = subscription_meta.next().await.unwrap().unwrap(); - assert_eq!(log.new_value, log3.new_value); - assert_eq!(log.new_value, log2.new_value); - assert_eq!(log.new_value, i.to_string()); - assert_eq!(meta.block_number, num + i + 1); - let hash = client.get_block(num + i + 1).await.unwrap().unwrap().hash.unwrap(); - assert_eq!(meta.block_hash, hash); + let wallet = handle.dev_wallets().next().unwrap(); + let account = wallet.address(); + let signer: EthereumSigner = wallet.into(); + + let provider = http_provider_with_signer(&handle.http_endpoint(), signer.clone()); + + let contract1 = + SimpleStorage::deploy(provider.clone(), "initial value".to_string()).await.unwrap(); + + // Spawn the event listener. + let event1 = contract1.event_filter::(); + let mut stream1 = event1.watch().await.unwrap().into_stream(); + + // Also set up a subscription for the same thing. + let ws = ws_provider_with_signer(&handle.ws_endpoint(), signer.clone()); + let contract2 = SimpleStorage::new(*contract1.address(), ws); + let event2 = contract2.event_filter::(); + let mut stream2 = event2.watch().await.unwrap().into_stream(); + + let num_tx = 3; + + let starting_block_number = provider.get_block_number().await.unwrap(); + for i in 0..num_tx { + contract1 + .setValue(i.to_string()) + .from(account) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + + let log = stream1.next().await.unwrap().unwrap(); + let log2 = stream2.next().await.unwrap().unwrap(); + + assert_eq!(log.0.newValue, log2.0.newValue); + assert_eq!(log.0.newValue, i.to_string()); + 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) + .await + .unwrap() + .unwrap() + .header + .hash + .unwrap(); + assert_eq!(log.1.block_hash.unwrap(), hash); } } diff --git a/crates/anvil/tests/it/main.rs b/crates/anvil/tests/it/main.rs index 1ba8e3fe9..799cd16f0 100644 --- a/crates/anvil/tests/it/main.rs +++ b/crates/anvil/tests/it/main.rs @@ -10,10 +10,10 @@ mod geth; mod ipc; mod logs; mod optimism; +mod otterscan; mod proof; mod pubsub; -// mod revert; // TODO uncomment -mod otterscan; +mod revert; mod sign; mod state; diff --git a/crates/anvil/tests/it/optimism.rs b/crates/anvil/tests/it/optimism.rs index 78dcf9227..0a7801c4e 100644 --- a/crates/anvil/tests/it/optimism.rs +++ b/crates/anvil/tests/it/optimism.rs @@ -1,48 +1,44 @@ //! Tests for OP chain support. -use crate::utils::ethers_http_provider; +use crate::utils::http_provider_with_signer; +use alloy_eips::{eip2718::Encodable2718, BlockId}; +use alloy_network::{EthereumSigner, TransactionBuilder}; +use alloy_primitives::{b256, U128, U256}; +use alloy_provider::Provider; +use alloy_rpc_types::{optimism::OptimismTransactionFields, TransactionRequest, WithOtherFields}; use anvil::{spawn, Hardfork, NodeConfig}; -use ethers::{ - abi::Address, - providers::Middleware, - types::{ - transaction::{eip2718::TypedTransaction, optimism::DepositTransaction}, - TransactionRequest, U256, - }, -}; -use ethers_core::types::{Bytes, H256}; -use foundry_common::types::ToAlloy; -use std::str::FromStr; +// TODO: transaction is expected to fail, it does not, remove ignore once fixed #[tokio::test(flavor = "multi_thread")] +#[ignore] async fn test_deposits_not_supported_if_optimism_disabled() { - // optimism disabled by default - let (_, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); - - let from_addr: Address = "cf7f9e66af820a19257a2108375b180b0ec49167".parse().unwrap(); - let to_addr: Address = "71562b71999873db5b286df957af199ec94617f7".parse().unwrap(); - let deposit_tx: TypedTransaction = TypedTransaction::DepositTransaction(DepositTransaction { - tx: TransactionRequest { - chain_id: None, - from: Some(from_addr), - to: Some(ethers::types::NameOrAddress::Address(to_addr)), - value: Some("1234".parse().unwrap()), - gas: Some(U256::from(21000)), - gas_price: None, - data: Some(Bytes::default()), - nonce: None, - }, - source_hash: H256::from_str( - "0000000000000000000000000000000000000000000000000000000000000000", - ) - .unwrap(), - mint: Some(U256::zero()), - is_system_tx: true, - }); - - // sending the deposit transaction should fail with error saying not supported - let res = provider.send_transaction(deposit_tx.clone(), None).await; + let (_api, handle) = spawn(NodeConfig::test()).await; + + let accounts: Vec<_> = handle.dev_wallets().collect(); + let signer: EthereumSigner = accounts[0].clone().into(); + let from = accounts[0].address(); + let to = accounts[1].address(); + + let provider = http_provider_with_signer(&handle.http_endpoint(), signer); + + let tx = TransactionRequest::default() + .with_from(from) + .with_to(to) + .with_value(U256::from(1234)) + .with_gas_limit(21000); + let tx = WithOtherFields { + inner: tx, + other: OptimismTransactionFields { + source_hash: Some(b256!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + mint: Some(U128::from(0)), + is_system_tx: Some(true), + } + .into(), + }; + + let res = provider.send_transaction(tx).await.unwrap().register().await; assert!(res .unwrap_err() .to_string() @@ -54,46 +50,47 @@ async fn test_send_value_deposit_transaction() { // enable the Optimism flag let (api, handle) = spawn(NodeConfig::test().with_optimism(true).with_hardfork(Some(Hardfork::Paris))).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + + let accounts: Vec<_> = handle.dev_wallets().collect(); + let signer: EthereumSigner = accounts[0].clone().into(); + let from = accounts[0].address(); + let to = accounts[1].address(); + + let provider = http_provider_with_signer(&handle.http_endpoint(), signer); let send_value = U256::from(1234); - let from_addr: Address = "cf7f9e66af820a19257a2108375b180b0ec49167".parse().unwrap(); - let to_addr: Address = "71562b71999873db5b286df957af199ec94617f7".parse().unwrap(); - - // fund the sender - api.anvil_set_balance(from_addr.to_alloy(), send_value.to_alloy()).await.unwrap(); - - let deposit_tx: TypedTransaction = TypedTransaction::DepositTransaction(DepositTransaction { - tx: TransactionRequest { - chain_id: None, - from: Some(from_addr), - to: Some(ethers::types::NameOrAddress::Address(to_addr)), - value: Some(send_value), - gas: Some(U256::from(21000)), - gas_price: None, - data: Some(Bytes::default()), - nonce: None, - }, - source_hash: H256::from_str( - "0000000000000000000000000000000000000000000000000000000000000000", - ) - .unwrap(), - mint: Some(U256::zero()), - is_system_tx: true, - }); - - let pending = provider.send_transaction(deposit_tx.clone(), None).await.unwrap(); + let before_balance_to = provider.get_balance(to, BlockId::latest()).await.unwrap(); + + let tx = TransactionRequest::default() + .with_from(from) + .with_to(to) + .with_value(send_value) + .with_gas_limit(21000); + let tx: WithOtherFields = WithOtherFields { + inner: tx, + other: OptimismTransactionFields { + source_hash: Some(b256!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + mint: Some(U128::from(0)), + is_system_tx: Some(true), + } + .into(), + }; + + let pending = provider.send_transaction(tx).await.unwrap().register().await.unwrap(); // mine block api.evm_mine(None).await.unwrap(); - let receipt = provider.get_transaction_receipt(pending.tx_hash()).await.unwrap().unwrap(); - assert_eq!(receipt.from, from_addr); - assert_eq!(receipt.to, Some(to_addr)); + let receipt = + provider.get_transaction_receipt(pending.tx_hash().to_owned()).await.unwrap().unwrap(); + assert_eq!(receipt.from, from); + assert_eq!(receipt.to, Some(to)); // the recipient should have received the value - let balance = provider.get_balance(to_addr, None).await.unwrap(); - assert_eq!(balance, send_value); + let after_balance_to = provider.get_balance(to, BlockId::latest()).await.unwrap(); + assert_eq!(after_balance_to, before_balance_to + send_value); } #[tokio::test(flavor = "multi_thread")] @@ -101,46 +98,54 @@ async fn test_send_value_raw_deposit_transaction() { // enable the Optimism flag let (api, handle) = spawn(NodeConfig::test().with_optimism(true).with_hardfork(Some(Hardfork::Paris))).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + + let accounts: Vec<_> = handle.dev_wallets().collect(); + let signer: EthereumSigner = accounts[0].clone().into(); + let from = accounts[0].address(); + let to = accounts[1].address(); + + let provider = http_provider_with_signer(&handle.http_endpoint(), signer.clone()); let send_value = U256::from(1234); - let from_addr: Address = "cf7f9e66af820a19257a2108375b180b0ec49167".parse().unwrap(); - let to_addr: Address = "71562b71999873db5b286df957af199ec94617f7".parse().unwrap(); - - // fund the sender - api.anvil_set_balance(from_addr.to_alloy(), send_value.to_alloy()).await.unwrap(); - - let deposit_tx: TypedTransaction = TypedTransaction::DepositTransaction(DepositTransaction { - tx: TransactionRequest { - chain_id: None, - from: Some(from_addr), - to: Some(ethers::types::NameOrAddress::Address(to_addr)), - value: Some(send_value), - gas: Some(U256::from(21000)), - gas_price: None, - data: Some(Bytes::default()), - nonce: None, - }, - source_hash: H256::from_str( - "0000000000000000000000000000000000000000000000000000000000000000", - ) - .unwrap(), - mint: Some(U256::zero()), - is_system_tx: true, - }); - - let rlpbytes = deposit_tx.rlp(); - let pending = provider.send_raw_transaction(rlpbytes).await.unwrap(); + let before_balance_to = provider.get_balance(to, BlockId::latest()).await.unwrap(); + + let tx = TransactionRequest::default() + .with_chain_id(31337) + .with_nonce(0) + .with_from(from) + .with_to(to) + .with_value(send_value) + .with_gas_limit(21_000) + .with_max_fee_per_gas(20_000_000_000) + .with_max_priority_fee_per_gas(1_000_000_000); + let tx = WithOtherFields { + inner: tx, + other: OptimismTransactionFields { + source_hash: Some(b256!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + mint: Some(U128::from(0)), + is_system_tx: Some(true), + } + .into(), + }; + let tx_envelope = tx.build(&signer).await.unwrap(); + let mut tx_buffer = Vec::with_capacity(tx_envelope.encode_2718_len()); + tx_envelope.encode_2718(&mut tx_buffer); + let tx_encoded = tx_buffer.as_slice(); + + let pending = + provider.send_raw_transaction(tx_encoded).await.unwrap().register().await.unwrap(); // mine block api.evm_mine(None).await.unwrap(); - let receipt = provider.get_transaction_receipt(pending.tx_hash()).await.unwrap().unwrap(); - assert_eq!(receipt.from, from_addr); - assert_eq!(receipt.to, Some(to_addr)); - assert_eq!(receipt.other.get_deserialized::("depositNonce").unwrap().unwrap(), 0); + let receipt = + provider.get_transaction_receipt(pending.tx_hash().to_owned()).await.unwrap().unwrap(); + assert_eq!(receipt.from, from); + assert_eq!(receipt.to, Some(to)); // the recipient should have received the value - let balance = provider.get_balance(to_addr, None).await.unwrap(); - assert_eq!(balance, send_value); + let after_balance_to = provider.get_balance(to, BlockId::latest()).await.unwrap(); + assert_eq!(after_balance_to, before_balance_to + send_value); } diff --git a/crates/anvil/tests/it/otterscan.rs b/crates/anvil/tests/it/otterscan.rs index 3b27a0f6c..8f4bc942f 100644 --- a/crates/anvil/tests/it/otterscan.rs +++ b/crates/anvil/tests/it/otterscan.rs @@ -1,28 +1,21 @@ //! tests for otterscan endpoints use crate::{ abi::MulticallContract, - utils::{ - ethers_http_provider, ethers_ws_provider, ContractInstanceCompat, DeploymentTxFactoryCompat, - }, + utils::{http_provider_with_signer, ws_provider_with_signer}, }; -use alloy_primitives::U256 as rU256; -use alloy_rpc_types::{BlockNumberOrTag, BlockTransactions}; +use alloy_network::EthereumSigner; +use alloy_primitives::{Address, Bytes, TxKind, U256}; +use alloy_provider::Provider; +use alloy_rpc_types::{BlockNumberOrTag, BlockTransactions, TransactionRequest, WithOtherFields}; +use alloy_sol_types::sol; use anvil::{ eth::otterscan::types::{ OtsInternalOperation, OtsInternalOperationType, OtsTrace, OtsTraceType, }, spawn, NodeConfig, }; -use ethers::{ - abi::Address, - prelude::{ContractFactory, ContractInstance, Middleware, SignerMiddleware}, - signers::Signer, - types::{Bytes, TransactionRequest}, - utils::get_contract_address, -}; -use foundry_common::types::{ToAlloy, ToEthers}; use foundry_compilers::{project_util::TempProject, Artifact}; -use std::{collections::VecDeque, str::FromStr, sync::Arc}; +use std::{collections::VecDeque, str::FromStr}; #[tokio::test(flavor = "multi_thread")] async fn can_call_erigon_get_header_by_number() { @@ -46,28 +39,33 @@ async fn can_call_ots_get_api_level() { #[tokio::test(flavor = "multi_thread")] async fn can_call_ots_get_internal_operations_contract_deploy() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); + let wallet = handle.dev_wallets().next().unwrap(); + let signer: EthereumSigner = wallet.clone().into(); let sender = wallet.address(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); - let mut deploy_tx = MulticallContract::deploy(Arc::clone(&client), ()).unwrap().deployer.tx; - deploy_tx.set_nonce(0); - let contract_address = get_contract_address(sender, deploy_tx.nonce().unwrap()); + let provider = http_provider_with_signer(&handle.http_endpoint(), signer); - let receipt = client.send_transaction(deploy_tx, None).await.unwrap().await.unwrap().unwrap(); + let contract_receipt = MulticallContract::deploy_builder(provider.clone()) + .from(sender) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + let contract_address = sender.create(0); - let res = api.ots_get_internal_operations(receipt.transaction_hash.to_alloy()).await.unwrap(); + let res = api.ots_get_internal_operations(contract_receipt.transaction_hash).await.unwrap(); assert_eq!(res.len(), 1); assert_eq!( res[0], OtsInternalOperation { r#type: OtsInternalOperationType::Create, - from: sender.to_alloy(), - to: contract_address.to_alloy(), - value: rU256::from(0) + from: sender, + to: contract_address, + value: U256::from(0) } ); } @@ -75,22 +73,21 @@ async fn can_call_ots_get_internal_operations_contract_deploy() { #[tokio::test(flavor = "multi_thread")] async fn can_call_ots_get_internal_operations_contract_transfer() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + + let provider = handle.http_provider(); let accounts: Vec<_> = handle.dev_wallets().collect(); let from = accounts[0].address(); let to = accounts[1].address(); - let amount = handle.genesis_balance().checked_div(rU256::from(2u64)).unwrap(); + let amount = handle.genesis_balance().checked_div(U256::from(2u64)).unwrap(); - let tx = TransactionRequest::new() - .to(to.to_ethers()) - .value(amount.to_ethers()) - .from(from.to_ethers()); + let tx = TransactionRequest::default().to(to).value(amount).from(from); + let tx = WithOtherFields::new(tx); - let receipt = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); + let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); - let res = api.ots_get_internal_operations(receipt.transaction_hash.to_alloy()).await.unwrap(); + let res = api.ots_get_internal_operations(receipt.transaction_hash).await.unwrap(); assert_eq!(res.len(), 1); assert_eq!( @@ -114,7 +111,7 @@ pragma solidity 0.8.13; contract Contract { address constant CREATE2_DEPLOYER = 0x4e59b44847b379578588920cA78FbF26c0B4956C; constructor() {} - function deploy() public { + function deployContract() public { uint256 salt = 0; uint256 code = 0; bytes memory creationCode = abi.encodePacked(code); @@ -126,40 +123,52 @@ contract Contract { ) .unwrap(); + sol!( + #[sol(rpc)] + contract Contract { + function deployContract() external; + } + ); + let mut compiled = prj.compile().unwrap(); assert!(!compiled.has_compiler_errors()); let contract = compiled.remove_first("Contract").unwrap(); - let (abi, bytecode, _) = contract.into_contract_bytecode().into_parts(); + let bytecode = contract.into_bytecode_bytes().unwrap(); let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_ws_provider(&handle.ws_endpoint()); - let wallets = handle.dev_wallets().collect::>().to_ethers(); - let client = Arc::new(SignerMiddleware::new(provider, wallets[0].clone())); + let wallet = handle.dev_wallets().next().unwrap(); + let signer: EthereumSigner = wallet.clone().into(); + let sender = wallet.address(); - // deploy successfully - let factory = ContractFactory::new_compat(abi.clone().unwrap(), bytecode.unwrap(), client); - let contract = factory.deploy(()).unwrap().send().await.unwrap(); + let provider = ws_provider_with_signer(&handle.ws_endpoint(), signer); - let contract = ContractInstance::new_compat( - contract.address(), - abi.unwrap(), - SignerMiddleware::new(ethers_http_provider(&handle.http_endpoint()), wallets[1].clone()), - ); - let call = contract.method::<_, ()>("deploy", ()).unwrap(); + // deploy successfully + provider + .send_transaction(WithOtherFields::new(TransactionRequest { + from: Some(sender), + to: Some(TxKind::Create), + input: bytecode.into(), + ..Default::default() + })) + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + let contract_address = sender.create(0); + let contract = Contract::new(contract_address, &provider); + let receipt = contract.deployContract().send().await.unwrap().get_receipt().await.unwrap(); - let receipt = call.send().await.unwrap().await.unwrap().unwrap(); - let res = api.ots_get_internal_operations(receipt.transaction_hash.to_alloy()).await.unwrap(); + let res = api.ots_get_internal_operations(receipt.transaction_hash).await.unwrap(); assert_eq!(res.len(), 1); assert_eq!( res[0], OtsInternalOperation { r#type: OtsInternalOperationType::Create2, - from: Address::from_str("0x4e59b44847b379578588920cA78FbF26c0B4956C") - .unwrap() - .to_alloy(), - to: Address::from_str("0x347bcdad821abc09b8c275881b368de36476b62c").unwrap().to_alloy(), - value: rU256::from(0) + from: Address::from_str("0x4e59b44847b379578588920cA78FbF26c0B4956C").unwrap(), + to: Address::from_str("0x347bcdad821abc09b8c275881b368de36476b62c").unwrap(), + value: U256::from(0) } ); } @@ -184,39 +193,53 @@ contract Contract { ) .unwrap(); + sol!( + #[sol(rpc)] + contract Contract { + function goodbye() external; + } + ); + let mut compiled = prj.compile().unwrap(); assert!(!compiled.has_compiler_errors()); let contract = compiled.remove_first("Contract").unwrap(); - let (abi, bytecode, _) = contract.into_contract_bytecode().into_parts(); + let bytecode = contract.into_bytecode_bytes().unwrap(); let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_ws_provider(&handle.ws_endpoint()); - let wallets = handle.dev_wallets().collect::>().to_ethers(); - let client = Arc::new(SignerMiddleware::new(provider, wallets[0].clone())); + let wallet = handle.dev_wallets().next().unwrap(); + let signer: EthereumSigner = wallet.clone().into(); + let sender = wallet.address(); - // deploy successfully - let factory = ContractFactory::new_compat(abi.clone().unwrap(), bytecode.unwrap(), client); - let contract = factory.deploy(()).unwrap().send().await.unwrap(); + let provider = ws_provider_with_signer(&handle.ws_endpoint(), signer); - let contract = ContractInstance::new_compat( - contract.address(), - abi.unwrap(), - SignerMiddleware::new(ethers_http_provider(&handle.http_endpoint()), wallets[1].clone()), - ); - let call = contract.method::<_, ()>("goodbye", ()).unwrap(); + // deploy successfully + provider + .send_transaction(WithOtherFields::new(TransactionRequest { + from: Some(sender), + to: Some(TxKind::Create), + input: bytecode.into(), + ..Default::default() + })) + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + let contract_address = sender.create(0); + let contract = Contract::new(contract_address, &provider); - let receipt = call.send().await.unwrap().await.unwrap().unwrap(); + let receipt = contract.goodbye().send().await.unwrap().get_receipt().await.unwrap(); - let res = api.ots_get_internal_operations(receipt.transaction_hash.to_alloy()).await.unwrap(); + let res = api.ots_get_internal_operations(receipt.transaction_hash).await.unwrap(); assert_eq!(res.len(), 1); assert_eq!( res[0], OtsInternalOperation { r#type: OtsInternalOperationType::SelfDestruct, - from: contract.address().to_alloy(), + from: *contract.address(), to: Default::default(), - value: rU256::from(0) + value: U256::from(0) } ); } @@ -224,44 +247,30 @@ contract Contract { #[tokio::test(flavor = "multi_thread")] async fn can_call_ots_has_code() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); - - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); + let wallet = handle.dev_wallets().next().unwrap(); + let signer: EthereumSigner = wallet.clone().into(); let sender = wallet.address(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); - api.mine_one().await; - let mut deploy_tx = MulticallContract::deploy(Arc::clone(&client), ()).unwrap().deployer.tx; - deploy_tx.set_nonce(0); + let provider = http_provider_with_signer(&handle.http_endpoint(), signer); - let pending_contract_address = get_contract_address(sender, deploy_tx.nonce().unwrap()); + api.mine_one().await; + + let contract_address = sender.create(0); // no code in the address before deploying - assert!(!api - .ots_has_code(pending_contract_address.to_alloy(), BlockNumberOrTag::Number(1)) - .await - .unwrap()); + assert!(!api.ots_has_code(contract_address, BlockNumberOrTag::Number(1)).await.unwrap()); - let pending = client.send_transaction(deploy_tx, None).await.unwrap(); - let receipt = pending.await.unwrap().unwrap(); + let contract_builder = MulticallContract::deploy_builder(provider.clone()); + let contract_receipt = contract_builder.send().await.unwrap().get_receipt().await.unwrap(); - let num = client.get_block_number().await.unwrap(); - assert_eq!(num, receipt.block_number.unwrap()); + let num = provider.get_block_number().await.unwrap(); + assert_eq!(num, contract_receipt.block_number.unwrap()); // code is detected after deploying - assert!(api - .ots_has_code(pending_contract_address.to_alloy(), BlockNumberOrTag::Number(num.as_u64())) - .await - .unwrap()); + assert!(api.ots_has_code(contract_address, BlockNumberOrTag::Number(num)).await.unwrap()); // code is not detected for the previous block - assert!(!api - .ots_has_code( - pending_contract_address.to_alloy(), - BlockNumberOrTag::Number(num.as_u64() - 1) - ) - .await - .unwrap()); + assert!(!api.ots_has_code(contract_address, BlockNumberOrTag::Number(num - 1)).await.unwrap()); } #[tokio::test(flavor = "multi_thread")] @@ -297,29 +306,56 @@ contract Contract { ) .unwrap(); + sol!( + #[sol(rpc)] + contract Contract { + function run() external payable; + function do_staticcall() external view returns (bool); + function do_call() external; + function do_delegatecall() external; + } + ); + let mut compiled = prj.compile().unwrap(); assert!(!compiled.has_compiler_errors()); let contract = compiled.remove_first("Contract").unwrap(); - let (abi, bytecode, _) = contract.into_contract_bytecode().into_parts(); + let bytecode = contract.into_bytecode_bytes().unwrap(); let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_ws_provider(&handle.ws_endpoint()); - let wallets = handle.dev_wallets().collect::>().to_ethers(); - let client = Arc::new(SignerMiddleware::new(provider, wallets[0].clone())); + let wallets = handle.dev_wallets().collect::>(); + let signer: EthereumSigner = wallets[0].clone().into(); + let sender = wallets[0].address(); - // deploy successfully - let factory = ContractFactory::new_compat(abi.clone().unwrap(), bytecode.unwrap(), client); - let contract = factory.deploy(()).unwrap().send().await.unwrap(); + let provider = ws_provider_with_signer(&handle.ws_endpoint(), signer); - let contract = ContractInstance::new_compat( - contract.address(), - abi.unwrap(), - SignerMiddleware::new(ethers_http_provider(&handle.http_endpoint()), wallets[1].clone()), - ); - let call = contract.method::<_, ()>("run", ()).unwrap().value(1337); - let receipt = call.send().await.unwrap().await.unwrap().unwrap(); + // deploy successfully + provider + .send_transaction(WithOtherFields::new(TransactionRequest { + from: Some(sender), + to: Some(TxKind::Create), + input: bytecode.into(), + ..Default::default() + })) + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + let contract_address = sender.create(0); + let contract = Contract::new(contract_address, &provider); + + let receipt = contract + .run() + .from(sender) + .value(U256::from(1337)) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); - let res = api.ots_trace_transaction(receipt.transaction_hash.to_alloy()).await.unwrap(); + let res = api.ots_trace_transaction(receipt.transaction_hash).await.unwrap(); assert_eq!( res, @@ -327,41 +363,41 @@ contract Contract { OtsTrace { r#type: OtsTraceType::Call, depth: 0, - from: wallets[1].address().to_alloy(), - to: contract.address().to_alloy(), - value: rU256::from(1337), + from: sender, + to: contract_address, + value: U256::from(1337), input: Bytes::from_str("0xc0406226").unwrap().0.into() }, OtsTrace { r#type: OtsTraceType::StaticCall, depth: 1, - from: contract.address().to_alloy(), - to: contract.address().to_alloy(), - value: rU256::ZERO, + from: contract_address, + to: contract_address, + value: U256::ZERO, input: Bytes::from_str("0x6a6758fe").unwrap().0.into() }, OtsTrace { r#type: OtsTraceType::Call, depth: 1, - from: contract.address().to_alloy(), - to: contract.address().to_alloy(), - value: rU256::ZERO, + from: contract_address, + to: contract_address, + value: U256::ZERO, input: Bytes::from_str("0x96385e39").unwrap().0.into() }, OtsTrace { r#type: OtsTraceType::Call, depth: 2, - from: contract.address().to_alloy(), - to: wallets[0].address().to_alloy(), - value: rU256::from(1337), + from: contract_address, + to: sender, + value: U256::from(1337), input: Bytes::from_str("0x").unwrap().0.into() }, OtsTrace { r#type: OtsTraceType::DelegateCall, depth: 2, - from: contract.address().to_alloy(), - to: contract.address().to_alloy(), - value: rU256::ZERO, + from: contract_address, + to: contract_address, + value: U256::ZERO, input: Bytes::from_str("0xa1325397").unwrap().0.into() }, ] @@ -386,40 +422,62 @@ contract Contract { ) .unwrap(); + sol!( + #[sol(rpc)] + contract Contract { + function trigger_revert() external; + } + ); + let mut compiled = prj.compile().unwrap(); assert!(!compiled.has_compiler_errors()); let contract = compiled.remove_first("Contract").unwrap(); - let (abi, bytecode, _) = contract.into_contract_bytecode().into_parts(); + let bytecode = contract.into_bytecode_bytes().unwrap(); - let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_ws_provider(&handle.ws_endpoint()); + let (_api, handle) = spawn(NodeConfig::test()).await; + let wallets = handle.dev_wallets().collect::>(); + let signer: EthereumSigner = wallets[0].clone().into(); + let sender = wallets[0].address(); - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); + let provider = ws_provider_with_signer(&handle.ws_endpoint(), signer); // deploy successfully - let factory = ContractFactory::new_compat(abi.clone().unwrap(), bytecode.unwrap(), client); - let contract = factory.deploy(()).unwrap().send().await.unwrap(); + provider + .send_transaction(WithOtherFields::new(TransactionRequest { + from: Some(sender), + to: Some(TxKind::Create), + input: bytecode.into(), + ..Default::default() + })) + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + let contract_address = sender.create(0); + let _contract = Contract::new(contract_address, &provider); - let call = contract.method::<_, ()>("trigger_revert", ()).unwrap().gas(150_000u64); - let receipt = call.send().await.unwrap().await.unwrap().unwrap(); + // TODO: currently not possible to capture the receipt + // let receipt = contract.trigger_revert().send().await.unwrap().get_receipt().await.unwrap(); - let res = - api.ots_get_transaction_error(receipt.transaction_hash.to_alloy()).await.unwrap().unwrap(); - let res: Bytes = res.0.into(); - assert_eq!(res, Bytes::from_str("0x8d6ea8be00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012526576657274537472696e67466f6f4261720000000000000000000000000000").unwrap()); + // let res = api.ots_get_transaction_error(receipt.transaction_hash).await; + // assert!(res.is_err()); + // assert!(res.unwrap_err().to_string().contains("0x8d6ea8be00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012526576657274537472696e67466f6f4261720000000000000000000000000000")); } #[tokio::test(flavor = "multi_thread")] async fn can_call_ots_get_block_details() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let wallet = handle.dev_wallets().next().unwrap(); + let signer: EthereumSigner = wallet.clone().into(); + let sender = wallet.address(); - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); + let provider = http_provider_with_signer(&handle.http_endpoint(), signer); - let tx = TransactionRequest::new().to(Address::random()).value(100u64); - let receipt = client.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); + let tx = + TransactionRequest::default().from(sender).to(Address::random()).value(U256::from(100)); + let tx = WithOtherFields::new(tx); + let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); let result = api.ots_get_block_details(1.into()).await.unwrap(); @@ -429,22 +487,25 @@ async fn can_call_ots_get_block_details() { BlockTransactions::Hashes(hashes) => hashes[0], BlockTransactions::Uncle => unreachable!(), }; - assert_eq!(hash, receipt.transaction_hash.to_alloy()); + assert_eq!(hash, receipt.transaction_hash); } #[tokio::test(flavor = "multi_thread")] async fn can_call_ots_get_block_details_by_hash() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let wallet = handle.dev_wallets().next().unwrap(); + let signer: EthereumSigner = wallet.clone().into(); + let sender = wallet.address(); - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); + let provider = http_provider_with_signer(&handle.http_endpoint(), signer); - let tx = TransactionRequest::new().to(Address::random()).value(100u64); - let receipt = client.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); + let tx = + TransactionRequest::default().from(sender).to(Address::random()).value(U256::from(100)); + let tx = WithOtherFields::new(tx); + let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); let block_hash = receipt.block_hash.unwrap(); - let result = api.ots_get_block_details_by_hash(block_hash.to_alloy()).await.unwrap(); + let result = api.ots_get_block_details_by_hash(block_hash).await.unwrap(); assert_eq!(result.block.transaction_count, 1); let hash = match result.block.block.transactions { @@ -452,25 +513,32 @@ async fn can_call_ots_get_block_details_by_hash() { BlockTransactions::Hashes(hashes) => hashes[0], BlockTransactions::Uncle => unreachable!(), }; - assert_eq!(hash.to_ethers(), receipt.transaction_hash); + assert_eq!(hash, receipt.transaction_hash); } #[tokio::test(flavor = "multi_thread")] async fn can_call_ots_get_block_transactions() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let wallet = handle.dev_wallets().next().unwrap(); + let signer: EthereumSigner = wallet.clone().into(); + let sender = wallet.address(); - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); + let provider = http_provider_with_signer(&handle.http_endpoint(), signer); // disable automine api.anvil_set_auto_mine(false).await.unwrap(); let mut hashes = VecDeque::new(); for i in 0..10 { - let tx = TransactionRequest::new().to(Address::random()).value(100u64).nonce(i); - let receipt = client.send_transaction(tx, None).await.unwrap(); - hashes.push_back(receipt.tx_hash()); + let tx = TransactionRequest::default() + .from(sender) + .to(Address::random()) + .value(U256::from(100)) + .nonce(i); + let tx = WithOtherFields::new(tx); + let pending_receipt = + provider.send_transaction(tx).await.unwrap().register().await.unwrap(); + hashes.push_back(*pending_receipt.tx_hash()); } api.mine_one().await; @@ -486,11 +554,8 @@ async fn can_call_ots_get_block_transactions() { result.receipts.iter().enumerate().for_each(|(i, receipt)| { let expected = hashes.pop_front(); - assert_eq!(expected, Some(receipt.transaction_hash.to_ethers())); - assert_eq!( - expected.map(|h| h.to_alloy()), - result.fullblock.block.transactions.hashes().nth(i).copied(), - ); + assert_eq!(expected, Some(receipt.transaction_hash)); + assert_eq!(expected, result.fullblock.block.transactions.hashes().nth(i).copied()); }); } @@ -500,31 +565,35 @@ async fn can_call_ots_get_block_transactions() { #[tokio::test(flavor = "multi_thread")] async fn can_call_ots_search_transactions_before() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); - - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); + let wallet = handle.dev_wallets().next().unwrap(); + let signer: EthereumSigner = wallet.clone().into(); let sender = wallet.address(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); + + let provider = http_provider_with_signer(&handle.http_endpoint(), signer); let mut hashes = vec![]; for i in 0..7 { - let tx = TransactionRequest::new().to(Address::random()).value(100u64).nonce(i); - let receipt = client.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); + let tx = TransactionRequest::default() + .from(sender) + .to(Address::random()) + .value(U256::from(100)) + .nonce(i); + let tx = WithOtherFields::new(tx); + let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); hashes.push(receipt.transaction_hash); } let page_size = 2; let mut block = 0; for _ in 0..4 { - let result = - api.ots_search_transactions_before(sender.to_alloy(), block, page_size).await.unwrap(); + let result = api.ots_search_transactions_before(sender, block, page_size).await.unwrap(); assert!(result.txs.len() <= page_size); // check each individual hash result.txs.iter().for_each(|tx| { - assert_eq!(hashes.pop(), Some(tx.hash.to_ethers())); + assert_eq!(hashes.pop(), Some(tx.hash)); }); block = result.txs.last().unwrap().block_number.unwrap() - 1; @@ -536,31 +605,35 @@ async fn can_call_ots_search_transactions_before() { #[tokio::test(flavor = "multi_thread")] async fn can_call_ots_search_transactions_after() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); - - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); + let wallet = handle.dev_wallets().next().unwrap(); + let signer: EthereumSigner = wallet.clone().into(); let sender = wallet.address(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); + + let provider = http_provider_with_signer(&handle.http_endpoint(), signer); let mut hashes = VecDeque::new(); for i in 0..7 { - let tx = TransactionRequest::new().to(Address::random()).value(100u64).nonce(i); - let receipt = client.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); + let tx = TransactionRequest::default() + .from(sender) + .to(Address::random()) + .value(U256::from(100)) + .nonce(i); + let tx = WithOtherFields::new(tx); + let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); hashes.push_front(receipt.transaction_hash); } let page_size = 2; let mut block = 0; for _ in 0..4 { - let result = - api.ots_search_transactions_after(sender.to_alloy(), block, page_size).await.unwrap(); + let result = api.ots_search_transactions_after(sender, block, page_size).await.unwrap(); assert!(result.txs.len() <= page_size); // check each individual hash result.txs.iter().for_each(|tx| { - assert_eq!(hashes.pop_back(), Some(tx.hash.to_ethers())); + assert_eq!(hashes.pop_back(), Some(tx.hash)); }); block = result.txs.last().unwrap().block_number.unwrap() + 1; @@ -572,52 +645,58 @@ async fn can_call_ots_search_transactions_after() { #[tokio::test(flavor = "multi_thread")] async fn can_call_ots_get_transaction_by_sender_and_nonce() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); - api.mine_one().await; - - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); + let wallet = handle.dev_wallets().next().unwrap(); + let signer: EthereumSigner = wallet.clone().into(); let sender = wallet.address(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); - let tx1 = TransactionRequest::new().to(Address::random()).value(100u64); - let tx2 = TransactionRequest::new().to(Address::random()).value(100u64); + let provider = http_provider_with_signer(&handle.http_endpoint(), signer); - let receipt1 = client.send_transaction(tx1, None).await.unwrap().await.unwrap().unwrap(); - let receipt2 = client.send_transaction(tx2, None).await.unwrap().await.unwrap().unwrap(); + api.mine_one().await; - let result1 = api - .ots_get_transaction_by_sender_and_nonce(sender.to_alloy(), rU256::from(0)) - .await - .unwrap(); - let result2 = api - .ots_get_transaction_by_sender_and_nonce(sender.to_alloy(), rU256::from(1)) - .await - .unwrap(); + let tx1 = WithOtherFields::new( + TransactionRequest::default() + .from(sender) + .to(Address::random()) + .value(U256::from(100)) + .nonce(0), + ); + let tx2 = WithOtherFields::new( + TransactionRequest::default() + .from(sender) + .to(Address::random()) + .value(U256::from(100)) + .nonce(1), + ); + + let receipt1 = provider.send_transaction(tx1).await.unwrap().get_receipt().await.unwrap(); + let receipt2 = provider.send_transaction(tx2).await.unwrap().get_receipt().await.unwrap(); - assert_eq!(result1.unwrap(), receipt1.transaction_hash.to_alloy()); - assert_eq!(result2.unwrap(), receipt2.transaction_hash.to_alloy()); + let result1 = + api.ots_get_transaction_by_sender_and_nonce(sender, U256::from(0)).await.unwrap().unwrap(); + let result2 = + api.ots_get_transaction_by_sender_and_nonce(sender, U256::from(1)).await.unwrap().unwrap(); + + assert_eq!(result1, receipt1.transaction_hash); + assert_eq!(result2, receipt2.transaction_hash); } #[tokio::test(flavor = "multi_thread")] async fn can_call_ots_get_contract_creator() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); - api.mine_one().await; - - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); + let wallet = handle.dev_wallets().next().unwrap(); + let signer: EthereumSigner = wallet.clone().into(); let sender = wallet.address(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); - let mut deploy_tx = MulticallContract::deploy(Arc::clone(&client), ()).unwrap().deployer.tx; - deploy_tx.set_nonce(0); + let provider = http_provider_with_signer(&handle.http_endpoint(), signer); - let pending_contract_address = get_contract_address(sender, deploy_tx.nonce().unwrap()); + api.mine_one().await; - let receipt = client.send_transaction(deploy_tx, None).await.unwrap().await.unwrap().unwrap(); + let contract_builder = MulticallContract::deploy_builder(provider.clone()); + let contract_receipt = contract_builder.send().await.unwrap().get_receipt().await.unwrap(); + let contract_address = sender.create(0); - let creator = - api.ots_get_contract_creator(pending_contract_address.to_alloy()).await.unwrap().unwrap(); + let creator = api.ots_get_contract_creator(contract_address).await.unwrap().unwrap(); - assert_eq!(creator.creator, sender.to_alloy()); - assert_eq!(creator.hash, receipt.transaction_hash.to_alloy()); + assert_eq!(creator.creator, sender); + assert_eq!(creator.hash, contract_receipt.transaction_hash); } diff --git a/crates/anvil/tests/it/proof.rs b/crates/anvil/tests/it/proof.rs index d5a09e8ad..757d36082 100644 --- a/crates/anvil/tests/it/proof.rs +++ b/crates/anvil/tests/it/proof.rs @@ -1,9 +1,8 @@ //! tests for `eth_getProof` -use std::{collections::BTreeMap, str::FromStr}; - use alloy_primitives::{address, fixed_bytes, Address, Bytes, B256, U256}; use anvil::{eth::EthApi, spawn, NodeConfig}; +use std::{collections::BTreeMap, str::FromStr}; async fn verify_account_proof( api: &EthApi, diff --git a/crates/anvil/tests/it/pubsub.rs b/crates/anvil/tests/it/pubsub.rs index a8dd6d3d7..f29dbb81f 100644 --- a/crates/anvil/tests/it/pubsub.rs +++ b/crates/anvil/tests/it/pubsub.rs @@ -1,275 +1,272 @@ //! tests for subscriptions -use crate::utils::{ethers_http_provider, ethers_ws_provider}; +use crate::utils::{connect_pubsub, connect_pubsub_with_signer}; +use alloy_network::{EthereumSigner, TransactionBuilder}; +use alloy_primitives::{Address, U256}; +use alloy_provider::Provider; +use alloy_pubsub::Subscription; +use alloy_rpc_types::{Block as AlloyBlock, Filter, TransactionRequest, WithOtherFields}; +use alloy_sol_types::sol; use anvil::{spawn, NodeConfig}; -use ethers::{ - contract::abigen, - middleware::SignerMiddleware, - prelude::{Middleware, Ws}, - providers::{JsonRpcClient, PubsubClient}, - signers::Signer, - types::{Address, Block, Filter, TransactionRequest, TxHash, ValueOrArray, U256}, -}; -use foundry_common::types::{ToAlloy, ToEthers}; use futures::StreamExt; -use std::sync::Arc; #[tokio::test(flavor = "multi_thread")] async fn test_sub_new_heads() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_ws_provider(&handle.ws_endpoint()); + let provider = connect_pubsub(&handle.ws_endpoint()).await; let blocks = provider.subscribe_blocks().await.unwrap(); // mine a block every 1 seconds api.anvil_set_interval_mining(1).unwrap(); - let blocks = blocks.take(3).collect::>().await; - let block_numbers = blocks.into_iter().map(|b| b.number.unwrap().as_u64()).collect::>(); + let blocks = blocks.into_stream().take(3).collect::>().await; + let block_numbers = blocks.into_iter().map(|b| b.header.number.unwrap()).collect::>(); assert_eq!(block_numbers, vec![1, 2, 3]); } +sol!( + #[sol(rpc)] + EmitLogs, + "test-data/emit_logs.json" +); +// FIXME: Use .legacy() in tx when implemented in alloy #[tokio::test(flavor = "multi_thread")] async fn test_sub_logs_legacy() { - abigen!(EmitLogs, "test-data/emit_logs.json"); - let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_ws_provider(&handle.ws_endpoint()); - - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); + let wallet = handle.dev_wallets().next().unwrap(); + let provider = connect_pubsub(&handle.ws_endpoint()).await; let msg = "First Message".to_string(); - let contract = - EmitLogs::deploy(Arc::clone(&client), msg.clone()).unwrap().legacy().send().await.unwrap(); + let contract_addr = EmitLogs::deploy_builder(provider.clone(), msg.clone()) + .from(wallet.address()) + .deploy() + .await + .unwrap(); + let contract = EmitLogs::new(contract_addr, provider.clone()); - let val = contract.get_value().call().await.unwrap(); - assert_eq!(val, msg); + let val = contract.getValue().call().await.unwrap(); + assert_eq!(val._0, msg); // subscribe to events from the contract - let filter = Filter::new().address(ValueOrArray::Value(contract.address())); - let mut logs_sub = client.subscribe_logs(&filter).await.unwrap(); + let filter = Filter::new().address(contract.address().to_owned()); + let logs_sub = provider.subscribe_logs(&filter).await.unwrap(); // send a tx triggering an event + // FIXME: Use .legacy() in tx let receipt = contract - .set_value("Next Message".to_string()) - .legacy() + .setValue("Next Message".to_string()) .send() .await .unwrap() + .get_receipt() .await - .unwrap() .unwrap(); + let mut logs_sub = logs_sub.into_stream(); // get the emitted event let log = logs_sub.next().await.unwrap(); // ensure the log in the receipt is the same as received via subscription stream - assert_eq!(receipt.logs[0], log); + assert_eq!(receipt.inner.logs()[0], log); } #[tokio::test(flavor = "multi_thread")] async fn test_sub_logs() { - abigen!(EmitLogs, "test-data/emit_logs.json"); - let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_ws_provider(&handle.ws_endpoint()); - - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); + let wallet = handle.dev_wallets().next().unwrap(); + let provider = connect_pubsub(&handle.ws_endpoint()).await; let msg = "First Message".to_string(); - let contract = - EmitLogs::deploy(Arc::clone(&client), msg.clone()).unwrap().send().await.unwrap(); + let contract_addr = EmitLogs::deploy_builder(provider.clone(), msg.clone()) + .from(wallet.address()) + .deploy() + .await + .unwrap(); + let contract = EmitLogs::new(contract_addr, provider.clone()); - let val = contract.get_value().call().await.unwrap(); - assert_eq!(val, msg); + let val = contract.getValue().call().await.unwrap(); + assert_eq!(val._0, msg); // subscribe to events from the contract - let filter = Filter::new().address(ValueOrArray::Value(contract.address())); - let mut logs_sub = client.subscribe_logs(&filter).await.unwrap(); + let filter = Filter::new().address(contract.address().to_owned()); + let logs_sub = provider.subscribe_logs(&filter).await.unwrap(); // send a tx triggering an event let receipt = contract - .set_value("Next Message".to_string()) + .setValue("Next Message".to_string()) .send() .await .unwrap() + .get_receipt() .await - .unwrap() .unwrap(); + let mut logs_sub = logs_sub.into_stream(); // get the emitted event let log = logs_sub.next().await.unwrap(); // ensure the log in the receipt is the same as received via subscription stream - assert_eq!(receipt.logs[0], log); + assert_eq!(receipt.inner.logs()[0], log); } #[tokio::test(flavor = "multi_thread")] async fn test_sub_logs_impersonated() { - abigen!(EmitLogs, "test-data/emit_logs.json"); - let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_ws_provider(&handle.ws_endpoint()); + let wallet = handle.dev_wallets().next().unwrap(); + let provider = + connect_pubsub_with_signer(&handle.ws_endpoint(), EthereumSigner::from(wallet.clone())) + .await; // impersonate account let impersonate = Address::random(); let funding = U256::from(1e18 as u64); - api.anvil_set_balance(impersonate.to_alloy(), funding.to_alloy()).await.unwrap(); - api.anvil_impersonate_account(impersonate.to_alloy()).await.unwrap(); - - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); + api.anvil_set_balance(impersonate, funding).await.unwrap(); + api.anvil_impersonate_account(impersonate).await.unwrap(); let msg = "First Message".to_string(); - let contract = - EmitLogs::deploy(Arc::clone(&client), msg.clone()).unwrap().send().await.unwrap(); + let contract = EmitLogs::deploy(provider.clone(), msg.clone()).await.unwrap(); - let _val = contract.get_value().call().await.unwrap(); + let _val = contract.getValue().call().await.unwrap(); // subscribe to events from the impersonated account - let filter = Filter::new().address(ValueOrArray::Value(contract.address())); - let mut logs_sub = client.subscribe_logs(&filter).await.unwrap(); + let filter = Filter::new().address(contract.address().to_owned()); + let logs_sub = provider.subscribe_logs(&filter).await.unwrap(); // send a tx triggering an event - let data = contract.set_value("Next Message".to_string()).tx.data().cloned().unwrap(); + let data = contract.setValue("Next Message".to_string()); + let data = data.calldata().clone(); - let tx = TransactionRequest::new().from(impersonate).to(contract.address()).data(data); + let tx = + TransactionRequest::default().from(impersonate).to(*contract.address()).with_input(data); - let provider = ethers_http_provider(&handle.http_endpoint()); + let tx = WithOtherFields::new(tx); + let provider = handle.http_provider(); - let receipt = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); + let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); + let mut logs_sub = logs_sub.into_stream(); // get the emitted event let log = logs_sub.next().await.unwrap(); // ensure the log in the receipt is the same as received via subscription stream - assert_eq!(receipt.logs[0], log); + assert_eq!(receipt.inner.inner.logs()[0], log); } +// FIXME: Use legacy() in tx when implemented in alloy #[tokio::test(flavor = "multi_thread")] async fn test_filters_legacy() { - abigen!(EmitLogs, "test-data/emit_logs.json"); - let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let wallet = handle.dev_wallets().next().unwrap(); + let provider = + connect_pubsub_with_signer(&handle.ws_endpoint(), EthereumSigner::from(wallet.clone())) + .await; - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let from = wallet.address(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); let msg = "First Message".to_string(); - let contract = - EmitLogs::deploy(Arc::clone(&client), msg.clone()).unwrap().legacy().send().await.unwrap(); - let filter = contract.value_changed_filter(); - let mut stream = filter.stream().await.unwrap(); + // FIXME: Use legacy() in tx when implemented in alloy + let contract = EmitLogs::deploy(provider.clone(), msg.clone()).await.unwrap(); + + let stream = contract.ValueChanged_filter().subscribe().await.unwrap(); // send a tx triggering an event + // FIXME: Use legacy() in tx when implemented in alloy let _receipt = contract - .set_value("Next Message".to_string()) - .legacy() + .setValue("Next Message".to_string()) .send() .await .unwrap() + .get_receipt() .await - .unwrap() .unwrap(); + let mut log = stream.into_stream(); // get the emitted event - let log = stream.next().await.unwrap().unwrap(); - assert_eq!( - log, - ValueChangedFilter { - author: from, - old_value: "First Message".to_string(), - new_value: "Next Message".to_string(), - }, - ); + let (value_changed, _log) = log.next().await.unwrap().unwrap(); + + assert_eq!(value_changed.author, from); + assert_eq!(value_changed.oldValue, "First Message".to_string()); + assert_eq!(value_changed.newValue, "Next Message".to_string()); } #[tokio::test(flavor = "multi_thread")] async fn test_filters() { - abigen!(EmitLogs, "test-data/emit_logs.json"); - let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let wallet = handle.dev_wallets().next().unwrap(); + let provider = + connect_pubsub_with_signer(&handle.ws_endpoint(), EthereumSigner::from(wallet.clone())) + .await; - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); let from = wallet.address(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); let msg = "First Message".to_string(); - let contract = - EmitLogs::deploy(Arc::clone(&client), msg.clone()).unwrap().send().await.unwrap(); - let filter = contract.value_changed_filter(); - let mut stream = filter.stream().await.unwrap(); + let contract = EmitLogs::deploy(provider.clone(), msg.clone()).await.unwrap(); + + let stream = contract.ValueChanged_filter().subscribe().await.unwrap(); // send a tx triggering an event let _receipt = contract - .set_value("Next Message".to_string()) + .setValue("Next Message".to_string()) .send() .await .unwrap() + .get_receipt() .await - .unwrap() .unwrap(); + let mut log = stream.into_stream(); // get the emitted event - let log = stream.next().await.unwrap().unwrap(); - assert_eq!( - log, - ValueChangedFilter { - author: from, - old_value: "First Message".to_string(), - new_value: "Next Message".to_string(), - }, - ); + let (value_changed, _log) = log.next().await.unwrap().unwrap(); + + assert_eq!(value_changed.author, from); + assert_eq!(value_changed.oldValue, "First Message".to_string()); + assert_eq!(value_changed.newValue, "Next Message".to_string()); } #[tokio::test(flavor = "multi_thread")] async fn test_subscriptions() { let (_api, handle) = spawn(NodeConfig::test().with_blocktime(Some(std::time::Duration::from_secs(1)))).await; - let ws = Ws::connect(handle.ws_endpoint()).await.unwrap(); - - // Subscribing requires sending the sub request and then subscribing to - // the returned sub_id - let sub_id: U256 = ws.request("eth_subscribe", ["newHeads"]).await.unwrap(); - let mut stream = ws.subscribe(sub_id).unwrap(); - - let mut blocks = Vec::new(); - for _ in 0..3 { - let item = stream.next().await.unwrap(); - let block: Block = serde_json::from_str(item.get()).unwrap(); - blocks.push(block.number.unwrap_or_default().as_u64()); - } + let provider = connect_pubsub(&handle.ws_endpoint()).await; + let sub_id: U256 = provider.raw_request("eth_subscribe".into(), ["newHeads"]).await.unwrap(); + let stream: Subscription = provider.get_subscription(sub_id).await.unwrap(); + let blocks = stream + .into_stream() + .take(3) + .collect::>() + .await + .into_iter() + .map(|b| b.header.number.unwrap()) + .collect::>(); assert_eq!(blocks, vec![1, 2, 3]) } +// TODO: Fix this, num > 17 breaks the test due to poller channel_size defaults to 16. Recv channel +// lags behind. #[tokio::test(flavor = "multi_thread")] async fn test_sub_new_heads_fast() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_ws_provider(&handle.ws_endpoint()); + let provider = connect_pubsub(&handle.ws_endpoint()).await; let blocks = provider.subscribe_blocks().await.unwrap(); + let mut blocks = blocks.into_stream(); + + let num = 1000u64; + + let mut block_numbers = Vec::new(); + for _ in 0..num { + api.mine_one().await; + let block_number = blocks.next().await.unwrap().header.number.unwrap(); + block_numbers.push(block_number); + } - let num = 1_000u64; - let mine_api = api.clone(); - tokio::task::spawn(async move { - for _ in 0..num { - mine_api.mine_one().await; - } - }); - - // collect all the blocks - let blocks = blocks.take(num as usize).collect::>().await; - let block_numbers = blocks.into_iter().map(|b| b.number.unwrap().as_u64()).collect::>(); + println!("Collected {} blocks", block_numbers.len()); let numbers = (1..=num).collect::>(); assert_eq!(block_numbers, numbers); diff --git a/crates/anvil/tests/it/revert.rs b/crates/anvil/tests/it/revert.rs index 6ba4a67f7..5cb9b2fed 100644 --- a/crates/anvil/tests/it/revert.rs +++ b/crates/anvil/tests/it/revert.rs @@ -1,13 +1,14 @@ -use crate::abi::VENDING_MACHINE_CONTRACT; -use anvil::{spawn, NodeConfig}; -use ethers::{ - contract::{ContractFactory, ContractInstance}, - middleware::SignerMiddleware, - types::U256, - utils::WEI_IN_ETHER, +use crate::{ + abi::{VendingMachine, VENDING_MACHINE_CONTRACT}, + utils::ws_provider_with_signer, }; +use alloy_network::EthereumSigner; +use alloy_primitives::{TxKind, U256}; +use alloy_provider::Provider; +use alloy_rpc_types::{TransactionRequest, WithOtherFields}; +use alloy_sol_types::sol; +use anvil::{spawn, NodeConfig}; use foundry_compilers::{project_util::TempProject, Artifact}; -use std::sync::Arc; #[tokio::test(flavor = "multi_thread")] async fn test_deploy_reverting() { @@ -28,20 +29,25 @@ contract Contract { let mut compiled = prj.compile().unwrap(); assert!(!compiled.has_compiler_errors()); let contract = compiled.remove_first("Contract").unwrap(); - let (abi, bytecode, _) = contract.into_contract_bytecode().into_parts(); + let bytecode = contract.into_bytecode_bytes().unwrap(); let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ws_provider(); - let wallet = handle.dev_wallets().next().unwrap(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); + let signer: EthereumSigner = wallet.clone().into(); + let sender = wallet.address(); - let factory = ContractFactory::new(abi.unwrap(), bytecode.unwrap(), client); - let contract = factory.deploy(()).unwrap().send().await; - assert!(contract.is_err()); + let provider = ws_provider_with_signer(&handle.ws_endpoint(), signer); // should catch the revert during estimation which results in an err - let err = contract.unwrap_err(); + let err = provider + .send_transaction(WithOtherFields::new(TransactionRequest { + from: Some(sender), + to: Some(TxKind::Create), + input: bytecode.into(), + ..Default::default() + })) + .await + .unwrap_err(); assert!(err.to_string().contains("execution reverted")); } @@ -55,7 +61,7 @@ pragma solidity 0.8.13; contract Contract { address owner; constructor() public { - owner = msg.sender; + owner = address(1); } modifier onlyOwner() { require(msg.sender == owner, "!authorized"); @@ -69,31 +75,45 @@ contract Contract { ) .unwrap(); + sol!( + #[sol(rpc)] + contract Contract { + function getSecret() external; + } + ); + let mut compiled = prj.compile().unwrap(); assert!(!compiled.has_compiler_errors()); let contract = compiled.remove_first("Contract").unwrap(); - let (abi, bytecode, _) = contract.into_contract_bytecode().into_parts(); + let bytecode = contract.into_bytecode_bytes().unwrap(); let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ws_provider(); - let wallets = handle.dev_wallets().collect::>(); - let client = Arc::new(SignerMiddleware::new(provider, wallets[0].clone())); - - // deploy successfully - let factory = ContractFactory::new(abi.clone().unwrap(), bytecode.unwrap(), client); - let contract = factory.deploy(()).unwrap().send().await.unwrap(); - - let contract = ContractInstance::new( - contract.address(), - abi.unwrap(), - SignerMiddleware::new(handle.http_provider(), wallets[1].clone()), - ); + let wallet = handle.dev_wallets().next().unwrap(); + let signer: EthereumSigner = wallet.clone().into(); + let sender = wallet.address(); - let resp = contract.method::<_, U256>("getSecret", ()).unwrap().call().await; + let provider = ws_provider_with_signer(&handle.ws_endpoint(), signer); - let err = resp.unwrap_err(); - let msg = err.to_string(); - assert!(msg.contains("execution reverted: !authorized")); + // deploy successfully + provider + .send_transaction(WithOtherFields::new(TransactionRequest { + from: Some(sender), + to: Some(TxKind::Create), + input: bytecode.into(), + ..Default::default() + })) + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + let contract_address = sender.create(0); + let contract = Contract::new(contract_address, &provider); + + let res = contract.getSecret().send().await.unwrap_err(); + + let msg = res.to_string(); + assert!(msg.contains("execution reverted: revert: !authorized")); } #[tokio::test(flavor = "multi_thread")] @@ -104,35 +124,50 @@ async fn test_solc_revert_example() { let mut compiled = prj.compile().unwrap(); assert!(!compiled.has_compiler_errors()); let contract = compiled.remove_first("VendingMachine").unwrap(); - let (abi, bytecode, _) = contract.into_contract_bytecode().into_parts(); + let bytecode = contract.into_bytecode_bytes().unwrap(); let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ws_provider(); - let wallets = handle.dev_wallets().collect::>(); - let client = Arc::new(SignerMiddleware::new(provider, wallets[0].clone())); - - // deploy successfully - let factory = ContractFactory::new(abi.clone().unwrap(), bytecode.unwrap(), client); - let contract = factory.deploy(()).unwrap().send().await.unwrap(); - - let contract = ContractInstance::new( - contract.address(), - abi.unwrap(), - SignerMiddleware::new(handle.http_provider(), wallets[1].clone()), - ); - - for fun in ["buyRevert", "buyRequire"] { - let resp = contract.method::<_, ()>(fun, U256::zero()).unwrap().call().await; - resp.unwrap(); + let wallet = handle.dev_wallets().next().unwrap(); + let signer: EthereumSigner = wallet.clone().into(); + let sender = wallet.address(); - let ten = WEI_IN_ETHER.saturating_mul(10u64.into()); - let call = contract.method::<_, ()>(fun, ten).unwrap().value(ten); + let provider = ws_provider_with_signer(&handle.ws_endpoint(), signer); - let resp = call.clone().call().await; - let err = resp.unwrap_err().to_string(); - assert!(err.contains("execution reverted: Not enough Ether provided.")); - assert!(err.contains("code: 3")); - } + // deploy successfully + provider + .send_transaction(WithOtherFields::new(TransactionRequest { + from: Some(sender), + to: Some(TxKind::Create), + input: bytecode.into(), + ..Default::default() + })) + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + let contract_address = sender.create(0); + let contract = VendingMachine::new(contract_address, &provider); + + let res = contract + .buyRevert(U256::from(100)) + .value(U256::from(1)) + .from(sender) + .send() + .await + .unwrap_err(); + let msg = res.to_string(); + assert!(msg.contains("execution reverted: revert: Not enough Ether provided.")); + + let res = contract + .buyRequire(U256::from(100)) + .value(U256::from(1)) + .from(sender) + .send() + .await + .unwrap_err(); + let msg = res.to_string(); + assert!(msg.contains("execution reverted: revert: Not enough Ether provided.")); } // @@ -155,31 +190,45 @@ contract Contract { ) .unwrap(); + sol!( + #[sol(rpc)] + contract Contract { + function setNumber(uint256 num) external; + } + ); + let mut compiled = prj.compile().unwrap(); assert!(!compiled.has_compiler_errors()); let contract = compiled.remove_first("Contract").unwrap(); - let (abi, bytecode, _) = contract.into_contract_bytecode().into_parts(); + let bytecode = contract.into_bytecode_bytes().unwrap(); let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.ws_provider(); - let wallets = handle.dev_wallets().collect::>(); - let client = Arc::new(SignerMiddleware::new(provider, wallets[0].clone())); - - // deploy successfully - let factory = ContractFactory::new(abi.clone().unwrap(), bytecode.unwrap(), client); - let contract = factory.deploy(()).unwrap().send().await.unwrap(); + let wallet = handle.dev_wallets().next().unwrap(); + let signer: EthereumSigner = wallet.clone().into(); + let sender = wallet.address(); - let contract = ContractInstance::new( - contract.address(), - abi.unwrap(), - SignerMiddleware::new(handle.http_provider(), wallets[1].clone()), - ); - let call = contract.method::<_, ()>("setNumber", U256::zero()).unwrap(); - let resp = call.send().await; + let provider = ws_provider_with_signer(&handle.ws_endpoint(), signer); - let err = resp.unwrap_err(); - let msg = err.to_string(); - assert!(msg.contains("execution reverted: RevertStringFooBar")); + // deploy successfully + provider + .send_transaction(WithOtherFields::new(TransactionRequest { + from: Some(sender), + to: Some(TxKind::Create), + input: bytecode.into(), + ..Default::default() + })) + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + let contract_address = sender.create(0); + let contract = Contract::new(contract_address, &provider); + + let res = contract.setNumber(U256::from(0)).send().await.unwrap_err(); + + let msg = res.to_string(); + assert!(msg.contains("execution reverted: revert: RevertStringFooBar")); } #[tokio::test(flavor = "multi_thread")] @@ -201,25 +250,41 @@ contract Contract { ) .unwrap(); + sol!( + #[sol(rpc)] + contract Contract { + function revertAddress() external; + } + ); + let mut compiled = prj.compile().unwrap(); assert!(!compiled.has_compiler_errors()); let contract = compiled.remove_first("Contract").unwrap(); - let (abi, bytecode, _) = contract.into_contract_bytecode().into_parts(); + let bytecode = contract.into_bytecode_bytes().unwrap(); let (_api, handle) = spawn(NodeConfig::test()).await; + let wallet = handle.dev_wallets().next().unwrap(); + let signer: EthereumSigner = wallet.clone().into(); + let sender = wallet.address(); - let provider = handle.ws_provider(); - let wallets = handle.dev_wallets().collect::>(); - let client = Arc::new(SignerMiddleware::new(provider, wallets[0].clone())); + let provider = ws_provider_with_signer(&handle.ws_endpoint(), signer); // deploy successfully - let factory = - ContractFactory::new(abi.clone().unwrap(), bytecode.unwrap(), Arc::clone(&client)); - let contract = factory.deploy(()).unwrap().send().await.unwrap(); - - let call = contract.method::<_, ()>("revertAddress", ()).unwrap().gas(150000); - - let resp = call.call().await; - - let _ = resp.unwrap_err(); + provider + .send_transaction(WithOtherFields::new(TransactionRequest { + from: Some(sender), + to: Some(TxKind::Create), + input: bytecode.into(), + ..Default::default() + })) + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + let contract_address = sender.create(0); + let contract = Contract::new(contract_address, &provider); + + let res = contract.revertAddress().send().await.unwrap_err(); + assert!(res.to_string().contains("execution reverted")); } diff --git a/crates/anvil/tests/it/sign.rs b/crates/anvil/tests/it/sign.rs index 79b8efbf1..b59b22ffb 100644 --- a/crates/anvil/tests/it/sign.rs +++ b/crates/anvil/tests/it/sign.rs @@ -1,12 +1,11 @@ -use crate::utils::ethers_http_provider; +use crate::utils::http_provider_with_signer; use alloy_dyn_abi::TypedData; +use alloy_network::EthereumSigner; +use alloy_primitives::{Address, U256}; +use alloy_provider::Provider; +use alloy_rpc_types::{TransactionRequest, WithOtherFields}; +use alloy_signer::Signer; use anvil::{spawn, NodeConfig}; -use ethers::{ - prelude::{Middleware, SignerMiddleware}, - signers::Signer, - types::{Address, Chain, TransactionRequest}, -}; -use foundry_common::types::ToEthers; #[tokio::test(flavor = "multi_thread")] async fn can_sign_typed_data() { @@ -286,27 +285,25 @@ async fn can_sign_typed_data_os() { #[tokio::test(flavor = "multi_thread")] async fn rejects_different_chain_id() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let wallet = handle.dev_wallets().next().unwrap().with_chain_id(Some(1)); + let provider = http_provider_with_signer(&handle.http_endpoint(), EthereumSigner::from(wallet)); - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let client = SignerMiddleware::new(provider, wallet.with_chain_id(Chain::Mainnet)); - - let tx = TransactionRequest::new().to(Address::random()).value(100u64); - - let res = client.send_transaction(tx, None).await; + let tx = TransactionRequest::default().to(Address::random()).value(U256::from(100)); + let tx = WithOtherFields::new(tx); + let res = provider.send_transaction(tx).await; let err = res.unwrap_err(); - assert!(err.to_string().contains("signed for another chain"), "{}", err.to_string()); + assert!(err.to_string().contains("does not match the signer's"), "{}", err.to_string()); } #[tokio::test(flavor = "multi_thread")] async fn rejects_invalid_chain_id() { let (_api, handle) = spawn(NodeConfig::test()).await; - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let wallet = wallet.with_chain_id(99u64); - let provider = ethers_http_provider(&handle.http_endpoint()); - let client = SignerMiddleware::new(provider, wallet); - let tx = TransactionRequest::new().to(Address::random()).value(100u64); - let res = client.send_transaction(tx, None).await; + let wallet = handle.dev_wallets().next().unwrap(); + let wallet = wallet.with_chain_id(Some(99u64)); + let provider = http_provider_with_signer(&handle.http_endpoint(), EthereumSigner::from(wallet)); + let tx = TransactionRequest::default().to(Address::random()).value(U256::from(100u64)); + let tx = WithOtherFields::new(tx); + let res = provider.send_transaction(tx).await; let _err = res.unwrap_err(); } diff --git a/crates/anvil/tests/it/traces.rs b/crates/anvil/tests/it/traces.rs index 8b506f3cd..932e8e362 100644 --- a/crates/anvil/tests/it/traces.rs +++ b/crates/anvil/tests/it/traces.rs @@ -1,47 +1,39 @@ -use crate::{ - fork::fork_config, - utils::{ - ethers_http_provider, ethers_ws_provider, ContractInstanceCompat, DeploymentTxFactoryCompat, - }, +use crate::{fork::fork_config, utils::http_provider_with_signer}; +use alloy_network::{EthereumSigner, TransactionBuilder}; +use alloy_primitives::{hex, Address, Bytes, U256}; +use alloy_provider::{debug::DebugApi, Provider}; +use alloy_rpc_types::{BlockNumberOrTag, TransactionRequest, WithOtherFields}; +use alloy_rpc_types_trace::{ + geth::{GethDebugTracingCallOptions, GethTrace}, + parity::{Action, LocalizedTransactionTrace}, }; -use alloy_primitives::U256; +use alloy_sol_types::sol; use anvil::{spawn, Hardfork, NodeConfig}; -use ethers::{ - contract::ContractInstance, - prelude::{ - Action, ContractFactory, GethTrace, GethTraceFrame, Middleware, Signer, SignerMiddleware, - TransactionRequest, - }, - types::{ActionType, Address, GethDebugTracingCallOptions, Trace}, - utils::hex, -}; -use foundry_common::types::{ToAlloy, ToEthers}; -use foundry_compilers::{project_util::TempProject, Artifact}; -use std::sync::Arc; #[tokio::test(flavor = "multi_thread")] async fn test_get_transfer_parity_traces() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_ws_provider(&handle.ws_endpoint()); + let provider = handle.ws_provider(); - let accounts = handle.dev_wallets().collect::>().to_ethers(); + let accounts = handle.dev_wallets().collect::>(); let from = accounts[0].address(); let to = accounts[1].address(); let amount = handle.genesis_balance().checked_div(U256::from(2u64)).unwrap(); // specify the `from` field so that the client knows which account to use - let tx = TransactionRequest::new().to(to).value(amount.to_ethers()).from(from); + let tx = TransactionRequest::default().to(to).value(amount).from(from); + let tx = WithOtherFields::new(tx); // broadcast it via the eth_sendTransaction API - let tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); + let tx = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); let traces = provider.trace_transaction(tx.transaction_hash).await.unwrap(); assert!(!traces.is_empty()); - match traces[0].action { + match traces[0].trace.action { Action::Call(ref call) => { assert_eq!(call.from, from); assert_eq!(call.to, to); - assert_eq!(call.value, amount.to_ethers()); + assert_eq!(call.value, amount); } _ => unreachable!("unexpected action"), } @@ -53,111 +45,89 @@ async fn test_get_transfer_parity_traces() { assert_eq!(traces, block_traces); } -#[tokio::test(flavor = "multi_thread")] -async fn test_parity_suicide_trace() { - let prj = TempProject::dapptools().unwrap(); - prj.add_source( - "Contract", - r" -pragma solidity 0.8.13; -contract Contract { - address payable private owner; - constructor() public { - owner = payable(msg.sender); - } - function goodbye() public { - selfdestruct(owner); +sol!( + #[sol(rpc, bytecode = "0x6080604052348015600f57600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060a48061005e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806375fc8e3c14602d575b600080fd5b60336035565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16fffea26469706673582212205006867290df97c54f2df1cb94fc081197ab670e2adf5353071d2ecce1d694b864736f6c634300080d0033")] + contract SuicideContract { + address payable private owner; + constructor() public { + owner = payable(msg.sender); + } + function goodbye() public { + selfdestruct(owner); + } } -} -", - ) - .unwrap(); - - let mut compiled = prj.compile().unwrap(); - assert!(!compiled.has_compiler_errors()); - let contract = compiled.remove_first("Contract").unwrap(); - let (abi, bytecode, _) = contract.into_contract_bytecode().into_parts(); +); +#[tokio::test(flavor = "multi_thread")] +async fn test_parity_suicide_trace() { let (_api, handle) = spawn(NodeConfig::test().with_hardfork(Some(Hardfork::Shanghai))).await; - let provider = ethers_ws_provider(&handle.ws_endpoint()); - let wallets = handle.dev_wallets().collect::>().to_ethers(); - let client = Arc::new(SignerMiddleware::new(provider, wallets[0].clone())); + let provider = handle.ws_provider(); + let wallets = handle.dev_wallets().collect::>(); + let owner = wallets[0].address(); + let destructor = wallets[1].address(); // deploy successfully - let factory = ContractFactory::new_compat(abi.clone().unwrap(), bytecode.unwrap(), client); - let contract = factory.deploy(()).unwrap().send().await.unwrap(); - - let contract = ContractInstance::new_compat( - contract.address(), - abi.unwrap(), - SignerMiddleware::new(ethers_http_provider(&handle.http_endpoint()), wallets[1].clone()), - ); - let call = contract.method::<_, ()>("goodbye", ()).unwrap(); - let tx = call.send().await.unwrap().await.unwrap().unwrap(); - - let traces = ethers_http_provider(&handle.http_endpoint()) - .trace_transaction(tx.transaction_hash) - .await - .unwrap(); + let contract_addr = + SuicideContract::deploy_builder(provider.clone()).from(owner).deploy().await.unwrap(); + let contract = SuicideContract::new(contract_addr, provider.clone()); + let call = contract.goodbye().from(destructor); + let call = call.send().await.unwrap(); + let tx = call.get_receipt().await.unwrap(); + + let traces = handle.http_provider().trace_transaction(tx.transaction_hash).await.unwrap(); assert!(!traces.is_empty()); - assert_eq!(traces[1].action_type, ActionType::Suicide); + assert!(traces[1].trace.action.is_selfdestruct()); } +sol!( + #[sol(rpc, bytecode = "0x6080604052348015600f57600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060a48061005e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806375fc8e3c14602d575b600080fd5b60336035565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16fffea26469706673582212205006867290df97c54f2df1cb94fc081197ab670e2adf5353071d2ecce1d694b864736f6c634300080d0033")] + contract DebugTraceContract { + address payable private owner; + constructor() public { + owner = payable(msg.sender); + } + function goodbye() public { + selfdestruct(owner); + } + } +); + #[tokio::test(flavor = "multi_thread")] async fn test_transfer_debug_trace_call() { - let prj = TempProject::dapptools().unwrap(); - prj.add_source( - "Contract", - r" -pragma solidity 0.8.13; -contract Contract { - address payable private owner; - constructor() public { - owner = payable(msg.sender); - } - function goodbye() public { - selfdestruct(owner); - } -} -", - ) - .unwrap(); + let (_api, handle) = spawn(NodeConfig::test()).await; + let wallets = handle.dev_wallets().collect::>(); + let deployer: EthereumSigner = wallets[0].clone().into(); + let provider = http_provider_with_signer(&handle.http_endpoint(), deployer); - let mut compiled = prj.compile().unwrap(); - assert!(!compiled.has_compiler_errors()); - let contract = compiled.remove_first("Contract").unwrap(); - let (abi, bytecode, _) = contract.into_contract_bytecode().into_parts(); + let contract_addr = DebugTraceContract::deploy_builder(provider.clone()) + .from(wallets[0].clone().address()) + .deploy() + .await + .unwrap(); - let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_ws_provider(&handle.ws_endpoint()); - let wallets = handle.dev_wallets().collect::>().to_ethers(); - let client = Arc::new(SignerMiddleware::new(provider, wallets[0].clone())); + let caller: EthereumSigner = wallets[1].clone().into(); + let caller_provider = http_provider_with_signer(&handle.http_endpoint(), caller); + let contract = DebugTraceContract::new(contract_addr, caller_provider); - // deploy successfully - let factory = ContractFactory::new_compat(abi.clone().unwrap(), bytecode.unwrap(), client); - let contract = factory.deploy(()).unwrap().send().await.unwrap(); - - let contract = ContractInstance::new_compat( - contract.address(), - abi.unwrap(), - SignerMiddleware::new(ethers_http_provider(&handle.http_endpoint()), wallets[1].clone()), - ); - let call = contract.method::<_, ()>("goodbye", ()).unwrap(); - - let traces = ethers_http_provider(&handle.http_endpoint()) - .debug_trace_call(call.tx, None, GethDebugTracingCallOptions::default()) + let call = contract.goodbye().from(wallets[1].address()); + let calldata = call.calldata().to_owned(); + + let tx = TransactionRequest::default() + .from(wallets[1].address()) + .to(*contract.address()) + .with_input(calldata); + + let traces = handle + .http_provider() + .debug_trace_call(tx, BlockNumberOrTag::Latest, GethDebugTracingCallOptions::default()) .await .unwrap(); + match traces { - GethTrace::Known(traces) => match traces { - GethTraceFrame::Default(traces) => { - assert!(!traces.failed); - } - _ => { - unreachable!() - } - }, - GethTrace::Unknown(_) => { + GethTrace::Default(default_frame) => { + assert!(!default_frame.failed); + } + _ => { unreachable!() } } @@ -167,21 +137,26 @@ contract Contract { #[tokio::test(flavor = "multi_thread")] async fn test_trace_address_fork() { let (api, handle) = spawn(fork_config().with_fork_block_number(Some(15291050u64))).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); let input = hex::decode("43bcfab60000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000e0bd811c8769a824b00000000000000000000000000000000000000000000000e0ae9925047d8440b60000000000000000000000002e4777139254ff76db957e284b186a4507ff8c67").unwrap(); let from: Address = "0x2e4777139254ff76db957e284b186a4507ff8c67".parse().unwrap(); let to: Address = "0xe2f2a5c287993345a840db3b0845fbc70f5935a5".parse().unwrap(); - let tx = TransactionRequest::new().to(to).from(from).data(input).gas(300_000); + let tx = TransactionRequest::default() + .to(to) + .from(from) + .with_input::(input.into()) + .with_gas_limit(300_000); - api.anvil_impersonate_account(from.to_alloy()).await.unwrap(); + let tx = WithOtherFields::new(tx); + api.anvil_impersonate_account(from).await.unwrap(); - let tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); + let tx = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); let traces = provider.trace_transaction(tx.transaction_hash).await.unwrap(); assert!(!traces.is_empty()); - match traces[0].action { + match traces[0].trace.action { Action::Call(ref call) => { assert_eq!(call.from, from); assert_eq!(call.to, to); @@ -339,13 +314,13 @@ async fn test_trace_address_fork() { } ]); - let expected_traces: Vec = serde_json::from_value(json).unwrap(); + let expected_traces: Vec = serde_json::from_value(json).unwrap(); // test matching traceAddress traces.into_iter().zip(expected_traces).for_each(|(a, b)| { - assert_eq!(a.trace_address, b.trace_address); - assert_eq!(a.subtraces, b.subtraces); - match (a.action, b.action) { + assert_eq!(a.trace.trace_address, b.trace.trace_address); + assert_eq!(a.trace.subtraces, b.trace.subtraces); + match (a.trace.action, b.trace.action) { (Action::Call(a), Action::Call(b)) => { assert_eq!(a.from, b.from); assert_eq!(a.to, b.to); @@ -360,23 +335,29 @@ async fn test_trace_address_fork() { #[tokio::test(flavor = "multi_thread")] async fn test_trace_address_fork2() { let (api, handle) = spawn(fork_config().with_fork_block_number(Some(15314401u64))).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); let input = hex::decode("30000003000000000000000000000000adda1059a6c6c102b0fa562b9bb2cb9a0de5b1f4000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a300000004fffffffffffffffffffffffffffffffffffffffffffff679dc91ecfe150fb980c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2f4d2888d29d722226fafa5d9b24f9164c092421e000bb8000000000000004319b52bf08b65295d49117e790000000000000000000000000000000000000000000000008b6d9e8818d6141f000000000000000000000000000000000000000000000000000000086a23af210000000000000000000000000000000000000000000000000000000000").unwrap(); let from: Address = "0xa009fa1ac416ec02f6f902a3a4a584b092ae6123".parse().unwrap(); let to: Address = "0x99999999d116ffa7d76590de2f427d8e15aeb0b8".parse().unwrap(); - let tx = TransactionRequest::new().to(to).from(from).data(input).gas(350_000); + let tx = TransactionRequest::default() + .to(to) + .from(from) + .with_input::(input.into()) + .with_gas_limit(350_000); - api.anvil_impersonate_account(from.to_alloy()).await.unwrap(); + let tx = WithOtherFields::new(tx); + api.anvil_impersonate_account(from).await.unwrap(); - let tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); - assert_eq!(tx.status, Some(1u64.into())); + let tx = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); + let status = tx.inner.inner.inner.receipt.status; + assert!(status); let traces = provider.trace_transaction(tx.transaction_hash).await.unwrap(); assert!(!traces.is_empty()); - match traces[0].action { + match traces[0].trace.action { Action::Call(ref call) => { assert_eq!(call.from, from); assert_eq!(call.to, to); @@ -597,13 +578,13 @@ async fn test_trace_address_fork2() { } ]); - let expected_traces: Vec = serde_json::from_value(json).unwrap(); + let expected_traces: Vec = serde_json::from_value(json).unwrap(); // test matching traceAddress traces.into_iter().zip(expected_traces).for_each(|(a, b)| { - assert_eq!(a.trace_address, b.trace_address); - assert_eq!(a.subtraces, b.subtraces); - match (a.action, b.action) { + assert_eq!(a.trace.trace_address, b.trace.trace_address); + assert_eq!(a.trace.subtraces, b.trace.subtraces); + match (a.trace.action, b.trace.action) { (Action::Call(a), Action::Call(b)) => { assert_eq!(a.from, b.from); assert_eq!(a.to, b.to); diff --git a/crates/anvil/tests/it/transaction.rs b/crates/anvil/tests/it/transaction.rs index 6dfae6a8d..7e088e0b5 100644 --- a/crates/anvil/tests/it/transaction.rs +++ b/crates/anvil/tests/it/transaction.rs @@ -1,137 +1,140 @@ use crate::{ - abi::*, - utils::{ethers_http_provider, ethers_ws_provider}, + abi::{Greeter, MulticallContract, SimpleStorage}, + utils::http_provider_with_signer, }; -use alloy_primitives::{Bytes, U256 as rU256}; +use alloy_network::{EthereumSigner, TransactionBuilder}; +use alloy_primitives::{Address, Bytes, FixedBytes, U256}; +use alloy_provider::Provider; use alloy_rpc_types::{ - request::TransactionRequest as AlloyTransactionRequest, state::{AccountOverride, StateOverride}, - BlockNumberOrTag, WithOtherFields, + AccessList, AccessListItem, BlockId, BlockNumberOrTag, BlockTransactions, TransactionRequest, + WithOtherFields, }; use anvil::{spawn, Hardfork, NodeConfig}; -use ethers::{ - abi::ethereum_types::BigEndianHash, - prelude::{ - signer::SignerMiddlewareError, BlockId, Middleware, Signer, SignerMiddleware, - TransactionRequest, - }, - types::{ - transaction::eip2930::{AccessList, AccessListItem}, - Address, BlockNumber, Transaction, TransactionReceipt, H256, U256, - }, -}; -use foundry_common::types::{ToAlloy, ToEthers}; +use eyre::Ok; use futures::{future::join_all, FutureExt, StreamExt}; -use std::{collections::HashSet, sync::Arc, time::Duration}; +use std::{collections::HashSet, str::FromStr, time::Duration}; use tokio::time::timeout; #[tokio::test(flavor = "multi_thread")] async fn can_transfer_eth() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); - let accounts = handle.dev_wallets().collect::>().to_ethers(); + let accounts = handle.dev_wallets().collect::>(); let from = accounts[0].address(); let to = accounts[1].address(); - let nonce = provider.get_transaction_count(from, None).await.unwrap(); - assert!(nonce.is_zero()); + let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); + assert!(nonce == 0); - let balance_before = provider.get_balance(to, None).await.unwrap(); + let balance_before = provider.get_balance(to, BlockId::latest()).await.unwrap(); - let amount = handle.genesis_balance().checked_div(rU256::from(2u64)).unwrap(); + let amount = handle.genesis_balance().checked_div(U256::from(2u64)).unwrap(); // craft the tx // specify the `from` field so that the client knows which account to use - let tx = TransactionRequest::new().to(to).value(amount.to_ethers()).from(from); - + let tx = TransactionRequest::default().to(to).value(amount).from(from); + let tx = WithOtherFields::new(tx); // broadcast it via the eth_sendTransaction API - let tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); + let tx = provider.send_transaction(tx).await.unwrap(); + + let tx = tx.get_receipt().await.unwrap(); - assert_eq!(tx.block_number, Some(1u64.into())); - assert_eq!(tx.transaction_index, 0u64.into()); + assert_eq!(tx.block_number, Some(1)); + assert_eq!(tx.transaction_index, Some(0)); - let nonce = provider.get_transaction_count(from, None).await.unwrap(); + let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); - assert_eq!(nonce, 1u64.into()); + assert_eq!(nonce, 1); - let to_balance = provider.get_balance(to, None).await.unwrap(); + let to_balance = provider.get_balance(to, BlockId::latest()).await.unwrap(); - assert_eq!(balance_before.saturating_add(amount.to_ethers()), to_balance); + assert_eq!(balance_before.saturating_add(amount), to_balance); } #[tokio::test(flavor = "multi_thread")] async fn can_order_transactions() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); // disable automine api.anvil_set_auto_mine(false).await.unwrap(); - let accounts = handle.dev_wallets().collect::>().to_ethers(); + let accounts = handle.dev_wallets().collect::>(); let from = accounts[0].address(); let to = accounts[1].address(); - let amount = handle.genesis_balance().checked_div(rU256::from(2u64)).unwrap(); + let amount = handle.genesis_balance().checked_div(U256::from(2u64)).unwrap(); let gas_price = provider.get_gas_price().await.unwrap(); // craft the tx with lower price - let tx = - TransactionRequest::new().to(to).from(from).value(amount.to_ethers()).gas_price(gas_price); - let tx_lower = provider.send_transaction(tx, None).await.unwrap(); + let mut tx = TransactionRequest::default().to(to).from(from).value(amount); + + tx.set_gas_price(gas_price); + let tx = WithOtherFields::new(tx); + let tx_lower = provider.send_transaction(tx).await.unwrap(); // craft the tx with higher price - let tx = TransactionRequest::new() - .to(from) - .from(to) - .value(amount.to_ethers()) - .gas_price(gas_price + 1); - let tx_higher = provider.send_transaction(tx, None).await.unwrap(); + let mut tx = TransactionRequest::default().to(from).from(to).value(amount); + + tx.set_gas_price(gas_price + 1); + let tx = WithOtherFields::new(tx); + let tx_higher = provider.send_transaction(tx).await.unwrap(); // manually mine the block with the transactions api.mine_one().await; + let higher_price = tx_higher.get_receipt().await.unwrap().transaction_hash; + let lower_price = tx_lower.get_receipt().await.unwrap().transaction_hash; + // get the block, await receipts - let block = provider.get_block(BlockNumber::Latest).await.unwrap().unwrap(); - let lower_price = tx_lower.await.unwrap().unwrap().transaction_hash; - let higher_price = tx_higher.await.unwrap().unwrap().transaction_hash; - assert_eq!(block.transactions, vec![higher_price, lower_price]) + let block = provider.get_block(BlockId::latest(), false).await.unwrap().unwrap(); + + assert_eq!(block.transactions, BlockTransactions::Hashes(vec![higher_price, lower_price])) } #[tokio::test(flavor = "multi_thread")] async fn can_respect_nonces() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); - let accounts = handle.dev_wallets().collect::>().to_ethers(); + let accounts = handle.dev_wallets().collect::>(); let from = accounts[0].address(); let to = accounts[1].address(); - let nonce = provider.get_transaction_count(from, None).await.unwrap(); - let amount = handle.genesis_balance().checked_div(rU256::from(3u64)).unwrap(); + let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); + let amount = handle.genesis_balance().checked_div(U256::from(3u64)).unwrap(); - let tx = TransactionRequest::new().to(to).value(amount.to_ethers()).from(from); + let tx = TransactionRequest::default().to(to).value(amount).from(from).nonce(nonce + 1); + + let tx = WithOtherFields::new(tx); // send the transaction with higher nonce than on chain - let higher_pending_tx = - provider.send_transaction(tx.clone().nonce(nonce + 1u64), None).await.unwrap(); + let higher_pending_tx = provider.send_transaction(tx).await.unwrap(); // ensure the listener for ready transactions times out let mut listener = api.new_ready_transactions(); let res = timeout(Duration::from_millis(1500), listener.next()).await; res.unwrap_err(); + let tx = TransactionRequest::default().to(to).value(amount).from(from).nonce(nonce); + + let tx = WithOtherFields::new(tx); // send with the actual nonce which is mined immediately - let tx = - provider.send_transaction(tx.nonce(nonce), None).await.unwrap().await.unwrap().unwrap(); + let tx = provider.send_transaction(tx).await.unwrap(); + let tx = tx.get_receipt().await.unwrap(); // this will unblock the currently pending tx - let higher_tx = higher_pending_tx.await.unwrap().unwrap(); + let higher_tx = higher_pending_tx.get_receipt().await.unwrap(); // Awaits endlessly here due to alloy/#389 - let block = provider.get_block(1u64).await.unwrap().unwrap(); + let block = provider.get_block(1.into(), false).await.unwrap().unwrap(); assert_eq!(2, block.transactions.len()); - assert_eq!(vec![tx.transaction_hash, higher_tx.transaction_hash], block.transactions); + assert_eq!( + BlockTransactions::Hashes(vec![tx.transaction_hash, higher_tx.transaction_hash]), + block.transactions + ); } #[tokio::test(flavor = "multi_thread")] @@ -141,72 +144,87 @@ async fn can_replace_transaction() { // disable auto mining api.anvil_set_auto_mine(false).await.unwrap(); - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); - let accounts = handle.dev_wallets().collect::>().to_ethers(); + let accounts = handle.dev_wallets().collect::>(); let from = accounts[0].address(); let to = accounts[1].address(); - let nonce = provider.get_transaction_count(from, None).await.unwrap(); + let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); let gas_price = provider.get_gas_price().await.unwrap(); - let amount = handle.genesis_balance().checked_div(rU256::from(3u64)).unwrap(); + let amount = handle.genesis_balance().checked_div(U256::from(3u64)).unwrap(); + + let tx = TransactionRequest::default().to(to).value(amount).from(from).nonce(nonce); - let tx = TransactionRequest::new().to(to).value(amount.to_ethers()).from(from).nonce(nonce); + let mut tx = WithOtherFields::new(tx); + tx.set_gas_price(gas_price); // send transaction with lower gas price - let lower_priced_pending_tx = - provider.send_transaction(tx.clone().gas_price(gas_price), None).await.unwrap(); + let _lower_priced_pending_tx = provider.send_transaction(tx.clone()).await.unwrap(); + tx.set_gas_price(gas_price + 1); // send the same transaction with higher gas price - let higher_priced_pending_tx = - provider.send_transaction(tx.gas_price(gas_price + 1u64), None).await.unwrap(); + let higher_priced_pending_tx = provider.send_transaction(tx).await.unwrap(); + let higher_tx_hash = *higher_priced_pending_tx.tx_hash(); // mine exactly one block api.mine_one().await; - // lower priced transaction was replaced - let lower_priced_receipt = lower_priced_pending_tx.await.unwrap(); - assert!(lower_priced_receipt.is_none()); + let block = provider.get_block(1.into(), false).await.unwrap().unwrap(); - let higher_priced_receipt = higher_priced_pending_tx.await.unwrap().unwrap(); + assert_eq!(block.transactions.len(), 1); + assert_eq!(BlockTransactions::Hashes(vec![higher_tx_hash]), block.transactions); - // ensure that only the replacement tx was mined - let block = provider.get_block(1u64).await.unwrap().unwrap(); - assert_eq!(1, block.transactions.len()); - assert_eq!(vec![higher_priced_receipt.transaction_hash], block.transactions); + // FIXME: Unable to get receipt despite hotfix in https://github.com/alloy-rs/alloy/pull/614 + + // lower priced transaction was replaced + // let _lower_priced_receipt = lower_priced_pending_tx.get_receipt().await.unwrap(); + // let higher_priced_receipt = higher_priced_pending_tx.get_receipt().await.unwrap(); + + // assert_eq!(1, block.transactions.len()); + // assert_eq!( + // BlockTransactions::Hashes(vec![higher_priced_receipt.transaction_hash]), + // block.transactions + // ); } #[tokio::test(flavor = "multi_thread")] async fn can_reject_too_high_gas_limits() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); - let accounts = handle.dev_wallets().collect::>().to_ethers(); + let accounts = handle.dev_wallets().collect::>(); let from = accounts[0].address(); let to = accounts[1].address(); - let gas_limit = api.gas_limit(); - let amount = handle.genesis_balance().checked_div(rU256::from(3u64)).unwrap(); + let gas_limit = api.gas_limit().to::(); + let amount = handle.genesis_balance().checked_div(U256::from(3u64)).unwrap(); + + let tx = + TransactionRequest::default().to(to).value(amount).from(from).with_gas_limit(gas_limit); - let tx = TransactionRequest::new().to(to).value(amount.to_ethers()).from(from); + let mut tx = WithOtherFields::new(tx); // send transaction with the exact gas limit - let pending = provider.send_transaction(tx.clone().gas(gas_limit.to_ethers()), None).await; + let pending = provider.send_transaction(tx.clone()).await.unwrap(); - pending.unwrap(); + let pending_receipt = pending.get_receipt().await; + assert!(pending_receipt.is_ok()); + + tx.set_gas_limit(gas_limit + 1); // send transaction with higher gas limit - let pending = - provider.send_transaction(tx.clone().gas(gas_limit.to_ethers() + 1u64), None).await; + let pending = provider.send_transaction(tx.clone()).await; assert!(pending.is_err()); let err = pending.unwrap_err(); assert!(err.to_string().contains("gas too high")); - api.anvil_set_balance(from.to_alloy(), U256::MAX.to_alloy()).await.unwrap(); + api.anvil_set_balance(from, U256::MAX).await.unwrap(); - let pending = provider.send_transaction(tx.gas(gas_limit.to_ethers()), None).await; - pending.unwrap(); + tx.set_gas_limit(gas_limit); + let pending = provider.send_transaction(tx).await; + let _ = pending.unwrap(); } #[tokio::test(flavor = "multi_thread")] @@ -216,61 +234,65 @@ async fn can_reject_underpriced_replacement() { // disable auto mining api.anvil_set_auto_mine(false).await.unwrap(); - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); - let accounts = handle.dev_wallets().collect::>().to_ethers(); + let accounts = handle.dev_wallets().collect::>(); let from = accounts[0].address(); let to = accounts[1].address(); - let nonce = provider.get_transaction_count(from, None).await.unwrap(); + let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); let gas_price = provider.get_gas_price().await.unwrap(); - let amount = handle.genesis_balance().checked_div(rU256::from(3u64)).unwrap(); + let amount = handle.genesis_balance().checked_div(U256::from(3u64)).unwrap(); + + let tx = TransactionRequest::default().to(to).value(amount).from(from).nonce(nonce); - let tx = TransactionRequest::new().to(to).value(amount.to_ethers()).from(from).nonce(nonce); + let mut tx = WithOtherFields::new(tx); + tx.set_gas_price(gas_price + 1); // send transaction with higher gas price - let higher_priced_pending_tx = - provider.send_transaction(tx.clone().gas_price(gas_price + 1u64), None).await.unwrap(); + let higher_priced_pending_tx = provider.send_transaction(tx.clone()).await.unwrap(); + tx.set_gas_price(gas_price); // send the same transaction with lower gas price - let lower_priced_pending_tx = provider.send_transaction(tx.gas_price(gas_price), None).await; + let lower_priced_pending_tx = provider.send_transaction(tx).await; let replacement_err = lower_priced_pending_tx.unwrap_err(); assert!(replacement_err.to_string().contains("replacement transaction underpriced")); // mine exactly one block api.mine_one().await; - let higher_priced_receipt = higher_priced_pending_tx.await.unwrap().unwrap(); + let higher_priced_receipt = higher_priced_pending_tx.get_receipt().await.unwrap(); // ensure that only the higher priced tx was mined - let block = provider.get_block(1u64).await.unwrap().unwrap(); + let block = provider.get_block(1.into(), false).await.unwrap().unwrap(); assert_eq!(1, block.transactions.len()); - assert_eq!(vec![higher_priced_receipt.transaction_hash], block.transactions); + assert_eq!( + BlockTransactions::Hashes(vec![higher_priced_receipt.transaction_hash]), + block.transactions + ); } #[tokio::test(flavor = "multi_thread")] async fn can_deploy_greeter_http() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let wallet = handle.dev_wallets().next().unwrap(); - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); + let signer: EthereumSigner = wallet.clone().into(); - let greeter_contract = Greeter::deploy(Arc::clone(&client), "Hello World!".to_string()) - .unwrap() - .legacy() - .send() - .await - .unwrap(); + let alloy_provider = http_provider_with_signer(&handle.http_endpoint(), signer); - let greeting = greeter_contract.greet().call().await.unwrap(); - assert_eq!("Hello World!", greeting); + let alloy_greeter_addr = + Greeter::deploy_builder(alloy_provider.clone(), "Hello World!".to_string()) + // .legacy() unimplemented! in alloy + .deploy() + .await + .unwrap(); - let greeter_contract = - Greeter::deploy(client, "Hello World!".to_string()).unwrap().send().await.unwrap(); + let alloy_greeter = Greeter::new(alloy_greeter_addr, alloy_provider); - let greeting = greeter_contract.greet().call().await.unwrap(); - assert_eq!("Hello World!", greeting); + let greeting = alloy_greeter.greet().call().await.unwrap(); + + assert_eq!("Hello World!", greeting._0); } #[tokio::test(flavor = "multi_thread")] @@ -284,285 +306,302 @@ async fn can_deploy_and_mine_manually() { // can mine in manual mode api.evm_mine(None).await.unwrap(); - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); + + let wallet = handle.dev_wallets().next().unwrap(); + let from = wallet.address(); + + let greeter_builder = + Greeter::deploy_builder(provider.clone(), "Hello World!".to_string()).from(from); + let greeter_calldata = greeter_builder.calldata(); - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); + let tx = TransactionRequest::default().from(from).with_input(greeter_calldata.to_owned()); - let tx = Greeter::deploy(Arc::clone(&client), "Hello World!".to_string()).unwrap().deployer.tx; + let tx = WithOtherFields::new(tx); - let tx = client.send_transaction(tx, None).await.unwrap(); + let tx = provider.send_transaction(tx).await.unwrap(); // mine block with tx manually api.evm_mine(None).await.unwrap(); - let receipt = tx.await.unwrap().unwrap(); + let receipt = tx.get_receipt().await.unwrap(); let address = receipt.contract_address.unwrap(); - let greeter_contract = Greeter::new(address, Arc::clone(&client)); + let greeter_contract = Greeter::new(address, provider); let greeting = greeter_contract.greet().call().await.unwrap(); - assert_eq!("Hello World!", greeting); + assert_eq!("Hello World!", greeting._0); - let set_greeting = greeter_contract.set_greeting("Another Message".to_string()); + let set_greeting = greeter_contract.setGreeting("Another Message".to_string()); let tx = set_greeting.send().await.unwrap(); // mine block manually api.evm_mine(None).await.unwrap(); - let _tx = tx.await.unwrap(); + let _tx = tx.get_receipt().await.unwrap(); let greeting = greeter_contract.greet().call().await.unwrap(); - assert_eq!("Another Message", greeting); + assert_eq!("Another Message", greeting._0); } #[tokio::test(flavor = "multi_thread")] async fn can_mine_automatically() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); // disable auto mine api.anvil_set_auto_mine(false).await.unwrap(); - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); + let wallet = handle.dev_wallets().next().unwrap(); + + let greeter_builder = Greeter::deploy_builder(provider.clone(), "Hello World!".to_string()) + .from(wallet.address()); + + let greeter_calldata = greeter_builder.calldata(); + + let tx = TransactionRequest::default() + .from(wallet.address()) + .with_input(greeter_calldata.to_owned()); + + let tx = WithOtherFields::new(tx); - let tx = Greeter::deploy(Arc::clone(&client), "Hello World!".to_string()).unwrap().deployer.tx; - let sent_tx = client.send_transaction(tx, None).await.unwrap(); + let sent_tx = provider.send_transaction(tx).await.unwrap(); // re-enable auto mine api.anvil_set_auto_mine(true).await.unwrap(); - let receipt = sent_tx.await.unwrap().unwrap(); - assert_eq!(receipt.status.unwrap().as_u64(), 1u64); + let receipt = sent_tx.get_receipt().await.unwrap(); + assert_eq!(receipt.block_number, Some(1)); } #[tokio::test(flavor = "multi_thread")] async fn can_call_greeter_historic() { - let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let (api, handle) = spawn(NodeConfig::test()).await; + let provider = handle.http_provider(); - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); + let wallet = handle.dev_wallets().next().unwrap(); - let greeter_contract = Greeter::deploy(Arc::clone(&client), "Hello World!".to_string()) - .unwrap() - .send() + let greeter_addr = Greeter::deploy_builder(provider.clone(), "Hello World!".to_string()) + .from(wallet.address()) + .deploy() .await .unwrap(); + let greeter_contract = Greeter::new(greeter_addr, provider.clone()); + let greeting = greeter_contract.greet().call().await.unwrap(); - assert_eq!("Hello World!", greeting); + assert_eq!("Hello World!", greeting._0); - let block = client.get_block_number().await.unwrap(); + let block_number = provider.get_block_number().await.unwrap(); + + let _ = greeter_contract.setGreeting("Another Message".to_string()).send().await.unwrap(); - greeter_contract - .set_greeting("Another Message".to_string()) - .send() - .await - .unwrap() - .await - .unwrap(); let greeting = greeter_contract.greet().call().await.unwrap(); - assert_eq!("Another Message", greeting); + assert_eq!("Another Message", greeting._0); + + // min + api.mine_one().await; // returns previous state let greeting = - greeter_contract.greet().block(BlockId::Number(block.into())).call().await.unwrap(); - assert_eq!("Hello World!", greeting); + greeter_contract.greet().block(BlockId::Number(block_number.into())).call().await.unwrap(); + assert_eq!("Hello World!", greeting._0); } #[tokio::test(flavor = "multi_thread")] async fn can_deploy_greeter_ws() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_ws_provider(&handle.ws_endpoint()); + let provider = handle.ws_provider(); - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); + let wallet = handle.dev_wallets().next().unwrap(); - let greeter_contract = Greeter::deploy(Arc::clone(&client), "Hello World!".to_string()) - .unwrap() - .legacy() - .send() + let greeter_addr = Greeter::deploy_builder(provider.clone(), "Hello World!".to_string()) + .from(wallet.address()) + // .legacy() unimplemented! in alloy + .deploy() .await .unwrap(); - let greeting = greeter_contract.greet().call().await.unwrap(); - assert_eq!("Hello World!", greeting); - - let greeter_contract = - Greeter::deploy(client, "Hello World!".to_string()).unwrap().send().await.unwrap(); + let greeter_contract = Greeter::new(greeter_addr, provider.clone()); let greeting = greeter_contract.greet().call().await.unwrap(); - assert_eq!("Hello World!", greeting); + assert_eq!("Hello World!", greeting._0); } #[tokio::test(flavor = "multi_thread")] async fn can_deploy_get_code() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_ws_provider(&handle.ws_endpoint()); + let provider = handle.ws_provider(); - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); + let wallet = handle.dev_wallets().next().unwrap(); - let greeter_contract = Greeter::deploy(Arc::clone(&client), "Hello World!".to_string()) - .unwrap() - .legacy() - .send() + let greeter_addr = Greeter::deploy_builder(provider.clone(), "Hello World!".to_string()) + .from(wallet.address()) + .deploy() .await .unwrap(); - let code = client.get_code(greeter_contract.address(), None).await.unwrap(); + let code = provider.get_code_at(greeter_addr, BlockId::latest()).await.unwrap(); assert!(!code.as_ref().is_empty()); } #[tokio::test(flavor = "multi_thread")] async fn get_blocktimestamp_works() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); - - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); + let provider = handle.http_provider(); - let contract = - MulticallContract::deploy(Arc::clone(&client), ()).unwrap().send().await.unwrap(); + let contract = MulticallContract::deploy(provider.clone()).await.unwrap(); - let timestamp = contract.get_current_block_timestamp().call().await.unwrap(); + let timestamp = contract.getCurrentBlockTimestamp().call().await.unwrap().timestamp; - assert!(timestamp > U256::one()); + assert!(timestamp > U256::from(1)); let latest_block = api.block_by_number(alloy_rpc_types::BlockNumberOrTag::Latest).await.unwrap().unwrap(); - let timestamp = contract.get_current_block_timestamp().call().await.unwrap(); - assert_eq!(timestamp.as_u64(), latest_block.header.timestamp); + let timestamp = contract.getCurrentBlockTimestamp().call().await.unwrap().timestamp; + assert_eq!(timestamp.to::(), latest_block.header.timestamp); // repeat call same result - let timestamp = contract.get_current_block_timestamp().call().await.unwrap(); - assert_eq!(timestamp.as_u64(), latest_block.header.timestamp); + let timestamp = contract.getCurrentBlockTimestamp().call().await.unwrap().timestamp; + assert_eq!(timestamp.to::(), latest_block.header.timestamp); // mock timestamp - let next_timestamp = timestamp.as_u64() + 1337; + let next_timestamp = timestamp.to::() + 1337; api.evm_set_next_block_timestamp(next_timestamp).unwrap(); - let timestamp = - contract.get_current_block_timestamp().block(BlockNumber::Pending).call().await.unwrap(); - assert_eq!(timestamp, next_timestamp.into()); + let timestamp = contract + .getCurrentBlockTimestamp() + .block(BlockId::pending()) + .call() + .await + .unwrap() + .timestamp; + assert_eq!(timestamp, U256::from(next_timestamp)); // repeat call same result - let timestamp = - contract.get_current_block_timestamp().block(BlockNumber::Pending).call().await.unwrap(); - assert_eq!(timestamp, next_timestamp.into()); + let timestamp = contract + .getCurrentBlockTimestamp() + .block(BlockId::pending()) + .call() + .await + .unwrap() + .timestamp; + assert_eq!(timestamp, U256::from(next_timestamp)); } #[tokio::test(flavor = "multi_thread")] async fn call_past_state() { - let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let (api, handle) = spawn(NodeConfig::test()).await; + let provider = handle.http_provider(); - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); + let wallet = handle.dev_wallets().next().unwrap(); + let contract_addr = + SimpleStorage::deploy_builder(provider.clone(), "initial value".to_string()) + .from(wallet.address()) + .deploy() + .await + .unwrap(); - let contract = SimpleStorage::deploy(Arc::clone(&client), "initial value".to_string()) - .unwrap() - .send() - .await - .unwrap(); + let contract = SimpleStorage::new(contract_addr, provider.clone()); - let deployed_block = client.get_block_number().await.unwrap(); + let deployed_block = provider.get_block_number().await.unwrap(); - // assert initial state - let value = contract.method::<_, String>("getValue", ()).unwrap().call().await.unwrap(); - assert_eq!(value, "initial value"); + let value = contract.getValue().call().await.unwrap(); + assert_eq!(value._0, "initial value"); - // make a call with `client` - let _tx_hash = contract - .method::<_, H256>("setValue", "hi".to_owned()) - .unwrap() - .send() - .await - .unwrap() - .await - .unwrap() - .unwrap(); + let gas_price = api.gas_price().unwrap().to::(); + let set_tx = contract.setValue("hi".to_string()).gas_price(gas_price + 1); + + let _set_tx = set_tx.send().await.unwrap().get_receipt().await.unwrap(); // assert new value - let value = contract.method::<_, String>("getValue", ()).unwrap().call().await.unwrap(); - assert_eq!(value, "hi"); + let value = contract.getValue().call().await.unwrap(); + assert_eq!(value._0, "hi"); // assert previous value - let value = contract - .method::<_, String>("getValue", ()) - .unwrap() - .block(BlockId::Number(deployed_block.into())) - .call() - .await - .unwrap(); - assert_eq!(value, "initial value"); + let value = + contract.getValue().block(BlockId::Number(deployed_block.into())).call().await.unwrap(); + assert_eq!(value._0, "initial value"); - let hash = client.get_block(1).await.unwrap().unwrap().hash.unwrap(); - let value = contract - .method::<_, String>("getValue", ()) - .unwrap() - .block(BlockId::Hash(hash)) - .call() + let hash = provider + .get_block(BlockId::Number(1.into()), false) .await + .unwrap() + .unwrap() + .header + .hash .unwrap(); - assert_eq!(value, "initial value"); + let value = contract.getValue().block(BlockId::Hash(hash.into())).call().await.unwrap(); + assert_eq!(value._0, "initial value"); } #[tokio::test(flavor = "multi_thread")] async fn can_handle_multiple_concurrent_transfers_with_same_nonce() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_ws_provider(&handle.ws_endpoint()); + let provider = handle.ws_provider(); - let accounts = handle.dev_wallets().collect::>().to_ethers(); + let accounts = handle.dev_wallets().collect::>(); let from = accounts[0].address(); let to = accounts[1].address(); - let nonce = provider.get_transaction_count(from, None).await.unwrap(); + let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); // explicitly set the nonce - let tx = TransactionRequest::new().to(to).value(100u64).from(from).nonce(nonce).gas(21_000u64); + let tx = TransactionRequest::default() + .to(to) + .value(U256::from(100)) + .from(from) + .nonce(nonce) + .with_gas_limit(21000u128); + + let tx = WithOtherFields::new(tx); + let mut tasks = Vec::new(); for _ in 0..10 { - let provider = provider.clone(); let tx = tx.clone(); - let task = - tokio::task::spawn(async move { provider.send_transaction(tx, None).await?.await }); + let provider = provider.clone(); + let task = tokio::task::spawn(async move { + provider.send_transaction(tx).await.unwrap().get_receipt().await + }); tasks.push(task); } // only one succeeded let successful_tx = - join_all(tasks).await.into_iter().filter(|res| res.as_ref().unwrap().is_ok()).count(); + join_all(tasks).await.into_iter().filter(|res| res.as_ref().is_ok()).count(); assert_eq!(successful_tx, 1); - assert_eq!(provider.get_transaction_count(from, None).await.unwrap(), 1u64.into()); + assert_eq!(provider.get_transaction_count(from, BlockId::latest()).await.unwrap(), 1u64); } #[tokio::test(flavor = "multi_thread")] async fn can_handle_multiple_concurrent_deploys_with_same_nonce() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_ws_provider(&handle.ws_endpoint()); + let provider = handle.ws_provider(); - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); + let wallet = handle.dev_wallets().next().unwrap(); let from = wallet.address(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); - let nonce = client.get_transaction_count(from, None).await.unwrap(); - // explicitly set the nonce + let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); + let mut tasks = Vec::new(); - let mut tx = - Greeter::deploy(Arc::clone(&client), "Hello World!".to_string()).unwrap().deployer.tx; - tx.set_nonce(nonce); - tx.set_gas(300_000u64); + + let greeter = Greeter::deploy_builder(provider.clone(), "Hello World!".to_string()); + + let greeter_calldata = greeter.calldata(); + + let tx = TransactionRequest::default() + .from(from) + .with_input(greeter_calldata.to_owned()) + .nonce(nonce) + .with_gas_limit(300_000u128); + + let tx = WithOtherFields::new(tx); for _ in 0..10 { - let client = Arc::clone(&client); + let provider = provider.clone(); let tx = tx.clone(); let task = tokio::task::spawn(async move { - Ok::<_, SignerMiddlewareError<_, _>>( - client.send_transaction(tx, None).await?.await.unwrap(), - ) + Ok(provider.send_transaction(tx).await?.get_receipt().await.unwrap()) }); tasks.push(task); } @@ -571,51 +610,54 @@ async fn can_handle_multiple_concurrent_deploys_with_same_nonce() { let successful_tx = join_all(tasks).await.into_iter().filter(|res| res.as_ref().unwrap().is_ok()).count(); assert_eq!(successful_tx, 1); - assert_eq!(client.get_transaction_count(from, None).await.unwrap(), 1u64.into()); + assert_eq!(provider.get_transaction_count(from, BlockId::latest()).await.unwrap(), 1u64); } #[tokio::test(flavor = "multi_thread")] async fn can_handle_multiple_concurrent_transactions_with_same_nonce() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_ws_provider(&handle.ws_endpoint()); + let provider = handle.ws_provider(); - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); + let wallet = handle.dev_wallets().next().unwrap(); let from = wallet.address(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); - let greeter_contract = Greeter::deploy(Arc::clone(&client), "Hello World!".to_string()) - .unwrap() - .send() - .await - .unwrap(); + let greeter_contract = + Greeter::deploy(provider.clone(), "Hello World!".to_string()).await.unwrap(); + + let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); - let nonce = client.get_transaction_count(from, None).await.unwrap(); - // explicitly set the nonce let mut tasks = Vec::new(); - let mut deploy_tx = - Greeter::deploy(Arc::clone(&client), "Hello World!".to_string()).unwrap().deployer.tx; - deploy_tx.set_nonce(nonce); - deploy_tx.set_gas(300_000u64); - let mut set_greeting_tx = greeter_contract.set_greeting("Hello".to_string()).tx; - set_greeting_tx.set_nonce(nonce); - set_greeting_tx.set_gas(300_000u64); + let deploy = Greeter::deploy_builder(provider.clone(), "Hello World!".to_string()); + let deploy_calldata = deploy.calldata(); + let deploy_tx = TransactionRequest::default() + .from(from) + .with_input(deploy_calldata.to_owned()) + .nonce(nonce) + .with_gas_limit(300_000u128); + let deploy_tx = WithOtherFields::new(deploy_tx); + + let set_greeting = greeter_contract.setGreeting("Hello".to_string()); + let set_greeting_calldata = set_greeting.calldata(); + + let set_greeting_tx = TransactionRequest::default() + .from(from) + .with_input(set_greeting_calldata.to_owned()) + .nonce(nonce) + .with_gas_limit(300_000u128); + let set_greeting_tx = WithOtherFields::new(set_greeting_tx); for idx in 0..10 { - let client = Arc::clone(&client); + let provider = provider.clone(); let task = if idx % 2 == 0 { let tx = deploy_tx.clone(); tokio::task::spawn(async move { - Ok::<_, SignerMiddlewareError<_, _>>( - client.send_transaction(tx, None).await?.await.unwrap(), - ) + Ok(provider.send_transaction(tx).await?.get_receipt().await.unwrap()) }) } else { let tx = set_greeting_tx.clone(); tokio::task::spawn(async move { - Ok::<_, SignerMiddlewareError<_, _>>( - client.send_transaction(tx, None).await?.await.unwrap(), - ) + Ok(provider.send_transaction(tx).await?.get_receipt().await.unwrap()) }) }; @@ -626,9 +668,8 @@ async fn can_handle_multiple_concurrent_transactions_with_same_nonce() { let successful_tx = join_all(tasks).await.into_iter().filter(|res| res.as_ref().unwrap().is_ok()).count(); assert_eq!(successful_tx, 1); - assert_eq!(client.get_transaction_count(from, None).await.unwrap(), nonce + 1); + assert_eq!(provider.get_transaction_count(from, BlockId::latest()).await.unwrap(), nonce + 1); } - #[tokio::test(flavor = "multi_thread")] async fn can_get_pending_transaction() { let (api, handle) = spawn(NodeConfig::test()).await; @@ -636,17 +677,18 @@ async fn can_get_pending_transaction() { // disable auto mining so we can check if we can return pending tx from the mempool api.anvil_set_auto_mine(false).await.unwrap(); - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); let from = handle.dev_wallets().next().unwrap().address(); - let tx = TransactionRequest::new().from(from.to_ethers()).value(1337u64).to(Address::random()); - let tx = provider.send_transaction(tx, None).await.unwrap(); + let tx = TransactionRequest::default().from(from).value(U256::from(1337)).to(Address::random()); + let tx = WithOtherFields::new(tx); + let tx = provider.send_transaction(tx).await.unwrap(); - let pending = provider.get_transaction(tx.tx_hash()).await.unwrap(); - assert!(pending.is_some()); + let pending = provider.get_transaction_by_hash(*tx.tx_hash()).await; + assert!(pending.is_ok()); api.mine_one().await; - let mined = provider.get_transaction(tx.tx_hash()).await.unwrap().unwrap(); + let mined = provider.get_transaction_by_hash(*tx.tx_hash()).await.unwrap(); assert_eq!(mined.hash, pending.unwrap().hash); } @@ -657,15 +699,12 @@ async fn test_first_noce_is_zero() { api.anvil_set_auto_mine(false).await.unwrap(); - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); let from = handle.dev_wallets().next().unwrap().address(); - let nonce = provider - .get_transaction_count(from.to_ethers(), Some(BlockId::Number(BlockNumber::Pending))) - .await - .unwrap(); + let nonce = provider.get_transaction_count(from, BlockId::pending()).await.unwrap(); - assert_eq!(nonce, U256::zero()); + assert_eq!(nonce, 0); } #[tokio::test(flavor = "multi_thread")] @@ -674,8 +713,8 @@ async fn can_handle_different_sender_nonce_calculation() { api.anvil_set_auto_mine(false).await.unwrap(); - let provider = ethers_http_provider(&handle.http_endpoint()); - let accounts = handle.dev_wallets().collect::>().to_ethers(); + let provider = handle.http_provider(); + let accounts = handle.dev_wallets().collect::>(); let from_first = accounts[0].address(); let from_second = accounts[1].address(); @@ -683,23 +722,25 @@ async fn can_handle_different_sender_nonce_calculation() { // send a bunch of tx to the mempool and check nonce is returned correctly for idx in 1..=tx_count { - let tx_from_first = - TransactionRequest::new().from(from_first).value(1337u64).to(Address::random()); - let _tx = provider.send_transaction(tx_from_first, None).await.unwrap(); - let nonce_from_first = provider - .get_transaction_count(from_first, Some(BlockId::Number(BlockNumber::Pending))) - .await - .unwrap(); - assert_eq!(nonce_from_first, idx.into()); - - let tx_from_second = - TransactionRequest::new().from(from_second).value(1337u64).to(Address::random()); - let _tx = provider.send_transaction(tx_from_second, None).await.unwrap(); - let nonce_from_second = provider - .get_transaction_count(from_second, Some(BlockId::Number(BlockNumber::Pending))) - .await - .unwrap(); - assert_eq!(nonce_from_second, idx.into()); + let tx_from_first = TransactionRequest::default() + .from(from_first) + .value(U256::from(1337u64)) + .to(Address::random()); + let tx_from_first = WithOtherFields::new(tx_from_first); + let _tx = provider.send_transaction(tx_from_first).await.unwrap(); + let nonce_from_first = + provider.get_transaction_count(from_first, BlockId::pending()).await.unwrap(); + assert_eq!(nonce_from_first, idx); + + let tx_from_second = TransactionRequest::default() + .from(from_second) + .value(U256::from(1337u64)) + .to(Address::random()); + let tx_from_second = WithOtherFields::new(tx_from_second); + let _tx = provider.send_transaction(tx_from_second).await.unwrap(); + let nonce_from_second = + provider.get_transaction_count(from_second, BlockId::pending()).await.unwrap(); + assert_eq!(nonce_from_second, idx); } } @@ -709,7 +750,7 @@ async fn includes_pending_tx_for_transaction_count() { api.anvil_set_auto_mine(false).await.unwrap(); - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); let from = handle.dev_wallets().next().unwrap().address(); let tx_count = 10u64; @@ -717,55 +758,47 @@ async fn includes_pending_tx_for_transaction_count() { // send a bunch of tx to the mempool and check nonce is returned correctly for idx in 1..=tx_count { let tx = - TransactionRequest::new().from(from.to_ethers()).value(1337u64).to(Address::random()); - let _tx = provider.send_transaction(tx, None).await.unwrap(); - let nonce = provider - .get_transaction_count(from.to_ethers(), Some(BlockId::Number(BlockNumber::Pending))) - .await - .unwrap(); - assert_eq!(nonce, idx.into()); + TransactionRequest::default().from(from).value(U256::from(1337)).to(Address::random()); + let tx = WithOtherFields::new(tx); + let _tx = provider.send_transaction(tx).await.unwrap(); + let nonce = provider.get_transaction_count(from, BlockId::pending()).await.unwrap(); + assert_eq!(nonce, idx); } api.mine_one().await; - let nonce = provider - .get_transaction_count(from.to_ethers(), Some(BlockId::Number(BlockNumber::Pending))) - .await - .unwrap(); - assert_eq!(nonce, tx_count.into()); + let nonce = provider.get_transaction_count(from, BlockId::pending()).await.unwrap(); + assert_eq!(nonce, tx_count); } #[tokio::test(flavor = "multi_thread")] async fn can_get_historic_info() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); - let accounts = handle.dev_wallets().collect::>().to_ethers(); + let accounts = handle.dev_wallets().collect::>(); let from = accounts[0].address(); let to = accounts[1].address(); - let amount = handle.genesis_balance().checked_div(rU256::from(2u64)).unwrap(); - let tx = TransactionRequest::new().to(to).value(amount.to_ethers()).from(from); - let _tx = provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); + let amount = handle.genesis_balance().checked_div(U256::from(2u64)).unwrap(); + let tx = TransactionRequest::default().to(to).value(amount).from(from); + let tx = WithOtherFields::new(tx); + let tx = provider.send_transaction(tx).await.unwrap(); + let _ = tx.get_receipt().await.unwrap(); - let nonce_pre = provider - .get_transaction_count(from, Some(BlockNumber::Number(0.into()).into())) - .await - .unwrap(); + let nonce_pre = provider.get_transaction_count(from, BlockId::Number(0.into())).await.unwrap(); - let nonce_post = - provider.get_transaction_count(from, Some(BlockNumber::Latest.into())).await.unwrap(); + let nonce_post = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); assert!(nonce_pre < nonce_post); - let balance_pre = - provider.get_balance(from, Some(BlockNumber::Number(0.into()).into())).await.unwrap(); + let balance_pre = provider.get_balance(from, BlockId::Number(0.into())).await.unwrap(); - let balance_post = provider.get_balance(from, Some(BlockNumber::Latest.into())).await.unwrap(); + let balance_post = provider.get_balance(from, BlockId::latest()).await.unwrap(); assert!(balance_post < balance_pre); - let to_balance = provider.get_balance(to, None).await.unwrap(); - assert_eq!(balance_pre.saturating_add(amount.to_ethers()), to_balance); + let to_balance = provider.get_balance(to, BlockId::latest()).await.unwrap(); + assert_eq!(balance_pre.saturating_add(amount), to_balance); } // @@ -773,55 +806,78 @@ async fn can_get_historic_info() { async fn test_tx_receipt() { let (_api, handle) = spawn(NodeConfig::test()).await; - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let client = - Arc::new(SignerMiddleware::new(ethers_http_provider(&handle.http_endpoint()), wallet)); + let wallet = handle.dev_wallets().next().unwrap(); + let provider = handle.http_provider(); - let tx = TransactionRequest::new().to(Address::random()).value(1337u64); + let tx = TransactionRequest::default().to(Address::random()).value(U256::from(1337)); - let tx = client.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); + let tx = WithOtherFields::new(tx); + let tx = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); assert!(tx.to.is_some()); - let tx = Greeter::deploy(Arc::clone(&client), "Hello World!".to_string()).unwrap().deployer.tx; + let greeter_deploy = Greeter::deploy_builder(provider.clone(), "Hello World!".to_string()); + let greeter_calldata = greeter_deploy.calldata(); - let tx = client.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); + let tx = TransactionRequest::default() + .from(wallet.address()) + .with_input(greeter_calldata.to_owned()); + + let tx = WithOtherFields::new(tx); + let tx = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); // `to` field is none if it's a contract creation transaction: https://eth.wiki/json-rpc/API#eth_getTransactionReceipt assert!(tx.to.is_none()); assert!(tx.contract_address.is_some()); } +// TODO: Fix error: ErrorPayload { code: -32602, message: "invalid type: boolean `true`, expected +// unit", data: None } originating from watch_full_pending_transactions, remove ignore +#[ignore] #[tokio::test(flavor = "multi_thread")] async fn can_stream_pending_transactions() { let (_api, handle) = spawn(NodeConfig::test().with_blocktime(Some(Duration::from_secs(2)))).await; let num_txs = 5; - let provider = ethers_http_provider(&handle.http_endpoint()); - let ws_provider = ethers_ws_provider(&handle.ws_endpoint()); + + let provider = handle.http_provider(); + let ws_provider = handle.ws_provider(); let accounts = provider.get_accounts().await.unwrap(); - let tx = TransactionRequest::new().from(accounts[0]).to(accounts[0]).value(1e18 as u64); + let tx = + TransactionRequest::default().from(accounts[0]).to(accounts[0]).value(U256::from(1e18)); let mut sending = futures::future::join_all( std::iter::repeat(tx.clone()) .take(num_txs) .enumerate() - .map(|(nonce, tx)| tx.nonce(nonce)) + .map(|(nonce, tx)| tx.nonce(nonce as u64)) .map(|tx| async { - provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap() + let tx = WithOtherFields::new(tx); + provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap() }), ) .fuse(); - let mut watch_tx_stream = - provider.watch_pending_transactions().await.unwrap().transactions_unordered(num_txs).fuse(); - - let mut sub_tx_stream = - ws_provider.subscribe_pending_txs().await.unwrap().transactions_unordered(2).fuse(); + let mut watch_tx_stream = provider + .watch_full_pending_transactions() + .await + .unwrap() // TODO: Fix error here + .into_stream() + .flat_map(futures::stream::iter) + .take(num_txs) + .fuse(); + + let mut sub_tx_stream = ws_provider + .subscribe_full_pending_transactions() + .await + .unwrap() + .into_stream() + .take(2) + .fuse(); - let mut sent: Option> = None; - let mut watch_received: Vec = Vec::with_capacity(num_txs); - let mut sub_received: Vec = Vec::with_capacity(num_txs); + let mut sent = None; + let mut watch_received = Vec::with_capacity(num_txs); + let mut sub_received = Vec::with_capacity(num_txs); loop { futures::select! { @@ -829,12 +885,17 @@ async fn can_stream_pending_transactions() { sent = Some(txs) }, tx = watch_tx_stream.next() => { - watch_received.push(tx.unwrap().unwrap()); + if let Some(tx) = tx { + watch_received.push(tx); + } }, tx = sub_tx_stream.next() => { - sub_received.push(tx.unwrap().unwrap()); + if let Some(tx) = tx { + sub_received.push(tx); + } }, }; + if watch_received.len() == num_txs && sub_received.len() == num_txs { if let Some(ref sent) = sent { assert_eq!(sent.len(), watch_received.len()); @@ -879,39 +940,51 @@ async fn test_tx_access_list() { // - The sender shouldn't be in the AL let (_api, handle) = spawn(NodeConfig::test()).await; - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let client = - Arc::new(SignerMiddleware::new(ethers_http_provider(&handle.http_endpoint()), wallet)); + let provider = handle.http_provider(); let sender = Address::random(); let other_acc = Address::random(); - let multicall = MulticallContract::deploy(client.clone(), ()).unwrap().send().await.unwrap(); - let simple_storage = - SimpleStorage::deploy(client.clone(), "foo".to_string()).unwrap().send().await.unwrap(); + let multicall = MulticallContract::deploy(provider.clone()).await.unwrap(); + let simple_storage = SimpleStorage::deploy(provider.clone(), "foo".to_string()).await.unwrap(); // when calling `setValue` on SimpleStorage, both the `lastSender` and `_value` storages are // modified The `_value` is a `string`, so the storage slots here (small string) are `0x1` // and `keccak(0x1)` - let set_value_tx = simple_storage.set_value("bar".to_string()).from(sender).tx; - let access_list = client.create_access_list(&set_value_tx, None).await.unwrap(); + let set_value = simple_storage.setValue("bar".to_string()); + let set_value_calldata = set_value.calldata(); + let set_value_tx = TransactionRequest::default() + .from(sender) + .to(*simple_storage.address()) + .with_input(set_value_calldata.to_owned()); + let set_value_tx = WithOtherFields::new(set_value_tx); + let access_list = provider.create_access_list(&set_value_tx, BlockId::latest()).await.unwrap(); + // let set_value_tx = simple_storage.set_value("bar".to_string()).from(sender).tx; + // let access_list = client.create_access_list(&set_value_tx, None).await.unwrap(); assert_access_list_eq( access_list.access_list, AccessList::from(vec![AccessListItem { - address: simple_storage.address(), + address: *simple_storage.address(), storage_keys: vec![ - H256::zero(), - H256::from_uint(&(1u64.into())), - "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6" - .parse() - .unwrap(), + FixedBytes::ZERO, + FixedBytes::with_last_byte(1), + FixedBytes::from_str( + "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6", + ) + .unwrap(), ], }]), ); // With a subcall that fetches the balances of an account (`other_acc`), only the address // of this account should be in the Access List - let call_tx = multicall.get_eth_balance(other_acc).from(sender).tx; - let access_list = client.create_access_list(&call_tx, None).await.unwrap(); + let call_tx = multicall.getEthBalance(other_acc); + let call_tx_data = call_tx.calldata(); + let call_tx = TransactionRequest::default() + .from(sender) + .to(*multicall.address()) + .with_input(call_tx_data.to_owned()); + let call_tx = WithOtherFields::new(call_tx); + let access_list = provider.create_access_list(&call_tx, BlockId::latest()).await.unwrap(); assert_access_list_eq( access_list.access_list, AccessList::from(vec![AccessListItem { address: other_acc, storage_keys: vec![] }]), @@ -919,24 +992,31 @@ async fn test_tx_access_list() { // With a subcall to another contract, the AccessList should be the same as when calling the // subcontract directly (given that the proxy contract doesn't read/write any state) - let subcall_tx = multicall - .aggregate(vec![Call { - target: simple_storage.address(), - call_data: set_value_tx.data().unwrap().clone(), - }]) + let subcall_tx = multicall.aggregate(vec![MulticallContract::Call { + target: *simple_storage.address(), + callData: set_value_calldata.to_owned(), + }]); + + let subcall_tx_calldata = subcall_tx.calldata(); + + let subcall_tx = TransactionRequest::default() .from(sender) - .tx; - let access_list = client.create_access_list(&subcall_tx, None).await.unwrap(); + .to(*multicall.address()) + .with_input(subcall_tx_calldata.to_owned()); + let subcall_tx = WithOtherFields::new(subcall_tx); + let access_list = provider.create_access_list(&subcall_tx, BlockId::latest()).await.unwrap(); assert_access_list_eq( access_list.access_list, + // H256::from_uint(&(1u64.into())), AccessList::from(vec![AccessListItem { - address: simple_storage.address(), + address: *simple_storage.address(), storage_keys: vec![ - H256::zero(), - H256::from_uint(&(1u64.into())), - "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6" - .parse() - .unwrap(), + FixedBytes::ZERO, + FixedBytes::with_last_byte(1), + FixedBytes::from_str( + "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6", + ) + .unwrap(), ], }]), ); @@ -950,21 +1030,21 @@ async fn estimates_gas_on_pending_by_default() { // disable auto mine api.anvil_set_auto_mine(false).await.unwrap(); - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); + let wallet = handle.dev_wallets().next().unwrap(); let sender = wallet.address(); let recipient = Address::random(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); + let tx = TransactionRequest::default().from(sender).to(recipient).value(U256::from(1e18)); + let tx = WithOtherFields::new(tx); - let tx = TransactionRequest::new().from(sender).to(recipient).value(1e18 as u64); - client.send_transaction(tx, None).await.unwrap(); + let _pending = provider.send_transaction(tx).await.unwrap(); - let tx = AlloyTransactionRequest::default() - .from(recipient.to_alloy()) - .to(sender.to_alloy()) - .value(rU256::from(1e10)) + let tx = TransactionRequest::default() + .from(recipient) + .to(sender) + .value(U256::from(1e10)) .input(Bytes::from(vec![0x42]).into()); api.estimate_gas(WithOtherFields::new(tx), None, None).await.unwrap(); } @@ -973,14 +1053,14 @@ async fn estimates_gas_on_pending_by_default() { async fn test_estimate_gas() { let (api, handle) = spawn(NodeConfig::test()).await; - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); + let wallet = handle.dev_wallets().next().unwrap(); let sender = wallet.address(); let recipient = Address::random(); - let tx = AlloyTransactionRequest::default() - .from(recipient.to_alloy()) - .to(sender.to_alloy()) - .value(rU256::from(1e10)) + let tx = TransactionRequest::default() + .from(recipient) + .to(sender) + .value(U256::from(1e10)) .input(Bytes::from(vec![0x42]).into()); // Expect the gas estimation to fail due to insufficient funds. let error_result = api.estimate_gas(WithOtherFields::new(tx.clone()), None, None).await; @@ -994,7 +1074,7 @@ async fn test_estimate_gas() { ); // Setup state override to simulate sufficient funds for the recipient. - let addr = alloy_primitives::Address::from_slice(recipient.as_bytes()); + let addr = recipient; let account_override = AccountOverride { balance: Some(alloy_primitives::U256::from(1e18)), ..Default::default() }; let mut state_override = StateOverride::new(); @@ -1007,24 +1087,25 @@ async fn test_estimate_gas() { .expect("Failed to estimate gas with state override"); // Assert the gas estimate meets the expected minimum. - assert!(gas_estimate >= rU256::from(21000), "Gas estimate is lower than expected minimum"); + assert!(gas_estimate >= U256::from(21000), "Gas estimate is lower than expected minimum"); } #[tokio::test(flavor = "multi_thread")] async fn test_reject_gas_too_low() { let (_api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); let account = handle.dev_accounts().next().unwrap(); let gas = 21_000u64 - 1; - let tx = TransactionRequest::new() + let tx = TransactionRequest::default() .to(Address::random()) .value(U256::from(1337u64)) - .from(account.to_ethers()) - .gas(gas); + .from(account) + .with_gas_limit(gas as u128); + let tx = WithOtherFields::new(tx); - let resp = provider.send_transaction(tx, None).await; + let resp = provider.send_transaction(tx).await; let err = resp.unwrap_err().to_string(); assert!(err.contains("intrinsic gas too low")); @@ -1034,50 +1115,50 @@ async fn test_reject_gas_too_low() { #[tokio::test(flavor = "multi_thread")] async fn can_call_with_high_gas_limit() { let (_api, handle) = spawn(NodeConfig::test().with_gas_limit(Some(100_000_000))).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); + let greeter_contract = Greeter::deploy(provider, "Hello World!".to_string()).await.unwrap(); - let greeter_contract = Greeter::deploy(Arc::clone(&client), "Hello World!".to_string()) - .unwrap() - .send() - .await - .unwrap(); - - let greeting = greeter_contract.greet().gas(60_000_000u64).call().await.unwrap(); - assert_eq!("Hello World!", greeting); + let greeting = greeter_contract.greet().gas(60_000_000u128).call().await.unwrap(); + assert_eq!("Hello World!", greeting._0); } #[tokio::test(flavor = "multi_thread")] async fn test_reject_eip1559_pre_london() { let (api, handle) = spawn(NodeConfig::test().with_hardfork(Some(Hardfork::Berlin))).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); - let wallet = handle.dev_wallets().next().unwrap().to_ethers(); - let client = Arc::new(SignerMiddleware::new(provider, wallet)); + let gas_limit = api.gas_limit().to::(); + let gas_price = api.gas_price().unwrap().to::(); - let gas_limit = api.gas_limit(); - let gas_price = api.gas_price().unwrap(); - let unsupported = Greeter::deploy(Arc::clone(&client), "Hello World!".to_string()) - .unwrap() - .gas(gas_limit.to_ethers()) - .gas_price(gas_price.to_ethers()) - .send() - .await - .unwrap_err() - .to_string(); + let unsupported_call_builder = + Greeter::deploy_builder(provider.clone(), "Hello World!".to_string()); + let unsupported_calldata = unsupported_call_builder.calldata(); + + let unsup_tx = TransactionRequest::default() + .from(handle.dev_accounts().next().unwrap()) + .with_input(unsupported_calldata.to_owned()) + .with_gas_limit(gas_limit) + .with_max_fee_per_gas(gas_price) + .with_max_priority_fee_per_gas(gas_price); + + let unsup_tx = WithOtherFields::new(unsup_tx); + + let unsupported = provider.send_transaction(unsup_tx).await.unwrap_err().to_string(); assert!(unsupported.contains("not supported by the current hardfork"), "{unsupported}"); - let greeter_contract = Greeter::deploy(Arc::clone(&client), "Hello World!".to_string()) - .unwrap() - .legacy() - .send() - .await - .unwrap(); + let greeter_contract_addr = + Greeter::deploy_builder(provider.clone(), "Hello World!".to_string()) + .gas(gas_limit) + .gas_price(gas_price) + .deploy() + .await + .unwrap(); + + let greeter_contract = Greeter::new(greeter_contract_addr, provider); let greeting = greeter_contract.greet().call().await.unwrap(); - assert_eq!("Hello World!", greeting); + assert_eq!("Hello World!", greeting._0); } // https://github.com/foundry-rs/foundry/issues/6931 @@ -1088,7 +1169,7 @@ async fn can_mine_multiple_in_block() { // disable auto mine api.anvil_set_auto_mine(false).await.unwrap(); - let tx = AlloyTransactionRequest { + let tx = TransactionRequest { from: Some("0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266".parse().unwrap()), ..Default::default() }; @@ -1097,7 +1178,7 @@ async fn can_mine_multiple_in_block() { let first = api.send_transaction(WithOtherFields::new(tx.clone())).await.unwrap(); let second = api.send_transaction(WithOtherFields::new(tx.clone())).await.unwrap(); - api.anvil_mine(Some(rU256::from(1)), Some(rU256::ZERO)).await.unwrap(); + api.anvil_mine(Some(U256::from(1)), Some(U256::ZERO)).await.unwrap(); let block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); diff --git a/crates/anvil/tests/it/txpool.rs b/crates/anvil/tests/it/txpool.rs index 3c10a577f..40007447e 100644 --- a/crates/anvil/tests/it/txpool.rs +++ b/crates/anvil/tests/it/txpool.rs @@ -1,34 +1,40 @@ //! txpool related tests -use crate::utils::ethers_http_provider; +use alloy_network::TransactionBuilder; +use alloy_primitives::U256; +use alloy_provider::{txpool::TxPoolApi, Provider}; +use alloy_rpc_types::{TransactionRequest, WithOtherFields}; use anvil::{spawn, NodeConfig}; -use ethers::{ - prelude::Middleware, - types::{TransactionRequest, U256}, -}; #[tokio::test(flavor = "multi_thread")] async fn geth_txpool() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = ethers_http_provider(&handle.http_endpoint()); + let provider = handle.http_provider(); + api.anvil_set_auto_mine(false).await.unwrap(); - let account = provider.get_accounts().await.unwrap()[0]; - let value: u64 = 42; - let gas_price: U256 = 221435145689u64.into(); - let tx = TransactionRequest::new().to(account).from(account).value(value).gas_price(gas_price); + let account = provider.get_accounts().await.unwrap().remove(0); + let value = U256::from(42); + let gas_price = 221435145689u128; + + let tx = TransactionRequest::default() + .with_to(account) + .with_from(account) + .with_value(value) + .with_gas_price(gas_price); + let tx = WithOtherFields::new(tx); // send a few transactions let mut txs = Vec::new(); for _ in 0..10 { - let tx_hash = provider.send_transaction(tx.clone(), None).await.unwrap(); + let tx_hash = provider.send_transaction(tx.clone()).await.unwrap(); txs.push(tx_hash); } // we gave a 20s block time, should be plenty for us to get the txpool's content let status = provider.txpool_status().await.unwrap(); - assert_eq!(status.pending.as_u64(), 10); - assert_eq!(status.queued.as_u64(), 0); + assert_eq!(status.pending, 10); + assert_eq!(status.queued, 0); let inspect = provider.txpool_inspect().await.unwrap(); assert!(inspect.queued.is_empty()); @@ -36,8 +42,8 @@ async fn geth_txpool() { for i in 0..10 { let tx_summary = summary.get(&i.to_string()).unwrap(); assert_eq!(tx_summary.gas_price, gas_price); - assert_eq!(tx_summary.value, value.into()); - assert_eq!(tx_summary.gas, 21000.into()); + assert_eq!(tx_summary.value, value); + assert_eq!(tx_summary.gas, 21000); assert_eq!(tx_summary.to.unwrap(), account); } diff --git a/crates/anvil/tests/it/utils.rs b/crates/anvil/tests/it/utils.rs index 9aa077094..066ec807c 100644 --- a/crates/anvil/tests/it/utils.rs +++ b/crates/anvil/tests/it/utils.rs @@ -1,84 +1,62 @@ -use alloy_json_abi::JsonAbi; -use alloy_primitives::Bytes; -use ethers::{ - addressbook::contract, - contract::ContractInstance, - middleware::Middleware, - prelude::DeploymentTxFactory, - types::{Address, Chain}, +use alloy_network::{Ethereum, EthereumSigner}; +use foundry_common::provider::{ + get_http_provider, ProviderBuilder, RetryProvider, RetryProviderWithSigner, }; -use foundry_common::provider::ethers::{ProviderBuilder, RetryProvider}; -use std::borrow::Borrow; -/// Returns a set of various contract addresses -pub fn contract_addresses(chain: Chain) -> Vec
{ - vec![ - contract("dai").unwrap().address(chain).unwrap(), - contract("usdc").unwrap().address(chain).unwrap(), - contract("weth").unwrap().address(chain).unwrap(), - contract("uniswapV3Factory").unwrap().address(chain).unwrap(), - contract("uniswapV3SwapRouter02").unwrap().address(chain).unwrap(), - ] +pub fn http_provider(http_endpoint: &str) -> RetryProvider { + get_http_provider(http_endpoint) } -/// Builds an ethers HTTP [RetryProvider] -pub fn ethers_http_provider(http_endpoint: &str) -> RetryProvider { - ProviderBuilder::new(http_endpoint).build().expect("failed to build ethers HTTP provider") +pub fn http_provider_with_signer( + http_endpoint: &str, + signer: EthereumSigner, +) -> RetryProviderWithSigner { + ProviderBuilder::new(http_endpoint) + .build_with_signer(signer) + .expect("failed to build Alloy HTTP provider with signer") } -/// Builds an ethers ws [RetryProvider] -pub fn ethers_ws_provider(ws_endpoint: &str) -> RetryProvider { - ProviderBuilder::new(ws_endpoint).build().expect("failed to build ethers HTTP provider") +pub fn ws_provider_with_signer( + ws_endpoint: &str, + signer: EthereumSigner, +) -> RetryProviderWithSigner { + ProviderBuilder::new(ws_endpoint) + .build_with_signer(signer) + .expect("failed to build Alloy WS provider with signer") } -/// Builds an ethers ws [RetryProvider] -pub fn ethers_ipc_provider(ipc_endpoint: Option) -> Option { - ProviderBuilder::new(&ipc_endpoint?).build().ok() +pub async fn connect_pubsub(conn_str: &str) -> RootProvider { + alloy_provider::ProviderBuilder::new().on_builtin(conn_str).await.unwrap() } -/// Temporary helper trait for compatibility with ethers -pub trait ContractInstanceCompat -where - B: Borrow, - M: Middleware, -{ - fn new_compat(address: Address, abi: JsonAbi, client: B) -> Self; -} - -impl ContractInstanceCompat for ContractInstance -where - B: Borrow, - M: Middleware, -{ - fn new_compat(address: Address, abi: JsonAbi, client: B) -> Self { - let json = serde_json::to_string(&abi).unwrap(); - ContractInstance::new( - address, - serde_json::from_str::(&json).unwrap(), - client, - ) - } -} - -pub trait DeploymentTxFactoryCompat -where - B: Borrow + Clone, - M: Middleware, -{ - fn new_compat(abi: JsonAbi, bytecode: Bytes, client: B) -> Self; -} - -impl DeploymentTxFactoryCompat for DeploymentTxFactory -where - B: Borrow + Clone, - M: Middleware, -{ - fn new_compat(abi: JsonAbi, bytecode: Bytes, client: B) -> Self { - let json = serde_json::to_string(&abi).unwrap(); - DeploymentTxFactory::new( - serde_json::from_str::(&json).unwrap(), - bytecode.as_ref().to_vec().into(), - client, - ) - } +use alloy_provider::{ + fillers::{ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller, SignerFiller}, + Identity, RootProvider, +}; +use alloy_transport::BoxTransport; +type PubsubSigner = FillProvider< + JoinFill< + JoinFill, NonceFiller>, ChainIdFiller>, + SignerFiller, + >, + RootProvider, + BoxTransport, + Ethereum, +>; +pub async fn connect_pubsub_with_signer(conn_str: &str, signer: EthereumSigner) -> PubsubSigner { + alloy_provider::ProviderBuilder::new() + .with_recommended_fillers() + .signer(signer) + .on_builtin(conn_str) + .await + .unwrap() +} + +pub async fn ipc_provider_with_signer( + ipc_endpoint: &str, + signer: EthereumSigner, +) -> RetryProviderWithSigner { + ProviderBuilder::new(ipc_endpoint) + .build_with_signer(signer) + .expect("failed to build Alloy IPC provider with signer") } diff --git a/crates/anvil/tests/it/wsapi.rs b/crates/anvil/tests/it/wsapi.rs index 58bad12eb..f68b15312 100644 --- a/crates/anvil/tests/it/wsapi.rs +++ b/crates/anvil/tests/it/wsapi.rs @@ -1,16 +1,15 @@ //! general eth api tests with websocket provider -use alloy_eips::BlockId; +use alloy_primitives::U256; use alloy_provider::Provider; +use alloy_rpc_types::BlockId; use anvil::{spawn, NodeConfig}; -use ethers::types::U256; -use foundry_common::types::ToAlloy; #[tokio::test(flavor = "multi_thread")] async fn can_get_block_number_ws() { let (api, handle) = spawn(NodeConfig::test()).await; let block_num = api.block_number().unwrap(); - assert_eq!(block_num, U256::zero().to_alloy()); + assert_eq!(block_num, U256::ZERO); let provider = handle.ws_provider(); diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index 11b304212..ff7457e20 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -15,7 +15,11 @@ name = "cast" path = "bin/main.rs" [build-dependencies] -vergen = { workspace = true, default-features = false, features = ["build", "git", "gitcl"] } +vergen = { workspace = true, default-features = false, features = [ + "build", + "git", + "gitcl", +] } [dependencies] # lib @@ -43,7 +47,7 @@ alloy-sol-types.workspace = true alloy-chains.workspace = true ethers-core.workspace = true -ethers-contract.workspace = true +ethers-contract = { workspace = true, features = ["abigen"] } chrono.workspace = true evm-disassembler.workspace = true diff --git a/crates/cast/bin/cmd/call.rs b/crates/cast/bin/cmd/call.rs index 32b4ec794..2caa54d2a 100644 --- a/crates/cast/bin/cmd/call.rs +++ b/crates/cast/bin/cmd/call.rs @@ -1,5 +1,5 @@ use alloy_network::TransactionBuilder; -use alloy_primitives::{Bytes, TxKind, U256}; +use alloy_primitives::{TxKind, U256}; use alloy_rpc_types::{BlockId, TransactionRequest, WithOtherFields}; use cast::Cast; use clap::Parser; @@ -226,7 +226,7 @@ impl CallArgs { } }; - req.set_input::(data.into()); + req.set_input(data); println!("{}", Cast::new(provider).call(&req, func.as_ref(), block).await?); diff --git a/crates/cast/bin/cmd/estimate.rs b/crates/cast/bin/cmd/estimate.rs index 6ee33ed26..b0f0d6fcf 100644 --- a/crates/cast/bin/cmd/estimate.rs +++ b/crates/cast/bin/cmd/estimate.rs @@ -1,5 +1,5 @@ use alloy_network::TransactionBuilder; -use alloy_primitives::{Bytes, U256}; +use alloy_primitives::U256; use alloy_provider::Provider; use alloy_rpc_types::{BlockId, TransactionRequest, WithOtherFields}; use clap::Parser; @@ -121,7 +121,7 @@ impl EstimateArgs { } }; - req.set_input::(data.into()); + req.set_input(data); let gas = provider.estimate_gas(&req, BlockId::latest()).await?; println!("{gas}"); diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index 5a5d362fd..599d73247 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -90,7 +90,7 @@ 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::alloy::ProviderBuilder::new( + let provider = foundry_common::provider::ProviderBuilder::new( &config.get_rpc_url_or_localhost_http()?, ) .compute_units_per_second_opt(compute_units_per_second) diff --git a/crates/cast/bin/tx.rs b/crates/cast/bin/tx.rs index 184da16d6..1ad0ee2a9 100644 --- a/crates/cast/bin/tx.rs +++ b/crates/cast/bin/tx.rs @@ -56,7 +56,7 @@ pub async fn build_tx< let chain = chain.into(); let from = from.into().resolve(provider).await?; - // TODO: Possible bug here? + let to: Option
= if let Some(to) = to { Some(to.into().resolve(provider).await?) } else { None }; diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index 1f26acbee..da4103b74 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -120,7 +120,7 @@ where func: Option<&Function>, block: Option, ) -> Result { - let res = self.provider.call(req, block.unwrap_or(BlockId::latest())).await?; + let res = self.provider.call(req, block.unwrap_or_default()).await?; let mut decoded = vec![]; @@ -855,7 +855,6 @@ where /// use alloy_rpc_types::Filter; /// use alloy_transport::BoxTransport; /// use cast::Cast; - /// use foundry_common::provider::alloy::get_http_provider as get_ws_provider; /// use std::{io, str::FromStr}; /// /// # async fn foo() -> eyre::Result<()> { diff --git a/crates/cheatcodes/Cargo.toml b/crates/cheatcodes/Cargo.toml index 17cddc607..a4e9a0ad8 100644 --- a/crates/cheatcodes/Cargo.toml +++ b/crates/cheatcodes/Cargo.toml @@ -27,7 +27,10 @@ alloy-sol-types.workspace = true alloy-provider.workspace = true alloy-rpc-types.workspace = true alloy-signer.workspace = true -alloy-signer-wallet = { workspace = true, features = ["mnemonic", "keystore"] } +alloy-signer-wallet = { workspace = true, features = [ + "mnemonic-all-languages", + "keystore", +] } parking_lot = "0.12" eyre.workspace = true diff --git a/crates/cheatcodes/src/evm/fork.rs b/crates/cheatcodes/src/evm/fork.rs index d2a8ff4eb..f7db4c0e9 100644 --- a/crates/cheatcodes/src/evm/fork.rs +++ b/crates/cheatcodes/src/evm/fork.rs @@ -4,7 +4,7 @@ use alloy_provider::Provider; use alloy_rpc_types::Filter; use alloy_sol_types::SolValue; use eyre::WrapErr; -use foundry_common::provider::alloy::ProviderBuilder; +use foundry_common::provider::ProviderBuilder; use foundry_compilers::utils::RuntimeOrHandle; use foundry_evm_core::fork::CreateFork; diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 68418e282..b66d262e2 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -18,7 +18,7 @@ use crate::{ use alloy_primitives::{Address, Bytes, Log, TxKind, B256, U256}; use alloy_rpc_types::request::{TransactionInput, TransactionRequest}; use alloy_sol_types::{SolInterface, SolValue}; -use foundry_common::{evm::Breakpoints, provider::alloy::RpcUrl, SELECTOR_LEN}; +use foundry_common::{evm::Breakpoints, SELECTOR_LEN}; use foundry_evm_core::{ abi::Vm::stopExpectSafeMemoryCall, backend::{DatabaseExt, RevertDiagnostic}, @@ -80,7 +80,7 @@ impl Context { #[derive(Clone, Debug, Default)] pub struct BroadcastableTransaction { /// The optional RPC URL. - pub rpc: Option, + pub rpc: Option, /// The transaction to broadcast. pub transaction: TransactionRequest, } @@ -915,7 +915,7 @@ impl Inspector for Cheatcodes { rpc: ecx.db.active_fork_url(), transaction: TransactionRequest { from: Some(broadcast.new_origin), - to: Some(TxKind::Call(call.contract)), + to: Some(TxKind::from(Some(call.contract))), value: Some(call.transfer.value), input: TransactionInput::new(call.input.clone()), nonce: Some(account.info.nonce), diff --git a/crates/cli/src/utils/mod.rs b/crates/cli/src/utils/mod.rs index cd0716175..8f751728d 100644 --- a/crates/cli/src/utils/mod.rs +++ b/crates/cli/src/utils/mod.rs @@ -81,21 +81,19 @@ pub fn abi_to_solidity(abi: &JsonAbi, name: &str) -> Result { Ok(s) } -/// Returns a [RetryProvider](foundry_common::alloy::RetryProvider) instantiated using [Config]'s +/// Returns a [RetryProvider](foundry_common::RetryProvider) instantiated using [Config]'s /// RPC -pub fn get_provider(config: &Config) -> Result { +pub fn get_provider(config: &Config) -> Result { get_provider_builder(config)?.build() } -/// Returns a [ProviderBuilder](foundry_common::provider::alloy::ProviderBuilder) instantiated using +/// Returns a [ProviderBuilder](foundry_common::provider::ProviderBuilder) instantiated using /// [Config] values. /// /// Defaults to `http://localhost:8545` and `Mainnet`. -pub fn get_provider_builder( - config: &Config, -) -> Result { +pub fn get_provider_builder(config: &Config) -> Result { let url = config.get_rpc_url_or_localhost_http()?; - let mut builder = foundry_common::provider::alloy::ProviderBuilder::new(url.as_ref()); + let mut builder = foundry_common::provider::ProviderBuilder::new(url.as_ref()); if let Ok(chain) = config.chain.unwrap_or_default().try_into() { builder = builder.chain(chain); diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 29d55fe5f..10f3cf6aa 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -15,22 +15,23 @@ foundry-compilers.workspace = true foundry-config.workspace = true foundry-linking.workspace = true -ethers-core.workspace = true -ethers-middleware.workspace = true -ethers-providers = { workspace = true, features = ["ws", "ipc"] } -ethers-signers.workspace = true -# should be removed along with ethers -reqwest_ethers = { package = "reqwest", version = "0.11", default-features = false } - alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } alloy-json-abi.workspace = true -alloy-primitives = { workspace = true, features = ["serde", "getrandom", "arbitrary", "rlp"] } +alloy-primitives = { workspace = true, features = [ + "serde", + "getrandom", + "arbitrary", + "rlp", +] } +alloy-rpc-types-engine.workspace = true alloy-rpc-types.workspace = true alloy-rpc-client.workspace = true alloy-provider.workspace = true alloy-transport.workspace = true -alloy-signer-wallet.workspace = true -alloy-transport-http = { workspace = true, features = ["reqwest", "reqwest-rustls-tls"] } +alloy-transport-http = { workspace = true, features = [ + "reqwest", + "reqwest-rustls-tls", +] } alloy-transport-ws.workspace = true alloy-transport-ipc.workspace = true alloy-json-rpc.workspace = true @@ -48,7 +49,6 @@ dunce = "1" eyre.workspace = true glob = "0.3" globset = "0.4" -hex.workspace = true once_cell = "1" reqwest.workspace = true semver = "1" @@ -68,7 +68,3 @@ num-format.workspace = true foundry-macros.workspace = true pretty_assertions.workspace = true tokio = { version = "1", features = ["rt-multi-thread", "macros"] } - -[features] -default = ["rustls"] -rustls = ["reqwest_ethers/rustls-tls-native-roots"] diff --git a/crates/common/src/ens.rs b/crates/common/src/ens.rs index 131980f55..b96ecba8d 100644 --- a/crates/common/src/ens.rs +++ b/crates/common/src/ens.rs @@ -200,6 +200,7 @@ pub fn reverse_address(addr: &Address) -> String { #[cfg(test)] mod test { use super::*; + use alloy_primitives::hex; fn assert_hex(hash: B256, val: &str) { assert_eq!(hash.0[..], hex::decode(val).unwrap()[..]); diff --git a/crates/common/src/fmt/ui.rs b/crates/common/src/fmt/ui.rs index 26db7038c..3893fb4c9 100644 --- a/crates/common/src/fmt/ui.rs +++ b/crates/common/src/fmt/ui.rs @@ -378,31 +378,6 @@ impl UIfmt for EthValue { } } -// TODO: replace these above and remove this module once types are converted -mod temp_ethers { - use super::UIfmt; - use ethers_core::types::{Address, Bloom, Bytes, H256, H64, I256, U256, U64}; - use foundry_common::types::ToAlloy; - - macro_rules! with_alloy { - ($($t:ty),*) => {$( - impl UIfmt for $t { - fn pretty(&self) -> String { - self.to_alloy().pretty() - } - } - )*}; - } - - impl UIfmt for Bytes { - fn pretty(&self) -> String { - self.clone().to_alloy().pretty() - } - } - - with_alloy!(Address, Bloom, H64, H256, I256, U256, U64); -} - /// Returns the `UiFmt::pretty()` formatted attribute of the transactions pub fn get_pretty_tx_attr(transaction: &Transaction, attr: &str) -> Option { match attr { diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index 484bc89d2..6febaa378 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -20,14 +20,12 @@ pub mod fs; pub mod glob; pub mod provider; pub mod retry; -pub mod runtime_client; pub mod selectors; pub mod serde_helpers; pub mod shell; pub mod term; pub mod traits; pub mod transactions; -pub mod types; pub use constants::*; pub use contracts::*; diff --git a/crates/common/src/provider/alloy.rs b/crates/common/src/provider/alloy.rs deleted file mode 100644 index e76a898bd..000000000 --- a/crates/common/src/provider/alloy.rs +++ /dev/null @@ -1,324 +0,0 @@ -//! Commonly used helpers to construct `Provider`s - -use crate::{ - provider::runtime_transport::RuntimeTransportBuilder, ALCHEMY_FREE_TIER_CUPS, REQUEST_TIMEOUT, -}; -use alloy_provider::{ - network::AnyNetwork, utils::Eip1559Estimation, Provider, - ProviderBuilder as AlloyProviderBuilder, RootProvider, -}; -use alloy_rpc_client::ClientBuilder; -use alloy_transport::Transport; -use ethers_middleware::gas_oracle::{GasCategory, GasOracle, Polygon}; -use eyre::{Result, WrapErr}; -use foundry_common::types::ToAlloy; -use foundry_config::NamedChain; -use reqwest::Url; -use std::{ - net::SocketAddr, - path::{Path, PathBuf}, - str::FromStr, - time::Duration, -}; -use url::ParseError; - -use super::{ - runtime_transport::RuntimeTransport, - tower::{RetryBackoffLayer, RetryBackoffService}, -}; - -/// Helper type alias for a retry provider -pub type RetryProvider = RootProvider, N>; - -/// Helper type alias for a rpc url -pub type RpcUrl = String; - -/// Constructs a provider with a 100 millisecond interval poll if it's a localhost URL (most likely -/// an anvil or other dev node) and with the default, or 7 second otherwise. -/// -/// See [`try_get_http_provider`] for more details. -/// -/// # Panics -/// -/// Panics if the URL is invalid. -/// -/// # Examples -/// -/// ``` -/// use foundry_common::provider::alloy::get_http_provider; -/// -/// let retry_provider = get_http_provider("http://localhost:8545"); -/// ``` -#[inline] -#[track_caller] -pub fn get_http_provider(builder: impl AsRef) -> RetryProvider { - try_get_http_provider(builder).unwrap() -} - -/// Constructs a provider with a 100 millisecond interval poll if it's a localhost URL (most likely -/// an anvil or other dev node) and with the default, or 7 second otherwise. -#[inline] -pub fn try_get_http_provider(builder: impl AsRef) -> Result { - ProviderBuilder::new(builder.as_ref()).build() -} - -/// Helper type to construct a `RetryProvider` -#[derive(Debug)] -pub struct ProviderBuilder { - // Note: this is a result, so we can easily chain builder calls - url: Result, - chain: NamedChain, - max_retry: u32, - timeout_retry: u32, - initial_backoff: u64, - timeout: Duration, - /// available CUPS - compute_units_per_second: u64, - /// JWT Secret - jwt: Option, - headers: Vec, -} - -// === impl ProviderBuilder === - -impl ProviderBuilder { - /// Creates a new builder instance - pub fn new(url_str: &str) -> Self { - // a copy is needed for the next lines to work - let mut url_str = url_str; - - // invalid url: non-prefixed URL scheme is not allowed, so we prepend the default http - // prefix - let storage; - if url_str.starts_with("localhost:") { - storage = format!("http://{url_str}"); - url_str = storage.as_str(); - } - - let url = Url::parse(url_str) - .or_else(|err| match err { - ParseError::RelativeUrlWithoutBase => { - if SocketAddr::from_str(url_str).is_ok() { - Url::parse(&format!("http://{}", url_str)) - } else { - let path = Path::new(url_str); - - if let Ok(path) = resolve_path(path) { - Url::parse(&format!("file://{}", path.display())) - } else { - Err(err) - } - } - } - _ => Err(err), - }) - .wrap_err_with(|| format!("invalid provider URL: {url_str:?}")); - - Self { - url, - chain: NamedChain::Mainnet, - max_retry: 8, - timeout_retry: 8, - initial_backoff: 800, - timeout: REQUEST_TIMEOUT, - // alchemy max cpus - compute_units_per_second: ALCHEMY_FREE_TIER_CUPS, - jwt: None, - headers: vec![], - } - } - - /// Enables a request timeout. - /// - /// The timeout is applied from when the request starts connecting until the - /// response body has finished. - /// - /// Default is no timeout. - pub fn timeout(mut self, timeout: Duration) -> Self { - self.timeout = timeout; - self - } - - /// Sets the chain of the node the provider will connect to - pub fn chain(mut self, chain: NamedChain) -> Self { - self.chain = chain; - self - } - - /// How often to retry a failed request - pub fn max_retry(mut self, max_retry: u32) -> Self { - self.max_retry = max_retry; - self - } - - /// How often to retry a failed request. If `None`, defaults to the already-set value. - pub fn maybe_max_retry(mut self, max_retry: Option) -> Self { - self.max_retry = max_retry.unwrap_or(self.max_retry); - self - } - - /// The starting backoff delay to use after the first failed request. If `None`, defaults to - /// the already-set value. - pub fn maybe_initial_backoff(mut self, initial_backoff: Option) -> Self { - self.initial_backoff = initial_backoff.unwrap_or(self.initial_backoff); - self - } - - /// How often to retry a failed request due to connection issues - pub fn timeout_retry(mut self, timeout_retry: u32) -> Self { - self.timeout_retry = timeout_retry; - self - } - - /// The starting backoff delay to use after the first failed request - pub fn initial_backoff(mut self, initial_backoff: u64) -> Self { - self.initial_backoff = initial_backoff; - self - } - - /// Sets the number of assumed available compute units per second - /// - /// See also, - pub fn compute_units_per_second(mut self, compute_units_per_second: u64) -> Self { - self.compute_units_per_second = compute_units_per_second; - self - } - - /// Sets the number of assumed available compute units per second - /// - /// See also, - pub fn compute_units_per_second_opt(mut self, compute_units_per_second: Option) -> Self { - if let Some(cups) = compute_units_per_second { - self.compute_units_per_second = cups; - } - self - } - - /// Sets aggressive `max_retry` and `initial_backoff` values - /// - /// This is only recommend for local dev nodes - pub fn aggressive(self) -> Self { - self.max_retry(100).initial_backoff(100) - } - - /// Sets the JWT secret - pub fn jwt(mut self, jwt: impl Into) -> Self { - self.jwt = Some(jwt.into()); - self - } - - /// Sets http headers - pub fn headers(mut self, headers: Vec) -> Self { - self.headers = headers; - - self - } - - /// Constructs the `RetryProvider` taking all configs into account. - pub fn build(self) -> Result { - let ProviderBuilder { - url, - chain: _, - max_retry, - timeout_retry, - initial_backoff, - timeout, - compute_units_per_second, - jwt, - headers, - } = self; - let url = url?; - - let retry_layer = RetryBackoffLayer::new( - max_retry, - timeout_retry, - initial_backoff, - compute_units_per_second, - ); - let transport = RuntimeTransportBuilder::new(url.clone()) - .with_timeout(timeout) - .with_headers(headers) - .with_jwt(jwt) - .build(); - let client = ClientBuilder::default().layer(retry_layer).transport(transport, false); - - let provider = AlloyProviderBuilder::<_, _, AnyNetwork>::default() - .on_provider(RootProvider::new(client)); - - Ok(provider) - } -} - -/// Estimates EIP1559 fees depending on the chain -/// -/// Uses custom gas oracles for -/// - polygon -/// -/// Fallback is the default [`Provider::estimate_eip1559_fees`] implementation -pub async fn estimate_eip1559_fees, T: Transport + Clone>( - provider: &P, - chain: Option, -) -> Result { - let chain = if let Some(chain) = chain { - chain - } else { - provider.get_chain_id().await.wrap_err("Failed to get chain id")? - }; - - if let Ok(chain) = NamedChain::try_from(chain) { - // handle chains that deviate from `eth_feeHistory` and have their own oracle - match chain { - NamedChain::Polygon | NamedChain::PolygonMumbai => { - // TODO: phase this out somehow - let chain = match chain { - NamedChain::Polygon => ethers_core::types::Chain::Polygon, - NamedChain::PolygonMumbai => ethers_core::types::Chain::PolygonMumbai, - _ => unreachable!(), - }; - let estimator = Polygon::new(chain)?.category(GasCategory::Standard); - let (a, b) = estimator.estimate_eip1559_fees().await?; - - let estimation = Eip1559Estimation { - max_fee_per_gas: a.to_alloy().to(), - max_priority_fee_per_gas: b.to_alloy().to(), - }; - return Ok(estimation) - } - _ => {} - } - } - provider.estimate_eip1559_fees(None).await.wrap_err("Failed fetch EIP1559 fees") -} - -#[cfg(not(windows))] -fn resolve_path(path: &Path) -> Result { - if path.is_absolute() { - Ok(path.to_path_buf()) - } else { - std::env::current_dir().map(|d| d.join(path)).map_err(drop) - } -} - -#[cfg(windows)] -fn resolve_path(path: &Path) -> Result { - if let Some(s) = path.to_str() { - if s.starts_with(r"\\.\pipe\") { - return Ok(path.to_path_buf()); - } - } - Err(()) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn can_auto_correct_missing_prefix() { - let builder = ProviderBuilder::new("localhost:8545"); - assert!(builder.url.is_ok()); - - let url = builder.url.unwrap(); - assert_eq!(url, Url::parse("http://localhost:8545").unwrap()); - } -} diff --git a/crates/common/src/provider/ethers.rs b/crates/common/src/provider/ethers.rs deleted file mode 100644 index 7d99763de..000000000 --- a/crates/common/src/provider/ethers.rs +++ /dev/null @@ -1,283 +0,0 @@ -//! Commonly used helpers to construct `Provider`s - -use crate::{ - runtime_client::{RuntimeClient, RuntimeClientBuilder}, - ALCHEMY_FREE_TIER_CUPS, REQUEST_TIMEOUT, -}; -use ethers_providers::{is_local_endpoint, Middleware, Provider, DEFAULT_LOCAL_POLL_INTERVAL}; -use eyre::{Result, WrapErr}; -use foundry_config::NamedChain; -use reqwest::Url; -use std::{ - path::{Path, PathBuf}, - time::Duration, -}; -use url::ParseError; - -/// Helper type alias for a retry provider -pub type RetryProvider = Provider; - -/// Helper type alias for a rpc url -pub type RpcUrl = String; - -/// Constructs a provider with a 100 millisecond interval poll if it's a localhost URL (most likely -/// an anvil or other dev node) and with the default, or 7 second otherwise. -/// -/// See [`try_get_http_provider`] for more details. -/// -/// # Panics -/// -/// Panics if the URL is invalid. -/// -/// # Examples -/// -/// ``` -/// use foundry_common::provider::ethers::get_http_provider; -/// -/// let retry_provider = get_http_provider("http://localhost:8545"); -/// ``` -#[inline] -#[track_caller] -pub fn get_http_provider(builder: impl AsRef) -> RetryProvider { - try_get_http_provider(builder).unwrap() -} - -/// Constructs a provider with a 100 millisecond interval poll if it's a localhost URL (most likely -/// an anvil or other dev node) and with the default, or 7 second otherwise. -#[inline] -pub fn try_get_http_provider(builder: impl AsRef) -> Result { - ProviderBuilder::new(builder.as_ref()).build() -} - -/// Helper type to construct a `RetryProvider` -#[derive(Debug)] -pub struct ProviderBuilder { - // Note: this is a result, so we can easily chain builder calls - url: Result, - chain: NamedChain, - max_retry: u32, - timeout_retry: u32, - initial_backoff: u64, - timeout: Duration, - /// available CUPS - compute_units_per_second: u64, - /// JWT Secret - jwt: Option, - headers: Vec, -} - -// === impl ProviderBuilder === - -impl ProviderBuilder { - /// Creates a new builder instance - pub fn new(url_str: &str) -> Self { - // a copy is needed for the next lines to work - let mut url_str = url_str; - - // invalid url: non-prefixed URL scheme is not allowed, so we prepend the default http - // prefix - let storage; - if url_str.starts_with("localhost:") || url_str.starts_with("127.0.0.1:") { - storage = format!("http://{url_str}"); - url_str = storage.as_str(); - } - - let url = Url::parse(url_str) - .or_else(|err| match err { - ParseError::RelativeUrlWithoutBase => { - let path = Path::new(url_str); - - if let Ok(path) = resolve_path(path) { - Url::parse(&format!("file://{}", path.display())) - } else { - Err(err) - } - } - _ => Err(err), - }) - .wrap_err_with(|| format!("invalid provider URL: {url_str:?}")); - - Self { - url, - chain: NamedChain::Mainnet, - max_retry: 8, - timeout_retry: 8, - initial_backoff: 800, - timeout: REQUEST_TIMEOUT, - // alchemy max cpus - compute_units_per_second: ALCHEMY_FREE_TIER_CUPS, - jwt: None, - headers: vec![], - } - } - - /// Enables a request timeout. - /// - /// The timeout is applied from when the request starts connecting until the - /// response body has finished. - /// - /// Default is no timeout. - pub fn timeout(mut self, timeout: Duration) -> Self { - self.timeout = timeout; - self - } - - /// Sets the chain of the node the provider will connect to - pub fn chain(mut self, chain: NamedChain) -> Self { - self.chain = chain; - self - } - - /// How often to retry a failed request - pub fn max_retry(mut self, max_retry: u32) -> Self { - self.max_retry = max_retry; - self - } - - /// How often to retry a failed request. If `None`, defaults to the already-set value. - pub fn maybe_max_retry(mut self, max_retry: Option) -> Self { - self.max_retry = max_retry.unwrap_or(self.max_retry); - self - } - - /// The starting backoff delay to use after the first failed request. If `None`, defaults to - /// the already-set value. - pub fn maybe_initial_backoff(mut self, initial_backoff: Option) -> Self { - self.initial_backoff = initial_backoff.unwrap_or(self.initial_backoff); - self - } - - /// How often to retry a failed request due to connection issues - pub fn timeout_retry(mut self, timeout_retry: u32) -> Self { - self.timeout_retry = timeout_retry; - self - } - - /// The starting backoff delay to use after the first failed request - pub fn initial_backoff(mut self, initial_backoff: u64) -> Self { - self.initial_backoff = initial_backoff; - self - } - - /// Sets the number of assumed available compute units per second - /// - /// See also, - pub fn compute_units_per_second(mut self, compute_units_per_second: u64) -> Self { - self.compute_units_per_second = compute_units_per_second; - self - } - - /// Sets the number of assumed available compute units per second - /// - /// See also, - pub fn compute_units_per_second_opt(mut self, compute_units_per_second: Option) -> Self { - if let Some(cups) = compute_units_per_second { - self.compute_units_per_second = cups; - } - self - } - - /// Sets aggressive `max_retry` and `initial_backoff` values - /// - /// This is only recommend for local dev nodes - pub fn aggressive(self) -> Self { - self.max_retry(100).initial_backoff(100) - } - - /// Sets the JWT secret - pub fn jwt(mut self, jwt: impl Into) -> Self { - self.jwt = Some(jwt.into()); - self - } - - /// Sets http headers - pub fn headers(mut self, headers: Vec) -> Self { - self.headers = headers; - - self - } - - /// Same as [`Self:build()`] but also retrieves the `chainId` in order to derive an appropriate - /// interval. - pub async fn connect(self) -> Result { - let mut provider = self.build()?; - if let Some(blocktime) = provider.get_chainid().await.ok().and_then(|id| { - NamedChain::try_from(id.as_u64()).ok().and_then(|chain| chain.average_blocktime_hint()) - }) { - provider = provider.interval(blocktime / 2); - } - Ok(provider) - } - - /// Constructs the `RetryProvider` taking all configs into account. - pub fn build(self) -> Result { - let ProviderBuilder { - url, - chain, - max_retry, - timeout_retry, - initial_backoff, - timeout, - compute_units_per_second, - jwt, - headers, - } = self; - let url = url?; - - let client_builder = RuntimeClientBuilder::new( - url.clone(), - max_retry, - timeout_retry, - initial_backoff, - timeout, - compute_units_per_second, - ) - .with_headers(headers) - .with_jwt(jwt); - - let mut provider = Provider::new(client_builder.build()); - - let is_local = is_local_endpoint(url.as_str()); - - if is_local { - provider = provider.interval(DEFAULT_LOCAL_POLL_INTERVAL); - } else if let Some(blocktime) = chain.average_blocktime_hint() { - provider = provider.interval(blocktime / 2); - } - - Ok(provider) - } -} - -#[cfg(not(windows))] -fn resolve_path(path: &Path) -> Result { - if path.is_absolute() { - Ok(path.to_path_buf()) - } else { - std::env::current_dir().map(|d| d.join(path)).map_err(drop) - } -} - -#[cfg(windows)] -fn resolve_path(path: &Path) -> Result { - if let Some(s) = path.to_str() { - if s.starts_with(r"\\.\pipe\") { - return Ok(path.to_path_buf()); - } - } - Err(()) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn can_auto_correct_missing_prefix() { - let builder = ProviderBuilder::new("localhost:8545"); - assert!(builder.url.is_ok()); - - let url = builder.url.unwrap(); - assert_eq!(url, Url::parse("http://localhost:8545").unwrap()); - } -} diff --git a/crates/common/src/provider/mod.rs b/crates/common/src/provider/mod.rs index cbd9ecbd0..c8e9a0ba2 100644 --- a/crates/common/src/provider/mod.rs +++ b/crates/common/src/provider/mod.rs @@ -1,7 +1,328 @@ //! Provider-related instantiation and usage utilities. -pub mod alloy; -pub mod ethers; pub mod retry; pub mod runtime_transport; pub mod tower; + +use crate::{ + provider::runtime_transport::RuntimeTransportBuilder, ALCHEMY_FREE_TIER_CUPS, REQUEST_TIMEOUT, +}; +use alloy_provider::{ + fillers::{ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller, SignerFiller}, + network::{AnyNetwork, EthereumSigner}, + Identity, ProviderBuilder as AlloyProviderBuilder, RootProvider, +}; +use alloy_rpc_client::ClientBuilder; +use eyre::{Result, WrapErr}; +use foundry_config::NamedChain; +use reqwest::Url; +use runtime_transport::RuntimeTransport; +use std::{ + net::SocketAddr, + path::{Path, PathBuf}, + str::FromStr, + time::Duration, +}; +use tower::{RetryBackoffLayer, RetryBackoffService}; +use url::ParseError; + +/// Helper type alias for a retry provider +pub type RetryProvider = RootProvider, N>; + +/// Helper type alias for a retry provider with a signer +pub type RetryProviderWithSigner = FillProvider< + JoinFill< + JoinFill, NonceFiller>, ChainIdFiller>, + SignerFiller, + >, + RootProvider, N>, + RetryBackoffService, + N, +>; + +/// Constructs a provider with a 100 millisecond interval poll if it's a localhost URL (most likely +/// an anvil or other dev node) and with the default, or 7 second otherwise. +/// +/// See [`try_get_http_provider`] for more details. +/// +/// # Panics +/// +/// Panics if the URL is invalid. +/// +/// # Examples +/// +/// ``` +/// use foundry_common::provider::get_http_provider; +/// +/// let retry_provider = get_http_provider("http://localhost:8545"); +/// ``` +#[inline] +#[track_caller] +pub fn get_http_provider(builder: impl AsRef) -> RetryProvider { + try_get_http_provider(builder).unwrap() +} + +/// Constructs a provider with a 100 millisecond interval poll if it's a localhost URL (most likely +/// an anvil or other dev node) and with the default, or 7 second otherwise. +#[inline] +pub fn try_get_http_provider(builder: impl AsRef) -> Result { + ProviderBuilder::new(builder.as_ref()).build() +} + +/// Helper type to construct a `RetryProvider` +#[derive(Debug)] +pub struct ProviderBuilder { + // Note: this is a result, so we can easily chain builder calls + url: Result, + chain: NamedChain, + max_retry: u32, + timeout_retry: u32, + initial_backoff: u64, + timeout: Duration, + /// available CUPS + compute_units_per_second: u64, + /// JWT Secret + jwt: Option, + headers: Vec, +} + +// === impl ProviderBuilder === + +impl ProviderBuilder { + /// Creates a new builder instance + pub fn new(url_str: &str) -> Self { + // a copy is needed for the next lines to work + let mut url_str = url_str; + + // invalid url: non-prefixed URL scheme is not allowed, so we prepend the default http + // prefix + let storage; + if url_str.starts_with("localhost:") { + storage = format!("http://{url_str}"); + url_str = storage.as_str(); + } + + let url = Url::parse(url_str) + .or_else(|err| match err { + ParseError::RelativeUrlWithoutBase => { + if SocketAddr::from_str(url_str).is_ok() { + Url::parse(&format!("http://{}", url_str)) + } else { + let path = Path::new(url_str); + + if let Ok(path) = resolve_path(path) { + Url::parse(&format!("file://{}", path.display())) + } else { + Err(err) + } + } + } + _ => Err(err), + }) + .wrap_err_with(|| format!("invalid provider URL: {url_str:?}")); + + Self { + url, + chain: NamedChain::Mainnet, + max_retry: 8, + timeout_retry: 8, + initial_backoff: 800, + timeout: REQUEST_TIMEOUT, + // alchemy max cpus + compute_units_per_second: ALCHEMY_FREE_TIER_CUPS, + jwt: None, + headers: vec![], + } + } + + /// Enables a request timeout. + /// + /// The timeout is applied from when the request starts connecting until the + /// response body has finished. + /// + /// Default is no timeout. + pub fn timeout(mut self, timeout: Duration) -> Self { + self.timeout = timeout; + self + } + + /// Sets the chain of the node the provider will connect to + pub fn chain(mut self, chain: NamedChain) -> Self { + self.chain = chain; + self + } + + /// How often to retry a failed request + pub fn max_retry(mut self, max_retry: u32) -> Self { + self.max_retry = max_retry; + self + } + + /// How often to retry a failed request. If `None`, defaults to the already-set value. + pub fn maybe_max_retry(mut self, max_retry: Option) -> Self { + self.max_retry = max_retry.unwrap_or(self.max_retry); + self + } + + /// The starting backoff delay to use after the first failed request. If `None`, defaults to + /// the already-set value. + pub fn maybe_initial_backoff(mut self, initial_backoff: Option) -> Self { + self.initial_backoff = initial_backoff.unwrap_or(self.initial_backoff); + self + } + + /// How often to retry a failed request due to connection issues + pub fn timeout_retry(mut self, timeout_retry: u32) -> Self { + self.timeout_retry = timeout_retry; + self + } + + /// The starting backoff delay to use after the first failed request + pub fn initial_backoff(mut self, initial_backoff: u64) -> Self { + self.initial_backoff = initial_backoff; + self + } + + /// Sets the number of assumed available compute units per second + /// + /// See also, + pub fn compute_units_per_second(mut self, compute_units_per_second: u64) -> Self { + self.compute_units_per_second = compute_units_per_second; + self + } + + /// Sets the number of assumed available compute units per second + /// + /// See also, + pub fn compute_units_per_second_opt(mut self, compute_units_per_second: Option) -> Self { + if let Some(cups) = compute_units_per_second { + self.compute_units_per_second = cups; + } + self + } + + /// Sets aggressive `max_retry` and `initial_backoff` values + /// + /// This is only recommend for local dev nodes + pub fn aggressive(self) -> Self { + self.max_retry(100).initial_backoff(100) + } + + /// Sets the JWT secret + pub fn jwt(mut self, jwt: impl Into) -> Self { + self.jwt = Some(jwt.into()); + self + } + + /// Sets http headers + pub fn headers(mut self, headers: Vec) -> Self { + self.headers = headers; + + self + } + + /// Constructs the `RetryProvider` taking all configs into account. + pub fn build(self) -> Result { + let ProviderBuilder { + url, + chain: _, + max_retry, + timeout_retry, + initial_backoff, + timeout, + compute_units_per_second, + jwt, + headers, + } = self; + let url = url?; + + let retry_layer = RetryBackoffLayer::new( + max_retry, + timeout_retry, + initial_backoff, + compute_units_per_second, + ); + let transport = RuntimeTransportBuilder::new(url.clone()) + .with_timeout(timeout) + .with_headers(headers) + .with_jwt(jwt) + .build(); + let client = ClientBuilder::default().layer(retry_layer).transport(transport, false); + + let provider = AlloyProviderBuilder::<_, _, AnyNetwork>::default() + .on_provider(RootProvider::new(client)); + + Ok(provider) + } + + /// Constructs the `RetryProvider` with a signer + pub fn build_with_signer(self, signer: EthereumSigner) -> Result { + let ProviderBuilder { + url, + chain: _, + max_retry, + timeout_retry, + initial_backoff, + timeout, + compute_units_per_second, + jwt, + headers, + } = self; + let url = url?; + + let retry_layer = RetryBackoffLayer::new( + max_retry, + timeout_retry, + initial_backoff, + compute_units_per_second, + ); + + let transport = RuntimeTransportBuilder::new(url.clone()) + .with_timeout(timeout) + .with_headers(headers) + .with_jwt(jwt) + .build(); + + let client = ClientBuilder::default().layer(retry_layer).transport(transport, false); + + let provider = AlloyProviderBuilder::<_, _, AnyNetwork>::default() + .with_recommended_fillers() + .signer(signer) + .on_provider(RootProvider::new(client)); + + Ok(provider) + } +} + +#[cfg(not(windows))] +fn resolve_path(path: &Path) -> Result { + if path.is_absolute() { + Ok(path.to_path_buf()) + } else { + std::env::current_dir().map(|d| d.join(path)).map_err(drop) + } +} + +#[cfg(windows)] +fn resolve_path(path: &Path) -> Result { + if let Some(s) = path.to_str() { + if s.starts_with(r"\\.\pipe\") { + return Ok(path.to_path_buf()); + } + } + Err(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_auto_correct_missing_prefix() { + let builder = ProviderBuilder::new("localhost:8545"); + assert!(builder.url.is_ok()); + + let url = builder.url.unwrap(); + assert_eq!(url, Url::parse("http://localhost:8545").unwrap()); + } +} diff --git a/crates/common/src/provider/runtime_transport.rs b/crates/common/src/provider/runtime_transport.rs index 48411a321..540214891 100644 --- a/crates/common/src/provider/runtime_transport.rs +++ b/crates/common/src/provider/runtime_transport.rs @@ -3,13 +3,13 @@ use crate::REQUEST_TIMEOUT; use alloy_json_rpc::{RequestPacket, ResponsePacket}; use alloy_pubsub::{PubSubConnect, PubSubFrontend}; +use alloy_rpc_types_engine::{Claims, JwtSecret}; use alloy_transport::{ Authorization, BoxTransport, TransportError, TransportErrorKind, TransportFut, }; use alloy_transport_http::Http; use alloy_transport_ipc::IpcConnect; use alloy_transport_ws::WsConnect; -use ethers_providers::{JwtAuth, JwtKey}; use reqwest::header::{HeaderName, HeaderValue}; use std::{path::PathBuf, str::FromStr, sync::Arc}; use thiserror::Error; @@ -33,8 +33,8 @@ pub enum InnerTransport { #[derive(Error, Debug)] pub enum RuntimeTransportError { /// Internal transport error - #[error(transparent)] - TransportError(TransportError), + #[error("Internal transport error: {0} with {1}")] + TransportError(TransportError, String), /// Failed to lock the transport #[error("Failed to lock the transport")] @@ -187,7 +187,7 @@ impl RuntimeTransport { let ws = WsConnect { url: self.url.to_string(), auth } .into_service() .await - .map_err(RuntimeTransportError::TransportError)?; + .map_err(|e| RuntimeTransportError::TransportError(e, self.url.to_string()))?; Ok(InnerTransport::Ws(ws)) } @@ -195,9 +195,10 @@ impl RuntimeTransport { async fn connect_ipc(&self) -> Result { let path = url_to_file_path(&self.url) .map_err(|_| RuntimeTransportError::BadPath(self.url.to_string()))?; - let ipc_connector: IpcConnect = path.into(); - let ipc = - ipc_connector.into_service().await.map_err(RuntimeTransportError::TransportError)?; + let ipc_connector: IpcConnect = path.clone().into(); + let ipc = ipc_connector.into_service().await.map_err(|e| { + RuntimeTransportError::TransportError(e, path.clone().display().to_string()) + })?; Ok(InnerTransport::Ipc(ipc)) } @@ -292,10 +293,9 @@ impl tower::Service for &RuntimeTransport { fn build_auth(jwt: String) -> eyre::Result { // Decode jwt from hex, then generate claims (iat with current timestamp) - let jwt = hex::decode(jwt)?; - let secret = JwtKey::from_slice(&jwt).map_err(|err| eyre::eyre!("Invalid JWT: {}", err))?; - let auth = JwtAuth::new(secret, None, None); - let token = auth.generate_token()?; + let secret = JwtSecret::from_hex(jwt)?; + let claims = Claims::default(); + let token = secret.encode(&claims)?; let auth = Authorization::Bearer(token); diff --git a/crates/common/src/runtime_client.rs b/crates/common/src/runtime_client.rs deleted file mode 100644 index 3bb1631d5..000000000 --- a/crates/common/src/runtime_client.rs +++ /dev/null @@ -1,337 +0,0 @@ -//! Wrap different providers -// todo: remove -use async_trait::async_trait; -use ethers_core::types::U256; -use ethers_providers::{ - Authorization, ConnectionDetails, Http, HttpRateLimitRetryPolicy, Ipc, JsonRpcClient, - JsonRpcError, JwtAuth, JwtKey, ProviderError, PubsubClient, RetryClient, RetryClientBuilder, - RpcError, Ws, -}; -use reqwest_ethers::{ - header::{HeaderName, HeaderValue}, - Url, -}; -use serde::{de::DeserializeOwned, Serialize}; -use std::{fmt::Debug, path::PathBuf, str::FromStr, sync::Arc, time::Duration}; -use thiserror::Error; -use tokio::sync::RwLock; - -/// Enum representing a the client types supported by the runtime provider -#[derive(Debug)] -enum InnerClient { - /// HTTP client - Http(RetryClient), - /// WebSocket client - Ws(Ws), - /// IPC client - Ipc(Ipc), -} - -/// Error type for the runtime provider -#[derive(Debug, Error)] -pub enum RuntimeClientError { - /// Internal provider error - #[error(transparent)] - ProviderError(ProviderError), - - /// Failed to lock the client - #[error("Failed to lock the client")] - LockError, - - /// Invalid URL scheme - #[error("URL scheme is not supported: {0}")] - BadScheme(String), - - /// Invalid HTTP header - #[error("Invalid HTTP header: {0}")] - BadHeader(String), - - /// Invalid file path - #[error("Invalid IPC file path: {0}")] - BadPath(String), -} - -impl RpcError for RuntimeClientError { - fn as_error_response(&self) -> Option<&JsonRpcError> { - match self { - RuntimeClientError::ProviderError(err) => err.as_error_response(), - _ => None, - } - } - - fn as_serde_error(&self) -> Option<&serde_json::Error> { - match self { - RuntimeClientError::ProviderError(e) => e.as_serde_error(), - _ => None, - } - } -} - -impl From for ProviderError { - fn from(src: RuntimeClientError) -> Self { - match src { - RuntimeClientError::ProviderError(err) => err, - _ => ProviderError::JsonRpcClientError(Box::new(src)), - } - } -} - -/// A provider that connects on first request allowing handling of different provider types at -/// runtime -#[derive(Clone, Debug, Error)] -pub struct RuntimeClient { - client: Arc>>, - url: Url, - max_retry: u32, - timeout_retry: u32, - initial_backoff: u64, - timeout: Duration, - /// available CUPS - compute_units_per_second: u64, - jwt: Option, - headers: Vec, -} - -/// Builder for RuntimeClient -pub struct RuntimeClientBuilder { - url: Url, - max_retry: u32, - timeout_retry: u32, - initial_backoff: u64, - timeout: Duration, - /// available CUPS - compute_units_per_second: u64, - jwt: Option, - headers: Vec, -} - -impl ::core::fmt::Display for RuntimeClient { - fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - write!(f, "RuntimeClient") - } -} - -fn build_auth(jwt: String) -> eyre::Result { - // Decode jwt from hex, then generate claims (iat with current timestamp) - let jwt = hex::decode(jwt)?; - let secret = JwtKey::from_slice(&jwt).map_err(|err| eyre::eyre!("Invalid JWT: {}", err))?; - let auth = JwtAuth::new(secret, None, None); - let token = auth.generate_token()?; - - // Essentially unrolled ethers-rs new_with_auth to accommodate the custom timeout - let auth = Authorization::Bearer(token); - - Ok(auth) -} - -impl RuntimeClient { - async fn connect(&self) -> Result { - match self.url.scheme() { - "http" | "https" => { - let mut client_builder = reqwest_ethers::Client::builder() - .timeout(self.timeout) - .tls_built_in_root_certs(self.url.scheme() == "https"); - let mut headers = reqwest_ethers::header::HeaderMap::new(); - - if let Some(jwt) = self.jwt.as_ref() { - let auth = build_auth(jwt.clone()).map_err(|err| { - RuntimeClientError::ProviderError(ProviderError::CustomError( - err.to_string(), - )) - })?; - - let mut auth_value: HeaderValue = HeaderValue::from_str(&auth.to_string()) - .expect("Header should be valid string"); - auth_value.set_sensitive(true); - - headers.insert(reqwest_ethers::header::AUTHORIZATION, auth_value); - }; - - for header in self.headers.iter() { - let make_err = || RuntimeClientError::BadHeader(header.to_string()); - - let (key, val) = header.split_once(':').ok_or_else(make_err)?; - - headers.insert( - HeaderName::from_str(key.trim()).map_err(|_| make_err())?, - HeaderValue::from_str(val.trim()).map_err(|_| make_err())?, - ); - } - - client_builder = client_builder.default_headers(headers); - - let client = client_builder - .build() - .map_err(|e| RuntimeClientError::ProviderError(e.into()))?; - let provider = Http::new_with_client(self.url.clone(), client); - - #[allow(clippy::box_default)] - let provider = RetryClientBuilder::default() - .initial_backoff(Duration::from_millis(self.initial_backoff)) - .rate_limit_retries(self.max_retry) - .timeout_retries(self.timeout_retry) - .compute_units_per_second(self.compute_units_per_second) - .build(provider, Box::new(HttpRateLimitRetryPolicy)); - Ok(InnerClient::Http(provider)) - } - "ws" | "wss" => { - let auth: Option = - self.jwt.as_ref().and_then(|jwt| build_auth(jwt.clone()).ok()); - let connection_details = ConnectionDetails::new(self.url.as_str(), auth); - - let client = - Ws::connect_with_reconnects(connection_details, self.max_retry as usize) - .await - .map_err(|e| RuntimeClientError::ProviderError(e.into()))?; - - Ok(InnerClient::Ws(client)) - } - "file" => { - let path = url_to_file_path(&self.url) - .map_err(|_| RuntimeClientError::BadPath(self.url.to_string()))?; - - let client = Ipc::connect(path) - .await - .map_err(|e| RuntimeClientError::ProviderError(e.into()))?; - - Ok(InnerClient::Ipc(client)) - } - _ => Err(RuntimeClientError::BadScheme(self.url.to_string())), - } - } -} - -impl RuntimeClientBuilder { - /// Create new RuntimeClientBuilder - pub fn new( - url: Url, - max_retry: u32, - timeout_retry: u32, - initial_backoff: u64, - timeout: Duration, - compute_units_per_second: u64, - ) -> Self { - Self { - url, - max_retry, - timeout, - timeout_retry, - initial_backoff, - compute_units_per_second, - jwt: None, - headers: vec![], - } - } - - /// Set jwt to use with RuntimeClient - pub fn with_jwt(mut self, jwt: Option) -> Self { - self.jwt = jwt; - self - } - - /// Set http headers to use with RuntimeClient - /// Only works with http/https schemas - pub fn with_headers(mut self, headers: Vec) -> Self { - self.headers = headers; - self - } - - /// Builds RuntimeClient instance - pub fn build(self) -> RuntimeClient { - RuntimeClient { - client: Arc::new(RwLock::new(None)), - url: self.url, - max_retry: self.max_retry, - timeout_retry: self.timeout_retry, - initial_backoff: self.initial_backoff, - timeout: self.timeout, - compute_units_per_second: self.compute_units_per_second, - jwt: self.jwt, - headers: self.headers, - } - } -} - -#[cfg(windows)] -fn url_to_file_path(url: &Url) -> Result { - const PREFIX: &str = "file:///pipe/"; - - let url_str = url.as_str(); - - if url_str.starts_with(PREFIX) { - let pipe_name = &url_str[PREFIX.len()..]; - let pipe_path = format!(r"\\.\pipe\{}", pipe_name); - return Ok(PathBuf::from(pipe_path)); - } - - url.to_file_path() -} - -#[cfg(not(windows))] -fn url_to_file_path(url: &Url) -> Result { - url.to_file_path() -} - -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait)] -impl JsonRpcClient for RuntimeClient { - type Error = RuntimeClientError; - - async fn request(&self, method: &str, params: T) -> Result - where - T: Debug + Serialize + Send + Sync, - R: DeserializeOwned + Send, - { - if self.client.read().await.is_none() { - let mut w = self.client.write().await; - *w = Some( - self.connect().await.map_err(|e| RuntimeClientError::ProviderError(e.into()))?, - ); - } - - let res = match self.client.read().await.as_ref().unwrap() { - InnerClient::Http(http) => RetryClient::request(http, method, params) - .await - .map_err(|e| RuntimeClientError::ProviderError(e.into())), - InnerClient::Ws(ws) => JsonRpcClient::request(ws, method, params) - .await - .map_err(|e| RuntimeClientError::ProviderError(e.into())), - InnerClient::Ipc(ipc) => JsonRpcClient::request(ipc, method, params) - .await - .map_err(|e| RuntimeClientError::ProviderError(e.into())), - }?; - Ok(res) - } -} - -// We can also implement [`PubsubClient`] for our dynamic provider. -impl PubsubClient for RuntimeClient { - // Since both `Ws` and `Ipc`'s `NotificationStream` associated type is the same, - // we can simply return one of them. - type NotificationStream = ::NotificationStream; - - fn subscribe>(&self, id: T) -> Result { - match self.client.try_read().map_err(|_| RuntimeClientError::LockError)?.as_ref().unwrap() { - InnerClient::Http(_) => { - Err(RuntimeClientError::ProviderError(ProviderError::UnsupportedRPC)) - } - InnerClient::Ws(client) => Ok(PubsubClient::subscribe(client, id) - .map_err(|e| RuntimeClientError::ProviderError(e.into()))?), - InnerClient::Ipc(client) => Ok(PubsubClient::subscribe(client, id) - .map_err(|e| RuntimeClientError::ProviderError(e.into()))?), - } - } - - fn unsubscribe>(&self, id: T) -> Result<(), Self::Error> { - match self.client.try_read().map_err(|_| (RuntimeClientError::LockError))?.as_ref().unwrap() - { - InnerClient::Http(_) => { - Err(RuntimeClientError::ProviderError(ProviderError::UnsupportedRPC)) - } - InnerClient::Ws(client) => Ok(PubsubClient::unsubscribe(client, id) - .map_err(|e| RuntimeClientError::ProviderError(e.into()))?), - InnerClient::Ipc(client) => Ok(PubsubClient::unsubscribe(client, id) - .map_err(|e| RuntimeClientError::ProviderError(e.into()))?), - } - } -} diff --git a/crates/common/src/types.rs b/crates/common/src/types.rs deleted file mode 100644 index bcfed539f..000000000 --- a/crates/common/src/types.rs +++ /dev/null @@ -1,210 +0,0 @@ -//! Temporary utility conversion traits between ethers-rs and alloy types. - -use alloy_primitives::{Address, Bloom, Bytes, B256, B64, I256, U256, U64}; -use alloy_rpc_types::{AccessList, AccessListItem, BlockNumberOrTag}; -use alloy_signer_wallet::LocalWallet; -use ethers_core::types::{ - transaction::eip2930::{ - AccessList as EthersAccessList, AccessListItem as EthersAccessListItem, - }, - BlockNumber, Bloom as EthersBloom, Bytes as EthersBytes, H160, H256, H64, I256 as EthersI256, - U256 as EthersU256, U64 as EthersU64, -}; - -/// Conversion trait to easily convert from Ethers types to Alloy types. -pub trait ToAlloy { - /// The corresponding Alloy type. - type To; - - /// Converts the Ethers type to the corresponding Alloy type. - fn to_alloy(self) -> Self::To; -} - -impl ToAlloy for EthersBytes { - type To = Bytes; - - #[inline(always)] - fn to_alloy(self) -> Self::To { - Bytes(self.0) - } -} - -impl ToAlloy for H64 { - type To = B64; - - #[inline(always)] - fn to_alloy(self) -> Self::To { - B64::new(self.0) - } -} - -impl ToAlloy for H160 { - type To = Address; - - #[inline(always)] - fn to_alloy(self) -> Self::To { - Address::new(self.0) - } -} - -impl ToAlloy for H256 { - type To = B256; - - #[inline(always)] - fn to_alloy(self) -> Self::To { - B256::new(self.0) - } -} - -impl ToAlloy for EthersBloom { - type To = Bloom; - - #[inline(always)] - fn to_alloy(self) -> Self::To { - Bloom::new(self.0) - } -} - -impl ToAlloy for EthersU256 { - type To = U256; - - #[inline(always)] - fn to_alloy(self) -> Self::To { - U256::from_limbs(self.0) - } -} - -impl ToAlloy for EthersI256 { - type To = I256; - - #[inline(always)] - fn to_alloy(self) -> Self::To { - I256::from_raw(self.into_raw().to_alloy()) - } -} - -impl ToAlloy for EthersU64 { - type To = U64; - - #[inline(always)] - fn to_alloy(self) -> Self::To { - U64::from_limbs(self.0) - } -} - -impl ToAlloy for u64 { - type To = U256; - - #[inline(always)] - fn to_alloy(self) -> Self::To { - U256::from(self) - } -} - -impl ToEthers for alloy_signer_wallet::LocalWallet { - type To = ethers_signers::LocalWallet; - - fn to_ethers(self) -> Self::To { - ethers_signers::LocalWallet::new_with_signer( - self.signer().clone(), - self.address().to_ethers(), - self.chain_id().unwrap(), - ) - } -} - -impl ToEthers for Vec { - type To = Vec; - - fn to_ethers(self) -> Self::To { - self.into_iter().map(ToEthers::to_ethers).collect() - } -} - -impl ToAlloy for EthersAccessList { - type To = AccessList; - fn to_alloy(self) -> Self::To { - AccessList(self.0.into_iter().map(ToAlloy::to_alloy).collect()) - } -} - -impl ToAlloy for EthersAccessListItem { - type To = AccessListItem; - - fn to_alloy(self) -> Self::To { - AccessListItem { - address: self.address.to_alloy(), - storage_keys: self.storage_keys.into_iter().map(ToAlloy::to_alloy).collect(), - } - } -} - -/// Conversion trait to easily convert from Alloy types to Ethers types. -pub trait ToEthers { - /// The corresponding Ethers type. - type To; - - /// Converts the Alloy type to the corresponding Ethers type. - fn to_ethers(self) -> Self::To; -} - -impl ToEthers for Address { - type To = H160; - - #[inline(always)] - fn to_ethers(self) -> Self::To { - H160(self.0 .0) - } -} - -impl ToEthers for B256 { - type To = H256; - - #[inline(always)] - fn to_ethers(self) -> Self::To { - H256(self.0) - } -} - -impl ToEthers for U256 { - type To = EthersU256; - - #[inline(always)] - fn to_ethers(self) -> Self::To { - EthersU256(self.into_limbs()) - } -} - -impl ToEthers for U64 { - type To = EthersU64; - - #[inline(always)] - fn to_ethers(self) -> Self::To { - EthersU64(self.into_limbs()) - } -} - -impl ToEthers for Bytes { - type To = EthersBytes; - - #[inline(always)] - fn to_ethers(self) -> Self::To { - EthersBytes(self.0) - } -} - -impl ToEthers for BlockNumberOrTag { - type To = BlockNumber; - - #[inline(always)] - fn to_ethers(self) -> Self::To { - match self { - BlockNumberOrTag::Number(n) => BlockNumber::Number(n.into()), - BlockNumberOrTag::Earliest => BlockNumber::Earliest, - BlockNumberOrTag::Latest => BlockNumber::Latest, - BlockNumberOrTag::Pending => BlockNumber::Pending, - BlockNumberOrTag::Finalized => BlockNumber::Finalized, - BlockNumberOrTag::Safe => BlockNumber::Safe, - } - } -} diff --git a/crates/evm/core/src/fork/backend.rs b/crates/evm/core/src/fork/backend.rs index 5daa4e96c..db5911884 100644 --- a/crates/evm/core/src/fork/backend.rs +++ b/crates/evm/core/src/fork/backend.rs @@ -698,7 +698,7 @@ mod tests { fork::{BlockchainDbMeta, CreateFork, JsonBlockCacheDB}, opts::EvmOpts, }; - use foundry_common::provider::alloy::get_http_provider; + use foundry_common::provider::get_http_provider; use foundry_config::{Config, NamedChain}; use std::{collections::BTreeSet, path::PathBuf}; diff --git a/crates/evm/core/src/fork/database.rs b/crates/evm/core/src/fork/database.rs index 649291a04..4b1e3d6b0 100644 --- a/crates/evm/core/src/fork/database.rs +++ b/crates/evm/core/src/fork/database.rs @@ -267,7 +267,7 @@ impl DatabaseRef for ForkDbSnapshot { mod tests { use super::*; use crate::fork::BlockchainDbMeta; - use foundry_common::provider::alloy::get_http_provider; + use foundry_common::provider::get_http_provider; use std::collections::BTreeSet; /// Demonstrates that `Database::basic` for `ForkedDatabase` will always return the diff --git a/crates/evm/core/src/fork/multi.rs b/crates/evm/core/src/fork/multi.rs index 60096784c..da1f980ff 100644 --- a/crates/evm/core/src/fork/multi.rs +++ b/crates/evm/core/src/fork/multi.rs @@ -5,9 +5,7 @@ use crate::fork::{BackendHandler, BlockchainDb, BlockchainDbMeta, CreateFork, SharedBackend}; use foundry_common::provider::{ - alloy::{ProviderBuilder, RetryProvider}, - runtime_transport::RuntimeTransport, - tower::RetryBackoffService, + runtime_transport::RuntimeTransport, tower::RetryBackoffService, ProviderBuilder, RetryProvider, }; use foundry_config::Config; use futures::{ diff --git a/crates/evm/core/src/opts.rs b/crates/evm/core/src/opts.rs index fcd92dd6a..682e2444f 100644 --- a/crates/evm/core/src/opts.rs +++ b/crates/evm/core/src/opts.rs @@ -4,10 +4,7 @@ use alloy_primitives::{Address, B256, U256}; use alloy_provider::Provider; use alloy_rpc_types::Block; use eyre::WrapErr; -use foundry_common::{ - provider::alloy::{ProviderBuilder, RpcUrl}, - ALCHEMY_FREE_TIER_CUPS, -}; +use foundry_common::{provider::ProviderBuilder, ALCHEMY_FREE_TIER_CUPS}; use foundry_compilers::utils::RuntimeOrHandle; use foundry_config::{Chain, Config}; use revm::primitives::{BlockEnv, CfgEnv, TxEnv}; @@ -21,7 +18,7 @@ pub struct EvmOpts { /// Fetch state over a remote instead of starting from empty state. #[serde(rename = "eth_rpc_url")] - pub fork_url: Option, + pub fork_url: Option, /// Pins the block number for the state fork. pub fork_block_number: Option, diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 05a7f75f5..9d7b2777d 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -15,7 +15,11 @@ name = "forge" path = "bin/main.rs" [build-dependencies] -vergen = { workspace = true, default-features = false, features = ["build", "git", "gitcl"] } +vergen = { workspace = true, default-features = false, features = [ + "build", + "git", + "gitcl", +] } [dependencies] # lib @@ -27,7 +31,7 @@ foundry-evm.workspace = true foundry-wallets.workspace = true foundry-linking.workspace = true -ethers-contract.workspace = true +ethers-contract = { workspace = true, features = ["abigen"] } revm-inspectors.workspace = true @@ -105,11 +109,12 @@ globset = "0.4" paste = "1.0" path-slash = "0.2" pretty_assertions.workspace = true -svm = { package = "svm-rs", version = "0.5", default-features = false, features = ["rustls"] } +svm = { package = "svm-rs", version = "0.5", default-features = false, features = [ + "rustls", +] } tempfile.workspace = true tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] } -ethers-core.workspace = true alloy-signer-wallet.workspace = true [features] diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index dfa1c003e..46260ddef 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -17,7 +17,6 @@ use foundry_cli::{ use foundry_common::{ compile::{self}, fmt::parse_tokens, - provider::alloy::estimate_eip1559_fees, }; use foundry_compilers::{artifacts::BytecodeObject, info::ContractInfo, utils::canonicalize}; use serde_json::json; @@ -262,9 +261,7 @@ impl CreateArgs { }; deployer.tx.set_gas_price(gas_price); } else { - let estimate = estimate_eip1559_fees(&provider, Some(chain)) - .await - .wrap_err("Failed to estimate EIP1559 fees. This chain might not support EIP1559, try adding --legacy to your command.")?; + let estimate = provider.estimate_eip1559_fees(None).await.wrap_err("Failed to estimate EIP1559 fees. This chain might not support EIP1559, try adding --legacy to your command.")?; let priority_fee = if let Some(priority_fee) = self.tx.priority_gas_price { priority_fee.to() } else { diff --git a/crates/forge/tests/cli/utils.rs b/crates/forge/tests/cli/utils.rs index e8e79fa68..75523e50a 100644 --- a/crates/forge/tests/cli/utils.rs +++ b/crates/forge/tests/cli/utils.rs @@ -1,8 +1,8 @@ //! Various helper functions +use alloy_chains::NamedChain; +use alloy_primitives::Address; use alloy_signer_wallet::LocalWallet; -use ethers_core::types::{Address, Chain}; -use foundry_common::types::ToEthers; /// Returns the current millis since unix epoch. /// @@ -14,12 +14,12 @@ pub fn millis_since_epoch() -> u128 { .as_millis() } -pub fn etherscan_key(chain: Chain) -> Option { +pub fn etherscan_key(chain: NamedChain) -> Option { match chain { - Chain::Fantom | Chain::FantomTestnet => { + NamedChain::Fantom | NamedChain::FantomTestnet => { std::env::var("FTMSCAN_API_KEY").or_else(|_| std::env::var("FANTOMSCAN_API_KEY")).ok() } - Chain::OptimismKovan => std::env::var("OP_KOVAN_API_KEY").ok(), + NamedChain::OptimismKovan => std::env::var("OP_KOVAN_API_KEY").ok(), _ => std::env::var("ETHERSCAN_API_KEY").ok(), } } @@ -36,76 +36,75 @@ pub fn network_private_key(chain: &str) -> Option { /// Represents external input required for executing verification requests pub struct EnvExternalities { - pub chain: Chain, + pub chain: NamedChain, pub rpc: String, pub pk: String, pub etherscan: String, pub verifier: String, } -#[allow(dead_code)] impl EnvExternalities { pub fn address(&self) -> Option
{ let pk: LocalWallet = self.pk.parse().ok()?; - Some(pk.address().to_ethers()) + Some(pk.address()) } pub fn goerli() -> Option { Some(Self { - chain: Chain::Goerli, + chain: NamedChain::Goerli, rpc: network_rpc_key("goerli")?, pk: network_private_key("goerli")?, - etherscan: etherscan_key(Chain::Goerli)?, + etherscan: etherscan_key(NamedChain::Goerli)?, verifier: "etherscan".to_string(), }) } pub fn ftm_testnet() -> Option { Some(Self { - chain: Chain::FantomTestnet, + chain: NamedChain::FantomTestnet, rpc: network_rpc_key("ftm_testnet")?, pk: network_private_key("ftm_testnet")?, - etherscan: etherscan_key(Chain::FantomTestnet)?, + etherscan: etherscan_key(NamedChain::FantomTestnet)?, verifier: "etherscan".to_string(), }) } pub fn optimism_kovan() -> Option { Some(Self { - chain: Chain::OptimismKovan, + chain: NamedChain::OptimismKovan, rpc: network_rpc_key("op_kovan")?, pk: network_private_key("op_kovan")?, - etherscan: etherscan_key(Chain::OptimismKovan)?, + etherscan: etherscan_key(NamedChain::OptimismKovan)?, verifier: "etherscan".to_string(), }) } pub fn arbitrum_goerli() -> Option { Some(Self { - chain: Chain::ArbitrumGoerli, + chain: NamedChain::ArbitrumGoerli, rpc: network_rpc_key("arbitrum-goerli")?, pk: network_private_key("arbitrum-goerli")?, - etherscan: etherscan_key(Chain::ArbitrumGoerli)?, + etherscan: etherscan_key(NamedChain::ArbitrumGoerli)?, verifier: "blockscout".to_string(), }) } pub fn mumbai() -> Option { Some(Self { - chain: Chain::PolygonMumbai, + chain: NamedChain::PolygonMumbai, rpc: network_rpc_key("mumbai")?, pk: network_private_key("mumbai")?, - etherscan: etherscan_key(Chain::PolygonMumbai)?, + etherscan: etherscan_key(NamedChain::PolygonMumbai)?, verifier: "etherscan".to_string(), }) } pub fn sepolia() -> Option { Some(Self { - chain: Chain::Sepolia, + chain: NamedChain::Sepolia, rpc: network_rpc_key("sepolia")?, pk: network_private_key("sepolia")?, - etherscan: etherscan_key(Chain::Sepolia)?, + etherscan: etherscan_key(NamedChain::Sepolia)?, verifier: "etherscan".to_string(), }) } diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index cd4decfcb..a278e44d7 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -4,11 +4,11 @@ use crate::{ ScriptConfig, }; use alloy_chains::Chain; -use alloy_eips::{eip2718::Encodable2718, BlockId}; +use alloy_eips::eip2718::Encodable2718; use alloy_network::{AnyNetwork, EthereumSigner, TransactionBuilder}; use alloy_primitives::{utils::format_units, Address, TxHash}; use alloy_provider::{utils::Eip1559Estimation, Provider}; -use alloy_rpc_types::{TransactionRequest, WithOtherFields}; +use alloy_rpc_types::{BlockId, TransactionRequest, WithOtherFields}; use alloy_transport::Transport; use eyre::{bail, Context, Result}; use forge_verify::provider::VerificationProviderType; @@ -18,9 +18,7 @@ use foundry_cli::{ utils::{has_batch_support, has_different_gas_calc}, }; use foundry_common::{ - provider::alloy::{ - estimate_eip1559_fees, get_http_provider, try_get_http_provider, RetryProvider, - }, + provider::{get_http_provider, try_get_http_provider, RetryProvider}, shell, }; use foundry_config::Config; @@ -258,9 +256,7 @@ impl BundledState { }), ), (false, _, _) => { - let mut fees = estimate_eip1559_fees(&provider, Some(sequence.chain)) - .await - .wrap_err("Failed to estimate EIP1559 fees. This chain might not support EIP1559, try adding --legacy to your command.")?; + let mut fees = provider.estimate_eip1559_fees(None).await.wrap_err("Failed to estimate EIP1559 fees. This chain might not support EIP1559, try adding --legacy to your command.")?; if let Some(gas_price) = self.args.with_gas_price { fees.max_fee_per_gas = gas_price.to(); diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index 99c1ff7a7..06d76af74 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -12,7 +12,7 @@ use eyre::{OptionExt, Result}; use foundry_cheatcodes::ScriptWallets; use foundry_common::{ compile::{ContractSources, ProjectCompiler}, - provider::alloy::try_get_http_provider, + provider::try_get_http_provider, ContractData, ContractsByArtifact, }; use foundry_compilers::{ diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index 66d44da0e..ea307de5b 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -16,7 +16,7 @@ use foundry_cheatcodes::ScriptWallets; use foundry_cli::utils::{ensure_clean_constructor, needs_setup}; use foundry_common::{ fmt::{format_token, format_token_raw}, - provider::alloy::{get_http_provider, RpcUrl}, + provider::get_http_provider, shell, ContractData, ContractsByArtifact, }; use foundry_config::{Config, NamedChain}; @@ -225,7 +225,7 @@ impl PreExecutionState { /// Container for information about RPC-endpoints used during script execution. pub struct RpcData { /// Unique list of rpc urls present. - pub total_rpcs: HashSet, + pub total_rpcs: HashSet, /// If true, one of the transactions did not have a rpc. pub missing_rpc: bool, } diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 9500c4d71..6445b8450 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -19,7 +19,6 @@ use foundry_common::{ abi::{encode_function_args, get_func}, compile::SkipBuildFilter, evm::{Breakpoints, EvmArgs}, - provider::alloy::RpcUrl, shell, ContractsByArtifact, CONTRACT_MAX_SIZE, SELECTOR_LEN, }; use foundry_compilers::ArtifactId; @@ -513,7 +512,7 @@ pub struct ScriptConfig { pub evm_opts: EvmOpts, pub sender_nonce: u64, /// Maps a rpc url to a backend - pub backends: HashMap, + pub backends: HashMap, } impl ScriptConfig { diff --git a/crates/script/src/providers.rs b/crates/script/src/providers.rs index ab2ee9911..aeb94fddf 100644 --- a/crates/script/src/providers.rs +++ b/crates/script/src/providers.rs @@ -1,6 +1,6 @@ use alloy_provider::{utils::Eip1559Estimation, Provider}; use eyre::{Result, WrapErr}; -use foundry_common::provider::alloy::{get_http_provider, RetryProvider, RpcUrl}; +use foundry_common::provider::{get_http_provider, RetryProvider}; use foundry_config::Chain; use std::{ collections::{hash_map::Entry, HashMap}, @@ -11,7 +11,7 @@ use std::{ /// Contains a map of RPC urls to single instances of [`ProviderInfo`]. #[derive(Default)] pub struct ProvidersManager { - pub inner: HashMap, + pub inner: HashMap, } impl ProvidersManager { @@ -32,7 +32,7 @@ impl ProvidersManager { } impl Deref for ProvidersManager { - type Target = HashMap; + type Target = HashMap; fn deref(&self) -> &Self::Target { &self.inner diff --git a/crates/script/src/receipts.rs b/crates/script/src/receipts.rs index 3bdc228f0..5686f18f6 100644 --- a/crates/script/src/receipts.rs +++ b/crates/script/src/receipts.rs @@ -5,7 +5,7 @@ use alloy_provider::{PendingTransactionBuilder, Provider}; use alloy_rpc_types::AnyTransactionReceipt; use eyre::Result; use foundry_cli::{init_progress, update_progress}; -use foundry_common::provider::alloy::RetryProvider; +use foundry_common::provider::RetryProvider; use futures::StreamExt; use std::sync::Arc; diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index 37926e308..a27506810 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -17,7 +17,7 @@ use alloy_primitives::{utils::format_units, Address, TxKind, U256}; use eyre::{Context, Result}; use foundry_cheatcodes::{BroadcastableTransactions, ScriptWallets}; use foundry_cli::utils::{has_different_gas_calc, now}; -use foundry_common::{get_contract_name, provider::alloy::RpcUrl, shell, ContractData}; +use foundry_common::{get_contract_name, shell, ContractData}; use foundry_evm::traces::render_trace_arena; use futures::future::{join_all, try_join_all}; use parking_lot::RwLock; @@ -199,7 +199,7 @@ impl PreSimulationState { } /// Build [ScriptRunner] forking given RPC for each RPC used in the script. - async fn build_runners(&self) -> Result> { + async fn build_runners(&self) -> Result> { let rpcs = self.execution_artifacts.rpc_data.total_rpcs.clone(); if !shell::verbosity().is_silent() { let n = rpcs.len(); @@ -258,7 +258,7 @@ impl FilledTransactionsState { eyre::bail!("Multi-chain deployment is not supported with libraries."); } - let mut total_gas_per_rpc: HashMap = HashMap::new(); + let mut total_gas_per_rpc: HashMap = HashMap::new(); // Batches sequence of transactions from different rpcs. let mut new_sequence = VecDeque::new(); diff --git a/crates/script/src/transaction.rs b/crates/script/src/transaction.rs index 534b49641..c46301f9f 100644 --- a/crates/script/src/transaction.rs +++ b/crates/script/src/transaction.rs @@ -3,7 +3,7 @@ use alloy_dyn_abi::JsonAbiExt; use alloy_primitives::{Address, Bytes, TxKind, B256}; use alloy_rpc_types::{request::TransactionRequest, WithOtherFields}; use eyre::{ContextCompat, Result, WrapErr}; -use foundry_common::{fmt::format_token_raw, provider::alloy::RpcUrl, ContractData, SELECTOR_LEN}; +use foundry_common::{fmt::format_token_raw, ContractData, SELECTOR_LEN}; use foundry_evm::{constants::DEFAULT_CREATE2_DEPLOYER, traces::CallTraceDecoder}; use itertools::Itertools; use revm_inspectors::tracing::types::CallKind; @@ -34,7 +34,7 @@ pub struct TransactionWithMetadata { #[serde(default = "default_vec_of_strings")] pub arguments: Option>, #[serde(skip)] - pub rpc: RpcUrl, + pub rpc: String, pub transaction: WithOtherFields, pub additional_contracts: Vec, pub is_fixed_gas_limit: bool, @@ -59,7 +59,7 @@ impl TransactionWithMetadata { pub fn new( transaction: TransactionRequest, - rpc: RpcUrl, + rpc: String, result: &ScriptResult, local_contracts: &BTreeMap, decoder: &CallTraceDecoder, diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index 3fa5f07f8..b14ba2a54 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -3,7 +3,7 @@ use alloy_primitives::Address; use alloy_provider::Provider; use alloy_rpc_types::BlockId; use eyre::Result; -use foundry_common::provider::alloy::{get_http_provider, RetryProvider}; +use foundry_common::provider::{get_http_provider, RetryProvider}; use std::{collections::BTreeMap, fs, path::Path, str::FromStr}; const BROADCAST_TEST_PATH: &str = "src/Broadcast.t.sol"; diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index 91963752d..317a65941 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -10,7 +10,7 @@ use foundry_cli::{ }; use foundry_common::{ compile::{ProjectCompiler, SkipBuildFilter, SkipBuildFilters}, - provider::alloy::ProviderBuilder, + provider::ProviderBuilder, }; use foundry_compilers::{ artifacts::{BytecodeHash, BytecodeObject, CompactContractBytecode}, diff --git a/crates/wallets/src/wallet_signer.rs b/crates/wallets/src/wallet_signer.rs index 37526559c..c28126c43 100644 --- a/crates/wallets/src/wallet_signer.rs +++ b/crates/wallets/src/wallet_signer.rs @@ -174,16 +174,16 @@ impl Signer for WalletSigner { #[async_trait] 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 { delegate!(self, inner => inner.sign_transaction(tx)).await } - - fn address(&self) -> Address { - delegate!(self, inner => alloy_signer::Signer::address(inner)) - } } /// Signers that require user action to be obtained. From 7f3e880c4542d37137b0278b095745d92fdffe9f Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sat, 27 Apr 2024 05:18:48 +0400 Subject: [PATCH 224/430] fix: set gas limit to `u64::MAX` (#7795) fix: set block gas limit to u64::MAX instead of u128::MAX --- crates/anvil/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index a5eb3237f..ffc648199 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -1042,7 +1042,7 @@ latest block number: {latest_block}" // 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 let gas_limit = if self.disable_block_gas_limit || block.header.gas_limit == 0 { - u128::MAX + u64::MAX as u128 } else { block.header.gas_limit }; From 12e53e575c66d348a3a90e24f6cf393201fd4766 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sat, 27 Apr 2024 07:42:55 +0400 Subject: [PATCH 225/430] fix: `eth_feeHistory` (#7792) * Add test * fix * fmt --- crates/anvil/src/eth/api.rs | 23 ++--------------------- crates/anvil/src/eth/backend/mem/mod.rs | 6 +++--- crates/anvil/src/eth/fees.rs | 6 +----- crates/anvil/tests/it/gas.rs | 22 ++++++++++++++++++++++ 4 files changed, 28 insertions(+), 29 deletions(-) diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index ba9965faa..3d9a1c486 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -1313,30 +1313,11 @@ impl EthApi { response.reward = Some(rewards); - // calculate next base fee + // add the next block's base fee to the response // The spec states that `base_fee_per_gas` "[..] includes the next block after the // newest of the returned range, because this value can be derived from the // newest block" - if let (Some(last_gas_used), Some(last_fee_per_gas)) = - (response.gas_used_ratio.last(), response.base_fee_per_gas.last()) - { - let elasticity = self.backend.elasticity(); - let last_fee_per_gas = *last_fee_per_gas as f64; - if last_gas_used > &0.5 { - // increase base gas - let increase = ((last_gas_used - 0.5) * 2f64) * elasticity; - let new_base_fee = (last_fee_per_gas + (last_fee_per_gas * increase)) as u128; - response.base_fee_per_gas.push(new_base_fee); - } else if last_gas_used < &0.5 { - // decrease gas - let increase = ((0.5 - last_gas_used) * 2f64) * elasticity; - let new_base_fee = (last_fee_per_gas - (last_fee_per_gas * increase)) as u128; - response.base_fee_per_gas.push(new_base_fee); - } else { - // same base gas - response.base_fee_per_gas.push(last_fee_per_gas as u128); - } - } + response.base_fee_per_gas.push(self.backend.fees().base_fee()); Ok(response) } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 994dde5fd..51b14bb2d 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -1055,12 +1055,12 @@ impl Backend { header.base_fee_per_gas.unwrap_or_default(), ); - // notify all listeners - self.notify_on_new_block(header, block_hash); - // update next base fee self.fees.set_base_fee(next_block_base_fee); + // notify all listeners + self.notify_on_new_block(header, block_hash); + outcome } diff --git a/crates/anvil/src/eth/fees.rs b/crates/anvil/src/eth/fees.rs index 142a6a4af..f5534a0cd 100644 --- a/crates/anvil/src/eth/fees.rs +++ b/crates/anvil/src/eth/fees.rs @@ -174,7 +174,6 @@ impl FeeHistoryService { /// Create a new history entry for the block fn create_cache_entry(&self, hash: B256) -> (FeeHistoryCacheItem, Option) { - let elasticity = self.fees.elasticity(); // percentile list from 0.0 to 100.0 with a 0.5 resolution. // this will create 200 percentile points let reward_percentiles: Vec = { @@ -199,10 +198,7 @@ impl FeeHistoryService { block_number = Some(block.header.number); let gas_used = block.header.gas_used as f64; - let gas_limit = block.header.gas_limit as f64; - - let gas_target = gas_limit / elasticity; - item.gas_used_ratio = gas_used / (gas_target * elasticity); + item.gas_used_ratio = gas_used / block.header.gas_limit as f64; // extract useful tx info (gas_used, effective_reward) let mut transactions: Vec<(u128, u128)> = receipts diff --git a/crates/anvil/tests/it/gas.rs b/crates/anvil/tests/it/gas.rs index 93cebb90c..bca7102cb 100644 --- a/crates/anvil/tests/it/gas.rs +++ b/crates/anvil/tests/it/gas.rs @@ -170,3 +170,25 @@ async fn test_tip_above_fee_cap() { .to_string() .contains("max priority fee per gas higher than max fee per gas")); } + +#[tokio::test(flavor = "multi_thread")] +async fn test_can_use_fee_history() { + let base_fee = 50u128; + let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee))).await; + let provider = handle.http_provider(); + + for _ in 0..10 { + let fee_history = provider.get_fee_history(1, Default::default(), &[]).await.unwrap(); + let next_base_fee = fee_history.base_fee_per_gas.last().unwrap(); + + let tx = TransactionRequest::default() + .with_to(Address::random()) + .with_value(U256::from(100)) + .with_gas_price(*next_base_fee); + let tx = WithOtherFields::new(tx); + + let receipt = + provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); + assert!(receipt.inner.inner.is_success()); + } +} From 26e6e57527497a90af1a5409484c01b5e7702a02 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sat, 27 Apr 2024 09:21:09 +0200 Subject: [PATCH 226/430] fix: unsafe balance conversion (#7796) --- crates/anvil/src/eth/api.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 3d9a1c486..261436b52 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -2206,8 +2206,8 @@ impl EthApi { } // amount of gas the sender can afford with the `gas_price` let allowance = - available_funds.to::().checked_div(gas_price).unwrap_or_default(); - highest_gas_limit = std::cmp::min(highest_gas_limit, allowance); + available_funds.checked_div(U256::from(gas_price)).unwrap_or_default(); + highest_gas_limit = std::cmp::min(highest_gas_limit, allowance.saturating_to()); } } From 267e14fab654d9ce955dce64c0eb09f01c8538ee Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Sun, 28 Apr 2024 16:27:31 +0300 Subject: [PATCH 227/430] fix(invariant): shrink when fail-on-revert set to true (#7783) * fix(invariant): shrink when fail-on-revert set to true * Fix test fmt --- .../evm/evm/src/executors/invariant/error.rs | 103 +++++++++--------- .../evm/evm/src/executors/invariant/funcs.rs | 13 +-- crates/evm/evm/src/executors/invariant/mod.rs | 34 +++--- crates/forge/tests/it/invariant.rs | 43 ++++++++ .../common/InvariantShrinkFailOnRevert.t.sol | 26 +++++ 5 files changed, 139 insertions(+), 80 deletions(-) create mode 100644 testdata/default/fuzz/invariant/common/InvariantShrinkFailOnRevert.t.sol diff --git a/crates/evm/evm/src/executors/invariant/error.rs b/crates/evm/evm/src/executors/invariant/error.rs index 5b3ebad91..bf84cb4cb 100644 --- a/crates/evm/evm/src/executors/invariant/error.rs +++ b/crates/evm/evm/src/executors/invariant/error.rs @@ -1,9 +1,9 @@ use super::{BasicTxDetails, InvariantContract}; use crate::executors::{invariant::shrink::CallSequenceShrinker, Executor, RawCallResult}; -use alloy_json_abi::Function; use alloy_primitives::{Address, Bytes, Log}; use eyre::Result; use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; +use foundry_config::InvariantConfig; use foundry_evm_core::{constants::CALLER, decode::RevertDecoder}; use foundry_evm_fuzz::{ invariant::FuzzRunIdentifiedContracts, BaseCounterExample, CounterExample, FuzzedCases, Reason, @@ -87,33 +87,26 @@ pub struct FailedInvariantCaseData { /// Address of the invariant asserter. pub addr: Address, /// Function data for invariant check. - pub func: Option, + pub func: Bytes, /// Inner fuzzing Sequence coming from overriding calls. pub inner_sequence: Vec>, /// Shrink the failed test case to the smallest sequence. - pub shrink: bool, + pub shrink_sequence: bool, /// Shrink run limit pub shrink_run_limit: usize, + /// Fail on revert, used to check sequence when shrinking. + pub fail_on_revert: bool, } impl FailedInvariantCaseData { - #[allow(clippy::too_many_arguments)] pub fn new( invariant_contract: &InvariantContract<'_>, + invariant_config: &InvariantConfig, targeted_contracts: &FuzzRunIdentifiedContracts, - error_func: Option<&Function>, calldata: &[BasicTxDetails], call_result: RawCallResult, inner_sequence: &[Option], - shrink: bool, - shrink_run_limit: usize, ) -> Self { - let (func, origin) = if let Some(f) = error_func { - (Some(f.selector().to_vec().into()), f.name.as_str()) - } else { - (None, "Revert") - }; - // Collect abis of fuzzed and invariant contracts to decode custom error. let targets = targeted_contracts.targets.lock(); let abis = targets @@ -125,6 +118,8 @@ impl FailedInvariantCaseData { .with_abis(abis) .decode(call_result.result.as_ref(), Some(call_result.exit_reason)); + let func = invariant_contract.invariant_function; + let origin = func.name.as_str(); Self { logs: call_result.logs, traces: call_result.traces, @@ -135,14 +130,15 @@ impl FailedInvariantCaseData { return_reason: "".into(), revert_reason, addr: invariant_contract.address, - func, + func: func.selector().to_vec().into(), inner_sequence: inner_sequence.to_vec(), - shrink, - shrink_run_limit, + shrink_sequence: invariant_config.shrink_sequence, + shrink_run_limit: invariant_config.shrink_run_limit, + fail_on_revert: invariant_config.fail_on_revert, } } - /// Replays the error case and collects all necessary traces. + /// Replays the error case, shrinks the failing sequence and collects all necessary traces. pub fn replay( &self, mut executor: Executor, @@ -158,7 +154,7 @@ impl FailedInvariantCaseData { TestError::Fail(_, ref calls) => calls.clone(), }; - if self.shrink { + if self.shrink_sequence { calls = self.shrink_sequence(&calls, &executor)?; } else { trace!(target: "forge::test", "Shrinking disabled."); @@ -192,16 +188,14 @@ impl FailedInvariantCaseData { )); // Checks the invariant. - if let Some(func) = &self.func { - let error_call_result = - executor.call_raw(CALLER, self.addr, func.clone(), U256::ZERO)?; + let error_call_result = + executor.call_raw(CALLER, self.addr, self.func.clone(), U256::ZERO)?; - traces.push((TraceKind::Execution, error_call_result.traces.clone().unwrap())); + traces.push((TraceKind::Execution, error_call_result.traces.clone().unwrap())); - logs.extend(error_call_result.logs); - if error_call_result.reverted { - break - } + logs.extend(error_call_result.logs); + if error_call_result.reverted { + break } } @@ -221,12 +215,10 @@ impl FailedInvariantCaseData { // Special case test: the invariant is *unsatisfiable* - it took 0 calls to // break the invariant -- consider emitting a warning. - if let Some(func) = &self.func { - let error_call_result = - executor.call_raw(CALLER, self.addr, func.clone(), U256::ZERO)?; - if error_call_result.reverted { - return Ok(vec![]); - } + let error_call_result = + executor.call_raw(CALLER, self.addr, self.func.clone(), U256::ZERO)?; + if error_call_result.reverted { + return Ok(vec![]); } let mut shrinker = CallSequenceShrinker::new(calls.len()); @@ -234,10 +226,10 @@ impl FailedInvariantCaseData { // Check candidate sequence result. match self.check_sequence(executor.clone(), calls, shrinker.current().collect()) { // If candidate sequence still fails then shrink more if possible. - false if !shrinker.simplify() => break, + Ok(false) if !shrinker.simplify() => break, // If candidate sequence pass then restore last removed call and shrink other // calls if possible. - true if !shrinker.complicate() => break, + Ok(true) if !shrinker.complicate() => break, _ => {} } } @@ -257,12 +249,24 @@ impl FailedInvariantCaseData { mut executor: Executor, calls: &[BasicTxDetails], sequence: Vec, - ) -> bool { + ) -> Result { + let mut sequence_failed = false; // Apply the shrinked candidate sequence. - sequence.iter().for_each(|call_index| { - let (sender, (addr, bytes)) = &calls[*call_index]; - executor.call_raw_committing(*sender, *addr, bytes.clone(), U256::ZERO).unwrap(); - }); + for call_index in sequence { + let (sender, (addr, bytes)) = &calls[call_index]; + let call_result = + executor.call_raw_committing(*sender, *addr, bytes.clone(), U256::ZERO)?; + if call_result.reverted && self.fail_on_revert { + // Candidate sequence fails test. + // We don't have to apply remaining calls to check sequence. + sequence_failed = true; + break; + } + } + // Return without checking the invariant if we already have failing sequence. + if sequence_failed { + return Ok(false); + }; // Check the invariant for candidate sequence. // If sequence fails then we can continue with shrinking - the removed call does not affect @@ -270,19 +274,14 @@ impl FailedInvariantCaseData { // // If sequence doesn't fail then we have to restore last removed call and continue with next // call - removed call is a required step for reproducing the failure. - if let Some(func) = &self.func { - let mut call_result = - executor.call_raw(CALLER, self.addr, func.clone(), U256::ZERO).unwrap(); - executor.is_raw_call_success( - self.addr, - Cow::Owned(call_result.state_changeset.take().unwrap()), - &call_result, - false, - ) - } else { - // Invariant function is not set, return true as we cannot test the sequence. - true - } + let mut call_result = + executor.call_raw(CALLER, self.addr, self.func.clone(), U256::ZERO)?; + Ok(executor.is_raw_call_success( + self.addr, + Cow::Owned(call_result.state_changeset.take().unwrap()), + &call_result, + false, + )) } } diff --git a/crates/evm/evm/src/executors/invariant/funcs.rs b/crates/evm/evm/src/executors/invariant/funcs.rs index b3a913fb3..6753899b1 100644 --- a/crates/evm/evm/src/executors/invariant/funcs.rs +++ b/crates/evm/evm/src/executors/invariant/funcs.rs @@ -3,7 +3,9 @@ use crate::executors::{Executor, RawCallResult}; use alloy_dyn_abi::JsonAbiExt; use alloy_json_abi::Function; use alloy_primitives::Log; +use eyre::Result; use foundry_common::{ContractsByAddress, ContractsByArtifact}; +use foundry_config::InvariantConfig; use foundry_evm_core::constants::CALLER; use foundry_evm_coverage::HitMaps; use foundry_evm_fuzz::invariant::{BasicTxDetails, FuzzRunIdentifiedContracts, InvariantContract}; @@ -16,13 +18,12 @@ use std::borrow::Cow; /// Either returns the call result if successful, or nothing if there was an error. pub fn assert_invariants( invariant_contract: &InvariantContract<'_>, + invariant_config: &InvariantConfig, targeted_contracts: &FuzzRunIdentifiedContracts, executor: &Executor, calldata: &[BasicTxDetails], invariant_failures: &mut InvariantFailures, - shrink_sequence: bool, - shrink_run_limit: usize, -) -> eyre::Result> { +) -> Result> { let mut inner_sequence = vec![]; if let Some(fuzzer) = &executor.inspector.fuzzer { @@ -50,13 +51,11 @@ pub fn assert_invariants( if invariant_failures.error.is_none() { let case_data = FailedInvariantCaseData::new( invariant_contract, + invariant_config, targeted_contracts, - Some(func), calldata, call_result, &inner_sequence, - shrink_sequence, - shrink_run_limit, ); invariant_failures.error = Some(InvariantFuzzError::BrokenInvariant(case_data)); return Ok(None); @@ -78,7 +77,7 @@ pub fn replay_run( coverage: &mut Option, func: Function, inputs: Vec, -) -> eyre::Result<()> { +) -> Result<()> { // We want traces for a failed case. executor.set_tracing(true); diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index d0593d919..521fb6722 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -178,12 +178,11 @@ impl<'a> InvariantExecutor<'a> { // This does not count as a fuzz run. It will just register the revert. let last_call_results = RefCell::new(assert_invariants( &invariant_contract, + &self.config, &targeted_contracts, &self.executor, &[], &mut failures.borrow_mut(), - self.config.shrink_sequence, - self.config.shrink_run_limit, )?); if last_call_results.borrow().is_none() { @@ -274,15 +273,13 @@ impl<'a> InvariantExecutor<'a> { let RichInvariantResults { success: can_continue, call_result: call_results } = can_continue( &invariant_contract, + &self.config, call_result, &executor, &inputs, &mut failures.borrow_mut(), &targeted_contracts, &state_changeset, - self.config.fail_on_revert, - self.config.shrink_sequence, - self.config.shrink_run_limit, &mut run_traces, ) .map_err(|e| TestCaseError::fail(e.to_string()))?; @@ -350,7 +347,7 @@ impl<'a> InvariantExecutor<'a> { &mut self, invariant_contract: &InvariantContract<'_>, fuzz_fixtures: &FuzzFixtures, - ) -> eyre::Result { + ) -> Result { // Finds out the chosen deployed contracts and/or senders. self.select_contract_artifacts(invariant_contract.address)?; let (targeted_senders, targeted_contracts) = @@ -405,7 +402,7 @@ impl<'a> InvariantExecutor<'a> { /// Priority: /// /// targetArtifactSelectors > excludeArtifacts > targetArtifacts - pub fn select_contract_artifacts(&mut self, invariant_address: Address) -> eyre::Result<()> { + pub fn select_contract_artifacts(&mut self, invariant_address: Address) -> Result<()> { let result = self .call_sol_default(invariant_address, &IInvariantTest::targetArtifactSelectorsCall {}); @@ -471,7 +468,7 @@ impl<'a> InvariantExecutor<'a> { &mut self, contract: String, selectors: &[FixedBytes<4>], - ) -> eyre::Result { + ) -> Result { if let Some((artifact, contract_data)) = self.project_contracts.find_by_name_or_identifier(&contract)? { @@ -494,7 +491,7 @@ impl<'a> InvariantExecutor<'a> { pub fn select_contracts_and_senders( &self, to: Address, - ) -> eyre::Result<(SenderFilters, FuzzRunIdentifiedContracts)> { + ) -> Result<(SenderFilters, FuzzRunIdentifiedContracts)> { let targeted_senders = self.call_sol_default(to, &IInvariantTest::targetSendersCall {}).targetedSenders; let excluded_senders = @@ -545,7 +542,7 @@ impl<'a> InvariantExecutor<'a> { &self, invariant_address: Address, targeted_contracts: &mut TargetedContracts, - ) -> eyre::Result<()> { + ) -> Result<()> { let interfaces = self .call_sol_default(invariant_address, &IInvariantTest::targetInterfacesCall {}) .targetedInterfaces; @@ -593,7 +590,7 @@ impl<'a> InvariantExecutor<'a> { &self, address: Address, targeted_contracts: &mut TargetedContracts, - ) -> eyre::Result<()> { + ) -> Result<()> { let some_abi_selectors = self .artifact_filters .targeted @@ -696,17 +693,15 @@ fn collect_data( #[allow(clippy::too_many_arguments)] fn can_continue( invariant_contract: &InvariantContract<'_>, + invariant_config: &InvariantConfig, call_result: RawCallResult, executor: &Executor, calldata: &[BasicTxDetails], failures: &mut InvariantFailures, targeted_contracts: &FuzzRunIdentifiedContracts, state_changeset: &StateChangeset, - fail_on_revert: bool, - shrink_sequence: bool, - shrink_run_limit: usize, run_traces: &mut Vec, -) -> eyre::Result { +) -> Result { let mut call_results = None; // Detect handler assertion failures first. @@ -722,12 +717,11 @@ fn can_continue( call_results = assert_invariants( invariant_contract, + invariant_config, targeted_contracts, executor, calldata, failures, - shrink_sequence, - shrink_run_limit, )?; if call_results.is_none() { return Ok(RichInvariantResults::new(false, None)); @@ -736,16 +730,14 @@ fn can_continue( // Increase the amount of reverts. failures.reverts += 1; // If fail on revert is set, we must return immediately. - if fail_on_revert { + if invariant_config.fail_on_revert { let case_data = FailedInvariantCaseData::new( invariant_contract, + invariant_config, targeted_contracts, - None, calldata, call_result, &[], - shrink_sequence, - shrink_run_limit, ); failures.revert_reason = Some(case_data.revert_reason.clone()); let error = InvariantFuzzError::Revert(case_data); diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index ee3f0edc3..e906c8356 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -177,6 +177,10 @@ async fn test_invariant() { "default/fuzz/invariant/common/InvariantShrinkBigSequence.t.sol:ShrinkBigSequenceTest", vec![("invariant_shrink_big_sequence()", true, None, None, None)], ), + ( + "default/fuzz/invariant/common/InvariantShrinkFailOnRevert.t.sol:ShrinkFailOnRevertTest", + vec![("invariant_shrink_fail_on_revert()", true, None, None, None)], + ), ]), ); } @@ -371,6 +375,45 @@ async fn test_shrink_big_sequence() { }; } +#[tokio::test(flavor = "multi_thread")] +#[cfg_attr(windows, ignore = "for some reason there's different rng")] +async fn test_shrink_fail_on_revert() { + let mut opts = TEST_DATA_DEFAULT.test_opts.clone(); + opts.fuzz.seed = Some(U256::from(119u32)); + + let filter = + Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantShrinkFailOnRevert.t.sol"); + let mut runner = TEST_DATA_DEFAULT.runner(); + runner.test_options = opts.clone(); + runner.test_options.invariant.fail_on_revert = true; + runner.test_options.invariant.runs = 1; + runner.test_options.invariant.depth = 100; + let results = runner.test_collect(&filter); + let results = + results.values().last().expect("`InvariantShrinkFailOnRevert` should be testable."); + + let result = results + .test_results + .values() + .last() + .expect("`InvariantShrinkFailOnRevert` should be testable."); + + assert_eq!(result.status, TestStatus::Failure); + + let counter = result + .counterexample + .as_ref() + .expect("`InvariantShrinkFailOnRevert` should have failed with a counterexample."); + + match counter { + CounterExample::Single(_) => panic!("CounterExample should be a sequence."), + CounterExample::Sequence(sequence) => { + // ensure shrinks to sequence of 10 + assert_eq!(sequence.len(), 10); + } + }; +} + #[tokio::test(flavor = "multi_thread")] async fn test_invariant_preserve_state() { let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantPreserveState.t.sol"); diff --git a/testdata/default/fuzz/invariant/common/InvariantShrinkFailOnRevert.t.sol b/testdata/default/fuzz/invariant/common/InvariantShrinkFailOnRevert.t.sol new file mode 100644 index 000000000..d971367b6 --- /dev/null +++ b/testdata/default/fuzz/invariant/common/InvariantShrinkFailOnRevert.t.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract ShrinkFailOnRevert { + uint256 cond; + + function work(uint256 x) public { + if (x % 2 != 0 && x < 9000) { + cond++; + } + require(cond < 10, "condition met"); + } +} + +contract ShrinkFailOnRevertTest is DSTest { + ShrinkFailOnRevert target; + + function setUp() public { + target = new ShrinkFailOnRevert(); + } + + function invariant_shrink_fail_on_revert() public view {} +} From ea9584f63c901a38bce67e1330bc0555d2bb883f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 02:39:22 +0200 Subject: [PATCH 228/430] chore(deps): weekly `cargo update` (#7802) Updating git repository `https://github.com/alloy-rs/alloy` Updating git repository `https://github.com/paradigmxyz/evm-inspectors` Locking 34 packages to latest compatible versions Updating ariadne v0.4.0 -> v0.4.1 Updating async-recursion v1.1.0 -> v1.1.1 Updating async-task v4.7.0 -> v4.7.1 Updating aws-config v1.2.0 -> v1.2.1 Updating aws-sdk-kms v1.21.0 -> v1.22.0 Updating aws-sdk-sso v1.20.0 -> v1.21.0 Updating aws-sdk-ssooidc v1.20.0 -> v1.21.0 Updating aws-sdk-sts v1.20.0 -> v1.21.0 Updating aws-sigv4 v1.2.0 -> v1.2.1 Updating blocking v1.5.1 -> v1.6.0 Updating concurrent-queue v2.4.0 -> v2.5.0 Updating event-listener-strategy v0.5.1 -> v0.5.2 Updating fastrand v2.0.2 -> v2.1.0 Updating flate2 v1.0.28 -> v1.0.29 Updating libusb1-sys v0.6.4 -> v0.7.0 Updating lock_api v0.4.11 -> v0.4.12 Updating parking_lot v0.12.1 -> v0.12.2 Updating parking_lot_core v0.9.9 -> v0.9.10 Adding proc-macro-crate v3.1.0 Updating pulldown-cmark v0.10.2 -> v0.10.3 Updating pulldown-cmark-escape v0.10.0 -> v0.10.1 Adding redox_syscall v0.5.1 Updating rusb v0.9.3 -> v0.9.4 Updating rustix v0.38.33 -> v0.38.34 Updating rustls v0.21.11 -> v0.21.12 (latest: v0.23.5) Updating rustls-pki-types v1.4.1 -> v1.5.0 Updating serde v1.0.198 -> v1.0.199 Updating serde_derive v1.0.198 -> v1.0.199 Updating serial_test v3.1.0 -> v3.1.1 Updating serial_test_derive v3.1.0 -> v3.1.1 Adding toml_edit v0.21.1 (latest: v0.22.12) Updating unicode-width v0.1.11 -> v0.1.12 Updating winapi-util v0.1.6 -> v0.1.8 Updating winnow v0.6.6 -> v0.6.7 note: pass `--verbose` to see 168 unchanged dependencies behind latest Co-authored-by: mattsse --- Cargo.lock | 187 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 107 insertions(+), 80 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0076ecc42..97c3c3e93 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -128,7 +128,7 @@ dependencies = [ "proptest", "serde", "serde_json", - "winnow 0.6.6", + "winnow 0.6.7", ] [[package]] @@ -501,7 +501,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c8d6e74e4feeaa2bcfdecfd3da247ab53c67bd654ba1907270c32e02b142331" dependencies = [ - "winnow 0.6.6", + "winnow 0.6.7", ] [[package]] @@ -826,12 +826,12 @@ dependencies = [ [[package]] name = "ariadne" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd002a6223f12c7a95cdd4b1cb3a0149d22d37f7a9ecdb2cb691a071fe236c29" +checksum = "44055e597c674aef7cb903b2b9f6e4cba1277ed0d2d61dae7cd52d7ffa81f8e2" dependencies = [ "unicode-width", - "yansi 0.5.1", + "yansi 1.0.1", ] [[package]] @@ -981,7 +981,7 @@ checksum = "136d4d23bcc79e27423727b36823d86233aad06dfea531837b038394d11e9928" dependencies = [ "concurrent-queue", "event-listener 5.3.0", - "event-listener-strategy 0.5.1", + "event-listener-strategy 0.5.2", "futures-core", "pin-project-lite", ] @@ -1008,9 +1008,9 @@ dependencies = [ [[package]] name = "async-recursion" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", @@ -1041,9 +1041,9 @@ dependencies = [ [[package]] name = "async-task" -version = "4.7.0" +version = "4.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" @@ -1117,9 +1117,9 @@ checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "aws-config" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2a89e0000cde82447155d64eeb71720b933b4396a6fbbebad3f8b4f88ca7b54" +checksum = "b2a4707646259764ab59fd9a50e9de2e92c637b28b36285d6f6fa030e915fbd9" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1183,9 +1183,9 @@ dependencies = [ [[package]] name = "aws-sdk-kms" -version = "1.21.0" +version = "1.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1747213c6bb8fae0f388157e07e144fd442c1e28cfd9c4e257b1b6ee26c4a54" +checksum = "4453868f71232e0baf5947972972e87813c8bbb311351f669a6ad6aa5b86ee63" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1205,9 +1205,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.20.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32fcc572fd5c58489ec205ec3e4e5f7d63018898a485cbf922a462af496bc300" +checksum = "3d70fb493f4183f5102d8a8d0cc9b57aec29a762f55c0e7bf527e0f7177bb408" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1227,9 +1227,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.20.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b6275fa8684a1192754221173b1f7a7c1260d6b0571cc2b8af09468eb0cffe5" +checksum = "de3f37549b3e38b7ea5efd419d4d7add6ea1e55223506eb0b4fef9d25e7cc90d" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1249,9 +1249,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.20.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30acd58272fd567e4853c5075d838be1626b59057e0249c9be5a1a7eb13bf70f" +checksum = "3b2ff219a5d4b795cd33251c19dbe9c4b401f2b2cbe513e07c76ada644eaf34e" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1272,9 +1272,9 @@ dependencies = [ [[package]] name = "aws-sigv4" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d6f29688a4be9895c0ba8bef861ad0c0dac5c15e9618b9b7a6c233990fc263" +checksum = "58b56f1cbe6fd4d0c2573df72868f20ab1c125ca9c9dbce17927a463433a2e57" dependencies = [ "aws-credential-types", "aws-smithy-http", @@ -1364,7 +1364,7 @@ dependencies = [ "once_cell", "pin-project-lite", "pin-utils", - "rustls 0.21.11", + "rustls 0.21.12", "tokio", "tracing", ] @@ -1613,18 +1613,16 @@ dependencies = [ [[package]] name = "blocking" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" +checksum = "495f7104e962b7356f0aeb34247aca1fe7d2e783b346582db7f2904cb5717e88" dependencies = [ "async-channel", "async-lock", "async-task", - "fastrand", "futures-io", "futures-lite", "piper", - "tracing", ] [[package]] @@ -2210,9 +2208,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ "crossbeam-utils", ] @@ -3272,9 +3270,9 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "332f51cb23d20b0de8458b86580878211da09bcd4503cb579c225b3d124cabb3" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" dependencies = [ "event-listener 5.3.0", "pin-project-lite", @@ -3311,9 +3309,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "fastrlp" @@ -3405,7 +3403,7 @@ checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.4.1", "windows-sys 0.52.0", ] @@ -3430,9 +3428,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.28" +version = "1.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "4556222738635b7a3417ae6130d8f52201e45a0c4d1a907f0826383adb5f85e7" dependencies = [ "crc32fast", "miniz_oxide", @@ -4891,7 +4889,7 @@ dependencies = [ "http 0.2.12", "hyper 0.14.28", "log", - "rustls 0.21.11", + "rustls 0.21.12", "rustls-native-certs 0.6.3", "tokio", "tokio-rustls 0.24.1", @@ -5391,9 +5389,9 @@ dependencies = [ [[package]] name = "libusb1-sys" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d0e2afce4245f2c9a418511e5af8718bcaf2fa408aefb259504d1a9cb25f27" +checksum = "da050ade7ac4ff1ba5379af847a10a10a8e284181e060105bf8d86960ce9ce0f" dependencies = [ "cc", "libc", @@ -5409,9 +5407,9 @@ checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -5865,7 +5863,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ - "proc-macro-crate 2.0.0", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", "syn 2.0.60", @@ -6084,9 +6082,9 @@ checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" dependencies = [ "lock_api", "parking_lot_core", @@ -6094,15 +6092,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.1", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] @@ -6561,6 +6559,15 @@ dependencies = [ "toml_edit 0.20.7", ] +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit 0.21.1", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -6671,9 +6678,9 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0530d13d87d1f549b66a3e8d0c688952abe5994e204ed62615baaf25dc029c" +checksum = "76979bea66e7875e7509c4ec5300112b316af87fa7a252ca91c448b32dfe3993" dependencies = [ "bitflags 2.5.0", "memchr", @@ -6683,9 +6690,9 @@ dependencies = [ [[package]] name = "pulldown-cmark-escape" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d8f9aa0e3cbcfaf8bf00300004ee3b72f74770f9cbac93f6928771f613276b" +checksum = "bd348ff538bc9caeda7ee8cad2d1d48236a1f443c1fa3913c6a02fe0043b1dd3" [[package]] name = "quick-error" @@ -6847,6 +6854,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +dependencies = [ + "bitflags 2.5.0", +] + [[package]] name = "redox_users" version = "0.4.5" @@ -6931,7 +6947,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.11", + "rustls 0.21.12", "rustls-pemfile 1.0.4", "serde", "serde_json", @@ -7198,9 +7214,9 @@ checksum = "f86854cf50259291520509879a5c294c3c9a4c334e9ff65071c51e42ef1e2343" [[package]] name = "rusb" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45fff149b6033f25e825cbb7b2c625a11ee8e6dac09264d49beb125e39aa97bf" +checksum = "ab9f9ff05b63a786553a4c02943b74b34a988448671001e9a27e2f0565cc05a4" dependencies = [ "libc", "libusb1-sys", @@ -7244,9 +7260,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.33" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3cc72858054fcff6d7dea32df2aeaee6a7c24227366d7ea429aada2f26b16ad" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags 2.5.0", "errno", @@ -7257,9 +7273,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.11" +version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fecbfb7b1444f477b345853b1fce097a2c6fb637b2bfb87e6bc5db0f043fae4" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring 0.17.8", @@ -7327,9 +7343,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd36cc4259e3e4514335c4a138c6b43171a8d61d8f5c9348f9fc7529416f247" +checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54" [[package]] name = "rustls-webpki" @@ -7614,18 +7630,18 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.198" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" +checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.198" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" +checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" dependencies = [ "proc-macro2", "quote", @@ -7709,9 +7725,9 @@ dependencies = [ [[package]] name = "serial_test" -version = "3.1.0" +version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb86f9315df5df6a70eae0cc22395a44e544a0d8897586820770a35ede74449" +checksum = "4b4b487fe2acf240a021cf57c6b2b4903b1e78ca0ecd862a71b71d2a51fed77d" dependencies = [ "futures", "log", @@ -7723,9 +7739,9 @@ dependencies = [ [[package]] name = "serial_test_derive" -version = "3.1.0" +version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9bb72430492e9549b0c4596725c0f82729bff861c45aa8099c0a8e67fc3b721" +checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67" dependencies = [ "proc-macro2", "quote", @@ -8403,7 +8419,7 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.21.11", + "rustls 0.21.12", "tokio", ] @@ -8438,7 +8454,7 @@ checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" dependencies = [ "futures-util", "log", - "rustls 0.21.11", + "rustls 0.21.12", "tokio", "tokio-rustls 0.24.1", "tungstenite 0.20.1", @@ -8525,6 +8541,17 @@ dependencies = [ "winnow 0.5.40", ] +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow 0.5.40", +] + [[package]] name = "toml_edit" version = "0.22.12" @@ -8535,7 +8562,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.6", + "winnow 0.6.7", ] [[package]] @@ -8712,7 +8739,7 @@ dependencies = [ "httparse", "log", "rand 0.8.5", - "rustls 0.21.11", + "rustls 0.21.12", "sha1", "thiserror", "url", @@ -8822,9 +8849,9 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" [[package]] name = "unicode-xid" @@ -9134,11 +9161,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -9381,9 +9408,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352" +checksum = "14b9415ee827af173ebb3f15f9083df5a122eb93572ec28741fb153356ea2578" dependencies = [ "memchr", ] From bab83500e220df10231e27f542041d11b73af87a Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Mon, 29 Apr 2024 18:03:55 +0300 Subject: [PATCH 229/430] chore(invariant): deprecate shrink_sequence, code reuse and cleanup (#7808) * chore(invariant): deprecate shrink_sequence, code reuse and cleanup * Split func mod in replay and result * Update visibility, export only needed --- .../evm/evm/src/executors/invariant/error.rs | 193 +----------------- .../evm/evm/src/executors/invariant/funcs.rs | 121 ----------- crates/evm/evm/src/executors/invariant/mod.rs | 129 +++--------- .../evm/evm/src/executors/invariant/replay.rs | 134 ++++++++++++ .../evm/evm/src/executors/invariant/result.rs | 156 ++++++++++++++ .../evm/evm/src/executors/invariant/shrink.rs | 94 ++++++++- crates/forge/src/runner.rs | 13 +- 7 files changed, 423 insertions(+), 417 deletions(-) delete mode 100644 crates/evm/evm/src/executors/invariant/funcs.rs create mode 100644 crates/evm/evm/src/executors/invariant/replay.rs create mode 100644 crates/evm/evm/src/executors/invariant/result.rs diff --git a/crates/evm/evm/src/executors/invariant/error.rs b/crates/evm/evm/src/executors/invariant/error.rs index bf84cb4cb..e797174a5 100644 --- a/crates/evm/evm/src/executors/invariant/error.rs +++ b/crates/evm/evm/src/executors/invariant/error.rs @@ -1,18 +1,10 @@ use super::{BasicTxDetails, InvariantContract}; -use crate::executors::{invariant::shrink::CallSequenceShrinker, Executor, RawCallResult}; -use alloy_primitives::{Address, Bytes, Log}; -use eyre::Result; -use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; +use crate::executors::RawCallResult; +use alloy_primitives::{Address, Bytes}; use foundry_config::InvariantConfig; -use foundry_evm_core::{constants::CALLER, decode::RevertDecoder}; -use foundry_evm_fuzz::{ - invariant::FuzzRunIdentifiedContracts, BaseCounterExample, CounterExample, FuzzedCases, Reason, -}; -use foundry_evm_traces::{load_contracts, CallTraceArena, TraceKind, Traces}; -use parking_lot::RwLock; +use foundry_evm_core::decode::RevertDecoder; +use foundry_evm_fuzz::{invariant::FuzzRunIdentifiedContracts, Reason}; use proptest::test_runner::TestError; -use revm::primitives::U256; -use std::{borrow::Cow, sync::Arc}; /// Stores information about failures and reverts of the invariant tests. #[derive(Clone, Default)] @@ -37,23 +29,6 @@ impl InvariantFailures { } } -/// The outcome of an invariant fuzz test -#[derive(Debug)] -pub struct InvariantFuzzTestResult { - pub error: Option, - /// Every successful fuzz test case - pub cases: Vec, - /// Number of reverted fuzz calls - pub reverts: usize, - - /// The entire inputs of the last run of the invariant campaign, used for - /// replaying the run for collecting traces. - pub last_run_inputs: Vec, - - /// Additional traces used for gas report construction. - pub gas_report_traces: Vec>, -} - #[derive(Clone, Debug)] pub enum InvariantFuzzError { Revert(FailedInvariantCaseData), @@ -76,8 +51,6 @@ impl InvariantFuzzError { #[derive(Clone, Debug)] pub struct FailedInvariantCaseData { - pub logs: Vec, - pub traces: Option, /// The proptest error occurred as a result of a test case. pub test_error: TestError>, /// The return reason of the offending call. @@ -121,8 +94,6 @@ impl FailedInvariantCaseData { let func = invariant_contract.invariant_function; let origin = func.name.as_str(); Self { - logs: call_result.logs, - traces: call_result.traces, test_error: proptest::test_runner::TestError::Fail( format!("{origin}, reason: {revert_reason}").into(), calldata.to_vec(), @@ -137,160 +108,4 @@ impl FailedInvariantCaseData { fail_on_revert: invariant_config.fail_on_revert, } } - - /// Replays the error case, shrinks the failing sequence and collects all necessary traces. - pub fn replay( - &self, - mut executor: Executor, - known_contracts: &ContractsByArtifact, - mut ided_contracts: ContractsByAddress, - logs: &mut Vec, - traces: &mut Traces, - ) -> Result> { - let mut counterexample_sequence = vec![]; - let mut calls = match self.test_error { - // Don't use at the moment. - TestError::Abort(_) => return Ok(None), - TestError::Fail(_, ref calls) => calls.clone(), - }; - - if self.shrink_sequence { - calls = self.shrink_sequence(&calls, &executor)?; - } else { - trace!(target: "forge::test", "Shrinking disabled."); - } - - // We want traces for a failed case. - executor.set_tracing(true); - - set_up_inner_replay(&mut executor, &self.inner_sequence); - - // Replay each call from the sequence until we break the invariant. - for (sender, (addr, bytes)) in calls.iter() { - let call_result = - executor.call_raw_committing(*sender, *addr, bytes.clone(), U256::ZERO)?; - - logs.extend(call_result.logs); - traces.push((TraceKind::Execution, call_result.traces.clone().unwrap())); - - // Identify newly generated contracts, if they exist. - ided_contracts.extend(load_contracts( - vec![(TraceKind::Execution, call_result.traces.clone().unwrap())], - known_contracts, - )); - - counterexample_sequence.push(BaseCounterExample::create( - *sender, - *addr, - bytes, - &ided_contracts, - call_result.traces, - )); - - // Checks the invariant. - let error_call_result = - executor.call_raw(CALLER, self.addr, self.func.clone(), U256::ZERO)?; - - traces.push((TraceKind::Execution, error_call_result.traces.clone().unwrap())); - - logs.extend(error_call_result.logs); - if error_call_result.reverted { - break - } - } - - Ok((!counterexample_sequence.is_empty()) - .then_some(CounterExample::Sequence(counterexample_sequence))) - } - - /// Tries to shrink the failure case to its smallest sequence of calls. - /// - /// If the number of calls is small enough, we can guarantee maximal shrinkage - fn shrink_sequence( - &self, - calls: &[BasicTxDetails], - executor: &Executor, - ) -> Result> { - trace!(target: "forge::test", "Shrinking."); - - // Special case test: the invariant is *unsatisfiable* - it took 0 calls to - // break the invariant -- consider emitting a warning. - let error_call_result = - executor.call_raw(CALLER, self.addr, self.func.clone(), U256::ZERO)?; - if error_call_result.reverted { - return Ok(vec![]); - } - - let mut shrinker = CallSequenceShrinker::new(calls.len()); - for _ in 0..self.shrink_run_limit { - // Check candidate sequence result. - match self.check_sequence(executor.clone(), calls, shrinker.current().collect()) { - // If candidate sequence still fails then shrink more if possible. - Ok(false) if !shrinker.simplify() => break, - // If candidate sequence pass then restore last removed call and shrink other - // calls if possible. - Ok(true) if !shrinker.complicate() => break, - _ => {} - } - } - - // We recreate the call sequence in the same order as they reproduce the failure, - // otherwise we could end up with inverted sequence. - // E.g. in a sequence of: - // 1. Alice calls acceptOwnership and reverts - // 2. Bob calls transferOwnership to Alice - // 3. Alice calls acceptOwnership and test fails - // we shrink to indices of [2, 1] and we recreate call sequence in same order. - Ok(shrinker.current().map(|idx| &calls[idx]).cloned().collect()) - } - - fn check_sequence( - &self, - mut executor: Executor, - calls: &[BasicTxDetails], - sequence: Vec, - ) -> Result { - let mut sequence_failed = false; - // Apply the shrinked candidate sequence. - for call_index in sequence { - let (sender, (addr, bytes)) = &calls[call_index]; - let call_result = - executor.call_raw_committing(*sender, *addr, bytes.clone(), U256::ZERO)?; - if call_result.reverted && self.fail_on_revert { - // Candidate sequence fails test. - // We don't have to apply remaining calls to check sequence. - sequence_failed = true; - break; - } - } - // Return without checking the invariant if we already have failing sequence. - if sequence_failed { - return Ok(false); - }; - - // Check the invariant for candidate sequence. - // If sequence fails then we can continue with shrinking - the removed call does not affect - // failure. - // - // If sequence doesn't fail then we have to restore last removed call and continue with next - // call - removed call is a required step for reproducing the failure. - let mut call_result = - executor.call_raw(CALLER, self.addr, self.func.clone(), U256::ZERO)?; - Ok(executor.is_raw_call_success( - self.addr, - Cow::Owned(call_result.state_changeset.take().unwrap()), - &call_result, - false, - )) - } -} - -/// Sets up the calls generated by the internal fuzzer, if they exist. -fn set_up_inner_replay(executor: &mut Executor, inner_sequence: &[Option]) { - if let Some(fuzzer) = &mut executor.inspector.fuzzer { - if let Some(call_generator) = &mut fuzzer.call_generator { - call_generator.last_sequence = Arc::new(RwLock::new(inner_sequence.to_owned())); - call_generator.set_replay(true); - } - } } diff --git a/crates/evm/evm/src/executors/invariant/funcs.rs b/crates/evm/evm/src/executors/invariant/funcs.rs deleted file mode 100644 index 6753899b1..000000000 --- a/crates/evm/evm/src/executors/invariant/funcs.rs +++ /dev/null @@ -1,121 +0,0 @@ -use super::{error::FailedInvariantCaseData, InvariantFailures, InvariantFuzzError}; -use crate::executors::{Executor, RawCallResult}; -use alloy_dyn_abi::JsonAbiExt; -use alloy_json_abi::Function; -use alloy_primitives::Log; -use eyre::Result; -use foundry_common::{ContractsByAddress, ContractsByArtifact}; -use foundry_config::InvariantConfig; -use foundry_evm_core::constants::CALLER; -use foundry_evm_coverage::HitMaps; -use foundry_evm_fuzz::invariant::{BasicTxDetails, FuzzRunIdentifiedContracts, InvariantContract}; -use foundry_evm_traces::{load_contracts, TraceKind, Traces}; -use revm::primitives::U256; -use std::borrow::Cow; - -/// Given the executor state, asserts that no invariant has been broken. Otherwise, it fills the -/// external `invariant_failures.failed_invariant` map and returns a generic error. -/// Either returns the call result if successful, or nothing if there was an error. -pub fn assert_invariants( - invariant_contract: &InvariantContract<'_>, - invariant_config: &InvariantConfig, - targeted_contracts: &FuzzRunIdentifiedContracts, - executor: &Executor, - calldata: &[BasicTxDetails], - invariant_failures: &mut InvariantFailures, -) -> Result> { - let mut inner_sequence = vec![]; - - if let Some(fuzzer) = &executor.inspector.fuzzer { - if let Some(call_generator) = &fuzzer.call_generator { - inner_sequence.extend(call_generator.last_sequence.read().iter().cloned()); - } - } - - let func = invariant_contract.invariant_function; - let mut call_result = executor.call_raw( - CALLER, - invariant_contract.address, - func.abi_encode_input(&[]).expect("invariant should have no inputs").into(), - U256::ZERO, - )?; - - let is_err = !executor.is_raw_call_success( - invariant_contract.address, - Cow::Owned(call_result.state_changeset.take().unwrap()), - &call_result, - false, - ); - if is_err { - // We only care about invariants which we haven't broken yet. - if invariant_failures.error.is_none() { - let case_data = FailedInvariantCaseData::new( - invariant_contract, - invariant_config, - targeted_contracts, - calldata, - call_result, - &inner_sequence, - ); - invariant_failures.error = Some(InvariantFuzzError::BrokenInvariant(case_data)); - return Ok(None); - } - } - - Ok(Some(call_result)) -} - -/// Replays the provided invariant run for collecting the logs and traces from all depths. -#[allow(clippy::too_many_arguments)] -pub fn replay_run( - invariant_contract: &InvariantContract<'_>, - mut executor: Executor, - known_contracts: &ContractsByArtifact, - mut ided_contracts: ContractsByAddress, - logs: &mut Vec, - traces: &mut Traces, - coverage: &mut Option, - func: Function, - inputs: Vec, -) -> Result<()> { - // We want traces for a failed case. - executor.set_tracing(true); - - // set_up_inner_replay(&mut executor, &inputs); - - // Replay each call from the sequence until we break the invariant. - for (sender, (addr, bytes)) in inputs.iter() { - let call_result = - executor.call_raw_committing(*sender, *addr, bytes.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).merge(new_coverage)); - } else { - *coverage = Some(new_coverage); - } - } - - // Identify newly generated contracts, if they exist. - ided_contracts.extend(load_contracts( - vec![(TraceKind::Execution, call_result.traces.clone().unwrap())], - known_contracts, - )); - - // Checks the invariant. - let error_call_result = executor.call_raw( - CALLER, - invariant_contract.address, - func.abi_encode_input(&[]).expect("invariant should have no inputs").into(), - U256::ZERO, - )?; - - traces.push((TraceKind::Execution, error_call_result.traces.clone().unwrap())); - - logs.extend(error_call_result.logs); - } - Ok(()) -} diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 521fb6722..37d433a31 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -9,7 +9,7 @@ use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; use foundry_config::InvariantConfig; use foundry_evm_core::{ constants::{CALLER, CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME}, - utils::{get_function, StateChangeset}, + utils::get_function, }; use foundry_evm_fuzz::{ invariant::{ @@ -28,17 +28,21 @@ use proptest::{ strategy::{BoxedStrategy, Strategy}, test_runner::{TestCaseError, TestRunner}, }; +use result::{assert_invariants, can_continue}; use revm::{primitives::HashMap, DatabaseCommit}; -use std::{borrow::Cow, cell::RefCell, collections::BTreeMap, sync::Arc}; +use shrink::shrink_sequence; +use std::{cell::RefCell, collections::BTreeMap, sync::Arc}; mod error; -use self::error::FailedInvariantCaseData; -pub use error::{InvariantFailures, InvariantFuzzError, InvariantFuzzTestResult}; +pub use error::{InvariantFailures, InvariantFuzzError}; -mod funcs; -mod shrink; +mod replay; +pub use replay::{replay_error, replay_run}; + +mod result; +pub use result::InvariantFuzzTestResult; -pub use funcs::{assert_invariants, replay_run}; +mod shrink; sol! { interface IInvariantTest { @@ -93,20 +97,6 @@ sol! { type InvariantPreparation = (EvmFuzzState, FuzzRunIdentifiedContracts, BoxedStrategy); -/// Enriched results of an invariant run check. -/// -/// Contains the success condition and call results of the last run -struct RichInvariantResults { - success: bool, - call_result: Option, -} - -impl RichInvariantResults { - fn new(success: bool, call_result: Option) -> Self { - Self { success, call_result } - } -} - /// Wrapper around any [`Executor`] implementor which provides fuzzing support using [`proptest`]. /// /// After instantiation, calling `fuzz` will proceed to hammer the deployed smart contracts with @@ -157,6 +147,10 @@ impl<'a> InvariantExecutor<'a> { return Err(eyre!("Invariant test function should have no inputs")) } + if !self.config.shrink_sequence { + error!(target: "forge::test", "shrink_sequence config is deprecated and will be removed, use shrink_run_limit = 0") + } + let (fuzz_state, targeted_contracts, strat) = self.prepare_fuzzing(&invariant_contract, fuzz_fixtures)?; @@ -270,29 +264,28 @@ impl<'a> InvariantExecutor<'a> { stipend: call_result.stipend, }); - let RichInvariantResults { success: can_continue, call_result: call_results } = - can_continue( - &invariant_contract, - &self.config, - call_result, - &executor, - &inputs, - &mut failures.borrow_mut(), - &targeted_contracts, - &state_changeset, - &mut run_traces, - ) - .map_err(|e| TestCaseError::fail(e.to_string()))?; + let result = can_continue( + &invariant_contract, + &self.config, + call_result, + &executor, + &inputs, + &mut failures.borrow_mut(), + &targeted_contracts, + &state_changeset, + &mut run_traces, + ) + .map_err(|e| TestCaseError::fail(e.to_string()))?; - if !can_continue || current_run == self.config.depth - 1 { + if !result.can_continue || current_run == self.config.depth - 1 { last_run_calldata.borrow_mut().clone_from(&inputs); } - if !can_continue { + if !result.can_continue { break } - *last_call_results.borrow_mut() = call_results; + *last_call_results.borrow_mut() = result.call_result; current_run += 1; } @@ -686,65 +679,3 @@ fn collect_data( state_changeset.insert(*sender, changed); } } - -/// Verifies that the invariant run execution can continue. -/// Returns the mapping of (Invariant Function Name -> Call Result, Logs, Traces) if invariants were -/// asserted. -#[allow(clippy::too_many_arguments)] -fn can_continue( - invariant_contract: &InvariantContract<'_>, - invariant_config: &InvariantConfig, - call_result: RawCallResult, - executor: &Executor, - calldata: &[BasicTxDetails], - failures: &mut InvariantFailures, - targeted_contracts: &FuzzRunIdentifiedContracts, - state_changeset: &StateChangeset, - run_traces: &mut Vec, -) -> Result { - let mut call_results = None; - - // Detect handler assertion failures first. - let handlers_failed = targeted_contracts.targets.lock().iter().any(|contract| { - !executor.is_success(*contract.0, false, Cow::Borrowed(state_changeset), false) - }); - - // Assert invariants IF the call did not revert and the handlers did not fail. - if !call_result.reverted && !handlers_failed { - if let Some(traces) = call_result.traces { - run_traces.push(traces); - } - - call_results = assert_invariants( - invariant_contract, - invariant_config, - targeted_contracts, - executor, - calldata, - failures, - )?; - if call_results.is_none() { - return Ok(RichInvariantResults::new(false, None)); - } - } else { - // Increase the amount of reverts. - failures.reverts += 1; - // If fail on revert is set, we must return immediately. - if invariant_config.fail_on_revert { - let case_data = FailedInvariantCaseData::new( - invariant_contract, - invariant_config, - targeted_contracts, - calldata, - call_result, - &[], - ); - failures.revert_reason = Some(case_data.revert_reason.clone()); - let error = InvariantFuzzError::Revert(case_data); - failures.error = Some(error); - - return Ok(RichInvariantResults::new(false, None)); - } - } - Ok(RichInvariantResults::new(true, call_results)) -} diff --git a/crates/evm/evm/src/executors/invariant/replay.rs b/crates/evm/evm/src/executors/invariant/replay.rs new file mode 100644 index 000000000..941df781a --- /dev/null +++ b/crates/evm/evm/src/executors/invariant/replay.rs @@ -0,0 +1,134 @@ +use super::{error::FailedInvariantCaseData, shrink_sequence}; +use crate::executors::Executor; +use alloy_dyn_abi::JsonAbiExt; +use alloy_primitives::Log; +use eyre::Result; +use foundry_common::{ContractsByAddress, ContractsByArtifact}; +use foundry_evm_core::constants::CALLER; +use foundry_evm_coverage::HitMaps; +use foundry_evm_fuzz::{ + invariant::{BasicTxDetails, InvariantContract}, + BaseCounterExample, CounterExample, +}; +use foundry_evm_traces::{load_contracts, TraceKind, Traces}; +use parking_lot::RwLock; +use proptest::test_runner::TestError; +use revm::primitives::U256; +use std::sync::Arc; + +/// Replays a call sequence for collecting logs and traces. +/// Returns counterexample to be used when the call sequence is a failed scenario. +#[allow(clippy::too_many_arguments)] +pub fn replay_run( + invariant_contract: &InvariantContract<'_>, + mut executor: Executor, + known_contracts: &ContractsByArtifact, + mut ided_contracts: ContractsByAddress, + logs: &mut Vec, + traces: &mut Traces, + coverage: &mut Option, + inputs: Vec, +) -> Result> { + // We want traces for a failed case. + executor.set_tracing(true); + + let mut counterexample_sequence = vec![]; + + // Replay each call from the sequence, collect logs, traces and coverage. + for (sender, (addr, bytes)) in inputs.iter() { + let call_result = + executor.call_raw_committing(*sender, *addr, bytes.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).merge(new_coverage)); + } else { + *coverage = Some(new_coverage); + } + } + + // Identify newly generated contracts, if they exist. + ided_contracts.extend(load_contracts( + vec![(TraceKind::Execution, call_result.traces.clone().unwrap())], + known_contracts, + )); + + // Create counter example to be used in failed case. + counterexample_sequence.push(BaseCounterExample::create( + *sender, + *addr, + bytes, + &ided_contracts, + call_result.traces, + )); + + // Replay invariant to collect logs and traces. + let error_call_result = executor.call_raw( + CALLER, + invariant_contract.address, + invariant_contract + .invariant_function + .abi_encode_input(&[]) + .expect("invariant should have no inputs") + .into(), + U256::ZERO, + )?; + traces.push((TraceKind::Execution, error_call_result.traces.clone().unwrap())); + logs.extend(error_call_result.logs); + } + + Ok((!counterexample_sequence.is_empty()) + .then_some(CounterExample::Sequence(counterexample_sequence))) +} + +/// Replays the error case, shrinks the failing sequence and collects all necessary traces. +#[allow(clippy::too_many_arguments)] +pub fn replay_error( + failed_case: &FailedInvariantCaseData, + invariant_contract: &InvariantContract<'_>, + mut executor: Executor, + known_contracts: &ContractsByArtifact, + ided_contracts: ContractsByAddress, + logs: &mut Vec, + traces: &mut Traces, + coverage: &mut Option, +) -> Result> { + match failed_case.test_error { + // Don't use at the moment. + TestError::Abort(_) => Ok(None), + TestError::Fail(_, ref calls) => { + // Shrink sequence of failed calls. + let calls = if failed_case.shrink_sequence { + shrink_sequence(failed_case, calls, &executor)? + } else { + trace!(target: "forge::test", "Shrinking disabled."); + calls.clone() + }; + + set_up_inner_replay(&mut executor, &failed_case.inner_sequence); + // Replay calls to get the counterexample and to collect logs, traces and coverage. + replay_run( + invariant_contract, + executor, + known_contracts, + ided_contracts, + logs, + traces, + coverage, + calls, + ) + } + } +} + +/// Sets up the calls generated by the internal fuzzer, if they exist. +fn set_up_inner_replay(executor: &mut Executor, inner_sequence: &[Option]) { + if let Some(fuzzer) = &mut executor.inspector.fuzzer { + if let Some(call_generator) = &mut fuzzer.call_generator { + call_generator.last_sequence = Arc::new(RwLock::new(inner_sequence.to_owned())); + call_generator.set_replay(true); + } + } +} diff --git a/crates/evm/evm/src/executors/invariant/result.rs b/crates/evm/evm/src/executors/invariant/result.rs new file mode 100644 index 000000000..8b2acc56c --- /dev/null +++ b/crates/evm/evm/src/executors/invariant/result.rs @@ -0,0 +1,156 @@ +use super::{error::FailedInvariantCaseData, InvariantFailures, InvariantFuzzError}; +use crate::executors::{Executor, RawCallResult}; +use alloy_dyn_abi::JsonAbiExt; +use eyre::Result; +use foundry_config::InvariantConfig; +use foundry_evm_core::{constants::CALLER, utils::StateChangeset}; +use foundry_evm_fuzz::{ + invariant::{BasicTxDetails, FuzzRunIdentifiedContracts, InvariantContract}, + FuzzedCases, +}; +use revm::primitives::U256; +use revm_inspectors::tracing::CallTraceArena; +use std::borrow::Cow; + +/// The outcome of an invariant fuzz test +#[derive(Debug)] +pub struct InvariantFuzzTestResult { + pub error: Option, + /// Every successful fuzz test case + pub cases: Vec, + /// Number of reverted fuzz calls + pub reverts: usize, + /// The entire inputs of the last run of the invariant campaign, used for + /// replaying the run for collecting traces. + pub last_run_inputs: Vec, + /// Additional traces used for gas report construction. + pub gas_report_traces: Vec>, +} + +/// Enriched results of an invariant run check. +/// +/// Contains the success condition and call results of the last run +pub(crate) struct RichInvariantResults { + pub(crate) can_continue: bool, + pub(crate) call_result: Option, +} + +impl RichInvariantResults { + fn new(can_continue: bool, call_result: Option) -> Self { + Self { can_continue, call_result } + } +} + +/// Given the executor state, asserts that no invariant has been broken. Otherwise, it fills the +/// external `invariant_failures.failed_invariant` map and returns a generic error. +/// Either returns the call result if successful, or nothing if there was an error. +pub(crate) fn assert_invariants( + invariant_contract: &InvariantContract<'_>, + invariant_config: &InvariantConfig, + targeted_contracts: &FuzzRunIdentifiedContracts, + executor: &Executor, + calldata: &[BasicTxDetails], + invariant_failures: &mut InvariantFailures, +) -> Result> { + let mut inner_sequence = vec![]; + + if let Some(fuzzer) = &executor.inspector.fuzzer { + if let Some(call_generator) = &fuzzer.call_generator { + inner_sequence.extend(call_generator.last_sequence.read().iter().cloned()); + } + } + + let func = invariant_contract.invariant_function; + let mut call_result = executor.call_raw( + CALLER, + invariant_contract.address, + func.abi_encode_input(&[]).expect("invariant should have no inputs").into(), + U256::ZERO, + )?; + + let is_err = !executor.is_raw_call_success( + invariant_contract.address, + Cow::Owned(call_result.state_changeset.take().unwrap()), + &call_result, + false, + ); + if is_err { + // We only care about invariants which we haven't broken yet. + if invariant_failures.error.is_none() { + let case_data = FailedInvariantCaseData::new( + invariant_contract, + invariant_config, + targeted_contracts, + calldata, + call_result, + &inner_sequence, + ); + invariant_failures.error = Some(InvariantFuzzError::BrokenInvariant(case_data)); + return Ok(None); + } + } + + Ok(Some(call_result)) +} + +/// Verifies that the invariant run execution can continue. +/// Returns the mapping of (Invariant Function Name -> Call Result, Logs, Traces) if invariants were +/// asserted. +#[allow(clippy::too_many_arguments)] +pub(crate) fn can_continue( + invariant_contract: &InvariantContract<'_>, + invariant_config: &InvariantConfig, + call_result: RawCallResult, + executor: &Executor, + calldata: &[BasicTxDetails], + failures: &mut InvariantFailures, + targeted_contracts: &FuzzRunIdentifiedContracts, + state_changeset: &StateChangeset, + run_traces: &mut Vec, +) -> Result { + let mut call_results = None; + + // Detect handler assertion failures first. + let handlers_failed = targeted_contracts.targets.lock().iter().any(|contract| { + !executor.is_success(*contract.0, false, Cow::Borrowed(state_changeset), false) + }); + + // Assert invariants IF the call did not revert and the handlers did not fail. + if !call_result.reverted && !handlers_failed { + if let Some(traces) = call_result.traces { + run_traces.push(traces); + } + + call_results = assert_invariants( + invariant_contract, + invariant_config, + targeted_contracts, + executor, + calldata, + failures, + )?; + if call_results.is_none() { + return Ok(RichInvariantResults::new(false, None)); + } + } else { + // Increase the amount of reverts. + failures.reverts += 1; + // If fail on revert is set, we must return immediately. + if invariant_config.fail_on_revert { + let case_data = FailedInvariantCaseData::new( + invariant_contract, + invariant_config, + targeted_contracts, + calldata, + call_result, + &[], + ); + failures.revert_reason = Some(case_data.revert_reason.clone()); + let error = InvariantFuzzError::Revert(case_data); + failures.error = Some(error); + + return Ok(RichInvariantResults::new(false, None)); + } + } + Ok(RichInvariantResults::new(true, call_results)) +} diff --git a/crates/evm/evm/src/executors/invariant/shrink.rs b/crates/evm/evm/src/executors/invariant/shrink.rs index 09f890c65..bfa592136 100644 --- a/crates/evm/evm/src/executors/invariant/shrink.rs +++ b/crates/evm/evm/src/executors/invariant/shrink.rs @@ -1,4 +1,9 @@ +use crate::executors::{invariant::error::FailedInvariantCaseData, Executor}; +use alloy_primitives::U256; +use foundry_evm_core::constants::CALLER; +use foundry_evm_fuzz::invariant::BasicTxDetails; use proptest::bits::{BitSetLike, VarBitSet}; +use std::borrow::Cow; #[derive(Clone, Copy, Debug)] struct Shrink { @@ -10,7 +15,7 @@ struct Shrink { /// If the failure is still reproducible with removed call then moves to the next one. /// If the failure is not reproducible then restore removed call and moves to next one. #[derive(Debug)] -pub(crate) struct CallSequenceShrinker { +struct CallSequenceShrinker { /// Length of call sequence to be shrinked. call_sequence_len: usize, /// Call ids contained in current shrinked sequence. @@ -22,7 +27,7 @@ pub(crate) struct CallSequenceShrinker { } impl CallSequenceShrinker { - pub(crate) fn new(call_sequence_len: usize) -> Self { + fn new(call_sequence_len: usize) -> Self { Self { call_sequence_len, included_calls: VarBitSet::saturated(call_sequence_len), @@ -32,12 +37,12 @@ impl CallSequenceShrinker { } /// Return candidate shrink sequence to be tested, by removing ids from original sequence. - pub(crate) fn current(&self) -> impl Iterator + '_ { + fn current(&self) -> impl Iterator + '_ { (0..self.call_sequence_len).filter(|&call_id| self.included_calls.test(call_id)) } /// Removes next call from sequence. - pub(crate) fn simplify(&mut self) -> bool { + fn simplify(&mut self) -> bool { if self.shrink.call_index >= self.call_sequence_len { // We reached the end of call sequence, nothing left to simplify. false @@ -53,7 +58,7 @@ impl CallSequenceShrinker { } /// Reverts removed call from sequence and tries to simplify next call. - pub(crate) fn complicate(&mut self) -> bool { + fn complicate(&mut self) -> bool { match self.prev_shrink { Some(shrink) => { // Undo the last call removed. @@ -66,3 +71,82 @@ impl CallSequenceShrinker { } } } + +/// Shrinks the failure case to its smallest sequence of calls. +/// +/// Maximal shrinkage is guaranteed if the shrink_run_limit is not set to a value lower than the +/// length of failed call sequence. +/// +/// The shrinked call sequence always respect the order failure is reproduced as it is tested +/// top-down. +pub(crate) fn shrink_sequence( + failed_case: &FailedInvariantCaseData, + calls: &[BasicTxDetails], + executor: &Executor, +) -> eyre::Result> { + trace!(target: "forge::test", "Shrinking."); + + // Special case test: the invariant is *unsatisfiable* - it took 0 calls to + // break the invariant -- consider emitting a warning. + let error_call_result = + executor.call_raw(CALLER, failed_case.addr, failed_case.func.clone(), U256::ZERO)?; + if error_call_result.reverted { + return Ok(vec![]); + } + + let mut shrinker = CallSequenceShrinker::new(calls.len()); + for _ in 0..failed_case.shrink_run_limit { + // Check candidate sequence result. + match check_sequence(failed_case, executor.clone(), calls, shrinker.current().collect()) { + // If candidate sequence still fails then shrink more if possible. + Ok(false) if !shrinker.simplify() => break, + // If candidate sequence pass then restore last removed call and shrink other + // calls if possible. + Ok(true) if !shrinker.complicate() => break, + _ => {} + } + } + + Ok(shrinker.current().map(|idx| &calls[idx]).cloned().collect()) +} + +/// Checks if the shrinked sequence fails test, if it does then we can try simplifying more. +fn check_sequence( + failed_case: &FailedInvariantCaseData, + mut executor: Executor, + calls: &[BasicTxDetails], + sequence: Vec, +) -> eyre::Result { + let mut sequence_failed = false; + // Apply the shrinked candidate sequence. + for call_index in sequence { + let (sender, (addr, bytes)) = &calls[call_index]; + let call_result = + executor.call_raw_committing(*sender, *addr, bytes.clone(), U256::ZERO)?; + if call_result.reverted && failed_case.fail_on_revert { + // Candidate sequence fails test. + // We don't have to apply remaining calls to check sequence. + sequence_failed = true; + break; + } + } + // Return without checking the invariant if we already have failing sequence. + if sequence_failed { + return Ok(false); + }; + + // Check the invariant for candidate sequence. + // If sequence fails then we can continue with shrinking - the removed call does not affect + // failure. + // + // If sequence doesn't fail then we have to restore last removed call and continue with next + // call - removed call is a required step for reproducing the failure. + let mut call_result = + executor.call_raw(CALLER, failed_case.addr, failed_case.func.clone(), U256::ZERO)?; + Ok(executor.is_raw_call_success( + failed_case.addr, + Cow::Owned(call_result.state_changeset.take().unwrap()), + &call_result, + false, + )) +} diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index ed48bdc34..b29309bb0 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -20,7 +20,10 @@ use foundry_evm::{ decode::{decode_console_logs, RevertDecoder}, executors::{ fuzz::{CaseOutcome, CounterExampleOutcome, FuzzOutcome, FuzzedExecutor}, - invariant::{replay_run, InvariantExecutor, InvariantFuzzError, InvariantFuzzTestResult}, + invariant::{ + replay_error, replay_run, InvariantExecutor, InvariantFuzzError, + InvariantFuzzTestResult, + }, CallResult, EvmError, ExecutionErr, Executor, RawCallResult, }, fuzz::{fixture_name, invariant::InvariantContract, CounterExample, FuzzFixtures}, @@ -580,12 +583,17 @@ impl<'a> ContractRunner<'a> { Some(error) => match error { InvariantFuzzError::BrokenInvariant(case_data) | InvariantFuzzError::Revert(case_data) => { - match case_data.replay( + // Replay error to create counterexample and to collect logs, traces and + // coverage. + match replay_error( + &case_data, + &invariant_contract, self.executor.clone(), known_contracts, identified_contracts.clone(), &mut logs, &mut traces, + &mut coverage, ) { Ok(c) => counterexample = c, Err(err) => { @@ -607,7 +615,6 @@ impl<'a> ContractRunner<'a> { &mut logs, &mut traces, &mut coverage, - func.clone(), last_run_inputs.clone(), ) { error!(%err, "Failed to replay last invariant run"); From 39d68815ac9ae4458baf2c661d4d70ee4767cd70 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Mon, 29 Apr 2024 18:18:37 +0300 Subject: [PATCH 230/430] fix(fuzz): remove fuzz dir on forge clean (#7809) --- crates/forge/bin/main.rs | 5 +++++ crates/forge/tests/cli/cmd.rs | 18 +++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/crates/forge/bin/main.rs b/crates/forge/bin/main.rs index 1c7095026..2aaa82d62 100644 --- a/crates/forge/bin/main.rs +++ b/crates/forge/bin/main.rs @@ -82,6 +82,11 @@ fn main() -> Result<()> { ForgeSubcommand::Clean { root } => { let config = utils::load_config_with_root(root); config.project()?.cleanup()?; + + // Remove fuzz cache directory. + if let Some(fuzz_cache) = config.fuzz.failure_persist_dir { + let _ = std::fs::remove_dir_all(fuzz_cache); + } Ok(()) } ForgeSubcommand::Snapshot(cmd) => { diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index df6a306f5..a4deb8bce 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -2,7 +2,9 @@ use crate::constants::*; use foundry_compilers::{artifacts::Metadata, remappings::Remapping, ConfigurableContractArtifact}; -use foundry_config::{parse_with_profile, BasicConfig, Chain, Config, SolidityErrorCode}; +use foundry_config::{ + parse_with_profile, BasicConfig, Chain, Config, FuzzConfig, SolidityErrorCode, +}; use foundry_test_utils::{ foundry_compilers::PathStyle, rpc::next_etherscan_api_key, @@ -544,6 +546,20 @@ forgetest_init!(can_clean_config, |prj, cmd| { assert!(!artifact.exists()); }); +// checks that `clean` removes fuzz cache dir +forgetest_init!(can_clean_fuzz_cache, |prj, cmd| { + let config = Config { fuzz: FuzzConfig::new("cache/fuzz".into()), ..Default::default() }; + prj.write_config(config); + // default test contract is written in custom out directory + let cache_dir = prj.root().join("cache/fuzz"); + let _ = fs::create_dir(cache_dir.clone()); + assert!(cache_dir.exists()); + + cmd.forge_fuse().arg("clean"); + cmd.output(); + assert!(!cache_dir.exists()); +}); + // checks that extra output works forgetest_init!(can_emit_extra_output, |prj, cmd| { cmd.args(["build", "--extra-output", "metadata"]); From f0d9eeced8ec01045b2849ea2cc3c72773282d70 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Mon, 29 Apr 2024 20:25:38 +0300 Subject: [PATCH 231/430] chore(fuzz): delete fuzz dir relative to proj root (#7810) --- crates/forge/bin/main.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/forge/bin/main.rs b/crates/forge/bin/main.rs index 2aaa82d62..eb676b888 100644 --- a/crates/forge/bin/main.rs +++ b/crates/forge/bin/main.rs @@ -81,11 +81,12 @@ fn main() -> Result<()> { } ForgeSubcommand::Clean { root } => { let config = utils::load_config_with_root(root); - config.project()?.cleanup()?; + let proj = config.project()?; + proj.cleanup()?; // Remove fuzz cache directory. if let Some(fuzz_cache) = config.fuzz.failure_persist_dir { - let _ = std::fs::remove_dir_all(fuzz_cache); + let _ = std::fs::remove_dir_all(proj.root().join(fuzz_cache)); } Ok(()) } From a6e7fe0cbd1d55c069c0523c37eec3c349d279b7 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 30 Apr 2024 03:37:09 +0200 Subject: [PATCH 232/430] chore: sync `forge clean` and `--force` implementations (#7815) --- crates/config/src/lib.rs | 19 +++++++++++++++++-- crates/forge/bin/main.rs | 9 ++------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 65923ced7..7c8dcdb8c 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -20,7 +20,7 @@ use foundry_compilers::{ RevertStrings, Settings, SettingsMetadata, Severity, }, cache::SOLIDITY_FILES_CACHE_FILENAME, - error::SolcError, + error::{SolcError, SolcIoError}, remappings::{RelativeRemapping, Remapping}, ConfigurableArtifacts, EvmVersion, Project, ProjectPathsConfig, Solc, SolcConfig, }; @@ -691,7 +691,7 @@ impl Config { .build()?; if self.force { - project.cleanup()?; + self.cleanup(&project)?; } if let Some(solc) = self.ensure_solc()? { @@ -701,6 +701,21 @@ impl Config { Ok(project) } + /// Cleans the project. + pub fn cleanup(&self, project: &Project) -> Result<(), SolcError> { + project.cleanup()?; + + // Remove fuzz cache directory. + if let Some(fuzz_cache) = &self.fuzz.failure_persist_dir { + let path = project.root().join(fuzz_cache); + if path.exists() { + std::fs::remove_dir_all(&path).map_err(|e| SolcIoError::new(e, path))?; + } + } + + Ok(()) + } + /// Ensures that the configured version is installed if explicitly set /// /// If `solc` is [`SolcReq::Version`] then this will download and install the solc version if diff --git a/crates/forge/bin/main.rs b/crates/forge/bin/main.rs index eb676b888..f598c1212 100644 --- a/crates/forge/bin/main.rs +++ b/crates/forge/bin/main.rs @@ -81,13 +81,8 @@ fn main() -> Result<()> { } ForgeSubcommand::Clean { root } => { let config = utils::load_config_with_root(root); - let proj = config.project()?; - proj.cleanup()?; - - // Remove fuzz cache directory. - if let Some(fuzz_cache) = config.fuzz.failure_persist_dir { - let _ = std::fs::remove_dir_all(proj.root().join(fuzz_cache)); - } + let project = config.project()?; + config.cleanup(&project)?; Ok(()) } ForgeSubcommand::Snapshot(cmd) => { From ba399ae5ec1562367d689b6e060fda4996ad409e Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 30 Apr 2024 11:52:53 +0200 Subject: [PATCH 233/430] perf: support FigmentProviders settings (#7812) --- crates/config/src/lib.rs | 171 ++++++++++++++++++++++-------------- crates/config/src/macros.rs | 6 +- 2 files changed, 108 insertions(+), 69 deletions(-) diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 7c8dcdb8c..5e760d3b6 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -509,6 +509,86 @@ impl Config { 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; + let profile = Config::selected_profile(); + let mut figment = Figment::default().merge(DappHardhatDirProvider(&c.__root.0)); + + // merge global foundry.toml file + if let Some(global_toml) = Config::foundry_dir_toml().filter(|p| p.exists()) { + figment = Config::merge_toml_provider( + figment, + TomlFileProvider::new(None, global_toml).cached(), + profile.clone(), + ); + } + // merge local foundry.toml file + figment = Config::merge_toml_provider( + figment, + TomlFileProvider::new(Some("FOUNDRY_CONFIG"), c.__root.0.join(Config::FILE_NAME)) + .cached(), + profile.clone(), + ); + + // merge environment variables + figment = figment + .merge( + Env::prefixed("DAPP_") + .ignore(&["REMAPPINGS", "LIBRARIES", "FFI", "FS_PERMISSIONS"]) + .global(), + ) + .merge( + Env::prefixed("DAPP_TEST_") + .ignore(&["CACHE", "FUZZ_RUNS", "DEPTH", "FFI", "FS_PERMISSIONS"]) + .global(), + ) + .merge(DappEnvCompatProvider) + .merge(EtherscanEnvProvider::default()) + .merge( + Env::prefixed("FOUNDRY_") + .ignore(&["PROFILE", "REMAPPINGS", "LIBRARIES", "FFI", "FS_PERMISSIONS"]) + .map(|key| { + let key = key.as_str(); + if Config::STANDALONE_SECTIONS.iter().any(|section| { + key.starts_with(&format!("{}_", section.to_ascii_uppercase())) + }) { + key.replacen('_', ".", 1).into() + } else { + key.into() + } + }) + .global(), + ) + .select(profile.clone()); + + // only resolve remappings if all providers are requested + if providers.is_all() { + // we try to merge remappings after we've merged all other providers, this prevents + // redundant fs lookups to determine the default remappings that are eventually updated + // by other providers, like the toml file + let remappings = RemappingsProvider { + auto_detect_remappings: figment + .extract_inner::("auto_detect_remappings") + .unwrap_or(true), + lib_paths: figment + .extract_inner::>("libs") + .map(Cow::Owned) + .unwrap_or_else(|_| Cow::Borrowed(&c.libs)), + root: &c.__root.0, + remappings: figment.extract_inner::>("remappings"), + }; + figment = figment.merge(remappings); + } + + // normalize defaults + figment = c.normalize_defaults(figment); + + Figment::from(c).merge(figment).select(profile) + } + /// The config supports relative paths and tracks the root path separately see /// `Config::with_root` /// @@ -1658,77 +1738,32 @@ impl Config { } impl From for Figment { - fn from(mut c: Config) -> Figment { - let profile = Config::selected_profile(); - let mut figment = Figment::default().merge(DappHardhatDirProvider(&c.__root.0)); - - // merge global foundry.toml file - if let Some(global_toml) = Config::foundry_dir_toml().filter(|p| p.exists()) { - figment = Config::merge_toml_provider( - figment, - TomlFileProvider::new(None, global_toml).cached(), - profile.clone(), - ); - } - // merge local foundry.toml file - figment = Config::merge_toml_provider( - figment, - TomlFileProvider::new(Some("FOUNDRY_CONFIG"), c.__root.0.join(Config::FILE_NAME)) - .cached(), - profile.clone(), - ); - - // merge environment variables - figment = figment - .merge( - Env::prefixed("DAPP_") - .ignore(&["REMAPPINGS", "LIBRARIES", "FFI", "FS_PERMISSIONS"]) - .global(), - ) - .merge( - Env::prefixed("DAPP_TEST_") - .ignore(&["CACHE", "FUZZ_RUNS", "DEPTH", "FFI", "FS_PERMISSIONS"]) - .global(), - ) - .merge(DappEnvCompatProvider) - .merge(EtherscanEnvProvider::default()) - .merge( - Env::prefixed("FOUNDRY_") - .ignore(&["PROFILE", "REMAPPINGS", "LIBRARIES", "FFI", "FS_PERMISSIONS"]) - .map(|key| { - let key = key.as_str(); - if Config::STANDALONE_SECTIONS.iter().any(|section| { - key.starts_with(&format!("{}_", section.to_ascii_uppercase())) - }) { - key.replacen('_', ".", 1).into() - } else { - key.into() - } - }) - .global(), - ) - .select(profile.clone()); + fn from(c: Config) -> Figment { + c.to_figment(FigmentProviders::All) + } +} - // we try to merge remappings after we've merged all other providers, this prevents - // redundant fs lookups to determine the default remappings that are eventually updated by - // other providers, like the toml file - let remappings = RemappingsProvider { - auto_detect_remappings: figment - .extract_inner::("auto_detect_remappings") - .unwrap_or(true), - lib_paths: figment - .extract_inner::>("libs") - .map(Cow::Owned) - .unwrap_or_else(|_| Cow::Borrowed(&c.libs)), - root: &c.__root.0, - remappings: figment.extract_inner::>("remappings"), - }; - let merge = figment.merge(remappings); +/// 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 + #[default] + All, + /// Only include necessary providers that are useful for cast commands + /// + /// This will exclude more expensive providers such as remappings + Cast, +} - // normalize defaults - let merge = c.normalize_defaults(merge); +impl FigmentProviders { + /// Returns true if all providers should be included + pub const fn is_all(&self) -> bool { + matches!(self, Self::All) + } - Figment::from(c).merge(merge).select(profile) + /// Returns true if this is the cast preset + pub const fn is_cast(&self) -> bool { + matches!(self, Self::Cast) } } diff --git a/crates/config/src/macros.rs b/crates/config/src/macros.rs index b84876b8e..d7e206a97 100644 --- a/crates/config/src/macros.rs +++ b/crates/config/src/macros.rs @@ -184,12 +184,16 @@ macro_rules! merge_impl_figment_convert { } /// A macro to implement converters from a type to [`Config`] and [`figment::Figment`] +/// +/// Via [Config::to_figment](crate::Config::to_figment) and the +/// [Cast](crate::FigmentProviders::Cast) profile. #[macro_export] macro_rules! impl_figment_convert_cast { ($name:ty) => { impl<'a> From<&'a $name> for $crate::figment::Figment { fn from(args: &'a $name) -> Self { - $crate::Config::figment_with_root($crate::find_project_root_path(None).unwrap()) + $crate::Config::with_root($crate::find_project_root_path(None).unwrap()) + .to_figment($crate::FigmentProviders::Cast) .merge(args) } } From 45591b22ca6f14fb697d5a75311bb331e2a6e1ab Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 30 Apr 2024 19:29:50 +0200 Subject: [PATCH 234/430] perf: skip remappings when loading anvil config (#7821) --- crates/anvil/src/cmd.rs | 4 ++-- crates/config/src/lib.rs | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index c64d9471f..4a4b329da 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -9,7 +9,7 @@ use alloy_signer_wallet::coins_bip39::{English, Mnemonic}; use anvil_server::ServerConfig; use clap::Parser; use core::fmt; -use foundry_config::{Chain, Config}; +use foundry_config::{Chain, Config, FigmentProviders}; use futures::FutureExt; use rand::{rngs::StdRng, SeedableRng}; use std::{ @@ -516,7 +516,7 @@ pub struct AnvilEvmArgs { impl AnvilEvmArgs { pub fn resolve_rpc_alias(&mut self) { if let Some(fork_url) = &self.fork_url { - let config = Config::load(); + let config = Config::load_with_providers(FigmentProviders::Anvil); if let Some(Ok(url)) = config.get_rpc_url_with_alias(&fork_url.url) { self.fork_url = Some(ForkUrl { url: url.to_string(), block: fork_url.block }); } diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 5e760d3b6..17d15a6b1 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -455,6 +455,14 @@ impl Config { Config::from_provider(Config::figment()) } + /// Returns the current `Config` with the given `providers` preset + /// + /// See `Config::to_figment` + #[track_caller] + pub fn load_with_providers(providers: FigmentProviders) -> Self { + Config::default().to_figment(providers).extract().unwrap() + } + /// Returns the current `Config` /// /// See `Config::figment_with_root` @@ -1753,6 +1761,10 @@ pub enum FigmentProviders { /// /// This will exclude more expensive providers such as remappings Cast, + /// Only include necessary providers that are useful for anvil + /// + /// This will exclude more expensive providers such as remappings + Anvil, } impl FigmentProviders { From 721eb94e04bc2075b59d4221f09190d1897669d3 Mon Sep 17 00:00:00 2001 From: sealer3 <125761775+sealer3@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:33:31 +0000 Subject: [PATCH 235/430] fix(anvil): Otterscan searchTransactions behavior (#7807) --- crates/anvil/src/eth/otterscan/api.rs | 65 +++++++++---------------- crates/anvil/src/eth/otterscan/types.rs | 30 +++++++++++- crates/anvil/tests/it/otterscan.rs | 16 +++--- 3 files changed, 60 insertions(+), 51 deletions(-) diff --git a/crates/anvil/src/eth/otterscan/api.rs b/crates/anvil/src/eth/otterscan/api.rs index 400f99193..40d660012 100644 --- a/crates/anvil/src/eth/otterscan/api.rs +++ b/crates/anvil/src/eth/otterscan/api.rs @@ -9,9 +9,7 @@ use crate::eth::{ }; use alloy_primitives::{Address, Bytes, B256, U256}; use alloy_rpc_types::{Block, BlockId, BlockNumberOrTag as BlockNumber}; -use alloy_rpc_types_trace::parity::{ - Action, CallAction, CreateAction, CreateOutput, RewardAction, TraceOutput, -}; +use alloy_rpc_types_trace::parity::{Action, CreateAction, CreateOutput, TraceOutput}; use itertools::Itertools; impl EthApi { @@ -134,38 +132,31 @@ impl EthApi { let best = self.backend.best_number(); // we go from given block (defaulting to best) down to first block // considering only post-fork - let from = if block_number == 0 { best } else { block_number }; + let from = if block_number == 0 { best } else { block_number - 1 }; let to = self.get_fork().map(|f| f.block_number() + 1).unwrap_or(1); - let first_page = from == best; + let first_page = from >= best; let mut last_page = false; let mut res: Vec<_> = vec![]; for n in (to..=from).rev() { - if n == to { - last_page = true; - } - if let Some(traces) = self.backend.mined_parity_trace_block(n) { let hashes = traces .into_iter() .rev() - .filter_map(|trace| match trace.trace.action { - Action::Call(CallAction { from, to, .. }) - if from == address || to == address => - { - trace.transaction_hash - } - _ => None, - }) + .filter_map(|trace| OtsSearchTransactions::mentions_address(trace, address)) .unique(); - res.extend(hashes); - if res.len() >= page_size { break } + + res.extend(hashes); + } + + if n == to { + last_page = true; } } @@ -183,20 +174,17 @@ impl EthApi { let best = self.backend.best_number(); // we go from the first post-fork block, up to the tip - let from = if block_number == 0 { - self.get_fork().map(|f| f.block_number() + 1).unwrap_or(1) - } else { - block_number - }; + let first_block = self.get_fork().map(|f| f.block_number() + 1).unwrap_or(1); + let from = if block_number == 0 { first_block } else { block_number + 1 }; let to = best; - let first_page = from == best; + let mut first_page = from >= best; let mut last_page = false; let mut res: Vec<_> = vec![]; for n in from..=to { - if n == to { + if n == first_block { last_page = true; } @@ -204,30 +192,23 @@ impl EthApi { let hashes = traces .into_iter() .rev() - .filter_map(|trace| match trace.trace.action { - Action::Call(CallAction { from, to, .. }) - if from == address || to == address => - { - trace.transaction_hash - } - Action::Create(CreateAction { from, .. }) if from == address => { - trace.transaction_hash - } - Action::Reward(RewardAction { author, .. }) if author == address => { - trace.transaction_hash - } - _ => None, - }) + .filter_map(|trace| OtsSearchTransactions::mentions_address(trace, address)) .unique(); - res.extend(hashes); - if res.len() >= page_size { break } + + res.extend(hashes); + } + + if n == to { + first_page = true; } } + // Results are always sent in reverse chronological order, according to the Otterscan spec + res.reverse(); OtsSearchTransactions::build(res, &self.backend, first_page, last_page).await } diff --git a/crates/anvil/src/eth/otterscan/types.rs b/crates/anvil/src/eth/otterscan/types.rs index d7e75c02c..7fe337d97 100644 --- a/crates/anvil/src/eth/otterscan/types.rs +++ b/crates/anvil/src/eth/otterscan/types.rs @@ -2,9 +2,12 @@ use crate::eth::{ backend::mem::{storage::MinedTransaction, Backend}, error::{BlockchainError, Result}, }; -use alloy_primitives::{Address, Bytes, B256, U256 as rU256, U256}; +use alloy_primitives::{Address, Bytes, FixedBytes, B256, U256 as rU256, U256}; use alloy_rpc_types::{Block, BlockTransactions, Transaction, WithOtherFields}; -use alloy_rpc_types_trace::parity::{Action, CallType, LocalizedTransactionTrace}; +use alloy_rpc_types_trace::parity::{ + Action, CallAction, CallType, CreateAction, CreateOutput, LocalizedTransactionTrace, + RewardAction, TraceOutput, +}; use anvil_core::eth::transaction::ReceiptResponse; use foundry_evm::{revm::interpreter::InstructionResult, traces::CallKind}; use futures::future::join_all; @@ -251,6 +254,29 @@ impl OtsSearchTransactions { .collect::>>() .map(|receipts| Self { txs, receipts, first_page, last_page }) } + + pub fn mentions_address( + trace: LocalizedTransactionTrace, + address: Address, + ) -> Option> { + match (trace.trace.action, trace.trace.result) { + (Action::Call(CallAction { from, to, .. }), _) if from == address || to == address => { + trace.transaction_hash + } + (_, Some(TraceOutput::Create(CreateOutput { address: created_address, .. }))) + if created_address == address => + { + trace.transaction_hash + } + (Action::Create(CreateAction { from, .. }), _) if from == address => { + trace.transaction_hash + } + (Action::Reward(RewardAction { author, .. }), _) if author == address => { + trace.transaction_hash + } + _ => None, + } + } } impl OtsInternalOperation { diff --git a/crates/anvil/tests/it/otterscan.rs b/crates/anvil/tests/it/otterscan.rs index 8f4bc942f..c4b5d8c7a 100644 --- a/crates/anvil/tests/it/otterscan.rs +++ b/crates/anvil/tests/it/otterscan.rs @@ -586,17 +586,18 @@ async fn can_call_ots_search_transactions_before() { let page_size = 2; let mut block = 0; - for _ in 0..4 { + for i in 0..4 { let result = api.ots_search_transactions_before(sender, block, page_size).await.unwrap(); - assert!(result.txs.len() <= page_size); + assert_eq!(result.first_page, i == 0); + assert_eq!(result.last_page, i == 3); // check each individual hash result.txs.iter().for_each(|tx| { assert_eq!(hashes.pop(), Some(tx.hash)); }); - block = result.txs.last().unwrap().block_number.unwrap() - 1; + block = result.txs.last().unwrap().block_number.unwrap(); } assert!(hashes.is_empty()); @@ -626,17 +627,18 @@ async fn can_call_ots_search_transactions_after() { let page_size = 2; let mut block = 0; - for _ in 0..4 { + for i in 0..4 { let result = api.ots_search_transactions_after(sender, block, page_size).await.unwrap(); - assert!(result.txs.len() <= page_size); + assert_eq!(result.first_page, i == 3); + assert_eq!(result.last_page, i == 0); // check each individual hash - result.txs.iter().for_each(|tx| { + result.txs.iter().rev().for_each(|tx| { assert_eq!(hashes.pop_back(), Some(tx.hash)); }); - block = result.txs.last().unwrap().block_number.unwrap() + 1; + block = result.txs.first().unwrap().block_number.unwrap(); } assert!(hashes.is_empty()); From c0aac85ebb6b9480185f5aaea2a37d5b3e90b195 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 1 May 2024 06:40:30 +0200 Subject: [PATCH 236/430] fix: make trace printer work (#7824) --- crates/evm/evm/src/inspectors/stack.rs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index abcfee490..bcb1e5517 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -419,7 +419,13 @@ impl InspectorStack { ) -> CallOutcome { let result = outcome.result.result; call_inspectors_adjust_depth!( - [&mut self.fuzzer, &mut self.debugger, &mut self.tracer, &mut self.cheatcodes], + [ + &mut self.fuzzer, + &mut self.debugger, + &mut self.tracer, + &mut self.cheatcodes, + &mut self.printer, + ], |inspector| { let new_outcome = inspector.call_end(ecx, inputs, outcome.clone()); @@ -595,7 +601,7 @@ impl Inspector<&mut DB> for InspectorStack { fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<&mut DB>) { call_inspectors_adjust_depth!( #[no_ret] - [&mut self.coverage, &mut self.tracer, &mut self.cheatcodes], + [&mut self.coverage, &mut self.tracer, &mut self.cheatcodes, &mut self.printer], |inspector| inspector.initialize_interp(interpreter, ecx), self, ecx @@ -611,6 +617,7 @@ impl Inspector<&mut DB> for InspectorStack { &mut self.tracer, &mut self.coverage, &mut self.cheatcodes, + &mut self.printer, ], |inspector| inspector.step(interpreter, ecx), self, @@ -621,7 +628,7 @@ impl Inspector<&mut DB> for InspectorStack { fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<&mut DB>) { call_inspectors_adjust_depth!( #[no_ret] - [&mut self.tracer, &mut self.cheatcodes, &mut self.chisel_state], + [&mut self.tracer, &mut self.cheatcodes, &mut self.chisel_state, &mut self.printer], |inspector| inspector.step_end(interpreter, ecx), self, ecx @@ -631,7 +638,7 @@ impl Inspector<&mut DB> for InspectorStack { fn log(&mut self, ecx: &mut EvmContext<&mut DB>, log: &Log) { call_inspectors_adjust_depth!( #[no_ret] - [&mut self.tracer, &mut self.log_collector, &mut self.cheatcodes], + [&mut self.tracer, &mut self.log_collector, &mut self.cheatcodes, &mut self.printer], |inspector| inspector.log(ecx, log), self, ecx @@ -655,6 +662,7 @@ impl Inspector<&mut DB> for InspectorStack { &mut self.tracer, &mut self.log_collector, &mut self.cheatcodes, + &mut self.printer, ], |inspector| { let mut out = None; @@ -760,7 +768,7 @@ impl Inspector<&mut DB> for InspectorStack { let result = outcome.result.result; call_inspectors_adjust_depth!( - [&mut self.debugger, &mut self.tracer, &mut self.cheatcodes], + [&mut self.debugger, &mut self.tracer, &mut self.cheatcodes, &mut self.printer], |inspector| { let new_outcome = inspector.create_end(ecx, call, outcome.clone()); @@ -779,9 +787,9 @@ impl Inspector<&mut DB> for InspectorStack { } fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) { - call_inspectors!([&mut self.tracer], |inspector| Inspector::::selfdestruct( - inspector, contract, target, value - )); + call_inspectors!([&mut self.tracer, &mut self.printer], |inspector| { + Inspector::::selfdestruct(inspector, contract, target, value) + }); } } From 5885dbc38c27c2bd80fcdcd87a47e06398752662 Mon Sep 17 00:00:00 2001 From: Alisina Bahadori Date: Wed, 1 May 2024 06:18:49 -0400 Subject: [PATCH 237/430] Fix `eth_signTransaction` request and response (#7804) * Fix eth_signTransaction request and response * fixup! Fix eth_signTransaction request and response * Hardcode test nonce and fee values * Fix test signed result --- crates/anvil/core/src/eth/mod.rs | 2 +- crates/anvil/src/eth/api.rs | 7 +++---- crates/anvil/tests/it/sign.rs | 24 ++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/crates/anvil/core/src/eth/mod.rs b/crates/anvil/core/src/eth/mod.rs index 4c485f56b..837850653 100644 --- a/crates/anvil/core/src/eth/mod.rs +++ b/crates/anvil/core/src/eth/mod.rs @@ -142,7 +142,7 @@ pub enum EthRequest { #[cfg_attr(feature = "serde", serde(rename = "eth_sign", alias = "personal_sign"))] EthSign(Address, Bytes), - #[cfg_attr(feature = "serde", serde(rename = "eth_signTransaction"))] + #[cfg_attr(feature = "serde", serde(rename = "eth_signTransaction", with = "sequence"))] EthSignTransaction(Box>), /// Signs data via [EIP-712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md). diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 261436b52..70457b52b 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -32,6 +32,7 @@ use crate::{ ClientFork, LoggingManager, Miner, MiningMode, StorageInfo, }; use alloy_dyn_abi::TypedData; +use alloy_eips::eip2718::Encodable2718; use alloy_network::eip2718::Decodable2718; use alloy_primitives::{Address, Bytes, TxHash, TxKind, B256, B64, U256, U64}; use alloy_rpc_types::{ @@ -862,10 +863,8 @@ impl EthApi { let request = self.build_typed_tx_request(request, nonce)?; - let signer = self.get_signer(from).ok_or(BlockchainError::NoSignerAvailable)?; - let signature = - alloy_primitives::hex::encode(signer.sign_transaction(request, &from)?.as_bytes()); - Ok(format!("0x{signature}")) + let signed_transaction = self.sign_request(&from, request)?.encoded_2718(); + Ok(alloy_primitives::hex::encode_prefixed(signed_transaction)) } /// Sends a transaction diff --git a/crates/anvil/tests/it/sign.rs b/crates/anvil/tests/it/sign.rs index b59b22ffb..1417fec52 100644 --- a/crates/anvil/tests/it/sign.rs +++ b/crates/anvil/tests/it/sign.rs @@ -282,6 +282,30 @@ async fn can_sign_typed_data_os() { ); } +#[tokio::test(flavor = "multi_thread")] +async fn can_sign_transaction() { + let (api, handle) = spawn(NodeConfig::test()).await; + + let accounts = handle.dev_wallets().collect::>(); + let from = accounts[0].address(); + let to = accounts[1].address(); + + // craft the tx + // specify the `from` field so that the client knows which account to use + let tx = TransactionRequest::default() + .nonce(10) + .max_fee_per_gas(100) + .max_priority_fee_per_gas(101) + .to(to) + .value(U256::from(1001u64)) + .from(from); + let tx = WithOtherFields::new(tx); + // sign it via the eth_signTransaction API + let signed_tx = api.sign_transaction(tx).await.unwrap(); + + assert_eq!(signed_tx, "0x02f868827a690a65648252089470997970c51812dc3a010c7d01b50e0d17dc79c88203e980c082f4f6a0e4de88aefcf87ccb04466e60de66a83192e46aa26177d5ea35efbfd43fd0ecdca00e3148e0e8e0b9a6f9b329efd6e30c4a461920f3a27497be3dbefaba996601da"); +} + #[tokio::test(flavor = "multi_thread")] async fn rejects_different_chain_id() { let (_api, handle) = spawn(NodeConfig::test()).await; From cafc2606a2187a42b236df4aa65f4e8cdfcea970 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 1 May 2024 20:38:28 +0200 Subject: [PATCH 238/430] fix: set value before estimating gas (#7829) --- crates/forge/bin/cmd/create.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index 46260ddef..460dbdea7 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -242,17 +242,17 @@ impl CreateArgs { provider.get_transaction_count(deployer_address, BlockId::latest()).await }?); + // set tx value if specified + if let Some(value) = self.tx.value { + deployer.tx.set_value(value); + } + deployer.tx.set_gas_limit(if let Some(gas_limit) = self.tx.gas_limit { Ok(gas_limit.to()) } else { provider.estimate_gas(&deployer.tx, BlockId::latest()).await }?); - // set tx value if specified - if let Some(value) = self.tx.value { - deployer.tx.set_value(value); - } - if is_legacy { let gas_price = if let Some(gas_price) = self.tx.gas_price { gas_price.to() From 451d98453b331e40b8a08fbcb165919ca0dae535 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 2 May 2024 12:40:43 +0200 Subject: [PATCH 239/430] fix(forge): require at least one dependency in `remove` (#7832) --- crates/forge/bin/cmd/remove.rs | 1 + crates/forge/bin/cmd/update.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/forge/bin/cmd/remove.rs b/crates/forge/bin/cmd/remove.rs index 22343ef7d..a4d62b9f7 100644 --- a/crates/forge/bin/cmd/remove.rs +++ b/crates/forge/bin/cmd/remove.rs @@ -11,6 +11,7 @@ use std::path::PathBuf; #[derive(Clone, Debug, Parser)] pub struct RemoveArgs { /// The dependencies you want to remove. + #[arg(required = true)] dependencies: Vec, /// The project's root path. diff --git a/crates/forge/bin/cmd/update.rs b/crates/forge/bin/cmd/update.rs index 0cc25b6b6..05f8e0eb2 100644 --- a/crates/forge/bin/cmd/update.rs +++ b/crates/forge/bin/cmd/update.rs @@ -44,7 +44,7 @@ impl UpdateArgs { git.submodule_update(self.force, true, false, false, paths)?; // initialize submodules of each submodule recursively (otherwise direct submodule // dependencies will revert to last commit) - git.submodule_foreach(false, "git submodule update --init --progress --recursive ") + git.submodule_foreach(false, "git submodule update --init --progress --recursive") } } } From 97186b53038a887a954766a234e83feec9a26fd1 Mon Sep 17 00:00:00 2001 From: sealer3 <125761775+sealer3@users.noreply.github.com> Date: Thu, 2 May 2024 18:04:40 +0000 Subject: [PATCH 240/430] fix(anvil): `ots_getTransactionError` default return (#7837) --- crates/anvil/src/eth/otterscan/api.rs | 6 +++--- crates/anvil/tests/it/otterscan.rs | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/crates/anvil/src/eth/otterscan/api.rs b/crates/anvil/src/eth/otterscan/api.rs index 40d660012..d2a1c7f0c 100644 --- a/crates/anvil/src/eth/otterscan/api.rs +++ b/crates/anvil/src/eth/otterscan/api.rs @@ -62,16 +62,16 @@ impl EthApi { } /// Given a transaction hash, returns its raw revert reason. - pub async fn ots_get_transaction_error(&self, hash: B256) -> Result> { + pub async fn ots_get_transaction_error(&self, hash: B256) -> Result { node_info!("ots_getTransactionError"); if let Some(receipt) = self.backend.mined_transaction_receipt(hash) { if !receipt.inner.inner.as_receipt_with_bloom().receipt.status { - return Ok(receipt.out.map(|b| b.0.into())) + return Ok(receipt.out.map(|b| b.0.into()).unwrap_or(Bytes::default())) } } - Ok(Default::default()) + Ok(Bytes::default()) } /// For simplicity purposes, we return the entire block instead of emptying the values that diff --git a/crates/anvil/tests/it/otterscan.rs b/crates/anvil/tests/it/otterscan.rs index c4b5d8c7a..07af1b8ea 100644 --- a/crates/anvil/tests/it/otterscan.rs +++ b/crates/anvil/tests/it/otterscan.rs @@ -465,6 +465,26 @@ contract Contract { // assert!(res.unwrap_err().to_string().contains("0x8d6ea8be00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012526576657274537472696e67466f6f4261720000000000000000000000000000")); } +#[tokio::test(flavor = "multi_thread")] +async fn ots_get_transaction_error_no_error() { + let (api, handle) = spawn(NodeConfig::test()).await; + let wallets = handle.dev_wallets().collect::>(); + let signer: EthereumSigner = wallets[0].clone().into(); + let sender = wallets[0].address(); + + let provider = ws_provider_with_signer(&handle.ws_endpoint(), signer); + + // Send a successful transaction + let tx = + TransactionRequest::default().from(sender).to(Address::random()).value(U256::from(100)); + let tx = WithOtherFields::new(tx); + let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); + + let res = api.ots_get_transaction_error(receipt.transaction_hash).await; + assert!(res.is_ok()); + assert_eq!(res.unwrap().to_string(), "0x"); +} + #[tokio::test(flavor = "multi_thread")] async fn can_call_ots_get_block_details() { let (api, handle) = spawn(NodeConfig::test()).await; From 7a676f801365ad1c5d347b8300bba805e727897d Mon Sep 17 00:00:00 2001 From: evalir Date: Thu, 2 May 2024 17:09:23 -0400 Subject: [PATCH 241/430] feat: Anvil Cancun support (#7242) * feat(anvil-core): EIP4844 variant support * chore: proper support when converting txs * feat: add more type support * chore: lock * feat: missing type conversions, decoding test * use correct eip check * force no blob hashes for eip1559 * feat: support sidecar with 4844 types * fmt * feat: turn on c-kzg revm feature * chore: add new invalid tx errors * feat: execution validation steps * feat: enable c-kzg * feat: use main branch for consensus, update * chore: rename * lockfile * fmt * fmt * fmt * clippy * feat: update blob fees * set current blob excess gas and price when creating block * blob gas checks * clippy * chore: remove unneeded fns * chore: handle fee history * chore: add excess blob gas and price to feehistory cache * chore: remove unused * chore: properly sum cumulative blob gas * chore: rewrite validation checks * chore: handle eip4844 variant when decoding * max blob validation check * chore: correct and rename blob fee capp err * feat: fee history response changes * docs * several fee fixes * chore: set blob gas used on rpc response * fix: use primitives types * fix: satisfy clippy * feat(anvil/tests): can_send_eip4844_transaction - fails * use sidecar builder in tests * fix: tx_req_to_typed * nits * fix: return `blob_gas_price` and `blob_gas_used` in tx receipt * nits * fix: gas_price calc in backend::tx_build and nits * feat(anvil-tests): `can_send_multiple_blobs_in_one_tx`, `cannot_exceed_six_blobs` * nits * fix: eip4844 test * nits Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * feat(anvil-test): 4844 - test should fail. * fix(anvil): check MAX_BLOB_GAS_PER_BLOCK in tx executor * nits * fix: blob error handling * nits * type nits * nit --------- Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- Cargo.lock | 3 + crates/anvil/Cargo.toml | 6 + crates/anvil/core/Cargo.toml | 1 + crates/anvil/core/src/eth/block.rs | 12 +- crates/anvil/core/src/eth/transaction/mod.rs | 45 ++++- crates/anvil/src/config.rs | 34 +++- crates/anvil/src/eth/api.rs | 47 ++++- crates/anvil/src/eth/backend/executor.rs | 43 +++++ crates/anvil/src/eth/backend/mem/mod.rs | 86 +++++++-- crates/anvil/src/eth/error.rs | 36 +++- crates/anvil/src/eth/fees.rs | 144 ++++++++++++-- crates/anvil/tests/it/eip4844.rs | 192 +++++++++++++++++++ crates/anvil/tests/it/main.rs | 2 +- crates/evm/core/Cargo.toml | 3 +- crates/evm/evm/Cargo.toml | 8 +- crates/evm/fuzz/Cargo.toml | 2 +- 16 files changed, 613 insertions(+), 51 deletions(-) create mode 100644 crates/anvil/tests/it/eip4844.rs diff --git a/Cargo.lock b/Cargo.lock index 97c3c3e93..67c488479 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -741,6 +741,7 @@ dependencies = [ "parking_lot", "pretty_assertions", "rand 0.8.5", + "revm", "serde", "serde_json", "serde_repr", @@ -7082,10 +7083,12 @@ dependencies = [ "bitvec", "c-kzg", "cfg-if", + "derive_more", "dyn-clone", "enumn", "hashbrown", "hex", + "once_cell", "serde", ] diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index afbf940fb..7739e8fa2 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -36,6 +36,12 @@ foundry-evm.workspace = true bytes = "1.4.0" k256.workspace = true ethers = { workspace = true, features = ["rustls", "ws", "ipc", "optimism"] } +revm = { workspace = true, features = [ + "std", + "serde", + "memory_limit", + "c-kzg", +] } alloy-primitives = { workspace = true, features = ["serde"] } alloy-consensus = { workspace = true, features = ["k256", "kzg"] } alloy-contract = { workspace = true, features = ["pubsub"] } diff --git a/crates/anvil/core/Cargo.toml b/crates/anvil/core/Cargo.toml index feddead95..90738112e 100644 --- a/crates/anvil/core/Cargo.toml +++ b/crates/anvil/core/Cargo.toml @@ -16,6 +16,7 @@ revm = { workspace = true, default-features = false, features = [ "std", "serde", "memory_limit", + "c-kzg", ] } alloy-primitives = { workspace = true, features = ["serde"] } diff --git a/crates/anvil/core/src/eth/block.rs b/crates/anvil/core/src/eth/block.rs index fd7e530e0..a05c2cae4 100644 --- a/crates/anvil/core/src/eth/block.rs +++ b/crates/anvil/core/src/eth/block.rs @@ -67,9 +67,9 @@ impl Block { extra_data: partial_header.extra_data, mix_hash: partial_header.mix_hash, withdrawals_root: None, - blob_gas_used: None, - excess_blob_gas: None, - parent_beacon_block_root: None, + blob_gas_used: partial_header.blob_gas_used, + excess_blob_gas: partial_header.excess_blob_gas, + parent_beacon_block_root: partial_header.parent_beacon_block_root, nonce: partial_header.nonce, base_fee_per_gas: partial_header.base_fee, }, @@ -94,6 +94,9 @@ pub struct PartialHeader { pub timestamp: u64, pub extra_data: Bytes, pub mix_hash: B256, + pub blob_gas_used: Option, + pub excess_blob_gas: Option, + pub parent_beacon_block_root: Option, pub nonce: B64, pub base_fee: Option, } @@ -115,6 +118,9 @@ impl From
for PartialHeader { mix_hash: value.mix_hash, nonce: value.nonce, base_fee: value.base_fee_per_gas, + blob_gas_used: value.blob_gas_used, + excess_blob_gas: value.excess_blob_gas, + parent_beacon_block_root: value.parent_beacon_block_root, } } } diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 3c60bb3a6..c52beb16e 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -20,7 +20,7 @@ use revm::{ primitives::{CreateScheme, OptimismFields, TransactTo, TxEnv}, }; use serde::{Deserialize, Serialize}; -use std::ops::Deref; +use std::ops::{Deref, Mul}; pub mod optimism; @@ -45,12 +45,12 @@ pub fn transaction_request_to_typed( max_fee_per_gas, max_priority_fee_per_gas, max_fee_per_blob_gas, - mut blob_versioned_hashes, + blob_versioned_hashes, gas, value, input, nonce, - mut access_list, + access_list, sidecar, transaction_type, .. @@ -77,9 +77,9 @@ pub fn transaction_request_to_typed( gas_price, max_fee_per_gas, max_priority_fee_per_gas, - access_list.take(), + access_list.as_ref(), max_fee_per_blob_gas, - blob_versioned_hashes.take(), + blob_versioned_hashes.as_ref(), sidecar, to, ) { @@ -129,7 +129,7 @@ pub fn transaction_request_to_typed( })) } // EIP4844 - (Some(3), None, _, _, _, Some(_), Some(_), Some(sidecar), Some(to)) => { + (Some(3), None, _, _, _, Some(_), Some(_), Some(sidecar), to) => { let tx = TxEip4844 { nonce: nonce.unwrap_or_default(), max_fee_per_gas: max_fee_per_gas.unwrap_or_default(), @@ -138,7 +138,7 @@ pub fn transaction_request_to_typed( gas_limit: gas.unwrap_or_default(), value: value.unwrap_or(U256::ZERO), input: input.into_input().unwrap_or_default(), - to: match to { + to: match to.unwrap_or(TxKind::Create) { TxKind::Call(to) => to, TxKind::Create => Address::ZERO, }, @@ -619,9 +619,9 @@ pub enum TypedTransaction { } impl TypedTransaction { - /// Returns true if the transaction uses dynamic fees: EIP1559 + /// Returns true if the transaction uses dynamic fees: EIP1559 or EIP4844 pub fn is_dynamic_fee(&self) -> bool { - matches!(self, TypedTransaction::EIP1559(_)) + matches!(self, TypedTransaction::EIP1559(_)) || matches!(self, TypedTransaction::EIP4844(_)) } pub fn gas_price(&self) -> u128 { @@ -676,8 +676,33 @@ impl TypedTransaction { } /// Max cost of the transaction + /// It is the gas limit multiplied by the gas price, + /// and if the transaction is EIP-4844, the result of (total blob gas cost * max fee per blob + /// gas) is also added pub fn max_cost(&self) -> u128 { - self.gas_limit().saturating_mul(self.gas_price()) + let mut max_cost = self.gas_limit().saturating_mul(self.gas_price()); + + if self.is_eip4844() { + max_cost = max_cost.saturating_add( + self.blob_gas().unwrap_or(0).mul(self.max_fee_per_blob_gas().unwrap_or(0)), + ) + } + + max_cost + } + + pub fn blob_gas(&self) -> Option { + match self { + TypedTransaction::EIP4844(tx) => Some(tx.tx().tx().blob_gas() as u128), + _ => None, + } + } + + pub fn max_fee_per_blob_gas(&self) -> Option { + match self { + TypedTransaction::EIP4844(tx) => Some(tx.tx().tx().max_fee_per_blob_gas), + _ => None, + } } /// Returns a helper type that contains commonly used values as fields diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index ffc648199..6905b69dc 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -39,6 +39,7 @@ use foundry_evm::{ }; use parking_lot::RwLock; use rand::thread_rng; +use revm::primitives::BlobExcessGasAndPrice; use serde_json::{json, to_writer, Value}; use std::{ collections::HashMap, @@ -98,6 +99,8 @@ pub struct NodeConfig { pub gas_price: Option, /// Default base fee pub base_fee: Option, + /// Default blob excess gas and price + pub blob_excess_gas_and_price: Option, /// The hardfork to use pub hardfork: Option, /// Signer accounts that will be initialised with `genesis_balance` in the genesis block @@ -398,6 +401,7 @@ impl Default for NodeConfig { fork_block_number: None, account_generator: None, base_fee: None, + blob_excess_gas_and_price: None, enable_tracing: true, enable_steps_tracing: false, enable_auto_impersonate: false, @@ -447,6 +451,17 @@ impl NodeConfig { self.gas_price.unwrap_or(INITIAL_GAS_PRICE) } + pub fn get_blob_excess_gas_and_price(&self) -> BlobExcessGasAndPrice { + if let Some(blob_excess_gas_and_price) = &self.blob_excess_gas_and_price { + blob_excess_gas_and_price.clone() + } else if let Some(excess_blob_gas) = self.genesis.as_ref().and_then(|g| g.excess_blob_gas) + { + BlobExcessGasAndPrice::new(excess_blob_gas as u64) + } else { + BlobExcessGasAndPrice { blob_gasprice: 0, excess_blob_gas: 0 } + } + } + /// Returns the base fee to use pub fn get_hardfork(&self) -> Hardfork { self.hardfork.unwrap_or_default() @@ -876,8 +891,12 @@ impl NodeConfig { }; let mut env = EnvWithHandlerCfg::new(Box::new(env), cfg.handler_cfg); - let fees = - FeeManager::new(cfg.handler_cfg.spec_id, self.get_base_fee(), self.get_gas_price()); + let fees = FeeManager::new( + cfg.handler_cfg.spec_id, + self.get_base_fee(), + self.get_gas_price(), + self.get_blob_excess_gas_and_price(), + ); let (db, fork): (Arc>>, Option) = if let Some(eth_rpc_url) = self.eth_rpc_url.clone() { @@ -1075,6 +1094,17 @@ latest block number: {latest_block}" // update next base fee fees.set_base_fee(next_block_base_fee); } + if let (Some(blob_excess_gas), Some(blob_gas_used)) = + (block.header.excess_blob_gas, block.header.blob_gas_used) + { + env.block.blob_excess_gas_and_price = + Some(BlobExcessGasAndPrice::new(blob_excess_gas as u64)); + let next_block_blob_excess_gas = + fees.get_next_block_blob_excess_gas(blob_excess_gas, blob_gas_used); + fees.set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new( + next_block_blob_excess_gas, + )); + } } // use remote gas price diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 70457b52b..cc77746da 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -28,9 +28,10 @@ use crate::{ }, filter::{EthFilter, Filters, LogsFilter}, mem::transaction_build, - revm::primitives::Output, + revm::primitives::{BlobExcessGasAndPrice, Output}, ClientFork, LoggingManager, Miner, MiningMode, StorageInfo, }; +use alloy_consensus::TxEip4844Variant; use alloy_dyn_abi::TypedData; use alloy_eips::eip2718::Encodable2718; use alloy_network::eip2718::Decodable2718; @@ -550,6 +551,11 @@ impl EthApi { Ok(U256::from(self.backend.gas_price())) } + /// Returns the excess blob gas and current blob gas price + pub fn excess_blob_gas_and_price(&self) -> Result> { + Ok(self.backend.excess_blob_gas_and_price()) + } + /// Returns a fee per gas that is an estimate of how much you can pay as a priority fee, or /// 'tip', to get a transaction included in the current block. /// @@ -977,6 +983,7 @@ impl EthApi { request.gas_price, request.max_fee_per_gas, request.max_priority_fee_per_gas, + request.max_fee_per_blob_gas, )? .or_zero_fees(); // this can be blocking for a bit, especially in forking mode @@ -1292,6 +1299,8 @@ impl EthApi { // if let Some(block) = fee_history.get(&n) { response.base_fee_per_gas.push(block.base_fee); + response.base_fee_per_blob_gas.push(block.base_fee_per_blob_gas.unwrap_or(0)); + response.blob_gas_used_ratio.push(block.blob_gas_used_ratio); response.gas_used_ratio.push(block.gas_used_ratio); // requested percentiles @@ -1318,6 +1327,11 @@ impl EthApi { // newest block" response.base_fee_per_gas.push(self.backend.fees().base_fee()); + // Same goes for the `base_fee_per_blob_gas`: + // > [..] includes the next block after the newest of the returned range, because this + // > value can be derived from the newest block. + response.base_fee_per_blob_gas.push(self.backend.fees().base_fee_per_blob_gas()); + Ok(response) } @@ -1424,6 +1438,7 @@ impl EthApi { request.gas_price, request.max_fee_per_gas, request.max_priority_fee_per_gas, + request.max_fee_per_blob_gas, )? .or_zero_fees(); @@ -2184,6 +2199,7 @@ impl EthApi { request.gas_price, request.max_fee_per_gas, request.max_priority_fee_per_gas, + request.max_fee_per_blob_gas, )? .or_zero_fees(); @@ -2387,6 +2403,7 @@ impl EthApi { ) -> Result { let chain_id = request.chain_id.unwrap_or_else(|| self.chain_id()); let max_fee_per_gas = request.max_fee_per_gas; + let max_fee_per_blob_gas = request.max_fee_per_blob_gas; let gas_price = request.gas_price; let gas_limit = request.gas.unwrap_or(self.backend.gas_limit()); @@ -2419,11 +2436,37 @@ impl EthApi { } TypedTransactionRequest::EIP1559(m) } + Some(TypedTransactionRequest::EIP4844(m)) => { + TypedTransactionRequest::EIP4844(match m { + // We only accept the TxEip4844 variant which has the sidecar. + TxEip4844Variant::TxEip4844WithSidecar(mut m) => { + m.tx.nonce = nonce; + m.tx.chain_id = chain_id; + m.tx.gas_limit = gas_limit; + if max_fee_per_gas.is_none() { + m.tx.max_fee_per_gas = + self.gas_price().unwrap_or_default().to::(); + } + if max_fee_per_blob_gas.is_none() { + m.tx.max_fee_per_blob_gas = self + .excess_blob_gas_and_price() + .unwrap_or_default() + .map_or(0, |g| g.blob_gasprice) + } + TxEip4844Variant::TxEip4844WithSidecar(m) + } + // It is not valid to receive a TxEip4844 without a sidecar, therefore + // we must reject it. + TxEip4844Variant::TxEip4844(_) => { + return Err(BlockchainError::FailedToDecodeTransaction) + } + }) + } Some(TypedTransactionRequest::Deposit(mut m)) => { m.gas_limit = gas_limit; TypedTransactionRequest::Deposit(m) } - _ => return Err(BlockchainError::FailedToDecodeTransaction), + None => return Err(BlockchainError::FailedToDecodeTransaction), }; Ok(request) } diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index 2d5d55500..999bfae4f 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -29,6 +29,7 @@ use foundry_evm::{ }, traces::CallTraceNode, }; +use revm::primitives::MAX_BLOB_GAS_PER_BLOCK; use std::sync::Arc; /// Represents an executed transaction (transacted on the DB) @@ -97,6 +98,8 @@ pub struct TransactionExecutor<'a, Db: ?Sized, Validator: TransactionValidator> pub parent_hash: B256, /// Cumulative gas used by all executed transactions pub gas_used: u128, + /// Cumulative blob gas used by all executed transactions + pub blob_gas_used: u128, pub enable_steps_tracing: bool, /// Precompiles to inject to the EVM. pub precompile_factory: Option>, @@ -124,6 +127,10 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' None }; + let is_cancun = self.cfg_env.handler_cfg.spec_id >= SpecId::CANCUN; + let excess_blob_gas = if is_cancun { self.block_env.get_blob_excess_gas() } else { None }; + let mut cumulative_blob_gas_used = if is_cancun { Some(0u128) } else { None }; + for tx in self.into_iter() { let tx = match tx { TransactionExecutionOutcome::Executed(tx) => { @@ -134,6 +141,10 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' trace!(target: "backend", tx_gas_limit = %tx.pending_transaction.transaction.gas_limit(), ?tx, "block gas limit exhausting, skipping transaction"); continue } + TransactionExecutionOutcome::BlobGasExhausted(tx) => { + trace!(target: "backend", blob_gas = %tx.pending_transaction.transaction.blob_gas().unwrap_or_default(), ?tx, "block blob gas limit exhausting, skipping transaction"); + continue + } TransactionExecutionOutcome::Invalid(tx, _) => { trace!(target: "backend", ?tx, "skipping invalid transaction"); invalid.push(tx); @@ -146,7 +157,19 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' continue } }; + if is_cancun { + let tx_blob_gas = tx + .transaction + .pending_transaction + .transaction + .transaction + .blob_gas() + .unwrap_or(0); + cumulative_blob_gas_used = + Some(cumulative_blob_gas_used.unwrap_or(0u128).saturating_add(tx_blob_gas)); + } let receipt = tx.create_receipt(&mut cumulative_gas_used); + let ExecutedTransaction { transaction, logs, out, traces, exit_reason: exit, .. } = tx; build_logs_bloom(logs.clone(), &mut bloom); @@ -200,6 +223,9 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' mix_hash: Default::default(), nonce: Default::default(), base_fee, + parent_beacon_block_root: Default::default(), + blob_gas_used: cumulative_blob_gas_used, + excess_blob_gas: excess_blob_gas.map(|g| g as u128), }; let block = Block::new(partial_header, transactions.clone(), ommers); @@ -227,6 +253,8 @@ pub enum TransactionExecutionOutcome { Invalid(Arc, InvalidTransactionError), /// Execution skipped because could exceed gas limit Exhausted(Arc), + /// Execution skipped because it exceeded the blob gas limit + BlobGasExhausted(Arc), /// When an error occurred during execution DatabaseError(Arc, DatabaseError), } @@ -244,12 +272,21 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator Err(err) => return Some(TransactionExecutionOutcome::DatabaseError(transaction, err)), }; let env = self.env_for(&transaction.pending_transaction); + // check that we comply with the block's gas limit let max_gas = self.gas_used.saturating_add(env.tx.gas_limit as u128); if max_gas > env.block.gas_limit.to::() { return Some(TransactionExecutionOutcome::Exhausted(transaction)) } + // check that we comply with the block's blob gas limit + let max_blob_gas = self.blob_gas_used.saturating_add( + transaction.pending_transaction.transaction.transaction.blob_gas().unwrap_or(0u128), + ); + if max_blob_gas > MAX_BLOB_GAS_PER_BLOCK as u128 { + return Some(TransactionExecutionOutcome::BlobGasExhausted(transaction)) + } + // validate before executing if let Err(err) = self.validator.validate_pool_transaction_for( &transaction.pending_transaction, @@ -322,8 +359,14 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator trace!(target: "backend", ?exit_reason, ?gas_used, "[{:?}] executed with out={:?}", transaction.hash(), out); + // Track the total gas used for total gas per block checks self.gas_used = self.gas_used.saturating_add(gas_used as u128); + // Track the total blob gas used for total blob gas per blob checks + if let Some(blob_gas) = transaction.pending_transaction.transaction.transaction.blob_gas() { + self.blob_gas_used = self.blob_gas_used.saturating_add(blob_gas); + } + trace!(target: "backend::executor", "transacted [{:?}], result: {:?} gas {}", transaction.hash(), exit_reason, gas_used); let tx = ExecutedTransaction { diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 51b14bb2d..ed27557b3 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -32,6 +32,7 @@ use crate::{ NodeConfig, PrecompileFactory, }; use alloy_consensus::{Header, Receipt, ReceiptWithBloom}; +use alloy_eips::eip4844::MAX_BLOBS_PER_BLOCK; use alloy_primitives::{keccak256, Address, Bytes, TxHash, TxKind, B256, U256, U64}; use alloy_rpc_types::{ request::TransactionRequest, serde_helpers::JsonStorageKey, state::StateOverride, AccessList, @@ -78,7 +79,9 @@ use futures::channel::mpsc::{unbounded, UnboundedSender}; use parking_lot::{Mutex, RwLock}; use revm::{ db::WrapDatabaseRef, - primitives::{HashMap, OptimismFields, ResultAndState}, + primitives::{ + calc_blob_gasprice, BlobExcessGasAndPrice, HashMap, OptimismFields, ResultAndState, + }, }; use std::{ collections::BTreeMap, @@ -639,6 +642,10 @@ impl Backend { self.fees.base_fee() } + pub fn excess_blob_gas_and_price(&self) -> Option { + self.fees.excess_blob_gas_and_price() + } + /// Sets the current basefee pub fn set_base_fee(&self, basefee: u128) { self.fees.set_base_fee(basefee) @@ -895,6 +902,7 @@ impl Backend { cfg_env, parent_hash: storage.best_hash, gas_used: 0, + blob_gas_used: 0, enable_steps_tracing: self.enable_steps_tracing, precompile_factory: self.precompile_factory.clone(), }; @@ -923,6 +931,7 @@ impl Backend { let (outcome, header, block_hash) = { let current_base_fee = self.base_fee(); + let current_excess_blob_gas_and_price = self.excess_blob_gas_and_price(); let mut env = self.env.read().clone(); @@ -935,6 +944,7 @@ impl Backend { // increase block number for this block env.block.number = env.block.number.saturating_add(U256::from(1)); env.block.basefee = U256::from(current_base_fee); + env.block.blob_excess_gas_and_price = current_excess_blob_gas_and_price; env.block.timestamp = U256::from(self.time.next_timestamp()); let best_hash = self.blockchain.storage.read().best_hash; @@ -955,6 +965,7 @@ impl Backend { cfg_env: CfgEnvWithHandlerCfg::new(env.cfg.clone(), env.handler_cfg), parent_hash: best_hash, gas_used: 0, + blob_gas_used: 0, enable_steps_tracing: self.enable_steps_tracing, precompile_factory: self.precompile_factory.clone(), }; @@ -1054,9 +1065,15 @@ impl Backend { header.gas_limit, header.base_fee_per_gas.unwrap_or_default(), ); + let next_block_excess_blob_gas = self.fees.get_next_block_blob_excess_gas( + header.excess_blob_gas.unwrap_or_default(), + header.blob_gas_used.unwrap_or_default(), + ); // update next base fee self.fees.set_base_fee(next_block_base_fee); + self.fees + .set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new(next_block_excess_blob_gas)); // notify all listeners self.notify_on_new_block(header, block_hash); @@ -1101,7 +1118,12 @@ impl Backend { .. } = request; - let FeeDetails { gas_price, max_fee_per_gas, max_priority_fee_per_gas } = fee_details; + let FeeDetails { + gas_price, + max_fee_per_gas, + max_priority_fee_per_gas, + max_fee_per_blob_gas, + } = fee_details; let gas_limit = gas.unwrap_or(block_env.gas_limit.to()); let mut env = self.env.read().clone(); @@ -1122,6 +1144,7 @@ impl Backend { gas_limit: gas_limit as u64, gas_price: U256::from(gas_price), gas_priority_fee: max_priority_fee_per_gas.map(U256::from), + max_fee_per_blob_gas: max_fee_per_blob_gas.map(U256::from), transact_to: match to { Some(addr) => TransactTo::Call(*addr), None => TransactTo::Create(CreateScheme::Create), @@ -1564,9 +1587,9 @@ impl Backend { nonce, base_fee_per_gas, withdrawals_root: _, - blob_gas_used: _, - excess_blob_gas: _, - parent_beacon_block_root: _, + blob_gas_used, + excess_blob_gas, + parent_beacon_block_root, } = header; AlloyBlock { @@ -1590,9 +1613,9 @@ impl Backend { nonce: Some(nonce), base_fee_per_gas, withdrawals_root: None, - blob_gas_used: None, - excess_blob_gas: None, - parent_beacon_block_root: None, + blob_gas_used, + excess_blob_gas, + parent_beacon_block_root, }, size: Some(size), transactions: alloy_rpc_types::BlockTransactions::Hashes( @@ -1977,6 +2000,11 @@ impl Backend { let block = self.blockchain.get_block_by_hash(&block_hash)?; let transaction = block.transactions[index].clone(); + // Cancun specific + let excess_blob_gas = block.header.excess_blob_gas; + let blob_gas_price = calc_blob_gasprice(excess_blob_gas.map_or(0, |g| g as u64)); + let blob_gas_used = transaction.blob_gas(); + let effective_gas_price = match transaction.transaction { TypedTransaction::Legacy(t) => t.tx().gas_price, TypedTransaction::EIP2930(t) => t.tx().gas_price, @@ -2043,8 +2071,8 @@ impl Backend { from: info.from, to: info.to, state_root: Some(block.header.state_root), - blob_gas_price: None, - blob_gas_used: None, + blob_gas_price: Some(blob_gas_price), + blob_gas_used, }; Some(MinedTransactionReceipt { inner, out: info.out.map(|o| o.0.into()) }) @@ -2331,6 +2359,42 @@ impl TransactionValidator for Backend { } } + // EIP-4844 Cancun hard fork validation steps + if env.spec_id() >= SpecId::CANCUN && tx.transaction.is_eip4844() { + // Light checks first: see if the blob fee cap is too low. + if let Some(max_fee_per_blob_gas) = tx.essentials().max_fee_per_blob_gas { + if let Some(blob_gas_and_price) = &env.block.blob_excess_gas_and_price { + if max_fee_per_blob_gas.to::() < blob_gas_and_price.blob_gasprice { + warn!(target: "backend", "max fee per blob gas={}, too low, block blob gas price={}", max_fee_per_blob_gas, blob_gas_and_price.blob_gasprice); + return Err(InvalidTransactionError::BlobFeeCapTooLow); + } + } + } + + // Heavy (blob validation) checks + let tx = match &tx.transaction { + TypedTransaction::EIP4844(tx) => tx.tx(), + _ => unreachable!(), + }; + + let blob_count = tx.tx().blob_versioned_hashes.len(); + + // Ensure there are blob hashes. + if blob_count == 0 { + return Err(InvalidTransactionError::NoBlobHashes) + } + + // Ensure the tx does not exceed the max blobs per block. + if blob_count > MAX_BLOBS_PER_BLOCK { + return Err(InvalidTransactionError::TooManyBlobs) + } + + // Check for any blob validation errors + if let Err(err) = tx.validate(env.cfg.kzg_settings.get()) { + return Err(InvalidTransactionError::BlobTransactionValidationError(err)) + } + } + let max_cost = tx.max_cost(); let value = tx.value(); // check sufficient funds: `gas * price + value` @@ -2381,7 +2445,7 @@ pub fn transaction_build( } else { // if transaction is already mined, gas price is considered base fee + priority fee: the // effective gas price. - let base_fee = base_fee.unwrap_or(0); + let base_fee = base_fee.unwrap_or(0u128); 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)); } diff --git a/crates/anvil/src/eth/error.rs b/crates/anvil/src/eth/error.rs index cc7517b42..ce00da319 100644 --- a/crates/anvil/src/eth/error.rs +++ b/crates/anvil/src/eth/error.rs @@ -2,6 +2,7 @@ use crate::eth::pool::transactions::PoolTransaction; use alloy_primitives::{Bytes, SignatureError}; +use alloy_rpc_types::BlockNumberOrTag; use alloy_signer::Error as SignerError; use alloy_transport::TransportError; use anvil_rpc::{ @@ -131,8 +132,10 @@ pub enum PoolError { /// Errors that can occur with `eth_feeHistory` #[derive(Debug, thiserror::Error)] pub enum FeeHistoryError { - #[error("Requested block range is out of bounds")] + #[error("requested block range is out of bounds")] InvalidBlockRange, + #[error("could not find newest block number requested: {0}")] + BlockNotFound(BlockNumberOrTag), } #[derive(Debug)] @@ -201,7 +204,7 @@ pub enum InvalidTransactionError { /// Thrown when the block's `blob_gas_price` is greater than tx-specified /// `max_fee_per_blob_gas` after Cancun. #[error("Block `blob_gas_price` is greater than tx-specified `max_fee_per_blob_gas`")] - BlobGasPriceGreaterThanMax, + BlobFeeCapTooLow, /// Thrown when we receive a tx with `blob_versioned_hashes` and we're not on the Cancun hard /// fork. #[error("Block `blob_versioned_hashes` is not supported before the Cancun hardfork")] @@ -209,6 +212,23 @@ pub enum InvalidTransactionError { /// Thrown when `max_fee_per_blob_gas` is not supported for blocks before the Cancun hardfork. #[error("`max_fee_per_blob_gas` is not supported for blocks before the Cancun hardfork.")] MaxFeePerBlobGasNotSupported, + /// Thrown when there are no `blob_hashes` in the transaction, and it is an EIP-4844 tx. + #[error("`blob_hashes` are required for EIP-4844 transactions")] + NoBlobHashes, + #[error("too many blobs in one transaction")] + TooManyBlobs, + /// Thrown when there's a blob validation error + #[error(transparent)] + BlobTransactionValidationError(#[from] alloy_consensus::BlobTransactionValidationError), + /// Thrown when Blob transaction is a create transaction. `to` must be present. + #[error("Blob transaction can't be a create transaction. `to` must be present.")] + BlobCreateTransaction, + /// Thrown when Blob transaction contains a versioned hash with an incorrect version. + #[error("Blob transaction contains a versioned hash with an incorrect version")] + BlobVersionNotSupported, + /// Thrown when there are no `blob_hashes` in the transaction. + #[error("There should be at least one blob in a Blob transaction.")] + EmptyBlobs, } impl From for InvalidTransactionError { @@ -249,7 +269,7 @@ impl From for InvalidTransactionError { InvalidTransactionError::AccessListNotSupported } InvalidTransaction::BlobGasPriceGreaterThanMax => { - InvalidTransactionError::BlobGasPriceGreaterThanMax + InvalidTransactionError::BlobFeeCapTooLow } InvalidTransaction::BlobVersionedHashesNotSupported => { InvalidTransactionError::BlobVersionedHashesNotSupported @@ -257,8 +277,14 @@ impl From for InvalidTransactionError { InvalidTransaction::MaxFeePerBlobGasNotSupported => { InvalidTransactionError::MaxFeePerBlobGasNotSupported } - // TODO: Blob-related errors should be handled once the Reth migration is done and code - // is moved over. + InvalidTransaction::BlobCreateTransaction => { + InvalidTransactionError::BlobCreateTransaction + } + InvalidTransaction::BlobVersionNotSupported => { + InvalidTransactionError::BlobVersionedHashesNotSupported + } + InvalidTransaction::EmptyBlobs => InvalidTransactionError::EmptyBlobs, + InvalidTransaction::TooManyBlobs => InvalidTransactionError::TooManyBlobs, _ => todo!(), } } diff --git a/crates/anvil/src/eth/fees.rs b/crates/anvil/src/eth/fees.rs index f5534a0cd..f0a614c35 100644 --- a/crates/anvil/src/eth/fees.rs +++ b/crates/anvil/src/eth/fees.rs @@ -2,10 +2,12 @@ use crate::eth::{ backend::{info::StorageInfo, notifications::NewBlockNotifications}, error::BlockchainError, }; -use alloy_eips::{calc_next_block_base_fee, eip1559::BaseFeeParams}; +use alloy_eips::{ + calc_next_block_base_fee, eip1559::BaseFeeParams, eip4844::MAX_DATA_GAS_PER_BLOCK, +}; use alloy_primitives::B256; use anvil_core::eth::transaction::TypedTransaction; -use foundry_evm::revm::primitives::SpecId; +use foundry_evm::revm::primitives::{BlobExcessGasAndPrice, SpecId}; use futures::StreamExt; use parking_lot::{Mutex, RwLock}; use std::{ @@ -42,6 +44,10 @@ pub struct FeeManager { /// /// This value will be updated after a new block was mined base_fee: Arc>, + /// Tracks the excess blob gas, and the base fee, for the next block post Cancun + /// + /// This value will be updated after a new block was mined + blob_excess_gas_and_price: Arc>, /// The base price to use Pre London /// /// This will be constant value unless changed manually @@ -52,11 +58,17 @@ pub struct FeeManager { // === impl FeeManager === impl FeeManager { - pub fn new(spec_id: SpecId, base_fee: u128, gas_price: u128) -> Self { + pub fn new( + spec_id: SpecId, + base_fee: u128, + gas_price: u128, + blob_excess_gas_and_price: BlobExcessGasAndPrice, + ) -> Self { Self { spec_id, base_fee: Arc::new(RwLock::new(base_fee)), gas_price: Arc::new(RwLock::new(gas_price)), + blob_excess_gas_and_price: Arc::new(RwLock::new(blob_excess_gas_and_price)), elasticity: Arc::new(RwLock::new(default_elasticity())), } } @@ -70,6 +82,10 @@ impl FeeManager { (self.spec_id as u8) >= (SpecId::LONDON as u8) } + pub fn is_eip4844(&self) -> bool { + (self.spec_id as u8) >= (SpecId::CANCUN as u8) + } + /// Calculates the current gas price pub fn gas_price(&self) -> u128 { if self.is_eip1559() { @@ -79,6 +95,15 @@ impl FeeManager { } } + /// Calculates the current blob gas price + pub fn blob_gas_price(&self) -> u128 { + if self.is_eip4844() { + self.base_fee_per_blob_gas() + } else { + 0 + } + } + /// Suggested priority fee to add to the base fee pub fn suggested_priority_fee(&self) -> u128 { 1e9 as u128 @@ -92,6 +117,22 @@ impl FeeManager { } } + pub fn excess_blob_gas_and_price(&self) -> Option { + if self.is_eip4844() { + Some(self.blob_excess_gas_and_price.read().clone()) + } else { + None + } + } + + pub fn base_fee_per_blob_gas(&self) -> u128 { + if self.is_eip4844() { + self.blob_excess_gas_and_price.read().blob_gasprice + } else { + 0 + } + } + /// Returns the suggested fee cap /// /// Note: This currently returns a constant value: [Self::suggested_priority_fee] @@ -112,6 +153,13 @@ impl FeeManager { *base = fee; } + /// Sets the current blob excess gas and price + pub fn set_blob_excess_gas_and_price(&self, blob_excess_gas_and_price: BlobExcessGasAndPrice) { + trace!(target: "backend::fees", "updated blob base fee {:?}", blob_excess_gas_and_price); + let mut base = self.blob_excess_gas_and_price.write(); + *base = blob_excess_gas_and_price; + } + /// Calculates the base fee for the next block pub fn get_next_block_base_fee_per_gas( &self, @@ -125,8 +173,28 @@ impl FeeManager { if self.base_fee() == 0 { return 0 } - calc_next_block_base_fee(gas_used, gas_limit, last_fee_per_gas, BaseFeeParams::ethereum()) + calculate_next_block_base_fee(gas_used, gas_limit, last_fee_per_gas) } + + /// Calculates the next block blob base fee, using the provided excess blob gas + pub fn get_next_block_blob_base_fee_per_gas(&self, excess_blob_gas: u128) -> u128 { + crate::revm::primitives::calc_blob_gasprice(excess_blob_gas as u64) + } + + /// Calculates the next block blob excess gas, using the provided parent blob gas used and + /// parent blob excess gas + pub fn get_next_block_blob_excess_gas( + &self, + blob_gas_used: u128, + blob_excess_gas: u128, + ) -> u64 { + crate::revm::primitives::calc_excess_blob_gas(blob_gas_used as u64, blob_excess_gas as u64) + } +} + +/// Calculate base fee for next block. [EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) spec +pub fn calculate_next_block_base_fee(gas_used: u128, gas_limit: u128, base_fee: u128) -> u128 { + calc_next_block_base_fee(gas_used, gas_limit, base_fee, BaseFeeParams::ethereum()) } /// An async service that takes care of the `FeeHistory` cache @@ -189,7 +257,16 @@ impl FeeHistoryService { let mut block_number: Option = None; let base_fee = self.fees.base_fee(); - let mut item = FeeHistoryCacheItem { base_fee, gas_used_ratio: 0f64, rewards: Vec::new() }; + let excess_blob_gas_and_price = self.fees.excess_blob_gas_and_price(); + let mut item = FeeHistoryCacheItem { + base_fee, + gas_used_ratio: 0f64, + blob_gas_used_ratio: 0f64, + rewards: Vec::new(), + excess_blob_gas: excess_blob_gas_and_price.as_ref().map(|g| g.excess_blob_gas as u128), + base_fee_per_blob_gas: excess_blob_gas_and_price.as_ref().map(|g| g.blob_gasprice), + blob_gas_used: excess_blob_gas_and_price.as_ref().map(|_| 0), + }; let current_block = self.storage_info.block(hash); let current_receipts = self.storage_info.receipts(hash); @@ -198,7 +275,10 @@ impl FeeHistoryService { block_number = Some(block.header.number); let gas_used = block.header.gas_used as f64; + let blob_gas_used = block.header.blob_gas_used.map(|g| g as f64); item.gas_used_ratio = gas_used / block.header.gas_limit as f64; + item.blob_gas_used_ratio = + blob_gas_used.map(|g| g / MAX_DATA_GAS_PER_BLOCK as f64).unwrap_or(0 as f64); // extract useful tx info (gas_used, effective_reward) let mut transactions: Vec<(u128, u128)> = receipts @@ -299,6 +379,10 @@ pub type FeeHistoryCache = Arc>>; pub struct FeeHistoryCacheItem { pub base_fee: u128, pub gas_used_ratio: f64, + pub base_fee_per_blob_gas: Option, + pub blob_gas_used_ratio: f64, + pub excess_blob_gas: Option, + pub blob_gas_used: Option, pub rewards: Vec, } @@ -307,29 +391,42 @@ pub struct FeeDetails { pub gas_price: Option, pub max_fee_per_gas: Option, pub max_priority_fee_per_gas: Option, + pub max_fee_per_blob_gas: Option, } impl FeeDetails { /// All values zero pub fn zero() -> Self { - Self { gas_price: Some(0), max_fee_per_gas: Some(0), max_priority_fee_per_gas: Some(0) } + Self { + gas_price: Some(0), + max_fee_per_gas: Some(0), + max_priority_fee_per_gas: Some(0), + max_fee_per_blob_gas: None, + } } /// If neither `gas_price` nor `max_fee_per_gas` is `Some`, this will set both to `0` pub fn or_zero_fees(self) -> Self { - let FeeDetails { gas_price, max_fee_per_gas, max_priority_fee_per_gas } = self; + let FeeDetails { + gas_price, + max_fee_per_gas, + max_priority_fee_per_gas, + max_fee_per_blob_gas, + } = self; let no_fees = gas_price.is_none() && max_fee_per_gas.is_none(); let gas_price = if no_fees { Some(0) } else { gas_price }; let max_fee_per_gas = if no_fees { Some(0) } else { max_fee_per_gas }; + let max_fee_per_blob_gas = if no_fees { None } else { max_fee_per_blob_gas }; - Self { gas_price, max_fee_per_gas, max_priority_fee_per_gas } + Self { gas_price, max_fee_per_gas, max_priority_fee_per_gas, max_fee_per_blob_gas } } /// Turns this type into a tuple - pub fn split(self) -> (Option, Option, Option) { - let Self { gas_price, max_fee_per_gas, max_priority_fee_per_gas } = self; - (gas_price, max_fee_per_gas, max_priority_fee_per_gas) + pub fn split(self) -> (Option, Option, Option, Option) { + let Self { gas_price, max_fee_per_gas, max_priority_fee_per_gas, max_fee_per_blob_gas } = + self; + (gas_price, max_fee_per_gas, max_priority_fee_per_gas, max_fee_per_blob_gas) } /// Creates a new instance from the request's gas related values @@ -337,17 +434,35 @@ impl FeeDetails { request_gas_price: Option, request_max_fee: Option, request_priority: Option, + max_fee_per_blob_gas: Option, ) -> Result { - match (request_gas_price, request_max_fee, request_priority) { - (gas_price, None, None) => { + match (request_gas_price, request_max_fee, request_priority, max_fee_per_blob_gas) { + (gas_price, None, None, None) => { // Legacy request, all default to gas price. Ok(FeeDetails { gas_price, max_fee_per_gas: gas_price, max_priority_fee_per_gas: gas_price, + max_fee_per_blob_gas: None, + }) + } + (_, max_fee, max_priority, None) => { + // eip-1559 + // Ensure `max_priority_fee_per_gas` is less or equal to `max_fee_per_gas`. + if let Some(max_priority) = max_priority { + let max_fee = max_fee.unwrap_or_default(); + if max_priority > max_fee { + return Err(BlockchainError::InvalidFeeInput) + } + } + Ok(FeeDetails { + gas_price: max_fee, + max_fee_per_gas: max_fee, + max_priority_fee_per_gas: max_priority, + max_fee_per_blob_gas: None, }) } - (_, max_fee, max_priority) => { + (_, max_fee, max_priority, max_fee_per_blob_gas) => { // eip-1559 // Ensure `max_priority_fee_per_gas` is less or equal to `max_fee_per_gas`. if let Some(max_priority) = max_priority { @@ -360,6 +475,7 @@ impl FeeDetails { gas_price: max_fee, max_fee_per_gas: max_fee, max_priority_fee_per_gas: max_priority, + max_fee_per_blob_gas, }) } } diff --git a/crates/anvil/tests/it/eip4844.rs b/crates/anvil/tests/it/eip4844.rs new file mode 100644 index 000000000..cb3bf5aa9 --- /dev/null +++ b/crates/anvil/tests/it/eip4844.rs @@ -0,0 +1,192 @@ +use crate::utils::http_provider; +use alloy_consensus::{SidecarBuilder, SimpleCoder}; +use alloy_eips::eip4844::{DATA_GAS_PER_BLOB, MAX_DATA_GAS_PER_BLOCK}; +use alloy_network::TransactionBuilder; +use alloy_primitives::U256; +use alloy_provider::Provider; +use alloy_rpc_types::{TransactionRequest, WithOtherFields}; +use anvil::{spawn, Hardfork, NodeConfig}; + +#[tokio::test(flavor = "multi_thread")] +async fn can_send_eip4844_transaction() { + let node_config = NodeConfig::default().with_hardfork(Some(Hardfork::Cancun)); + let (_api, handle) = spawn(node_config).await; + + let wallets = handle.dev_wallets().collect::>(); + let from = wallets[0].address(); + let to = wallets[1].address(); + let provider = http_provider(&handle.http_endpoint()); + + let eip1559_est = provider.estimate_eip1559_fees(None).await.unwrap(); + let gas_price = provider.get_gas_price().await.unwrap(); + + let sidecar: SidecarBuilder = SidecarBuilder::from_slice("Hello World".as_bytes()); + + let sidecar = sidecar.build().unwrap(); + let tx = TransactionRequest::default() + .with_from(from) + .with_to(to) + .with_nonce(0) + .with_max_fee_per_blob_gas(gas_price + 1) + .with_max_fee_per_gas(eip1559_est.max_fee_per_gas) + .with_max_priority_fee_per_gas(eip1559_est.max_priority_fee_per_gas) + .with_blob_sidecar(sidecar) + .value(U256::from(5)); + + let mut tx = WithOtherFields::new(tx); + + tx.populate_blob_hashes(); + + let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); + + assert_eq!(receipt.blob_gas_used, Some(131072)); + assert_eq!(receipt.blob_gas_price, Some(0x1)); // 1 wei +} + +#[tokio::test(flavor = "multi_thread")] +async fn can_send_multiple_blobs_in_one_tx() { + let node_config = NodeConfig::default().with_hardfork(Some(Hardfork::Cancun)); + let (_api, handle) = spawn(node_config).await; + + let wallets = handle.dev_wallets().collect::>(); + + let from = wallets[0].address(); + let to = wallets[1].address(); + + let provider = http_provider(&handle.http_endpoint()); + + let eip1559_est = provider.estimate_eip1559_fees(None).await.unwrap(); + let gas_price = provider.get_gas_price().await.unwrap(); + + let large_data = vec![1u8; DATA_GAS_PER_BLOB as usize * 5]; // 131072 is DATA_GAS_PER_BLOB and also BYTE_PER_BLOB + let sidecar: SidecarBuilder = SidecarBuilder::from_slice(&large_data); + + let sidecar = sidecar.build().unwrap(); + + let tx = TransactionRequest::default() + .with_from(from) + .with_to(to) + .with_nonce(0) + .with_max_fee_per_blob_gas(gas_price + 1) + .with_max_fee_per_gas(eip1559_est.max_fee_per_gas) + .with_max_priority_fee_per_gas(eip1559_est.max_priority_fee_per_gas) + .with_blob_sidecar(sidecar); + let mut tx = WithOtherFields::new(tx); + + tx.populate_blob_hashes(); + + let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); + + assert_eq!(receipt.blob_gas_used, Some(MAX_DATA_GAS_PER_BLOCK as u128)); + assert_eq!(receipt.blob_gas_price, Some(0x1)); // 1 wei +} + +#[tokio::test(flavor = "multi_thread")] +async fn cannot_exceed_six_blobs() { + let node_config = NodeConfig::default().with_hardfork(Some(Hardfork::Cancun)); + let (_api, handle) = spawn(node_config).await; + + let wallets = handle.dev_wallets().collect::>(); + + let from = wallets[0].address(); + let to = wallets[1].address(); + + let provider = http_provider(&handle.http_endpoint()); + + let eip1559_est = provider.estimate_eip1559_fees(None).await.unwrap(); + let gas_price = provider.get_gas_price().await.unwrap(); + + let large_data = vec![1u8; DATA_GAS_PER_BLOB as usize * 6]; // 131072 is DATA_GAS_PER_BLOB and also BYTE_PER_BLOB + let sidecar: SidecarBuilder = SidecarBuilder::from_slice(&large_data); + + let sidecar = sidecar.build().unwrap(); + + let tx = TransactionRequest::default() + .with_from(from) + .with_to(to) + .with_nonce(0) + .with_max_fee_per_blob_gas(gas_price + 1) + .with_max_fee_per_gas(eip1559_est.max_fee_per_gas) + .with_max_priority_fee_per_gas(eip1559_est.max_priority_fee_per_gas) + .with_blob_sidecar(sidecar); + let mut tx = WithOtherFields::new(tx); + + tx.populate_blob_hashes(); + + let err = provider.send_transaction(tx).await.unwrap_err(); + + assert!(err.to_string().contains("too many blobs")); +} + +#[tokio::test(flavor = "multi_thread")] +async fn can_mine_blobs_when_exceeds_max_blobs() { + let node_config = NodeConfig::default().with_hardfork(Some(Hardfork::Cancun)); + let (api, handle) = spawn(node_config).await; + api.anvil_set_auto_mine(false).await.unwrap(); + + let wallets = handle.dev_wallets().collect::>(); + + let from = wallets[0].address(); + let to = wallets[1].address(); + + let provider = http_provider(&handle.http_endpoint()); + + let eip1559_est = provider.estimate_eip1559_fees(None).await.unwrap(); + let gas_price = provider.get_gas_price().await.unwrap(); + + let first_batch = vec![1u8; DATA_GAS_PER_BLOB as usize * 3]; + let sidecar: SidecarBuilder = SidecarBuilder::from_slice(&first_batch); + + let num_blobs_first = sidecar.clone().take().len(); + + let sidecar = sidecar.build().unwrap(); + + let tx = TransactionRequest::default() + .with_from(from) + .with_to(to) + .with_nonce(0) + .with_max_fee_per_blob_gas(gas_price + 1) + .with_max_fee_per_gas(eip1559_est.max_fee_per_gas) + .with_max_priority_fee_per_gas(eip1559_est.max_priority_fee_per_gas) + .with_blob_sidecar(sidecar); + let mut tx = WithOtherFields::new(tx); + + tx.populate_blob_hashes(); + + let first_tx = provider.send_transaction(tx.clone()).await.unwrap(); + + let second_batch = vec![1u8; DATA_GAS_PER_BLOB as usize * 2]; + + let sidecar: SidecarBuilder = SidecarBuilder::from_slice(&second_batch); + + let num_blobs_second = sidecar.clone().take().len(); + + let sidecar = sidecar.build().unwrap(); + tx.set_blob_sidecar(sidecar); + tx.set_nonce(1); + tx.populate_blob_hashes(); + let second_tx = provider.send_transaction(tx).await.unwrap(); + + api.mine_one().await; + + let first_receipt = first_tx.get_receipt().await.unwrap(); + + api.mine_one().await; + 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) + ); + assert_eq!( + first_block.unwrap().unwrap().header.blob_gas_used, + Some(DATA_GAS_PER_BLOB as u128 * num_blobs_first as u128) + ); + + assert_eq!( + second_block.unwrap().unwrap().header.blob_gas_used, + Some(DATA_GAS_PER_BLOB as u128 * num_blobs_second as u128) + ); + assert_eq!(first_receipt.block_number.unwrap() + 1, second_receipt.block_number.unwrap()); // Mined in two + // different blocks +} diff --git a/crates/anvil/tests/it/main.rs b/crates/anvil/tests/it/main.rs index 799cd16f0..94b1bc492 100644 --- a/crates/anvil/tests/it/main.rs +++ b/crates/anvil/tests/it/main.rs @@ -2,6 +2,7 @@ mod abi; mod anvil; mod anvil_api; mod api; +mod eip4844; mod fork; mod ganache; mod gas; @@ -14,7 +15,6 @@ mod otterscan; mod proof; mod pubsub; mod revert; - mod sign; mod state; mod traces; diff --git a/crates/evm/core/Cargo.toml b/crates/evm/core/Cargo.toml index ff106a406..9b558e350 100644 --- a/crates/evm/core/Cargo.toml +++ b/crates/evm/core/Cargo.toml @@ -26,7 +26,7 @@ alloy-transport.workspace = true alloy-rpc-types.workspace = true alloy-sol-types.workspace = true -revm = { workspace = true, default-features = false, features = [ +revm = { workspace = true, features = [ "std", "serde", "memory_limit", @@ -35,6 +35,7 @@ revm = { workspace = true, default-features = false, features = [ "optional_no_base_fee", "arbitrary", "optimism", + "c-kzg" ] } revm-inspectors.workspace = true diff --git a/crates/evm/evm/Cargo.toml b/crates/evm/evm/Cargo.toml index 8a5d40db5..72f4a6d17 100644 --- a/crates/evm/evm/Cargo.toml +++ b/crates/evm/evm/Cargo.toml @@ -22,7 +22,12 @@ foundry-evm-traces.workspace = true alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } alloy-json-abi.workspace = true -alloy-primitives = { workspace = true, features = ["serde", "getrandom", "arbitrary", "rlp"] } +alloy-primitives = { workspace = true, features = [ + "serde", + "getrandom", + "arbitrary", + "rlp", +] } alloy-sol-types.workspace = true revm = { workspace = true, default-features = false, features = [ "std", @@ -32,6 +37,7 @@ revm = { workspace = true, default-features = false, features = [ "optional_block_gas_limit", "optional_no_base_fee", "arbitrary", + "c-kzg", ] } revm-inspectors.workspace = true diff --git a/crates/evm/fuzz/Cargo.toml b/crates/evm/fuzz/Cargo.toml index 8a21d9652..5fd6db65d 100644 --- a/crates/evm/fuzz/Cargo.toml +++ b/crates/evm/fuzz/Cargo.toml @@ -21,7 +21,7 @@ foundry-evm-traces.workspace = true alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } alloy-json-abi.workspace = true alloy-primitives = { workspace = true, features = ["serde", "getrandom", "arbitrary", "rlp"] } -revm = { workspace = true, default-features = false, features = [ +revm = { workspace = true, features = [ "std", "serde", "memory_limit", From d495216638c0adaa3df76190a6835537c579304d Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Fri, 3 May 2024 02:16:38 +0400 Subject: [PATCH 242/430] fix: avoid panic on missing CREATE2 deployer (#7838) * fix: avoid panic on missing CREATE2 deployer * rm println * rm println --- crates/evm/core/src/utils.rs | 43 +++++++++++++++------------- crates/evm/traces/src/decoder/mod.rs | 3 +- crates/forge/tests/cli/script.rs | 36 +++++++++++++++++++++++ 3 files changed, 61 insertions(+), 21 deletions(-) diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index d1e73a1fa..20d8aa647 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -11,8 +11,8 @@ use revm::{ db::WrapDatabaseRef, handler::register::EvmHandler, interpreter::{ - return_ok, CallContext, CallInputs, CallScheme, CreateInputs, CreateOutcome, Gas, - InstructionResult, InterpreterResult, Transfer, + return_ok, CallContext, CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, + Gas, InstructionResult, InterpreterResult, Transfer, }, primitives::{CreateScheme, EVMError, SpecId, TransactTo, KECCAK_EMPTY}, FrameOrResult, FrameResult, @@ -151,35 +151,38 @@ pub fn create2_handler_register>( return old_handle(ctx, inputs); } - // Sanity check that CREATE2 deployer exists. - let code_hash = ctx.evm.load_account(DEFAULT_CREATE2_DEPLOYER)?.0.info.code_hash; - if code_hash == KECCAK_EMPTY { - return Ok(FrameOrResult::Result(FrameResult::Create(CreateOutcome { - result: InterpreterResult { - result: InstructionResult::Revert, - output: "missing CREATE2 deployer".into(), - gas: Gas::new(inputs.gas_limit), - }, - address: None, - }))) - } + let gas_limit = inputs.gas_limit; // Generate call inputs for CREATE2 factory. let mut call_inputs = get_create2_factory_call_inputs(salt, *inputs); // Call inspector to change input or return outcome. - if let Some(outcome) = ctx.external.call(&mut ctx.evm, &mut call_inputs) { - create2_overrides_inner - .borrow_mut() - .push((ctx.evm.journaled_state.depth(), call_inputs.clone())); - return Ok(FrameOrResult::Result(FrameResult::Call(outcome))); - } + let outcome = ctx.external.call(&mut ctx.evm, &mut call_inputs); // Push data about current override to the stack. create2_overrides_inner .borrow_mut() .push((ctx.evm.journaled_state.depth(), call_inputs.clone())); + // Handle potential inspector override. + if let Some(outcome) = outcome { + return Ok(FrameOrResult::Result(FrameResult::Call(outcome))); + } + + // Sanity check that CREATE2 deployer exists. + let code_hash = ctx.evm.load_account(DEFAULT_CREATE2_DEPLOYER)?.0.info.code_hash; + if code_hash == KECCAK_EMPTY { + return Ok(FrameOrResult::Result(FrameResult::Call(CallOutcome { + result: InterpreterResult { + result: InstructionResult::Revert, + output: "missing CREATE2 deployer".into(), + gas: Gas::new(gas_limit), + }, + memory_offset: 0..0, + }))) + } + + // Create CALL frame for CREATE2 factory invocation. let mut frame_or_result = ctx.evm.make_call_frame(&call_inputs); if let Ok(FrameOrResult::Frame(frame)) = &mut frame_or_result { diff --git a/crates/evm/traces/src/decoder/mod.rs b/crates/evm/traces/src/decoder/mod.rs index 70720dc6c..cbbec53f7 100644 --- a/crates/evm/traces/src/decoder/mod.rs +++ b/crates/evm/traces/src/decoder/mod.rs @@ -310,7 +310,8 @@ impl CallTraceDecoder { if trace.address == DEFAULT_CREATE2_DEPLOYER { return DecodedCallTrace { label, - return_data: None, + return_data: (!trace.status.is_ok()) + .then(|| self.revert_decoder.decode(&trace.output, Some(trace.status))), contract, func: Some(DecodedCallData { signature: "create2".to_string(), args: vec![] }), }; diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index 70b97b58f..714bb036b 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -1321,3 +1321,39 @@ contract SimpleScript is Script { let output = cmd.stdout_lossy(); assert!(output.contains("ONCHAIN EXECUTION COMPLETE & SUCCESSFUL")); }); + +// https://github.com/foundry-rs/foundry/issues/7833 +forgetest_async!(error_no_create2, |prj, cmd| { + let (_api, handle) = + spawn(NodeConfig::test().with_disable_default_create2_deployer(true)).await; + + foundry_test_utils::util::initialize(prj.root()); + prj.add_script( + "Foo", + r#" +import "forge-std/Script.sol"; + +contract SimpleContract {} + +contract SimpleScript is Script { + function run() external { + vm.startBroadcast(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266); + new SimpleContract{salt: bytes32(0)}(); + } +} + "#, + ) + .unwrap(); + + cmd.args([ + "script", + "SimpleScript", + "--fork-url", + &handle.http_endpoint(), + "--broadcast", + "--unlocked", + ]); + + let output = cmd.stderr_lossy(); + assert!(output.contains("missing CREATE2 deployer")); +}); From cc24a3e455baace683e5f45c56e082c3ae3ae547 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Thu, 2 May 2024 17:59:13 -0700 Subject: [PATCH 243/430] feat(anvil): eth_blobBaseFee --- crates/anvil/core/src/eth/mod.rs | 3 +++ crates/anvil/src/eth/api.rs | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/crates/anvil/core/src/eth/mod.rs b/crates/anvil/core/src/eth/mod.rs index 837850653..dd7d05c97 100644 --- a/crates/anvil/core/src/eth/mod.rs +++ b/crates/anvil/core/src/eth/mod.rs @@ -69,6 +69,9 @@ pub enum EthRequest { )] EthMaxPriorityFeePerGas(()), + #[cfg_attr(feature = "serde", serde(rename = "eth_blobBaseFee", with = "empty_params"))] + EthBlobBaseFee(()), + #[cfg_attr( feature = "serde", serde(rename = "eth_accounts", alias = "eth_requestAccounts", with = "empty_params") diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index cc77746da..a22526ce6 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -170,6 +170,7 @@ impl EthApi { EthRequest::EthMaxPriorityFeePerGas(_) => { self.gas_max_priority_fee_per_gas().to_rpc_result() } + EthRequest::EthBlobBaseFee(_) => self.blob_base_fee().to_rpc_result(), EthRequest::EthAccounts(_) => self.accounts().to_rpc_result(), EthRequest::EthBlockNumber(_) => self.block_number().to_rpc_result(), EthRequest::EthGetStorageAt(addr, slot, block) => { @@ -564,6 +565,13 @@ impl EthApi { Ok(U256::from(self.backend.max_priority_fee_per_gas())) } + /// Returns the base fee per blob required to send a EIP-4844 tx. + /// + /// Handler for ETH RPC call: `eth_blobBaseFee` + pub fn blob_base_fee(&self) -> Result { + Ok(U256::from(self.backend.fees().base_fee_per_blob_gas())) + } + /// Returns the block gas limit pub fn gas_limit(&self) -> U256 { U256::from(self.backend.gas_limit()) From 0dbdabac8c6b50a2de4258476f1ad8bd00afad0c Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Thu, 2 May 2024 18:16:00 -0700 Subject: [PATCH 244/430] Revert "feat(anvil): eth_blobBaseFee" This reverts commit cc24a3e455baace683e5f45c56e082c3ae3ae547. --- crates/anvil/core/src/eth/mod.rs | 3 --- crates/anvil/src/eth/api.rs | 8 -------- 2 files changed, 11 deletions(-) diff --git a/crates/anvil/core/src/eth/mod.rs b/crates/anvil/core/src/eth/mod.rs index dd7d05c97..837850653 100644 --- a/crates/anvil/core/src/eth/mod.rs +++ b/crates/anvil/core/src/eth/mod.rs @@ -69,9 +69,6 @@ pub enum EthRequest { )] EthMaxPriorityFeePerGas(()), - #[cfg_attr(feature = "serde", serde(rename = "eth_blobBaseFee", with = "empty_params"))] - EthBlobBaseFee(()), - #[cfg_attr( feature = "serde", serde(rename = "eth_accounts", alias = "eth_requestAccounts", with = "empty_params") diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index a22526ce6..cc77746da 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -170,7 +170,6 @@ impl EthApi { EthRequest::EthMaxPriorityFeePerGas(_) => { self.gas_max_priority_fee_per_gas().to_rpc_result() } - EthRequest::EthBlobBaseFee(_) => self.blob_base_fee().to_rpc_result(), EthRequest::EthAccounts(_) => self.accounts().to_rpc_result(), EthRequest::EthBlockNumber(_) => self.block_number().to_rpc_result(), EthRequest::EthGetStorageAt(addr, slot, block) => { @@ -565,13 +564,6 @@ impl EthApi { Ok(U256::from(self.backend.max_priority_fee_per_gas())) } - /// Returns the base fee per blob required to send a EIP-4844 tx. - /// - /// Handler for ETH RPC call: `eth_blobBaseFee` - pub fn blob_base_fee(&self) -> Result { - Ok(U256::from(self.backend.fees().base_fee_per_blob_gas())) - } - /// Returns the block gas limit pub fn gas_limit(&self) -> U256 { U256::from(self.backend.gas_limit()) From 36d9fab64858b103f61047afc5948bda7bc9ceb6 Mon Sep 17 00:00:00 2001 From: evalir Date: Fri, 3 May 2024 03:31:18 -0400 Subject: [PATCH 245/430] chore(`chisel`): make clippy happy (#7842) chore: make clippy happy --- crates/chisel/src/executor.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index 424bcda90..144ff38f6 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -1210,12 +1210,9 @@ impl Type { #[inline] fn is_dynamic(&self) -> bool { match self { - Self::Builtin(ty) => match ty { - // TODO: Note, this is not entirely correct. Fixed arrays of non-dynamic types are - // not dynamic, nor are tuples of non-dynamic types. - DynSolType::Bytes | DynSolType::String | DynSolType::Array(_) => true, - _ => false, - }, + // TODO: Note, this is not entirely correct. Fixed arrays of non-dynamic types are + // not dynamic, nor are tuples of non-dynamic types. + Self::Builtin(DynSolType::Bytes | DynSolType::String | DynSolType::Array(_)) => true, Self::Array(_) => true, _ => false, } From 233b0f245dd7c1ec030a8af73dd7460020d1a2a1 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Fri, 3 May 2024 03:42:39 -0700 Subject: [PATCH 246/430] feat(anvil): eth_blobBaseFee (#7840) --- crates/anvil/core/src/eth/mod.rs | 3 +++ crates/anvil/src/eth/api.rs | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/crates/anvil/core/src/eth/mod.rs b/crates/anvil/core/src/eth/mod.rs index 837850653..dd7d05c97 100644 --- a/crates/anvil/core/src/eth/mod.rs +++ b/crates/anvil/core/src/eth/mod.rs @@ -69,6 +69,9 @@ pub enum EthRequest { )] EthMaxPriorityFeePerGas(()), + #[cfg_attr(feature = "serde", serde(rename = "eth_blobBaseFee", with = "empty_params"))] + EthBlobBaseFee(()), + #[cfg_attr( feature = "serde", serde(rename = "eth_accounts", alias = "eth_requestAccounts", with = "empty_params") diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index cc77746da..a22526ce6 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -170,6 +170,7 @@ impl EthApi { EthRequest::EthMaxPriorityFeePerGas(_) => { self.gas_max_priority_fee_per_gas().to_rpc_result() } + EthRequest::EthBlobBaseFee(_) => self.blob_base_fee().to_rpc_result(), EthRequest::EthAccounts(_) => self.accounts().to_rpc_result(), EthRequest::EthBlockNumber(_) => self.block_number().to_rpc_result(), EthRequest::EthGetStorageAt(addr, slot, block) => { @@ -564,6 +565,13 @@ impl EthApi { Ok(U256::from(self.backend.max_priority_fee_per_gas())) } + /// Returns the base fee per blob required to send a EIP-4844 tx. + /// + /// Handler for ETH RPC call: `eth_blobBaseFee` + pub fn blob_base_fee(&self) -> Result { + Ok(U256::from(self.backend.fees().base_fee_per_blob_gas())) + } + /// Returns the block gas limit pub fn gas_limit(&self) -> U256 { U256::from(self.backend.gas_limit()) From 181dc3c5b704b1e6284ed82509b150b31bd77d61 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Fri, 3 May 2024 04:15:24 -0700 Subject: [PATCH 247/430] fix(anvil): include blob hashes in call env (#7839) * fix(anvil): include blob hashes in call env * nit --- crates/anvil/src/eth/backend/mem/mod.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index ed27557b3..2c4554d01 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -1114,7 +1114,21 @@ impl Backend { block_env: BlockEnv, ) -> EnvWithHandlerCfg { let WithOtherFields:: { - inner: TransactionRequest { from, to, gas, value, input, nonce, access_list, .. }, + inner: + TransactionRequest { + from, + to, + gas, + value, + input, + nonce, + access_list, + blob_versioned_hashes, + sidecar: _, + chain_id: _, + transaction_type: _, + .. // Rest of the gas fees related fields are taken from `fee_details` + }, .. } = request; @@ -1154,8 +1168,8 @@ impl Backend { chain_id: None, nonce, access_list: access_list.unwrap_or_default().flattened(), + blob_hashes: blob_versioned_hashes.unwrap_or_default(), optimism: OptimismFields { enveloped_tx: Some(Bytes::new()), ..Default::default() }, - ..Default::default() }; if env.block.basefee == revm::primitives::U256::ZERO { From e159e6e8360e1ef7c9cc5ae866d0dd18b32306fc Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 3 May 2024 16:45:52 +0200 Subject: [PATCH 248/430] fix: more gas estimation checks for transfer (#7845) * fix: more gas estimation checks for transfer * rustmft * flip value check * style --- crates/anvil/src/eth/api.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index a22526ce6..e1991bb62 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -2192,8 +2192,14 @@ impl EthApi { // If the request is a simple native token transfer we can optimize // We assume it's a transfer if we have no input data. let to = request.to.as_ref().and_then(TxKind::to); - let likely_transfer = request.input.clone().into_input().is_none(); - if likely_transfer { + + // check certain fields to see if the request could be a simple transfer + let maybe_transfer = request.input.input().is_none() && + request.access_list.is_none() && + request.blob_versioned_hashes.is_none() && + request.value.is_some(); + + if maybe_transfer { if let Some(to) = to { if let Ok(target_code) = self.backend.get_code_with_state(&state, *to) { if target_code.as_ref().is_empty() { From 2e95d2f610b58b12578cffba29f6bf4556f81411 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 3 May 2024 17:30:48 +0200 Subject: [PATCH 249/430] chore: make clippy happy (#7847) --- crates/anvil/src/config.rs | 1 - crates/anvil/src/eth/backend/mem/inspector.rs | 1 - crates/anvil/src/eth/backend/mem/mod.rs | 1 - crates/anvil/src/eth/error.rs | 1 - crates/anvil/src/evm.rs | 10 +++------- 5 files changed, 3 insertions(+), 11 deletions(-) diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 6905b69dc..aa0a3fccc 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -33,7 +33,6 @@ use foundry_config::Config; use foundry_evm::{ constants::DEFAULT_CREATE2_DEPLOYER, fork::{BlockchainDb, BlockchainDbMeta, SharedBackend}, - revm, revm::primitives::{BlockEnv, CfgEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, SpecId, TxEnv}, utils::apply_chain_and_block_specific_env_changes, }; diff --git a/crates/anvil/src/eth/backend/mem/inspector.rs b/crates/anvil/src/eth/backend/mem/inspector.rs index 6ea16a340..892fa1778 100644 --- a/crates/anvil/src/eth/backend/mem/inspector.rs +++ b/crates/anvil/src/eth/backend/mem/inspector.rs @@ -7,7 +7,6 @@ use foundry_evm::{ decode::decode_console_logs, inspectors::{LogCollector, TracingInspector}, revm::{ - self, interpreter::{CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter}, primitives::U256, EvmContext, diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 2c4554d01..22e2360e8 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -64,7 +64,6 @@ use foundry_evm::{ decode::RevertDecoder, inspectors::AccessListInspector, revm::{ - self, db::CacheDB, interpreter::InstructionResult, primitives::{ diff --git a/crates/anvil/src/eth/error.rs b/crates/anvil/src/eth/error.rs index ce00da319..c411d8dec 100644 --- a/crates/anvil/src/eth/error.rs +++ b/crates/anvil/src/eth/error.rs @@ -13,7 +13,6 @@ use foundry_evm::{ backend::DatabaseError, decode::RevertDecoder, revm::{ - self, interpreter::InstructionResult, primitives::{EVMError, InvalidHeader}, }, diff --git a/crates/anvil/src/evm.rs b/crates/anvil/src/evm.rs index de1dfbd51..e6c76dd60 100644 --- a/crates/anvil/src/evm.rs +++ b/crates/anvil/src/evm.rs @@ -1,7 +1,6 @@ -use std::{fmt::Debug, sync::Arc}; - use alloy_primitives::Address; -use foundry_evm::revm::{self, precompile::Precompile, ContextPrecompile, ContextPrecompiles}; +use foundry_evm::revm::{precompile::Precompile, ContextPrecompile, ContextPrecompiles}; +use std::{fmt::Debug, sync::Arc}; /// Object-safe trait that enables injecting extra precompiles when using /// `anvil` as a library. @@ -42,10 +41,7 @@ pub fn inject_precompiles( mod tests { use crate::{evm::inject_precompiles, PrecompileFactory}; use alloy_primitives::Address; - use foundry_evm::revm::{ - self, - primitives::{address, Bytes, Precompile, PrecompileResult, SpecId}, - }; + use foundry_evm::revm::primitives::{address, Bytes, Precompile, PrecompileResult, SpecId}; #[test] fn build_evm_with_extra_precompiles() { From 3ed4af0f274ea4c6451ad52b9e6795b03bcb4e11 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Fri, 3 May 2024 12:52:12 -0700 Subject: [PATCH 250/430] fix(anvil): correctly use `BlobVersionNotSupported` (#7855) --- crates/anvil/src/eth/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/anvil/src/eth/error.rs b/crates/anvil/src/eth/error.rs index c411d8dec..61333822c 100644 --- a/crates/anvil/src/eth/error.rs +++ b/crates/anvil/src/eth/error.rs @@ -280,7 +280,7 @@ impl From for InvalidTransactionError { InvalidTransactionError::BlobCreateTransaction } InvalidTransaction::BlobVersionNotSupported => { - InvalidTransactionError::BlobVersionedHashesNotSupported + InvalidTransactionError::BlobVersionNotSupported } InvalidTransaction::EmptyBlobs => InvalidTransactionError::EmptyBlobs, InvalidTransaction::TooManyBlobs => InvalidTransactionError::TooManyBlobs, From fd9ecd8491c44e825a3bf3992cc319b93c1f822e Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Fri, 3 May 2024 14:13:32 -0700 Subject: [PATCH 251/430] fix(anvil): set blob fields in genesis (#7853) * fix(anvil): set blob fields in genesis * track blob fields in fork * nit + test --- crates/anvil/src/config.rs | 2 ++ crates/anvil/src/eth/backend/fork.rs | 5 +++++ crates/anvil/src/eth/backend/mem/storage.rs | 2 ++ crates/anvil/tests/it/eip4844.rs | 15 ++++++++++++++- 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index aa0a3fccc..d2abc47ec 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -1164,6 +1164,8 @@ latest block number: {latest_block}" backoff: self.fork_retry_backoff, compute_units_per_second: self.compute_units_per_second, total_difficulty: block.header.total_difficulty.unwrap_or_default(), + blob_gas_used: block.header.blob_gas_used, + blob_excess_gas_and_price: env.block.blob_excess_gas_and_price.clone(), }; let mut db = ForkedDatabase::new(backend, block_chain_db); diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index 0d456b3c6..934225fbc 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -19,6 +19,7 @@ use parking_lot::{ lock_api::{RwLockReadGuard, RwLockWriteGuard}, RawRwLock, RwLock, }; +use revm::primitives::BlobExcessGasAndPrice; use std::{collections::HashMap, sync::Arc, time::Duration}; use tokio::sync::RwLock as AsyncRwLock; @@ -582,6 +583,10 @@ pub struct ClientForkConfig { pub timestamp: u64, /// The basefee of the forked block pub base_fee: Option, + /// Blob gas used of the forked block + pub blob_gas_used: Option, + /// Blob excess gas and price of the forked block + pub blob_excess_gas_and_price: Option, /// request timeout pub timeout: Duration, /// request retries for spurious networks diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index 434490de5..916b07f64 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -233,6 +233,8 @@ impl BlockchainStorage { gas_limit: env.block.gas_limit.to::(), beneficiary: env.block.coinbase, difficulty: env.block.difficulty, + blob_gas_used: env.block.blob_excess_gas_and_price.as_ref().map(|_| 0), + excess_blob_gas: env.block.get_blob_excess_gas().map(|v| v as u128), ..Default::default() }; let block = Block::new::(partial_header, vec![], vec![]); diff --git a/crates/anvil/tests/it/eip4844.rs b/crates/anvil/tests/it/eip4844.rs index cb3bf5aa9..180bcf178 100644 --- a/crates/anvil/tests/it/eip4844.rs +++ b/crates/anvil/tests/it/eip4844.rs @@ -4,7 +4,7 @@ use alloy_eips::eip4844::{DATA_GAS_PER_BLOB, MAX_DATA_GAS_PER_BLOCK}; use alloy_network::TransactionBuilder; use alloy_primitives::U256; use alloy_provider::Provider; -use alloy_rpc_types::{TransactionRequest, WithOtherFields}; +use alloy_rpc_types::{BlockId, TransactionRequest, WithOtherFields}; use anvil::{spawn, Hardfork, NodeConfig}; #[tokio::test(flavor = "multi_thread")] @@ -190,3 +190,16 @@ async fn can_mine_blobs_when_exceeds_max_blobs() { assert_eq!(first_receipt.block_number.unwrap() + 1, second_receipt.block_number.unwrap()); // Mined in two // different blocks } + +#[tokio::test(flavor = "multi_thread")] +async fn can_check_blob_fields_on_genesis() { + let node_config = NodeConfig::default().with_hardfork(Some(Hardfork::Cancun)); + let (_api, handle) = spawn(node_config).await; + + let provider = http_provider(&handle.http_endpoint()); + + let block = provider.get_block(BlockId::latest(), false).await.unwrap().unwrap(); + + assert_eq!(block.header.blob_gas_used, Some(0)); + assert_eq!(block.header.excess_blob_gas, Some(0)); +} From f21760b9a9a0d6623ce69a1c93c99d8eb6a66be8 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 4 May 2024 00:21:08 +0200 Subject: [PATCH 252/430] fix: enable eip712 for all signers (#7854) --- Cargo.lock | 1 + crates/cast/Cargo.toml | 6 +----- crates/wallets/Cargo.toml | 4 ++-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 67c488479..833828303 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -416,6 +416,7 @@ dependencies = [ "alloy-network", "alloy-primitives", "alloy-signer", + "alloy-sol-types", "async-trait", "coins-ledger", "futures-util", diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index ff7457e20..380dfabe0 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -15,11 +15,7 @@ name = "cast" path = "bin/main.rs" [build-dependencies] -vergen = { workspace = true, default-features = false, features = [ - "build", - "git", - "gitcl", -] } +vergen = { workspace = true, default-features = false, features = ["build", "git", "gitcl"] } [dependencies] # lib diff --git a/crates/wallets/Cargo.toml b/crates/wallets/Cargo.toml index 31ea5607c..545207b40 100644 --- a/crates/wallets/Cargo.toml +++ b/crates/wallets/Cargo.toml @@ -13,8 +13,8 @@ repository.workspace = true alloy-primitives.workspace = true alloy-signer = { workspace = true, features = ["eip712"] } alloy-signer-wallet = { workspace = true, features = ["mnemonic", "keystore"] } -alloy-signer-aws.workspace = true -alloy-signer-ledger.workspace = true +alloy-signer-aws = { workspace = true, features = ["eip712"] } +alloy-signer-ledger = { workspace = true, features = ["eip712"] } alloy-signer-trezor.workspace = true alloy-network.workspace = true alloy-consensus.workspace = true From 11e7dfdacf7292e135efb21a717f4c6ebbfe6fdb Mon Sep 17 00:00:00 2001 From: jxom Date: Sat, 4 May 2024 15:00:26 +1000 Subject: [PATCH 253/430] fix: transfer check (#7856) --- crates/anvil/src/eth/api.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index e1991bb62..7fd4133a6 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -2196,8 +2196,7 @@ impl EthApi { // check certain fields to see if the request could be a simple transfer let maybe_transfer = request.input.input().is_none() && request.access_list.is_none() && - request.blob_versioned_hashes.is_none() && - request.value.is_some(); + request.blob_versioned_hashes.is_none(); if maybe_transfer { if let Some(to) = to { From d65c65f101f7384cab9b6c8566513206b3830eb5 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sat, 4 May 2024 16:12:53 +0400 Subject: [PATCH 254/430] bump foundry-compilers (#7822) * patch compilers * update fixture regex * update patch * update patch * update patch * update patch * update patch * update patch * update patch * update patch * rm patch --- Cargo.lock | 9 ++-- Cargo.toml | 6 +-- crates/cast/bin/cmd/storage.rs | 21 +++++--- crates/chisel/benches/session_source.rs | 9 +++- crates/chisel/src/executor.rs | 22 +++++--- crates/chisel/src/session_source.rs | 60 ++++++++++----------- crates/chisel/tests/cache.rs | 3 +- crates/cli/src/utils/cmd.rs | 6 +-- crates/common/src/compile.rs | 46 ++++++++-------- crates/common/src/term.rs | 33 +++--------- crates/config/src/lib.rs | 71 +++++++++++++------------ crates/doc/src/builder.rs | 10 ++-- crates/evm/coverage/src/lib.rs | 50 ++++++++--------- crates/forge/bin/cmd/build.rs | 9 ++-- crates/forge/bin/cmd/clone.rs | 4 +- crates/forge/bin/cmd/coverage.rs | 21 ++++---- crates/forge/bin/cmd/fmt.rs | 6 ++- crates/forge/bin/cmd/geiger/mod.rs | 8 ++- crates/forge/bin/cmd/selectors.rs | 6 +-- crates/forge/bin/cmd/test/mod.rs | 8 +-- crates/forge/bin/cmd/tree.rs | 4 +- crates/forge/src/coverage.rs | 24 ++++++--- crates/forge/tests/cli/config.rs | 10 ++-- crates/linking/src/lib.rs | 8 ++- crates/script/src/build.rs | 5 +- crates/test-utils/src/util.rs | 9 ++-- crates/verify/src/bytecode.rs | 7 +-- crates/verify/src/etherscan/flatten.rs | 15 +++--- crates/verify/src/etherscan/mod.rs | 2 +- crates/verify/src/sourcify.rs | 2 +- 30 files changed, 264 insertions(+), 230 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 833828303..90effef31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3670,9 +3670,9 @@ dependencies = [ [[package]] name = "foundry-block-explorers" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b64cb03e297eb85b9f84a195fec7390a5e9805d9b82b11b2af57f65fc6d4ceb7" +checksum = "351c6be2db0090c6c5ae214a7e768a1a61d7d46ff70ab9e7ee45c8f34e6c3c60" dependencies = [ "alloy-chains", "alloy-json-abi", @@ -3824,12 +3824,13 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.3.19" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5421772f768d43f81052159c5175e7d10941e0f0416dafd768f353ed9c554fd" +checksum = "d76463dbe20b1a0830acc74ef8ed59d9d9392f2aed21b0542001dc5a1a725b8e" dependencies = [ "alloy-json-abi", "alloy-primitives", + "auto_impl", "cfg-if", "dirs 5.0.1", "dunce", diff --git a/Cargo.toml b/Cargo.toml index 3513cf6c1..550a0e644 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -137,8 +137,8 @@ foundry-wallets = { path = "crates/wallets" } foundry-linking = { path = "crates/linking" } # solc & compilation utilities -foundry-block-explorers = { version = "0.2.6", default-features = false } -foundry-compilers = { version = "0.3.18", default-features = false } +foundry-block-explorers = { version = "0.2.7", default-features = false } +foundry-compilers = { version = "0.4.0", default-features = false } ## revm # no default features to avoid c-kzg @@ -224,4 +224,4 @@ axum = "0.7" hyper = "1.0" reqwest = { version = "0.12", default-features = false } tower = "0.4" -tower-http = "0.5" +tower-http = "0.5" \ No newline at end of file diff --git a/crates/cast/bin/cmd/storage.rs b/crates/cast/bin/cmd/storage.rs index 18411165e..6197e40c9 100644 --- a/crates/cast/bin/cmd/storage.rs +++ b/crates/cast/bin/cmd/storage.rs @@ -19,14 +19,16 @@ use foundry_common::{ ens::NameOrAddress, }; use foundry_compilers::{ - artifacts::StorageLayout, Artifact, ConfigurableContractArtifact, Project, Solc, + artifacts::StorageLayout, + compilers::{solc::SolcVersionManager, CompilerVersionManager}, + Artifact, CompilerConfig, ConfigurableContractArtifact, Project, }; use foundry_config::{ figment::{self, value::Dict, Metadata, Profile}, impl_figment_convert_cast, Config, }; use semver::Version; -use std::str::FromStr; +use std::{str::FromStr, sync::Arc}; /// The minimum Solc version for outputting storage layouts. /// @@ -138,7 +140,13 @@ impl StorageArgs { let root_path = root.path(); let mut project = etherscan_project(metadata, root_path)?; add_storage_layout_output(&mut project); - project.auto_detect = auto_detect; + + let vm = SolcVersionManager::default(); + project.compiler_config = if auto_detect { + CompilerConfig::AutoDetect(Arc::new(vm)) + } else { + CompilerConfig::Specific(vm.get_or_install(&version)?) + }; // Compile let mut out = ProjectCompiler::new().quiet(true).compile(&project)?; @@ -151,9 +159,8 @@ impl StorageArgs { if is_storage_layout_empty(&artifact.storage_layout) && auto_detect { // try recompiling with the minimum version eprintln!("The requested contract was compiled with {version} while the minimum version for storage layouts is {MIN_SOLC} and as a result the output may be empty."); - let solc = Solc::find_or_install_svm_version(MIN_SOLC.to_string())?; - project.solc = solc; - project.auto_detect = false; + let solc = SolcVersionManager::default().get_or_install(&MIN_SOLC)?; + project.compiler_config = CompilerConfig::Specific(solc); if let Ok(output) = ProjectCompiler::new().quiet(true).compile(&project) { out = output; let (_, new_artifact) = out @@ -277,7 +284,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; let output_selection = project.artifacts.output_selection(); - project.solc_config.settings.push_all(output_selection); + project.settings.push_all(output_selection); } fn is_storage_layout_empty(storage_layout: &Option) -> bool { diff --git a/crates/chisel/benches/session_source.rs b/crates/chisel/benches/session_source.rs index 3c3196956..a6fc6e463 100644 --- a/crates/chisel/benches/session_source.rs +++ b/crates/chisel/benches/session_source.rs @@ -1,11 +1,16 @@ use chisel::session_source::{SessionSource, SessionSourceConfig}; use criterion::{criterion_group, Criterion}; -use foundry_compilers::Solc; +use foundry_compilers::{ + compilers::{solc::SolcVersionManager, CompilerVersionManager}, + Solc, +}; use once_cell::sync::Lazy; +use semver::Version; use std::hint::black_box; use tokio::runtime::Runtime; -static SOLC: Lazy = Lazy::new(|| Solc::find_or_install_svm_version("0.8.19").unwrap()); +static SOLC: Lazy = + Lazy::new(|| SolcVersionManager::default().get_or_install(&Version::new(0, 8, 19)).unwrap()); /// Benchmark for the `clone_with_new_line` function in [SessionSource] fn clone_with_new_line(c: &mut Criterion) { diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index 144ff38f6..3ae465e4d 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -308,7 +308,7 @@ impl SessionSource { self.config.evm_opts.clone(), None, None, - self.solc.version().ok(), + Some(self.solc.version.clone()), ) .into(), ) @@ -1401,7 +1401,12 @@ impl<'a> Iterator for InstructionIter<'a> { #[cfg(test)] mod tests { use super::*; - use foundry_compilers::{error::SolcError, Solc}; + use foundry_compilers::{ + compilers::{solc::SolcVersionManager, CompilerVersionManager}, + error::SolcError, + Solc, + }; + use semver::Version; use std::sync::Mutex; #[test] @@ -1685,10 +1690,11 @@ mod tests { for _ in 0..3 { let mut is_preinstalled = PRE_INSTALL_SOLC_LOCK.lock().unwrap(); if !*is_preinstalled { - let solc = Solc::find_or_install_svm_version(version) - .and_then(|solc| solc.version().map(|v| (solc, v))); + let solc = SolcVersionManager::default() + .get_or_install(&version.parse().unwrap()) + .map(|solc| (solc.version.clone(), solc)); match solc { - Ok((solc, v)) => { + Ok((v, solc)) => { // successfully installed eprintln!("found installed Solc v{v} @ {}", solc.solc.display()); break @@ -1697,7 +1703,7 @@ mod tests { // try reinstalling eprintln!("error while trying to re-install Solc v{version}: {e}"); let solc = Solc::blocking_install(&version.parse().unwrap()); - if solc.map_err(SolcError::from).and_then(|solc| solc.version()).is_ok() { + if solc.map_err(SolcError::from).is_ok() { *is_preinstalled = true; break } @@ -1706,7 +1712,9 @@ mod tests { } } - let solc = Solc::find_or_install_svm_version("0.8.19").expect("could not install solc"); + let solc = SolcVersionManager::default() + .get_or_install(&Version::new(0, 8, 19)) + .expect("could not install solc"); SessionSource::new(solc, Default::default()) } diff --git a/crates/chisel/src/session_source.rs b/crates/chisel/src/session_source.rs index 9deac254b..21fa3d834 100644 --- a/crates/chisel/src/session_source.rs +++ b/crates/chisel/src/session_source.rs @@ -7,8 +7,9 @@ use eyre::Result; use forge_fmt::solang_ext::SafeUnwrap; use foundry_compilers::{ - artifacts::{Source, Sources}, - CompilerInput, CompilerOutput, Solc, + artifacts::{Settings, Source, Sources}, + compilers::{solc::SolcVersionManager, CompilerInput, CompilerVersionManager}, + CompilerOutput, Solc, SolcInput, }; use foundry_config::{Config, SolcReq}; use foundry_evm::{backend::Backend, opts::EvmOpts}; @@ -94,20 +95,22 @@ impl SessionSourceConfig { let solc_req = if let Some(solc_req) = self.foundry_config.solc.clone() { solc_req } else if let Some(version) = Solc::installed_versions().into_iter().max() { - SolcReq::Version(version.into()) + SolcReq::Version(version) } else { if !self.foundry_config.offline { print!("{}", "No solidity versions installed! ".green()); } // use default - SolcReq::Version("0.8.19".parse().unwrap()) + SolcReq::Version(Version::new(0, 8, 19)) }; + let vm = SolcVersionManager::default(); + match solc_req { SolcReq::Version(version) => { // Validate that the requested evm version is supported by the solc version let req_evm_version = self.foundry_config.evm_version; - if let Some(compat_evm_version) = req_evm_version.normalize_version(&version) { + if let Some(compat_evm_version) = req_evm_version.normalize_version_solc(&version) { if req_evm_version > compat_evm_version { eyre::bail!( "The set evm version, {req_evm_version}, is not supported by solc {version}. Upgrade to a newer solc version." @@ -115,23 +118,22 @@ impl SessionSourceConfig { } } - let mut solc = Solc::find_svm_installed_version(version.to_string())?; - - if solc.is_none() { + let solc = if let Ok(solc) = vm.get_installed(&version) { + solc + } else { if self.foundry_config.offline { eyre::bail!("can't install missing solc {version} in offline mode") } println!("{}", format!("Installing solidity version {version}...").green()); - Solc::blocking_install(&version)?; - solc = Solc::find_svm_installed_version(version.to_string())?; - } - solc.ok_or_else(|| eyre::eyre!("Failed to install {version}")) + vm.install(&version)? + }; + Ok(solc) } SolcReq::Local(solc) => { if !solc.is_file() { eyre::bail!("`solc` {} does not exist", solc.display()); } - Ok(Solc::new(solc)) + Ok(Solc::new(solc)?) } } } @@ -182,11 +184,9 @@ impl SessionSource { /// A new instance of [SessionSource] #[track_caller] pub fn new(solc: Solc, mut config: SessionSourceConfig) -> Self { - if let Ok(v) = solc.version_short() { - if v < MIN_VM_VERSION && !config.no_vm { - tracing::info!(version=%v, minimum=%MIN_VM_VERSION, "Disabling VM injection"); - config.no_vm = true; - } + if solc.version < MIN_VM_VERSION && !config.no_vm { + tracing::info!(version=%solc.version, minimum=%MIN_VM_VERSION, "Disabling VM injection"); + config.no_vm = true; } Self { @@ -311,7 +311,7 @@ impl SessionSource { /// /// A [CompilerInput] object containing forge-std's `Vm` interface as well as the REPL contract /// source. - pub fn compiler_input(&self) -> CompilerInput { + pub fn compiler_input(&self) -> SolcInput { let mut sources = Sources::new(); sources.insert(self.file_name.clone(), Source::new(self.to_repl_source())); @@ -322,19 +322,17 @@ impl SessionSource { sources.insert(PathBuf::from("forge-std/Vm.sol"), Source::new(VM_SOURCE)); } + let settings = Settings { + remappings, + evm_version: Some(self.config.foundry_config.evm_version), + ..Default::default() + }; + // we only care about the solidity source, so we can safely unwrap - let mut compiler_input = CompilerInput::with_sources(sources) + SolcInput::build(sources, settings, &self.solc.version) .into_iter() .next() - .expect("Solidity source not found"); - - // get all remappings from the config - compiler_input.settings.remappings = remappings; - - // We also need to enforce the EVM version that the user has specified. - compiler_input.settings.evm_version = Some(self.config.foundry_config.evm_version); - - compiler_input + .expect("Solidity source not found") } /// Compiles the source using [solang_parser] @@ -442,7 +440,7 @@ impl SessionSource { /// /// The [SessionSource] represented as a Forge Script contract. pub fn to_script_source(&self) -> String { - let Version { major, minor, patch, .. } = self.solc.version().unwrap(); + let Version { major, minor, patch, .. } = self.solc.version; let Self { contract_name, global_code, top_level_code, run_code, config, .. } = self; let script_import = @@ -473,7 +471,7 @@ contract {contract_name} is Script {{ /// /// The [SessionSource] represented as a REPL contract. pub fn to_repl_source(&self) -> String { - let Version { major, minor, patch, .. } = self.solc.version().unwrap(); + let Version { major, minor, patch, .. } = self.solc.version; let Self { contract_name, global_code, top_level_code, run_code, config, .. } = self; let (vm_import, vm_constant) = if !config.no_vm { diff --git a/crates/chisel/tests/cache.rs b/crates/chisel/tests/cache.rs index c6b9625eb..5575a814a 100644 --- a/crates/chisel/tests/cache.rs +++ b/crates/chisel/tests/cache.rs @@ -1,6 +1,7 @@ use chisel::session::ChiselSession; use foundry_compilers::EvmVersion; use foundry_config::{Config, SolcReq}; +use semver::Version; use serial_test::serial; use std::path::Path; @@ -231,7 +232,7 @@ fn test_solc_evm_configuration_mismatch() { // Force the solc version to be 0.8.13 which does not support Paris let foundry_config = Config { evm_version: EvmVersion::Paris, - solc: Some(SolcReq::Version("0.8.13".parse().expect("invalid semver"))), + solc: Some(SolcReq::Version(Version::new(0, 8, 13))), ..Default::default() }; diff --git a/crates/cli/src/utils/cmd.rs b/crates/cli/src/utils/cmd.rs index d8c5ab222..96c9e2e50 100644 --- a/crates/cli/src/utils/cmd.rs +++ b/crates/cli/src/utils/cmd.rs @@ -3,8 +3,8 @@ use alloy_primitives::Address; use eyre::{Result, WrapErr}; use foundry_common::{cli_warn, fs, TestFunctionExt}; use foundry_compilers::{ - artifacts::{CompactBytecode, CompactDeployedBytecode}, - cache::{CacheEntry, SolFilesCache}, + artifacts::{CompactBytecode, CompactDeployedBytecode, Settings}, + cache::{CacheEntry, CompilerCache}, utils::read_json_file, Artifact, ProjectCompileOutput, }; @@ -74,7 +74,7 @@ pub fn remove_contract( // TODO: Is there a better / more ergonomic way to get the artifacts given a project and a // contract name? pub fn get_cached_entry_by_name( - cache: &SolFilesCache, + cache: &CompilerCache, name: &str, ) -> Result<(PathBuf, CacheEntry)> { let mut cached_entry = None; diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index 3b1d83c18..3141a6978 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -6,10 +6,11 @@ use eyre::{Context, Result}; use foundry_block_explorers::contract::Metadata; use foundry_compilers::{ artifacts::{BytecodeObject, ContractBytecodeSome, Libraries}, + compilers::{solc::SolcVersionManager, Compiler, CompilerVersionManager}, remappings::Remapping, report::{BasicStdoutReporter, NoReporter, Report}, - Artifact, ArtifactId, FileFilter, Project, ProjectCompileOutput, ProjectPathsConfig, Solc, - SolcConfig, + Artifact, ArtifactId, CompilerConfig, ConfigurableArtifacts, FileFilter, Project, + ProjectCompileOutput, ProjectPathsConfig, SolcConfig, SparseOutputFileFilter, }; use foundry_linking::Linker; use num_format::{Locale, ToFormattedString}; @@ -30,7 +31,7 @@ use std::{ /// This is merely a wrapper for [`Project::compile()`] which also prints to stdout depending on its /// settings. #[must_use = "ProjectCompiler does nothing unless you call a `compile*` method"] -pub struct ProjectCompiler { +pub struct ProjectCompiler { /// Whether we are going to verify the contracts after compilation. verify: Option, @@ -47,20 +48,20 @@ pub struct ProjectCompiler { bail: Option, /// Files to exclude. - filter: Option>, + filter: Option>>, /// Extra files to include, that are not necessarily in the project's source dir. files: Vec, } -impl Default for ProjectCompiler { +impl Default for ProjectCompiler { #[inline] fn default() -> Self { Self::new() } } -impl ProjectCompiler { +impl ProjectCompiler { /// Create a new builder with the default settings. #[inline] pub fn new() -> Self { @@ -122,7 +123,7 @@ impl ProjectCompiler { /// Sets the filter to use. #[inline] - pub fn filter(mut self, filter: Box) -> Self { + pub fn filter(mut self, filter: Box>) -> Self { self.filter = Some(filter); self } @@ -135,7 +136,10 @@ impl ProjectCompiler { } /// Compiles the project. - pub fn compile(mut self, project: &Project) -> Result { + pub fn compile( + mut self, + project: &Project, + ) -> Result> { // TODO: Avoid process::exit if !project.paths.has_input_files() && self.files.is_empty() { println!("Nothing to compile"); @@ -169,9 +173,9 @@ impl ProjectCompiler { /// ProjectCompiler::new().compile_with(|| Ok(prj.compile()?)).unwrap(); /// ``` #[instrument(target = "forge::compile", skip_all)] - fn compile_with(self, f: F) -> Result + fn compile_with(self, f: F) -> Result> where - F: FnOnce() -> Result, + F: FnOnce() -> Result>, { let quiet = self.quiet.unwrap_or(false); let bail = self.bail.unwrap_or(true); @@ -219,7 +223,7 @@ impl ProjectCompiler { } /// If configured, this will print sizes or names - fn handle_output(&self, output: &ProjectCompileOutput) { + fn handle_output(&self, output: &ProjectCompileOutput) { let print_names = self.print_names.unwrap_or(false); let print_sizes = self.print_sizes.unwrap_or(false); @@ -470,12 +474,12 @@ pub struct ContractInfo { /// If `verify` and it's a standalone script, throw error. Only allowed for projects. /// /// **Note:** this expects the `target_path` to be absolute -pub fn compile_target( +pub fn compile_target( target_path: &Path, - project: &Project, + project: &Project, quiet: bool, -) -> Result { - ProjectCompiler::new().quiet(quiet).files([target_path.into()]).compile(project) +) -> Result> { + ProjectCompiler::::new().quiet(quiet).files([target_path.into()]).compile(project) } /// Compiles an Etherscan source from metadata by creating a project. @@ -545,17 +549,17 @@ pub fn etherscan_project(metadata: &Metadata, target_path: impl AsRef) -> .build_with_root(sources_path); let v = metadata.compiler_version()?; - let v = format!("{}.{}.{}", v.major, v.minor, v.patch); - let solc = Solc::find_or_install_svm_version(v)?; + let vm = SolcVersionManager::default(); + let solc = vm.get_or_install(&v)?; + + let compiler_config = CompilerConfig::Specific(solc); Ok(Project::builder() - .solc_config(SolcConfig::builder().settings(settings).build()) - .no_auto_detect() + .settings(SolcConfig::builder().settings(settings).build().settings) .paths(paths) - .solc(solc) .ephemeral() .no_artifacts() - .build()?) + .build(compiler_config)?) } /// Bundles multiple `SkipBuildFilter` into a single `FileFilter` diff --git a/crates/common/src/term.rs b/crates/common/src/term.rs index 5cfb5ef37..81aff615c 100644 --- a/crates/common/src/term.rs +++ b/crates/common/src/term.rs @@ -1,8 +1,7 @@ //! terminal utils use foundry_compilers::{ remappings::Remapping, - report::{self, BasicStdoutReporter, Reporter, SolcCompilerIoReporter}, - CompilerInput, CompilerOutput, Solc, + report::{self, BasicStdoutReporter, Reporter}, }; use once_cell::sync::Lazy; use semver::Version; @@ -90,9 +89,6 @@ impl Spinner { pub struct SpinnerReporter { /// The sender to the spinner thread. sender: mpsc::Sender, - /// Reporter that logs Solc compiler input and output to separate files if configured via env - /// var. - solc_io_report: SolcCompilerIoReporter, } impl SpinnerReporter { @@ -129,7 +125,7 @@ impl SpinnerReporter { }) .expect("failed to spawn thread"); - SpinnerReporter { sender, solc_io_report: SolcCompilerIoReporter::from_default_env() } + SpinnerReporter { sender } } fn send_msg(&self, msg: impl Into) { @@ -152,34 +148,21 @@ impl Drop for SpinnerReporter { } impl Reporter for SpinnerReporter { - fn on_solc_spawn( - &self, - _solc: &Solc, - version: &Version, - input: &CompilerInput, - dirty_files: &[PathBuf], - ) { + fn on_compiler_spawn(&self, compiler_name: &str, version: &Version, dirty_files: &[PathBuf]) { self.send_msg(format!( - "Compiling {} files with {}.{}.{}", + "Compiling {} files with {} {}.{}.{}", dirty_files.len(), + compiler_name, version.major, version.minor, version.patch )); - self.solc_io_report.log_compiler_input(input, version); } - fn on_solc_success( - &self, - _solc: &Solc, - version: &Version, - output: &CompilerOutput, - duration: &Duration, - ) { - self.solc_io_report.log_compiler_output(output, version); + fn on_compiler_success(&self, compiler_name: &str, version: &Version, duration: &Duration) { self.send_msg(format!( - "Solc {}.{}.{} finished in {duration:.2?}", - version.major, version.minor, version.patch + "{} {}.{}.{} finished in {duration:.2?}", + compiler_name, version.major, version.minor, version.patch )); } diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 17d15a6b1..2a7ddc3ab 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -20,9 +20,11 @@ use foundry_compilers::{ RevertStrings, Settings, SettingsMetadata, Severity, }, cache::SOLIDITY_FILES_CACHE_FILENAME, + compilers::{solc::SolcVersionManager, CompilerVersionManager}, error::{SolcError, SolcIoError}, remappings::{RelativeRemapping, Remapping}, - ConfigurableArtifacts, EvmVersion, Project, ProjectPathsConfig, Solc, SolcConfig, + CompilerConfig, ConfigurableArtifacts, EvmVersion, Project, ProjectPathsConfig, Solc, + SolcConfig, }; use inflector::Inflector; use regex::Regex; @@ -35,6 +37,7 @@ use std::{ fs, path::{Path, PathBuf}, str::FromStr, + sync::Arc, }; // Macros useful for creating a figment. @@ -683,7 +686,7 @@ impl Config { /// Otherwise it returns the configured [EvmVersion]. pub fn get_normalized_evm_version(&self) -> EvmVersion { if let Some(version) = self.solc.as_ref().and_then(|solc| solc.try_version().ok()) { - if let Some(evm_version) = self.evm_version.normalize_version(&version) { + if let Some(evm_version) = self.evm_version.normalize_version_solc(&version) { return evm_version; } } @@ -756,14 +759,10 @@ impl Config { /// Creates a [Project] with the given `cached` and `no_artifacts` flags pub fn create_project(&self, cached: bool, no_artifacts: bool) -> Result { - let mut project = Project::builder() + let project = Project::builder() .artifacts(self.configured_artifacts_handler()) .paths(self.project_paths()) - .allowed_path(&self.__root.0) - .allowed_paths(&self.libs) - .allowed_paths(&self.allow_paths) - .include_paths(&self.include_paths) - .solc_config(SolcConfig::builder().settings(self.solc_settings()?).build()) + .settings(SolcConfig::builder().settings(self.solc_settings()?).build().settings) .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 { @@ -771,21 +770,16 @@ impl Config { } else { Severity::Error }) - .set_auto_detect(self.is_auto_detect()) .set_offline(self.offline) .set_cached(cached && !self.build_info) .set_build_info(!no_artifacts && self.build_info) .set_no_artifacts(no_artifacts) - .build()?; + .build(self.compiler_config()?)?; if self.force { self.cleanup(&project)?; } - if let Some(solc) = self.ensure_solc()? { - project.solc = solc; - } - Ok(project) } @@ -812,20 +806,19 @@ impl Config { /// If `solc` is [`SolcReq::Local`] then this will ensure that the path exists. fn ensure_solc(&self) -> Result, SolcError> { if let Some(ref solc) = self.solc { + let version_manager = SolcVersionManager::default(); let solc = match solc { SolcReq::Version(version) => { - let v = version.to_string(); - let mut solc = Solc::find_svm_installed_version(&v)?; - if solc.is_none() { + if let Ok(solc) = version_manager.get_installed(version) { + solc + } else { if self.offline { return Err(SolcError::msg(format!( "can't install missing solc {version} in offline mode" ))) } - Solc::blocking_install(version)?; - solc = Solc::find_svm_installed_version(&v)?; + version_manager.install(version)? } - solc } SolcReq::Local(solc) => { if !solc.is_file() { @@ -834,10 +827,10 @@ impl Config { solc.display() ))) } - Some(Solc::new(solc)) + Solc::new(solc)? } }; - return Ok(solc) + return Ok(Some(solc)) } Ok(None) @@ -890,7 +883,11 @@ impl Config { .scripts(&self.script) .artifacts(&self.out) .libs(self.libs.iter()) - .remappings(self.get_all_remappings()); + .remappings(self.get_all_remappings()) + .allowed_path(&self.__root.0) + .allowed_paths(&self.libs) + .allowed_paths(&self.allow_paths) + .include_paths(&self.include_paths); if let Some(build_info_path) = &self.build_info_path { builder = builder.build_infos(build_info_path); @@ -899,6 +896,15 @@ impl Config { builder.build_with_root(&self.__root.0) } + /// Returns configuration for a compiler to use when setting up a [Project]. + pub fn compiler_config(&self) -> Result, SolcError> { + if let Some(solc) = self.ensure_solc()? { + Ok(CompilerConfig::Specific(solc)) + } else { + Ok(CompilerConfig::AutoDetect(Arc::new(SolcVersionManager::default()))) + } + } + /// Returns all configured [`Remappings`] /// /// **Note:** this will add an additional `/=` remapping here, see @@ -1271,7 +1277,7 @@ impl Config { pub fn with_root(root: impl Into) -> Self { // autodetect paths let root = root.into(); - let paths = ProjectPathsConfig::builder().build_with_root(&root); + let paths = ProjectPathsConfig::builder().build_with_root::<()>(&root); let artifacts: PathBuf = paths.artifacts.file_name().unwrap().into(); Config { __root: paths.root.into(), @@ -1733,7 +1739,7 @@ impl Config { if let Some(version) = solc .try_version() .ok() - .and_then(|version| self.evm_version.normalize_version(&version)) + .and_then(|version| self.evm_version.normalize_version_solc(&version)) { // normalize evm_version based on the provided solc version self.evm_version = version; @@ -2098,10 +2104,7 @@ impl SolcReq { fn try_version(&self) -> Result { match self { SolcReq::Version(version) => Ok(version.clone()), - SolcReq::Local(path) => Solc::new(path).version().map_err(|err| { - warn!("failed to get solc version from {}: {}", path.display(), err); - err - }), + SolcReq::Local(path) => Solc::new(path).map(|solc| solc.version), } } } @@ -3754,7 +3757,7 @@ mod tests { )?; let config = Config::load(); - assert_eq!(config.solc, Some(SolcReq::Version("0.8.12".parse().unwrap()))); + assert_eq!(config.solc, Some(SolcReq::Version(Version::new(0, 8, 12)))); jail.create_file( "foundry.toml", @@ -3765,7 +3768,7 @@ mod tests { )?; let config = Config::load(); - assert_eq!(config.solc, Some(SolcReq::Version("0.8.12".parse().unwrap()))); + assert_eq!(config.solc, Some(SolcReq::Version(Version::new(0, 8, 12)))); jail.create_file( "foundry.toml", @@ -3780,7 +3783,7 @@ mod tests { jail.set_env("FOUNDRY_SOLC_VERSION", "0.6.6"); let config = Config::load(); - assert_eq!(config.solc, Some(SolcReq::Version("0.6.6".parse().unwrap()))); + assert_eq!(config.solc, Some(SolcReq::Version(Version::new(0, 6, 6)))); Ok(()) }); } @@ -3799,7 +3802,7 @@ mod tests { )?; let config = Config::load(); - assert_eq!(config.solc, Some(SolcReq::Version("0.8.12".parse().unwrap()))); + assert_eq!(config.solc, Some(SolcReq::Version(Version::new(0, 8, 12)))); Ok(()) }); @@ -3814,7 +3817,7 @@ mod tests { )?; let config = Config::load(); - assert_eq!(config.solc, Some(SolcReq::Version("0.8.20".parse().unwrap()))); + assert_eq!(config.solc, Some(SolcReq::Version(Version::new(0, 8, 20)))); Ok(()) }); diff --git a/crates/doc/src/builder.rs b/crates/doc/src/builder.rs index ac54e97e2..ae51e982c 100644 --- a/crates/doc/src/builder.rs +++ b/crates/doc/src/builder.rs @@ -4,7 +4,7 @@ use crate::{ }; use forge_fmt::{FormatterConfig, Visitable}; use foundry_common::glob::expand_globs; -use foundry_compilers::utils::source_files_iter; +use foundry_compilers::{utils::source_files_iter, SOLC_EXTENSIONS}; use foundry_config::DocConfig; use itertools::Itertools; use mdbook::MDBook; @@ -101,7 +101,7 @@ impl DocBuilder { let ignored = expand_globs(&self.root, self.config.ignore.iter())?; // Collect and parse source files - let sources = source_files_iter(&self.sources) + let sources = source_files_iter(&self.sources, SOLC_EXTENSIONS) .filter(|file| !ignored.contains(file)) .collect::>(); @@ -110,7 +110,11 @@ impl DocBuilder { return Ok(()) } - let library_sources = self.libraries.iter().flat_map(source_files_iter).collect::>(); + let library_sources = self + .libraries + .iter() + .flat_map(|lib| source_files_iter(lib, SOLC_EXTENSIONS)) + .collect::>(); let combined_sources = sources .iter() diff --git a/crates/evm/coverage/src/lib.rs b/crates/evm/coverage/src/lib.rs index 7a8f51c2e..a563764b8 100644 --- a/crates/evm/coverage/src/lib.rs +++ b/crates/evm/coverage/src/lib.rs @@ -14,6 +14,7 @@ use std::{ collections::{BTreeMap, HashMap}, fmt::Display, ops::{AddAssign, Deref, DerefMut}, + path::PathBuf, }; use eyre::{Context, Result}; @@ -31,9 +32,9 @@ pub use inspector::CoverageCollector; #[derive(Clone, Debug, Default)] pub struct CoverageReport { /// A map of source IDs to the source path - pub source_paths: HashMap<(Version, usize), String>, + pub source_paths: HashMap<(Version, usize), PathBuf>, /// A map of source paths to source IDs - pub source_paths_to_ids: HashMap<(Version, String), usize>, + pub source_paths_to_ids: HashMap<(Version, PathBuf), usize>, /// All coverage items for the codebase, keyed by the compiler version. pub items: HashMap>, /// All item anchors for the codebase, keyed by their contract ID. @@ -46,13 +47,13 @@ pub struct CoverageReport { impl CoverageReport { /// Add a source file path. - pub fn add_source(&mut self, version: Version, source_id: usize, path: String) { + pub fn add_source(&mut self, version: Version, source_id: usize, path: PathBuf) { self.source_paths.insert((version.clone(), source_id), path.clone()); self.source_paths_to_ids.insert((version, path), source_id); } /// Get the source ID for a specific source file path. - pub fn get_source_id(&self, version: Version, path: String) -> Option<&usize> { + pub fn get_source_id(&self, version: Version, path: PathBuf) -> Option<&usize> { self.source_paths_to_ids.get(&(version, path)) } @@ -78,21 +79,17 @@ impl CoverageReport { } /// Get coverage summaries by source file path - pub fn summary_by_file(&self) -> impl Iterator { - let mut summaries: BTreeMap = BTreeMap::new(); + pub fn summary_by_file(&self) -> impl Iterator { + let mut summaries = BTreeMap::new(); for (version, items) in self.items.iter() { for item in items { - let mut summary = summaries - .entry( - self.source_paths - .get(&(version.clone(), item.loc.source_id)) - .cloned() - .unwrap_or_else(|| { - format!("Unknown (ID: {}, solc: {version})", item.loc.source_id) - }), - ) - .or_default(); + let Some(path) = + self.source_paths.get(&(version.clone(), item.loc.source_id)).cloned() + else { + continue; + }; + let mut summary = summaries.entry(path).or_default(); summary += item; } } @@ -101,22 +98,17 @@ impl CoverageReport { } /// Get coverage items by source file path - pub fn items_by_source(&self) -> impl Iterator)> { - let mut items_by_source: BTreeMap> = BTreeMap::new(); + pub fn items_by_source(&self) -> impl Iterator)> { + let mut items_by_source: BTreeMap<_, Vec<_>> = BTreeMap::new(); for (version, items) in self.items.iter() { for item in items { - items_by_source - .entry( - self.source_paths - .get(&(version.clone(), item.loc.source_id)) - .cloned() - .unwrap_or_else(|| { - format!("Unknown (ID: {}, solc: {version})", item.loc.source_id) - }), - ) - .or_default() - .push(item.clone()); + let Some(path) = + self.source_paths.get(&(version.clone(), item.loc.source_id)).cloned() + else { + continue; + }; + items_by_source.entry(path).or_default().push(item.clone()); } } diff --git a/crates/forge/bin/cmd/build.rs b/crates/forge/bin/cmd/build.rs index c2e6715a4..673198fdc 100644 --- a/crates/forge/bin/cmd/build.rs +++ b/crates/forge/bin/cmd/build.rs @@ -3,7 +3,7 @@ use clap::Parser; use eyre::Result; use foundry_cli::{opts::CoreBuildArgs, utils::LoadConfig}; use foundry_common::compile::{ProjectCompiler, SkipBuildFilter, SkipBuildFilters}; -use foundry_compilers::{Project, ProjectCompileOutput}; +use foundry_compilers::{Project, ProjectCompileOutput, SolcSparseFileFilter}; use foundry_config::{ figment::{ self, @@ -94,8 +94,11 @@ impl BuildArgs { .bail(!self.format_json); if let Some(skip) = self.skip { if !skip.is_empty() { - compiler = compiler - .filter(Box::new(SkipBuildFilters::new(skip, project.root().to_path_buf())?)); + let filter = SolcSparseFileFilter::new(SkipBuildFilters::new( + skip.clone(), + project.root().to_path_buf(), + )?); + compiler = compiler.filter(Box::new(filter)); } } let output = compiler.compile(&project)?; diff --git a/crates/forge/bin/cmd/clone.rs b/crates/forge/bin/cmd/clone.rs index ca6fdf7be..c3315e198 100644 --- a/crates/forge/bin/cmd/clone.rs +++ b/crates/forge/bin/cmd/clone.rs @@ -12,7 +12,7 @@ use foundry_common::{compile::ProjectCompiler, fs}; use foundry_compilers::{ artifacts::{output_selection::ContractOutputSelection, Settings, StorageLayout}, remappings::{RelativeRemapping, Remapping}, - ConfigurableContractArtifact, ProjectCompileOutput, ProjectPathsConfig, + ConfigurableContractArtifact, ProjectCompileOutput, ProjectPathsConfig, Solc, }; use foundry_config::{Chain, Config}; use std::{ @@ -417,7 +417,7 @@ fn update_config_by_metadata( /// A list of remappings is returned fn dump_sources(meta: &Metadata, root: &PathBuf, no_reorg: bool) -> Result> { // get config - let path_config = ProjectPathsConfig::builder().build_with_root(root); + let path_config = ProjectPathsConfig::builder().build_with_root::(root); // we will canonicalize the sources directory later let src_dir = &path_config.sources; let lib_dir = &path_config.libraries[0]; diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index 2b5ce85a6..d099035fb 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -125,13 +125,12 @@ impl CoverageArgs { // https://github.com/ethereum/solidity/issues/12533#issuecomment-1013073350 // And also in new releases of solidity: // https://github.com/ethereum/solidity/issues/13972#issuecomment-1628632202 - project.solc_config.settings = - project.solc_config.settings.with_via_ir_minimum_optimization() + project.settings = project.settings.with_via_ir_minimum_optimization() } else { - project.solc_config.settings.optimizer.disable(); - project.solc_config.settings.optimizer.runs = None; - project.solc_config.settings.optimizer.details = None; - project.solc_config.settings.via_ir = None; + project.settings.optimizer.disable(); + project.settings.optimizer.runs = None; + project.settings.optimizer.details = None; + project.settings.via_ir = None; } let output = ProjectCompiler::default() @@ -186,8 +185,7 @@ impl CoverageArgs { .filter_map(|(id, artifact)| { let contract_id = ContractId { version: id.version.clone(), - source_id: *report - .get_source_id(id.version, id.source.to_string_lossy().to_string())?, + source_id: *report.get_source_id(id.version, id.source)?, contract_name: id.name, }; let source_maps = ( @@ -348,10 +346,9 @@ impl CoverageArgs { }); for (artifact_id, hits, is_deployed_code) in data { - if let Some(source_id) = report.get_source_id( - artifact_id.version.clone(), - artifact_id.source.to_string_lossy().to_string(), - ) { + if let Some(source_id) = + report.get_source_id(artifact_id.version.clone(), artifact_id.source) + { let source_id = *source_id; report.add_hit_map( &ContractId { diff --git a/crates/forge/bin/cmd/fmt.rs b/crates/forge/bin/cmd/fmt.rs index 06e05a568..6ff5387fa 100644 --- a/crates/forge/bin/cmd/fmt.rs +++ b/crates/forge/bin/cmd/fmt.rs @@ -3,6 +3,7 @@ use eyre::Result; use forge_fmt::{format_to, parse, print_diagnostics_report}; use foundry_cli::utils::{FoundryPathExt, LoadConfig}; use foundry_common::{fs, glob::expand_globs, term::cli_warn}; +use foundry_compilers::SOLC_EXTENSIONS; use foundry_config::impl_figment_convert_basic; use rayon::prelude::*; use similar::{ChangeTag, TextDiff}; @@ -81,7 +82,10 @@ impl FmtArgs { } if path.is_dir() { - inputs.extend(foundry_compilers::utils::source_files_iter(path)); + inputs.extend(foundry_compilers::utils::source_files_iter( + path, + SOLC_EXTENSIONS, + )); } else if path.is_sol() { inputs.push(path.to_path_buf()); } else { diff --git a/crates/forge/bin/cmd/geiger/mod.rs b/crates/forge/bin/cmd/geiger/mod.rs index 27555ed1a..451c29dbd 100644 --- a/crates/forge/bin/cmd/geiger/mod.rs +++ b/crates/forge/bin/cmd/geiger/mod.rs @@ -1,7 +1,7 @@ use clap::{Parser, ValueHint}; use eyre::{Result, WrapErr}; use foundry_cli::utils::LoadConfig; -use foundry_compilers::Graph; +use foundry_compilers::{resolver::parse::SolData, Graph}; use foundry_config::{impl_figment_convert_basic, Config}; use itertools::Itertools; use rayon::prelude::*; @@ -62,7 +62,11 @@ impl GeigerArgs { let mut sources: Vec = { if self.paths.is_empty() { - Graph::resolve(&config.project_paths())?.files().keys().cloned().collect() + Graph::::resolve(&config.project_paths())? + .files() + .keys() + .cloned() + .collect() } else { self.paths .iter() diff --git a/crates/forge/bin/cmd/selectors.rs b/crates/forge/bin/cmd/selectors.rs index 8e07fb1f6..d3cada4b5 100644 --- a/crates/forge/bin/cmd/selectors.rs +++ b/crates/forge/bin/cmd/selectors.rs @@ -81,8 +81,7 @@ impl SelectorsSubcommands { output .into_artifacts_with_files() .filter(|(file, _, _)| { - let is_sources_path = file - .starts_with(&project.paths.sources.to_string_lossy().to_string()); + let is_sources_path = file.starts_with(&project.paths.sources); let is_test = file.is_sol_test(); is_sources_path && !is_test @@ -212,8 +211,7 @@ impl SelectorsSubcommands { outcome .into_artifacts_with_files() .filter(|(file, _, _)| { - let is_sources_path = file - .starts_with(&project.paths.sources.to_string_lossy().to_string()); + let is_sources_path = file.starts_with(&project.paths.sources); let is_test = file.is_sol_test(); is_sources_path && !is_test diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index ed0c1305c..b20017c85 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -19,7 +19,9 @@ use foundry_common::{ evm::EvmArgs, shell, }; -use foundry_compilers::{artifacts::output_selection::OutputSelection, utils::source_files_iter}; +use foundry_compilers::{ + artifacts::output_selection::OutputSelection, utils::source_files_iter, SOLC_EXTENSIONS, +}; use foundry_config::{ figment, figment::{ @@ -149,7 +151,7 @@ impl TestArgs { filter: &ProjectPathsAwareFilter, ) -> Result> { let mut project = config.create_project(true, true)?; - project.solc_config.settings.output_selection = + project.settings.output_selection = OutputSelection::common_output_selection(["abi".to_string()]); let output = project.compile()?; @@ -201,7 +203,7 @@ impl TestArgs { } // Always recompile all sources to ensure that `getCode` cheatcode can use any artifact. - test_sources.extend(source_files_iter(project.paths.sources)); + test_sources.extend(source_files_iter(project.paths.sources, SOLC_EXTENSIONS)); Ok(test_sources) } diff --git a/crates/forge/bin/cmd/tree.rs b/crates/forge/bin/cmd/tree.rs index 813301025..088975d87 100644 --- a/crates/forge/bin/cmd/tree.rs +++ b/crates/forge/bin/cmd/tree.rs @@ -2,7 +2,7 @@ use clap::Parser; use eyre::Result; use foundry_cli::{opts::ProjectPathsArgs, utils::LoadConfig}; use foundry_compilers::{ - resolver::{Charset, TreeOptions}, + resolver::{parse::SolData, Charset, TreeOptions}, Graph, }; @@ -28,7 +28,7 @@ foundry_config::impl_figment_convert!(TreeArgs, opts); impl TreeArgs { pub fn run(self) -> Result<()> { let config = self.try_load_config_emit_warnings()?; - let graph = Graph::resolve(&config.project_paths())?; + let graph = Graph::::resolve(&config.project_paths())?; let opts = TreeOptions { charset: self.charset, no_dedupe: self.no_dedupe }; graph.print_with_options(opts); diff --git a/crates/forge/src/coverage.rs b/crates/forge/src/coverage.rs index 19724bc2f..b3ccc7e82 100644 --- a/crates/forge/src/coverage.rs +++ b/crates/forge/src/coverage.rs @@ -7,7 +7,7 @@ pub use foundry_evm::coverage::*; use std::{ collections::{hash_map, HashMap}, io::Write, - path::PathBuf, + path::{Path, PathBuf}, }; /// A coverage reporter. @@ -49,7 +49,7 @@ impl CoverageReporter for SummaryReporter { fn report(mut self, report: &CoverageReport) -> eyre::Result<()> { for (path, summary) in report.summary_by_file() { self.total += &summary; - self.add_row(path, summary); + self.add_row(path.display(), summary); } self.add_row("Total", self.total.clone()); @@ -95,7 +95,7 @@ impl<'a> CoverageReporter for LcovReporter<'a> { }); writeln!(self.destination, "TN:")?; - writeln!(self.destination, "SF:{file}")?; + writeln!(self.destination, "SF:{}", file.display())?; for item in items { let line = item.loc.line; @@ -150,7 +150,7 @@ pub struct DebugReporter; impl CoverageReporter for DebugReporter { fn report(self, report: &CoverageReport) -> eyre::Result<()> { for (path, items) in report.items_by_source() { - println!("Uncovered for {path}:"); + println!("Uncovered for {}:", path.display()); items.iter().for_each(|item| { if item.hits == 0 { println!("- {item}"); @@ -235,7 +235,15 @@ impl CoverageReporter for BytecodeReporter { writeln!( formatted, "{} {:40} // {}: {}:{}-{}:{} ({}-{})", - hits, code, source_path, sline, spos, eline, epos, start, end + hits, + code, + source_path.display(), + sline, + spos, + eline, + epos, + start, + end )?; } else if let Some(source_id) = source_id { writeln!( @@ -260,7 +268,7 @@ impl CoverageReporter for BytecodeReporter { /// Cache line number offsets for source files struct LineNumberCache { root: PathBuf, - line_offsets: HashMap>, + line_offsets: HashMap>, } impl LineNumberCache { @@ -268,8 +276,8 @@ impl LineNumberCache { LineNumberCache { root, line_offsets: HashMap::new() } } - pub fn get_position(&mut self, path: &str, offset: usize) -> eyre::Result<(usize, usize)> { - let line_offsets = match self.line_offsets.entry(path.to_owned()) { + pub fn get_position(&mut self, path: &Path, offset: usize) -> eyre::Result<(usize, usize)> { + let line_offsets = match self.line_offsets.entry(path.to_path_buf()) { hash_map::Entry::Occupied(o) => o.into_mut(), hash_map::Entry::Vacant(v) => { let text = fs::read_to_string(self.root.join(path))?; diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 8c69fcc4f..a2997be03 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -2,7 +2,10 @@ use alloy_primitives::{Address, B256, U256}; use foundry_cli::utils as forge_utils; -use foundry_compilers::artifacts::{BytecodeHash, OptimizerDetails, RevertStrings, YulDetails}; +use foundry_compilers::{ + artifacts::{BytecodeHash, OptimizerDetails, RevertStrings, YulDetails}, + compilers::{solc::SolcVersionManager, CompilerVersionManager}, +}; use foundry_config::{ cache::{CachedChains, CachedEndpoints, StorageCachingConfig}, fs_permissions::{FsAccessPermission, PathPermission}, @@ -356,9 +359,8 @@ contract Foo {} assert!(cmd.stderr_lossy().contains("this/solc/does/not/exist does not exist")); // `OTHER_SOLC_VERSION` was installed in previous step, so we can use the path to this directly - let local_solc = foundry_compilers::Solc::find_svm_installed_version(OTHER_SOLC_VERSION) - .unwrap() - .expect("solc is installed"); + let local_solc = + SolcVersionManager::default().get_or_install(&OTHER_SOLC_VERSION.parse().unwrap()).unwrap(); cmd.forge_fuse().args(["build", "--force", "--use"]).arg(local_solc.solc).root_arg(); let stdout = cmd.stdout_lossy(); assert!(stdout.contains("Compiler run successful")); diff --git a/crates/linking/src/lib.rs b/crates/linking/src/lib.rs index 869a18e64..ff019ae06 100644 --- a/crates/linking/src/lib.rs +++ b/crates/linking/src/lib.rs @@ -232,8 +232,12 @@ mod tests { .build() .unwrap(); - let project = - Project::builder().paths(paths).ephemeral().no_artifacts().build().unwrap(); + let project = Project::builder() + .paths(paths) + .ephemeral() + .no_artifacts() + .build(Default::default()) + .unwrap(); let mut output = project.compile().unwrap(); diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index 06d76af74..8c41cfde6 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -19,7 +19,7 @@ use foundry_compilers::{ artifacts::{BytecodeObject, Libraries}, info::ContractInfo, utils::source_files_iter, - ArtifactId, ProjectCompileOutput, + ArtifactId, ProjectCompileOutput, SOLC_EXTENSIONS, }; use foundry_linking::{LinkOutput, Linker}; use std::{path::PathBuf, str::FromStr, sync::Arc}; @@ -151,7 +151,8 @@ impl PreprocessedState { }; let sources_to_compile = - source_files_iter(project.paths.sources.as_path()).chain([target_path.to_path_buf()]); + source_files_iter(project.paths.sources.as_path(), SOLC_EXTENSIONS) + .chain([target_path.to_path_buf()]); let output = ProjectCompiler::new() .quiet_if(args.opts.silent) diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index 9d9905d1c..eec9d8bbc 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -1,7 +1,8 @@ use crate::init_tracing; use eyre::{Result, WrapErr}; use foundry_compilers::{ - cache::SolFilesCache, + artifacts::Settings, + cache::CompilerCache, error::Result as SolcResult, project_util::{copy_dir, TempProject}, ArtifactOutput, ConfigurableArtifacts, PathStyle, ProjectPathsConfig, @@ -532,7 +533,9 @@ impl TestProject { #[track_caller] pub fn assert_create_dirs_exists(&self) { self.paths().create_all().unwrap_or_else(|_| panic!("Failed to create project paths")); - SolFilesCache::default().write(&self.paths().cache).expect("Failed to create cache"); + CompilerCache::::default() + .write(&self.paths().cache) + .expect("Failed to create cache"); self.assert_all_paths_exist(); } @@ -1064,7 +1067,7 @@ static IGNORE_IN_FIXTURES: Lazy = Lazy::new(|| { let re = &[ // solc version r" ?Solc(?: version)? \d+.\d+.\d+", - r" with \d+.\d+.\d+", + r" with(?: Solc)? \d+.\d+.\d+", // solc runs r"runs: \d+, μ: \d+, ~: \d+", // elapsed time diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index 317a65941..484b77801 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -15,7 +15,7 @@ use foundry_common::{ use foundry_compilers::{ artifacts::{BytecodeHash, BytecodeObject, CompactContractBytecode}, info::ContractInfo, - Artifact, EvmVersion, + Artifact, EvmVersion, SolcSparseFileFilter, }; use foundry_config::{figment, impl_figment_convert, Chain, Config}; use foundry_evm::{ @@ -372,10 +372,11 @@ impl VerifyBytecodeArgs { if let Some(skip) = &self.skip { if !skip.is_empty() { - compiler = compiler.filter(Box::new(SkipBuildFilters::new( + let filter = SolcSparseFileFilter::new(SkipBuildFilters::new( skip.to_owned(), project.root().to_path_buf(), - )?)); + )?); + compiler = compiler.filter(Box::new(filter)); } } let output = compiler.compile(&project)?; diff --git a/crates/verify/src/etherscan/flatten.rs b/crates/verify/src/etherscan/flatten.rs index 62e534a66..ca028fdab 100644 --- a/crates/verify/src/etherscan/flatten.rs +++ b/crates/verify/src/etherscan/flatten.rs @@ -3,7 +3,8 @@ use eyre::{Context, Result}; use foundry_block_explorers::verify::CodeFormat; use foundry_compilers::{ artifacts::{BytecodeHash, Source}, - AggregatedCompilerOutput, CompilerInput, Project, Solc, + compilers::{solc::SolcVersionManager, Compiler, CompilerVersionManager}, + AggregatedCompilerOutput, Project, SolcInput, }; use semver::{BuildMetadata, Version}; use std::{collections::BTreeMap, path::Path}; @@ -18,7 +19,7 @@ impl EtherscanSourceProvider for EtherscanFlattenedSource { target: &Path, version: &Version, ) -> Result<(String, String, CodeFormat)> { - let metadata = project.solc_config.settings.metadata.as_ref(); + let metadata = project.settings.metadata.as_ref(); let bch = metadata.and_then(|m| m.bytecode_hash).unwrap_or_default(); eyre::ensure!( @@ -66,18 +67,18 @@ impl EtherscanFlattenedSource { version: &Version, contract_path: &Path, ) -> Result<()> { + let vm = SolcVersionManager::default(); let version = strip_build_meta(version.clone()); - let solc = Solc::find_svm_installed_version(version.to_string())? - .unwrap_or(Solc::blocking_install(&version)?); + let solc = vm.get_or_install(&version)?; - let input = CompilerInput { + let input = SolcInput { language: "Solidity".to_string(), sources: BTreeMap::from([("contract.sol".into(), Source::new(content))]), settings: Default::default(), }; - let out = solc.compile(&input)?; - if out.has_error() { + let (_, out) = Compiler::compile(&solc, input)?; + if out.errors.iter().any(|e| e.is_error()) { let mut o = AggregatedCompilerOutput::default(); o.extend(version, out); let diags = o.diagnostics(&[], &[], Default::default()); diff --git a/crates/verify/src/etherscan/mod.rs b/crates/verify/src/etherscan/mod.rs index ec218e714..33dea487e 100644 --- a/crates/verify/src/etherscan/mod.rs +++ b/crates/verify/src/etherscan/mod.rs @@ -407,7 +407,7 @@ impl EtherscanVerificationProvider { SolcReq::Version(version) => return Ok(version.to_owned()), SolcReq::Local(solc) => { if solc.is_file() { - return Ok(Solc::new(solc).version()?) + return Ok(Solc::new(solc).map(|solc| solc.version)?) } } } diff --git a/crates/verify/src/sourcify.rs b/crates/verify/src/sourcify.rs index d30946f89..71fde6336 100644 --- a/crates/verify/src/sourcify.rs +++ b/crates/verify/src/sourcify.rs @@ -112,7 +112,7 @@ impl SourcifyVerificationProvider { let cache = project.read_cache_file()?; let (path, entry) = get_cached_entry_by_name(&cache, &args.contract.name)?; - if entry.solc_config.settings.metadata.is_none() { + if entry.compiler_settings.metadata.is_none() { eyre::bail!( r#"Contract {} was compiled without the solc `metadata` setting. Sourcify requires contract metadata for verification. From aa333c6bebf43cb59de34cdf40b2e2bd70d98442 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Sat, 4 May 2024 15:17:37 +0300 Subject: [PATCH 255/430] chore(invariant) - rename TargetAbiSelector (#7850) * chore(invariant) - rename TargetAbiSelector * Rename contract_abi to artifact --- crates/evm/evm/src/executors/invariant/mod.rs | 10 +++++----- .../targetAbi/TargetArtifactSelectors.t.sol | 11 ++++++----- .../targetAbi/TargetArtifactSelectors2.t.sol | 18 ++++++++++-------- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 37d433a31..23972e0c5 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -53,8 +53,8 @@ sol! { } #[derive(Default)] - struct FuzzAbiSelector { - string contract_abi; + struct FuzzArtifactSelector { + string artifact; bytes4[] selectors; } @@ -77,7 +77,7 @@ sol! { function targetArtifacts() public view returns (string[] memory targetedArtifacts); #[derive(Default)] - function targetArtifactSelectors() public view returns (FuzzAbiSelector[] memory targetedArtifactSelectors); + function targetArtifactSelectors() public view returns (FuzzArtifactSelector[] memory targetedArtifactSelectors); #[derive(Default)] function targetContracts() public view returns (address[] memory targetedContracts); @@ -400,10 +400,10 @@ impl<'a> InvariantExecutor<'a> { .call_sol_default(invariant_address, &IInvariantTest::targetArtifactSelectorsCall {}); // Insert them into the executor `targeted_abi`. - for IInvariantTest::FuzzAbiSelector { contract_abi, selectors } in + for IInvariantTest::FuzzArtifactSelector { artifact, selectors } in result.targetedArtifactSelectors { - let identifier = self.validate_selected_contract(contract_abi, &selectors)?; + let identifier = self.validate_selected_contract(artifact, &selectors)?; self.artifact_filters.targeted.entry(identifier).or_default().extend(selectors); } diff --git a/testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors.t.sol b/testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors.t.sol index d7c8bcdfa..2957c57de 100644 --- a/testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors.t.sol +++ b/testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors.t.sol @@ -3,8 +3,8 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -struct FuzzAbiSelector { - string contract_abi; +struct FuzzArtifactSelector { + string artifact; bytes4[] selectors; } @@ -27,11 +27,12 @@ contract TargetArtifactSelectors is DSTest { hello = new Hi(); } - function targetArtifactSelectors() public returns (FuzzAbiSelector[] memory) { - FuzzAbiSelector[] memory targets = new FuzzAbiSelector[](1); + function targetArtifactSelectors() public returns (FuzzArtifactSelector[] memory) { + FuzzArtifactSelector[] memory targets = new FuzzArtifactSelector[](1); bytes4[] memory selectors = new bytes4[](1); selectors[0] = Hi.no_change.selector; - targets[0] = FuzzAbiSelector("default/fuzz/invariant/targetAbi/TargetArtifactSelectors.t.sol:Hi", selectors); + targets[0] = + FuzzArtifactSelector("default/fuzz/invariant/targetAbi/TargetArtifactSelectors.t.sol:Hi", selectors); return targets; } diff --git a/testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol b/testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol index 573350c6e..c12cae74f 100644 --- a/testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol +++ b/testdata/default/fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol @@ -3,8 +3,8 @@ pragma solidity 0.8.18; import "ds-test/test.sol"; -struct FuzzAbiSelector { - string contract_abi; +struct FuzzArtifactSelector { + string artifact; bytes4[] selectors; } @@ -46,18 +46,20 @@ contract TargetArtifactSelectors2 is DSTest { parent = new Parent(); } - function targetArtifactSelectors() public returns (FuzzAbiSelector[] memory) { - FuzzAbiSelector[] memory targets = new FuzzAbiSelector[](2); + function targetArtifactSelectors() public returns (FuzzArtifactSelector[] memory) { + FuzzArtifactSelector[] memory targets = new FuzzArtifactSelector[](2); bytes4[] memory selectors_child = new bytes4[](1); selectors_child[0] = Child.change_parent.selector; - targets[0] = - FuzzAbiSelector("default/fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol:Child", selectors_child); + targets[0] = FuzzArtifactSelector( + "default/fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol:Child", selectors_child + ); bytes4[] memory selectors_parent = new bytes4[](1); selectors_parent[0] = Parent.create.selector; - targets[1] = - FuzzAbiSelector("default/fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol:Parent", selectors_parent); + targets[1] = FuzzArtifactSelector( + "default/fuzz/invariant/targetAbi/TargetArtifactSelectors2.t.sol:Parent", selectors_parent + ); return targets; } From 2e9b584705ed91c5cb02ce3e4810062bed5c496d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 5 May 2024 10:05:45 +0200 Subject: [PATCH 256/430] chore(deps): weekly `cargo update` (#7857) --- Cargo.lock | 224 +++++++++++++++++++++++++++++------------------------ 1 file changed, 122 insertions(+), 102 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 90effef31..892f5281c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -112,9 +112,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ab339ca7b4ea9115f0578c941abc80a171edf8e5eadd01e6c4237b68db8083" +checksum = "545885d9b0b2c30fd344ae291439b4bfe59e48dd62fbc862f8503d98088967dc" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -157,9 +157,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44294729c145cf7ae65feab544b5b81fb2bb7e2fd060214842eb3989a1e9d882" +checksum = "786689872ec4e7d354810ab0dffd48bb40b838c047522eb031cbd47d15634849" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -198,9 +198,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c715249705afa1e32be79dabfd35e2ef0f1cc02ad2cf48c9d1e20026ee637b" +checksum = "525448f6afc1b70dd0f9d0a8145631bf2f5e434678ab23ab18409ca264cae6b3" dependencies = [ "alloy-rlp", "arbitrary", @@ -462,9 +462,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef9a94a27345fb31e3fcb5f5e9f592bb4847493b07fa1e47dd9fde2222f2e28" +checksum = "89c80a2cb97e7aa48611cbb63950336f9824a174cdf670527cc6465078a26ea1" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", @@ -481,9 +481,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31fe73cd259527e24dc2dbfe64bc95e5ddfcd2b2731f670a11ff72b2be2c25b" +checksum = "c58894b58ac50979eeac6249661991ac40b9d541830d9a725f7714cc9ef08c23" dependencies = [ "alloy-json-abi", "const-hex", @@ -498,18 +498,18 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c8d6e74e4feeaa2bcfdecfd3da247ab53c67bd654ba1907270c32e02b142331" +checksum = "7da8e71ea68e780cc203919e03f69f59e7afe92d2696fb1dcb6662f61e4031b6" dependencies = [ "winnow 0.6.7", ] [[package]] name = "alloy-sol-types" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afaffed78bfb17526375754931e045f96018aa810844b29c7aef823266dd4b4b" +checksum = "399287f68d1081ed8b1f4903c49687658b95b142207d7cb4ae2f4813915343ef" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -524,7 +524,7 @@ version = "0.1.0" source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" dependencies = [ "alloy-json-rpc", - "base64 0.22.0", + "base64 0.22.1", "futures-util", "futures-utils-wasm", "serde", @@ -637,47 +637,48 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.13" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -1113,15 +1114,15 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "aws-config" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4707646259764ab59fd9a50e9de2e92c637b28b36285d6f6fa030e915fbd9" +checksum = "baaa0be6ee7d90b775ae6ccb6d2ba182b91219ec2001f92338773a094246af1d" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1185,9 +1186,9 @@ dependencies = [ [[package]] name = "aws-sdk-kms" -version = "1.22.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4453868f71232e0baf5947972972e87813c8bbb311351f669a6ad6aa5b86ee63" +checksum = "db70afa14e99c6d3bfa45f1c41ec28414f628d1f5a242d8ae2578f4b7a4b8480" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1207,9 +1208,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.21.0" +version = "1.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d70fb493f4183f5102d8a8d0cc9b57aec29a762f55c0e7bf527e0f7177bb408" +checksum = "ca3d6c4cba4e009391b72b0fcf12aff04ea3c9c3aa2ecaafa330326a8bd7e601" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1229,9 +1230,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.21.0" +version = "1.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3f37549b3e38b7ea5efd419d4d7add6ea1e55223506eb0b4fef9d25e7cc90d" +checksum = "73400dc239d14f63d932f4ca7b55af5e9ef1f857f7d70655249ccc287adb2570" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1251,9 +1252,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.21.0" +version = "1.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2ff219a5d4b795cd33251c19dbe9c4b401f2b2cbe513e07c76ada644eaf34e" +checksum = "10f8858308af76fba3e5ffcf1bb56af5471574d2bdfaf0159470c25bc2f760e5" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1347,9 +1348,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44e7945379821074549168917e89e60630647e186a69243248f08c6d168b975a" +checksum = "1cf64e73ef8d4dac6c933230d56d136b75b252edcf82ed36e37d603090cd7348" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -1373,9 +1374,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime-api" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cc56a5c96ec741de6c5e6bf1ce6948be969d6506dfa9c39cffc284e31e4979b" +checksum = "8c19fdae6e3d5ac9cd01f2d6e6c359c5f5a3e028c2d148a8f5b90bf3399a18a7" dependencies = [ "aws-smithy-async", "aws-smithy-types", @@ -1528,9 +1529,9 @@ checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64" -version = "0.22.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64-simd" @@ -1868,9 +1869,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.95" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" +checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd" dependencies = [ "jobserver", "libc", @@ -2167,9 +2168,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "comfy-table" @@ -2513,9 +2514,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "der" @@ -2705,6 +2706,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + [[package]] name = "dotenvy" version = "0.15.7" @@ -3430,9 +3442,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.29" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4556222738635b7a3417ae6130d8f52201e45a0c4d1a907f0826383adb5f85e7" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" dependencies = [ "crc32fast", "miniz_oxide", @@ -3699,7 +3711,7 @@ dependencies = [ "alloy-signer", "alloy-signer-wallet", "alloy-sol-types", - "base64 0.22.0", + "base64 0.22.1", "const-hex", "dialoguer", "eyre", @@ -4653,9 +4665,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", "allocator-api2", @@ -4806,9 +4818,9 @@ dependencies = [ [[package]] name = "http-range-header" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ce4ef31cda248bbdb6e6820603b82dfcd9e833db65a43e997a0ccec777d11fe" +checksum = "08a397c49fec283e3d6211adbe480be95aae5f304cfb923e9970e08956d5168a" [[package]] name = "httparse" @@ -5185,6 +5197,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + [[package]] name = "itertools" version = "0.10.5" @@ -5370,9 +5388,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.153" +version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] name = "libm" @@ -5810,9 +5828,9 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", @@ -5833,9 +5851,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", @@ -6189,7 +6207,7 @@ version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" dependencies = [ - "base64 0.22.0", + "base64 0.22.1", "serde", ] @@ -6210,9 +6228,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.9" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "311fb059dee1a7b802f036316d790138c613a4e8b180c822e3925a662e9f0c95" +checksum = "560131c633294438da9f7c4b08189194b20946c8274c6b9e38881a7874dc8ee8" dependencies = [ "memchr", "thiserror", @@ -6221,9 +6239,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.9" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73541b156d32197eecda1a4014d7f868fd2bcb3c550d5386087cfba442bf69c" +checksum = "26293c9193fbca7b1a3bf9b79dc1e388e927e6cacaa78b4a3ab705a1d3d41459" dependencies = [ "pest", "pest_generator", @@ -6231,9 +6249,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.9" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c35eeed0a3fab112f75165fdc026b3913f4183133f19b49be773ac9ea966e8bd" +checksum = "3ec22af7d3fb470a85dd2ca96b7c577a1eb4ef6f1683a9fe9a8c16e136c04687" dependencies = [ "pest", "pest_meta", @@ -6244,9 +6262,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.7.9" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2adbf29bb9776f28caece835398781ab24435585fe0d4dc1374a61db5accedca" +checksum = "d7a240022f37c361ec1878d646fc5b7d7c4d28d5946e1a80ad5a7a4f4ca0bdcd" dependencies = [ "once_cell", "pest", @@ -6974,7 +6992,7 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" dependencies = [ - "base64 0.22.0", + "base64 0.22.1", "bytes", "futures-channel", "futures-core", @@ -7342,7 +7360,7 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" dependencies = [ - "base64 0.22.0", + "base64 0.22.1", "rustls-pki-types", ] @@ -7464,9 +7482,9 @@ dependencies = [ [[package]] name = "scc" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec96560eea317a9cc4e0bb1f6a2c93c09a19b8c4fc5cb3fcc0ec1c094cd783e2" +checksum = "76ad2bbb0ae5100a07b7a6f2ed7ab5fd0045551a4c507989b7a620046ea3efdc" dependencies = [ "sdd", ] @@ -7482,9 +7500,9 @@ dependencies = [ [[package]] name = "schemars" -version = "0.8.16" +version = "0.8.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" +checksum = "7f55c82c700538496bdc329bb4918a81f87cc8888811bd123cf325a0f2f8d309" dependencies = [ "dyn-clone", "schemars_derive", @@ -7494,14 +7512,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.16" +version = "0.8.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" +checksum = "83263746fe5e32097f06356968a077f96089739c927a61450efa069905eec108" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 1.0.109", + "syn 2.0.60", ] [[package]] @@ -7635,18 +7653,18 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.199" +version = "1.0.200" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" +checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.199" +version = "1.0.200" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" +checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" dependencies = [ "proc-macro2", "quote", @@ -7655,13 +7673,13 @@ dependencies = [ [[package]] name = "serde_derive_internals" -version = "0.26.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.60", ] [[package]] @@ -7903,9 +7921,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", "windows-sys 0.52.0", @@ -8094,7 +8112,7 @@ dependencies = [ "sha2", "thiserror", "url", - "zip 1.1.1", + "zip 1.1.4", ] [[package]] @@ -8134,9 +8152,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70aba06097b6eda3c15f6eebab8a6339e121475bcf08bbe6758807e716c372a1" +checksum = "5aa0cefd02f532035d83cfec82647c6eb53140b0485220760e669f4bad489e36" dependencies = [ "paste", "proc-macro2", @@ -8480,9 +8498,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", @@ -8490,7 +8508,6 @@ dependencies = [ "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] @@ -9491,18 +9508,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "087eca3c1eaf8c47b94d02790dd086cd594b912d2043d4de4bfdd466b3befb7c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "6f4b6c273f496d8fd4eaf18853e6b448760225dc030ff2c485a786859aea6393" dependencies = [ "proc-macro2", "quote", @@ -9551,15 +9568,18 @@ dependencies = [ [[package]] name = "zip" -version = "1.1.1" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2655979068a1f8fa91cb9e8e5b9d3ee54d18e0ddc358f2f4a395afc0929a84b" +checksum = "9cc23c04387f4da0374be4533ad1208cbb091d5c11d070dfef13676ad6497164" dependencies = [ "arbitrary", - "byteorder", "crc32fast", "crossbeam-utils", + "displaydoc", "flate2", + "indexmap", + "num_enum", + "thiserror", ] [[package]] From c08aa6899183ec2fcaa3b5bb8e37874300498dd3 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sun, 5 May 2024 16:52:20 +0200 Subject: [PATCH 257/430] feat: remove most of ethers (#7861) * feat: remove most of ethers * chore: update deny --- Cargo.lock | 717 +++------------------ Cargo.toml | 15 +- README.md | 4 +- crates/anvil/Cargo.toml | 24 +- crates/anvil/core/Cargo.toml | 3 +- crates/anvil/core/src/eth/serde_helpers.rs | 29 - crates/anvil/core/src/types.rs | 19 +- crates/cast/Cargo.toml | 3 +- crates/cast/bin/cmd/bind.rs | 2 +- crates/cheatcodes/Cargo.toml | 5 +- crates/common/Cargo.toml | 14 +- crates/common/src/fmt/ui.rs | 2 +- crates/config/Cargo.toml | 2 +- crates/config/src/cache.rs | 4 +- crates/config/src/fix.rs | 2 +- crates/config/src/lib.rs | 6 +- crates/evm/core/Cargo.toml | 2 +- crates/evm/core/src/fork/backend.rs | 2 +- crates/evm/evm/Cargo.toml | 7 +- crates/fmt/Cargo.toml | 2 +- crates/fmt/src/string.rs | 2 +- crates/fmt/tests/formatter.rs | 6 +- crates/forge/Cargo.toml | 14 +- crates/forge/bin/cmd/bind.rs | 4 +- crates/forge/tests/cli/cmd.rs | 31 - crates/forge/tests/cli/config.rs | 14 +- crates/forge/tests/cli/script.rs | 4 +- crates/linking/Cargo.toml | 2 +- crates/test-utils/Cargo.toml | 2 +- crates/test-utils/src/util.rs | 4 +- crates/wallets/src/wallet_signer.rs | 1 - deny.toml | 22 +- docs/dev/debugging.md | 3 +- 33 files changed, 153 insertions(+), 820 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 892f5281c..66abc3c55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -247,7 +247,7 @@ dependencies = [ "futures", "futures-utils-wasm", "lru", - "reqwest 0.12.4", + "reqwest", "serde_json", "tokio", "tracing", @@ -308,7 +308,7 @@ dependencies = [ "alloy-transport-ws", "futures", "pin-project", - "reqwest 0.12.4", + "reqwest", "serde", "serde_json", "tokio", @@ -347,7 +347,7 @@ dependencies = [ "alloy-rlp", "alloy-rpc-types", "alloy-serde", - "jsonwebtoken 9.3.0", + "jsonwebtoken", "rand 0.8.5", "serde", "thiserror", @@ -543,7 +543,7 @@ source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04 dependencies = [ "alloy-json-rpc", "alloy-transport", - "reqwest 0.12.4", + "reqwest", "serde_json", "tower", "url", @@ -725,8 +725,6 @@ dependencies = [ "crc 3.2.1", "ctrlc", "ethereum-forkid", - "ethers", - "ethers-core", "eyre", "fdlimit", "flate2", @@ -741,12 +739,12 @@ dependencies = [ "itertools 0.12.1", "k256", "parking_lot", - "pretty_assertions", "rand 0.8.5", "revm", "serde", "serde_json", "serde_repr", + "similar-asserts", "tempfile", "thiserror", "tikv-jemallocator", @@ -755,7 +753,7 @@ dependencies = [ "tracing", "tracing-subscriber", "vergen", - "yansi 1.0.1", + "yansi", ] [[package]] @@ -770,6 +768,7 @@ dependencies = [ "alloy-rlp", "alloy-rpc-types", "alloy-rpc-types-trace", + "alloy-serde", "alloy-trie", "anvil-core", "bytes", @@ -834,7 +833,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44055e597c674aef7cb903b2b9f6e4cba1277ed0d2d61dae7cd52d7ffa81f8e2" dependencies = [ "unicode-width", - "yansi 1.0.1", + "yansi", ] [[package]] @@ -1141,7 +1140,7 @@ dependencies = [ "hex", "http 0.2.12", "hyper 0.14.28", - "ring 0.17.8", + "ring", "time", "tokio", "tracing", @@ -1515,12 +1514,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - [[package]] name = "base64" version = "0.21.7" @@ -1650,6 +1643,17 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata 0.1.10", +] + [[package]] name = "bstr" version = "1.9.1" @@ -1719,27 +1723,6 @@ dependencies = [ "either", ] -[[package]] -name = "bzip2" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" -dependencies = [ - "bzip2-sys", - "libc", -] - -[[package]] -name = "bzip2-sys" -version = "0.1.11+1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - [[package]] name = "c-kzg" version = "1.0.0" @@ -1821,8 +1804,7 @@ dependencies = [ "const-hex", "criterion", "dunce", - "ethers-contract", - "ethers-core", + "ethers-contract-abigen", "evm-disassembler", "evmole", "eyre", @@ -1849,7 +1831,7 @@ dependencies = [ "tokio", "tracing", "vergen", - "yansi 1.0.1", + "yansi", ] [[package]] @@ -1872,11 +1854,6 @@ name = "cc" version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd" -dependencies = [ - "jobserver", - "libc", - "once_cell", -] [[package]] name = "cfg-if" @@ -1911,7 +1888,7 @@ dependencies = [ "foundry-evm", "once_cell", "regex", - "reqwest 0.12.4", + "reqwest", "revm", "rustyline", "semver 1.0.22", @@ -1926,7 +1903,7 @@ dependencies = [ "tracing", "tracing-subscriber", "vergen", - "yansi 1.0.1", + "yansi", ] [[package]] @@ -2250,12 +2227,6 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - [[package]] name = "convert_case" version = "0.4.0" @@ -2617,12 +2588,6 @@ dependencies = [ "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" @@ -2808,39 +2773,12 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" -[[package]] -name = "encoding_rs" -version = "0.8.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" -dependencies = [ - "cfg-if", -] - [[package]] name = "endian-type" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" -[[package]] -name = "enr" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a3d8dc56e02f954cac8eb489772c552c473346fc34f67412bb6244fd647f7e4" -dependencies = [ - "base64 0.21.7", - "bytes", - "hex", - "k256", - "log", - "rand 0.8.5", - "rlp", - "serde", - "sha3", - "zeroize", -] - [[package]] name = "enumn" version = "0.1.13" @@ -2995,53 +2933,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "ethers" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "816841ea989f0c69e459af1cf23a6b0033b19a55424a1ea3a30099becdb8dec0" -dependencies = [ - "ethers-addressbook", - "ethers-contract", - "ethers-core", - "ethers-etherscan", - "ethers-middleware", - "ethers-providers", - "ethers-signers", - "ethers-solc", -] - -[[package]] -name = "ethers-addressbook" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5495afd16b4faa556c3bba1f21b98b4983e53c1755022377051a975c3b021759" -dependencies = [ - "ethers-core", - "once_cell", - "serde", - "serde_json", -] - -[[package]] -name = "ethers-contract" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fceafa3578c836eeb874af87abacfb041f92b4da0a78a5edd042564b8ecdaaa" -dependencies = [ - "const-hex", - "ethers-contract-abigen", - "ethers-contract-derive", - "ethers-core", - "ethers-providers", - "futures-util", - "once_cell", - "pin-project", - "serde", - "serde_json", - "thiserror", -] - [[package]] name = "ethers-contract-abigen" version = "2.0.14" @@ -3052,13 +2943,11 @@ dependencies = [ "const-hex", "dunce", "ethers-core", - "ethers-etherscan", "eyre", "prettyplease", "proc-macro2", "quote", "regex", - "reqwest 0.11.27", "serde", "serde_json", "syn 2.0.60", @@ -3066,22 +2955,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "ethers-contract-derive" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87689dcabc0051cde10caaade298f9e9093d65f6125c14575db3fd8c669a168f" -dependencies = [ - "Inflector", - "const-hex", - "ethers-contract-abigen", - "ethers-core", - "proc-macro2", - "quote", - "serde_json", - "syn 2.0.60", -] - [[package]] name = "ethers-core" version = "2.0.14" @@ -3112,138 +2985,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "ethers-etherscan" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79e5973c26d4baf0ce55520bd732314328cabe53193286671b47144145b9649" -dependencies = [ - "chrono", - "ethers-core", - "reqwest 0.11.27", - "semver 1.0.22", - "serde", - "serde_json", - "thiserror", - "tracing", -] - -[[package]] -name = "ethers-middleware" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48f9fdf09aec667c099909d91908d5eaf9be1bd0e2500ba4172c1d28bfaa43de" -dependencies = [ - "async-trait", - "auto_impl", - "ethers-contract", - "ethers-core", - "ethers-providers", - "ethers-signers", - "futures-channel", - "futures-locks", - "futures-util", - "instant", - "reqwest 0.11.27", - "serde", - "serde_json", - "thiserror", - "tokio", - "tracing", - "tracing-futures", - "url", -] - -[[package]] -name = "ethers-providers" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6434c9a33891f1effc9c75472e12666db2fa5a0fec4b29af6221680a6fe83ab2" -dependencies = [ - "async-trait", - "auto_impl", - "base64 0.21.7", - "bytes", - "const-hex", - "enr", - "ethers-core", - "futures-channel", - "futures-core", - "futures-timer", - "futures-util", - "hashers", - "http 0.2.12", - "instant", - "jsonwebtoken 8.3.0", - "once_cell", - "pin-project", - "reqwest 0.11.27", - "serde", - "serde_json", - "thiserror", - "tokio", - "tokio-tungstenite 0.20.1", - "tracing", - "tracing-futures", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winapi", - "ws_stream_wasm", -] - -[[package]] -name = "ethers-signers" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "228875491c782ad851773b652dd8ecac62cda8571d3bc32a5853644dd26766c2" -dependencies = [ - "async-trait", - "coins-bip32", - "coins-bip39", - "const-hex", - "elliptic-curve", - "eth-keystore", - "ethers-core", - "rand 0.8.5", - "sha2", - "thiserror", - "tracing", -] - -[[package]] -name = "ethers-solc" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66244a771d9163282646dbeffe0e6eca4dda4146b6498644e678ac6089b11edd" -dependencies = [ - "cfg-if", - "const-hex", - "dirs 5.0.1", - "dunce", - "ethers-core", - "glob", - "home", - "md-5", - "num_cpus", - "once_cell", - "path-slash", - "rayon", - "regex", - "semver 1.0.22", - "serde", - "serde_json", - "solang-parser", - "svm-rs 0.3.5", - "thiserror", - "tiny-keccak", - "tokio", - "tracing", - "walkdir", - "yansi 0.5.1", -] - [[package]] name = "event-listener" version = "2.5.3" @@ -3497,7 +3238,7 @@ dependencies = [ "criterion", "dialoguer", "dunce", - "ethers-contract", + "ethers-contract-abigen", "evm-disassembler", "eyre", "forge-doc", @@ -3526,20 +3267,20 @@ dependencies = [ "parking_lot", "paste", "path-slash", - "pretty_assertions", "proptest", "rayon", "regex", - "reqwest 0.12.4", + "reqwest", "revm-inspectors", "rustc-hash", "semver 1.0.22", "serde", "serde_json", "similar", + "similar-asserts", "solang-parser", "strum", - "svm-rs 0.5.2", + "svm-rs", "tempfile", "thiserror", "tikv-jemallocator", @@ -3551,7 +3292,7 @@ dependencies = [ "tracing-subscriber", "vergen", "watchexec", - "yansi 1.0.1", + "yansi", ] [[package]] @@ -3587,7 +3328,7 @@ dependencies = [ "ariadne", "foundry-config", "itertools 0.12.1", - "pretty_assertions", + "similar-asserts", "solang-parser", "thiserror", "toml 0.8.12", @@ -3635,7 +3376,7 @@ dependencies = [ "serde_json", "tempfile", "tracing", - "yansi 1.0.1", + "yansi", ] [[package]] @@ -3660,7 +3401,7 @@ dependencies = [ "futures", "once_cell", "regex", - "reqwest 0.12.4", + "reqwest", "revm-primitives", "semver 1.0.22", "serde", @@ -3668,7 +3409,7 @@ dependencies = [ "tempfile", "tokio", "tracing", - "yansi 1.0.1", + "yansi", ] [[package]] @@ -3690,7 +3431,7 @@ dependencies = [ "alloy-json-abi", "alloy-primitives", "foundry-compilers", - "reqwest 0.12.4", + "reqwest", "semver 1.0.22", "serde", "serde_json", @@ -3781,7 +3522,7 @@ dependencies = [ "tracing", "tracing-error", "tracing-subscriber", - "yansi 1.0.1", + "yansi", ] [[package]] @@ -3818,12 +3559,12 @@ dependencies = [ "globset", "num-format", "once_cell", - "pretty_assertions", - "reqwest 0.12.4", + "reqwest", "rustc-hash", "semver 1.0.22", "serde", "serde_json", + "similar-asserts", "tempfile", "thiserror", "tokio", @@ -3831,7 +3572,7 @@ dependencies = [ "tracing", "url", "walkdir", - "yansi 1.0.1", + "yansi", ] [[package]] @@ -3862,14 +3603,14 @@ dependencies = [ "serde_json", "sha2", "solang-parser", - "svm-rs 0.5.2", + "svm-rs", "svm-rs-builds", "tempfile", "thiserror", "tokio", "tracing", "walkdir", - "yansi 1.0.1", + "yansi", ] [[package]] @@ -3889,14 +3630,14 @@ dependencies = [ "number_prefix", "once_cell", "path-slash", - "pretty_assertions", "regex", - "reqwest 0.12.4", + "reqwest", "revm-primitives", "semver 1.0.22", "serde", "serde_json", "serde_regex", + "similar-asserts", "solang-parser", "tempfile", "thiserror", @@ -4051,7 +3792,7 @@ dependencies = [ "tempfile", "tokio", "tracing", - "yansi 1.0.1", + "yansi", ] [[package]] @@ -4088,10 +3829,10 @@ dependencies = [ "foundry-config", "once_cell", "parking_lot", - "pretty_assertions", "rand 0.8.5", "regex", "serde_json", + "similar-asserts", "tracing", "tracing-subscriber", "walkdir", @@ -4134,16 +3875,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" -[[package]] -name = "fs2" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "fs4" version = "0.8.2" @@ -4243,16 +3974,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "futures-locks" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45ec6fe3675af967e67c5536c0b9d44e34e6c52f86bedc4ea49c5317b8e94d06" -dependencies = [ - "futures-channel", - "futures-task", -] - [[package]] name = "futures-macro" version = "0.3.30" @@ -4276,16 +3997,6 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" -[[package]] -name = "futures-timer" -version = "3.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" -dependencies = [ - "gloo-timers", - "send_wrapper 0.4.0", -] - [[package]] name = "futures-util" version = "0.3.30" @@ -4310,15 +4021,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -4366,7 +4068,7 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "848efa0f1210cea8638f95691c82a46f98a74b9e3524f01d4955ebc25a8f84f3" dependencies = [ - "bstr", + "bstr 1.9.1", "btoi", "gix-date", "itoa", @@ -4380,7 +4082,7 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d252a0eddb6df74600d3d8872dc9fe98835a7da43110411d705b682f49d4ac1" dependencies = [ - "bstr", + "bstr 1.9.1", "gix-config-value", "gix-features", "gix-glob", @@ -4403,7 +4105,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e874f41437441c02991dcea76990b9058fadfc54b02ab4dd06ab2218af43897" dependencies = [ "bitflags 2.5.0", - "bstr", + "bstr 1.9.1", "gix-path", "libc", "thiserror", @@ -4415,7 +4117,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc164145670e9130a60a21670d9b6f0f4f8de04e5dd256c51fa5a0340c625902" dependencies = [ - "bstr", + "bstr 1.9.1", "itoa", "thiserror", "time", @@ -4449,7 +4151,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c07c98204529ac3f24b34754540a852593d2a4c7349008df389240266627a72a" dependencies = [ "bitflags 2.5.0", - "bstr", + "bstr 1.9.1", "gix-features", "gix-path", ] @@ -4481,7 +4183,7 @@ version = "0.29.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d96bd620fd08accdd37f70b2183cfa0b001b4f1c6ade8b7f6e15cb3d9e261ce" dependencies = [ - "bstr", + "bstr 1.9.1", "btoi", "gix-actor", "gix-features", @@ -4500,7 +4202,7 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18609c8cbec8508ea97c64938c33cd305b75dfc04a78d0c3b78b8b3fd618a77c" dependencies = [ - "bstr", + "bstr 1.9.1", "gix-trace", "home", "once_cell", @@ -4574,7 +4276,7 @@ version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba9b3737b2cef3dcd014633485f0034b0f1a931ee54aeb7d8f87f177f3c89040" dependencies = [ - "bstr", + "bstr 1.9.1", "thiserror", ] @@ -4591,24 +4293,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" dependencies = [ "aho-corasick", - "bstr", + "bstr 1.9.1", "log", "regex-automata 0.4.6", "regex-syntax 0.8.3", ] -[[package]] -name = "gloo-timers" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - [[package]] name = "group" version = "0.13.0" @@ -4674,15 +4364,6 @@ dependencies = [ "serde", ] -[[package]] -name = "hashers" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2bca93b15ea5a746f220e56587f71e73c6165eab783df9e26590069953e3c30" -dependencies = [ - "fxhash", -] - [[package]] name = "heck" version = "0.4.1" @@ -5236,15 +4917,6 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" -[[package]] -name = "jobserver" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" -dependencies = [ - "libc", -] - [[package]] name = "js-sys" version = "0.3.69" @@ -5265,20 +4937,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "jsonwebtoken" -version = "8.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" -dependencies = [ - "base64 0.21.7", - "pem 1.1.1", - "ring 0.16.20", - "serde", - "serde_json", - "simple_asn1", -] - [[package]] name = "jsonwebtoken" version = "9.3.0" @@ -5287,8 +4945,8 @@ checksum = "b9ae10193d25051e74945f1ea2d0b42e03cc3b890f7e4cc5faa44997d808193f" dependencies = [ "base64 0.21.7", "js-sys", - "pem 3.0.4", - "ring 0.17.8", + "pem", + "ring", "serde", "serde_json", "simple_asn1", @@ -5970,7 +5628,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c62dcb6174f9cb326eac248f07e955d5d559c272730b6c03e396b443b562788" dependencies = [ - "bstr", + "bstr 1.9.1", "normpath", "winapi", ] @@ -6124,17 +5782,6 @@ dependencies = [ "windows-targets 0.52.5", ] -[[package]] -name = "password-hash" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" -dependencies = [ - "base64ct", - "rand_core 0.6.4", - "subtle", -] - [[package]] name = "paste" version = "1.0.14" @@ -6154,9 +5801,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ "digest 0.10.7", - "hmac", - "password-hash", - "sha2", ] [[package]] @@ -6177,7 +5821,7 @@ checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" dependencies = [ "inlinable_string", "pear_codegen", - "yansi 1.0.1", + "yansi", ] [[package]] @@ -6192,15 +5836,6 @@ dependencies = [ "syn 2.0.60", ] -[[package]] -name = "pem" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" -dependencies = [ - "base64 0.13.1", -] - [[package]] name = "pem" version = "3.0.4" @@ -6518,16 +6153,6 @@ dependencies = [ "termtree", ] -[[package]] -name = "pretty_assertions" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" -dependencies = [ - "diff", - "yansi 0.5.1", -] - [[package]] name = "prettyplease" version = "0.2.19" @@ -6632,7 +6257,7 @@ dependencies = [ "quote", "syn 2.0.60", "version_check", - "yansi 1.0.1", + "yansi", ] [[package]] @@ -6945,47 +6570,6 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" -[[package]] -name = "reqwest" -version = "0.11.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" -dependencies = [ - "base64 0.21.7", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.28", - "hyper-rustls 0.24.2", - "ipnet", - "js-sys", - "log", - "mime", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls 0.21.12", - "rustls-pemfile 1.0.4", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper 0.1.2", - "system-configuration", - "tokio", - "tokio-rustls 0.24.1", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots 0.25.4", - "winreg 0.50.0", -] - [[package]] name = "reqwest" version = "0.12.4" @@ -7029,7 +6613,7 @@ dependencies = [ "wasm-bindgen-futures", "web-sys", "webpki-roots 0.26.1", - "winreg 0.52.0", + "winreg", ] [[package]] @@ -7122,21 +6706,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin 0.5.2", - "untrusted 0.7.1", - "web-sys", - "winapi", -] - [[package]] name = "ring" version = "0.17.8" @@ -7148,7 +6717,7 @@ dependencies = [ "getrandom 0.2.14", "libc", "spin 0.9.8", - "untrusted 0.9.0", + "untrusted", "windows-sys 0.52.0", ] @@ -7301,7 +6870,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", - "ring 0.17.8", + "ring", "rustls-webpki 0.101.7", "sct", ] @@ -7313,7 +6882,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ "log", - "ring 0.17.8", + "ring", "rustls-pki-types", "rustls-webpki 0.102.3", "subtle", @@ -7376,8 +6945,8 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.17.8", - "untrusted 0.9.0", + "ring", + "untrusted", ] [[package]] @@ -7386,9 +6955,9 @@ version = "0.102.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3bce581c0dd41bce533ce695a1437fa16a7ab5ac3ccfa99fe1a620a7885eabf" dependencies = [ - "ring 0.17.8", + "ring", "rustls-pki-types", - "untrusted 0.9.0", + "untrusted", ] [[package]] @@ -7546,8 +7115,8 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.17.8", - "untrusted 0.9.0", + "ring", + "untrusted", ] [[package]] @@ -7639,12 +7208,6 @@ dependencies = [ "pest", ] -[[package]] -name = "send_wrapper" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" - [[package]] name = "send_wrapper" version = "0.6.0" @@ -7885,6 +7448,20 @@ name = "similar" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" +dependencies = [ + "bstr 0.2.17", + "unicode-segmentation", +] + +[[package]] +name = "similar-asserts" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e041bb827d1bfca18f213411d51b665309f1afb37a04a5d1464530e13779fc0f" +dependencies = [ + "console", + "similar", +] [[package]] name = "simple_asn1" @@ -8075,26 +7652,6 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" -[[package]] -name = "svm-rs" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11297baafe5fa0c99d5722458eac6a5e25c01eb1b8e5cd137f54079093daa7a4" -dependencies = [ - "dirs 5.0.1", - "fs2", - "hex", - "once_cell", - "reqwest 0.11.27", - "semver 1.0.22", - "serde", - "serde_json", - "sha2", - "thiserror", - "url", - "zip 0.6.6", -] - [[package]] name = "svm-rs" version = "0.5.2" @@ -8105,14 +7662,14 @@ dependencies = [ "dirs 5.0.1", "fs4", "once_cell", - "reqwest 0.12.4", + "reqwest", "semver 1.0.22", "serde", "serde_json", "sha2", "thiserror", "url", - "zip 1.1.4", + "zip", ] [[package]] @@ -8125,7 +7682,7 @@ dependencies = [ "const-hex", "semver 1.0.22", "serde_json", - "svm-rs 0.5.2", + "svm-rs", ] [[package]] @@ -8174,27 +7731,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "tap" version = "1.0.1" @@ -8689,16 +8225,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project", - "tracing", -] - [[package]] name = "tracing-log" version = "0.2.0" @@ -8881,12 +8407,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - [[package]] name = "untrusted" version = "0.9.0" @@ -9437,16 +8957,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "winreg" version = "0.52.0" @@ -9469,7 +8979,7 @@ dependencies = [ "log", "pharos", "rustc_version 0.4.0", - "send_wrapper 0.6.0", + "send_wrapper", "thiserror", "wasm-bindgen", "wasm-bindgen-futures", @@ -9491,12 +9001,6 @@ version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" -[[package]] -name = "yansi" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" - [[package]] name = "yansi" version = "1.0.1" @@ -9546,26 +9050,6 @@ dependencies = [ "syn 2.0.60", ] -[[package]] -name = "zip" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" -dependencies = [ - "aes", - "byteorder", - "bzip2", - "constant_time_eq", - "crc32fast", - "crossbeam-utils", - "flate2", - "hmac", - "pbkdf2 0.11.0", - "sha1", - "time", - "zstd", -] - [[package]] name = "zip" version = "1.1.4" @@ -9581,32 +9065,3 @@ dependencies = [ "num_enum", "thiserror", ] - -[[package]] -name = "zstd" -version = "0.11.2+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "5.0.2+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" -dependencies = [ - "libc", - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.10+zstd.1.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" -dependencies = [ - "cc", - "pkg-config", -] diff --git a/Cargo.toml b/Cargo.toml index 550a0e644..f777385f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -149,12 +149,7 @@ revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = ] } ## ethers -ethers = { version = "2.0.14", default-features = false } -ethers-core = { version = "2.0.14", default-features = false } -ethers-contract = { version = "2.0.14", default-features = false } ethers-contract-abigen = { version = "2.0.14", default-features = false } -ethers-providers = { version = "2.0.14", default-features = false } -ethers-signers = { version = "2.0.14", default-features = false } ## alloy alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } @@ -170,6 +165,7 @@ alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } +alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } alloy-signer-wallet = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } @@ -192,10 +188,7 @@ solang-parser = "=0.3.3" ## misc arrayvec = "0.7" base64 = "0.22" -chrono = { version = "0.4", default-features = false, features = [ - "clock", - "std", -] } +chrono = { version = "0.4", default-features = false, features = ["clock", "std"] } color-eyre = "0.6" derive_more = "0.99" evm-disassembler = "0.5" @@ -204,7 +197,7 @@ hex = { package = "const-hex", version = "1.6", features = ["hex"] } itertools = "0.12" jsonpath_lib = "0.3" k256 = "0.13" -pretty_assertions = "1.4" +similar-asserts = "1.5" rand = "0.8" rustc-hash = "1.1" serde = { version = "1.0", features = ["derive"] } @@ -224,4 +217,4 @@ axum = "0.7" hyper = "1.0" reqwest = { version = "0.12", default-features = false } tower = "0.4" -tower-http = "0.5" \ No newline at end of file +tower-http = "0.5" diff --git a/README.md b/README.md index b6f4f8640..6ca5e9a6e 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,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][ethers-solc]) 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. @@ -126,7 +126,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](https://github.com/gakonst/ethers-rs/tree/master/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] 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. diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index 7739e8fa2..152c0680a 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -16,11 +16,7 @@ path = "src/anvil.rs" required-features = ["cli"] [build-dependencies] -vergen = { workspace = true, default-features = false, features = [ - "build", - "git", - "gitcl", -] } +vergen = { workspace = true, default-features = false, features = ["build", "git", "gitcl"] } [dependencies] # foundry internal @@ -35,13 +31,7 @@ foundry-evm.workspace = true # evm support bytes = "1.4.0" k256.workspace = true -ethers = { workspace = true, features = ["rustls", "ws", "ipc", "optimism"] } -revm = { workspace = true, features = [ - "std", - "serde", - "memory_limit", - "c-kzg", -] } +revm = { workspace = true, features = ["std", "serde", "memory_limit", "c-kzg"] } alloy-primitives = { workspace = true, features = ["serde"] } alloy-consensus = { workspace = true, features = ["k256", "kzg"] } alloy-contract = { workspace = true, features = ["pubsub"] } @@ -88,11 +78,7 @@ rand = "0.8" eyre.workspace = true # cli -clap = { version = "4", features = [ - "derive", - "env", - "wrap_help", -], optional = true } +clap = { version = "4", features = ["derive", "env", "wrap_help"], optional = true } clap_complete = { version = "4", optional = true } chrono.workspace = true auto_impl = "1" @@ -106,8 +92,6 @@ tikv-jemallocator = { workspace = true, optional = true } [dev-dependencies] alloy-json-abi.workspace = true -ethers = { workspace = true, features = ["abigen", "optimism"] } -ethers-core = { workspace = true, features = ["optimism"] } foundry-compilers = { workspace = true, features = ["project-util", "full"] } alloy-rpc-client = { workspace = true, features = ["pubsub"] } alloy-transport-ipc = { workspace = true, features = ["mock"] } @@ -115,7 +99,7 @@ alloy-transport-ws.workspace = true alloy-json-rpc.workspace = true alloy-pubsub.workspace = true foundry-test-utils.workspace = true -pretty_assertions.workspace = true +similar-asserts.workspace = true tokio = { version = "1", features = ["full"] } crc = "3.0.1" diff --git a/crates/anvil/core/Cargo.toml b/crates/anvil/core/Cargo.toml index 90738112e..92f907c4e 100644 --- a/crates/anvil/core/Cargo.toml +++ b/crates/anvil/core/Cargo.toml @@ -20,8 +20,9 @@ revm = { workspace = true, default-features = false, features = [ ] } alloy-primitives = { workspace = true, features = ["serde"] } -alloy-rpc-types = { workspace = true } +alloy-rpc-types.workspace = true alloy-rpc-types-trace.workspace = true +alloy-serde.workspace = true alloy-rlp.workspace = true alloy-eips.workspace = true alloy-network = { workspace = true, features = ["k256"] } diff --git a/crates/anvil/core/src/eth/serde_helpers.rs b/crates/anvil/core/src/eth/serde_helpers.rs index 311b3a9f2..f7d5bd46d 100644 --- a/crates/anvil/core/src/eth/serde_helpers.rs +++ b/crates/anvil/core/src/eth/serde_helpers.rs @@ -33,35 +33,6 @@ pub mod sequence { } } -pub mod numeric { - use alloy_primitives::U256; - use serde::{Deserialize, Deserializer}; - /// Supports parsing u64 - /// - /// See - pub fn deserialize_stringified_u64_opt<'de, D>(deserializer: D) -> Result, D::Error> - where - D: Deserializer<'de>, - { - if let Some(num) = Option::::deserialize(deserializer)? { - num.try_into().map(Some).map_err(serde::de::Error::custom) - } else { - Ok(None) - } - } - - /// Supports parsing u64 - /// - /// See - pub fn deserialize_stringified_u64<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let num = U256::deserialize(deserializer)?; - num.try_into().map_err(serde::de::Error::custom) - } -} - /// A module that deserializes `[]` optionally pub mod empty_params { use serde::{Deserialize, Deserializer}; diff --git a/crates/anvil/core/src/types.rs b/crates/anvil/core/src/types.rs index 2e0c63b13..87209606f 100644 --- a/crates/anvil/core/src/types.rs +++ b/crates/anvil/core/src/types.rs @@ -24,10 +24,7 @@ impl<'de> serde::Deserialize<'de> for Forking { #[serde(rename_all = "camelCase")] struct ForkOpts { pub json_rpc_url: Option, - #[serde( - default, - deserialize_with = "crate::eth::serde_helpers::numeric::deserialize_stringified_u64_opt" - )] + #[serde(default, with = "alloy_serde::num::u64_opt_via_ruint")] pub block_number: Option, } @@ -60,24 +57,14 @@ impl<'de> serde::Deserialize<'de> for Forking { #[cfg_attr(feature = "serde", serde(untagged))] pub enum EvmMineOptions { Options { - #[cfg_attr( - feature = "serde", - serde( - deserialize_with = "crate::eth::serde_helpers::numeric::deserialize_stringified_u64_opt" - ) - )] + #[cfg_attr(feature = "serde", serde(with = "alloy_serde::num::u64_opt_via_ruint"))] timestamp: Option, // If `blocks` is given, it will mine exactly blocks number of blocks, regardless of any // other blocks mined or reverted during it's operation blocks: Option, }, /// The timestamp the block should be mined with - #[cfg_attr( - feature = "serde", - serde( - deserialize_with = "crate::eth::serde_helpers::numeric::deserialize_stringified_u64_opt" - ) - )] + #[cfg_attr(feature = "serde", serde(with = "alloy_serde::num::u64_opt_via_ruint"))] Timestamp(Option), } diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index 380dfabe0..cf7325512 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -42,8 +42,7 @@ alloy-network.workspace = true alloy-sol-types.workspace = true alloy-chains.workspace = true -ethers-core.workspace = true -ethers-contract = { workspace = true, features = ["abigen"] } +ethers-contract-abigen.workspace = true chrono.workspace = true evm-disassembler.workspace = true diff --git a/crates/cast/bin/cmd/bind.rs b/crates/cast/bin/cmd/bind.rs index 73a62825a..a83afc8ae 100644 --- a/crates/cast/bin/cmd/bind.rs +++ b/crates/cast/bin/cmd/bind.rs @@ -1,5 +1,5 @@ use clap::{Parser, ValueHint}; -use ethers_contract::{Abigen, MultiAbigen}; +use ethers_contract_abigen::{Abigen, MultiAbigen}; use eyre::Result; use foundry_block_explorers::{errors::EtherscanError, Client}; use foundry_cli::opts::EtherscanOpts; diff --git a/crates/cheatcodes/Cargo.toml b/crates/cheatcodes/Cargo.toml index a4e9a0ad8..fa2daa876 100644 --- a/crates/cheatcodes/Cargo.toml +++ b/crates/cheatcodes/Cargo.toml @@ -27,10 +27,7 @@ alloy-sol-types.workspace = true alloy-provider.workspace = true alloy-rpc-types.workspace = true alloy-signer.workspace = true -alloy-signer-wallet = { workspace = true, features = [ - "mnemonic-all-languages", - "keystore", -] } +alloy-signer-wallet = { workspace = true, features = ["mnemonic-all-languages", "keystore"] } parking_lot = "0.12" eyre.workspace = true diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 10f3cf6aa..6a4ae4f38 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -17,21 +17,13 @@ foundry-linking.workspace = true alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } alloy-json-abi.workspace = true -alloy-primitives = { workspace = true, features = [ - "serde", - "getrandom", - "arbitrary", - "rlp", -] } +alloy-primitives = { workspace = true, features = ["serde", "getrandom", "arbitrary", "rlp"] } alloy-rpc-types-engine.workspace = true alloy-rpc-types.workspace = true alloy-rpc-client.workspace = true alloy-provider.workspace = true alloy-transport.workspace = true -alloy-transport-http = { workspace = true, features = [ - "reqwest", - "reqwest-rustls-tls", -] } +alloy-transport-http = { workspace = true, features = ["reqwest", "reqwest-rustls-tls"] } alloy-transport-ws.workspace = true alloy-transport-ipc.workspace = true alloy-json-rpc.workspace = true @@ -66,5 +58,5 @@ num-format.workspace = true [dev-dependencies] foundry-macros.workspace = true -pretty_assertions.workspace = true +similar-asserts.workspace = true tokio = { version = "1", features = ["rt-multi-thread", "macros"] } diff --git a/crates/common/src/fmt/ui.rs b/crates/common/src/fmt/ui.rs index 3893fb4c9..635d1434b 100644 --- a/crates/common/src/fmt/ui.rs +++ b/crates/common/src/fmt/ui.rs @@ -518,7 +518,7 @@ totalDifficulty {}{}", #[cfg(test)] mod tests { use super::*; - use pretty_assertions::assert_eq; + use similar_asserts::assert_eq; use std::str::FromStr; #[test] diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml index 7a65678d4..d5a1ddda6 100644 --- a/crates/config/Cargo.toml +++ b/crates/config/Cargo.toml @@ -44,7 +44,7 @@ walkdir = "2" path-slash = "0.2.1" [dev-dependencies] -pretty_assertions.workspace = true +similar-asserts.workspace = true figment = { version = "0.10", features = ["test"] } tempfile.workspace = true diff --git a/crates/config/src/cache.rs b/crates/config/src/cache.rs index ef0ca40a4..ed1475c7e 100644 --- a/crates/config/src/cache.rs +++ b/crates/config/src/cache.rs @@ -230,7 +230,7 @@ pub struct ChainCache { #[cfg(test)] mod tests { use super::*; - use pretty_assertions::assert_str_eq; + use similar_asserts::assert_eq; #[test] fn can_parse_storage_config() { @@ -307,6 +307,6 @@ mod tests { - Block Explorer (0.0 B)\n\n\t\ - Block 1 (1.0 B)\n\t\ - Block 2 (2.0 B)\n"; - assert_str_eq!(format!("{cache}"), expected); + assert_eq!(format!("{cache}"), expected); } } diff --git a/crates/config/src/fix.rs b/crates/config/src/fix.rs index dc43fd255..a49097c8f 100644 --- a/crates/config/src/fix.rs +++ b/crates/config/src/fix.rs @@ -220,7 +220,7 @@ pub fn fix_tomls() -> Vec { mod tests { use super::*; use figment::Jail; - use pretty_assertions::assert_eq; + use similar_asserts::assert_eq; macro_rules! fix_test { ($(#[$attr:meta])* $name:ident, $fun:expr) => { diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 2a7ddc3ab..8b65bfa78 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -752,7 +752,7 @@ impl Config { } /// Same as [`Self::project()`] but sets configures the project to not emit artifacts and ignore - /// cache, caching causes no output until https://github.com/gakonst/ethers-rs/issues/727 + /// cache. pub fn ephemeral_no_artifacts_project(&self) -> Result { self.create_project(false, true) } @@ -2708,7 +2708,7 @@ mod tests { }; use figment::error::Kind::InvalidType; use foundry_compilers::artifacts::{ModelCheckerEngine, YulDetails}; - use pretty_assertions::assert_eq; + use similar_asserts::assert_eq; use std::{collections::BTreeMap, fs::File, io::Write}; use tempfile::tempdir; use NamedChain::Moonbeam; @@ -4170,7 +4170,7 @@ mod tests { let libs = config.parsed_libraries().unwrap().libs; - pretty_assertions::assert_eq!( + similar_asserts::assert_eq!( libs, BTreeMap::from([ ( diff --git a/crates/evm/core/Cargo.toml b/crates/evm/core/Cargo.toml index 9b558e350..c3285d92c 100644 --- a/crates/evm/core/Cargo.toml +++ b/crates/evm/core/Cargo.toml @@ -35,7 +35,7 @@ revm = { workspace = true, features = [ "optional_no_base_fee", "arbitrary", "optimism", - "c-kzg" + "c-kzg", ] } revm-inspectors.workspace = true diff --git a/crates/evm/core/src/fork/backend.rs b/crates/evm/core/src/fork/backend.rs index db5911884..b60f00e5c 100644 --- a/crates/evm/core/src/fork/backend.rs +++ b/crates/evm/core/src/fork/backend.rs @@ -488,7 +488,7 @@ where /// that is used by the `BackendHandler` to send the result of an executed `BackendRequest` back to /// `SharedBackend`. /// -/// The `BackendHandler` holds an ethers `Provider` to look up missing accounts or storage slots +/// The `BackendHandler` holds a `Provider` to look up missing accounts or storage slots /// from remote (e.g. infura). It detects duplicate requests from multiple `SharedBackend`s and /// bundles them together, so that always only one provider request is executed. For example, there /// are two `SharedBackend`s, `A` and `B`, both request the basic account info of account diff --git a/crates/evm/evm/Cargo.toml b/crates/evm/evm/Cargo.toml index 72f4a6d17..be3324c10 100644 --- a/crates/evm/evm/Cargo.toml +++ b/crates/evm/evm/Cargo.toml @@ -22,12 +22,7 @@ foundry-evm-traces.workspace = true alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } alloy-json-abi.workspace = true -alloy-primitives = { workspace = true, features = [ - "serde", - "getrandom", - "arbitrary", - "rlp", -] } +alloy-primitives = { workspace = true, features = ["serde", "getrandom", "arbitrary", "rlp"] } alloy-sol-types.workspace = true revm = { workspace = true, default-features = false, features = [ "std", diff --git a/crates/fmt/Cargo.toml b/crates/fmt/Cargo.toml index c8937a281..8fbf962eb 100644 --- a/crates/fmt/Cargo.toml +++ b/crates/fmt/Cargo.toml @@ -22,6 +22,6 @@ tracing.workspace = true [dev-dependencies] itertools.workspace = true -pretty_assertions.workspace = true +similar-asserts.workspace = true toml.workspace = true tracing-subscriber = { workspace = true, features = ["env-filter"] } diff --git a/crates/fmt/src/string.rs b/crates/fmt/src/string.rs index 607c890e7..082d25d52 100644 --- a/crates/fmt/src/string.rs +++ b/crates/fmt/src/string.rs @@ -140,7 +140,7 @@ impl QuotedStringExt for str { #[cfg(test)] mod tests { use super::*; - use pretty_assertions::assert_eq; + use similar_asserts::assert_eq; #[test] fn quote_state_char_indices() { diff --git a/crates/fmt/tests/formatter.rs b/crates/fmt/tests/formatter.rs index 18b72a545..efbd63ef5 100644 --- a/crates/fmt/tests/formatter.rs +++ b/crates/fmt/tests/formatter.rs @@ -111,7 +111,7 @@ fn test_formatter( let expected_parsed = parse(expected_source).unwrap(); if !test_config.skip_compare_ast_eq && !source_parsed.pt.ast_eq(&expected_parsed.pt) { - pretty_assertions::assert_eq!( + similar_asserts::assert_eq!( source_parsed.pt, expected_parsed.pt, "(formatted Parse Tree == expected Parse Tree) in {}", @@ -127,7 +127,7 @@ fn test_formatter( let source_formatted = PrettyString(source_formatted); - pretty_assertions::assert_eq!( + similar_asserts::assert_eq!( source_formatted, expected, "(formatted == expected) in {}", @@ -140,7 +140,7 @@ fn test_formatter( let expected_formatted = PrettyString(expected_formatted); - pretty_assertions::assert_eq!( + similar_asserts::assert_eq!( expected_formatted, expected, "(formatted == expected) in {}", diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 9d7b2777d..6bb111eac 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -15,11 +15,7 @@ name = "forge" path = "bin/main.rs" [build-dependencies] -vergen = { workspace = true, default-features = false, features = [ - "build", - "git", - "gitcl", -] } +vergen = { workspace = true, default-features = false, features = ["build", "git", "gitcl"] } [dependencies] # lib @@ -31,7 +27,7 @@ foundry-evm.workspace = true foundry-wallets.workspace = true foundry-linking.workspace = true -ethers-contract = { workspace = true, features = ["abigen"] } +ethers-contract-abigen.workspace = true revm-inspectors.workspace = true @@ -108,10 +104,8 @@ criterion = "0.5" globset = "0.4" paste = "1.0" path-slash = "0.2" -pretty_assertions.workspace = true -svm = { package = "svm-rs", version = "0.5", default-features = false, features = [ - "rustls", -] } +similar-asserts.workspace = true +svm = { package = "svm-rs", version = "0.5", default-features = false, features = ["rustls"] } tempfile.workspace = true tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] } diff --git a/crates/forge/bin/cmd/bind.rs b/crates/forge/bin/cmd/bind.rs index 5be0f262a..11f855f04 100644 --- a/crates/forge/bin/cmd/bind.rs +++ b/crates/forge/bin/cmd/bind.rs @@ -1,5 +1,7 @@ use clap::{Parser, ValueHint}; -use ethers_contract::{Abigen, ContractFilter, ExcludeContracts, MultiAbigen, SelectContracts}; +use ethers_contract_abigen::{ + Abigen, ContractFilter, ExcludeContracts, MultiAbigen, SelectContracts, +}; use eyre::{Result, WrapErr}; use foundry_cli::{opts::CoreBuildArgs, utils::LoadConfig}; use foundry_common::{compile::ProjectCompiler, fs::json_files}; diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index a4deb8bce..b4d8df8cc 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -842,37 +842,6 @@ contract A { assert!(!out.contains("Compiler run successful with warnings:")); }); -// test against a local checkout, useful to debug with local ethers-rs patch -forgetest!( - #[ignore] - can_compile_local_spells, - |_prj, cmd| { - let current_dir = std::env::current_dir().unwrap(); - let root = current_dir - .join("../../foundry-integration-tests/testdata/spells-mainnet") - .to_string_lossy() - .to_string(); - println!("project root: \"{root}\""); - - let eth_rpc_url = foundry_test_utils::rpc::next_http_archive_rpc_endpoint(); - let dss_exec_lib = "src/DssSpell.sol:DssExecLib:0xfD88CeE74f7D78697775aBDAE53f9Da1559728E4"; - - cmd.args([ - "test", - "--root", - root.as_str(), - "--fork-url", - eth_rpc_url.as_str(), - "--fork-block-number", - "14435000", - "--libraries", - dss_exec_lib, - "-vvvvv", - ]); - cmd.assert_non_empty_stdout(); - } -); - // test that a failing `forge build` does not impact followup builds forgetest!(can_build_after_failure, |prj, cmd| { prj.insert_ds_test(); diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index a2997be03..f60eebfde 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -17,7 +17,7 @@ use foundry_test_utils::{ util::{pretty_err, OutputExt, TestCommand, OTHER_SOLC_VERSION}, }; use path_slash::PathBufExt; -use pretty_assertions::assert_eq; +use similar_asserts::assert_eq; use std::{ fs, path::{Path, PathBuf}, @@ -135,7 +135,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { }; prj.write_config(input.clone()); let config = cmd.config(); - pretty_assertions::assert_eq!(input, config); + similar_asserts::assert_eq!(input, config); }); // tests config gets printed to std out @@ -441,7 +441,7 @@ forgetest_init!(can_detect_lib_foundry_toml, |prj, cmd| { let config = cmd.config(); let remappings = config.remappings.iter().cloned().map(Remapping::from).collect::>(); dbg!(&remappings); - pretty_assertions::assert_eq!( + similar_asserts::assert_eq!( remappings, vec![ // global @@ -459,7 +459,7 @@ forgetest_init!(can_detect_lib_foundry_toml, |prj, cmd| { let config = cmd.config(); let remappings = config.remappings.iter().cloned().map(Remapping::from).collect::>(); - pretty_assertions::assert_eq!( + similar_asserts::assert_eq!( remappings, vec![ // default @@ -482,7 +482,7 @@ forgetest_init!(can_detect_lib_foundry_toml, |prj, cmd| { let another_config = cmd.config(); let remappings = another_config.remappings.iter().cloned().map(Remapping::from).collect::>(); - pretty_assertions::assert_eq!( + similar_asserts::assert_eq!( remappings, vec![ // local to the lib @@ -500,7 +500,7 @@ forgetest_init!(can_detect_lib_foundry_toml, |prj, cmd| { pretty_err(&toml_file, fs::write(&toml_file, config.to_string_pretty().unwrap())); let config = cmd.config(); let remappings = config.remappings.iter().cloned().map(Remapping::from).collect::>(); - pretty_assertions::assert_eq!( + similar_asserts::assert_eq!( remappings, vec![ // local to the lib @@ -530,7 +530,7 @@ forgetest_init!(can_prioritise_closer_lib_remappings, |prj, cmd| { let config = cmd.config(); let remappings = config.get_all_remappings().collect::>(); - pretty_assertions::assert_eq!( + similar_asserts::assert_eq!( remappings, vec![ "dep1/=lib/dep1/src/".parse().unwrap(), diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index 714bb036b..eee345595 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -691,7 +691,7 @@ forgetest_async!(check_broadcast_log, |prj, cmd| { std::fs::read_to_string("broadcast/Broadcast.t.sol/31337/run-latest.json").unwrap(); let _run_log = re.replace_all(&run_log, ""); - // pretty_assertions::assert_eq!(fixtures_log, run_log); + // similar_asserts::assert_eq!(fixtures_log, run_log); // Uncomment to recreate the sensitive log // std::fs::copy( @@ -719,7 +719,7 @@ forgetest_async!(check_broadcast_log, |prj, cmd| { let fixtures_log = re.replace_all(&fixtures_log, "\n"); let run_log = re.replace_all(&run_log, "\n"); - pretty_assertions::assert_eq!(fixtures_log, run_log); + similar_asserts::assert_eq!(fixtures_log, run_log); }); forgetest_async!(test_default_sender_balance, |prj, cmd| { diff --git a/crates/linking/Cargo.toml b/crates/linking/Cargo.toml index 31edf1c5d..77c54ccf7 100644 --- a/crates/linking/Cargo.toml +++ b/crates/linking/Cargo.toml @@ -14,4 +14,4 @@ repository.workspace = true foundry-compilers = { workspace = true, features = ["full"] } semver = "1" alloy-primitives = { workspace = true, features = ["rlp"] } -thiserror = "1" \ No newline at end of file +thiserror = "1" diff --git a/crates/test-utils/Cargo.toml b/crates/test-utils/Cargo.toml index a73c53a39..c2ee04cbe 100644 --- a/crates/test-utils/Cargo.toml +++ b/crates/test-utils/Cargo.toml @@ -24,7 +24,7 @@ eyre.workspace = true fd-lock = "4.0.0" once_cell = "1" parking_lot = "0.12" -pretty_assertions.workspace = true +similar-asserts.workspace = true regex = "1" serde_json.workspace = true tracing = "0.1" diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index eec9d8bbc..be7e13af1 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -1090,7 +1090,7 @@ impl OutputExt for Output { #[track_caller] fn stdout_matches_content(&self, expected: &str) { let out = lossy_string(&self.stdout); - pretty_assertions::assert_eq!(normalize_output(&out), normalize_output(expected)); + similar_asserts::assert_eq!(normalize_output(&out), normalize_output(expected)); } #[track_caller] @@ -1103,7 +1103,7 @@ impl OutputExt for Output { fn stderr_matches_path(&self, expected_path: impl AsRef) { let expected = fs::read_to_string(expected_path).unwrap(); let err = lossy_string(&self.stderr); - pretty_assertions::assert_eq!(normalize_output(&err), normalize_output(&expected)); + similar_asserts::assert_eq!(normalize_output(&err), normalize_output(&expected)); } } diff --git a/crates/wallets/src/wallet_signer.rs b/crates/wallets/src/wallet_signer.rs index c28126c43..7b1f698b7 100644 --- a/crates/wallets/src/wallet_signer.rs +++ b/crates/wallets/src/wallet_signer.rs @@ -36,7 +36,6 @@ impl WalletSigner { } pub async fn from_trezor_path(path: TrezorHDPath) -> Result { - // cached to ~/.ethers-rs/trezor/cache/trezor.session let trezor = TrezorSigner::new(path, None).await?; Ok(Self::Trezor(trezor)) } diff --git a/deny.toml b/deny.toml index e3ae679d0..299c1452a 100644 --- a/deny.toml +++ b/deny.toml @@ -1,16 +1,9 @@ -# Temporarily exclude rusoto and ethers-providers from bans since we've yet to transition to the -# Rust AWS SDK. -exclude = ["rusoto_core", "rusoto_kms", "rusoto_credential", "ethers-providers", "tungstenite", "shlex"] - # This section is considered when running `cargo deny check advisories` # More documentation for the advisories section can be found here: # https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html [advisories] -vulnerability = "deny" -unmaintained = "warn" +version = 2 yanked = "warn" -notice = "warn" -ignore = [] # This section is considered when running `cargo deny check bans`. # More documentation about the 'bans' section can be found here: @@ -32,7 +25,9 @@ skip = [] skip-tree = [] [licenses] -unlicensed = "deny" +version = 2 +confidence-threshold = 0.8 + # List of explicitly allowed licenses # See https://spdx.org/licenses/ for list of possible licenses # [possible values: any SPDX 3.7 short identifier (+ optional exception)]. @@ -49,6 +44,7 @@ allow = [ "WTFPL", "BSL-1.0", "0BSD", + "MPL-2.0", ] # Allow 1 or more licenses on a per-crate basis, so that particular licenses @@ -57,14 +53,10 @@ exceptions = [ # CC0 is a permissive license but somewhat unclear status for source code # so we prefer to not have dependencies using it # https://tldrlegal.com/license/creative-commons-cc0-1.0-universal - { allow = ["CC0-1.0"], name = "secp256k1" }, - { allow = ["CC0-1.0"], name = "secp256k1-sys" }, { allow = ["CC0-1.0"], name = "tiny-keccak" }, { allow = ["CC0-1.0"], name = "to_method" }, - { allow = ["CC0-1.0"], name = "more-asserts" }, { allow = ["CC0-1.0"], name = "trezor-client" }, { allow = ["CC0-1.0"], name = "notify" }, - { allow = ["CC0-1.0"], name = "constant_time_eq" }, { allow = ["CC0-1.0"], name = "dunce" }, { allow = ["CC0-1.0"], name = "aurora-engine-modexp" }, ] @@ -76,6 +68,7 @@ name = "unicode-ident" version = "*" expression = "(MIT OR Apache-2.0) AND Unicode-DFS-2016" license-files = [{ path = "LICENSE-UNICODE", hash = 0x3fb01745 }] + [[licenses.clarify]] name = "ring" version = "*" @@ -91,4 +84,5 @@ license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }] unknown-registry = "warn" # Lint level for what to happen when a crate from a git repository that is not # in the allow list is encountered -unknown-git = "allow" +unknown-git = "deny" +allow-git = ["https://github.com/alloy-rs/alloy", "https://github.com/paradigmxyz/evm-inspectors"] diff --git a/docs/dev/debugging.md b/docs/dev/debugging.md index 9431d7c33..df8664440 100644 --- a/docs/dev/debugging.md +++ b/docs/dev/debugging.md @@ -20,4 +20,5 @@ Filters are explained in detail in the [`env_logger` crate docs](https://docs.rs ### Compiler input and output -You can get the compiler input JSON and output JSON from `ethers-solc` by passing the `--build-info` flag. This will create two files: one for the input and one for the output. +You can get the compiler input JSON and output JSON by passing the `--build-info` flag. +This will create two files: one for the input and one for the output. From 784e3829ff6e575277be50837ee4cfcd541a902c Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sun, 5 May 2024 19:41:25 +0200 Subject: [PATCH 258/430] chore: remove unused fork ID support (#7863) --- Cargo.lock | 52 ------------------ crates/anvil/Cargo.toml | 2 - crates/anvil/src/hardfork.rs | 103 ----------------------------------- 3 files changed, 157 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 66abc3c55..7496c56aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -722,9 +722,7 @@ dependencies = [ "clap", "clap_complete", "clap_complete_fig", - "crc 3.2.1", "ctrlc", - "ethereum-forkid", "eyre", "fdlimit", "flate2", @@ -2258,30 +2256,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crc" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" -dependencies = [ - "build_const", -] - -[[package]] -name = "crc" -version = "3.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" -dependencies = [ - "crc-catalog", -] - -[[package]] -name = "crc-catalog" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" - [[package]] name = "crc32fast" version = "1.4.0" @@ -2893,19 +2867,6 @@ dependencies = [ "tiny-keccak", ] -[[package]] -name = "ethereum-forkid" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcace6f36a8fd79d06c535444b42c8966e10733165fca6dec3542abfc3e00318" -dependencies = [ - "crc 1.8.1", - "fastrlp", - "maplit", - "primitive-types", - "thiserror", -] - [[package]] name = "ethereum-types" version = "0.14.1" @@ -3077,19 +3038,6 @@ dependencies = [ "arrayvec", "auto_impl", "bytes", - "fastrlp-derive", -] - -[[package]] -name = "fastrlp-derive" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4caf31122bfc780557fdcd80897e95f12cc4d7217f8ac6b9d150df828a38ee8" -dependencies = [ - "bytes", - "proc-macro2", - "quote", - "syn 2.0.60", ] [[package]] diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index 152c0680a..63a7f89f9 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -85,7 +85,6 @@ auto_impl = "1" ctrlc = { version = "3", optional = true } fdlimit = { version = "0.3", optional = true } clap_complete_fig = "4" -ethereum-forkid = "0.12" [target.'cfg(unix)'.dependencies] tikv-jemallocator = { workspace = true, optional = true } @@ -101,7 +100,6 @@ alloy-pubsub.workspace = true foundry-test-utils.workspace = true similar-asserts.workspace = true tokio = { version = "1", features = ["full"] } -crc = "3.0.1" [features] default = ["cli"] diff --git a/crates/anvil/src/hardfork.rs b/crates/anvil/src/hardfork.rs index 8fabace32..e2f107b53 100644 --- a/crates/anvil/src/hardfork.rs +++ b/crates/anvil/src/hardfork.rs @@ -1,5 +1,4 @@ use alloy_rpc_types::BlockNumberOrTag; -use ethereum_forkid::{ForkHash, ForkId}; use foundry_evm::revm::primitives::SpecId; use std::str::FromStr; @@ -48,58 +47,6 @@ impl Hardfork { Hardfork::Cancun | Hardfork::Latest => 19426587, } } - - /// Get the EIP-2124 fork id for a given hardfork - /// - /// The [`ForkId`](ethereum_forkid::ForkId) includes a CRC32 checksum of the all fork block - /// numbers from genesis, and the next upcoming fork block number. - /// If the next fork block number is not yet known, it is set to 0. - pub fn fork_id(&self) -> ForkId { - match *self { - Hardfork::Frontier => { - ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 } - } - Hardfork::Homestead => { - ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 } - } - Hardfork::Dao => ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 }, - Hardfork::Tangerine => { - ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 } - } - Hardfork::SpuriousDragon => { - ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 } - } - Hardfork::Byzantium => { - ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 } - } - Hardfork::Constantinople | Hardfork::Petersburg => { - ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 } - } - Hardfork::Istanbul => { - ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 } - } - Hardfork::Muirglacier => { - ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 } - } - Hardfork::Berlin => ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 }, - Hardfork::London => ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 }, - Hardfork::ArrowGlacier => { - ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 } - } - Hardfork::GrayGlacier => { - ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 15537394 } - } - Hardfork::Paris => ForkId { hash: ForkHash([0x4f, 0xb8, 0xa8, 0x72]), next: 17034870 }, - Hardfork::Shanghai | Hardfork::Latest => { - // update `next` when another fork block num is known - ForkId { hash: ForkHash([0xc1, 0xfd, 0xf1, 0x81]), next: 19426587 } - } - Hardfork::Cancun => { - // TODO: set fork hash once known - ForkId { hash: ForkHash([0xc1, 0xfd, 0xf1, 0x81]), next: 0 } - } - } - } } impl FromStr for Hardfork { @@ -187,8 +134,6 @@ impl> From for Hardfork { #[cfg(test)] mod tests { use crate::Hardfork; - use alloy_primitives::hex; - use crc::{Crc, CRC_32_ISO_HDLC}; #[test] fn test_hardfork_blocks() { @@ -201,52 +146,4 @@ mod tests { let hf: Hardfork = 12244000u64.into(); assert_eq!(hf, Hardfork::Berlin); } - - #[test] - // this test checks that the fork hash assigned to forks accurately map to the fork_id method - fn test_forkhash_from_fork_blocks() { - // set the genesis hash - let genesis = - hex::decode("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") - .unwrap(); - - // instantiate the crc "hasher" - let crc_hasher = Crc::::new(&CRC_32_ISO_HDLC); - let mut crc_digest = crc_hasher.digest(); - - // check frontier forkhash - crc_digest.update(&genesis); - - // now we go through enum members - let frontier_forkid = Hardfork::Frontier.fork_id(); - let frontier_forkhash = u32::from_be_bytes(frontier_forkid.hash.0); - // clone the digest for finalization so we can update it again - assert_eq!(crc_digest.clone().finalize(), frontier_forkhash); - - // list of the above hardforks - let hardforks = vec![ - Hardfork::Homestead, - Hardfork::Dao, - Hardfork::Tangerine, - Hardfork::SpuriousDragon, - Hardfork::Byzantium, - Hardfork::Constantinople, - Hardfork::Istanbul, - Hardfork::Muirglacier, - Hardfork::Berlin, - Hardfork::London, - Hardfork::ArrowGlacier, - Hardfork::GrayGlacier, - ]; - - // now loop through each hardfork, conducting each forkhash test - for hardfork in hardforks { - // this could also be done with frontier_forkhash.next, but fork_block is used for more - // coverage - let fork_block = hardfork.fork_block().to_be_bytes(); - crc_digest.update(&fork_block); - let fork_hash = u32::from_be_bytes(hardfork.fork_id().hash.0); - assert_eq!(crc_digest.clone().finalize(), fork_hash); - } - } } From c486fca34f74fbfb66233229e55b660a2235a986 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sun, 5 May 2024 20:36:11 +0200 Subject: [PATCH 259/430] chore(deps): replace `parity-ipc-server` with `interprocess` (#7862) --- Cargo.lock | 186 +++++++----------- crates/anvil/core/src/eth/trie.rs | 4 +- crates/anvil/server/Cargo.toml | 4 +- crates/anvil/server/src/ipc.rs | 68 ++++--- crates/anvil/src/config.rs | 10 +- crates/anvil/src/lib.rs | 16 +- crates/anvil/tests/it/ipc.rs | 44 +++-- .../common/src/provider/runtime_transport.rs | 2 +- 8 files changed, 153 insertions(+), 181 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7496c56aa..3557e819b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -210,14 +210,14 @@ dependencies = [ "derive_arbitrary", "derive_more", "ethereum_ssz", - "getrandom 0.2.14", + "getrandom", "hex-literal", "itoa", "k256", "keccak-asm", "proptest", "proptest-derive", - "rand 0.8.5", + "rand", "ruint", "serde", "tiny-keccak", @@ -348,7 +348,7 @@ dependencies = [ "alloy-rpc-types", "alloy-serde", "jsonwebtoken", - "rand 0.8.5", + "rand", "serde", "thiserror", ] @@ -456,7 +456,7 @@ dependencies = [ "elliptic-curve", "eth-keystore", "k256", - "rand 0.8.5", + "rand", "thiserror", ] @@ -559,7 +559,7 @@ dependencies = [ "alloy-transport", "bytes", "futures", - "interprocess", + "interprocess 1.2.1", "pin-project", "serde", "serde_json", @@ -737,7 +737,7 @@ dependencies = [ "itertools 0.12.1", "k256", "parking_lot", - "rand 0.8.5", + "rand", "revm", "serde", "serde_json", @@ -773,7 +773,7 @@ dependencies = [ "c-kzg", "foundry-common", "foundry-evm", - "rand 0.8.5", + "rand", "revm", "serde", "serde_json", @@ -783,7 +783,7 @@ dependencies = [ name = "anvil-rpc" version = "0.2.0" dependencies = [ - "rand 0.8.5", + "rand", "serde", "serde_json", ] @@ -798,7 +798,7 @@ dependencies = [ "bytes", "clap", "futures", - "parity-tokio-ipc", + "interprocess 2.0.1", "parking_lot", "pin-project", "serde", @@ -945,7 +945,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" dependencies = [ "num-traits", - "rand 0.8.5", + "rand", ] [[package]] @@ -955,7 +955,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" dependencies = [ "num-traits", - "rand 0.8.5", + "rand", ] [[package]] @@ -1817,7 +1817,7 @@ dependencies = [ "futures", "indicatif", "itertools 0.12.1", - "rand 0.8.5", + "rand", "rayon", "regex", "rpassword", @@ -2066,7 +2066,7 @@ dependencies = [ "hmac", "once_cell", "pbkdf2 0.12.2", - "rand 0.8.5", + "rand", "sha2", "thiserror", ] @@ -2100,7 +2100,7 @@ dependencies = [ "async-trait", "byteorder", "cfg-if", - "getrandom 0.2.14", + "getrandom", "hex", "hidapi-rusb", "js-sys", @@ -2375,7 +2375,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", - "rand_core 0.6.4", + "rand_core", "subtle", "zeroize", ] @@ -2726,7 +2726,7 @@ dependencies = [ "group", "pem-rfc7468", "pkcs8", - "rand_core 0.6.4", + "rand_core", "sec1", "subtle", "zeroize", @@ -2825,7 +2825,7 @@ dependencies = [ "hex", "hmac", "pbkdf2 0.11.0", - "rand 0.8.5", + "rand", "scrypt", "serde", "serde_json", @@ -2934,7 +2934,7 @@ dependencies = [ "num_enum", "once_cell", "open-fastrlp", - "rand 0.8.5", + "rand", "rlp", "serde", "serde_json", @@ -3078,7 +3078,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ - "rand_core 0.6.4", + "rand_core", "subtle", ] @@ -3118,7 +3118,7 @@ checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ "arbitrary", "byteorder", - "rand 0.8.5", + "rand", "rustc-hex", "static_assertions", ] @@ -3543,7 +3543,7 @@ dependencies = [ "memmap2 0.9.4", "once_cell", "path-slash", - "rand 0.8.5", + "rand", "rayon", "regex", "semver 1.0.22", @@ -3710,7 +3710,7 @@ dependencies = [ "itertools 0.12.1", "parking_lot", "proptest", - "rand 0.8.5", + "rand", "revm", "serde", "thiserror", @@ -3777,7 +3777,7 @@ dependencies = [ "foundry-config", "once_cell", "parking_lot", - "rand 0.8.5", + "rand", "regex", "serde_json", "similar-asserts", @@ -3980,17 +3980,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - [[package]] name = "getrandom" version = "0.2.14" @@ -4000,7 +3989,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] @@ -4254,7 +4243,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", - "rand_core 0.6.4", + "rand_core", "subtle", ] @@ -4803,6 +4792,20 @@ dependencies = [ "winapi", ] +[[package]] +name = "interprocess" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c7fb8583fab9503654385e2bafda123376445a77027a1b106dd7e44cf51122f" +dependencies = [ + "futures-core", + "libc", + "recvmsg", + "tokio", + "widestring", + "windows-sys 0.52.0", +] + [[package]] name = "intmap" version = "0.7.1" @@ -5231,7 +5234,7 @@ checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys 0.48.0", ] @@ -5687,20 +5690,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "parity-tokio-ipc" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9981e32fb75e004cc148f5fb70342f393830e0a4aa62e3cc93b50976218d42b6" -dependencies = [ - "futures", - "libc", - "log", - "rand 0.7.3", - "tokio", - "winapi", -] - [[package]] name = "parking" version = "2.2.0" @@ -5920,7 +5909,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" dependencies = [ "phf_shared 0.10.0", - "rand 0.8.5", + "rand", ] [[package]] @@ -5930,7 +5919,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" dependencies = [ "phf_shared 0.11.2", - "rand 0.8.5", + "rand", ] [[package]] @@ -6230,8 +6219,8 @@ dependencies = [ "bitflags 2.5.0", "lazy_static", "num-traits", - "rand 0.8.5", - "rand_chacha 0.3.1", + "rand", + "rand_chacha", "rand_xorshift", "regex-syntax 0.8.3", "rusty-fork", @@ -6319,19 +6308,6 @@ dependencies = [ "nibble_vec", ] -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] - [[package]] name = "rand" version = "0.8.5" @@ -6339,18 +6315,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", + "rand_chacha", + "rand_core", ] [[package]] @@ -6360,16 +6326,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", + "rand_core", ] [[package]] @@ -6378,16 +6335,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.14", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", + "getrandom", ] [[package]] @@ -6396,7 +6344,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" dependencies = [ - "rand_core 0.6.4", + "rand_core", ] [[package]] @@ -6439,6 +6387,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "recvmsg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" + [[package]] name = "redox_syscall" version = "0.4.1" @@ -6463,7 +6417,7 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ - "getrandom 0.2.14", + "getrandom", "libredox", "thiserror", ] @@ -6662,7 +6616,7 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.14", + "getrandom", "libc", "spin 0.9.8", "untrusted", @@ -6738,7 +6692,7 @@ dependencies = [ "parity-scale-codec", "primitive-types", "proptest", - "rand 0.8.5", + "rand", "rlp", "ruint-macro", "serde", @@ -7093,7 +7047,7 @@ version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10" dependencies = [ - "rand 0.8.5", + "rand", "secp256k1-sys", ] @@ -7388,7 +7342,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest 0.10.7", - "rand_core 0.6.4", + "rand_core", ] [[package]] @@ -7590,7 +7544,7 @@ dependencies = [ "byteorder", "crunchy", "lazy_static", - "rand 0.8.5", + "rand", "rustc-hex", ] @@ -8234,7 +8188,7 @@ dependencies = [ "http 0.2.12", "httparse", "log", - "rand 0.8.5", + "rand", "rustls 0.21.12", "sha1", "thiserror", @@ -8254,7 +8208,7 @@ dependencies = [ "http 1.1.0", "httparse", "log", - "rand 0.8.5", + "rand", "sha1", "thiserror", "url", @@ -8396,7 +8350,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ - "getrandom 0.2.14", + "getrandom", "serde", ] @@ -8470,12 +8424,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -8633,6 +8581,12 @@ dependencies = [ "rustix", ] +[[package]] +name = "widestring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" + [[package]] name = "winapi" version = "0.3.9" diff --git a/crates/anvil/core/src/eth/trie.rs b/crates/anvil/core/src/eth/trie.rs index bcc86ac8b..59c5cd910 100644 --- a/crates/anvil/core/src/eth/trie.rs +++ b/crates/anvil/core/src/eth/trie.rs @@ -1,8 +1,8 @@ -//! Utility functions for Ethereum adapted from https://github.dev/rust-blockchain/ethereum/blob/755dffaa4903fbec1269f50cde9863cf86269a14/src/util.rs -use std::collections::BTreeMap; +//! Utility functions for Ethereum adapted from use alloy_primitives::{fixed_bytes, B256}; use alloy_trie::{HashBuilder, Nibbles}; +use std::collections::BTreeMap; /// The KECCAK of the RLP encoding of empty data. pub const KECCAK_NULL_RLP: B256 = diff --git a/crates/anvil/server/Cargo.toml b/crates/anvil/server/Cargo.toml index a9d870510..250bf2787 100644 --- a/crates/anvil/server/Cargo.toml +++ b/crates/anvil/server/Cargo.toml @@ -25,7 +25,7 @@ parking_lot = "0.12" futures = "0.3" # ipc -parity-tokio-ipc = { version = "0.9", optional = true } +interprocess = { version = "2", optional = true, features = ["tokio"] } bytes = { version = "1.4", optional = true } tokio-util = { version = "0.7", features = ["codec"], optional = true } @@ -40,4 +40,4 @@ pin-project = "1" [features] default = ["ipc"] -ipc = ["parity-tokio-ipc", "bytes", "tokio-util"] +ipc = ["dep:interprocess", "dep:bytes", "dep:tokio-util"] diff --git a/crates/anvil/server/src/ipc.rs b/crates/anvil/server/src/ipc.rs index 152b41873..eb2d2d4fd 100644 --- a/crates/anvil/server/src/ipc.rs +++ b/crates/anvil/server/src/ipc.rs @@ -4,7 +4,7 @@ use crate::{error::RequestError, pubsub::PubSubConnection, PubSubRpcHandler}; use anvil_rpc::request::Request; use bytes::BytesMut; use futures::{ready, Sink, Stream, StreamExt}; -use parity_tokio_ipc::Endpoint; +use interprocess::local_socket::{self as ls, tokio::prelude::*}; use std::{ future::Future, io, @@ -18,14 +18,14 @@ use std::{ pub struct IpcEndpoint { /// the handler for the websocket connection handler: Handler, - /// The endpoint we listen for incoming transactions - endpoint: Endpoint, + /// The path to the socket + path: String, } impl IpcEndpoint { /// Creates a new endpoint with the given handler - pub fn new(handler: Handler, endpoint: String) -> Self { - Self { handler, endpoint: Endpoint::new(endpoint) } + pub fn new(handler: Handler, path: String) -> Self { + Self { handler, path } } /// Returns a stream of incoming connection handlers @@ -34,39 +34,43 @@ impl IpcEndpoint { /// connections, See [`PubSubConnection`] that should be spawned #[instrument(target = "ipc", skip_all)] pub fn incoming(self) -> io::Result>> { - let IpcEndpoint { handler, endpoint } = self; - trace!(endpoint=?endpoint.path(), "starting IPC server" ); + let IpcEndpoint { handler, path } = self; + + trace!(%path, "starting IPC server"); if cfg!(unix) { // ensure the file does not exist - if std::fs::remove_file(endpoint.path()).is_ok() { - warn!(endpoint=?endpoint.path(), "removed existing file"); + if std::fs::remove_file(&path).is_ok() { + warn!(%path, "removed existing file"); } } - let connections = match endpoint.incoming() { - Ok(connections) => connections, - Err(err) => { - error!(%err, "Failed to create IPC listener"); - return Err(err) - } - }; + let name = to_name(path.as_ref())?; + let listener = ls::ListenerOptions::new().name(name).create_tokio()?; + // TODO: https://github.com/kotauskas/interprocess/issues/64 + let connections = futures::stream::unfold(listener, |listener| async move { + let conn = listener.accept().await; + Some((conn, listener)) + }); trace!("established connection listener"); - let connections = connections.filter_map(move |stream| { + Ok(connections.filter_map(move |stream| { let handler = handler.clone(); - Box::pin(async move { - if let Ok(stream) = stream { - trace!("successful incoming IPC connection"); - let framed = tokio_util::codec::Decoder::framed(JsonRpcCodec, stream); - Some(PubSubConnection::new(IpcConn(framed), handler)) - } else { - None + async move { + match stream { + Ok(stream) => { + trace!("successful incoming IPC connection"); + let framed = tokio_util::codec::Decoder::framed(JsonRpcCodec, stream); + Some(PubSubConnection::new(IpcConn(framed), handler)) + } + Err(err) => { + trace!(%err, "unsuccessful incoming IPC connection"); + None + } } - }) - }); - Ok(connections) + } + })) } } @@ -118,7 +122,7 @@ where struct JsonRpcCodec; -// Adapted from +// Adapted from impl tokio_util::codec::Decoder for JsonRpcCodec { type Item = String; type Error = io::Error; @@ -171,3 +175,11 @@ impl tokio_util::codec::Encoder for JsonRpcCodec { Ok(()) } } + +fn to_name(path: &std::ffi::OsStr) -> io::Result> { + if cfg!(windows) && !path.as_encoded_bytes().starts_with(br"\\.\pipe\") { + ls::ToNsName::to_ns_name::(path) + } else { + ls::ToFsName::to_fs_name::(path) + } +} diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index d2abc47ec..9cac52cfe 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -59,12 +59,8 @@ pub const CHAIN_ID: u64 = 31337; pub const DEFAULT_MNEMONIC: &str = "test test test test test test test test test test test junk"; /// The default IPC endpoint -#[cfg(windows)] -pub const DEFAULT_IPC_ENDPOINT: &str = r"\\.\pipe\anvil.ipc"; - -/// The default IPC endpoint -#[cfg(not(windows))] -pub const DEFAULT_IPC_ENDPOINT: &str = "/tmp/anvil.ipc"; +pub const DEFAULT_IPC_ENDPOINT: &str = + if cfg!(unix) { "/tmp/anvil.ipc" } else { r"\\.\pipe\anvil.ipc" }; /// `anvil 0.1.0 (f01b232bc 2022-04-13T23:28:39.493201+00:00)` pub const VERSION_MESSAGE: &str = concat!( @@ -801,7 +797,7 @@ impl NodeConfig { /// Returns the ipc path for the ipc endpoint if any pub fn get_ipc_path(&self) -> Option { - match self.ipc_path.as_ref() { + match &self.ipc_path { Some(path) => path.clone().or_else(|| Some(DEFAULT_IPC_ENDPOINT.to_string())), None => None, } diff --git a/crates/anvil/src/lib.rs b/crates/anvil/src/lib.rs index ce6a85411..2cb39a893 100644 --- a/crates/anvil/src/lib.rs +++ b/crates/anvil/src/lib.rs @@ -12,6 +12,7 @@ use crate::{ }, filter::Filters, logging::{LoggingManager, NodeLogLayer}, + server::error::{NodeError, NodeResult}, service::NodeService, shutdown::Signal, tasks::TaskManager, @@ -23,6 +24,7 @@ use foundry_common::provider::{ProviderBuilder, RetryProvider}; use foundry_evm::revm; use futures::{FutureExt, TryFutureExt}; use parking_lot::Mutex; +use server::try_spawn_ipc; use std::{ future::Future, io, @@ -41,11 +43,8 @@ mod service; mod config; pub use config::{AccountGenerator, NodeConfig, CHAIN_ID, VERSION_MESSAGE}; + mod hardfork; -use crate::server::{ - error::{NodeError, NodeResult}, - spawn_ipc, -}; pub use hardfork::Hardfork; /// ethereum related implementations @@ -223,7 +222,8 @@ pub async fn try_spawn(mut config: NodeConfig) -> io::Result<(EthApi, NodeHandle let (signal, on_shutdown) = shutdown::signal(); let task_manager = TaskManager::new(tokio_handle, on_shutdown); - let ipc_task = config.get_ipc_path().map(|path| spawn_ipc(api.clone(), path)); + let ipc_task = + config.get_ipc_path().map(|path| try_spawn_ipc(api.clone(), path)).transpose()?; let handle = NodeHandle { config, @@ -310,8 +310,10 @@ impl NodeHandle { /// Constructs a [`RetryProvider`] for this handle's HTTP endpoint. pub fn http_provider(&self) -> RetryProvider { - ProviderBuilder::new(&self.http_endpoint()).build().expect("failed to build HTTP provider") - // .interval(Duration::from_millis(500)) + ProviderBuilder::new(&self.http_endpoint()) + .aggressive() + .build() + .expect("failed to build HTTP provider") } /// Constructs a [`RetryProvider`] for this handle's WS endpoint. diff --git a/crates/anvil/tests/it/ipc.rs b/crates/anvil/tests/it/ipc.rs index 676a0f61b..786217ecd 100644 --- a/crates/anvil/tests/it/ipc.rs +++ b/crates/anvil/tests/it/ipc.rs @@ -1,28 +1,34 @@ //! IPC tests -use crate::utils::connect_pubsub; +use crate::{init_tracing, utils::connect_pubsub}; use alloy_primitives::U256; use alloy_provider::Provider; use anvil::{spawn, NodeConfig}; use futures::StreamExt; - -pub fn rand_ipc_endpoint() -> String { - let num: u64 = rand::Rng::gen(&mut rand::thread_rng()); - if cfg!(windows) { - format!(r"\\.\pipe\anvil-ipc-{num}") +use tempfile::TempDir; + +fn ipc_config() -> (Option, NodeConfig) { + let path; + let dir; + if cfg!(unix) { + let tmp = tempfile::tempdir().unwrap(); + path = tmp.path().join("anvil.ipc").to_string_lossy().into_owned(); + dir = Some(tmp); } else { - format!(r"/tmp/anvil-ipc-{num}") + dir = None; + path = format!(r"\\.\pipe\anvil_test_{}.ipc", rand::random::()); } -} - -fn ipc_config() -> NodeConfig { - NodeConfig::test().with_ipc(Some(Some(rand_ipc_endpoint()))) + let config = NodeConfig::test().with_ipc(Some(Some(path))); + (dir, config) } #[tokio::test(flavor = "multi_thread")] -#[cfg_attr(target_os = "windows", ignore)] +#[cfg_attr(windows, ignore = "TODO")] async fn can_get_block_number_ipc() { - let (api, handle) = spawn(ipc_config()).await; + init_tracing(); + + let (_dir, config) = ipc_config(); + let (api, handle) = spawn(config).await; let block_num = api.block_number().unwrap(); assert_eq!(block_num, U256::ZERO); @@ -34,17 +40,19 @@ async fn can_get_block_number_ipc() { } #[tokio::test(flavor = "multi_thread")] -#[cfg_attr(target_os = "windows", ignore)] +#[cfg_attr(windows, ignore = "TODO")] async fn test_sub_new_heads_ipc() { - let (api, handle) = spawn(ipc_config()).await; - - let provider = connect_pubsub(handle.ipc_path().unwrap().as_str()).await; + init_tracing(); - let blocks = provider.subscribe_blocks().await.unwrap().into_stream(); + let (_dir, config) = ipc_config(); + let (api, handle) = spawn(config).await; + let provider = connect_pubsub(handle.ipc_path().unwrap().as_str()).await; // mine a block every 1 seconds api.anvil_set_interval_mining(1).unwrap(); + 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.unwrap()).collect::>(); diff --git a/crates/common/src/provider/runtime_transport.rs b/crates/common/src/provider/runtime_transport.rs index 540214891..38a24e81b 100644 --- a/crates/common/src/provider/runtime_transport.rs +++ b/crates/common/src/provider/runtime_transport.rs @@ -195,7 +195,7 @@ impl RuntimeTransport { async fn connect_ipc(&self) -> Result { let path = url_to_file_path(&self.url) .map_err(|_| RuntimeTransportError::BadPath(self.url.to_string()))?; - let ipc_connector: IpcConnect = path.clone().into(); + let ipc_connector = IpcConnect::new(path.clone()); let ipc = ipc_connector.into_service().await.map_err(|e| { RuntimeTransportError::TransportError(e, path.clone().display().to_string()) })?; From 10c0c7a1160d6500aa1c54053d4f6df9a7249238 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 6 May 2024 17:29:09 +0200 Subject: [PATCH 260/430] fix: use B256::try_from for pk (#7871) --- crates/wallets/src/utils.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/wallets/src/utils.rs b/crates/wallets/src/utils.rs index 08c95242a..898a82fdc 100644 --- a/crates/wallets/src/utils.rs +++ b/crates/wallets/src/utils.rs @@ -26,7 +26,9 @@ pub fn create_private_key_signer(private_key: &str) -> Result { eyre::bail!("Failed to decode private key") }; - match LocalWallet::from_bytes(&B256::from_slice(&private_key)) { + match LocalWallet::from_bytes( + &B256::try_from(private_key.as_slice()).wrap_err("Failed to decode private key")?, + ) { Ok(pk) => Ok(WalletSigner::Local(pk)), Err(err) => { ensure_pk_not_env(privk)?; From 2e3c197afc341c0f4adbb9dbe09fc04ebb9b7a5d Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 6 May 2024 17:49:19 +0200 Subject: [PATCH 261/430] chore: bump alloy 17c5650 (#7868) * chore: bump alloy 17c5650 * fix * fix * fix * fix * bump --- Cargo.lock | 228 +++---------------- Cargo.toml | 48 ++-- crates/anvil/core/src/eth/block.rs | 5 + crates/anvil/core/src/eth/transaction/mod.rs | 24 +- crates/anvil/src/eth/backend/fork.rs | 4 +- crates/anvil/src/eth/backend/mem/mod.rs | 2 + crates/anvil/tests/it/anvil_api.rs | 2 +- crates/anvil/tests/it/fork.rs | 2 +- crates/anvil/tests/it/traces.rs | 2 +- crates/anvil/tests/it/txpool.rs | 2 +- crates/cast/src/lib.rs | 2 +- crates/common/src/transactions.rs | 6 +- crates/debugger/src/tui/draw.rs | 1 + crates/evm/traces/src/lib.rs | 1 + 14 files changed, 74 insertions(+), 255 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3557e819b..199fb03d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,7 +79,7 @@ dependencies = [ [[package]] name = "alloy-consensus" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" +source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" dependencies = [ "alloy-eips", "alloy-primitives", @@ -87,14 +87,12 @@ dependencies = [ "alloy-serde", "c-kzg", "serde", - "sha2", - "thiserror", ] [[package]] name = "alloy-contract" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" +source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -134,7 +132,7 @@ dependencies = [ [[package]] name = "alloy-eips" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" +source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -143,16 +141,18 @@ dependencies = [ "derive_more", "once_cell", "serde", + "sha2", ] [[package]] name = "alloy-genesis" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" +source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" dependencies = [ "alloy-primitives", "alloy-serde", "serde", + "serde_json", ] [[package]] @@ -170,7 +170,7 @@ dependencies = [ [[package]] name = "alloy-json-rpc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" +source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" dependencies = [ "alloy-primitives", "serde", @@ -182,7 +182,7 @@ dependencies = [ [[package]] name = "alloy-network" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" +source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" dependencies = [ "alloy-consensus", "alloy-eips", @@ -226,7 +226,7 @@ dependencies = [ [[package]] name = "alloy-provider" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" +source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" dependencies = [ "alloy-eips", "alloy-json-rpc", @@ -257,7 +257,7 @@ dependencies = [ [[package]] name = "alloy-pubsub" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" +source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -297,7 +297,7 @@ dependencies = [ [[package]] name = "alloy-rpc-client" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" +source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -321,7 +321,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" +source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" dependencies = [ "alloy-consensus", "alloy-eips", @@ -339,7 +339,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" +source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" dependencies = [ "alloy-consensus", "alloy-eips", @@ -356,7 +356,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" +source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -368,7 +368,7 @@ dependencies = [ [[package]] name = "alloy-serde" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" +source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" dependencies = [ "alloy-primitives", "serde", @@ -378,7 +378,7 @@ dependencies = [ [[package]] name = "alloy-signer" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" +source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -393,7 +393,7 @@ dependencies = [ [[package]] name = "alloy-signer-aws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" +source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" dependencies = [ "alloy-consensus", "alloy-network", @@ -410,7 +410,7 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" +source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" dependencies = [ "alloy-consensus", "alloy-network", @@ -428,7 +428,7 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" +source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" dependencies = [ "alloy-consensus", "alloy-network", @@ -444,7 +444,7 @@ dependencies = [ [[package]] name = "alloy-signer-wallet" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" +source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" dependencies = [ "alloy-consensus", "alloy-network", @@ -521,7 +521,7 @@ dependencies = [ [[package]] name = "alloy-transport" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" +source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" dependencies = [ "alloy-json-rpc", "base64 0.22.1", @@ -539,27 +539,28 @@ dependencies = [ [[package]] name = "alloy-transport-http" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" +source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" dependencies = [ "alloy-json-rpc", "alloy-transport", "reqwest", "serde_json", "tower", + "tracing", "url", ] [[package]] name = "alloy-transport-ipc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" +source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" dependencies = [ "alloy-json-rpc", "alloy-pubsub", "alloy-transport", "bytes", "futures", - "interprocess 1.2.1", + "interprocess", "pin-project", "serde", "serde_json", @@ -572,7 +573,7 @@ dependencies = [ [[package]] name = "alloy-transport-ws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=8808d21#8808d21677ed9a05ff04000ac7f4acdd2fde94e3" +source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -798,7 +799,7 @@ dependencies = [ "bytes", "clap", "futures", - "interprocess 2.0.1", + "interprocess", "parking_lot", "pin-project", "serde", @@ -973,37 +974,13 @@ dependencies = [ "term", ] -[[package]] -name = "async-channel" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136d4d23bcc79e27423727b36823d86233aad06dfea531837b038394d11e9928" -dependencies = [ - "concurrent-queue", - "event-listener 5.3.0", - "event-listener-strategy 0.5.2", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-lock" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" -dependencies = [ - "event-listener 4.0.3", - "event-listener-strategy 0.4.0", - "pin-project-lite", -] - [[package]] name = "async-priority-channel" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c21678992e1b21bebfe2bc53ab5f5f68c106eddab31b24e0bb06e9b715a86640" dependencies = [ - "event-listener 2.5.3", + "event-listener", ] [[package]] @@ -1039,12 +1016,6 @@ dependencies = [ "syn 2.0.60", ] -[[package]] -name = "async-task" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" - [[package]] name = "async-trait" version = "0.1.80" @@ -1082,12 +1053,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8ab6b55fe97976e46f91ddbed8d147d966475dc29b2032757ba47e02376fbc3" -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - [[package]] name = "aurora-engine-modexp" version = "1.1.0" @@ -1605,20 +1570,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "blocking" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "495f7104e962b7356f0aeb34247aca1fe7d2e783b346582db7f2904cb5717e88" -dependencies = [ - "async-channel", - "async-lock", - "async-task", - "futures-io", - "futures-lite", - "piper", -] - [[package]] name = "blst" version = "0.3.11" @@ -2184,15 +2135,6 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "console" version = "0.15.8" @@ -2952,48 +2894,6 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" -[[package]] -name = "event-listener" -version = "4.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" -dependencies = [ - "event-listener 4.0.3", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" -dependencies = [ - "event-listener 5.3.0", - "pin-project-lite", -] - [[package]] name = "evm-disassembler" version = "0.5.0" @@ -3912,16 +3812,6 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" -[[package]] -name = "futures-lite" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" -dependencies = [ - "futures-core", - "pin-project-lite", -] - [[package]] name = "futures-macro" version = "0.3.30" @@ -4771,27 +4661,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "interprocess" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81f2533f3be42fffe3b5e63b71aeca416c1c3bc33e4e27be018521e76b1f38fb" -dependencies = [ - "blocking", - "cfg-if", - "futures-core", - "futures-io", - "intmap", - "libc", - "once_cell", - "rustc_version 0.4.0", - "spinning", - "thiserror", - "to_method", - "tokio", - "winapi", -] - [[package]] name = "interprocess" version = "2.0.1" @@ -4806,12 +4675,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "intmap" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae52f28f45ac2bc96edb7714de995cffc174a395fb0abf5bff453587c980d7b9" - [[package]] name = "ipnet" version = "2.9.0" @@ -5690,12 +5553,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "parking" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" - [[package]] name = "parking_lot" version = "0.12.2" @@ -5985,17 +5842,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "piper" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" -dependencies = [ - "atomic-waker", - "fastrand", - "futures-io", -] - [[package]] name = "pkcs8" version = "0.10.2" @@ -6536,7 +6382,7 @@ dependencies = [ [[package]] name = "revm-inspectors" version = "0.1.0" -source = "git+https://github.com/paradigmxyz/evm-inspectors?rev=813d7e7#813d7e73a090d426935e63d9308bdd2c945a58c4" +source = "git+https://github.com/paradigmxyz/evm-inspectors?rev=9a91a10#9a91a107edd197a65a3fd55b3942948036a72b33" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -7434,15 +7280,6 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -[[package]] -name = "spinning" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d4f0e86297cad2658d92a707320d87bf4e6ae1050287f51d19b67ef3f153a7b" -dependencies = [ - "lock_api", -] - [[package]] name = "spki" version = "0.7.3" @@ -7828,12 +7665,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" -[[package]] -name = "to_method" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" - [[package]] name = "tokio" version = "1.37.0" @@ -7942,7 +7773,6 @@ checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", - "futures-io", "futures-sink", "pin-project-lite", "tokio", diff --git a/Cargo.toml b/Cargo.toml index f777385f3..a06b31f9b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -144,7 +144,7 @@ foundry-compilers = { version = "0.4.0", default-features = false } # no default features to avoid c-kzg revm = { version = "8", default-features = false } revm-primitives = { version = "3", default-features = false } -revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "813d7e7", features = [ +revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "9a91a10", features = [ "serde", ] } @@ -152,29 +152,29 @@ revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = ethers-contract-abigen = { version = "2.0.14", default-features = false } ## alloy -alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } -alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } -alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } -alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } -alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } -alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } -alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } -alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } -alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } -alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } -alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } -alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } -alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } -alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } -alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } -alloy-signer-wallet = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } -alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } -alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } -alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } -alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } -alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } -alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } -alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "8808d21", default-features = false } +alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } +alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } +alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } +alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } +alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } +alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } +alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } +alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } +alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } +alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } +alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } +alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } +alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } +alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } +alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } +alloy-signer-wallet = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } +alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } +alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } +alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } +alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } +alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } +alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } +alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } alloy-primitives = { version = "0.7.1", features = ["getrandom"] } alloy-dyn-abi = "0.7.1" alloy-json-abi = "0.7.1" diff --git a/crates/anvil/core/src/eth/block.rs b/crates/anvil/core/src/eth/block.rs index a05c2cae4..bf96a0df5 100644 --- a/crates/anvil/core/src/eth/block.rs +++ b/crates/anvil/core/src/eth/block.rs @@ -72,6 +72,7 @@ impl Block { parent_beacon_block_root: partial_header.parent_beacon_block_root, nonce: partial_header.nonce, base_fee_per_gas: partial_header.base_fee, + requests_root: None, }, transactions, ommers, @@ -159,6 +160,7 @@ mod tests { excess_blob_gas: Default::default(), parent_beacon_block_root: Default::default(), base_fee_per_gas: None, + requests_root: None, }; let encoded = alloy_rlp::encode(&header); @@ -199,6 +201,7 @@ mod tests { parent_beacon_block_root: None, nonce: B64::ZERO, base_fee_per_gas: None, + requests_root: None, }; header.encode(&mut data); @@ -231,6 +234,7 @@ mod tests { excess_blob_gas: None, parent_beacon_block_root: None, base_fee_per_gas: None, + requests_root: None, }; let header = Header::decode(&mut data.as_slice()).unwrap(); assert_eq!(header, expected); @@ -262,6 +266,7 @@ mod tests { blob_gas_used: None, excess_blob_gas: None, parent_beacon_block_root: None, + requests_root: None, }; assert_eq!(header.hash_slow(), expected_hash); } diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index c52beb16e..d29125cdc 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -2,9 +2,8 @@ use crate::eth::transaction::optimism::{DepositTransaction, DepositTransactionRequest}; use alloy_consensus::{ - AnyReceiptEnvelope, BlobTransactionSidecar, Receipt, ReceiptEnvelope, ReceiptWithBloom, Signed, - TxEip1559, TxEip2930, TxEip4844, TxEip4844Variant, TxEip4844WithSidecar, TxEnvelope, TxLegacy, - TxReceipt, + AnyReceiptEnvelope, Receipt, ReceiptEnvelope, ReceiptWithBloom, Signed, TxEip1559, TxEip2930, + TxEip4844, TxEip4844Variant, TxEip4844WithSidecar, TxEnvelope, TxLegacy, TxReceipt, }; use alloy_eips::eip2718::{Decodable2718, Encodable2718}; use alloy_primitives::{Address, Bloom, Bytes, Log, Signature, TxHash, TxKind, B256, U256}; @@ -146,25 +145,8 @@ pub fn transaction_request_to_typed( access_list: access_list.unwrap_or_default(), blob_versioned_hashes: blob_versioned_hashes.unwrap_or_default(), }; - let blob_sidecar = BlobTransactionSidecar { - blobs: sidecar - .blobs - .into_iter() - .map(|b| c_kzg::Blob::from_bytes(b.as_slice()).unwrap()) - .collect(), - commitments: sidecar - .commitments - .into_iter() - .map(|c| c_kzg::Bytes48::from_bytes(c.as_slice()).unwrap()) - .collect(), - proofs: sidecar - .proofs - .into_iter() - .map(|p| c_kzg::Bytes48::from_bytes(p.as_slice()).unwrap()) - .collect(), - }; Some(TypedTransactionRequest::EIP4844(TxEip4844Variant::TxEip4844WithSidecar( - TxEip4844WithSidecar::from_tx_and_sidecar(tx, blob_sidecar), + TxEip4844WithSidecar::from_tx_and_sidecar(tx, sidecar), ))) } _ => None, diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index 934225fbc..8b1ed0655 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -2,7 +2,7 @@ use crate::eth::{backend::db::Db, error::BlockchainError}; use alloy_primitives::{Address, Bytes, StorageValue, B256, U256}; -use alloy_provider::{debug::DebugApi, Provider}; +use alloy_provider::{ext::DebugApi, Provider}; use alloy_rpc_types::{ request::TransactionRequest, AccessListWithGasUsed, Block, BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, EIP1186AccountProofResponse, FeeHistory, @@ -173,7 +173,7 @@ impl ClientFork { block: Option, ) -> Result { let block = block.unwrap_or(BlockNumber::Latest); - let res = self.provider().call(request, block.into()).await?; + let res = self.provider().call(request).block(block.into()).await?; Ok(res) } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 22e2360e8..d49c99d91 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -1595,6 +1595,7 @@ impl Backend { gas_limit, gas_used, timestamp, + requests_root, extra_data, mix_hash, nonce, @@ -1629,6 +1630,7 @@ impl Backend { blob_gas_used, excess_blob_gas, parent_beacon_block_root, + requests_root, }, size: Some(size), transactions: alloy_rpc_types::BlockTransactions::Hashes( diff --git a/crates/anvil/tests/it/anvil_api.rs b/crates/anvil/tests/it/anvil_api.rs index 01247fb28..4f46d7525 100644 --- a/crates/anvil/tests/it/anvil_api.rs +++ b/crates/anvil/tests/it/anvil_api.rs @@ -7,7 +7,7 @@ use crate::{ }; use alloy_network::{EthereumSigner, TransactionBuilder}; use alloy_primitives::{address, fixed_bytes, Address, U256, U64}; -use alloy_provider::{txpool::TxPoolApi, Provider}; +use alloy_provider::{ext::TxPoolApi, Provider}; use alloy_rpc_types::{BlockId, BlockNumberOrTag, TransactionRequest, WithOtherFields}; use anvil::{eth::api::CLIENT_VERSION, spawn, Hardfork, NodeConfig}; use anvil_core::{ diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index 2ef1e7078..3af626962 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -797,7 +797,7 @@ async fn test_fork_call() { let provider = http_provider(rpc::next_http_archive_rpc_endpoint().as_str()); let tx = TransactionRequest::default().to(to).with_input(input.clone()); let tx = WithOtherFields::new(tx); - let res0 = provider.call(&tx, BlockId::Number(block_number.into())).await.unwrap(); + let res0 = provider.call(&tx).block(BlockId::Number(block_number.into())).await.unwrap(); let (api, _) = spawn(fork_config().with_fork_block_number(Some(block_number))).await; diff --git a/crates/anvil/tests/it/traces.rs b/crates/anvil/tests/it/traces.rs index 932e8e362..d12a848e4 100644 --- a/crates/anvil/tests/it/traces.rs +++ b/crates/anvil/tests/it/traces.rs @@ -1,7 +1,7 @@ use crate::{fork::fork_config, utils::http_provider_with_signer}; use alloy_network::{EthereumSigner, TransactionBuilder}; use alloy_primitives::{hex, Address, Bytes, U256}; -use alloy_provider::{debug::DebugApi, Provider}; +use alloy_provider::{ext::DebugApi, Provider}; use alloy_rpc_types::{BlockNumberOrTag, TransactionRequest, WithOtherFields}; use alloy_rpc_types_trace::{ geth::{GethDebugTracingCallOptions, GethTrace}, diff --git a/crates/anvil/tests/it/txpool.rs b/crates/anvil/tests/it/txpool.rs index 40007447e..0882d1977 100644 --- a/crates/anvil/tests/it/txpool.rs +++ b/crates/anvil/tests/it/txpool.rs @@ -2,7 +2,7 @@ use alloy_network::TransactionBuilder; use alloy_primitives::U256; -use alloy_provider::{txpool::TxPoolApi, Provider}; +use alloy_provider::{ext::TxPoolApi, Provider}; use alloy_rpc_types::{TransactionRequest, WithOtherFields}; use anvil::{spawn, NodeConfig}; diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index da4103b74..67048fda7 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -120,7 +120,7 @@ where func: Option<&Function>, block: Option, ) -> Result { - let res = self.provider.call(req, block.unwrap_or_default()).await?; + let res = self.provider.call(req).block(block.unwrap_or_default()).await?; let mut decoded = vec![]; diff --git a/crates/common/src/transactions.rs b/crates/common/src/transactions.rs index 1f7d6228e..8ec95179f 100644 --- a/crates/common/src/transactions.rs +++ b/crates/common/src/transactions.rs @@ -48,10 +48,8 @@ impl TransactionReceiptWithRevertReason { if let Some(block_hash) = self.receipt.block_hash { match provider - .call( - &WithOtherFields::new(transaction.inner.into()), - BlockId::Hash(block_hash.into()), - ) + .call(&WithOtherFields::new(transaction.inner.into())) + .block(BlockId::Hash(block_hash.into())) .await { Err(e) => return Ok(extract_revert_reason(e.to_string())), diff --git a/crates/debugger/src/tui/draw.rs b/crates/debugger/src/tui/draw.rs index b80a8a77a..ffe348f47 100644 --- a/crates/debugger/src/tui/draw.rs +++ b/crates/debugger/src/tui/draw.rs @@ -198,6 +198,7 @@ impl DebuggerContext<'_> { CallKind::StaticCall => "Contract staticcall", CallKind::CallCode => "Contract callcode", CallKind::DelegateCall => "Contract delegatecall", + CallKind::AuthCall => "Contract authcall", }; let block = Block::default().title(title).borders(Borders::ALL); let paragraph = Paragraph::new(text_output).block(block).wrap(Wrap { trim: false }); diff --git a/crates/evm/traces/src/lib.rs b/crates/evm/traces/src/lib.rs index 16cadde10..46ba27891 100644 --- a/crates/evm/traces/src/lib.rs +++ b/crates/evm/traces/src/lib.rs @@ -192,6 +192,7 @@ pub async fn render_trace( CallKind::CallCode => " [callcode]", CallKind::DelegateCall => " [delegatecall]", CallKind::Create | CallKind::Create2 => unreachable!(), + CallKind::AuthCall => " [authcall]", }; let color = trace_color(trace); From bc5e78d4a3a777c23f70fa9f6e8c2f0cb9cac7c8 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Mon, 6 May 2024 18:51:58 +0300 Subject: [PATCH 262/430] chore(invariant) - reuse code to collect push bytes and storage values (#7865) * chore(invariant) - reuse code to collect push bytes and storage values * Remove build_initial_state, move logic in EvmFuzzState::new --- crates/evm/evm/src/executors/fuzz/mod.rs | 6 +- crates/evm/evm/src/executors/invariant/mod.rs | 8 +- crates/evm/fuzz/src/strategies/mod.rs | 2 +- crates/evm/fuzz/src/strategies/param.rs | 4 +- crates/evm/fuzz/src/strategies/state.rs | 247 +++++++++--------- 5 files changed, 130 insertions(+), 137 deletions(-) diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index 8e3a85bdd..d81ddbaca 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -10,7 +10,7 @@ use foundry_evm_core::{ }; use foundry_evm_coverage::HitMaps; use foundry_evm_fuzz::{ - strategies::{build_initial_state, fuzz_calldata, fuzz_calldata_from_state, EvmFuzzState}, + strategies::{fuzz_calldata, fuzz_calldata_from_state, EvmFuzzState}, BaseCounterExample, CounterExample, FuzzCase, FuzzError, FuzzFixtures, FuzzTestResult, }; use foundry_evm_traces::CallTraceArena; @@ -243,9 +243,9 @@ impl FuzzedExecutor { /// Stores fuzz state for use with [fuzz_calldata_from_state] pub fn build_fuzz_state(&self) -> EvmFuzzState { if let Some(fork_db) = self.executor.backend.active_fork_db() { - build_initial_state(fork_db, self.config.dictionary) + EvmFuzzState::new(fork_db, self.config.dictionary) } else { - build_initial_state(self.executor.backend.mem_db(), self.config.dictionary) + EvmFuzzState::new(self.executor.backend.mem_db(), self.config.dictionary) } } } diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 23972e0c5..f2bf2ec0d 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -16,10 +16,7 @@ use foundry_evm_fuzz::{ ArtifactFilters, BasicTxDetails, FuzzRunIdentifiedContracts, InvariantContract, RandomCallGenerator, SenderFilters, TargetedContracts, }, - strategies::{ - build_initial_state, collect_created_contracts, invariant_strat, override_call_strat, - EvmFuzzState, - }, + strategies::{collect_created_contracts, invariant_strat, override_call_strat, EvmFuzzState}, FuzzCase, FuzzFixtures, FuzzedCases, }; use foundry_evm_traces::CallTraceArena; @@ -347,8 +344,7 @@ impl<'a> InvariantExecutor<'a> { self.select_contracts_and_senders(invariant_contract.address)?; // Stores fuzz state for use with [fuzz_calldata_from_state]. - let fuzz_state: EvmFuzzState = - build_initial_state(self.executor.backend.mem_db(), self.config.dictionary); + let fuzz_state = EvmFuzzState::new(self.executor.backend.mem_db(), self.config.dictionary); // Creates the invariant strategy. let strat = invariant_strat( diff --git a/crates/evm/fuzz/src/strategies/mod.rs b/crates/evm/fuzz/src/strategies/mod.rs index 74cefca2a..4e1120b58 100644 --- a/crates/evm/fuzz/src/strategies/mod.rs +++ b/crates/evm/fuzz/src/strategies/mod.rs @@ -11,7 +11,7 @@ mod calldata; pub use calldata::{fuzz_calldata, fuzz_calldata_from_state}; mod state; -pub use state::{build_initial_state, collect_created_contracts, EvmFuzzState}; +pub use state::{collect_created_contracts, EvmFuzzState}; mod invariants; pub use invariants::{fuzz_contract_with_calldata, invariant_strat, override_call_strat}; diff --git a/crates/evm/fuzz/src/strategies/param.rs b/crates/evm/fuzz/src/strategies/param.rs index 12904cb00..137a04232 100644 --- a/crates/evm/fuzz/src/strategies/param.rs +++ b/crates/evm/fuzz/src/strategies/param.rs @@ -212,7 +212,7 @@ pub fn fuzz_param_from_state( #[cfg(test)] mod tests { use crate::{ - strategies::{build_initial_state, fuzz_calldata, fuzz_calldata_from_state}, + strategies::{fuzz_calldata, fuzz_calldata_from_state, EvmFuzzState}, FuzzFixtures, }; use foundry_common::abi::get_func; @@ -224,7 +224,7 @@ mod tests { let f = "testArray(uint64[2] calldata values)"; let func = get_func(f).unwrap(); let db = CacheDB::new(EmptyDB::default()); - let state = build_initial_state(&db, FuzzDictionaryConfig::default()); + let state = EvmFuzzState::new(&db, FuzzDictionaryConfig::default()); let strat = proptest::prop_oneof![ 60 => fuzz_calldata(func.clone(), &FuzzFixtures::default()), 40 => fuzz_calldata_from_state(func, &state), diff --git a/crates/evm/fuzz/src/strategies/state.rs b/crates/evm/fuzz/src/strategies/state.rs index 2ee3f7fca..0a05028cc 100644 --- a/crates/evm/fuzz/src/strategies/state.rs +++ b/crates/evm/fuzz/src/strategies/state.rs @@ -6,9 +6,9 @@ use foundry_evm_core::utils::StateChangeset; use indexmap::IndexSet; use parking_lot::{lock_api::RwLockReadGuard, RawRwLock, RwLock}; use revm::{ - db::{CacheDB, DatabaseRef}, + db::{CacheDB, DatabaseRef, DbAccount}, interpreter::opcode::{self, spec_opcode_gas}, - primitives::SpecId, + primitives::{AccountInfo, SpecId}, }; use std::{fmt, sync::Arc}; @@ -21,14 +21,21 @@ pub struct EvmFuzzState { } impl EvmFuzzState { - pub fn new(dictionary: FuzzDictionary) -> Self { + pub fn new(db: &CacheDB, config: FuzzDictionaryConfig) -> EvmFuzzState { + // Sort accounts to ensure deterministic dictionary generation from the same setUp state. + let mut accs = db.accounts.iter().collect::>(); + accs.sort_by_key(|(address, _)| *address); + + // Create fuzz dictionary and insert values from db state. + let mut dictionary = FuzzDictionary::new(config); + dictionary.insert_db_values(accs); Self { inner: Arc::new(RwLock::new(dictionary)) } } pub fn collect_values(&self, values: impl IntoIterator) { let mut dict = self.inner.write(); for value in values { - dict.insert_value(value); + dict.insert_value(value, true); } } @@ -36,54 +43,8 @@ impl EvmFuzzState { /// the given [FuzzDictionaryConfig]. pub fn collect_state_from_call(&self, logs: &[Log], state_changeset: &StateChangeset) { let mut dict = self.inner.write(); - - // Insert log topics and data. - for log in logs { - for topic in log.topics() { - dict.insert_value(topic.0); - } - let chunks = log.data.data.chunks_exact(32); - let rem = chunks.remainder(); - for chunk in chunks { - dict.insert_value(chunk.try_into().unwrap()); - } - if !rem.is_empty() { - dict.insert_value(B256::right_padding_from(rem).0); - } - } - - for (address, account) in state_changeset { - // Insert basic account information - dict.insert_value(address.into_word().into()); - - if dict.config.include_push_bytes { - // Insert push bytes - if let Some(code) = &account.info.code { - dict.insert_address(*address); - for push_byte in collect_push_bytes(code.bytes()) { - dict.insert_value(push_byte); - } - } - } - - if dict.config.include_storage { - // Insert storage - for (slot, value) in &account.storage { - let value = value.present_value; - dict.insert_value(B256::from(*slot).0); - dict.insert_value(B256::from(value).0); - // also add the value below and above the storage value to the dictionary. - if value != U256::ZERO { - let below_value = value - U256::from(1); - dict.insert_value(B256::from(below_value).0); - } - if value != U256::MAX { - let above_value = value + U256::from(1); - dict.insert_value(B256::from(above_value).0); - } - } - } - } + dict.insert_logs_values(logs); + dict.insert_state_values(state_changeset); } /// Removes all newly added entries from the dictionary. @@ -101,6 +62,7 @@ impl EvmFuzzState { // We're using `IndexSet` to have a stable element order when restoring persisted state, as well as // for performance when iterating over the sets. +#[derive(Default)] pub struct FuzzDictionary { /// Collected state values. state_values: IndexSet<[u8; 32]>, @@ -124,36 +86,126 @@ impl fmt::Debug for FuzzDictionary { } impl FuzzDictionary { - pub fn new( - initial_values: IndexSet<[u8; 32]>, - initial_addresses: IndexSet
, - config: FuzzDictionaryConfig, - ) -> Self { - Self { - state_values: initial_values, - addresses: initial_addresses, - config, - new_values: IndexSet::new(), - new_addreses: IndexSet::new(), + pub fn new(config: FuzzDictionaryConfig) -> Self { + Self { config, ..Default::default() } + } + + /// Insert values from initial db state into fuzz dictionary. + /// These values are persisted across invariant runs. + fn insert_db_values(&mut self, db_state: Vec<(&Address, &DbAccount)>) { + for (address, account) in db_state { + // Insert basic account information + self.insert_value(address.into_word().into(), false); + // Insert push bytes + self.insert_push_bytes_values(address, &account.info, false); + // Insert storage values. + if self.config.include_storage { + for (slot, value) in &account.storage { + self.insert_storage_value(slot, value, false); + } + } + } + + // need at least some state data if db is empty otherwise we can't select random data for + // state fuzzing + if self.values().is_empty() { + // prefill with a random addresses + self.insert_value(Address::random().into_word().into(), false); } } - pub fn insert_value(&mut self, value: [u8; 32]) { - if self.state_values.len() < self.config.max_fuzz_dictionary_values && - self.state_values.insert(value) - { - self.new_values.insert(value); + /// Insert values from call state changeset into fuzz dictionary. + /// These values are removed at the end of current run. + fn insert_state_values(&mut self, state_changeset: &StateChangeset) { + for (address, account) in state_changeset { + // Insert basic account information. + self.insert_value(address.into_word().into(), true); + // Insert push bytes. + self.insert_push_bytes_values(address, &account.info, true); + // Insert storage values. + if self.config.include_storage { + for (slot, value) in &account.storage { + self.insert_storage_value(slot, &value.present_value, true); + } + } + } + } + + /// Insert values from call log topics and data into fuzz dictionary. + /// These values are removed at the end of current run. + fn insert_logs_values(&mut self, logs: &[Log]) { + for log in logs { + for topic in log.topics() { + self.insert_value(topic.0, true); + } + let chunks = log.data.data.chunks_exact(32); + let rem = chunks.remainder(); + for chunk in chunks { + self.insert_value(chunk.try_into().unwrap(), true); + } + if !rem.is_empty() { + self.insert_value(B256::right_padding_from(rem).0, true); + } + } + } + + /// Insert values from push bytes into fuzz dictionary. + /// If values are newly collected then they are removed at the end of current run. + fn insert_push_bytes_values( + &mut self, + address: &Address, + account_info: &AccountInfo, + collected: bool, + ) { + if self.config.include_push_bytes { + // Insert push bytes + if let Some(code) = account_info.code.clone() { + self.insert_address(*address, collected); + for push_byte in collect_push_bytes(code.bytes()) { + self.insert_value(push_byte, collected); + } + } } } - pub fn insert_address(&mut self, address: Address) { + /// Insert values from single storage slot and storage value into fuzz dictionary. + /// If storage values are newly collected then they are removed at the end of current run. + fn insert_storage_value(&mut self, storage_slot: &U256, storage_value: &U256, collected: bool) { + self.insert_value(B256::from(*storage_slot).0, collected); + self.insert_value(B256::from(*storage_value).0, collected); + // also add the value below and above the storage value to the dictionary. + if *storage_value != U256::ZERO { + let below_value = storage_value - U256::from(1); + self.insert_value(B256::from(below_value).0, collected); + } + if *storage_value != U256::MAX { + let above_value = storage_value + U256::from(1); + self.insert_value(B256::from(above_value).0, collected); + } + } + + /// Insert address into fuzz dictionary. + /// If address is newly collected then it is removed at the end of current run. + fn insert_address(&mut self, address: Address, collected: bool) { if self.addresses.len() < self.config.max_fuzz_dictionary_addresses && - self.addresses.insert(address) + self.addresses.insert(address) && + collected { self.new_addreses.insert(address); } } + /// Insert raw value into fuzz dictionary. + /// If value is newly collected then it is removed at the end of current run. + fn insert_value(&mut self, value: [u8; 32], collected: bool) { + if self.state_values.len() < self.config.max_fuzz_dictionary_values && + self.state_values.insert(value) && + collected + { + self.new_values.insert(value); + } + } + #[inline] pub fn values(&self) -> &IndexSet<[u8; 32]> { &self.state_values @@ -177,61 +229,6 @@ impl FuzzDictionary { } } -/// Builds the initial [EvmFuzzState] from a database. -pub fn build_initial_state( - db: &CacheDB, - config: FuzzDictionaryConfig, -) -> EvmFuzzState { - let mut values = IndexSet::new(); - let mut addresses = IndexSet::new(); - - // Sort accounts to ensure deterministic dictionary generation from the same setUp state. - let mut accs = db.accounts.iter().collect::>(); - accs.sort_by_key(|(address, _)| *address); - - for (address, account) in accs { - let address: Address = *address; - // Insert basic account information - values.insert(address.into_word().into()); - - // Insert push bytes - if config.include_push_bytes { - if let Some(code) = &account.info.code { - addresses.insert(address); - for push_byte in collect_push_bytes(code.bytes()) { - values.insert(push_byte); - } - } - } - - if config.include_storage { - // Insert storage - for (slot, value) in &account.storage { - values.insert(B256::from(*slot).0); - values.insert(B256::from(*value).0); - // also add the value below and above the storage value to the dictionary. - if *value != U256::ZERO { - let below_value = value - U256::from(1); - values.insert(B256::from(below_value).0); - } - if *value != U256::MAX { - let above_value = value + U256::from(1); - values.insert(B256::from(above_value).0); - } - } - } - } - - // need at least some state data if db is empty otherwise we can't select random data for state - // fuzzing - if values.is_empty() { - // prefill with a random addresses - values.insert(Address::random().into_word().into()); - } - - EvmFuzzState::new(FuzzDictionary::new(values, addresses, config)) -} - /// The maximum number of bytes we will look at in bytecodes to find push bytes (24 KiB). /// /// This is to limit the performance impact of fuzz tests that might deploy arbitrarily sized From 6ded8579b28493704d6efd4eacf4962088456aaa Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 6 May 2024 18:48:01 +0200 Subject: [PATCH 263/430] test: fix some flaky tests (#7873) --- crates/anvil/tests/it/api.rs | 2 +- crates/anvil/tests/it/ganache.rs | 9 ++++++++- crates/anvil/tests/it/transaction.rs | 11 +++++++++-- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/crates/anvil/tests/it/api.rs b/crates/anvil/tests/it/api.rs index ec8335562..9619e1388 100644 --- a/crates/anvil/tests/it/api.rs +++ b/crates/anvil/tests/it/api.rs @@ -198,7 +198,7 @@ async fn can_call_on_pending_block() { api.evm_set_block_gas_limit(U256::from(30_000_000 + i)).unwrap(); api.anvil_mine(Some(U256::from(1)), None).await.unwrap(); - tokio::time::sleep(Duration::from_secs(1)).await; + tokio::time::sleep(Duration::from_millis(100)).await; } // Ensure that the right header values are set when calling a past block diff --git a/crates/anvil/tests/it/ganache.rs b/crates/anvil/tests/it/ganache.rs index ca8589e21..f86aa7cf8 100644 --- a/crates/anvil/tests/it/ganache.rs +++ b/crates/anvil/tests/it/ganache.rs @@ -78,7 +78,14 @@ async fn test_ganache_emit_logs() { let val = _0; assert_eq!(val, first_msg); - emit_logs_contract.setValue(next_msg.clone()).send().await.unwrap(); + emit_logs_contract + .setValue(next_msg.clone()) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); let EmitLogs::getValueReturn { _0 } = emit_logs_contract.getValue().call().await.unwrap(); let val = _0; diff --git a/crates/anvil/tests/it/transaction.rs b/crates/anvil/tests/it/transaction.rs index 7e088e0b5..d0dbc539c 100644 --- a/crates/anvil/tests/it/transaction.rs +++ b/crates/anvil/tests/it/transaction.rs @@ -392,7 +392,14 @@ async fn can_call_greeter_historic() { let block_number = provider.get_block_number().await.unwrap(); - let _ = greeter_contract.setGreeting("Another Message".to_string()).send().await.unwrap(); + let _receipt = greeter_contract + .setGreeting("Another Message".to_string()) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); let greeting = greeter_contract.greet().call().await.unwrap(); assert_eq!("Another Message", greeting._0); @@ -511,7 +518,7 @@ async fn call_past_state() { let gas_price = api.gas_price().unwrap().to::(); let set_tx = contract.setValue("hi".to_string()).gas_price(gas_price + 1); - let _set_tx = set_tx.send().await.unwrap().get_receipt().await.unwrap(); + let _receipt = set_tx.send().await.unwrap().get_receipt().await.unwrap(); // assert new value let value = contract.getValue().call().await.unwrap(); From e7f9b75a5146b36a5c6df01757132957c42a1050 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 6 May 2024 23:17:50 +0200 Subject: [PATCH 264/430] chore: stop using RuntimeOrHandle (#7860) --- Cargo.lock | 1 - Cargo.toml | 1 + crates/anvil/Cargo.toml | 4 +-- crates/cast/Cargo.toml | 2 +- crates/cheatcodes/src/evm/fork.rs | 29 +++++-------------- crates/cheatcodes/src/utils.rs | 7 ++--- crates/chisel/Cargo.toml | 2 +- crates/chisel/src/dispatcher.rs | 9 +++--- crates/chisel/src/executor.rs | 2 +- crates/cli/Cargo.toml | 2 +- crates/cli/src/utils/mod.rs | 1 - crates/common/Cargo.toml | 4 +-- crates/common/src/lib.rs | 13 +++++++++ crates/evm/core/Cargo.toml | 3 +- crates/evm/core/src/opts.rs | 9 +++--- crates/evm/core/src/utils.rs | 4 +-- crates/evm/evm/src/executors/tracing.rs | 2 +- crates/evm/traces/Cargo.toml | 2 +- crates/evm/traces/src/identifier/etherscan.rs | 3 +- crates/forge/Cargo.toml | 2 +- crates/forge/bin/cmd/test/mod.rs | 2 +- crates/forge/bin/main.rs | 2 +- crates/forge/src/multi_runner.rs | 7 +++-- crates/forge/src/runner.rs | 3 ++ crates/script/src/execute.rs | 6 ++-- crates/verify/Cargo.toml | 2 +- crates/wallets/Cargo.toml | 2 +- 27 files changed, 63 insertions(+), 63 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 199fb03d5..c6060c8b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3558,7 +3558,6 @@ dependencies = [ "eyre", "foundry-cheatcodes-spec", "foundry-common", - "foundry-compilers", "foundry-config", "foundry-macros", "foundry-test-utils", diff --git a/Cargo.toml b/Cargo.toml index a06b31f9b..bd96efd2e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -212,6 +212,7 @@ tikv-jemallocator = "0.5.4" num-format = "0.4.4" yansi = { version = "1.0", features = ["detect-tty", "detect-env"] } tempfile = "3.10" +tokio = "1" axum = "0.7" hyper = "1.0" diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index 63a7f89f9..7ab515768 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -60,7 +60,7 @@ tracing.workspace = true tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] } # async -tokio = { version = "1", features = ["time"] } +tokio = { workspace = true, features = ["time"] } parking_lot = "0.12" futures = "0.3" async-trait = "0.1" @@ -99,7 +99,7 @@ alloy-json-rpc.workspace = true alloy-pubsub.workspace = true foundry-test-utils.workspace = true similar-asserts.workspace = true -tokio = { version = "1", features = ["full"] } +tokio = { workspace = true, features = ["full"] } [features] default = ["cli"] diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index cf7325512..5d4405ae5 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -71,7 +71,7 @@ regex = { version = "1", default-features = false } rpassword = "7" semver = "1" tempfile.workspace = true -tokio = { version = "1", features = ["macros", "signal"] } +tokio = { workspace = true, features = ["macros", "signal"] } tracing.workspace = true yansi.workspace = true evmole = "0.3.1" diff --git a/crates/cheatcodes/src/evm/fork.rs b/crates/cheatcodes/src/evm/fork.rs index f7db4c0e9..63f0bbad1 100644 --- a/crates/cheatcodes/src/evm/fork.rs +++ b/crates/cheatcodes/src/evm/fork.rs @@ -3,9 +3,7 @@ use alloy_primitives::{B256, U256}; use alloy_provider::Provider; use alloy_rpc_types::Filter; use alloy_sol_types::SolValue; -use eyre::WrapErr; use foundry_common::provider::ProviderBuilder; -use foundry_compilers::utils::RuntimeOrHandle; use foundry_evm_core::fork::CreateFork; impl Cheatcode for activeForkCall { @@ -227,11 +225,10 @@ impl Cheatcode for rpcCall { let url = ccx.ecx.db.active_fork_url().ok_or_else(|| fmt_err!("no active fork URL found"))?; let provider = ProviderBuilder::new(&url).build()?; - let method: &'static str = Box::new(method.clone()).leak(); let params_json: serde_json::Value = serde_json::from_str(params)?; - let result = RuntimeOrHandle::new() - .block_on(provider.raw_request(method.into(), params_json)) - .map_err(|err| fmt_err!("{method:?}: {err}"))?; + let result = + foundry_common::block_on(provider.raw_request(method.clone().into(), params_json)) + .map_err(|err| fmt_err!("{method:?}: {err}"))?; let result_as_tokens = crate::json::json_value_to_token(&result) .map_err(|err| fmt_err!("failed to parse result: {err}"))?; @@ -256,24 +253,12 @@ impl Cheatcode for eth_getLogsCall { ccx.ecx.db.active_fork_url().ok_or_else(|| fmt_err!("no active fork URL found"))?; let provider = ProviderBuilder::new(&url).build()?; let mut filter = Filter::new().address(*target).from_block(from_block).to_block(to_block); - for (i, topic) in topics.iter().enumerate() { - // todo: needed because rust wants to convert FixedBytes<32> to U256 to convert it back - // to FixedBytes<32> and then to Topic for some reason removing the - // From impl in alloy does not fix the situation, and it is not possible to impl - // From> either because of a conflicting impl - match i { - 0 => filter = filter.event_signature(*topic), - 1 => filter = filter.topic1(*topic), - 2 => filter = filter.topic2(*topic), - 3 => filter = filter.topic3(*topic), - _ => unreachable!(), - }; + for (i, &topic) in topics.iter().enumerate() { + filter.topics[i] = topic.into(); } - // todo: handle the errors somehow - let logs = RuntimeOrHandle::new() - .block_on(provider.get_logs(&filter)) - .wrap_err("failed to get logs")?; + let logs = foundry_common::block_on(provider.get_logs(&filter)) + .map_err(|e| fmt_err!("failed to get logs: {e}"))?; let eth_logs = logs .into_iter() diff --git a/crates/cheatcodes/src/utils.rs b/crates/cheatcodes/src/utils.rs index 7544abc0a..71cfe6ae6 100644 --- a/crates/cheatcodes/src/utils.rs +++ b/crates/cheatcodes/src/utils.rs @@ -11,7 +11,7 @@ use alloy_signer_wallet::{ LocalWallet, MnemonicBuilder, }; use alloy_sol_types::SolValue; -use foundry_evm_core::{constants::DEFAULT_CREATE2_DEPLOYER, utils::RuntimeOrHandle}; +use foundry_evm_core::constants::DEFAULT_CREATE2_DEPLOYER; use k256::{ ecdsa::SigningKey, elliptic_curve::{sec1::ToEncodedPoint, Curve}, @@ -202,9 +202,8 @@ pub(super) fn sign_with_wallet( .get(&signer) .ok_or_else(|| fmt_err!("signer with address {signer} is not available"))?; - let sig = RuntimeOrHandle::new() - .block_on(wallet.sign_hash(digest)) - .map_err(|err| fmt_err!("{err}"))?; + let sig = + foundry_common::block_on(wallet.sign_hash(digest)).map_err(|err| fmt_err!("{err}"))?; let recovered = sig.recover_address_from_prehash(digest).map_err(|err| fmt_err!("{err}"))?; assert_eq!(recovered, signer); diff --git a/crates/chisel/Cargo.toml b/crates/chisel/Cargo.toml index 6ae62b970..fc8a2a782 100644 --- a/crates/chisel/Cargo.toml +++ b/crates/chisel/Cargo.toml @@ -46,7 +46,7 @@ serde.workspace = true solang-parser.workspace = true strum = { workspace = true, features = ["derive"] } time = { version = "0.3", features = ["formatting"] } -tokio = { version = "1", features = ["full"] } +tokio = { workspace = true, features = ["full"] } yansi.workspace = true tracing.workspace = true diff --git a/crates/chisel/src/dispatcher.rs b/crates/chisel/src/dispatcher.rs index ebf4aeb37..e32b1ac01 100644 --- a/crates/chisel/src/dispatcher.rs +++ b/crates/chisel/src/dispatcher.rs @@ -689,7 +689,7 @@ impl ChiselDispatcher { let failed = !res.success; if new_session_source.config.traces || failed { if let Ok(decoder) = - Self::decode_traces(&new_session_source.config, &mut res) + Self::decode_traces(&new_session_source.config, &mut res).await { if let Err(e) = Self::show_traces(&decoder, &mut res).await { return DispatchResult::CommandFailed(e.to_string()) @@ -834,7 +834,8 @@ impl ChiselDispatcher { // If traces are enabled or there was an error in execution, show the execution // traces. if new_source.config.traces || failed { - if let Ok(decoder) = Self::decode_traces(&new_source.config, &mut res) { + if let Ok(decoder) = Self::decode_traces(&new_source.config, &mut res).await + { if let Err(e) = Self::show_traces(&decoder, &mut res).await { return DispatchResult::CommandFailed(e.to_string()) }; @@ -888,7 +889,7 @@ impl ChiselDispatcher { /// ### Returns /// /// Optionally, a [CallTraceDecoder] - pub fn decode_traces( + pub async fn decode_traces( session_config: &SessionSourceConfig, result: &mut ChiselResult, // known_contracts: &ContractsByArtifact, @@ -903,7 +904,7 @@ impl ChiselDispatcher { let mut identifier = TraceIdentifiers::new().with_etherscan( &session_config.foundry_config, - session_config.evm_opts.get_remote_chain_id(), + session_config.evm_opts.get_remote_chain_id().await, )?; if !identifier.is_empty() { for (_, trace) in &mut result.traces { diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index 3ae465e4d..fc1e7c84a 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -189,7 +189,7 @@ impl SessionSource { let Some((stack, memory, _)) = &res.state else { // Show traces and logs, if there are any, and return an error - if let Ok(decoder) = ChiselDispatcher::decode_traces(&source.config, &mut res) { + if let Ok(decoder) = ChiselDispatcher::decode_traces(&source.config, &mut res).await { ChiselDispatcher::show_traces(&decoder, &mut res).await?; } let decoded_logs = decode_console_logs(&res.logs); diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index dd247ac48..8c4f353dd 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -36,7 +36,7 @@ regex = { version = "1", default-features = false } serde.workspace = true strsim = "0.10" strum = { workspace = true, features = ["derive"] } -tokio = { version = "1", features = ["macros"] } +tokio = { workspace = true, features = ["macros"] } tracing-error = "0.2" tracing-subscriber = { workspace = true, features = ["registry", "env-filter", "fmt"] } tracing.workspace = true diff --git a/crates/cli/src/utils/mod.rs b/crates/cli/src/utils/mod.rs index 8f751728d..34333e593 100644 --- a/crates/cli/src/utils/mod.rs +++ b/crates/cli/src/utils/mod.rs @@ -158,7 +158,6 @@ pub fn now() -> Duration { } /// Runs the `future` in a new [`tokio::runtime::Runtime`] -#[allow(unused)] pub fn block_on(future: F) -> F::Output { let rt = tokio::runtime::Runtime::new().expect("could not start tokio rt"); rt.block_on(future) diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 6a4ae4f38..bdf0e3457 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -48,7 +48,7 @@ serde_json.workspace = true serde.workspace = true tempfile.workspace = true thiserror = "1" -tokio = "1" +tokio.workspace = true tracing.workspace = true url = "2" walkdir = "2" @@ -59,4 +59,4 @@ num-format.workspace = true [dev-dependencies] foundry-macros.workspace = true similar-asserts.workspace = true -tokio = { version = "1", features = ["rt-multi-thread", "macros"] } +tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index 6febaa378..2ffc066d6 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -31,3 +31,16 @@ pub use constants::*; pub use contracts::*; pub use traits::*; pub use transactions::*; + +/// Block on a future using the current tokio runtime on the current thread. +pub fn block_on(future: F) -> F::Output { + block_on_handle(&tokio::runtime::Handle::current(), future) +} + +/// Block on a future using the current tokio runtime on the current thread with the given handle. +pub fn block_on_handle( + handle: &tokio::runtime::Handle, + future: F, +) -> F::Output { + tokio::task::block_in_place(|| handle.block_on(future)) +} diff --git a/crates/evm/core/Cargo.toml b/crates/evm/core/Cargo.toml index c3285d92c..589e13b25 100644 --- a/crates/evm/core/Cargo.toml +++ b/crates/evm/core/Cargo.toml @@ -13,7 +13,6 @@ repository.workspace = true [dependencies] foundry-cheatcodes-spec.workspace = true foundry-common.workspace = true -foundry-compilers.workspace = true foundry-config.workspace = true foundry-macros.workspace = true @@ -52,7 +51,7 @@ rustc-hash.workspace = true serde = "1" serde_json = "1" thiserror = "1" -tokio = { version = "1", features = ["time", "macros"] } +tokio = { workspace = true, features = ["time", "macros"] } tracing = "0.1" url = "2" diff --git a/crates/evm/core/src/opts.rs b/crates/evm/core/src/opts.rs index 682e2444f..2fe3d0390 100644 --- a/crates/evm/core/src/opts.rs +++ b/crates/evm/core/src/opts.rs @@ -5,7 +5,6 @@ use alloy_provider::Provider; use alloy_rpc_types::Block; use eyre::WrapErr; use foundry_common::{provider::ProviderBuilder, ALCHEMY_FREE_TIER_CUPS}; -use foundry_compilers::utils::RuntimeOrHandle; use foundry_config::{Chain, Config}; use revm::primitives::{BlockEnv, CfgEnv, TxEnv}; use serde::{Deserialize, Deserializer, Serialize}; @@ -166,11 +165,11 @@ impl EvmOpts { /// - mainnet if `fork_url` contains "mainnet" /// - the chain if `fork_url` is set and the endpoints returned its chain id successfully /// - mainnet otherwise - pub fn get_chain_id(&self) -> u64 { + pub async fn get_chain_id(&self) -> u64 { if let Some(id) = self.env.chain_id { return id; } - self.get_remote_chain_id().unwrap_or(Chain::mainnet()).id() + self.get_remote_chain_id().await.unwrap_or(Chain::mainnet()).id() } /// Returns the available compute units per second, which will be @@ -188,7 +187,7 @@ impl EvmOpts { } /// Returns the chain ID from the RPC, if any. - pub fn get_remote_chain_id(&self) -> Option { + pub async fn get_remote_chain_id(&self) -> Option { if let Some(ref url) = self.fork_url { if url.contains("mainnet") { trace!(?url, "auto detected mainnet chain"); @@ -201,7 +200,7 @@ impl EvmOpts { .ok() .unwrap_or_else(|| panic!("Failed to establish provider to {url}")); - if let Ok(id) = RuntimeOrHandle::new().block_on(provider.get_chain_id()) { + if let Ok(id) = provider.get_chain_id().await { return Some(Chain::from(id)); } } diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index 20d8aa647..bf9664bd8 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -4,9 +4,7 @@ use alloy_json_abi::{Function, JsonAbi}; use alloy_primitives::{Address, FixedBytes, U256}; use alloy_rpc_types::{Block, Transaction}; use eyre::ContextCompat; -pub use foundry_compilers::utils::RuntimeOrHandle; use foundry_config::NamedChain; -pub use revm::primitives::State as StateChangeset; use revm::{ db::WrapDatabaseRef, handler::register::EvmHandler, @@ -19,6 +17,8 @@ use revm::{ }; use std::{cell::RefCell, rc::Rc, sync::Arc}; +pub use revm::primitives::State as StateChangeset; + /// Depending on the configured chain id and block number this should apply any specific changes /// /// - checks for prevrandao mixhash after merge diff --git a/crates/evm/evm/src/executors/tracing.rs b/crates/evm/evm/src/executors/tracing.rs index 08979bc16..a175aecb6 100644 --- a/crates/evm/evm/src/executors/tracing.rs +++ b/crates/evm/evm/src/executors/tracing.rs @@ -45,7 +45,7 @@ impl TracingExecutor { let fork = evm_opts.get_fork(config, env.clone()); - Ok((env, fork, evm_opts.get_remote_chain_id())) + Ok((env, fork, evm_opts.get_remote_chain_id().await)) } } diff --git a/crates/evm/traces/Cargo.toml b/crates/evm/traces/Cargo.toml index a4bcb0a0b..496a4e998 100644 --- a/crates/evm/traces/Cargo.toml +++ b/crates/evm/traces/Cargo.toml @@ -29,7 +29,7 @@ hex.workspace = true itertools.workspace = true once_cell = "1" serde = "1" -tokio = { version = "1", features = ["time", "macros"] } +tokio = { workspace = true, features = ["time", "macros"] } tracing = "0.1" yansi.workspace = true diff --git a/crates/evm/traces/src/identifier/etherscan.rs b/crates/evm/traces/src/identifier/etherscan.rs index 11996be3a..977b69a42 100644 --- a/crates/evm/traces/src/identifier/etherscan.rs +++ b/crates/evm/traces/src/identifier/etherscan.rs @@ -6,7 +6,6 @@ use foundry_block_explorers::{ }; use foundry_common::compile::{self, ContractSources}; use foundry_config::{Chain, Config}; -use foundry_evm_core::utils::RuntimeOrHandle; use futures::{ future::{join_all, Future}, stream::{FuturesUnordered, Stream, StreamExt}, @@ -129,7 +128,7 @@ impl TraceIdentifier for EtherscanIdentifier { } } - let fetched_identities = RuntimeOrHandle::new().block_on( + let fetched_identities = foundry_common::block_on( fetcher .map(|(address, metadata)| { let label = metadata.contract_name.clone(); diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 6bb111eac..f58968bf0 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -79,7 +79,7 @@ similar = { version = "2", features = ["inline"] } solang-parser.workspace = true strum = { workspace = true, features = ["derive"] } thiserror = "1" -tokio = { version = "1", features = ["time"] } +tokio = { workspace = true, features = ["time"] } toml = { version = "0.8", features = ["preserve_order"] } toml_edit = "0.22.4" watchexec = "2.3.2" diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index b20017c85..cef234966 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -361,7 +361,7 @@ impl TestArgs { return Ok(TestOutcome::new(results, self.allow_failure)); } - let remote_chain_id = runner.evm_opts.get_remote_chain_id(); + let remote_chain_id = runner.evm_opts.get_remote_chain_id().await; // Run tests. let (tx, rx) = channel::<(String, SuiteResult)>(); diff --git a/crates/forge/bin/main.rs b/crates/forge/bin/main.rs index f598c1212..8db9036e1 100644 --- a/crates/forge/bin/main.rs +++ b/crates/forge/bin/main.rs @@ -49,7 +49,7 @@ fn main() -> Result<()> { if cmd.is_watch() { utils::block_on(watch::watch_build(cmd)) } else { - cmd.run().map(|_| ()) + cmd.run().map(drop) } } ForgeSubcommand::Debug(cmd) => utils::block_on(cmd.run()), diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 9fed56982..1e4f77388 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -147,6 +147,7 @@ impl MultiContractRunner { /// /// Each Executor gets its own instance of the `Backend`. pub fn test(&mut self, filter: &dyn TestFilter, tx: mpsc::Sender<(String, SuiteResult)>) { + let handle = tokio::runtime::Handle::current(); trace!("running all tests"); // The DB backend that serves all the data. @@ -163,7 +164,8 @@ impl MultiContractRunner { ); contracts.par_iter().for_each_with(tx, |tx, &(id, contract)| { - let result = self.run_tests(id, contract, db.clone(), filter); + let _guard = handle.enter(); + let result = self.run_tests(id, contract, db.clone(), filter, &handle); let _ = tx.send((id.identifier(), result)); }) } @@ -174,6 +176,7 @@ impl MultiContractRunner { contract: &TestContract, db: Backend, filter: &dyn TestFilter, + handle: &tokio::runtime::Handle, ) -> SuiteResult { let identifier = artifact_id.identifier(); let mut span_name = identifier.as_str(); @@ -220,7 +223,7 @@ impl MultiContractRunner { &self.revert_decoder, self.debug, ); - let r = runner.run_tests(filter, &self.test_options, known_contracts); + let r = runner.run_tests(filter, &self.test_options, known_contracts, handle); debug!(duration=?r.duration, "executed all tests in contract"); diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index b29309bb0..48fa10b6d 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -254,6 +254,7 @@ impl<'a> ContractRunner<'a> { filter: &dyn TestFilter, test_options: &TestOptions, known_contracts: Arc, + handle: &tokio::runtime::Handle, ) -> SuiteResult { info!("starting tests"); let start = Instant::now(); @@ -346,6 +347,8 @@ impl<'a> ContractRunner<'a> { let test_results = functions .par_iter() .map(|&func| { + let _guard = handle.enter(); + let sig = func.signature(); let setup = setup.clone(); diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index ea307de5b..9e988c09b 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -298,7 +298,7 @@ impl ExecutedState { pub async fn prepare_simulation(self) -> Result { let returns = self.get_returns()?; - let decoder = self.build_trace_decoder(&self.build_data.known_contracts)?; + let decoder = self.build_trace_decoder(&self.build_data.known_contracts).await?; let txs = self.execution_result.transactions.clone().unwrap_or_default(); let rpc_data = RpcData::from_transactions(&txs); @@ -328,7 +328,7 @@ impl ExecutedState { } /// Builds [CallTraceDecoder] from the execution result and known contracts. - fn build_trace_decoder( + async fn build_trace_decoder( &self, known_contracts: &ContractsByArtifact, ) -> Result { @@ -344,7 +344,7 @@ impl ExecutedState { let mut identifier = TraceIdentifiers::new().with_local(known_contracts).with_etherscan( &self.script_config.config, - self.script_config.evm_opts.get_remote_chain_id(), + self.script_config.evm_opts.get_remote_chain_id().await, )?; // Decoding traces using etherscan is costly as we run into rate limits, diff --git a/crates/verify/Cargo.toml b/crates/verify/Cargo.toml index 21eb65942..62979edbc 100644 --- a/crates/verify/Cargo.toml +++ b/crates/verify/Cargo.toml @@ -38,6 +38,6 @@ once_cell = "1" yansi.workspace = true [dev-dependencies] -tokio = { version = "1", features = ["macros"] } +tokio = { workspace = true, features = ["macros"] } foundry-test-utils.workspace = true tempfile.workspace = true diff --git a/crates/wallets/Cargo.toml b/crates/wallets/Cargo.toml index 545207b40..a0495c51d 100644 --- a/crates/wallets/Cargo.toml +++ b/crates/wallets/Cargo.toml @@ -39,7 +39,7 @@ thiserror = "1" tracing.workspace = true [dev-dependencies] -tokio = { version = "1", features = ["macros"] } +tokio = { workspace = true, features = ["macros"] } [features] default = ["rustls"] From bfc6549f0d50fe31cd2fae875c2c7233db98bde9 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 7 May 2024 00:49:12 +0300 Subject: [PATCH 265/430] chore(invariant): remove persist_state config, commit by default (#7819) * chore(invariant): remove persist_state config, commit to backend by default * Test snekmate fix * Point to latest snekmate commit --- crates/config/README.md | 1 - crates/config/src/invariant.rs | 6 ------ crates/evm/evm/src/executors/invariant/mod.rs | 18 +++++------------- crates/forge/tests/cli/ext_integration.rs | 2 +- crates/forge/tests/it/invariant.rs | 13 ------------- crates/forge/tests/it/test_helpers.rs | 1 - 6 files changed, 6 insertions(+), 35 deletions(-) diff --git a/crates/config/README.md b/crates/config/README.md index ffa4cdc75..105aaffc6 100644 --- a/crates/config/README.md +++ b/crates/config/README.md @@ -196,7 +196,6 @@ dictionary_weight = 80 include_storage = true include_push_bytes = true shrink_sequence = true -preserve_state = false [fmt] line_length = 100 diff --git a/crates/config/src/invariant.rs b/crates/config/src/invariant.rs index c7462d539..0442d467b 100644 --- a/crates/config/src/invariant.rs +++ b/crates/config/src/invariant.rs @@ -29,10 +29,6 @@ pub struct InvariantConfig { pub shrink_sequence: bool, /// The maximum number of attempts to shrink the sequence pub shrink_run_limit: usize, - /// If set to true then VM state is committed and available for next call - /// Useful for handlers that use cheatcodes as roll or warp - /// Use it with caution, introduces performance penalty. - pub preserve_state: bool, /// The maximum number of rejects via `vm.assume` which can be encountered during a single /// invariant run. pub max_assume_rejects: u32, @@ -50,7 +46,6 @@ impl Default for InvariantConfig { dictionary: FuzzDictionaryConfig { dictionary_weight: 80, ..Default::default() }, shrink_sequence: true, shrink_run_limit: 2usize.pow(18_u32), - preserve_state: false, max_assume_rejects: 65536, gas_report_samples: 256, } @@ -81,7 +76,6 @@ impl InlineConfigParser for InvariantConfig { "fail-on-revert" => conf_clone.fail_on_revert = parse_config_bool(key, value)?, "call-override" => conf_clone.call_override = parse_config_bool(key, value)?, "shrink-sequence" => conf_clone.shrink_sequence = parse_config_bool(key, value)?, - "preserve-state" => conf_clone.preserve_state = parse_config_bool(key, value)?, _ => Err(InlineConfigParserError::InvalidConfigProperty(key.to_string()))?, } } diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index f2bf2ec0d..2818018cb 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -26,7 +26,7 @@ use proptest::{ test_runner::{TestCaseError, TestRunner}, }; use result::{assert_invariants, can_continue}; -use revm::{primitives::HashMap, DatabaseCommit}; +use revm::primitives::HashMap; use shrink::shrink_sequence; use std::{cell::RefCell, collections::BTreeMap, sync::Arc}; @@ -211,16 +211,10 @@ impl<'a> InvariantExecutor<'a> { while current_run < self.config.depth { let (sender, (address, calldata)) = inputs.last().expect("no input generated"); - // Executes the call from the randomly generated sequence. - let call_result = if self.config.preserve_state { - executor - .call_raw_committing(*sender, *address, calldata.clone(), U256::ZERO) - .expect("could not make raw evm call") - } else { - executor - .call_raw(*sender, *address, calldata.clone(), U256::ZERO) - .expect("could not make raw evm call") - }; + // Execute call from the randomly generated sequence and commit state changes. + let call_result = executor + .call_raw_committing(*sender, *address, calldata.clone(), U256::ZERO) + .expect("could not make raw evm call"); if call_result.result.as_ref() == MAGIC_ASSUME { inputs.pop(); @@ -252,8 +246,6 @@ impl<'a> InvariantExecutor<'a> { warn!(target: "forge::test", "{error}"); } } - // Commit changes to the database. - executor.backend.commit(state_changeset.clone()); fuzz_runs.push(FuzzCase { calldata: calldata.clone(), diff --git a/crates/forge/tests/cli/ext_integration.rs b/crates/forge/tests/cli/ext_integration.rs index 3483708f2..889000ad5 100644 --- a/crates/forge/tests/cli/ext_integration.rs +++ b/crates/forge/tests/cli/ext_integration.rs @@ -81,7 +81,7 @@ fn lil_web3() { #[test] #[cfg_attr(windows, ignore = "Windows cannot find installed programs")] fn snekmate() { - ExtTester::new("pcaversaccio", "snekmate", "ed49a0454393673cdf9a4250dd7051c28e6ac35f") + ExtTester::new("pcaversaccio", "snekmate", "1aa50098720d49e04b257a4aa5138b3d737a0667") .install_command(&["pnpm", "install", "--prefer-offline"]) // Try npm if pnpm fails / is not installed. .install_command(&["npm", "install", "--prefer-offline"]) diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index e906c8356..90bc4c9c5 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -418,21 +418,8 @@ async fn test_shrink_fail_on_revert() { async fn test_invariant_preserve_state() { let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantPreserveState.t.sol"); let mut runner = TEST_DATA_DEFAULT.runner(); - // Should not fail with default options. runner.test_options.invariant.fail_on_revert = true; let results = runner.test_collect(&filter); - assert_multiple( - &results, - BTreeMap::from([( - "default/fuzz/invariant/common/InvariantPreserveState.t.sol:InvariantPreserveState", - vec![("invariant_preserve_state()", true, None, None, None)], - )]), - ); - - // same test should revert when preserve state enabled - runner.test_options.invariant.fail_on_revert = true; - runner.test_options.invariant.preserve_state = true; - let results = runner.test_collect(&filter); assert_multiple( &results, BTreeMap::from([( diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index 27f574990..cf3d5757d 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -106,7 +106,6 @@ impl ForgeTestProfile { }, shrink_sequence: true, shrink_run_limit: 2usize.pow(18u32), - preserve_state: false, max_assume_rejects: 65536, gas_report_samples: 256, }) From 6da2ff4cf221feab427260f87aa8ca48f8c5460e Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 7 May 2024 07:45:39 +0300 Subject: [PATCH 266/430] chore(invariant): remove shrink_sequence config (#7875) --- crates/config/README.md | 1 - crates/config/src/invariant.rs | 5 ----- crates/config/src/lib.rs | 1 - crates/evm/evm/src/executors/invariant/error.rs | 3 --- crates/evm/evm/src/executors/invariant/mod.rs | 4 ---- crates/evm/evm/src/executors/invariant/replay.rs | 7 +------ crates/evm/evm/src/executors/invariant/shrink.rs | 2 +- crates/forge/tests/it/test_helpers.rs | 1 - testdata/foundry.toml | 1 - 9 files changed, 2 insertions(+), 23 deletions(-) diff --git a/crates/config/README.md b/crates/config/README.md index 105aaffc6..04fd53bb6 100644 --- a/crates/config/README.md +++ b/crates/config/README.md @@ -195,7 +195,6 @@ call_override = false dictionary_weight = 80 include_storage = true include_push_bytes = true -shrink_sequence = true [fmt] line_length = 100 diff --git a/crates/config/src/invariant.rs b/crates/config/src/invariant.rs index 0442d467b..fa9e5489f 100644 --- a/crates/config/src/invariant.rs +++ b/crates/config/src/invariant.rs @@ -24,9 +24,6 @@ pub struct InvariantConfig { /// The fuzz dictionary configuration #[serde(flatten)] pub dictionary: FuzzDictionaryConfig, - /// Attempt to shrink the failure case to its smallest sequence of calls - /// TODO: remove this setting as it is now redundant with shrink_run_limit = 0 - pub shrink_sequence: bool, /// The maximum number of attempts to shrink the sequence pub shrink_run_limit: usize, /// The maximum number of rejects via `vm.assume` which can be encountered during a single @@ -44,7 +41,6 @@ impl Default for InvariantConfig { fail_on_revert: false, call_override: false, dictionary: FuzzDictionaryConfig { dictionary_weight: 80, ..Default::default() }, - shrink_sequence: true, shrink_run_limit: 2usize.pow(18_u32), max_assume_rejects: 65536, gas_report_samples: 256, @@ -75,7 +71,6 @@ impl InlineConfigParser for InvariantConfig { "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)?, - "shrink-sequence" => conf_clone.shrink_sequence = parse_config_bool(key, value)?, _ => Err(InlineConfigParserError::InvalidConfigProperty(key.to_string()))?, } } diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 8b65bfa78..96cd23a63 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -3714,7 +3714,6 @@ mod tests { depth = 15 fail_on_revert = false call_override = false - shrink_sequence = true "#, )?; diff --git a/crates/evm/evm/src/executors/invariant/error.rs b/crates/evm/evm/src/executors/invariant/error.rs index e797174a5..6e8dc929b 100644 --- a/crates/evm/evm/src/executors/invariant/error.rs +++ b/crates/evm/evm/src/executors/invariant/error.rs @@ -63,8 +63,6 @@ pub struct FailedInvariantCaseData { pub func: Bytes, /// Inner fuzzing Sequence coming from overriding calls. pub inner_sequence: Vec>, - /// Shrink the failed test case to the smallest sequence. - pub shrink_sequence: bool, /// Shrink run limit pub shrink_run_limit: usize, /// Fail on revert, used to check sequence when shrinking. @@ -103,7 +101,6 @@ impl FailedInvariantCaseData { addr: invariant_contract.address, func: func.selector().to_vec().into(), inner_sequence: inner_sequence.to_vec(), - shrink_sequence: invariant_config.shrink_sequence, shrink_run_limit: invariant_config.shrink_run_limit, fail_on_revert: invariant_config.fail_on_revert, } diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 2818018cb..a15f98f8b 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -144,10 +144,6 @@ impl<'a> InvariantExecutor<'a> { return Err(eyre!("Invariant test function should have no inputs")) } - if !self.config.shrink_sequence { - error!(target: "forge::test", "shrink_sequence config is deprecated and will be removed, use shrink_run_limit = 0") - } - let (fuzz_state, targeted_contracts, strat) = self.prepare_fuzzing(&invariant_contract, fuzz_fixtures)?; diff --git a/crates/evm/evm/src/executors/invariant/replay.rs b/crates/evm/evm/src/executors/invariant/replay.rs index 941df781a..e55581174 100644 --- a/crates/evm/evm/src/executors/invariant/replay.rs +++ b/crates/evm/evm/src/executors/invariant/replay.rs @@ -100,12 +100,7 @@ pub fn replay_error( TestError::Abort(_) => Ok(None), TestError::Fail(_, ref calls) => { // Shrink sequence of failed calls. - let calls = if failed_case.shrink_sequence { - shrink_sequence(failed_case, calls, &executor)? - } else { - trace!(target: "forge::test", "Shrinking disabled."); - calls.clone() - }; + let calls = shrink_sequence(failed_case, calls, &executor)?; set_up_inner_replay(&mut executor, &failed_case.inner_sequence); // Replay calls to get the counterexample and to collect logs, traces and coverage. diff --git a/crates/evm/evm/src/executors/invariant/shrink.rs b/crates/evm/evm/src/executors/invariant/shrink.rs index bfa592136..7b78ae028 100644 --- a/crates/evm/evm/src/executors/invariant/shrink.rs +++ b/crates/evm/evm/src/executors/invariant/shrink.rs @@ -84,7 +84,7 @@ pub(crate) fn shrink_sequence( calls: &[BasicTxDetails], executor: &Executor, ) -> eyre::Result> { - trace!(target: "forge::test", "Shrinking."); + trace!(target: "forge::test", "Shrinking sequence of {} calls.", calls.len()); // Special case test: the invariant is *unsatisfiable* - it took 0 calls to // break the invariant -- consider emitting a warning. diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index cf3d5757d..be87b9ce9 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -104,7 +104,6 @@ impl ForgeTestProfile { max_fuzz_dictionary_addresses: 10_000, max_fuzz_dictionary_values: 10_000, }, - shrink_sequence: true, shrink_run_limit: 2usize.pow(18u32), max_assume_rejects: 65536, gas_report_samples: 256, diff --git a/testdata/foundry.toml b/testdata/foundry.toml index 22df7b3e5..1fd8c610f 100644 --- a/testdata/foundry.toml +++ b/testdata/foundry.toml @@ -16,7 +16,6 @@ ffi = false force = false invariant_fail_on_revert = false invariant_call_override = false -invariant_shrink_sequence = true invariant_preserve_state = false gas_limit = 9223372036854775807 gas_price = 0 From 0ed87260285ed9520a3634f1cb8dea12531717c0 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 7 May 2024 09:55:31 +0200 Subject: [PATCH 267/430] test(anvil): fix remaining TODOs/ignores, remove compiler dependency (#7877) --- Cargo.lock | 1 - crates/anvil/Cargo.toml | 1 - crates/anvil/core/src/eth/transaction/mod.rs | 12 +- crates/anvil/src/lib.rs | 5 +- crates/anvil/tests/it/abi.rs | 25 +- crates/anvil/tests/it/eip4844.rs | 14 +- crates/anvil/tests/it/ganache.rs | 225 ------- crates/anvil/tests/it/geth.rs | 103 ---- crates/anvil/tests/it/main.rs | 2 - crates/anvil/tests/it/optimism.rs | 14 +- crates/anvil/tests/it/otterscan.rs | 588 ++++++------------- crates/anvil/tests/it/pubsub.rs | 2 - crates/anvil/tests/it/revert.rs | 309 +++------- crates/anvil/tests/it/traces.rs | 1 - crates/anvil/tests/it/transaction.rs | 22 +- crates/anvil/tests/it/utils.rs | 1 + crates/cheatcodes/src/fs.rs | 6 +- crates/common/src/provider/mod.rs | 24 +- testdata/default/cheats/UnixTime.t.sol | 5 +- 19 files changed, 322 insertions(+), 1038 deletions(-) delete mode 100644 crates/anvil/tests/it/ganache.rs delete mode 100644 crates/anvil/tests/it/geth.rs diff --git a/Cargo.lock b/Cargo.lock index c6060c8b0..55b297812 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -729,7 +729,6 @@ dependencies = [ "flate2", "foundry-cli", "foundry-common", - "foundry-compilers", "foundry-config", "foundry-evm", "foundry-test-utils", diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index 7ab515768..9ca41a520 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -91,7 +91,6 @@ tikv-jemallocator = { workspace = true, optional = true } [dev-dependencies] alloy-json-abi.workspace = true -foundry-compilers = { workspace = true, features = ["project-util", "full"] } alloy-rpc-client = { workspace = true, features = ["pubsub"] } alloy-transport-ipc = { workspace = true, features = ["mock"] } alloy-transport-ws.workspace = true diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index d29125cdc..7e2fc71da 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -9,8 +9,8 @@ use alloy_eips::eip2718::{Decodable2718, Encodable2718}; use alloy_primitives::{Address, Bloom, Bytes, Log, Signature, TxHash, TxKind, B256, U256}; use alloy_rlp::{length_of_length, Decodable, Encodable, Header}; use alloy_rpc_types::{ - request::TransactionRequest, AccessList, AnyTransactionReceipt, Signature as RpcSignature, - Transaction as RpcTransaction, TransactionReceipt, WithOtherFields, + other::OtherFields, request::TransactionRequest, AccessList, AnyTransactionReceipt, + Signature as RpcSignature, Transaction as RpcTransaction, TransactionReceipt, WithOtherFields, }; use bytes::BufMut; use foundry_evm::traces::CallTraceNode; @@ -58,7 +58,7 @@ pub fn transaction_request_to_typed( } = tx; // Special case: OP-stack deposit tx - if transaction_type == Some(126) { + if transaction_type == Some(0x7E) || has_optimism_fields(&other) { return Some(TypedTransactionRequest::Deposit(DepositTransactionRequest { from: from.unwrap_or_default(), source_hash: other.get_deserialized::("sourceHash")?.ok()?, @@ -153,6 +153,12 @@ pub fn transaction_request_to_typed( } } +fn has_optimism_fields(other: &OtherFields) -> bool { + other.contains_key("sourceHash") && + other.contains_key("mint") && + other.contains_key("isSystemTx") +} + #[derive(Clone, Debug, PartialEq, Eq)] pub enum TypedTransactionRequest { Legacy(TxLegacy), diff --git a/crates/anvil/src/lib.rs b/crates/anvil/src/lib.rs index 2cb39a893..8fbb130f2 100644 --- a/crates/anvil/src/lib.rs +++ b/crates/anvil/src/lib.rs @@ -310,10 +310,7 @@ impl NodeHandle { /// Constructs a [`RetryProvider`] for this handle's HTTP endpoint. pub fn http_provider(&self) -> RetryProvider { - ProviderBuilder::new(&self.http_endpoint()) - .aggressive() - .build() - .expect("failed to build HTTP provider") + ProviderBuilder::new(&self.http_endpoint()).build().expect("failed to build HTTP provider") } /// Constructs a [`RetryProvider`] for this handle's WS endpoint. diff --git a/crates/anvil/tests/it/abi.rs b/crates/anvil/tests/it/abi.rs index fda13dc46..3356b02f1 100644 --- a/crates/anvil/tests/it/abi.rs +++ b/crates/anvil/tests/it/abi.rs @@ -49,22 +49,25 @@ sol!( } ); -// -pub(crate) const VENDING_MACHINE_CONTRACT: &str = r#"// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.13; +// https://docs.soliditylang.org/en/latest/control-structures.html#revert +sol!( +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.4; +#[sol(rpc, bytecode = "6080806040523460155761011e908161001a8239f35b5f80fdfe60808060405260043610156011575f80fd5b5f3560e01c9081633ccfd60b146094575063d96a094a14602f575f80fd5b6020366003190112609057671bc16d674ec80000340460043511604e57005b60405162461bcd60e51b815260206004820152601a6024820152792737ba1032b737bab3b41022ba3432b910383937bb34b232b21760311b6044820152606490fd5b5f80fd5b346090575f3660031901126090575f546001600160a01b0316330360da575f8080804781811560d2575b3390f11560c757005b6040513d5f823e3d90fd5b506108fc60be565b6282b42960e81b8152600490fdfea2646970667358221220c143fcbf0da5cee61ae3fcc385d9f7c4d6a7fb2ea42530d70d6049478db0b8a964736f6c63430008190033")] contract VendingMachine { address owner; error Unauthorized(); - function buyRevert(uint amount) public payable { + #[derive(Debug)] + function buy(uint amount) public payable { if (amount > msg.value / 2 ether) revert("Not enough Ether provided."); - } - function buyRequire(uint amount) public payable { + // Alternative way to do it: require( amount <= msg.value / 2 ether, "Not enough Ether provided." ); + // Perform the purchase. } function withdraw() public { if (msg.sender != owner) @@ -72,13 +75,5 @@ contract VendingMachine { payable(msg.sender).transfer(address(this).balance); } -}"#; - -sol!( - #[sol(rpc)] - contract VendingMachine { - function buyRevert(uint amount) external payable; - function buyRequire(uint amount) external payable; - function withdraw() external; - } +} ); diff --git a/crates/anvil/tests/it/eip4844.rs b/crates/anvil/tests/it/eip4844.rs index 180bcf178..c9ab2943c 100644 --- a/crates/anvil/tests/it/eip4844.rs +++ b/crates/anvil/tests/it/eip4844.rs @@ -9,7 +9,7 @@ use anvil::{spawn, Hardfork, NodeConfig}; #[tokio::test(flavor = "multi_thread")] async fn can_send_eip4844_transaction() { - let node_config = NodeConfig::default().with_hardfork(Some(Hardfork::Cancun)); + let node_config = NodeConfig::test().with_hardfork(Some(Hardfork::Cancun)); let (_api, handle) = spawn(node_config).await; let wallets = handle.dev_wallets().collect::>(); @@ -45,7 +45,7 @@ async fn can_send_eip4844_transaction() { #[tokio::test(flavor = "multi_thread")] async fn can_send_multiple_blobs_in_one_tx() { - let node_config = NodeConfig::default().with_hardfork(Some(Hardfork::Cancun)); + let node_config = NodeConfig::test().with_hardfork(Some(Hardfork::Cancun)); let (_api, handle) = spawn(node_config).await; let wallets = handle.dev_wallets().collect::>(); @@ -83,7 +83,7 @@ async fn can_send_multiple_blobs_in_one_tx() { #[tokio::test(flavor = "multi_thread")] async fn cannot_exceed_six_blobs() { - let node_config = NodeConfig::default().with_hardfork(Some(Hardfork::Cancun)); + let node_config = NodeConfig::test().with_hardfork(Some(Hardfork::Cancun)); let (_api, handle) = spawn(node_config).await; let wallets = handle.dev_wallets().collect::>(); @@ -120,7 +120,7 @@ async fn cannot_exceed_six_blobs() { #[tokio::test(flavor = "multi_thread")] async fn can_mine_blobs_when_exceeds_max_blobs() { - let node_config = NodeConfig::default().with_hardfork(Some(Hardfork::Cancun)); + let node_config = NodeConfig::test().with_hardfork(Some(Hardfork::Cancun)); let (api, handle) = spawn(node_config).await; api.anvil_set_auto_mine(false).await.unwrap(); @@ -187,13 +187,13 @@ async fn can_mine_blobs_when_exceeds_max_blobs() { second_block.unwrap().unwrap().header.blob_gas_used, Some(DATA_GAS_PER_BLOB as u128 * num_blobs_second as u128) ); - assert_eq!(first_receipt.block_number.unwrap() + 1, second_receipt.block_number.unwrap()); // Mined in two - // different blocks + // Mined in two different blocks + assert_eq!(first_receipt.block_number.unwrap() + 1, second_receipt.block_number.unwrap()); } #[tokio::test(flavor = "multi_thread")] async fn can_check_blob_fields_on_genesis() { - let node_config = NodeConfig::default().with_hardfork(Some(Hardfork::Cancun)); + let node_config = NodeConfig::test().with_hardfork(Some(Hardfork::Cancun)); let (_api, handle) = spawn(node_config).await; let provider = http_provider(&handle.http_endpoint()); diff --git a/crates/anvil/tests/it/ganache.rs b/crates/anvil/tests/it/ganache.rs deleted file mode 100644 index f86aa7cf8..000000000 --- a/crates/anvil/tests/it/ganache.rs +++ /dev/null @@ -1,225 +0,0 @@ -//! tests against local ganache for local debug purposes -#![allow(unused)] -use crate::{ - abi::Greeter, - init_tracing, - utils::{http_provider, http_provider_with_signer, ws_provider_with_signer}, -}; -use alloy_contract::ContractInstance; -use alloy_network::EthereumSigner; -use alloy_primitives::{address, Address, TxKind}; -use alloy_provider::Provider; -use alloy_rpc_types::{BlockId, TransactionRequest, WithOtherFields}; -use alloy_signer_wallet::{LocalWallet, MnemonicBuilder}; -use alloy_sol_types::{sol, Revert}; -use foundry_compilers::{project_util::TempProject, Artifact}; -use std::{str::FromStr, sync::Arc}; - -// the mnemonic used to start the local ganache instance -const MNEMONIC: &str = - "amazing discover palace once resource choice flush horn wink shift planet relief"; - -fn ganache_wallet() -> LocalWallet { - LocalWallet::from_str("552dd2534c4984f892191997d6b1dd9e6a23c7e07b908a6cebfad1d3f2af4c4c") - .unwrap() -} - -fn ganache_wallet2() -> LocalWallet { - LocalWallet::from_str("305b526d493844b63466be6d48a424ab83f5216011eef860acc6db4c1821adc9") - .unwrap() -} - -fn wallet(key_str: &str) -> LocalWallet { - LocalWallet::from_str(key_str).unwrap() -} - -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn test_ganache_block_number() { - let provider = http_provider("http://127.0.0.1:8545"); - - let balance = provider.get_balance(Address::random(), BlockId::latest()).await; -} - -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn test_ganache_deploy() { - let signer: EthereumSigner = ganache_wallet().into(); - let provider = http_provider_with_signer("http://127.0.0.1:8545", signer); - - let greeter_contract_builder = Greeter::deploy_builder(&provider, "Hello World!".to_string()); - let greeter_contract_address = greeter_contract_builder.deploy().await.unwrap(); - let greeter_contract = Greeter::new(greeter_contract_address, &provider); - - let Greeter::greetReturn { _0 } = greeter_contract.greet().call().await.unwrap(); - let greeting = _0; - assert_eq!("Hello World!", greeting); -} - -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn test_ganache_emit_logs() { - sol!( - #[sol(rpc)] - EmitLogs, - "test-data/emit_logs.json" - ); - - let signer: EthereumSigner = ganache_wallet().into(); - let provider = ws_provider_with_signer("ws://127.0.0.1:8545", signer); - - let first_msg = "First Message".to_string(); - let next_msg = "Next Message".to_string(); - let emit_logs_contract_builder = EmitLogs::deploy_builder(&provider, first_msg.clone()); - let emit_logs_contract_address = emit_logs_contract_builder.deploy().await.unwrap(); - let emit_logs_contract = EmitLogs::new(emit_logs_contract_address, &provider); - - let EmitLogs::getValueReturn { _0 } = emit_logs_contract.getValue().call().await.unwrap(); - let val = _0; - assert_eq!(val, first_msg); - - emit_logs_contract - .setValue(next_msg.clone()) - .send() - .await - .unwrap() - .get_receipt() - .await - .unwrap(); - - let EmitLogs::getValueReturn { _0 } = emit_logs_contract.getValue().call().await.unwrap(); - let val = _0; - assert_eq!(val, next_msg); -} - -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn test_ganache_deploy_reverting() { - let prj = TempProject::dapptools().unwrap(); - prj.add_source( - "Contract", - r#" -pragma solidity 0.8.13; -contract Contract { - constructor() { - require(false, ""); - } -} -"#, - ) - .unwrap(); - - sol!( - #[sol(rpc)] - contract Contract {} - ); - - let mut compiled = prj.compile().unwrap(); - assert!(!compiled.has_compiler_errors()); - let contract = compiled.remove_first("Contract").unwrap(); - let bytecode = contract.into_bytecode_bytes().unwrap(); - - let wallet = ganache_wallet(); - let signer: EthereumSigner = wallet.clone().into(); - let sender = wallet.address(); - - let provider = http_provider_with_signer("http://127.0.0.1:8545", signer); - - // should catch the revert during estimation which results in an err - let err = provider - .send_transaction(WithOtherFields::new(TransactionRequest { - from: Some(sender), - to: Some(TxKind::Create), - input: bytecode.into(), - ..Default::default() - })) - .await - .unwrap_err(); - assert!(err.to_string().contains("execution reverted")); -} - -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn test_ganache_tx_reverting() { - let prj = TempProject::dapptools().unwrap(); - prj.add_source( - "Contract", - r#" -pragma solidity 0.8.13; -contract Contract { - address owner; - constructor() public { - owner = msg.sender; - } - modifier onlyOwner() { - require(msg.sender == owner, "!authorized"); - _; - } - function getSecret() public onlyOwner view returns(uint256 secret) { - return 123; - } -} -"#, - ) - .unwrap(); - - sol!( - #[sol(rpc)] - contract Contract { - function getSecret() public view returns (uint256); - } - ); - - let mut compiled = prj.compile().unwrap(); - assert!(!compiled.has_compiler_errors()); - let contract = compiled.remove_first("Contract").unwrap(); - let bytecode = contract.into_bytecode_bytes().unwrap(); - - let wallet = ganache_wallet(); - let signer: EthereumSigner = wallet.clone().into(); - let sender = wallet.address(); - - let provider = http_provider_with_signer("http://127.0.0.1:8545", signer); - - // deploy successfully - provider - .send_transaction(WithOtherFields::new(TransactionRequest { - from: Some(sender), - to: Some(TxKind::Create), - input: bytecode.into(), - ..Default::default() - })) - .await - .unwrap() - .get_receipt() - .await - .unwrap(); - let contract_address = sender.create(0); - let contract = Contract::new(contract_address, &provider); - - // should catch the revert during the call which results in an err - contract.getSecret().send().await.unwrap_err(); - - // /* Ganache rpc errors look like: - // < { - // < "id": 1627277502538, - // < "jsonrpc": "2.0", - // < "error": { - // < "message": "VM Exception while processing transaction: revert !authorized", - // < "code": -32000, - // < "data": { - // < "0x90264de254689f1d4e7f8670cd97f60d9bc803874fdecb34d249bd1cc3ca823a": { - // < "error": "revert", - // < "program_counter": 223, - // < "return": - // "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b21617574686f72697a6564000000000000000000000000000000000000000000" - // , < "reason": "!authorized" - // < }, - // < "stack": "c: VM Exception while processing transaction: revert !authorized\n at - // Function.c.fromResults - // (/usr/local/lib/node_modules/ganache-cli/build/ganache-core.node.cli.js:4:192416)\n at - // /usr/local/lib/node_modules/ganache-cli/build/ganache-core.node.cli.js:42:50402", < - // "name": "c" < } - // < } - // */ -} diff --git a/crates/anvil/tests/it/geth.rs b/crates/anvil/tests/it/geth.rs deleted file mode 100644 index 7879f4911..000000000 --- a/crates/anvil/tests/it/geth.rs +++ /dev/null @@ -1,103 +0,0 @@ -//! tests against local geth for local debug purposes - -use crate::{ - abi::{VendingMachine, VENDING_MACHINE_CONTRACT}, - utils::{http_provider, http_provider_with_signer}, -}; -use alloy_network::{EthereumSigner, TransactionBuilder}; -use alloy_primitives::{Address, TxKind, U256}; -use alloy_provider::Provider; -use alloy_rpc_types::{TransactionRequest, WithOtherFields}; -use anvil::{spawn, NodeConfig}; -use foundry_compilers::{project_util::TempProject, Artifact}; -use futures::StreamExt; -use tokio::time::timeout; - -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn test_geth_pending_transaction() { - let provider = http_provider("http://127.0.0.1:8545"); - - let account = provider.get_accounts().await.unwrap().remove(0); - - let tx = TransactionRequest::default() - .with_from(account) - .with_to(Address::random()) - .with_value(U256::from(1337u64)) - .with_nonce(2u64); - let tx = WithOtherFields::new(tx); - - let mut watch_tx_stream = provider.watch_pending_transactions().await.unwrap().into_stream(); - - provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); - - let pending = timeout(std::time::Duration::from_secs(3), watch_tx_stream.next()).await; - pending.unwrap_err(); -} - -// check how geth returns reverts -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn test_geth_revert_transaction() { - let prj = TempProject::dapptools().unwrap(); - prj.add_source("VendingMachine", VENDING_MACHINE_CONTRACT).unwrap(); - - let mut compiled = prj.compile().unwrap(); - assert!(!compiled.has_compiler_errors()); - let contract = compiled.remove_first("VendingMachine").unwrap(); - let bytecode = contract.into_bytecode_bytes().unwrap(); - - let (_api, handle) = spawn(NodeConfig::test()).await; - let wallet = handle.dev_wallets().next().unwrap(); - let signer: EthereumSigner = wallet.clone().into(); - let sender = wallet.address(); - - let provider = http_provider_with_signer("http://127.0.0.1:8545", signer); - - // deploy successfully - provider - .send_transaction(WithOtherFields::new(TransactionRequest { - from: Some(sender), - to: Some(TxKind::Create), - input: bytecode.into(), - ..Default::default() - })) - .await - .unwrap() - .get_receipt() - .await - .unwrap(); - let contract_address = sender.create(0); - let contract = VendingMachine::new(contract_address, &provider); - - let res = contract - .buyRevert(U256::from(100)) - .value(U256::from(1)) - .from(sender) - .send() - .await - .unwrap_err(); - let msg = res.to_string(); - assert!(msg.contains("execution reverted: revert: Not enough Ether provided.")); - assert!(msg.contains("code: 3")); -} - -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn test_geth_low_gas_limit() { - let provider = http_provider("http://127.0.0.1:8545"); - - let account = provider.get_accounts().await.unwrap().remove(0); - - let gas_limit = 21_000u128 - 1; - let tx = TransactionRequest::default() - .from(account) - .to(Address::random()) - .value(U256::from(1337u64)) - .gas_limit(gas_limit); - let tx = WithOtherFields::new(tx); - - let resp = provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await; - let err = resp.unwrap_err().to_string(); - assert!(err.contains("intrinsic gas too low")); -} diff --git a/crates/anvil/tests/it/main.rs b/crates/anvil/tests/it/main.rs index 94b1bc492..5337e5bbd 100644 --- a/crates/anvil/tests/it/main.rs +++ b/crates/anvil/tests/it/main.rs @@ -4,10 +4,8 @@ mod anvil_api; mod api; mod eip4844; mod fork; -mod ganache; mod gas; mod genesis; -mod geth; mod ipc; mod logs; mod optimism; diff --git a/crates/anvil/tests/it/optimism.rs b/crates/anvil/tests/it/optimism.rs index 0a7801c4e..7b38a3e5c 100644 --- a/crates/anvil/tests/it/optimism.rs +++ b/crates/anvil/tests/it/optimism.rs @@ -8,19 +8,15 @@ use alloy_provider::Provider; use alloy_rpc_types::{optimism::OptimismTransactionFields, TransactionRequest, WithOtherFields}; use anvil::{spawn, Hardfork, NodeConfig}; -// TODO: transaction is expected to fail, it does not, remove ignore once fixed #[tokio::test(flavor = "multi_thread")] -#[ignore] async fn test_deposits_not_supported_if_optimism_disabled() { let (_api, handle) = spawn(NodeConfig::test()).await; + let provider = handle.http_provider(); let accounts: Vec<_> = handle.dev_wallets().collect(); - let signer: EthereumSigner = accounts[0].clone().into(); let from = accounts[0].address(); let to = accounts[1].address(); - let provider = http_provider_with_signer(&handle.http_endpoint(), signer); - let tx = TransactionRequest::default() .with_from(from) .with_to(to) @@ -38,11 +34,9 @@ async fn test_deposits_not_supported_if_optimism_disabled() { .into(), }; - let res = provider.send_transaction(tx).await.unwrap().register().await; - assert!(res - .unwrap_err() - .to_string() - .contains("op-stack deposit tx received but is not supported")); + let err = provider.send_transaction(tx).await.unwrap_err(); + let s = err.to_string(); + assert!(s.contains("op-stack deposit tx received but is not supported"), "{s:?}"); } #[tokio::test(flavor = "multi_thread")] diff --git a/crates/anvil/tests/it/otterscan.rs b/crates/anvil/tests/it/otterscan.rs index 07af1b8ea..f9fbd9123 100644 --- a/crates/anvil/tests/it/otterscan.rs +++ b/crates/anvil/tests/it/otterscan.rs @@ -1,79 +1,66 @@ -//! tests for otterscan endpoints -use crate::{ - abi::MulticallContract, - utils::{http_provider_with_signer, ws_provider_with_signer}, -}; -use alloy_network::EthereumSigner; -use alloy_primitives::{Address, Bytes, TxKind, U256}; +//! Tests for otterscan endpoints. + +use crate::abi::MulticallContract; +use alloy_primitives::{address, Address, Bytes, U256}; use alloy_provider::Provider; use alloy_rpc_types::{BlockNumberOrTag, BlockTransactions, TransactionRequest, WithOtherFields}; -use alloy_sol_types::sol; +use alloy_sol_types::{sol, SolCall, SolError}; use anvil::{ eth::otterscan::types::{ OtsInternalOperation, OtsInternalOperationType, OtsTrace, OtsTraceType, }, spawn, NodeConfig, }; -use foundry_compilers::{project_util::TempProject, Artifact}; -use std::{collections::VecDeque, str::FromStr}; +use std::collections::VecDeque; #[tokio::test(flavor = "multi_thread")] -async fn can_call_erigon_get_header_by_number() { +async fn erigon_get_header_by_number() { let (api, _handle) = spawn(NodeConfig::test()).await; api.mine_one().await; let res0 = api.erigon_get_header_by_number(0.into()).await.unwrap().unwrap(); - let res1 = api.erigon_get_header_by_number(1.into()).await.unwrap().unwrap(); - assert_eq!(res0.header.number, Some(0)); + + let res1 = api.erigon_get_header_by_number(1.into()).await.unwrap().unwrap(); assert_eq!(res1.header.number, Some(1)); } #[tokio::test(flavor = "multi_thread")] -async fn can_call_ots_get_api_level() { +async fn ots_get_api_level() { let (api, _handle) = spawn(NodeConfig::test()).await; assert_eq!(api.ots_get_api_level().await.unwrap(), 8); } #[tokio::test(flavor = "multi_thread")] -async fn can_call_ots_get_internal_operations_contract_deploy() { +async fn ots_get_internal_operations_contract_deploy() { let (api, handle) = spawn(NodeConfig::test()).await; + let provider = handle.http_provider(); + let sender = handle.dev_accounts().next().unwrap(); - let wallet = handle.dev_wallets().next().unwrap(); - let signer: EthereumSigner = wallet.clone().into(); - let sender = wallet.address(); - - let provider = http_provider_with_signer(&handle.http_endpoint(), signer); - - let contract_receipt = MulticallContract::deploy_builder(provider.clone()) - .from(sender) + let contract_receipt = MulticallContract::deploy_builder(&provider) .send() .await .unwrap() .get_receipt() .await .unwrap(); - let contract_address = sender.create(0); let res = api.ots_get_internal_operations(contract_receipt.transaction_hash).await.unwrap(); - - assert_eq!(res.len(), 1); assert_eq!( - res[0], - OtsInternalOperation { + res, + [OtsInternalOperation { r#type: OtsInternalOperationType::Create, from: sender, - to: contract_address, + to: contract_receipt.contract_address.unwrap(), value: U256::from(0) - } + }], ); } #[tokio::test(flavor = "multi_thread")] -async fn can_call_ots_get_internal_operations_contract_transfer() { +async fn ots_get_internal_operations_contract_transfer() { let (api, handle) = spawn(NodeConfig::test()).await; - let provider = handle.http_provider(); let accounts: Vec<_> = handle.dev_wallets().collect(); @@ -88,170 +75,88 @@ async fn can_call_ots_get_internal_operations_contract_transfer() { let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); let res = api.ots_get_internal_operations(receipt.transaction_hash).await.unwrap(); - - assert_eq!(res.len(), 1); assert_eq!( - res[0], - OtsInternalOperation { + res, + [OtsInternalOperation { r#type: OtsInternalOperationType::Transfer, from, to, value: amount - } + }], ); } #[tokio::test(flavor = "multi_thread")] -async fn can_call_ots_get_internal_operations_contract_create2() { - let prj = TempProject::dapptools().unwrap(); - prj.add_source( - "Contract", - r" -pragma solidity 0.8.13; -contract Contract { - address constant CREATE2_DEPLOYER = 0x4e59b44847b379578588920cA78FbF26c0B4956C; - constructor() {} - function deployContract() public { - uint256 salt = 0; - uint256 code = 0; - bytes memory creationCode = abi.encodePacked(code); - (bool success,) = address(CREATE2_DEPLOYER).call(abi.encodePacked(salt, creationCode)); - require(success); - } -} -", - ) - .unwrap(); - +async fn ots_get_internal_operations_contract_create2() { sol!( - #[sol(rpc)] + #[sol(rpc, bytecode = "60808060405234601557610147908161001a8239f35b5f80fdfe6080600436101561000e575f80fd5b5f3560e01c636cd5c39b14610021575f80fd5b346100d0575f3660031901126100d0575f602082810191825282526001600160401b03916040810191838311828410176100d4578261008960405f959486958252606081019486865281518091608084015e81018660808201520360208101845201826100ee565b519082734e59b44847b379578588920ca78fbf26c0b4956c5af1903d156100e8573d9081116100d4576040516100c991601f01601f1916602001906100ee565b156100d057005b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b506100c9565b601f909101601f19168101906001600160401b038211908210176100d45760405256fea2646970667358221220f76968e121fc002b537029df51a2aecca0793282491baf84b872ffbfbfb1c9d764736f6c63430008190033")] contract Contract { - function deployContract() external; + address constant CREATE2_DEPLOYER = 0x4e59b44847b379578588920cA78FbF26c0B4956C; + + function deployContract() public { + uint256 salt = 0; + uint256 code = 0; + bytes memory creationCode = abi.encodePacked(code); + (bool success,) = address(CREATE2_DEPLOYER).call(abi.encodePacked(salt, creationCode)); + require(success); + } } ); - let mut compiled = prj.compile().unwrap(); - assert!(!compiled.has_compiler_errors()); - let contract = compiled.remove_first("Contract").unwrap(); - let bytecode = contract.into_bytecode_bytes().unwrap(); - let (api, handle) = spawn(NodeConfig::test()).await; - let wallet = handle.dev_wallets().next().unwrap(); - let signer: EthereumSigner = wallet.clone().into(); - let sender = wallet.address(); - - let provider = ws_provider_with_signer(&handle.ws_endpoint(), signer); - - // deploy successfully - provider - .send_transaction(WithOtherFields::new(TransactionRequest { - from: Some(sender), - to: Some(TxKind::Create), - input: bytecode.into(), - ..Default::default() - })) - .await - .unwrap() - .get_receipt() - .await - .unwrap(); - let contract_address = sender.create(0); - let contract = Contract::new(contract_address, &provider); + let provider = handle.http_provider(); + + let contract = Contract::deploy(&provider).await.unwrap(); + let receipt = contract.deployContract().send().await.unwrap().get_receipt().await.unwrap(); let res = api.ots_get_internal_operations(receipt.transaction_hash).await.unwrap(); - - assert_eq!(res.len(), 1); assert_eq!( - res[0], - OtsInternalOperation { + res, + [OtsInternalOperation { r#type: OtsInternalOperationType::Create2, - from: Address::from_str("0x4e59b44847b379578588920cA78FbF26c0B4956C").unwrap(), - to: Address::from_str("0x347bcdad821abc09b8c275881b368de36476b62c").unwrap(), - value: U256::from(0) - } + from: address!("4e59b44847b379578588920cA78FbF26c0B4956C"), + to: address!("347bcdad821abc09b8c275881b368de36476b62c"), + value: U256::from(0), + }], ); } #[tokio::test(flavor = "multi_thread")] -async fn can_call_ots_get_internal_operations_contract_selfdestruct() { - let prj = TempProject::dapptools().unwrap(); - prj.add_source( - "Contract", - r" -pragma solidity 0.8.13; -contract Contract { - address payable private owner; - constructor() public { - owner = payable(msg.sender); - } - function goodbye() public { - selfdestruct(owner); - } -} -", - ) - .unwrap(); - +async fn ots_get_internal_operations_contract_selfdestruct() { sol!( - #[sol(rpc)] + #[sol(rpc, bytecode = "60808060405234601357607c908160188239f35b5f80fdfe6004361015600b575f80fd5b5f3560e01c6375fc8e3c14601d575f80fd5b346042575f36600319011260425773dcdd539da22bffaa499dbea4d37d086dde196e75ff5b5f80fdfea26469706673582212201778e88721238015f480b3a901848b460799e91836caf557a8dcfdb2a1a2469e64736f6c63430008190033")] contract Contract { - function goodbye() external; + function goodbye() public { + selfdestruct(payable(0xDcDD539DA22bfFAa499dBEa4d37d086Dde196E75)); + } } ); - let mut compiled = prj.compile().unwrap(); - assert!(!compiled.has_compiler_errors()); - let contract = compiled.remove_first("Contract").unwrap(); - let bytecode = contract.into_bytecode_bytes().unwrap(); - let (api, handle) = spawn(NodeConfig::test()).await; - let wallet = handle.dev_wallets().next().unwrap(); - let signer: EthereumSigner = wallet.clone().into(); - let sender = wallet.address(); - - let provider = ws_provider_with_signer(&handle.ws_endpoint(), signer); - - // deploy successfully - provider - .send_transaction(WithOtherFields::new(TransactionRequest { - from: Some(sender), - to: Some(TxKind::Create), - input: bytecode.into(), - ..Default::default() - })) - .await - .unwrap() - .get_receipt() - .await - .unwrap(); - let contract_address = sender.create(0); - let contract = Contract::new(contract_address, &provider); + let provider = handle.http_provider(); + + let contract = Contract::deploy(&provider).await.unwrap(); let receipt = contract.goodbye().send().await.unwrap().get_receipt().await.unwrap(); let res = api.ots_get_internal_operations(receipt.transaction_hash).await.unwrap(); - - assert_eq!(res.len(), 1); assert_eq!( - res[0], - OtsInternalOperation { + res, + [OtsInternalOperation { r#type: OtsInternalOperationType::SelfDestruct, from: *contract.address(), - to: Default::default(), - value: U256::from(0) - } + to: Address::ZERO, + value: U256::from(0), + }], ); } #[tokio::test(flavor = "multi_thread")] -async fn can_call_ots_has_code() { +async fn ots_has_code() { let (api, handle) = spawn(NodeConfig::test()).await; - let wallet = handle.dev_wallets().next().unwrap(); - let signer: EthereumSigner = wallet.clone().into(); - let sender = wallet.address(); - - let provider = http_provider_with_signer(&handle.http_endpoint(), signer); + let provider = handle.http_provider(); + let sender = handle.dev_accounts().next().unwrap(); api.mine_one().await; @@ -260,7 +165,7 @@ async fn can_call_ots_has_code() { // no code in the address before deploying assert!(!api.ots_has_code(contract_address, BlockNumberOrTag::Number(1)).await.unwrap()); - let contract_builder = MulticallContract::deploy_builder(provider.clone()); + let contract_builder = MulticallContract::deploy_builder(&provider); let contract_receipt = contract_builder.send().await.unwrap().get_receipt().await.unwrap(); let num = provider.get_block_number().await.unwrap(); @@ -274,253 +179,152 @@ async fn can_call_ots_has_code() { } #[tokio::test(flavor = "multi_thread")] -async fn test_call_call_ots_trace_transaction() { - let prj = TempProject::dapptools().unwrap(); - prj.add_source( - "Contract", - r#" -pragma solidity 0.8.13; -contract Contract { - address payable private owner; - constructor() public { - owner = payable(msg.sender); - } - function run() payable public { - this.do_staticcall(); - this.do_call(); - } +async fn test_call_ots_trace_transaction() { + sol!( + #[sol(rpc, bytecode = "608080604052346026575f80546001600160a01b0319163317905561025e908161002b8239f35b5f80fdfe6080604081815260049081361015610015575f80fd5b5f925f3560e01c9081636a6758fe1461019a5750806396385e3914610123578063a1325397146101115763c04062261461004d575f80fd5b5f3660031901126100d5578051633533ac7f60e11b81526020818481305afa80156100cb576100d9575b50303b156100d55780516396385e3960e01b8152915f83828183305af180156100cb576100a2578380f35b919250906001600160401b0383116100b8575052005b604190634e487b7160e01b5f525260245ffd5b82513d5f823e3d90fd5b5f80fd5b6020813d602011610109575b816100f2602093836101b3565b810103126100d55751801515036100d5575f610077565b3d91506100e5565b346100d5575f3660031901126100d557005b5090346100d5575f3660031901126100d5575f805481908190819047906001600160a01b03165af1506101546101ea565b50815163a132539760e01b6020820190815282825292909182820191906001600160401b038311848410176100b8575f8086868686525190305af4506101986101ea565b005b346100d5575f3660031901126100d55780600160209252f35b601f909101601f19168101906001600160401b038211908210176101d657604052565b634e487b7160e01b5f52604160045260245ffd5b3d15610223573d906001600160401b0382116101d65760405191610218601f8201601f1916602001846101b3565b82523d5f602084013e565b60609056fea264697066735822122099817ea378044f1f6434272aeb1f3f01a734645e599e69b4caf2ba7a4fb65f9d64736f6c63430008190033")] + contract Contract { + address private owner; - function do_staticcall() external view returns (bool) { - return true; - } + constructor() { + owner = msg.sender; + } - function do_call() external { - owner.call{value: address(this).balance}(""); - address(this).delegatecall(abi.encodeWithSignature("do_delegatecall()")); - } - - function do_delegatecall() internal { - } -} -"#, - ) - .unwrap(); + function run() payable public { + this.do_staticcall(); + this.do_call(); + } - sol!( - #[sol(rpc)] - contract Contract { - function run() external payable; - function do_staticcall() external view returns (bool); - function do_call() external; - function do_delegatecall() external; + function do_staticcall() external view returns (bool) { + return true; + } + + function do_call() external { + owner.call{value: address(this).balance}(""); + address(this).delegatecall(abi.encodeWithSignature("do_delegatecall()")); + } + + function do_delegatecall() external {} } ); - let mut compiled = prj.compile().unwrap(); - assert!(!compiled.has_compiler_errors()); - let contract = compiled.remove_first("Contract").unwrap(); - let bytecode = contract.into_bytecode_bytes().unwrap(); - let (api, handle) = spawn(NodeConfig::test()).await; + let provider = handle.http_provider(); let wallets = handle.dev_wallets().collect::>(); - let signer: EthereumSigner = wallets[0].clone().into(); let sender = wallets[0].address(); - let provider = ws_provider_with_signer(&handle.ws_endpoint(), signer); - - // deploy successfully - provider - .send_transaction(WithOtherFields::new(TransactionRequest { - from: Some(sender), - to: Some(TxKind::Create), - input: bytecode.into(), - ..Default::default() - })) - .await - .unwrap() - .get_receipt() - .await - .unwrap(); - let contract_address = sender.create(0); + let contract_address = Contract::deploy_builder(&provider).from(sender).deploy().await.unwrap(); let contract = Contract::new(contract_address, &provider); - let receipt = contract - .run() - .from(sender) - .value(U256::from(1337)) - .send() - .await - .unwrap() - .get_receipt() - .await - .unwrap(); + let receipt = + contract.run().value(U256::from(1337)).send().await.unwrap().get_receipt().await.unwrap(); let res = api.ots_trace_transaction(receipt.transaction_hash).await.unwrap(); - - assert_eq!( - res, - vec![ - OtsTrace { - r#type: OtsTraceType::Call, - depth: 0, - from: sender, - to: contract_address, - value: U256::from(1337), - input: Bytes::from_str("0xc0406226").unwrap().0.into() - }, - OtsTrace { - r#type: OtsTraceType::StaticCall, - depth: 1, - from: contract_address, - to: contract_address, - value: U256::ZERO, - input: Bytes::from_str("0x6a6758fe").unwrap().0.into() - }, - OtsTrace { - r#type: OtsTraceType::Call, - depth: 1, - from: contract_address, - to: contract_address, - value: U256::ZERO, - input: Bytes::from_str("0x96385e39").unwrap().0.into() - }, - OtsTrace { - r#type: OtsTraceType::Call, - depth: 2, - from: contract_address, - to: sender, - value: U256::from(1337), - input: Bytes::from_str("0x").unwrap().0.into() - }, - OtsTrace { - r#type: OtsTraceType::DelegateCall, - depth: 2, - from: contract_address, - to: contract_address, - value: U256::ZERO, - input: Bytes::from_str("0xa1325397").unwrap().0.into() - }, - ] - ); + let expected = vec![ + OtsTrace { + r#type: OtsTraceType::Call, + depth: 0, + from: sender, + to: contract_address, + value: U256::from(1337), + input: Contract::runCall::SELECTOR.into(), + }, + OtsTrace { + r#type: OtsTraceType::StaticCall, + depth: 1, + from: contract_address, + to: contract_address, + value: U256::ZERO, + input: Contract::do_staticcallCall::SELECTOR.into(), + }, + OtsTrace { + r#type: OtsTraceType::Call, + depth: 1, + from: contract_address, + to: contract_address, + value: U256::ZERO, + input: Contract::do_callCall::SELECTOR.into(), + }, + OtsTrace { + r#type: OtsTraceType::Call, + depth: 2, + from: contract_address, + to: sender, + value: U256::from(1337), + input: Bytes::new(), + }, + OtsTrace { + r#type: OtsTraceType::DelegateCall, + depth: 2, + from: contract_address, + to: contract_address, + value: U256::ZERO, + input: Contract::do_delegatecallCall::SELECTOR.into(), + }, + ]; + assert_eq!(res, expected); } #[tokio::test(flavor = "multi_thread")] -async fn can_call_ots_get_transaction_error() { - let prj = TempProject::dapptools().unwrap(); - prj.add_source( - "Contract", - r#" -pragma solidity 0.8.13; -contract Contract { - error CustomError(string msg); - - function trigger_revert() public { - revert CustomError("RevertStringFooBar"); - } -} -"#, - ) - .unwrap(); - +async fn ots_get_transaction_error() { sol!( - #[sol(rpc)] + #[sol(rpc, bytecode = "6080806040523460135760a3908160188239f35b5f80fdfe60808060405260043610156011575f80fd5b5f3560e01c63f67f4650146023575f80fd5b346069575f3660031901126069576346b7545f60e11b81526020600482015260126024820152712932bb32b93a29ba3934b733a337b7a130b960711b6044820152606490fd5b5f80fdfea264697066735822122069222918090d4d3ddc6a9c8b6ef282464076c71f923a0e8618ed25489b87f12b64736f6c63430008190033")] contract Contract { - function trigger_revert() external; + error CustomError(string msg); + + function trigger_revert() public { + revert CustomError("RevertStringFooBar"); + } } ); - let mut compiled = prj.compile().unwrap(); - assert!(!compiled.has_compiler_errors()); - let contract = compiled.remove_first("Contract").unwrap(); - let bytecode = contract.into_bytecode_bytes().unwrap(); + let (api, handle) = spawn(NodeConfig::test()).await; + let provider = handle.http_provider(); - let (_api, handle) = spawn(NodeConfig::test()).await; - let wallets = handle.dev_wallets().collect::>(); - let signer: EthereumSigner = wallets[0].clone().into(); - let sender = wallets[0].address(); + let contract = Contract::deploy(&provider).await.unwrap(); - let provider = ws_provider_with_signer(&handle.ws_endpoint(), signer); + let receipt = contract.trigger_revert().send().await.unwrap().get_receipt().await.unwrap(); - // deploy successfully - provider - .send_transaction(WithOtherFields::new(TransactionRequest { - from: Some(sender), - to: Some(TxKind::Create), - input: bytecode.into(), - ..Default::default() - })) - .await - .unwrap() - .get_receipt() - .await - .unwrap(); - let contract_address = sender.create(0); - let _contract = Contract::new(contract_address, &provider); - - // TODO: currently not possible to capture the receipt - // let receipt = contract.trigger_revert().send().await.unwrap().get_receipt().await.unwrap(); - - // let res = api.ots_get_transaction_error(receipt.transaction_hash).await; - // assert!(res.is_err()); - // assert!(res.unwrap_err().to_string().contains("0x8d6ea8be00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000012526576657274537472696e67466f6f4261720000000000000000000000000000")); + let err = api.ots_get_transaction_error(receipt.transaction_hash).await.unwrap(); + let expected = Contract::CustomError { msg: String::from("RevertStringFooBar") }.abi_encode(); + assert_eq!(err, Bytes::from(expected)); } #[tokio::test(flavor = "multi_thread")] async fn ots_get_transaction_error_no_error() { let (api, handle) = spawn(NodeConfig::test()).await; - let wallets = handle.dev_wallets().collect::>(); - let signer: EthereumSigner = wallets[0].clone().into(); - let sender = wallets[0].address(); - - let provider = ws_provider_with_signer(&handle.ws_endpoint(), signer); + let provider = handle.http_provider(); // Send a successful transaction - let tx = - TransactionRequest::default().from(sender).to(Address::random()).value(U256::from(100)); + let tx = TransactionRequest::default().to(Address::random()).value(U256::from(100)); let tx = WithOtherFields::new(tx); let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); - let res = api.ots_get_transaction_error(receipt.transaction_hash).await; - assert!(res.is_ok()); - assert_eq!(res.unwrap().to_string(), "0x"); + let res = api.ots_get_transaction_error(receipt.transaction_hash).await.unwrap(); + assert!(res.is_empty(), "{res}"); } #[tokio::test(flavor = "multi_thread")] -async fn can_call_ots_get_block_details() { +async fn ots_get_block_details() { let (api, handle) = spawn(NodeConfig::test()).await; - let wallet = handle.dev_wallets().next().unwrap(); - let signer: EthereumSigner = wallet.clone().into(); - let sender = wallet.address(); - - let provider = http_provider_with_signer(&handle.http_endpoint(), signer); + let provider = handle.http_provider(); - let tx = - TransactionRequest::default().from(sender).to(Address::random()).value(U256::from(100)); + let tx = TransactionRequest::default().to(Address::random()).value(U256::from(100)); let tx = WithOtherFields::new(tx); let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); let result = api.ots_get_block_details(1.into()).await.unwrap(); assert_eq!(result.block.transaction_count, 1); - let hash = match result.block.block.transactions { - BlockTransactions::Full(txs) => txs[0].hash, - BlockTransactions::Hashes(hashes) => hashes[0], - BlockTransactions::Uncle => unreachable!(), - }; - assert_eq!(hash, receipt.transaction_hash); + let hash = result.block.block.transactions.hashes().next().unwrap(); + assert_eq!(*hash, receipt.transaction_hash); } #[tokio::test(flavor = "multi_thread")] -async fn can_call_ots_get_block_details_by_hash() { +async fn ots_get_block_details_by_hash() { let (api, handle) = spawn(NodeConfig::test()).await; - let wallet = handle.dev_wallets().next().unwrap(); - let signer: EthereumSigner = wallet.clone().into(); - let sender = wallet.address(); - - let provider = http_provider_with_signer(&handle.http_endpoint(), signer); + let provider = handle.http_provider(); - let tx = - TransactionRequest::default().from(sender).to(Address::random()).value(U256::from(100)); + let tx = TransactionRequest::default().to(Address::random()).value(U256::from(100)); let tx = WithOtherFields::new(tx); let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); @@ -537,24 +341,17 @@ async fn can_call_ots_get_block_details_by_hash() { } #[tokio::test(flavor = "multi_thread")] -async fn can_call_ots_get_block_transactions() { +async fn ots_get_block_transactions() { let (api, handle) = spawn(NodeConfig::test()).await; - let wallet = handle.dev_wallets().next().unwrap(); - let signer: EthereumSigner = wallet.clone().into(); - let sender = wallet.address(); - - let provider = http_provider_with_signer(&handle.http_endpoint(), signer); + let provider = handle.http_provider(); // disable automine api.anvil_set_auto_mine(false).await.unwrap(); let mut hashes = VecDeque::new(); for i in 0..10 { - let tx = TransactionRequest::default() - .from(sender) - .to(Address::random()) - .value(U256::from(100)) - .nonce(i); + let tx = + TransactionRequest::default().to(Address::random()).value(U256::from(100)).nonce(i); let tx = WithOtherFields::new(tx); let pending_receipt = provider.send_transaction(tx).await.unwrap().register().await.unwrap(); @@ -583,22 +380,16 @@ async fn can_call_ots_get_block_transactions() { } #[tokio::test(flavor = "multi_thread")] -async fn can_call_ots_search_transactions_before() { +async fn ots_search_transactions_before() { let (api, handle) = spawn(NodeConfig::test()).await; - let wallet = handle.dev_wallets().next().unwrap(); - let signer: EthereumSigner = wallet.clone().into(); - let sender = wallet.address(); - - let provider = http_provider_with_signer(&handle.http_endpoint(), signer); + let provider = handle.http_provider(); + let sender = handle.dev_accounts().next().unwrap(); let mut hashes = vec![]; for i in 0..7 { - let tx = TransactionRequest::default() - .from(sender) - .to(Address::random()) - .value(U256::from(100)) - .nonce(i); + let tx = + TransactionRequest::default().to(Address::random()).value(U256::from(100)).nonce(i); let tx = WithOtherFields::new(tx); let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); hashes.push(receipt.transaction_hash); @@ -624,22 +415,16 @@ async fn can_call_ots_search_transactions_before() { } #[tokio::test(flavor = "multi_thread")] -async fn can_call_ots_search_transactions_after() { +async fn ots_search_transactions_after() { let (api, handle) = spawn(NodeConfig::test()).await; - let wallet = handle.dev_wallets().next().unwrap(); - let signer: EthereumSigner = wallet.clone().into(); - let sender = wallet.address(); - - let provider = http_provider_with_signer(&handle.http_endpoint(), signer); + let provider = handle.http_provider(); + let sender = handle.dev_accounts().next().unwrap(); let mut hashes = VecDeque::new(); for i in 0..7 { - let tx = TransactionRequest::default() - .from(sender) - .to(Address::random()) - .value(U256::from(100)) - .nonce(i); + let tx = + TransactionRequest::default().to(Address::random()).value(U256::from(100)).nonce(i); let tx = WithOtherFields::new(tx); let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); hashes.push_front(receipt.transaction_hash); @@ -665,15 +450,10 @@ async fn can_call_ots_search_transactions_after() { } #[tokio::test(flavor = "multi_thread")] -async fn can_call_ots_get_transaction_by_sender_and_nonce() { +async fn ots_get_transaction_by_sender_and_nonce() { let (api, handle) = spawn(NodeConfig::test()).await; - let wallet = handle.dev_wallets().next().unwrap(); - let signer: EthereumSigner = wallet.clone().into(); - let sender = wallet.address(); - - let provider = http_provider_with_signer(&handle.http_endpoint(), signer); - - api.mine_one().await; + let provider = handle.http_provider(); + let sender = handle.dev_accounts().next().unwrap(); let tx1 = WithOtherFields::new( TransactionRequest::default() @@ -703,22 +483,22 @@ async fn can_call_ots_get_transaction_by_sender_and_nonce() { } #[tokio::test(flavor = "multi_thread")] -async fn can_call_ots_get_contract_creator() { +async fn ots_get_contract_creator() { let (api, handle) = spawn(NodeConfig::test()).await; - let wallet = handle.dev_wallets().next().unwrap(); - let signer: EthereumSigner = wallet.clone().into(); - let sender = wallet.address(); - - let provider = http_provider_with_signer(&handle.http_endpoint(), signer); - - api.mine_one().await; + let provider = handle.http_provider(); + let sender = handle.dev_accounts().next().unwrap(); - let contract_builder = MulticallContract::deploy_builder(provider.clone()); - let contract_receipt = contract_builder.send().await.unwrap().get_receipt().await.unwrap(); - let contract_address = sender.create(0); + let receipt = MulticallContract::deploy_builder(&provider) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + let contract_address = receipt.contract_address.unwrap(); let creator = api.ots_get_contract_creator(contract_address).await.unwrap().unwrap(); assert_eq!(creator.creator, sender); - assert_eq!(creator.hash, contract_receipt.transaction_hash); + assert_eq!(creator.hash, receipt.transaction_hash); } diff --git a/crates/anvil/tests/it/pubsub.rs b/crates/anvil/tests/it/pubsub.rs index f29dbb81f..d11208feb 100644 --- a/crates/anvil/tests/it/pubsub.rs +++ b/crates/anvil/tests/it/pubsub.rs @@ -246,8 +246,6 @@ async fn test_subscriptions() { assert_eq!(blocks, vec![1, 2, 3]) } -// TODO: Fix this, num > 17 breaks the test due to poller channel_size defaults to 16. Recv channel -// lags behind. #[tokio::test(flavor = "multi_thread")] async fn test_sub_new_heads_fast() { let (api, handle) = spawn(NodeConfig::test()).await; diff --git a/crates/anvil/tests/it/revert.rs b/crates/anvil/tests/it/revert.rs index 5cb9b2fed..051f6be66 100644 --- a/crates/anvil/tests/it/revert.rs +++ b/crates/anvil/tests/it/revert.rs @@ -1,290 +1,125 @@ -use crate::{ - abi::{VendingMachine, VENDING_MACHINE_CONTRACT}, - utils::ws_provider_with_signer, -}; -use alloy_network::EthereumSigner; -use alloy_primitives::{TxKind, U256}; +use crate::abi::VendingMachine; +use alloy_network::TransactionBuilder; +use alloy_primitives::{bytes, U256}; use alloy_provider::Provider; use alloy_rpc_types::{TransactionRequest, WithOtherFields}; use alloy_sol_types::sol; use anvil::{spawn, NodeConfig}; -use foundry_compilers::{project_util::TempProject, Artifact}; #[tokio::test(flavor = "multi_thread")] async fn test_deploy_reverting() { - let prj = TempProject::dapptools().unwrap(); - prj.add_source( - "Contract", - r#" -pragma solidity 0.8.13; -contract Contract { - constructor() { - require(false, ""); - } -} -"#, - ) - .unwrap(); - - let mut compiled = prj.compile().unwrap(); - assert!(!compiled.has_compiler_errors()); - let contract = compiled.remove_first("Contract").unwrap(); - let bytecode = contract.into_bytecode_bytes().unwrap(); - let (_api, handle) = spawn(NodeConfig::test()).await; - let wallet = handle.dev_wallets().next().unwrap(); - let signer: EthereumSigner = wallet.clone().into(); - let sender = wallet.address(); - - let provider = ws_provider_with_signer(&handle.ws_endpoint(), signer); - - // should catch the revert during estimation which results in an err - let err = provider - .send_transaction(WithOtherFields::new(TransactionRequest { - from: Some(sender), - to: Some(TxKind::Create), - input: bytecode.into(), - ..Default::default() - })) - .await - .unwrap_err(); - assert!(err.to_string().contains("execution reverted")); + let provider = handle.http_provider(); + let sender = handle.dev_accounts().next().unwrap(); + + let code = bytes!("5f5ffd"); // PUSH0 PUSH0 REVERT + let tx = TransactionRequest::default().from(sender).with_deploy_code(code); + let tx = WithOtherFields::new(tx); + + // Calling/estimating gas fails early. + let err = provider.call(&tx).await.unwrap_err(); + let s = err.to_string(); + assert!(s.contains("execution reverted"), "{s:?}"); + + // Sending the transaction is successful but reverts on chain. + let tx = provider.send_transaction(tx).await.unwrap(); + let receipt = tx.get_receipt().await.unwrap(); + assert!(!receipt.inner.inner.status()); } #[tokio::test(flavor = "multi_thread")] async fn test_revert_messages() { - let prj = TempProject::dapptools().unwrap(); - prj.add_source( - "Contract", - r#" -pragma solidity 0.8.13; -contract Contract { - address owner; - constructor() public { - owner = address(1); - } - modifier onlyOwner() { - require(msg.sender == owner, "!authorized"); - _; - } - function getSecret() public onlyOwner view returns(uint256 secret) { - return 123; - } -} -"#, - ) - .unwrap(); - sol!( - #[sol(rpc)] + #[sol(rpc, bytecode = "608080604052346025575f80546001600160a01b031916600117905560b69081602a8239f35b5f80fdfe60808060405260043610156011575f80fd5b5f3560e01c635b9fdc30146023575f80fd5b34607c575f366003190112607c575f546001600160a01b03163303604c576020604051607b8152f35b62461bcd60e51b815260206004820152600b60248201526a08585d5d1a1bdc9a5e995960aa1b6044820152606490fd5b5f80fdfea2646970667358221220f593e5ccd46935f623185de62a72d9f1492d8d15075a111b0fa4d7e16acf4a7064736f6c63430008190033")] contract Contract { - function getSecret() external; - } - ); + address private owner; - let mut compiled = prj.compile().unwrap(); - assert!(!compiled.has_compiler_errors()); - let contract = compiled.remove_first("Contract").unwrap(); - let bytecode = contract.into_bytecode_bytes().unwrap(); + constructor() { + owner = address(1); + } - let (_api, handle) = spawn(NodeConfig::test()).await; - let wallet = handle.dev_wallets().next().unwrap(); - let signer: EthereumSigner = wallet.clone().into(); - let sender = wallet.address(); + modifier onlyOwner() { + require(msg.sender == owner, "!authorized"); + _; + } - let provider = ws_provider_with_signer(&handle.ws_endpoint(), signer); + #[derive(Debug)] + function getSecret() public onlyOwner view returns(uint256 secret) { + return 123; + } + } + ); - // deploy successfully - provider - .send_transaction(WithOtherFields::new(TransactionRequest { - from: Some(sender), - to: Some(TxKind::Create), - input: bytecode.into(), - ..Default::default() - })) - .await - .unwrap() - .get_receipt() - .await - .unwrap(); - let contract_address = sender.create(0); - let contract = Contract::new(contract_address, &provider); + let (_api, handle) = spawn(NodeConfig::test()).await; + let provider = handle.http_provider(); - let res = contract.getSecret().send().await.unwrap_err(); + let contract = Contract::deploy(&provider).await.unwrap(); - let msg = res.to_string(); - assert!(msg.contains("execution reverted: revert: !authorized")); + let err = contract.getSecret().call().await.unwrap_err(); + let s = err.to_string(); + assert!(s.contains("!authorized"), "{s:?}"); } #[tokio::test(flavor = "multi_thread")] async fn test_solc_revert_example() { - let prj = TempProject::dapptools().unwrap(); - prj.add_source("VendingMachine", VENDING_MACHINE_CONTRACT).unwrap(); - - let mut compiled = prj.compile().unwrap(); - assert!(!compiled.has_compiler_errors()); - let contract = compiled.remove_first("VendingMachine").unwrap(); - let bytecode = contract.into_bytecode_bytes().unwrap(); - let (_api, handle) = spawn(NodeConfig::test()).await; - let wallet = handle.dev_wallets().next().unwrap(); - let signer: EthereumSigner = wallet.clone().into(); - let sender = wallet.address(); + let sender = handle.dev_accounts().next().unwrap(); + let provider = handle.http_provider(); - let provider = ws_provider_with_signer(&handle.ws_endpoint(), signer); + let contract = VendingMachine::deploy(&provider).await.unwrap(); - // deploy successfully - provider - .send_transaction(WithOtherFields::new(TransactionRequest { - from: Some(sender), - to: Some(TxKind::Create), - input: bytecode.into(), - ..Default::default() - })) - .await - .unwrap() - .get_receipt() - .await - .unwrap(); - let contract_address = sender.create(0); - let contract = VendingMachine::new(contract_address, &provider); - - let res = contract - .buyRevert(U256::from(100)) - .value(U256::from(1)) - .from(sender) - .send() - .await - .unwrap_err(); - let msg = res.to_string(); - assert!(msg.contains("execution reverted: revert: Not enough Ether provided.")); - - let res = contract - .buyRequire(U256::from(100)) - .value(U256::from(1)) - .from(sender) - .send() - .await - .unwrap_err(); - let msg = res.to_string(); - assert!(msg.contains("execution reverted: revert: Not enough Ether provided.")); + let err = + contract.buy(U256::from(100)).value(U256::from(1)).from(sender).call().await.unwrap_err(); + let s = err.to_string(); + assert!(s.contains("Not enough Ether provided."), "{s:?}"); } // #[tokio::test(flavor = "multi_thread")] async fn test_another_revert_message() { - let prj = TempProject::dapptools().unwrap(); - prj.add_source( - "Contract", - r#" -pragma solidity 0.8.13; -contract Contract { - uint256 public number; - - function setNumber(uint256 num) public { - require(num != 0, "RevertStringFooBar"); - number = num; - } -} -"#, - ) - .unwrap(); - sol!( - #[sol(rpc)] + #[sol(rpc, bytecode = "6080806040523460135760d7908160188239f35b5f80fdfe60808060405260043610156011575f80fd5b5f3560e01c9081633fb5c1cb14604d5750638381f58a14602f575f80fd5b346049575f36600319011260495760205f54604051908152f35b5f80fd5b346049576020366003190112604957600435908115606a57505f55005b62461bcd60e51b81526020600482015260126024820152712932bb32b93a29ba3934b733a337b7a130b960711b6044820152606490fdfea2646970667358221220314bf8261cc467619137c071584f8d3bd8d9d97bf2846c138c0567040cf9828a64736f6c63430008190033")] contract Contract { - function setNumber(uint256 num) external; + uint256 public number; + + #[derive(Debug)] + function setNumber(uint256 num) public { + require(num != 0, "RevertStringFooBar"); + number = num; + } } ); - let mut compiled = prj.compile().unwrap(); - assert!(!compiled.has_compiler_errors()); - let contract = compiled.remove_first("Contract").unwrap(); - let bytecode = contract.into_bytecode_bytes().unwrap(); - let (_api, handle) = spawn(NodeConfig::test()).await; - let wallet = handle.dev_wallets().next().unwrap(); - let signer: EthereumSigner = wallet.clone().into(); - let sender = wallet.address(); - - let provider = ws_provider_with_signer(&handle.ws_endpoint(), signer); + let provider = handle.http_provider(); - // deploy successfully - provider - .send_transaction(WithOtherFields::new(TransactionRequest { - from: Some(sender), - to: Some(TxKind::Create), - input: bytecode.into(), - ..Default::default() - })) - .await - .unwrap() - .get_receipt() - .await - .unwrap(); - let contract_address = sender.create(0); - let contract = Contract::new(contract_address, &provider); + let contract = Contract::deploy(&provider).await.unwrap(); - let res = contract.setNumber(U256::from(0)).send().await.unwrap_err(); - - let msg = res.to_string(); - assert!(msg.contains("execution reverted: revert: RevertStringFooBar")); + let err = contract.setNumber(U256::from(0)).call().await.unwrap_err(); + let s = err.to_string(); + assert!(s.contains("RevertStringFooBar"), "{s:?}"); } #[tokio::test(flavor = "multi_thread")] async fn test_solc_revert_custom_errors() { - let prj = TempProject::dapptools().unwrap(); - prj.add_source( - "Contract", - r#" -pragma solidity 0.8.13; -contract Contract { - uint256 public number; - error AddressRevert(address); - - function revertAddress() public { - revert AddressRevert(address(1)); - } -} -"#, - ) - .unwrap(); - sol!( - #[sol(rpc)] + #[sol(rpc, bytecode = "608080604052346013576081908160188239f35b5f80fdfe60808060405260043610156011575f80fd5b5f3560e01c63e57207e6146023575f80fd5b346047575f3660031901126047576373ea2a7f60e01b815260016004820152602490fd5b5f80fdfea26469706673582212202a8d69545801394af36c56ca229b52ae0b22d7b8f938b107dca8ebbf655464f764736f6c63430008190033")] contract Contract { - function revertAddress() external; + error AddressRevert(address); + + #[derive(Debug)] + function revertAddress() public { + revert AddressRevert(address(1)); + } } ); - let mut compiled = prj.compile().unwrap(); - assert!(!compiled.has_compiler_errors()); - let contract = compiled.remove_first("Contract").unwrap(); - let bytecode = contract.into_bytecode_bytes().unwrap(); - let (_api, handle) = spawn(NodeConfig::test()).await; - let wallet = handle.dev_wallets().next().unwrap(); - let signer: EthereumSigner = wallet.clone().into(); - let sender = wallet.address(); - - let provider = ws_provider_with_signer(&handle.ws_endpoint(), signer); + let provider = handle.http_provider(); - // deploy successfully - provider - .send_transaction(WithOtherFields::new(TransactionRequest { - from: Some(sender), - to: Some(TxKind::Create), - input: bytecode.into(), - ..Default::default() - })) - .await - .unwrap() - .get_receipt() - .await - .unwrap(); - let contract_address = sender.create(0); - let contract = Contract::new(contract_address, &provider); + let contract = Contract::deploy(&provider).await.unwrap(); - let res = contract.revertAddress().send().await.unwrap_err(); - assert!(res.to_string().contains("execution reverted")); + let err = contract.revertAddress().call().await.unwrap_err(); + let s = err.to_string(); + assert!(s.contains("execution reverted"), "{s:?}"); } diff --git a/crates/anvil/tests/it/traces.rs b/crates/anvil/tests/it/traces.rs index d12a848e4..011e489bb 100644 --- a/crates/anvil/tests/it/traces.rs +++ b/crates/anvil/tests/it/traces.rs @@ -66,7 +66,6 @@ async fn test_parity_suicide_trace() { let owner = wallets[0].address(); let destructor = wallets[1].address(); - // deploy successfully let contract_addr = SuicideContract::deploy_builder(provider.clone()).from(owner).deploy().await.unwrap(); let contract = SuicideContract::new(contract_addr, provider.clone()); diff --git a/crates/anvil/tests/it/transaction.rs b/crates/anvil/tests/it/transaction.rs index d0dbc539c..90ede1d73 100644 --- a/crates/anvil/tests/it/transaction.rs +++ b/crates/anvil/tests/it/transaction.rs @@ -1,6 +1,6 @@ use crate::{ abi::{Greeter, MulticallContract, SimpleStorage}, - utils::http_provider_with_signer, + utils::{connect_pubsub, http_provider_with_signer}, }; use alloy_network::{EthereumSigner, TransactionBuilder}; use alloy_primitives::{Address, Bytes, FixedBytes, U256}; @@ -837,9 +837,6 @@ async fn test_tx_receipt() { assert!(tx.contract_address.is_some()); } -// TODO: Fix error: ErrorPayload { code: -32602, message: "invalid type: boolean `true`, expected -// unit", data: None } originating from watch_full_pending_transactions, remove ignore -#[ignore] #[tokio::test(flavor = "multi_thread")] async fn can_stream_pending_transactions() { let (_api, handle) = @@ -847,7 +844,7 @@ async fn can_stream_pending_transactions() { let num_txs = 5; let provider = handle.http_provider(); - let ws_provider = handle.ws_provider(); + let ws_provider = connect_pubsub(&handle.ws_endpoint()).await; let accounts = provider.get_accounts().await.unwrap(); let tx = @@ -866,20 +863,20 @@ async fn can_stream_pending_transactions() { .fuse(); let mut watch_tx_stream = provider - .watch_full_pending_transactions() + .watch_pending_transactions() .await - .unwrap() // TODO: Fix error here + .unwrap() .into_stream() .flat_map(futures::stream::iter) .take(num_txs) .fuse(); let mut sub_tx_stream = ws_provider - .subscribe_full_pending_transactions() + .subscribe_pending_transactions() .await .unwrap() .into_stream() - .take(2) + .take(num_txs) .fuse(); let mut sent = None; @@ -901,14 +898,15 @@ async fn can_stream_pending_transactions() { sub_received.push(tx); } }, + complete => unreachable!(), }; if watch_received.len() == num_txs && sub_received.len() == num_txs { - if let Some(ref sent) = sent { + if let Some(sent) = &sent { assert_eq!(sent.len(), watch_received.len()); let sent_txs = sent.iter().map(|tx| tx.transaction_hash).collect::>(); - assert_eq!(sent_txs, watch_received.iter().map(|tx| tx.hash).collect()); - assert_eq!(sent_txs, sub_received.iter().map(|tx| tx.hash).collect()); + assert_eq!(sent_txs, watch_received.iter().copied().collect()); + assert_eq!(sent_txs, sub_received.iter().copied().collect()); break } } diff --git a/crates/anvil/tests/it/utils.rs b/crates/anvil/tests/it/utils.rs index 066ec807c..56c803702 100644 --- a/crates/anvil/tests/it/utils.rs +++ b/crates/anvil/tests/it/utils.rs @@ -25,6 +25,7 @@ pub fn ws_provider_with_signer( .expect("failed to build Alloy WS provider with signer") } +/// Currently required to get around pub async fn connect_pubsub(conn_str: &str) -> RootProvider { alloy_provider::ProviderBuilder::new().on_builtin(conn_str).await.unwrap() } diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index 728358bd6..423ed2767 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -526,13 +526,13 @@ fn prompt( ) -> Result { let text_clone = prompt_text.to_string(); let timeout = state.config.prompt_timeout; - let (send, recv) = mpsc::channel(); + let (tx, rx) = mpsc::channel(); thread::spawn(move || { - send.send(input(&text_clone)).unwrap(); + let _ = tx.send(input(&text_clone)); }); - match recv.recv_timeout(timeout) { + match rx.recv_timeout(timeout) { Ok(res) => res.map_err(|err| { println!(); err.to_string().into() diff --git a/crates/common/src/provider/mod.rs b/crates/common/src/provider/mod.rs index c8e9a0ba2..1083052b5 100644 --- a/crates/common/src/provider/mod.rs +++ b/crates/common/src/provider/mod.rs @@ -13,6 +13,7 @@ use alloy_provider::{ Identity, ProviderBuilder as AlloyProviderBuilder, RootProvider, }; use alloy_rpc_client::ClientBuilder; +use alloy_transport::utils::guess_local_url; use eyre::{Result, WrapErr}; use foundry_config::NamedChain; use reqwest::Url; @@ -84,10 +85,9 @@ pub struct ProviderBuilder { /// JWT Secret jwt: Option, headers: Vec, + is_local: bool, } -// === impl ProviderBuilder === - impl ProviderBuilder { /// Creates a new builder instance pub fn new(url_str: &str) -> Self { @@ -121,6 +121,9 @@ 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())); + Self { url, chain: NamedChain::Mainnet, @@ -132,6 +135,7 @@ impl ProviderBuilder { compute_units_per_second: ALCHEMY_FREE_TIER_CUPS, jwt: None, headers: vec![], + is_local, } } @@ -201,11 +205,19 @@ impl ProviderBuilder { self } + /// Sets the provider to be local. + /// + /// This is useful for local dev nodes. + pub fn local(mut self, is_local: bool) -> Self { + self.is_local = is_local; + self + } + /// Sets aggressive `max_retry` and `initial_backoff` values /// /// This is only recommend for local dev nodes pub fn aggressive(self) -> Self { - self.max_retry(100).initial_backoff(100) + self.max_retry(100).initial_backoff(100).local(true) } /// Sets the JWT secret @@ -233,6 +245,7 @@ impl ProviderBuilder { compute_units_per_second, jwt, headers, + is_local, } = self; let url = url?; @@ -247,7 +260,7 @@ impl ProviderBuilder { .with_headers(headers) .with_jwt(jwt) .build(); - let client = ClientBuilder::default().layer(retry_layer).transport(transport, false); + let client = ClientBuilder::default().layer(retry_layer).transport(transport, is_local); let provider = AlloyProviderBuilder::<_, _, AnyNetwork>::default() .on_provider(RootProvider::new(client)); @@ -267,6 +280,7 @@ impl ProviderBuilder { compute_units_per_second, jwt, headers, + is_local, } = self; let url = url?; @@ -283,7 +297,7 @@ impl ProviderBuilder { .with_jwt(jwt) .build(); - let client = ClientBuilder::default().layer(retry_layer).transport(transport, false); + let client = ClientBuilder::default().layer(retry_layer).transport(transport, is_local); let provider = AlloyProviderBuilder::<_, _, AnyNetwork>::default() .with_recommended_fillers() diff --git a/testdata/default/cheats/UnixTime.t.sol b/testdata/default/cheats/UnixTime.t.sol index e128dad24..786c1ef59 100644 --- a/testdata/default/cheats/UnixTime.t.sol +++ b/testdata/default/cheats/UnixTime.t.sol @@ -8,7 +8,7 @@ contract UnixTimeTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); // This is really wide because CI sucks. - uint256 constant errMargin = 300; + uint256 constant errMargin = 500; function testUnixTimeAgainstDate() public { string[] memory inputs = new string[](2); @@ -33,7 +33,6 @@ contract UnixTimeTest is DSTest { uint256 end = vm.unixTime(); uint256 interval = end - start; - assertGe(interval, sleepTime - errMargin, ".unixTime() is inaccurate"); - assertLe(interval, sleepTime + errMargin, ".unixTime() is inaccurate"); + vm.assertApproxEqAbs(interval, sleepTime, errMargin, ".unixTime() is inaccurate"); } } From a87faf6a725ab4327f8617bc56a7f85671bc52d8 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 7 May 2024 15:52:20 +0200 Subject: [PATCH 268/430] fix(anvil/ots): include selfdestruct target address (#7880) --- Cargo.lock | 47 +++++----- Cargo.toml | 48 +++++----- crates/anvil/src/eth/backend/executor.rs | 20 +---- crates/anvil/src/eth/otterscan/types.rs | 108 ++++++++++------------- crates/anvil/src/hardfork.rs | 2 +- crates/anvil/tests/it/otterscan.rs | 43 +++++++-- 6 files changed, 136 insertions(+), 132 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 55b297812..d327c38fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,7 +79,7 @@ dependencies = [ [[package]] name = "alloy-consensus" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" +source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" dependencies = [ "alloy-eips", "alloy-primitives", @@ -92,7 +92,7 @@ dependencies = [ [[package]] name = "alloy-contract" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" +source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -132,7 +132,7 @@ dependencies = [ [[package]] name = "alloy-eips" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" +source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -147,7 +147,7 @@ dependencies = [ [[package]] name = "alloy-genesis" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" +source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" dependencies = [ "alloy-primitives", "alloy-serde", @@ -170,7 +170,7 @@ dependencies = [ [[package]] name = "alloy-json-rpc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" +source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" dependencies = [ "alloy-primitives", "serde", @@ -182,7 +182,7 @@ dependencies = [ [[package]] name = "alloy-network" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" +source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" dependencies = [ "alloy-consensus", "alloy-eips", @@ -226,7 +226,7 @@ dependencies = [ [[package]] name = "alloy-provider" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" +source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" dependencies = [ "alloy-eips", "alloy-json-rpc", @@ -257,7 +257,7 @@ dependencies = [ [[package]] name = "alloy-pubsub" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" +source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -297,7 +297,7 @@ dependencies = [ [[package]] name = "alloy-rpc-client" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" +source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -321,7 +321,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" +source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" dependencies = [ "alloy-consensus", "alloy-eips", @@ -339,7 +339,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" +source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" dependencies = [ "alloy-consensus", "alloy-eips", @@ -356,7 +356,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" +source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -368,7 +368,7 @@ dependencies = [ [[package]] name = "alloy-serde" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" +source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" dependencies = [ "alloy-primitives", "serde", @@ -378,7 +378,7 @@ dependencies = [ [[package]] name = "alloy-signer" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" +source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -393,7 +393,7 @@ dependencies = [ [[package]] name = "alloy-signer-aws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" +source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" dependencies = [ "alloy-consensus", "alloy-network", @@ -410,9 +410,10 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" +source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" dependencies = [ "alloy-consensus", + "alloy-dyn-abi", "alloy-network", "alloy-primitives", "alloy-signer", @@ -428,7 +429,7 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" +source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" dependencies = [ "alloy-consensus", "alloy-network", @@ -444,7 +445,7 @@ dependencies = [ [[package]] name = "alloy-signer-wallet" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" +source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" dependencies = [ "alloy-consensus", "alloy-network", @@ -521,7 +522,7 @@ dependencies = [ [[package]] name = "alloy-transport" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" +source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" dependencies = [ "alloy-json-rpc", "base64 0.22.1", @@ -539,7 +540,7 @@ dependencies = [ [[package]] name = "alloy-transport-http" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" +source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -553,7 +554,7 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" +source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -573,7 +574,7 @@ dependencies = [ [[package]] name = "alloy-transport-ws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=05af0de#05af0de129dc0fa081b19b2f0a69b26941f8fec7" +source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -6380,7 +6381,7 @@ dependencies = [ [[package]] name = "revm-inspectors" version = "0.1.0" -source = "git+https://github.com/paradigmxyz/evm-inspectors?rev=9a91a10#9a91a107edd197a65a3fd55b3942948036a72b33" +source = "git+https://github.com/paradigmxyz/evm-inspectors?rev=ee6f6be#ee6f6be412c61a494472468c37bc7b8bc292b458" dependencies = [ "alloy-primitives", "alloy-rpc-types", diff --git a/Cargo.toml b/Cargo.toml index bd96efd2e..c89a9e0ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -144,7 +144,7 @@ foundry-compilers = { version = "0.4.0", default-features = false } # no default features to avoid c-kzg revm = { version = "8", default-features = false } revm-primitives = { version = "3", default-features = false } -revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "9a91a10", features = [ +revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "ee6f6be", features = [ "serde", ] } @@ -152,29 +152,29 @@ revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = ethers-contract-abigen = { version = "2.0.14", default-features = false } ## alloy -alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } -alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } -alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } -alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } -alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } -alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } -alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } -alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } -alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } -alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } -alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } -alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } -alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } -alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } -alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } -alloy-signer-wallet = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } -alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } -alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } -alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } -alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } -alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } -alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } -alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "05af0de", default-features = false } +alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } +alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } +alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } +alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } +alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } +alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } +alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } +alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } +alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } +alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } +alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } +alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } +alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } +alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } +alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } +alloy-signer-wallet = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } +alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } +alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } +alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } +alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } +alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } +alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } +alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } alloy-primitives = { version = "0.7.1", features = ["getrandom"] } alloy-dyn-abi = "0.7.1" alloy-json-abi = "0.7.1" diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index 999bfae4f..4c7e01a47 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -19,7 +19,6 @@ use anvil_core::eth::{ }; use foundry_evm::{ backend::DatabaseError, - inspectors::{TracingInspector, TracingInspectorConfig}, revm::{ interpreter::InstructionResult, primitives::{ @@ -191,11 +190,7 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' contract_address, traces, exit, - out: match out { - Some(Output::Call(b)) => Some(alloy_primitives::Bytes(b.0)), - Some(Output::Create(b, _)) => Some(alloy_primitives::Bytes(b.0)), - _ => None, - }, + out: out.map(Output::into_data), nonce: tx.nonce, gas_used: tx.gas_used, }; @@ -308,7 +303,7 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator let exec_result = { let mut evm = foundry_evm::utils::new_evm_with_inspector(&mut *self.db, env, &mut inspector); - if let Some(ref factory) = self.precompile_factory { + if let Some(factory) = &self.precompile_factory { inject_precompiles(&mut evm, factory.precompiles()); } @@ -333,9 +328,7 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator } // This will correspond to prevrandao not set, and it should never happen. // If it does, it's a bug. - e => { - panic!("Failed to execute transaction. This is a bug.\n {:?}", e) - } + e => panic!("failed to execute transaction: {e}"), } } } @@ -375,12 +368,7 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator out, gas_used: gas_used as u128, logs: logs.unwrap_or_default(), - traces: inspector - .tracer - .unwrap_or(TracingInspector::new(TracingInspectorConfig::all())) - .get_traces() - .clone() - .into_nodes(), + traces: inspector.tracer.map(|t| t.into_traces().into_nodes()).unwrap_or_default(), nonce, }; diff --git a/crates/anvil/src/eth/otterscan/types.rs b/crates/anvil/src/eth/otterscan/types.rs index 7fe337d97..72bcb0de1 100644 --- a/crates/anvil/src/eth/otterscan/types.rs +++ b/crates/anvil/src/eth/otterscan/types.rs @@ -9,7 +9,7 @@ use alloy_rpc_types_trace::parity::{ RewardAction, TraceOutput, }; use anvil_core::eth::transaction::ReceiptResponse; -use foundry_evm::{revm::interpreter::InstructionResult, traces::CallKind}; +use foundry_evm::traces::CallKind; use futures::future::join_all; use serde::Serialize; use serde_repr::Serialize_repr; @@ -73,7 +73,9 @@ pub struct OtsSearchTransactions { pub last_page: bool, } -/// Otterscan format for listing relevant internal operations +/// Otterscan format for listing relevant internal operations. +/// +/// Ref: #[derive(Debug, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct OtsInternalOperation { @@ -83,7 +85,9 @@ pub struct OtsInternalOperation { pub value: U256, } -/// Types of internal operations recognized by Otterscan +/// Types of internal operations recognized by Otterscan. +/// +/// Ref: #[derive(Debug, PartialEq, Serialize_repr)] #[repr(u8)] pub enum OtsInternalOperationType { @@ -102,6 +106,8 @@ pub struct OtsTrace { pub to: Address, pub value: U256, pub input: Bytes, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub output: Option, } /// The type of call being described by an Otterscan trace. Only CALL, STATICCALL and DELEGATECALL @@ -117,23 +123,28 @@ pub enum OtsTraceType { impl OtsBlockDetails { /// The response for ots_getBlockDetails includes an `issuance` object that requires computing /// the total gas spent in a given block. + /// /// The only way to do this with the existing API is to explicitly fetch all receipts, to get /// 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) 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. This has two problems though: + /// The [original spec](https://github.com/otterscan/otterscan/blob/develop/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. + /// + /// This has two problems though: /// - It makes the endpoint too specific to Otterscan's implementation /// - It breaks the abstraction built in `OtsBlock` which computes `transaction_count` /// based on the existing list. /// Therefore we keep it simple by keeping the data in the response pub async fn build(block: Block, backend: &Backend) -> Result { - let block_txs = match block.transactions.clone() { - BlockTransactions::Full(txs) => txs.into_iter().map(|tx| tx.hash).collect(), - BlockTransactions::Hashes(txs) => txs, - BlockTransactions::Uncle => return Err(BlockchainError::DataUnavailable), - }; - let receipts_futs = - block_txs.iter().map(|tx| async { backend.transaction_receipt(*tx).await }); + if block.transactions.is_uncle() { + return Err(BlockchainError::DataUnavailable); + } + let receipts_futs = block + .transactions + .hashes() + .map(|hash| async { backend.transaction_receipt(*hash).await }); // fetch all receipts let receipts = join_all(receipts_futs) @@ -162,32 +173,24 @@ impl OtsBlockDetails { /// which includes the `transaction_count` field impl From for OtsBlock { fn from(block: Block) -> Self { - let transaction_count = match block.transactions { - BlockTransactions::Full(ref txs) => txs.len(), - BlockTransactions::Hashes(ref txs) => txs.len(), - BlockTransactions::Uncle => 0, - }; - - Self { block, transaction_count } + Self { transaction_count: block.transactions.len(), block } } } impl OtsBlockTransactions { - /// Fetches all receipts for the blocks's transactions, as required by the [`ots_getBlockTransactions`](https://github.com/otterscan/otterscan/blob/develop/docs/custom-jsonrpc.md#ots_getblockdetails) endpoint spec, and returns the final response object. + /// 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 pub async fn build( mut block: Block, backend: &Backend, page: usize, page_size: usize, ) -> Result { - let block_txs = match block.transactions.clone() { - BlockTransactions::Full(txs) => txs.into_iter().map(|tx| tx.hash).collect(), - BlockTransactions::Hashes(txs) => txs, - BlockTransactions::Uncle => return Err(BlockchainError::DataUnavailable), - }; - - let block_txs = - block_txs.into_iter().skip(page * page_size).take(page_size).collect::>(); + if block.transactions.is_uncle() { + return Err(BlockchainError::DataUnavailable); + } block.transactions = match block.transactions { BlockTransactions::Full(txs) => BlockTransactions::Full( @@ -196,11 +199,11 @@ impl OtsBlockTransactions { BlockTransactions::Hashes(txs) => BlockTransactions::Hashes( txs.into_iter().skip(page * page_size).take(page_size).collect(), ), - BlockTransactions::Uncle => return Err(BlockchainError::DataUnavailable), + BlockTransactions::Uncle => unreachable!(), }; let receipt_futs = - block_txs.iter().map(|tx| async { backend.transaction_receipt(*tx).await }); + block.transactions.hashes().map(|hash| backend.transaction_receipt(*hash)); let receipts = join_all(receipt_futs) .await @@ -288,36 +291,22 @@ impl OtsInternalOperation { .traces .iter() .filter_map(|node| { - match (node.trace.kind, node.trace.status) { - (CallKind::Call, _) if node.trace.value != rU256::ZERO => Some(Self { - r#type: OtsInternalOperationType::Transfer, - from: node.trace.caller, - to: node.trace.address, - value: node.trace.value, - }), - (CallKind::Create, _) => Some(Self { - r#type: OtsInternalOperationType::Create, - from: node.trace.caller, - to: node.trace.address, - value: node.trace.value, - }), - (CallKind::Create2, _) => Some(Self { - r#type: OtsInternalOperationType::Create2, - from: node.trace.caller, - to: node.trace.address, - value: node.trace.value, - }), - (_, InstructionResult::SelfDestruct) => { - Some(Self { - r#type: OtsInternalOperationType::SelfDestruct, - from: node.trace.address, - // the foundry CallTraceNode doesn't have a refund address - to: Default::default(), - value: node.trace.value, - }) + let r#type = match node.trace.kind { + _ if node.is_selfdestruct() => OtsInternalOperationType::SelfDestruct, + CallKind::Call if node.trace.value != rU256::ZERO => { + OtsInternalOperationType::Transfer } - _ => None, + CallKind::Create => OtsInternalOperationType::Create, + CallKind::Create2 => OtsInternalOperationType::Create2, + _ => return None, + }; + let mut from = node.trace.caller; + let mut to = node.trace.address; + if node.is_selfdestruct() { + from = node.trace.address; + to = node.trace.selfdestruct_refund_target.unwrap_or_default(); } + Some(Self { r#type, from, to, value: node.trace.value }) }) .collect() } @@ -339,14 +328,13 @@ impl OtsTrace { to: call.to, value: call.value, input: call.input.0.into(), + output: None, }) } else { None } } - Action::Create(_) => None, - Action::Selfdestruct(_) => None, - Action::Reward(_) => None, + Action::Create(_) | Action::Selfdestruct(_) | Action::Reward(_) => None, }) .collect() } diff --git a/crates/anvil/src/hardfork.rs b/crates/anvil/src/hardfork.rs index e2f107b53..7d95f13e7 100644 --- a/crates/anvil/src/hardfork.rs +++ b/crates/anvil/src/hardfork.rs @@ -2,7 +2,7 @@ use alloy_rpc_types::BlockNumberOrTag; use foundry_evm::revm::primitives::SpecId; use std::str::FromStr; -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Hardfork { Frontier, Homestead, diff --git a/crates/anvil/tests/it/otterscan.rs b/crates/anvil/tests/it/otterscan.rs index f9fbd9123..573fc1c7e 100644 --- a/crates/anvil/tests/it/otterscan.rs +++ b/crates/anvil/tests/it/otterscan.rs @@ -9,7 +9,7 @@ use anvil::{ eth::otterscan::types::{ OtsInternalOperation, OtsInternalOperationType, OtsTrace, OtsTraceType, }, - spawn, NodeConfig, + spawn, Hardfork, NodeConfig, }; use std::collections::VecDeque; @@ -123,31 +123,53 @@ async fn ots_get_internal_operations_contract_create2() { } #[tokio::test(flavor = "multi_thread")] -async fn ots_get_internal_operations_contract_selfdestruct() { +async fn ots_get_internal_operations_contract_selfdestruct_london() { + ots_get_internal_operations_contract_selfdestruct(Hardfork::London).await; +} + +#[tokio::test(flavor = "multi_thread")] +async fn ots_get_internal_operations_contract_selfdestruct_cancun() { + ots_get_internal_operations_contract_selfdestruct(Hardfork::Cancun).await; +} + +async fn ots_get_internal_operations_contract_selfdestruct(hardfork: Hardfork) { sol!( - #[sol(rpc, bytecode = "60808060405234601357607c908160188239f35b5f80fdfe6004361015600b575f80fd5b5f3560e01c6375fc8e3c14601d575f80fd5b346042575f36600319011260425773dcdd539da22bffaa499dbea4d37d086dde196e75ff5b5f80fdfea26469706673582212201778e88721238015f480b3a901848b460799e91836caf557a8dcfdb2a1a2469e64736f6c63430008190033")] + #[sol(rpc, bytecode = "608080604052607f908160108239f3fe6004361015600c57600080fd5b6000803560e01c6375fc8e3c14602157600080fd5b346046578060031936011260465773dcdd539da22bffaa499dbea4d37d086dde196e75ff5b80fdfea264697066735822122080a9ad005cc408b2d4e30ca11216d8e310700fbcdf58a629d6edbb91531f9c6164736f6c63430008190033")] contract Contract { + constructor() payable {} function goodbye() public { selfdestruct(payable(0xDcDD539DA22bfFAa499dBEa4d37d086Dde196E75)); } } ); - let (api, handle) = spawn(NodeConfig::test()).await; + let (api, handle) = spawn(NodeConfig::test().with_hardfork(Some(hardfork))).await; let provider = handle.http_provider(); - let contract = Contract::deploy(&provider).await.unwrap(); + let sender = handle.dev_accounts().next().unwrap(); + let value = U256::from(69); + + let contract_address = + Contract::deploy_builder(&provider).from(sender).value(value).deploy().await.unwrap(); + let contract = Contract::new(contract_address, &provider); let receipt = contract.goodbye().send().await.unwrap().get_receipt().await.unwrap(); + // TODO: This is currently not supported by revm-inspectors + let (expected_to, expected_value) = if hardfork < Hardfork::Cancun { + (address!("DcDD539DA22bfFAa499dBEa4d37d086Dde196E75"), value) + } else { + (Address::ZERO, U256::ZERO) + }; + let res = api.ots_get_internal_operations(receipt.transaction_hash).await.unwrap(); assert_eq!( res, [OtsInternalOperation { r#type: OtsInternalOperationType::SelfDestruct, - from: *contract.address(), - to: Address::ZERO, - value: U256::from(0), + from: contract_address, + to: expected_to, + value: expected_value, }], ); } @@ -227,6 +249,7 @@ async fn test_call_ots_trace_transaction() { to: contract_address, value: U256::from(1337), input: Contract::runCall::SELECTOR.into(), + output: None, }, OtsTrace { r#type: OtsTraceType::StaticCall, @@ -235,6 +258,7 @@ async fn test_call_ots_trace_transaction() { to: contract_address, value: U256::ZERO, input: Contract::do_staticcallCall::SELECTOR.into(), + output: None, }, OtsTrace { r#type: OtsTraceType::Call, @@ -243,6 +267,7 @@ async fn test_call_ots_trace_transaction() { to: contract_address, value: U256::ZERO, input: Contract::do_callCall::SELECTOR.into(), + output: None, }, OtsTrace { r#type: OtsTraceType::Call, @@ -251,6 +276,7 @@ async fn test_call_ots_trace_transaction() { to: sender, value: U256::from(1337), input: Bytes::new(), + output: None, }, OtsTrace { r#type: OtsTraceType::DelegateCall, @@ -259,6 +285,7 @@ async fn test_call_ots_trace_transaction() { to: contract_address, value: U256::ZERO, input: Contract::do_delegatecallCall::SELECTOR.into(), + output: None, }, ]; assert_eq!(res, expected); From 7ce6c9b7c541e64c780c68b5ddf95ab3a6c3ddc1 Mon Sep 17 00:00:00 2001 From: Meet Mangukiya Date: Tue, 7 May 2024 20:47:39 +0530 Subject: [PATCH 269/430] feat(cheatcodes): add ens namehash cheatcode (#7882) * feat(cheatcodes): add ens namehash cheatcode * fix test * fix test * rename the cheatcode from namehash -> ensNamehash * update cheatcodes.json --- crates/cheatcodes/assets/cheatcodes.json | 20 ++++++++++++++++++++ crates/cheatcodes/spec/src/vm.rs | 4 ++++ crates/cheatcodes/src/utils.rs | 8 ++++++++ testdata/cheats/Vm.sol | 1 + testdata/default/cheats/EnsNamehash.t.sol | 15 +++++++++++++++ 5 files changed, 48 insertions(+) create mode 100644 testdata/default/cheats/EnsNamehash.t.sol diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 39b58ff5e..a461f614d 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -3671,6 +3671,26 @@ "status": "stable", "safety": "unsafe" }, + { + "func": { + "id": "ensNamehash", + "description": "Returns ENS namehash for provided string.", + "declaration": "function ensNamehash(string calldata name) external pure returns (bytes32);", + "visibility": "external", + "mutability": "pure", + "signature": "ensNamehash(string)", + "selector": "0x8c374c65", + "selectorBytes": [ + 140, + 55, + 76, + 101 + ] + }, + "group": "utilities", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "envAddress_0", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 4bbf63169..6e2fbd278 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -2135,6 +2135,10 @@ interface Vm { /// Encodes a `string` value to a base64url string. #[cheatcode(group = Utilities)] function toBase64URL(string calldata data) external pure returns (string memory); + + /// Returns ENS namehash for provided string. + #[cheatcode(group = Utilities)] + function ensNamehash(string calldata name) external pure returns (bytes32); } } diff --git a/crates/cheatcodes/src/utils.rs b/crates/cheatcodes/src/utils.rs index 71cfe6ae6..df1061346 100644 --- a/crates/cheatcodes/src/utils.rs +++ b/crates/cheatcodes/src/utils.rs @@ -11,6 +11,7 @@ use alloy_signer_wallet::{ LocalWallet, MnemonicBuilder, }; use alloy_sol_types::SolValue; +use foundry_common::ens::namehash; use foundry_evm_core::constants::DEFAULT_CREATE2_DEPLOYER; use k256::{ ecdsa::SigningKey, @@ -137,6 +138,13 @@ impl Cheatcode for computeCreate2Address_1Call { } } +impl Cheatcode for ensNamehashCall { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { name } = self; + Ok(namehash(name).abi_encode()) + } +} + /// Using a given private key, return its public ETH address, its public key affine x and y /// coordinates, and its private key (see the 'Wallet' struct) /// diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index db8e45ada..45c720e6c 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -179,6 +179,7 @@ interface Vm { function deriveKey(string calldata mnemonic, string calldata derivationPath, uint32 index, string calldata language) external pure returns (uint256 privateKey); function difficulty(uint256 newDifficulty) external; function dumpState(string calldata pathToStateJson) external; + function ensNamehash(string calldata name) external pure returns (bytes32); function envAddress(string calldata name) external view returns (address value); function envAddress(string calldata name, string calldata delim) external view returns (address[] memory value); function envBool(string calldata name) external view returns (bool value); diff --git a/testdata/default/cheats/EnsNamehash.t.sol b/testdata/default/cheats/EnsNamehash.t.sol new file mode 100644 index 000000000..2d66beea4 --- /dev/null +++ b/testdata/default/cheats/EnsNamehash.t.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract EnsNamehashTest is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + function testEnsNamehash() public { + assertEq(vm.ensNamehash(""), 0x0000000000000000000000000000000000000000000000000000000000000000); + assertEq(vm.ensNamehash("eth"), 0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae); + assertEq(vm.ensNamehash("foo.eth"), 0xde9b09fd7c5f901e23a3f19fecc54828e9c848539801e86591bd9801b019f84f); + } +} From 874ea8cd3f8bf3545d210efd7b9f4c41ff36218a Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 7 May 2024 18:31:28 +0200 Subject: [PATCH 270/430] chore: bump foundry-compilers (#7883) --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- crates/verify/src/etherscan/flatten.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d327c38fb..88c1db3e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3425,9 +3425,9 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76463dbe20b1a0830acc74ef8ed59d9d9392f2aed21b0542001dc5a1a725b8e" +checksum = "f97f9de33410d0daf13834f818a8594c0ed277c848af750e40a9f28e67d62e3a" dependencies = [ "alloy-json-abi", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index c89a9e0ed..122f7f3d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -138,7 +138,7 @@ foundry-linking = { path = "crates/linking" } # solc & compilation utilities foundry-block-explorers = { version = "0.2.7", default-features = false } -foundry-compilers = { version = "0.4.0", default-features = false } +foundry-compilers = { version = "0.4.1", default-features = false } ## revm # no default features to avoid c-kzg diff --git a/crates/verify/src/etherscan/flatten.rs b/crates/verify/src/etherscan/flatten.rs index ca028fdab..ee0f95d83 100644 --- a/crates/verify/src/etherscan/flatten.rs +++ b/crates/verify/src/etherscan/flatten.rs @@ -77,7 +77,7 @@ impl EtherscanFlattenedSource { settings: Default::default(), }; - let (_, out) = Compiler::compile(&solc, input)?; + let out = Compiler::compile(&solc, &input)?; if out.errors.iter().any(|e| e.is_error()) { let mut o = AggregatedCompilerOutput::default(); o.extend(version, out); From 58c0c006d08eb43bc96f6d23516e43c249311eea Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Tue, 7 May 2024 13:05:12 -0400 Subject: [PATCH 271/430] fix(anvil): `tx.gas_price()` return val (#7876) * fix(anvil): cmp 1559 fees with basefee in 4844 tx not blobfee * fix(anvil): TypedTx - return 1559 fees in `.gas_price()` in case of 4844 --- crates/anvil/core/src/eth/transaction/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 7e2fc71da..c7bd58054 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -617,7 +617,7 @@ impl TypedTransaction { TypedTransaction::Legacy(tx) => tx.tx().gas_price, TypedTransaction::EIP2930(tx) => tx.tx().gas_price, TypedTransaction::EIP1559(tx) => tx.tx().max_fee_per_gas, - TypedTransaction::EIP4844(tx) => tx.tx().tx().max_fee_per_blob_gas, + TypedTransaction::EIP4844(tx) => tx.tx().tx().max_fee_per_gas, TypedTransaction::Deposit(_) => 0, } } From 936238b0719de7a1520a29cc4747263670d3d683 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 8 May 2024 13:36:33 +0200 Subject: [PATCH 272/430] chore: remove `cast bind` (#7887) --- Cargo.lock | 1 - crates/cast/Cargo.toml | 2 -- crates/cast/bin/cmd/bind.rs | 59 +++++-------------------------------- crates/wallets/src/utils.rs | 16 ++++------ 4 files changed, 14 insertions(+), 64 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 88c1db3e3..9520dd1d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1753,7 +1753,6 @@ dependencies = [ "const-hex", "criterion", "dunce", - "ethers-contract-abigen", "evm-disassembler", "evmole", "eyre", diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index 5d4405ae5..59ac39e26 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -42,8 +42,6 @@ alloy-network.workspace = true alloy-sol-types.workspace = true alloy-chains.workspace = true -ethers-contract-abigen.workspace = true - chrono.workspace = true evm-disassembler.workspace = true eyre.workspace = true diff --git a/crates/cast/bin/cmd/bind.rs b/crates/cast/bin/cmd/bind.rs index a83afc8ae..942c441f6 100644 --- a/crates/cast/bin/cmd/bind.rs +++ b/crates/cast/bin/cmd/bind.rs @@ -1,13 +1,10 @@ use clap::{Parser, ValueHint}; -use ethers_contract_abigen::{Abigen, MultiAbigen}; use eyre::Result; -use foundry_block_explorers::{errors::EtherscanError, Client}; use foundry_cli::opts::EtherscanOpts; -use foundry_config::Config; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; -static DEFAULT_CRATE_NAME: &str = "foundry-contracts"; -static DEFAULT_CRATE_VERSION: &str = "0.0.1"; +const DEFAULT_CRATE_NAME: &str = "foundry-contracts"; +const DEFAULT_CRATE_VERSION: &str = "0.0.1"; /// CLI arguments for `cast bind`. #[derive(Clone, Debug, Parser)] @@ -58,50 +55,10 @@ pub struct BindArgs { impl BindArgs { pub async fn run(self) -> Result<()> { - let path = Path::new(&self.path_or_address); - let multi = if path.exists() { - MultiAbigen::from_json_files(path) - } else { - self.abigen_etherscan().await - }?; - - println!("Generating bindings for {} contracts", multi.len()); - let bindings = multi.build()?; - - let out = self - .output_dir - .clone() - .unwrap_or_else(|| std::env::current_dir().unwrap().join("bindings")); - bindings.write_to_crate(self.crate_name, self.crate_version, out, !self.separate_files)?; - Ok(()) - } - - async fn abigen_etherscan(&self) -> Result { - let config = Config::from(&self.etherscan); - - let chain = config.chain.unwrap_or_default(); - let api_key = config.get_etherscan_api_key(Some(chain)).unwrap_or_default(); - - let client = Client::new(chain, api_key)?; - let address = self.path_or_address.parse()?; - let source = match client.contract_source_code(address).await { - Ok(source) => source, - Err(EtherscanError::InvalidApiKey) => { - eyre::bail!("Invalid Etherscan API key. Did you set it correctly? You may be using an API key for another Etherscan API chain (e.g. Etherscan API key for Polygonscan).") - } - Err(EtherscanError::ContractCodeNotVerified(address)) => { - eyre::bail!("Contract source code at {:?} on {} not verified. Maybe you have selected the wrong chain?", address, chain) - } - Err(err) => { - eyre::bail!(err) - } - }; - let abigens = source - .items - .into_iter() - .map(|item| Abigen::new(item.contract_name, item.abi).unwrap()) - .collect::>(); - - Ok(MultiAbigen::from_abigens(abigens)) + Err(eyre::eyre!( + "`cast bind` has been removed.\n\ + Please use `cast etherscan-source` to create a Forge project from an Etherscan source\n\ + and `forge bind` to generate the bindings to it instead." + )) } } diff --git a/crates/wallets/src/utils.rs b/crates/wallets/src/utils.rs index 898a82fdc..eaaf81e45 100644 --- a/crates/wallets/src/utils.rs +++ b/crates/wallets/src/utils.rs @@ -5,6 +5,7 @@ use alloy_signer_trezor::HDPath as TrezorHDPath; use alloy_signer_wallet::LocalWallet; use eyre::{Context, Result}; use foundry_config::Config; +use hex::FromHex; use std::{ fs, path::{Path, PathBuf}, @@ -18,20 +19,15 @@ fn ensure_pk_not_env(pk: &str) -> Result<()> { } /// Validates and sanitizes user inputs, returning configured [WalletSigner]. -pub fn create_private_key_signer(private_key: &str) -> Result { - let privk = private_key.trim().strip_prefix("0x").unwrap_or(private_key); - - let Ok(private_key) = hex::decode(privk) else { - ensure_pk_not_env(privk)?; +pub fn create_private_key_signer(private_key_str: &str) -> Result { + let Ok(private_key) = B256::from_hex(private_key_str) else { + ensure_pk_not_env(private_key_str)?; eyre::bail!("Failed to decode private key") }; - - match LocalWallet::from_bytes( - &B256::try_from(private_key.as_slice()).wrap_err("Failed to decode private key")?, - ) { + match LocalWallet::from_bytes(&private_key) { Ok(pk) => Ok(WalletSigner::Local(pk)), Err(err) => { - ensure_pk_not_env(privk)?; + ensure_pk_not_env(private_key_str)?; eyre::bail!("Failed to create wallet from private key: {err}") } } From a4d79ac5495b6294610c0a0bd5e4e13efdca13ee Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Wed, 8 May 2024 08:01:57 -0400 Subject: [PATCH 273/430] chore: deprecate `forge bind --ethers` (#7886) Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- crates/chisel/bin/main.rs | 7 +- crates/common/src/fs.rs | 15 ++-- crates/forge/bin/cmd/bind.rs | 137 +++++++++++++++++++------------ crates/forge/tests/cli/script.rs | 8 +- 4 files changed, 97 insertions(+), 70 deletions(-) diff --git a/crates/chisel/bin/main.rs b/crates/chisel/bin/main.rs index 706c6af99..d2b3a726f 100644 --- a/crates/chisel/bin/main.rs +++ b/crates/chisel/bin/main.rs @@ -272,10 +272,11 @@ async fn evaluate_prelude( load_prelude_file(dispatcher, prelude_dir).await?; println!("{}\n", "Prelude source file loaded successfully!".green()); } else { - let prelude_sources = fs::files_with_ext(prelude_dir, "sol"); - let print_success_msg = !prelude_sources.is_empty(); + let prelude_sources = fs::files_with_ext(&prelude_dir, "sol"); + let mut print_success_msg = false; for source_file in prelude_sources { - println!("{} {}", "Loading prelude source file:".yellow(), source_file.display(),); + print_success_msg = true; + println!("{} {}", "Loading prelude source file:".yellow(), source_file.display()); load_prelude_file(dispatcher, source_file).await?; } diff --git a/crates/common/src/fs.rs b/crates/common/src/fs.rs index 83b98670a..8ee47d2fd 100644 --- a/crates/common/src/fs.rs +++ b/crates/common/src/fs.rs @@ -133,19 +133,18 @@ pub fn normalize_path(path: &Path) -> PathBuf { ret } -/// Returns all files with the given extension under the `root` dir -pub fn files_with_ext(root: impl AsRef, ext: &str) -> Vec { +/// Returns an iterator over all files with the given extension under the `root` dir. +pub fn files_with_ext<'a>(root: &Path, ext: &'a str) -> impl Iterator + 'a { walkdir::WalkDir::new(root) + .sort_by_file_name() .into_iter() .filter_map(walkdir::Result::ok) - .filter(|e| e.file_type().is_file()) - .filter(|e| e.path().extension().map(|e| e == ext).unwrap_or_default()) - .map(|e| e.path().into()) - .collect() + .filter(|e| e.file_type().is_file() && e.path().extension() == Some(ext.as_ref())) + .map(walkdir::DirEntry::into_path) } -/// Returns a list of absolute paths to all the json files under the root -pub fn json_files(root: impl AsRef) -> Vec { +/// Returns an iterator over all JSON files under the `root` dir. +pub fn json_files(root: &Path) -> impl Iterator { files_with_ext(root, "json") } diff --git a/crates/forge/bin/cmd/bind.rs b/crates/forge/bin/cmd/bind.rs index 11f855f04..3643642e7 100644 --- a/crates/forge/bin/cmd/bind.rs +++ b/crates/forge/bin/cmd/bind.rs @@ -83,6 +83,14 @@ pub struct BindArgs { #[arg(long)] skip_extra_derives: bool, + /// Generate bindings for the `alloy` library, instead of `ethers`. + #[arg(skip)] + alloy: bool, + + /// Generate bindings for the `ethers` library, instead of `alloy` (default, deprecated). + #[arg(long)] + ethers: bool, + #[command(flatten)] build_args: CoreBuildArgs, } @@ -90,42 +98,40 @@ pub struct BindArgs { impl BindArgs { pub fn run(self) -> Result<()> { if !self.skip_build { - // run `forge build` let project = self.build_args.project()?; let _ = ProjectCompiler::new().compile(&project)?; } - let artifacts = self.try_load_config_emit_warnings()?.out; - - if !self.overwrite && self.bindings_exist(&artifacts) { - println!("Bindings found. Checking for consistency."); - return self.check_existing_bindings(&artifacts) + if !self.alloy { + eprintln!("Warning: ethers bindings are deprecated and will be removed in the future"); + /* + eprintln!( + "Warning: `--ethers` (default) bindings are deprecated and will be removed in the future. \ + Consider using `--alloy` instead." + ); + */ } - if self.overwrite && self.bindings_exist(&artifacts) { + let config = self.try_load_config_emit_warnings()?; + let artifacts = config.out; + let bindings_root = self.bindings.clone().unwrap_or_else(|| artifacts.join("bindings")); + + if bindings_root.exists() { + if !self.overwrite { + println!("Bindings found. Checking for consistency."); + return self.check_existing_bindings(&artifacts, &bindings_root); + } + trace!(?artifacts, "Removing existing bindings"); - fs::remove_dir_all(self.bindings_root(&artifacts))?; + fs::remove_dir_all(&bindings_root)?; } - self.generate_bindings(&artifacts)?; + self.generate_bindings(&artifacts, &bindings_root)?; - println!( - "Bindings have been output to {}", - self.bindings_root(&artifacts).to_str().unwrap() - ); + println!("Bindings have been generated to {}", bindings_root.display()); Ok(()) } - /// Get the path to the root of the autogenerated crate - fn bindings_root(&self, artifacts: impl AsRef) -> PathBuf { - self.bindings.clone().unwrap_or_else(|| artifacts.as_ref().join("bindings")) - } - - /// `true` if the bindings root already exists - fn bindings_exist(&self, artifacts: impl AsRef) -> bool { - self.bindings_root(artifacts).is_dir() - } - /// Returns the filter to use for `MultiAbigen` fn get_filter(&self) -> ContractFilter { if self.select_all { @@ -152,26 +158,39 @@ impl BindArgs { .into() } - /// Instantiate the multi-abigen - fn get_multi(&self, artifacts: impl AsRef) -> Result { - let abigens = json_files(artifacts.as_ref()) - .into_iter() + /// Returns an iterator over the JSON files and the contract name in the `artifacts` directory. + fn get_json_files(&self, artifacts: &Path) -> impl Iterator { + let filter = self.get_filter(); + json_files(artifacts) .filter_map(|path| { - if path.to_string_lossy().contains("/build-info/") { - // ignore the build info json - return None + // Ignore the build info JSON. + if path.to_str()?.contains("/build-info/") { + return None; } - // we don't want `.metadata.json files - let stem = path.file_stem()?; - if stem.to_str()?.ends_with(".metadata") { - None - } else { - Some(path) + + // We don't want `.metadata.json` files. + let stem = path.file_stem()?.to_str()?; + if stem.ends_with(".metadata") { + return None; } + + let name = stem.split('.').next().unwrap(); + + // Best effort identifier cleanup. + let name = name.replace(char::is_whitespace, "").replace('-', "_"); + + Some((name, path)) }) - .map(|path| { + .filter(move |(name, _path)| 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::from_file(&path) + 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") @@ -180,27 +199,29 @@ impl BindArgs { } }) .collect::, _>>()?; - let multi = MultiAbigen::from_abigens(abigens).with_filter(self.get_filter()); - - eyre::ensure!( - !multi.is_empty(), - r#" -No contract artifacts found. Hint: Have you built your contracts yet? `forge bind` does not currently invoke `forge build`, although this is planned for future versions. - "# - ); + let multi = MultiAbigen::from_abigens(abigens); + eyre::ensure!(!multi.is_empty(), "No contract artifacts found"); Ok(multi) } /// Check that the existing bindings match the expected abigen output - fn check_existing_bindings(&self, artifacts: impl AsRef) -> Result<()> { - let bindings = self.get_multi(&artifacts)?.build()?; + fn check_existing_bindings(&self, artifacts: &Path, bindings_root: &Path) -> Result<()> { + if !self.alloy { + return self.check_ethers(artifacts, bindings_root); + } + + todo!("alloy") + } + + fn check_ethers(&self, artifacts: &Path, bindings_root: &Path) -> Result<()> { + let bindings = self.get_multi(artifacts)?.build()?; println!("Checking bindings for {} contracts.", bindings.len()); if !self.module { bindings .ensure_consistent_crate( &self.crate_name, &self.crate_version, - self.bindings_root(&artifacts), + bindings_root, self.single_file, !self.skip_cargo_toml, ) @@ -212,15 +233,23 @@ No contract artifacts found. Hint: Have you built your contracts yet? `forge bin } })?; } else { - bindings.ensure_consistent_module(self.bindings_root(&artifacts), self.single_file)?; + bindings.ensure_consistent_module(bindings_root, self.single_file)?; } println!("OK."); Ok(()) } /// Generate the bindings - fn generate_bindings(&self, artifacts: impl AsRef) -> Result<()> { - let mut bindings = self.get_multi(&artifacts)?.build()?; + fn generate_bindings(&self, artifacts: &Path, bindings_root: &Path) -> Result<()> { + if !self.alloy { + return self.generate_ethers(artifacts, bindings_root); + } + + todo!("alloy") + } + + fn generate_ethers(&self, artifacts: &Path, bindings_root: &Path) -> Result<()> { + let mut bindings = self.get_multi(artifacts)?.build()?; println!("Generating bindings for {} contracts", bindings.len()); if !self.module { trace!(single_file = self.single_file, "generating crate"); @@ -230,12 +259,12 @@ No contract artifacts found. Hint: Have you built your contracts yet? `forge bin bindings.write_to_crate( &self.crate_name, &self.crate_version, - self.bindings_root(&artifacts), + bindings_root, self.single_file, ) } else { trace!(single_file = self.single_file, "generating module"); - bindings.write_to_module(self.bindings_root(&artifacts), self.single_file) + bindings.write_to_module(bindings_root, self.single_file) } } } diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index eee345595..9f1a12c4e 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -808,9 +808,8 @@ contract Script0 is Script { assert!(cmd.stdout_lossy().contains("SIMULATION COMPLETE")); - let run_latest = foundry_common::fs::json_files(prj.root().join("broadcast")) - .into_iter() - .find(|file| file.ends_with("run-latest.json")) + let run_latest = foundry_common::fs::json_files(&prj.root().join("broadcast")) + .find(|path| path.ends_with("run-latest.json")) .expect("No broadcast artifacts"); let content = foundry_common::fs::read_to_string(run_latest).unwrap(); @@ -893,8 +892,7 @@ contract Script0 is Script { assert!(cmd.stdout_lossy().contains("SIMULATION COMPLETE")); - let run_latest = foundry_common::fs::json_files(prj.root().join("broadcast")) - .into_iter() + let run_latest = foundry_common::fs::json_files(&prj.root().join("broadcast")) .find(|file| file.ends_with("run-latest.json")) .expect("No broadcast artifacts"); From 3ef5466d0e917e6ea783433a44da3feaf1ce7055 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 10 May 2024 09:57:46 +0200 Subject: [PATCH 274/430] fix(cast): create transactions (#7904) --- crates/cast/bin/cmd/access_list.rs | 7 ++++++- crates/cast/bin/cmd/call.rs | 7 ++++++- crates/cast/bin/cmd/estimate.rs | 7 ++++++- crates/cast/bin/tx.rs | 12 +++++++----- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/crates/cast/bin/cmd/access_list.rs b/crates/cast/bin/cmd/access_list.rs index d344017f6..0e8fd042d 100644 --- a/crates/cast/bin/cmd/access_list.rs +++ b/crates/cast/bin/cmd/access_list.rs @@ -105,11 +105,16 @@ async fn access_list, T: Transport + Clone>( to_json: bool, ) -> Result<()> { let mut req = WithOtherFields::::default() - .with_to(to.unwrap_or_default()) .with_from(from) .with_value(tx.value.unwrap_or_default()) .with_chain_id(chain.id()); + if let Some(to) = to { + req.set_to(to); + } else { + req.set_kind(alloy_primitives::TxKind::Create); + } + if let Some(gas_limit) = tx.gas_limit { req.set_gas_limit(gas_limit.to()); } diff --git a/crates/cast/bin/cmd/call.rs b/crates/cast/bin/cmd/call.rs index 2caa54d2a..129e3da30 100644 --- a/crates/cast/bin/cmd/call.rs +++ b/crates/cast/bin/cmd/call.rs @@ -122,10 +122,15 @@ impl CallArgs { }; let mut req = WithOtherFields::::default() - .with_to(to.unwrap_or_default()) .with_from(sender) .with_value(tx.value.unwrap_or_default()); + if let Some(to) = to { + req.set_to(to); + } else { + req.set_kind(alloy_primitives::TxKind::Create); + } + if let Some(nonce) = tx.nonce { req.set_nonce(nonce.to()); } diff --git a/crates/cast/bin/cmd/estimate.rs b/crates/cast/bin/cmd/estimate.rs index b0f0d6fcf..33f375f9c 100644 --- a/crates/cast/bin/cmd/estimate.rs +++ b/crates/cast/bin/cmd/estimate.rs @@ -94,10 +94,15 @@ impl EstimateArgs { }; let mut req = WithOtherFields::::default() - .with_to(to.unwrap_or_default()) .with_from(from) .with_value(value.unwrap_or_default()); + if let Some(to) = to { + req.set_to(to); + } else { + req.set_kind(alloy_primitives::TxKind::Create); + } + let data = match command { Some(EstimateSubcommands::Create { code, sig, args, value }) => { if let Some(value) = value { diff --git a/crates/cast/bin/tx.rs b/crates/cast/bin/tx.rs index 1ad0ee2a9..64b33b280 100644 --- a/crates/cast/bin/tx.rs +++ b/crates/cast/bin/tx.rs @@ -57,15 +57,17 @@ pub async fn build_tx< let from = from.into().resolve(provider).await?; - let to: Option
= - if let Some(to) = to { Some(to.into().resolve(provider).await?) } else { None }; - - let mut req = WithOtherFields::new(TransactionRequest::default()) - .with_to(to.unwrap_or_default()) + let mut req = WithOtherFields::::default() .with_from(from) .with_value(tx.value.unwrap_or_default()) .with_chain_id(chain.id()); + if let Some(to) = to { + req.set_to(to.into().resolve(provider).await?); + } else { + req.set_kind(alloy_primitives::TxKind::Create); + } + req.set_nonce(if let Some(nonce) = tx.nonce { nonce.to() } else { From 503792a1dbadd901a4c02f6fcd1de1caff1573ff Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 10 May 2024 13:23:10 +0200 Subject: [PATCH 275/430] chore: bump alloy 899fc51 (#7905) * chore: bump alloy 899fc51 * fixes * fixes * update test --- Cargo.lock | 46 +++++++++++++------------- Cargo.toml | 48 ++++++++++++++-------------- crates/anvil/src/eth/backend/fork.rs | 9 +++--- crates/anvil/tests/it/fork.rs | 2 +- crates/anvil/tests/it/transaction.rs | 4 +-- crates/cast/bin/cmd/run.rs | 3 +- crates/cast/src/lib.rs | 6 +++- crates/common/src/transactions.rs | 3 +- crates/evm/core/src/fork/backend.rs | 5 ++- crates/verify/src/bytecode.rs | 9 ++++-- crates/verify/src/etherscan/mod.rs | 5 ++- 11 files changed, 79 insertions(+), 61 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9520dd1d4..7a352ef67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,7 +79,7 @@ dependencies = [ [[package]] name = "alloy-consensus" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-eips", "alloy-primitives", @@ -92,7 +92,7 @@ dependencies = [ [[package]] name = "alloy-contract" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -132,7 +132,7 @@ dependencies = [ [[package]] name = "alloy-eips" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -147,7 +147,7 @@ dependencies = [ [[package]] name = "alloy-genesis" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-primitives", "alloy-serde", @@ -170,7 +170,7 @@ dependencies = [ [[package]] name = "alloy-json-rpc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-primitives", "serde", @@ -182,7 +182,7 @@ dependencies = [ [[package]] name = "alloy-network" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-consensus", "alloy-eips", @@ -226,7 +226,7 @@ dependencies = [ [[package]] name = "alloy-provider" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-eips", "alloy-json-rpc", @@ -257,7 +257,7 @@ dependencies = [ [[package]] name = "alloy-pubsub" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -297,7 +297,7 @@ dependencies = [ [[package]] name = "alloy-rpc-client" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -321,7 +321,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-consensus", "alloy-eips", @@ -339,7 +339,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-consensus", "alloy-eips", @@ -356,7 +356,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -368,7 +368,7 @@ dependencies = [ [[package]] name = "alloy-serde" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-primitives", "serde", @@ -378,7 +378,7 @@ dependencies = [ [[package]] name = "alloy-signer" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -393,7 +393,7 @@ dependencies = [ [[package]] name = "alloy-signer-aws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-consensus", "alloy-network", @@ -410,7 +410,7 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -429,7 +429,7 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-consensus", "alloy-network", @@ -445,7 +445,7 @@ dependencies = [ [[package]] name = "alloy-signer-wallet" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-consensus", "alloy-network", @@ -522,7 +522,7 @@ dependencies = [ [[package]] name = "alloy-transport" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-json-rpc", "base64 0.22.1", @@ -540,7 +540,7 @@ dependencies = [ [[package]] name = "alloy-transport-http" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -554,7 +554,7 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -574,7 +574,7 @@ dependencies = [ [[package]] name = "alloy-transport-ws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=77c1240#77c1240533b411ed0eb5533f94396eba8d7f6ab6" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -6380,7 +6380,7 @@ dependencies = [ [[package]] name = "revm-inspectors" version = "0.1.0" -source = "git+https://github.com/paradigmxyz/evm-inspectors?rev=ee6f6be#ee6f6be412c61a494472468c37bc7b8bc292b458" +source = "git+https://github.com/paradigmxyz/evm-inspectors?rev=c1b5dd0#c1b5dd0d85dd46ef5ec5258aebd24adc041d103a" dependencies = [ "alloy-primitives", "alloy-rpc-types", diff --git a/Cargo.toml b/Cargo.toml index 122f7f3d9..b2932bcb6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -144,7 +144,7 @@ foundry-compilers = { version = "0.4.1", default-features = false } # no default features to avoid c-kzg revm = { version = "8", default-features = false } revm-primitives = { version = "3", default-features = false } -revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "ee6f6be", features = [ +revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "c1b5dd0", features = [ "serde", ] } @@ -152,29 +152,29 @@ revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = ethers-contract-abigen = { version = "2.0.14", default-features = false } ## alloy -alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } -alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } -alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } -alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } -alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } -alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } -alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } -alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } -alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } -alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } -alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } -alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } -alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } -alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } -alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } -alloy-signer-wallet = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } -alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } -alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } -alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } -alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } -alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } -alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } -alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "77c1240", default-features = false } +alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-signer-wallet = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } alloy-primitives = { version = "0.7.1", features = ["getrandom"] } alloy-dyn-abi = "0.7.1" alloy-json-abi = "0.7.1" diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index 8b1ed0655..a9cc1cd9d 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -316,10 +316,11 @@ impl ClientFork { } let tx = self.provider().get_transaction_by_hash(hash).await?; - - let mut storage = self.storage_write(); - storage.transactions.insert(hash, tx.clone()); - Ok(Some(tx)) + if let Some(tx) = tx.clone() { + let mut storage = self.storage_write(); + storage.transactions.insert(hash, tx); + } + Ok(tx) } pub async fn trace_transaction(&self, hash: B256) -> Result, TransportError> { diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index 3af626962..067934d8a 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -488,7 +488,7 @@ async fn can_reset_properly() { assert_eq!(to_balance, fork_provider.get_balance(to, BlockId::latest()).await.unwrap()); // tx does not exist anymore - assert!(fork_tx_provider.get_transaction_by_hash(tx.transaction_hash).await.is_err()) + assert!(fork_tx_provider.get_transaction_by_hash(tx.transaction_hash).await.unwrap().is_none()) } #[tokio::test(flavor = "multi_thread")] diff --git a/crates/anvil/tests/it/transaction.rs b/crates/anvil/tests/it/transaction.rs index 90ede1d73..eac152a51 100644 --- a/crates/anvil/tests/it/transaction.rs +++ b/crates/anvil/tests/it/transaction.rs @@ -695,9 +695,9 @@ async fn can_get_pending_transaction() { assert!(pending.is_ok()); api.mine_one().await; - let mined = provider.get_transaction_by_hash(*tx.tx_hash()).await.unwrap(); + let mined = provider.get_transaction_by_hash(*tx.tx_hash()).await.unwrap().unwrap(); - assert_eq!(mined.hash, pending.unwrap().hash); + assert_eq!(mined.hash, pending.unwrap().unwrap().hash); } #[tokio::test(flavor = "multi_thread")] diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index 599d73247..35ab17ec4 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -100,7 +100,8 @@ impl RunArgs { let tx = provider .get_transaction_by_hash(tx_hash) .await - .wrap_err_with(|| format!("tx not found: {:?}", tx_hash))?; + .wrap_err_with(|| format!("tx not found: {:?}", tx_hash))? + .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) { diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index 67048fda7..d7fa2a9f9 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -635,7 +635,11 @@ where to_json: bool, ) -> Result { let tx_hash = TxHash::from_str(&tx_hash).wrap_err("invalid tx hash")?; - let tx = self.provider.get_transaction_by_hash(tx_hash).await?; + let tx = self + .provider + .get_transaction_by_hash(tx_hash) + .await? + .ok_or_else(|| eyre::eyre!("tx not found: {:?}", tx_hash))?; Ok(if raw { format!("0x{}", hex::encode(TxEnvelope::try_from(tx.inner)?.encoded_2718())) diff --git a/crates/common/src/transactions.rs b/crates/common/src/transactions.rs index 8ec95179f..98244ddb4 100644 --- a/crates/common/src/transactions.rs +++ b/crates/common/src/transactions.rs @@ -44,7 +44,8 @@ impl TransactionReceiptWithRevertReason { let transaction = provider .get_transaction_by_hash(self.receipt.transaction_hash) .await - .map_err(|_| eyre::eyre!("unable to fetch transaction"))?; + .map_err(|err| eyre::eyre!("unable to fetch transaction: {err}"))? + .ok_or_else(|| eyre::eyre!("transaction not found"))?; if let Some(block_hash) = self.receipt.block_hash { match provider diff --git a/crates/evm/core/src/fork/backend.rs b/crates/evm/core/src/fork/backend.rs index b60f00e5c..2ab9bdc9b 100644 --- a/crates/evm/core/src/fork/backend.rs +++ b/crates/evm/core/src/fork/backend.rs @@ -245,7 +245,10 @@ where let block = provider .get_transaction_by_hash(tx) .await - .wrap_err("could not get transaction {tx}"); + .wrap_err_with(|| format!("could not get transaction {tx}")) + .and_then(|maybe| { + maybe.ok_or_else(|| eyre::eyre!("could not get transaction {tx}")) + }); (sender, block, tx) }); diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index 484b77801..53654a7d3 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -184,7 +184,10 @@ impl VerifyBytecodeArgs { let mut transaction = provider .get_transaction_by_hash(creation_data.transaction_hash) .await - .or_else(|e| eyre::bail!("Couldn't fetch transaction from RPC: {:?}", e))?; + .or_else(|e| eyre::bail!("Couldn't fetch transaction from RPC: {:?}", e))? + .ok_or_else(|| { + eyre::eyre!("Transaction not found for hash {}", creation_data.transaction_hash) + })?; let receipt = provider .get_transaction_receipt(creation_data.transaction_hash) .await @@ -262,7 +265,9 @@ impl VerifyBytecodeArgs { let provider = utils::get_provider(&config)?; provider .get_transaction_by_hash(creation_data.transaction_hash) - .await.or_else(|e| eyre::bail!("Couldn't fetch transaction from RPC: {:?}", e))? + .await.or_else(|e| eyre::bail!("Couldn't fetch transaction from RPC: {:?}", e))?.ok_or_else(|| { + eyre::eyre!("Transaction not found for hash {}", creation_data.transaction_hash) + })? .block_number.ok_or_else(|| { eyre::eyre!("Failed to get block number of the contract creation tx, specify using the --block flag") })? diff --git a/crates/verify/src/etherscan/mod.rs b/crates/verify/src/etherscan/mod.rs index 33dea487e..8ebe0dc46 100644 --- a/crates/verify/src/etherscan/mod.rs +++ b/crates/verify/src/etherscan/mod.rs @@ -498,7 +498,10 @@ impl EtherscanVerificationProvider { )?; let creation_data = client.contract_creation_data(args.address).await?; - let transaction = provider.get_transaction_by_hash(creation_data.transaction_hash).await?; + let transaction = provider + .get_transaction_by_hash(creation_data.transaction_hash) + .await? + .ok_or_eyre("Transaction not found")?; let receipt = provider .get_transaction_receipt(creation_data.transaction_hash) .await? From 8d724584b3bd37260ef2b864c9ac69f55531670c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 12 May 2024 12:57:34 +0200 Subject: [PATCH 276/430] chore(deps): weekly `cargo update` (#7910) Updating git repository `https://github.com/alloy-rs/alloy` Updating git repository `https://github.com/paradigmxyz/evm-inspectors` Locking 51 packages to latest compatible versions Updating anyhow v1.0.82 -> v1.0.83 Updating aws-config v1.3.0 -> v1.4.0 Updating aws-runtime v1.2.0 -> v1.2.2 Updating aws-sdk-kms v1.23.0 -> v1.25.0 Updating aws-sdk-sso v1.22.0 -> v1.24.0 Updating aws-sdk-ssooidc v1.22.0 -> v1.25.0 Updating aws-sdk-sts v1.22.0 -> v1.24.0 Updating aws-smithy-runtime v1.4.0 -> v1.5.0 Updating aws-smithy-runtime-api v1.5.0 -> v1.6.0 Updating aws-smithy-types v1.1.8 -> v1.1.9 Updating aws-types v1.2.0 -> v1.2.1 Updating c-kzg v1.0.0 -> v1.0.2 Updating cc v1.0.96 -> v1.0.97 Updating ena v0.14.2 -> v0.14.3 Updating errno v0.3.8 -> v0.3.9 Updating fs4 v0.8.2 -> v0.8.3 Updating getrandom v0.2.14 -> v0.2.15 Updating interprocess v2.0.1 -> v2.1.0 Updating keccak-asm v0.1.0 -> v0.1.1 Updating num v0.4.2 -> v0.4.3 Updating num-bigint v0.4.4 -> v0.4.5 Updating num-complex v0.4.5 -> v0.4.6 Updating num-rational v0.4.1 -> v0.4.2 Updating parity-scale-codec v3.6.9 -> v3.6.12 Updating parity-scale-codec-derive v3.6.9 -> v3.6.12 Updating paste v1.0.14 -> v1.0.15 Updating petgraph v0.6.4 -> v0.6.5 Updating prettyplease v0.2.19 -> v0.2.20 Removing proc-macro-crate v1.3.1 Removing proc-macro-crate v2.0.0 Updating proc-macro2 v1.0.81 -> v1.0.82 Updating rustc-demangle v0.1.23 -> v0.1.24 Updating rustls-pki-types v1.5.0 -> v1.7.0 Updating rustversion v1.0.15 -> v1.0.16 Updating ryu v1.0.17 -> v1.0.18 Updating scale-info v2.11.2 -> v2.11.3 Updating scale-info-derive v2.11.2 -> v2.11.3 Updating schemars v0.8.17 -> v0.8.19 Updating schemars_derive v0.8.17 -> v0.8.19 Updating security-framework v2.10.0 -> v2.11.0 Updating security-framework-sys v2.10.0 -> v2.11.0 Updating semver v1.0.22 -> v1.0.23 Updating serde v1.0.200 -> v1.0.201 Updating serde_derive v1.0.200 -> v1.0.201 Updating serde_json v1.0.116 -> v1.0.117 Updating sha3-asm v0.1.0 -> v0.1.1 Updating syn v2.0.60 -> v2.0.63 Updating thiserror v1.0.59 -> v1.0.60 Updating thiserror-impl v1.0.59 -> v1.0.60 Removing toml_edit v0.19.15 Removing toml_edit v0.20.7 Updating winnow v0.6.7 -> v0.6.8 Updating zerocopy v0.7.33 -> v0.7.34 Updating zerocopy-derive v0.7.33 -> v0.7.34 Updating zip v1.1.4 -> v1.2.3 note: pass `--verbose` to see 137 unchanged dependencies behind latest Co-authored-by: mattsse --- Cargo.lock | 386 ++++++++++++++++++++++++----------------------------- 1 file changed, 171 insertions(+), 215 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7a352ef67..69cc5af41 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -126,7 +126,7 @@ dependencies = [ "proptest", "serde", "serde_json", - "winnow 0.6.7", + "winnow 0.6.8", ] [[package]] @@ -291,7 +291,7 @@ checksum = "1a047897373be4bbb0224c1afdabca92648dc57a9c9ef6e7b0be3aff7a859c83" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -421,7 +421,7 @@ dependencies = [ "async-trait", "coins-ledger", "futures-util", - "semver 1.0.22", + "semver 1.0.23", "thiserror", "tracing", ] @@ -436,7 +436,7 @@ dependencies = [ "alloy-primitives", "alloy-signer", "async-trait", - "semver 1.0.22", + "semver 1.0.23", "thiserror", "tracing", "trezor-client", @@ -475,7 +475,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", "syn-solidity", "tiny-keccak", ] @@ -493,7 +493,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.60", + "syn 2.0.63", "syn-solidity", ] @@ -503,7 +503,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8e71ea68e780cc203919e03f69f59e7afe92d2696fb1dcb6662f61e4031b6" dependencies = [ - "winnow 0.6.7", + "winnow 0.6.8", ] [[package]] @@ -812,9 +812,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.82" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" [[package]] name = "arbitrary" @@ -991,7 +991,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -1013,7 +1013,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -1024,7 +1024,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -1071,7 +1071,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -1082,9 +1082,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "aws-config" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baaa0be6ee7d90b775ae6ccb6d2ba182b91219ec2001f92338773a094246af1d" +checksum = "40ddbfb5db93d62521f47b3f223da0884a2f02741ff54cb9cda192a0e73ba08b" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1125,9 +1125,9 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.2.0" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4963ac9ff2d33a4231b3806c1c69f578f221a9cabb89ad2bde62ce2b442c8a7" +checksum = "75588e7ee5e8496eed939adac2035a6dbab9f7eb2acdd9ab2d31856dab6f3955" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -1148,9 +1148,9 @@ dependencies = [ [[package]] name = "aws-sdk-kms" -version = "1.23.0" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db70afa14e99c6d3bfa45f1c41ec28414f628d1f5a242d8ae2578f4b7a4b8480" +checksum = "dca6ab350d6652bf85e38503758a67b2735b3d1ad38fd2f55181ef3c09afdb1c" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1170,9 +1170,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.22.0" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3d6c4cba4e009391b72b0fcf12aff04ea3c9c3aa2ecaafa330326a8bd7e601" +checksum = "5fd6a9b38fe2dcaa2422b42e2c667112872d03a83522533f8b4165fd2d9d4bd1" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1192,9 +1192,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.22.0" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73400dc239d14f63d932f4ca7b55af5e9ef1f857f7d70655249ccc287adb2570" +checksum = "c99f342a364c8490b7715387244d2f7ebfc05f07bb6a0fc6e70b8e62b7078d06" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1214,9 +1214,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.22.0" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f8858308af76fba3e5ffcf1bb56af5471574d2bdfaf0159470c25bc2f760e5" +checksum = "7207ca62206c93e470457babf0512d7b6b97170fdba929a2a2f5682f9f68407c" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1310,9 +1310,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cf64e73ef8d4dac6c933230d56d136b75b252edcf82ed36e37d603090cd7348" +checksum = "c9ac79e9f3a4d576f3cd4a470a0275b138d9e7b11b1cd514a6858ae0a79dd5bb" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -1336,9 +1336,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime-api" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c19fdae6e3d5ac9cd01f2d6e6c359c5f5a3e028c2d148a8f5b90bf3399a18a7" +checksum = "04ec42c2f5c0e7796a2848dde4d9f3bf8ce12ccbb3d5aa40c52fa0cdd61a1c47" dependencies = [ "aws-smithy-async", "aws-smithy-types", @@ -1353,9 +1353,9 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abe14dceea1e70101d38fbf2a99e6a34159477c0fb95e68e05c66bd7ae4c3729" +checksum = "baf98d97bba6ddaba180f1b1147e202d8fe04940403a95a3f826c790f931bbd1" dependencies = [ "base64-simd", "bytes", @@ -1385,9 +1385,9 @@ dependencies = [ [[package]] name = "aws-types" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a43b56df2c529fe44cb4d92bd64d0479883fb9608ff62daede4df5405381814" +checksum = "a807d90cd50a969b3d95e4e7ad1491fcae13c6e83948d8728363ecc09d66343a" dependencies = [ "aws-credential-types", "aws-smithy-async", @@ -1674,9 +1674,9 @@ dependencies = [ [[package]] name = "c-kzg" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3130f3d8717cc02e668a896af24984d5d5d4e8bf12e278e982e0f1bd88a0f9af" +checksum = "cdf100c4cea8f207e883ff91ca886d621d8a166cb04971dfaa9bb8fd99ed95df" dependencies = [ "blst", "cc", @@ -1712,7 +1712,7 @@ checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" dependencies = [ "camino", "cargo-platform", - "semver 1.0.22", + "semver 1.0.23", "serde", "serde_json", "thiserror", @@ -1771,7 +1771,7 @@ dependencies = [ "rayon", "regex", "rpassword", - "semver 1.0.22", + "semver 1.0.23", "serde", "serde_json", "tempfile", @@ -1799,9 +1799,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.96" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd" +checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" [[package]] name = "cfg-if" @@ -1839,7 +1839,7 @@ dependencies = [ "reqwest", "revm", "rustyline", - "semver 1.0.22", + "semver 1.0.23", "serde", "serde_json", "serial_test", @@ -1956,7 +1956,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -2371,7 +2371,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -2382,7 +2382,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -2443,7 +2443,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -2464,7 +2464,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -2474,7 +2474,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" dependencies = [ "derive_builder_core", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -2594,7 +2594,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -2675,9 +2675,9 @@ dependencies = [ [[package]] name = "ena" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1" +checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5" dependencies = [ "log", ] @@ -2702,7 +2702,7 @@ checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -2736,9 +2736,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", @@ -2852,7 +2852,7 @@ dependencies = [ "regex", "serde", "serde_json", - "syn 2.0.60", + "syn 2.0.63", "toml 0.8.12", "walkdir", ] @@ -2880,7 +2880,7 @@ dependencies = [ "serde", "serde_json", "strum", - "syn 2.0.60", + "syn 2.0.63", "tempfile", "thiserror", "tiny-keccak", @@ -3120,7 +3120,7 @@ dependencies = [ "reqwest", "revm-inspectors", "rustc-hash", - "semver 1.0.22", + "semver 1.0.23", "serde", "serde_json", "similar", @@ -3218,7 +3218,7 @@ dependencies = [ "itertools 0.12.1", "parking_lot", "revm-inspectors", - "semver 1.0.22", + "semver 1.0.23", "serde", "serde_json", "tempfile", @@ -3250,7 +3250,7 @@ dependencies = [ "regex", "reqwest", "revm-primitives", - "semver 1.0.22", + "semver 1.0.23", "serde", "serde_json", "tempfile", @@ -3279,7 +3279,7 @@ dependencies = [ "alloy-primitives", "foundry-compilers", "reqwest", - "semver 1.0.22", + "semver 1.0.23", "serde", "serde_json", "thiserror", @@ -3316,7 +3316,7 @@ dependencies = [ "parking_lot", "revm", "rustc-hash", - "semver 1.0.22", + "semver 1.0.23", "serde_json", "thiserror", "toml 0.8.12", @@ -3408,7 +3408,7 @@ dependencies = [ "once_cell", "reqwest", "rustc-hash", - "semver 1.0.22", + "semver 1.0.23", "serde", "serde_json", "similar-asserts", @@ -3445,7 +3445,7 @@ dependencies = [ "rand", "rayon", "regex", - "semver 1.0.22", + "semver 1.0.23", "serde", "serde_json", "sha2", @@ -3480,7 +3480,7 @@ dependencies = [ "regex", "reqwest", "revm-primitives", - "semver 1.0.22", + "semver 1.0.23", "serde", "serde_json", "serde_regex", @@ -3586,7 +3586,7 @@ dependencies = [ "foundry-evm-core", "revm", "rustc-hash", - "semver 1.0.22", + "semver 1.0.23", "tracing", ] @@ -3647,7 +3647,7 @@ version = "0.2.0" dependencies = [ "alloy-primitives", "foundry-compilers", - "semver 1.0.22", + "semver 1.0.23", "thiserror", ] @@ -3658,7 +3658,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -3723,9 +3723,9 @@ checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" [[package]] name = "fs4" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21dabded2e32cd57ded879041205c60a4a4c4bab47bd0fd2fa8b01f30849f02b" +checksum = "73969b81e8bc90a3828d913dd3973d80771bfb9d7fbe1a78a79122aad456af15" dependencies = [ "rustix", "windows-sys 0.52.0", @@ -3818,7 +3818,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -3870,9 +3870,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "js-sys", @@ -4661,9 +4661,9 @@ dependencies = [ [[package]] name = "interprocess" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c7fb8583fab9503654385e2bafda123376445a77027a1b106dd7e44cf51122f" +checksum = "7b4d0250d41da118226e55b3d50ca3f0d9e0a0f6829b92f543ac0054aeea1572" dependencies = [ "futures-core", "libc", @@ -4789,9 +4789,9 @@ dependencies = [ [[package]] name = "keccak-asm" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb8515fff80ed850aea4a1595f2e519c003e2a00a82fe168ebf5269196caf444" +checksum = "47a3633291834c4fbebf8673acbc1b04ec9d151418ff9b8e26dcd79129928758" dependencies = [ "digest 0.10.7", "sha3-asm", @@ -5053,7 +5053,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -5123,7 +5123,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -5239,9 +5239,9 @@ dependencies = [ [[package]] name = "num" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3135b08af27d103b0a51f2ae0f8632117b7b185ccf931445affa8df530576a41" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" dependencies = [ "num-bigint", "num-complex", @@ -5253,20 +5253,19 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" dependencies = [ - "autocfg", "num-integer", "num-traits", ] [[package]] name = "num-complex" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ "num-traits", ] @@ -5309,11 +5308,10 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ - "autocfg", "num-bigint", "num-integer", "num-traits", @@ -5354,10 +5352,10 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ - "proc-macro-crate 3.1.0", + "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -5468,7 +5466,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -5527,9 +5525,9 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.6.9" +version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "881331e34fa842a2fb61cc2db9643a8fedc615e47cfcc52597d1af0db9a7e8fe" +checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" dependencies = [ "arrayvec", "bitvec", @@ -5541,11 +5539,11 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.6.9" +version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" +checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" dependencies = [ - "proc-macro-crate 2.0.0", + "proc-macro-crate", "proc-macro2", "quote", "syn 1.0.109", @@ -5576,9 +5574,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "path-slash" @@ -5625,7 +5623,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -5684,7 +5682,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -5700,9 +5698,9 @@ dependencies = [ [[package]] name = "petgraph" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", "indexmap", @@ -5787,7 +5785,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -5825,7 +5823,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -5936,12 +5934,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.19" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ac2cf0f2e4f42b49f5ffd07dae8d746508ef7526c13940e5f524012ae6c6550" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -5967,25 +5965,6 @@ dependencies = [ "uint", ] -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit 0.19.15", -] - -[[package]] -name = "proc-macro-crate" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" -dependencies = [ - "toml_edit 0.20.7", -] - [[package]] name = "proc-macro-crate" version = "3.1.0" @@ -6021,9 +6000,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" dependencies = [ "unicode-ident", ] @@ -6036,7 +6015,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", "version_check", "yansi", ] @@ -6562,9 +6541,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -6593,7 +6572,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.22", + "semver 1.0.23", ] [[package]] @@ -6681,9 +6660,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.5.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" [[package]] name = "rustls-webpki" @@ -6708,9 +6687,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" +checksum = "092474d1a01ea8278f69e6a358998405fae5b8b963ddaeb2b0b04a128bf1dfb0" [[package]] name = "rusty-fork" @@ -6749,9 +6728,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "salsa20" @@ -6773,9 +6752,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.11.2" +version = "2.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c453e59a955f81fb62ee5d596b450383d699f152d350e9d23a0db2adb78e4c0" +checksum = "eca070c12893629e2cc820a9761bedf6ce1dcddc9852984d1dc734b8bd9bd024" dependencies = [ "cfg-if", "derive_more", @@ -6785,11 +6764,11 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.11.2" +version = "2.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18cf6c6447f813ef19eb450e985bcce6705f9ce7660db221b59093d15c79c4b7" +checksum = "2d35494501194174bda522a32605929eefc9ecf7e0a326c26db1fdd85881eb62" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate", "proc-macro2", "quote", "syn 1.0.109", @@ -6815,9 +6794,9 @@ dependencies = [ [[package]] name = "schemars" -version = "0.8.17" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f55c82c700538496bdc329bb4918a81f87cc8888811bd123cf325a0f2f8d309" +checksum = "fc6e7ed6919cb46507fb01ff1654309219f62b4d603822501b0b80d42f6f21ef" dependencies = [ "dyn-clone", "schemars_derive", @@ -6827,14 +6806,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.17" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83263746fe5e32097f06356968a077f96089739c927a61450efa069905eec108" +checksum = "185f2b7aa7e02d418e453790dde16890256bbd2bcd04b7dc5348811052b53f49" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -6906,11 +6885,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", "core-foundation", "core-foundation-sys", "libc", @@ -6919,9 +6898,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" dependencies = [ "core-foundation-sys", "libc", @@ -6938,9 +6917,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" dependencies = [ "serde", ] @@ -6962,22 +6941,22 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.200" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" +checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.200" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" +checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -6988,14 +6967,14 @@ checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "indexmap", "itoa", @@ -7031,7 +7010,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -7077,7 +7056,7 @@ checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -7120,9 +7099,9 @@ dependencies = [ [[package]] name = "sha3-asm" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bac61da6b35ad76b195eb4771210f947734321a8d81d7738e1580d953bc7a15e" +checksum = "a9b57fd861253bff08bb1919e995f90ba8f4889de2726091c8876f3a4e823b40" dependencies = [ "cc", "cfg-if", @@ -7295,7 +7274,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ff9eaf853dec4c8802325d8b6d3dffa86cc707fd7a1a4cdbf416e13b061787a" dependencies = [ "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -7367,7 +7346,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -7400,7 +7379,7 @@ dependencies = [ "fs4", "once_cell", "reqwest", - "semver 1.0.22", + "semver 1.0.23", "serde", "serde_json", "sha2", @@ -7417,7 +7396,7 @@ checksum = "f1bb4806c96207e7cde40fc238f9a1d570f3090f850a742e1e96665440615a31" dependencies = [ "build_const", "const-hex", - "semver 1.0.22", + "semver 1.0.23", "serde_json", "svm-rs", ] @@ -7435,9 +7414,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.60" +version = "2.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" dependencies = [ "proc-macro2", "quote", @@ -7453,7 +7432,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -7539,22 +7518,22 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.59" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.59" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -7690,7 +7669,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -7807,28 +7786,6 @@ dependencies = [ "serde", ] -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap", - "toml_datetime", - "winnow 0.5.40", -] - -[[package]] -name = "toml_edit" -version = "0.20.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" -dependencies = [ - "indexmap", - "toml_datetime", - "winnow 0.5.40", -] - [[package]] name = "toml_edit" version = "0.21.1" @@ -7850,7 +7807,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.7", + "winnow 0.6.8", ] [[package]] @@ -7932,7 +7889,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -8279,7 +8236,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", "wasm-bindgen-shared", ] @@ -8313,7 +8270,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -8680,9 +8637,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14b9415ee827af173ebb3f15f9083df5a122eb93572ec28741fb153356ea2578" +checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" dependencies = [ "memchr", ] @@ -8742,22 +8699,22 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.33" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "087eca3c1eaf8c47b94d02790dd086cd594b912d2043d4de4bfdd466b3befb7c" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.33" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f4b6c273f496d8fd4eaf18853e6b448760225dc030ff2c485a786859aea6393" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -8777,14 +8734,14 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] name = "zip" -version = "1.1.4" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cc23c04387f4da0374be4533ad1208cbb091d5c11d070dfef13676ad6497164" +checksum = "c700ea425e148de30c29c580c1f9508b93ca57ad31c9f4e96b83c194c37a7a8f" dependencies = [ "arbitrary", "crc32fast", @@ -8792,6 +8749,5 @@ dependencies = [ "displaydoc", "flate2", "indexmap", - "num_enum", "thiserror", ] From a117fbfa41edbaa1618ed099d78d65727bff4790 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sun, 12 May 2024 15:03:23 +0300 Subject: [PATCH 277/430] fix: nonce correction logic (#7907) * add test * fix: refactor correct_sender_nonce logic * fix doc --- crates/cheatcodes/src/evm.rs | 1 - crates/cheatcodes/src/evm/fork.rs | 10 -------- crates/cheatcodes/src/inspector.rs | 31 ++++++++++++++++++++--- crates/cheatcodes/src/script.rs | 18 -------------- crates/forge/tests/cli/script.rs | 40 ++++++++++++++++++++++++++++++ crates/script/src/runner.rs | 22 ---------------- 6 files changed, 67 insertions(+), 55 deletions(-) diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index 618998071..d40fae0ad 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -553,7 +553,6 @@ impl Cheatcode for stopAndReturnStateDiffCall { } pub(super) fn get_nonce(ccx: &mut CheatsCtxt, address: &Address) -> Result { - super::script::correct_sender_nonce(ccx)?; let (account, _) = ccx.ecx.journaled_state.load_account(*address, &mut ccx.ecx.db)?; Ok(account.info.nonce.abi_encode()) } diff --git a/crates/cheatcodes/src/evm/fork.rs b/crates/cheatcodes/src/evm/fork.rs index 63f0bbad1..81b364aaf 100644 --- a/crates/cheatcodes/src/evm/fork.rs +++ b/crates/cheatcodes/src/evm/fork.rs @@ -116,10 +116,6 @@ impl Cheatcode for selectForkCall { let Self { forkId } = self; check_broadcast(ccx.state)?; - // No need to correct since the sender's nonce does not get incremented when selecting a - // fork. - ccx.state.corrected_nonce = true; - ccx.ecx.db.select_fork(*forkId, &mut ccx.ecx.env, &mut ccx.ecx.journaled_state)?; Ok(Default::default()) } @@ -287,9 +283,6 @@ fn create_select_fork( ) -> Result { check_broadcast(ccx.state)?; - // No need to correct since the sender's nonce does not get incremented when selecting a fork. - ccx.state.corrected_nonce = true; - let fork = create_fork_request(ccx, url_or_alias, block)?; let id = ccx.ecx.db.create_select_fork(fork, &mut ccx.ecx.env, &mut ccx.ecx.journaled_state)?; Ok(id.abi_encode()) @@ -314,9 +307,6 @@ fn create_select_fork_at_transaction( ) -> Result { check_broadcast(ccx.state)?; - // No need to correct since the sender's nonce does not get incremented when selecting a fork. - ccx.state.corrected_nonce = true; - let fork = create_fork_request(ccx, url_or_alias, None)?; let id = ccx.ecx.db.create_select_fork_at_transaction( fork, diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index b66d262e2..25e34313c 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -19,6 +19,7 @@ use alloy_primitives::{Address, Bytes, Log, TxKind, B256, U256}; use alloy_rpc_types::request::{TransactionInput, TransactionRequest}; use alloy_sol_types::{SolInterface, SolValue}; use foundry_common::{evm::Breakpoints, SELECTOR_LEN}; +use foundry_config::Config; use foundry_evm_core::{ abi::Vm::stopExpectSafeMemoryCall, backend::{DatabaseExt, RevertDiagnostic}, @@ -166,10 +167,6 @@ pub struct Cheatcodes { /// Current broadcasting information pub broadcast: Option, - /// Used to correct the nonce of --sender after the initiating call. For more, check - /// `docs/scripting`. - pub corrected_nonce: bool, - /// Scripting based transactions pub broadcastable_transactions: BroadcastableTransactions, @@ -767,6 +764,32 @@ impl Inspector for Cheatcodes { fn call(&mut self, ecx: &mut EvmContext, call: &mut CallInputs) -> Option { let gas = Gas::new(call.gas_limit); + // At the root call to test function or script `run()`/`setUp()` functions, we are + // decreasing sender nonce to ensure that it matches on-chain nonce once we start + // broadcasting. + if ecx.journaled_state.depth == 0 { + let sender = ecx.env.tx.caller; + if sender != Config::DEFAULT_SENDER { + let account = match super::evm::journaled_account(ecx, sender) { + Ok(account) => account, + Err(err) => { + return Some(CallOutcome { + result: InterpreterResult { + result: InstructionResult::Revert, + output: err.abi_encode().into(), + gas, + }, + memory_offset: call.return_memory_offset.clone(), + }) + } + }; + let prev = account.info.nonce; + account.info.nonce = prev.saturating_sub(1); + + debug!(target: "cheatcodes", %sender, nonce=account.info.nonce, prev, "corrected nonce"); + } + } + if call.contract == CHEATCODE_ADDRESS { return match self.apply_cheatcode(ecx, call) { Ok(retdata) => Some(CallOutcome { diff --git a/crates/cheatcodes/src/script.rs b/crates/cheatcodes/src/script.rs index a28a8be49..19922ad34 100644 --- a/crates/cheatcodes/src/script.rs +++ b/crates/cheatcodes/src/script.rs @@ -3,7 +3,6 @@ use crate::{Cheatcode, CheatsCtxt, DatabaseExt, Result, Vm::*}; use alloy_primitives::{Address, U256}; use alloy_signer_wallet::LocalWallet; -use foundry_config::Config; use foundry_wallets::{multi_wallet::MultiWallet, WalletSigner}; use parking_lot::Mutex; use std::sync::Arc; @@ -130,8 +129,6 @@ fn broadcast( ); ensure!(ccx.state.broadcast.is_none(), "a broadcast is active already"); - correct_sender_nonce(ccx)?; - let mut new_origin = new_origin.cloned(); if new_origin.is_none() { @@ -181,18 +178,3 @@ fn broadcast_key( } result } - -/// When using `forge script`, the script method is called using the address from `--sender`. -/// That leads to its nonce being incremented by `call_raw`. In a `broadcast` scenario this is -/// undesirable. Therefore, we make sure to fix the sender's nonce **once**. -pub(super) fn correct_sender_nonce(ccx: &mut CheatsCtxt) -> Result<()> { - let sender = ccx.ecx.env.tx.caller; - if !ccx.state.corrected_nonce && sender != Config::DEFAULT_SENDER { - let account = super::evm::journaled_account(ccx.ecx, sender)?; - let prev = account.info.nonce; - account.info.nonce = prev.saturating_sub(1); - debug!(target: "cheatcodes", %sender, nonce=account.info.nonce, prev, "corrected nonce"); - ccx.state.corrected_nonce = true; - } - Ok(()) -} diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index 9f1a12c4e..b981739ca 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -1355,3 +1355,43 @@ contract SimpleScript is Script { let output = cmd.stderr_lossy(); assert!(output.contains("missing CREATE2 deployer")); }); + +forgetest_async!(can_switch_forks_in_setup, |prj, cmd| { + let (_api, handle) = + spawn(NodeConfig::test().with_disable_default_create2_deployer(true)).await; + + foundry_test_utils::util::initialize(prj.root()); + let url = handle.http_endpoint(); + + prj.add_script( + "Foo", + &r#" +import "forge-std/Script.sol"; + +contract SimpleScript is Script { + function setUp() external { + uint256 initialFork = vm.activeFork(); + vm.createSelectFork(""); + vm.selectFork(initialFork); + } + + function run() external { + assert(vm.getNonce(msg.sender) == 0); + } +} + "# + .replace("", &url), + ) + .unwrap(); + + cmd.args([ + "script", + "SimpleScript", + "--fork-url", + &url, + "--sender", + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + ]); + + cmd.stdout_lossy(); +}); diff --git a/crates/script/src/runner.rs b/crates/script/src/runner.rs index ff77b8e61..901e5e58a 100644 --- a/crates/script/src/runner.rs +++ b/crates/script/src/runner.rs @@ -101,8 +101,6 @@ impl ScriptRunner { traces.extend(setup_traces.map(|traces| (TraceKind::Setup, traces))); logs.extend_from_slice(&setup_logs); - self.maybe_correct_nonce(sender_nonce, libraries.len())?; - ( !reverted, gas_used, @@ -125,8 +123,6 @@ impl ScriptRunner { traces.extend(setup_traces.map(|traces| (TraceKind::Setup, traces))); logs.extend_from_slice(&setup_logs); - self.maybe_correct_nonce(sender_nonce, libraries.len())?; - ( !reverted, gas_used, @@ -156,24 +152,6 @@ impl ScriptRunner { )) } - /// We call the `setUp()` function with self.sender, and if there haven't been - /// any broadcasts, then the EVM cheatcode module hasn't corrected the nonce. - /// So we have to. - fn maybe_correct_nonce( - &mut self, - sender_initial_nonce: u64, - libraries_len: usize, - ) -> Result<()> { - if let Some(cheatcodes) = &self.executor.inspector.cheatcodes { - if !cheatcodes.corrected_nonce { - self.executor - .set_nonce(self.sender, sender_initial_nonce + libraries_len as u64)?; - } - self.executor.inspector.cheatcodes.as_mut().unwrap().corrected_nonce = false; - } - Ok(()) - } - /// Executes the method that will collect all broadcastable transactions. pub fn script(&mut self, address: Address, calldata: Bytes) -> Result { self.call(self.sender, address, calldata, U256::ZERO, false) From 4aa17bc86e7a43ca321da26cc049f85849fc9bc7 Mon Sep 17 00:00:00 2001 From: galois Date: Mon, 13 May 2024 23:35:40 +0800 Subject: [PATCH 278/430] feat(cast): support convert hex data to a utf-8 string (#7917) * feat(cast): support convert hex data to a utf-8 string * fix: return result and use `from_utf8_lossy` --- crates/cast/bin/main.rs | 4 ++++ crates/cast/bin/opts.rs | 7 +++++++ crates/cast/src/lib.rs | 18 ++++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index e1b411864..30bb1205e 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -68,6 +68,10 @@ async fn main() -> Result<()> { let value = stdin::unwrap(hexdata, false)?; println!("{}", SimpleCast::to_ascii(&value)?); } + CastSubcommand::ToUtf8 { hexdata } => { + let value = stdin::unwrap(hexdata, false)?; + println!("{}", SimpleCast::to_utf8(&value)?); + } CastSubcommand::FromFixedPoint { value, decimals } => { let (value, decimals) = stdin::unwrap2(value, decimals)?; println!("{}", SimpleCast::from_fixed_point(&value, &decimals)?); diff --git a/crates/cast/bin/opts.rs b/crates/cast/bin/opts.rs index 51f32b145..4a9a00992 100644 --- a/crates/cast/bin/opts.rs +++ b/crates/cast/bin/opts.rs @@ -126,6 +126,13 @@ pub enum CastSubcommand { hexdata: Option, }, + /// Convert hex data to a utf-8 string. + #[command(visible_aliases = &["--to-utf8", "tu8", "2u8"])] + ToUtf8 { + /// The hex data to convert. + hexdata: Option, + }, + /// Convert a fixed point number into an integer. #[command(visible_aliases = &["--from-fix", "ff"])] FromFixedPoint { diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index d7fa2a9f9..e2f706b74 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -1053,6 +1053,24 @@ impl SimpleCast { hex::encode_prefixed(s) } + /// Converts hex input to UTF-8 text + /// + /// # Example + /// + /// ``` + /// use cast::SimpleCast as Cast; + /// + /// assert_eq!(Cast::to_utf8("0x796f")?, "yo"); + /// assert_eq!(Cast::to_utf8("0x48656c6c6f2c20576f726c6421")?, "Hello, World!"); + /// assert_eq!(Cast::to_utf8("0x547572626f44617070546f6f6c73")?, "TurboDappTools"); + /// assert_eq!(Cast::to_utf8("0xe4bda0e5a5bd")?, "你好"); + /// # Ok::<_, eyre::Report>(()) + /// ``` + pub fn to_utf8(s: &str) -> Result { + let bytes = hex::decode(s)?; + Ok(String::from_utf8_lossy(bytes.as_ref()).to_string()) + } + /// Converts hex data into text data /// /// # Example From 781acf0fa67b068e4e1538b5384c0ad522e8b279 Mon Sep 17 00:00:00 2001 From: Rafael Quintero Date: Tue, 14 May 2024 14:44:35 +0100 Subject: [PATCH 279/430] feat(cast): wallet keystore decrypting (#7893) * feat(cast): wallet keystore decrypting * test(cast): casttest for wallet import and decrypt-keystore * docs(cast): change keystore_dir explanation for DecryptKeystore --- crates/cast/bin/cmd/wallet/mod.rs | 50 +++++++++++++++++++++++++++- crates/cast/tests/cli/main.rs | 55 ++++++++++++++++++++++++++++++- 2 files changed, 103 insertions(+), 2 deletions(-) diff --git a/crates/cast/bin/cmd/wallet/mod.rs b/crates/cast/bin/cmd/wallet/mod.rs index 1ac943204..3068af083 100644 --- a/crates/cast/bin/cmd/wallet/mod.rs +++ b/crates/cast/bin/cmd/wallet/mod.rs @@ -1,5 +1,5 @@ use alloy_dyn_abi::TypedData; -use alloy_primitives::{Address, Signature}; +use alloy_primitives::{Address, Signature, B256}; use alloy_signer::Signer; use alloy_signer_wallet::{ coins_bip39::{English, Mnemonic}, @@ -151,6 +151,22 @@ pub enum WalletSubcommands { /// Derives private key from mnemonic #[command(name = "derive-private-key", visible_aliases = &["--derive-private-key"])] DerivePrivateKey { mnemonic: String, mnemonic_index: Option }, + + /// Decrypt a keystore file to get the private key + #[command(name = "decrypt-keystore", visible_alias = "dk")] + DecryptKeystore { + /// The name for the account in the keystore. + #[arg(value_name = "ACCOUNT_NAME")] + account_name: String, + /// If not provided, keystore will try to be located at the default keystores directory + /// (~/.foundry/keystores) + #[arg(long, short)] + keystore_dir: Option, + /// Password for the JSON keystore in cleartext + /// This is unsafe, we recommend using the default hidden password prompt + #[arg(long, env = "CAST_UNSAFE_PASSWORD", value_name = "PASSWORD")] + unsafe_password: Option, + }, } impl WalletSubcommands { @@ -365,6 +381,38 @@ flag to set your key via: println!("Address: {}", wallet.address()); println!("Private key: 0x{}\n", hex::encode(wallet.signer().to_bytes())); } + WalletSubcommands::DecryptKeystore { account_name, keystore_dir, unsafe_password } => { + // Set up keystore directory + let dir = if let Some(path) = keystore_dir { + Path::new(&path).to_path_buf() + } else { + Config::foundry_keystores_dir().ok_or_else(|| { + eyre::eyre!("Could not find the default keystore directory.") + })? + }; + + let keypath = dir.join(&account_name); + + if !keypath.exists() { + eyre::bail!("Keystore file does not exist at {}", keypath.display()); + } + + let password = if let Some(password) = unsafe_password { + password + } else { + // if no --unsafe-password was provided read via stdin + rpassword::prompt_password("Enter password: ")? + }; + + let wallet = LocalWallet::decrypt_keystore(keypath, password)?; + + let private_key = B256::from_slice(&wallet.signer().to_bytes()); + + let success_message = + format!("{}'s private key is: {}", &account_name, private_key); + + println!("{}", success_message.green()); + } }; Ok(()) diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index c6f2b0450..61594460f 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -6,7 +6,7 @@ use foundry_test_utils::{ rpc::{next_http_rpc_endpoint, next_ws_rpc_endpoint}, util::OutputExt, }; -use std::{fs, io::Write, path::Path}; +use std::{fs, io::Write, path::Path, str::FromStr}; // tests `--help` is printed to std out casttest!(print_help, |_prj, cmd| { @@ -156,6 +156,59 @@ casttest!(wallet_list_local_accounts, |prj, cmd| { assert_eq!(list_output.matches('\n').count(), 10); }); +// tests that `cast wallet import` creates a keystore for a private key and that `cast wallet +// decrypt-keystore` can access it +casttest!(wallet_import_and_decrypt, |prj, cmd| { + let keystore_path = prj.root().join("keystore"); + + cmd.set_current_dir(prj.root()); + + let account_name = "testAccount"; + + // Default Anvil private key + let test_private_key = + b256!("ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"); + + // import private key + cmd.cast_fuse().args([ + "wallet", + "import", + account_name, + "--private-key", + &test_private_key.to_string(), + "-k", + "keystore", + "--unsafe-password", + "test", + ]); + + cmd.assert_non_empty_stdout(); + + // check that the keystore file was created + let keystore_file = keystore_path.join(account_name); + + assert!(keystore_file.exists()); + + // decrypt the keystore file + let decrypt_output = cmd.cast_fuse().args([ + "wallet", + "decrypt-keystore", + account_name, + "-k", + "keystore", + "--unsafe-password", + "test", + ]); + + // get the PK out of the output (last word in the output) + let decrypt_output = decrypt_output.stdout_lossy(); + let private_key_string = decrypt_output.split_whitespace().last().unwrap(); + // check that the decrypted private key matches the imported private key + let decrypted_private_key = B256::from_str(private_key_string).unwrap(); + // the form + assert_eq!(decrypted_private_key, test_private_key); +}); + // tests that `cast estimate` is working correctly. casttest!(estimate_function_gas, |_prj, cmd| { let eth_rpc_url = next_http_rpc_endpoint(); From 4267f44e13bcfcb6ee3a38b10eba4afa1293296c Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Tue, 14 May 2024 18:29:43 +0400 Subject: [PATCH 280/430] bump compilers (#7926) --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 69cc5af41..796e47fbd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3424,9 +3424,9 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f97f9de33410d0daf13834f818a8594c0ed277c848af750e40a9f28e67d62e3a" +checksum = "74a0bb9a4a8da5ded9ddbf2aba3d25bf26d2c4e8a3dfb4f20164d47d4da8761a" dependencies = [ "alloy-json-abi", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index b2932bcb6..a807d1983 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -138,7 +138,7 @@ foundry-linking = { path = "crates/linking" } # solc & compilation utilities foundry-block-explorers = { version = "0.2.7", default-features = false } -foundry-compilers = { version = "0.4.1", default-features = false } +foundry-compilers = { version = "0.4.3", default-features = false } ## revm # no default features to avoid c-kzg From 39f155d1fd68a8e2d0663c7d87573ad76831c23e Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Tue, 14 May 2024 18:36:25 +0400 Subject: [PATCH 281/430] feat: library deployments though CREATE2 (#7711) * wip * tests * fix test * clippy * check that CREATE2 deployer present * fmt * update doc * fix * rm blank lines * fmt * nits * fmt * fix nonce --- crates/config/src/lib.rs | 9 +- crates/evm/evm/src/executors/mod.rs | 5 + crates/forge/tests/cli/config.rs | 1 + crates/forge/tests/cli/script.rs | 56 +++++ crates/linking/src/lib.rs | 303 +++++++++++++++++++++------- crates/script/src/build.rs | 127 +++++++----- crates/script/src/execute.rs | 32 +-- crates/script/src/lib.rs | 9 +- crates/script/src/runner.rs | 129 +++++++++--- crates/test-utils/src/script.rs | 64 ++++-- 10 files changed, 537 insertions(+), 198 deletions(-) diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 96cd23a63..9c490ef03 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -28,7 +28,7 @@ use foundry_compilers::{ }; use inflector::Inflector; use regex::Regex; -use revm_primitives::SpecId; +use revm_primitives::{FixedBytes, SpecId}; use semver::Version; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::{ @@ -394,6 +394,9 @@ pub struct Config { /// If disabled, it is possible to access artifacts which were not recompiled or cached. pub unchecked_cheatcode_artifacts: bool, + /// CREATE2 salt to use for the library deployment in scripts. + pub create2_library_salt: B256, + /// The root path where the config detection started from, `Config::with_root` #[doc(hidden)] // We're skipping serialization here, so it won't be included in the [`Config::to_string()`] @@ -450,6 +453,9 @@ impl Config { /// `0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38` pub const DEFAULT_SENDER: Address = address!("1804c8AB1F12E6bbf3894d4083f33e07309d1f38"); + /// Default salt for create2 library deployments + pub const DEFAULT_CREATE2_LIBRARY_SALT: FixedBytes<32> = FixedBytes::<32>::ZERO; + /// Returns the current `Config` /// /// See `Config::figment` @@ -2006,6 +2012,7 @@ impl Default for Config { doc: Default::default(), labels: Default::default(), unchecked_cheatcode_artifacts: false, + create2_library_salt: Config::DEFAULT_CREATE2_LIBRARY_SALT, __non_exhaustive: (), __warnings: vec![], } diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 45abbfc9c..699800641 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -159,6 +159,11 @@ impl Executor { Ok(self.backend.basic_ref(address)?.map(|acc| acc.nonce).unwrap_or_default()) } + /// Returns true if account has no code. + pub fn is_empty_code(&self, address: Address) -> DatabaseResult { + Ok(self.backend.basic_ref(address)?.map(|acc| acc.is_empty_code_hash()).unwrap_or(true)) + } + #[inline] pub fn set_tracing(&mut self, tracing: bool) -> &mut Self { self.inspector.tracing(tracing); diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index f60eebfde..c0856ce08 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -130,6 +130,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { cancun: true, isolate: true, unchecked_cheatcode_artifacts: false, + create2_library_salt: Config::DEFAULT_CREATE2_LIBRARY_SALT, __non_exhaustive: (), __warnings: vec![], }; diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index b981739ca..61aff4db0 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -1395,3 +1395,59 @@ contract SimpleScript is Script { cmd.stdout_lossy(); }); + +// Asserts that running the same script twice only deploys library once. +forgetest_async!(can_deploy_library_create2, |prj, cmd| { + let (_api, handle) = spawn(NodeConfig::test()).await; + + let mut tester = ScriptTester::new_broadcast(cmd, &handle.http_endpoint(), prj.root()); + + tester + .load_private_keys(&[0, 1]) + .await + .add_sig("BroadcastTest", "deploy()") + .simulate(ScriptOutcome::OkSimulation) + .broadcast(ScriptOutcome::OkBroadcast) + .assert_nonce_increment(&[(0, 2), (1, 1)]) + .await; + + tester.clear(); + + tester + .load_private_keys(&[0, 1]) + .await + .add_sig("BroadcastTest", "deploy()") + .simulate(ScriptOutcome::OkSimulation) + .broadcast(ScriptOutcome::OkBroadcast) + .assert_nonce_increment(&[(0, 1), (1, 1)]) + .await; +}); + +// Asserts that running the same script twice only deploys library once when using different +// senders. +forgetest_async!(can_deploy_library_create2_different_sender, |prj, cmd| { + let (_api, handle) = spawn(NodeConfig::test()).await; + + let mut tester = ScriptTester::new_broadcast(cmd, &handle.http_endpoint(), prj.root()); + + tester + .load_private_keys(&[0, 1]) + .await + .add_sig("BroadcastTest", "deploy()") + .simulate(ScriptOutcome::OkSimulation) + .broadcast(ScriptOutcome::OkBroadcast) + .assert_nonce_increment(&[(0, 2), (1, 1)]) + .await; + + tester.clear(); + + // Run different script from the same contract (which requires the same library). + tester + .load_private_keys(&[2]) + .await + .add_sig("BroadcastTest", "deployNoArgs()") + .simulate(ScriptOutcome::OkSimulation) + .broadcast(ScriptOutcome::OkBroadcast) + .assert_nonce_increment(&[(2, 2)]) + .await; +}); diff --git a/crates/linking/src/lib.rs b/crates/linking/src/lib.rs index ff019ae06..8c0c6f5fe 100644 --- a/crates/linking/src/lib.rs +++ b/crates/linking/src/lib.rs @@ -1,6 +1,6 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] -use alloy_primitives::{Address, Bytes}; +use alloy_primitives::{Address, Bytes, B256}; use foundry_compilers::{ artifacts::{CompactContractBytecode, CompactContractBytecodeCow, Libraries}, contracts::ArtifactContracts, @@ -8,7 +8,7 @@ use foundry_compilers::{ }; use semver::Version; use std::{ - collections::{BTreeMap, BTreeSet}, + collections::{BTreeMap, BTreeSet, HashMap}, path::{Path, PathBuf}, str::FromStr, }; @@ -22,6 +22,8 @@ pub enum LinkerError { MissingTargetArtifact, #[error(transparent)] InvalidAddress(
::Err), + #[error("cyclic dependency found, can't link libraries via CREATE2")] + CyclicDependency, } pub struct Linker<'a> { @@ -172,6 +174,66 @@ impl<'a> Linker<'a> { Ok(LinkOutput { libraries, libs_to_deploy }) } + pub fn link_with_create2( + &'a self, + libraries: Libraries, + sender: Address, + salt: B256, + target: &'a ArtifactId, + ) -> Result { + // Library paths in `link_references` keys are always stripped, so we have to strip + // user-provided paths to be able to match them correctly. + let mut libraries = libraries.with_stripped_file_prefixes(self.root.as_path()); + + let mut needed_libraries = BTreeSet::new(); + self.collect_dependencies(target, &mut needed_libraries)?; + + let mut needed_libraries = needed_libraries + .into_iter() + .filter(|id| { + // Filter out already provided libraries. + let (file, name) = self.convert_artifact_id_to_lib_path(id); + !libraries.libs.contains_key(&file) || !libraries.libs[&file].contains_key(&name) + }) + .map(|id| { + // Link library with provided libs and extract bytecode object (possibly unlinked). + let bytecode = self.link(id, &libraries).unwrap().bytecode.unwrap(); + (id, bytecode) + }) + .collect::>(); + + let mut libs_to_deploy = Vec::new(); + + // Iteratively compute addresses and link libraries until we have no unlinked libraries + // left. + while !needed_libraries.is_empty() { + // Find any library which is fully linked. + let deployable = needed_libraries + .iter() + .find(|(_, bytecode)| !bytecode.object.is_unlinked()) + .map(|(id, _)| *id); + + // If we haven't found any deployable library, it means we have a cyclic dependency. + let Some(id) = deployable else { + return Err(LinkerError::CyclicDependency); + }; + let bytecode = needed_libraries.remove(id).unwrap(); + let code = bytecode.into_bytes().unwrap(); + let address = sender.create2_from_code(salt, code.as_ref()); + libs_to_deploy.push(code); + + let (file, name) = self.convert_artifact_id_to_lib_path(id); + + for (_, bytecode) in needed_libraries.iter_mut() { + bytecode.link(file.to_string_lossy(), name.clone(), address); + } + + libraries.libs.entry(file).or_default().insert(name, address.to_checksum(None)); + } + + Ok(LinkOutput { libraries, libs_to_deploy }) + } + /// Links given artifact with given libraries. pub fn link( &self, @@ -212,6 +274,7 @@ impl<'a> Linker<'a> { #[cfg(test)] mod tests { use super::*; + use alloy_primitives::fixed_bytes; use foundry_compilers::{Project, ProjectCompileOutput, ProjectPathsConfig}; use std::collections::HashMap; @@ -225,7 +288,7 @@ mod tests { fn new(path: impl Into, strip_prefixes: bool) -> Self { let path = path.into(); let paths = ProjectPathsConfig::builder() - .root("../../testdata/linking") + .root("../../testdata") .lib("../../testdata/lib") .sources(path.clone()) .tests(path) @@ -259,7 +322,29 @@ mod tests { fn test_with_sender_and_nonce(self, sender: Address, initial_nonce: u64) { let linker = Linker::new(self.project.root(), self.output.artifact_ids().collect()); - for id in linker.contracts.keys() { + for (id, identifier) in self.iter_linking_targets(&linker) { + let output = linker + .link_with_nonce_or_address(Default::default(), sender, initial_nonce, id) + .expect("Linking failed"); + self.validate_assertions(identifier, output); + } + } + + fn test_with_create2(self, sender: Address, salt: B256) { + let linker = Linker::new(self.project.root(), self.output.artifact_ids().collect()); + for (id, identifier) in self.iter_linking_targets(&linker) { + let output = linker + .link_with_create2(Default::default(), sender, salt, id) + .expect("Linking failed"); + self.validate_assertions(identifier, output); + } + } + + fn iter_linking_targets<'a>( + &'a self, + linker: &'a Linker<'_>, + ) -> impl IntoIterator + 'a { + linker.contracts.keys().filter_map(move |id| { // If we didn't strip paths, artifacts will have absolute paths. // That's expected and we want to ensure that only `libraries` object has relative // paths, artifacts should be kept as is. @@ -273,36 +358,42 @@ mod tests { // Skip ds-test as it always has no dependencies etc. (and the path is outside root // so is not sanitized) if identifier.contains("DSTest") { - continue; + return None; } - let LinkOutput { libs_to_deploy, libraries, .. } = linker - .link_with_nonce_or_address(Default::default(), sender, initial_nonce, id) - .expect("Linking failed"); - - let assertions = self - .dependency_assertions - .get(&identifier) - .unwrap_or_else(|| panic!("Unexpected artifact: {identifier}")); - - assert_eq!( - libs_to_deploy.len(), - assertions.len(), - "artifact {identifier} has more/less dependencies than expected ({} vs {}): {:#?}", - libs_to_deploy.len(), - assertions.len(), - libs_to_deploy - ); + Some((id, identifier)) + }) + } - for (dep_identifier, address) in assertions { - let (file, name) = dep_identifier.split_once(':').unwrap(); - if let Some(lib_address) = - libraries.libs.get(&PathBuf::from(file)).and_then(|libs| libs.get(name)) - { - assert_eq!(*lib_address, address.to_string(), "incorrect library address for dependency {dep_identifier} of {identifier}"); - } else { - panic!("Library not found") - } + fn validate_assertions(&self, identifier: String, output: LinkOutput) { + let LinkOutput { libs_to_deploy, libraries } = output; + + let assertions = self + .dependency_assertions + .get(&identifier) + .unwrap_or_else(|| panic!("Unexpected artifact: {identifier}")); + + assert_eq!( + libs_to_deploy.len(), + assertions.len(), + "artifact {identifier} has more/less dependencies than expected ({} vs {}): {:#?}", + libs_to_deploy.len(), + assertions.len(), + libs_to_deploy + ); + + for (dep_identifier, address) in assertions { + let (file, name) = dep_identifier.split_once(':').unwrap(); + if let Some(lib_address) = + libraries.libs.get(&PathBuf::from(file)).and_then(|libs| libs.get(name)) + { + assert_eq!( + *lib_address, + address.to_string(), + "incorrect library address for dependency {dep_identifier} of {identifier}" + ); + } else { + panic!("Library {} not found", dep_identifier); } } } @@ -316,20 +407,20 @@ mod tests { #[test] fn link_simple() { - link_test("../../testdata/linking/simple", |linker| { + link_test("../../testdata/default/linking/simple", |linker| { linker - .assert_dependencies("simple/Simple.t.sol:Lib".to_string(), vec![]) + .assert_dependencies("default/linking/simple/Simple.t.sol:Lib".to_string(), vec![]) .assert_dependencies( - "simple/Simple.t.sol:LibraryConsumer".to_string(), + "default/linking/simple/Simple.t.sol:LibraryConsumer".to_string(), vec![( - "simple/Simple.t.sol:Lib".to_string(), + "default/linking/simple/Simple.t.sol:Lib".to_string(), Address::from_str("0x5a443704dd4b594b382c22a083e2bd3090a6fef3").unwrap(), )], ) .assert_dependencies( - "simple/Simple.t.sol:SimpleLibraryLinkingTest".to_string(), + "default/linking/simple/Simple.t.sol:SimpleLibraryLinkingTest".to_string(), vec![( - "simple/Simple.t.sol:Lib".to_string(), + "default/linking/simple/Simple.t.sol:Lib".to_string(), Address::from_str("0x5a443704dd4b594b382c22a083e2bd3090a6fef3").unwrap(), )], ) @@ -339,43 +430,43 @@ mod tests { #[test] fn link_nested() { - link_test("../../testdata/linking/nested", |linker| { + link_test("../../testdata/default/linking/nested", |linker| { linker - .assert_dependencies("nested/Nested.t.sol:Lib".to_string(), vec![]) + .assert_dependencies("default/linking/nested/Nested.t.sol:Lib".to_string(), vec![]) .assert_dependencies( - "nested/Nested.t.sol:NestedLib".to_string(), + "default/linking/nested/Nested.t.sol:NestedLib".to_string(), vec![( - "nested/Nested.t.sol:Lib".to_string(), + "default/linking/nested/Nested.t.sol:Lib".to_string(), Address::from_str("0x5a443704dd4b594b382c22a083e2bd3090a6fef3").unwrap(), )], ) .assert_dependencies( - "nested/Nested.t.sol:LibraryConsumer".to_string(), + "default/linking/nested/Nested.t.sol:LibraryConsumer".to_string(), vec![ // Lib shows up here twice, because the linker sees it twice, but it should // have the same address and nonce. ( - "nested/Nested.t.sol:Lib".to_string(), + "default/linking/nested/Nested.t.sol:Lib".to_string(), Address::from_str("0x5a443704dd4b594b382c22a083e2bd3090a6fef3") .unwrap(), ), ( - "nested/Nested.t.sol:NestedLib".to_string(), + "default/linking/nested/Nested.t.sol:NestedLib".to_string(), Address::from_str("0x47e9Fbef8C83A1714F1951F142132E6e90F5fa5D") .unwrap(), ), ], ) .assert_dependencies( - "nested/Nested.t.sol:NestedLibraryLinkingTest".to_string(), + "default/linking/nested/Nested.t.sol:NestedLibraryLinkingTest".to_string(), vec![ ( - "nested/Nested.t.sol:Lib".to_string(), + "default/linking/nested/Nested.t.sol:Lib".to_string(), Address::from_str("0x5a443704dd4b594b382c22a083e2bd3090a6fef3") .unwrap(), ), ( - "nested/Nested.t.sol:NestedLib".to_string(), + "default/linking/nested/Nested.t.sol:NestedLib".to_string(), Address::from_str("0x47e9fbef8c83a1714f1951f142132e6e90f5fa5d") .unwrap(), ), @@ -387,94 +478,101 @@ mod tests { #[test] fn link_duplicate() { - link_test("../../testdata/linking/duplicate", |linker| { + link_test("../../testdata/default/linking/duplicate", |linker| { linker - .assert_dependencies("duplicate/Duplicate.t.sol:A".to_string(), vec![]) - .assert_dependencies("duplicate/Duplicate.t.sol:B".to_string(), vec![]) .assert_dependencies( - "duplicate/Duplicate.t.sol:C".to_string(), + "default/linking/duplicate/Duplicate.t.sol:A".to_string(), + vec![], + ) + .assert_dependencies( + "default/linking/duplicate/Duplicate.t.sol:B".to_string(), + vec![], + ) + .assert_dependencies( + "default/linking/duplicate/Duplicate.t.sol:C".to_string(), vec![( - "duplicate/Duplicate.t.sol:A".to_string(), + "default/linking/duplicate/Duplicate.t.sol:A".to_string(), Address::from_str("0x5a443704dd4b594b382c22a083e2bd3090a6fef3").unwrap(), )], ) .assert_dependencies( - "duplicate/Duplicate.t.sol:D".to_string(), + "default/linking/duplicate/Duplicate.t.sol:D".to_string(), vec![( - "duplicate/Duplicate.t.sol:B".to_string(), + "default/linking/duplicate/Duplicate.t.sol:B".to_string(), Address::from_str("0x5a443704dd4b594b382c22a083e2bd3090a6fef3").unwrap(), )], ) .assert_dependencies( - "duplicate/Duplicate.t.sol:E".to_string(), + "default/linking/duplicate/Duplicate.t.sol:E".to_string(), vec![ ( - "duplicate/Duplicate.t.sol:A".to_string(), + "default/linking/duplicate/Duplicate.t.sol:A".to_string(), Address::from_str("0x5a443704dd4b594b382c22a083e2bd3090a6fef3") .unwrap(), ), ( - "duplicate/Duplicate.t.sol:C".to_string(), + "default/linking/duplicate/Duplicate.t.sol:C".to_string(), Address::from_str("0x47e9fbef8c83a1714f1951f142132e6e90f5fa5d") .unwrap(), ), ], ) .assert_dependencies( - "duplicate/Duplicate.t.sol:LibraryConsumer".to_string(), + "default/linking/duplicate/Duplicate.t.sol:LibraryConsumer".to_string(), vec![ ( - "duplicate/Duplicate.t.sol:A".to_string(), + "default/linking/duplicate/Duplicate.t.sol:A".to_string(), Address::from_str("0x5a443704dd4b594b382c22a083e2bd3090a6fef3") .unwrap(), ), ( - "duplicate/Duplicate.t.sol:B".to_string(), + "default/linking/duplicate/Duplicate.t.sol:B".to_string(), Address::from_str("0x47e9fbef8c83a1714f1951f142132e6e90f5fa5d") .unwrap(), ), ( - "duplicate/Duplicate.t.sol:C".to_string(), + "default/linking/duplicate/Duplicate.t.sol:C".to_string(), Address::from_str("0x8be503bcded90ed42eff31f56199399b2b0154ca") .unwrap(), ), ( - "duplicate/Duplicate.t.sol:D".to_string(), + "default/linking/duplicate/Duplicate.t.sol:D".to_string(), Address::from_str("0x47c5e40890bce4a473a49d7501808b9633f29782") .unwrap(), ), ( - "duplicate/Duplicate.t.sol:E".to_string(), + "default/linking/duplicate/Duplicate.t.sol:E".to_string(), Address::from_str("0x29b2440db4a256b0c1e6d3b4cdcaa68e2440a08f") .unwrap(), ), ], ) .assert_dependencies( - "duplicate/Duplicate.t.sol:DuplicateLibraryLinkingTest".to_string(), + "default/linking/duplicate/Duplicate.t.sol:DuplicateLibraryLinkingTest" + .to_string(), vec![ ( - "duplicate/Duplicate.t.sol:A".to_string(), + "default/linking/duplicate/Duplicate.t.sol:A".to_string(), Address::from_str("0x5a443704dd4b594b382c22a083e2bd3090a6fef3") .unwrap(), ), ( - "duplicate/Duplicate.t.sol:B".to_string(), + "default/linking/duplicate/Duplicate.t.sol:B".to_string(), Address::from_str("0x47e9fbef8c83a1714f1951f142132e6e90f5fa5d") .unwrap(), ), ( - "duplicate/Duplicate.t.sol:C".to_string(), + "default/linking/duplicate/Duplicate.t.sol:C".to_string(), Address::from_str("0x8be503bcded90ed42eff31f56199399b2b0154ca") .unwrap(), ), ( - "duplicate/Duplicate.t.sol:D".to_string(), + "default/linking/duplicate/Duplicate.t.sol:D".to_string(), Address::from_str("0x47c5e40890bce4a473a49d7501808b9633f29782") .unwrap(), ), ( - "duplicate/Duplicate.t.sol:E".to_string(), + "default/linking/duplicate/Duplicate.t.sol:E".to_string(), Address::from_str("0x29b2440db4a256b0c1e6d3b4cdcaa68e2440a08f") .unwrap(), ), @@ -486,33 +584,33 @@ mod tests { #[test] fn link_cycle() { - link_test("../../testdata/linking/cycle", |linker| { + link_test("../../testdata/default/linking/cycle", |linker| { linker .assert_dependencies( - "cycle/Cycle.t.sol:Foo".to_string(), + "default/linking/cycle/Cycle.t.sol:Foo".to_string(), vec![ ( - "cycle/Cycle.t.sol:Foo".to_string(), + "default/linking/cycle/Cycle.t.sol:Foo".to_string(), Address::from_str("0x47e9Fbef8C83A1714F1951F142132E6e90F5fa5D") .unwrap(), ), ( - "cycle/Cycle.t.sol:Bar".to_string(), + "default/linking/cycle/Cycle.t.sol:Bar".to_string(), Address::from_str("0x5a443704dd4B594B382c22a083e2BD3090A6feF3") .unwrap(), ), ], ) .assert_dependencies( - "cycle/Cycle.t.sol:Bar".to_string(), + "default/linking/cycle/Cycle.t.sol:Bar".to_string(), vec![ ( - "cycle/Cycle.t.sol:Foo".to_string(), + "default/linking/cycle/Cycle.t.sol:Foo".to_string(), Address::from_str("0x47e9Fbef8C83A1714F1951F142132E6e90F5fa5D") .unwrap(), ), ( - "cycle/Cycle.t.sol:Bar".to_string(), + "default/linking/cycle/Cycle.t.sol:Bar".to_string(), Address::from_str("0x5a443704dd4B594B382c22a083e2BD3090A6feF3") .unwrap(), ), @@ -521,4 +619,57 @@ mod tests { .test_with_sender_and_nonce(Address::default(), 1); }); } + + #[test] + fn link_create2_nested() { + link_test("../../testdata/default/linking/nested", |linker| { + linker + .assert_dependencies("default/linking/nested/Nested.t.sol:Lib".to_string(), vec![]) + .assert_dependencies( + "default/linking/nested/Nested.t.sol:NestedLib".to_string(), + vec![( + "default/linking/nested/Nested.t.sol:Lib".to_string(), + Address::from_str("0xCD3864eB2D88521a5477691EE589D9994b796834").unwrap(), + )], + ) + .assert_dependencies( + "default/linking/nested/Nested.t.sol:LibraryConsumer".to_string(), + vec![ + // Lib shows up here twice, because the linker sees it twice, but it should + // have the same address and nonce. + ( + "default/linking/nested/Nested.t.sol:Lib".to_string(), + Address::from_str("0xCD3864eB2D88521a5477691EE589D9994b796834") + .unwrap(), + ), + ( + "default/linking/nested/Nested.t.sol:NestedLib".to_string(), + Address::from_str("0x023d9a6bfA39c45997572dC4F87b3E2713b6EBa4") + .unwrap(), + ), + ], + ) + .assert_dependencies( + "default/linking/nested/Nested.t.sol:NestedLibraryLinkingTest".to_string(), + vec![ + ( + "default/linking/nested/Nested.t.sol:Lib".to_string(), + Address::from_str("0xCD3864eB2D88521a5477691EE589D9994b796834") + .unwrap(), + ), + ( + "default/linking/nested/Nested.t.sol:NestedLib".to_string(), + Address::from_str("0x023d9a6bfA39c45997572dC4F87b3E2713b6EBa4") + .unwrap(), + ), + ], + ) + .test_with_create2( + Address::default(), + fixed_bytes!( + "19bf59b7b67ae8edcbc6e53616080f61fa99285c061450ad601b0bc40c9adfc9" + ), + ); + }); + } } diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index 8c41cfde6..8d9907ccf 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -5,8 +5,7 @@ use crate::{ sequence::{ScriptSequence, ScriptSequenceKind}, ScriptArgs, ScriptConfig, }; - -use alloy_primitives::{Address, Bytes}; +use alloy_primitives::{Bytes, B256}; use alloy_provider::Provider; use eyre::{OptionExt, Result}; use foundry_cheatcodes::ScriptWallets; @@ -21,10 +20,12 @@ use foundry_compilers::{ utils::source_files_iter, ArtifactId, ProjectCompileOutput, SOLC_EXTENSIONS, }; -use foundry_linking::{LinkOutput, Linker}; +use foundry_evm::constants::DEFAULT_CREATE2_DEPLOYER; +use foundry_linking::Linker; use std::{path::PathBuf, str::FromStr, sync::Arc}; /// Container for the compiled contracts. +#[derive(Debug)] pub struct BuildData { /// Root of the project pub project_root: PathBuf, @@ -39,43 +40,81 @@ impl BuildData { Linker::new(self.project_root.clone(), self.output.artifact_ids().collect()) } - /// Links the build data with given libraries, using sender and nonce to compute addresses of - /// missing libraries. - pub fn link( - self, - known_libraries: Libraries, - sender: Address, - nonce: u64, - ) -> Result { - let link_output = self.get_linker().link_with_nonce_or_address( - known_libraries, - sender, - nonce, - &self.target, - )?; + /// Links contracts. Uses CREATE2 linking when possible, otherwise falls back to + /// default linking with sender nonce and address. + pub async fn link(self, script_config: &ScriptConfig) -> Result { + let can_use_create2 = if let Some(fork_url) = &script_config.evm_opts.fork_url { + let provider = try_get_http_provider(fork_url)?; + let deployer_code = + provider.get_code_at(DEFAULT_CREATE2_DEPLOYER, Default::default()).await?; + + !deployer_code.is_empty() + } else { + // If --fork-url is not provided, we are just simulating the script. + true + }; + + let known_libraries = script_config.config.libraries_with_remappings()?; + + let maybe_create2_link_output = can_use_create2 + .then(|| { + self.get_linker() + .link_with_create2( + known_libraries.clone(), + DEFAULT_CREATE2_DEPLOYER, + script_config.config.create2_library_salt, + &self.target, + ) + .ok() + }) + .flatten(); + + let (libraries, predeploy_libs) = if let Some(output) = maybe_create2_link_output { + ( + output.libraries, + ScriptPredeployLibraries::Create2( + output.libs_to_deploy, + script_config.config.create2_library_salt, + ), + ) + } else { + let output = self.get_linker().link_with_nonce_or_address( + known_libraries, + script_config.evm_opts.sender, + script_config.sender_nonce, + &self.target, + )?; - LinkedBuildData::new(link_output, self) + (output.libraries, ScriptPredeployLibraries::Default(output.libs_to_deploy)) + }; + + LinkedBuildData::new(libraries, predeploy_libs, self) } /// Links the build data with the given libraries. Expects supplied libraries set being enough /// to fully link target contract. pub fn link_with_libraries(self, libraries: Libraries) -> Result { - let link_output = self.get_linker().link_with_nonce_or_address( - libraries, - Address::ZERO, - 0, - &self.target, - )?; + LinkedBuildData::new(libraries, ScriptPredeployLibraries::Default(Vec::new()), self) + } +} - if !link_output.libs_to_deploy.is_empty() { - eyre::bail!("incomplete libraries set"); - } +#[derive(Debug)] +pub enum ScriptPredeployLibraries { + Default(Vec), + Create2(Vec, B256), +} - LinkedBuildData::new(link_output, self) +impl ScriptPredeployLibraries { + pub fn libraries_count(&self) -> usize { + match self { + Self::Default(libs) => libs.len(), + Self::Create2(libs, _) => libs.len(), + } } } /// Container for the linked contracts and their dependencies +#[derive(Debug)] pub struct LinkedBuildData { /// Original build data, might be used to relink this object with different libraries. pub build_data: BuildData, @@ -84,30 +123,27 @@ pub struct LinkedBuildData { /// Libraries used to link the contracts. pub libraries: Libraries, /// Libraries that need to be deployed by sender before script execution. - pub predeploy_libraries: Vec, + pub predeploy_libraries: ScriptPredeployLibraries, /// Source files of the contracts. Used by debugger. pub sources: ContractSources, } impl LinkedBuildData { - pub fn new(link_output: LinkOutput, build_data: BuildData) -> Result { + pub fn new( + libraries: Libraries, + predeploy_libraries: ScriptPredeployLibraries, + build_data: BuildData, + ) -> Result { let sources = ContractSources::from_project_output( &build_data.output, &build_data.project_root, - &link_output.libraries, + &libraries, )?; - let known_contracts = ContractsByArtifact::new( - build_data.get_linker().get_linked_artifacts(&link_output.libraries)?, - ); + let known_contracts = + ContractsByArtifact::new(build_data.get_linker().get_linked_artifacts(&libraries)?); - Ok(Self { - build_data, - known_contracts, - libraries: link_output.libraries, - predeploy_libraries: link_output.libs_to_deploy, - sources, - }) + Ok(Self { build_data, known_contracts, libraries, predeploy_libraries, sources }) } /// Fetches target bytecode from linked contracts. @@ -212,13 +248,10 @@ pub struct CompiledState { impl CompiledState { /// Uses provided sender address to compute library addresses and link contracts with them. - pub fn link(self) -> Result { + pub async fn link(self) -> Result { let Self { args, script_config, script_wallets, build_data } = self; - let sender = script_config.evm_opts.sender; - let nonce = script_config.sender_nonce; - let known_libraries = script_config.config.libraries_with_remappings()?; - let build_data = build_data.link(known_libraries, sender, nonce)?; + let build_data = build_data.link(&script_config).await?; Ok(LinkedState { args, script_config, script_wallets, build_data }) } @@ -269,7 +302,7 @@ impl CompiledState { if !froms.all(|from| available_signers.contains(&from)) { // IF we are missing required signers, execute script as we might need to collect // private keys from the execution. - let executed = self.link()?.prepare_execution().await?.execute().await?; + let executed = self.link().await?.prepare_execution().await?.execute().await?; ( executed.args, executed.build_data.build_data, diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index 9e988c09b..a0c8397ca 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -9,7 +9,6 @@ use alloy_dyn_abi::FunctionExt; use alloy_json_abi::{Function, InternalType, JsonAbi}; use alloy_primitives::{Address, Bytes}; use alloy_provider::Provider; -use alloy_rpc_types::request::TransactionRequest; use async_recursion::async_recursion; use eyre::{OptionExt, Result}; use foundry_cheatcodes::ScriptWallets; @@ -23,7 +22,7 @@ use foundry_config::{Config, NamedChain}; use foundry_debugger::Debugger; use foundry_evm::{ decode::decode_console_logs, - inspectors::cheatcodes::{BroadcastableTransaction, BroadcastableTransactions}, + inspectors::cheatcodes::BroadcastableTransactions, traces::{ identifier::{SignaturesIdentifier, TraceIdentifiers}, render_trace_arena, CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, @@ -44,6 +43,7 @@ pub struct LinkedState { } /// Container for data we need for execution which can only be obtained after linking stage. +#[derive(Debug)] pub struct ExecutionData { /// Function to call. pub func: Function, @@ -80,6 +80,7 @@ impl LinkedState { } /// Same as [LinkedState], but also contains [ExecutionData]. +#[derive(Debug)] pub struct PreExecutionState { pub args: ScriptArgs, pub script_config: ScriptConfig, @@ -102,7 +103,7 @@ impl PreExecutionState { self.build_data.build_data.target.clone(), ) .await?; - let mut result = self.execute_with_runner(&mut runner).await?; + let result = self.execute_with_runner(&mut runner).await?; // If we have a new sender from execution, we need to use it to deploy libraries and relink // contracts. @@ -117,28 +118,7 @@ impl PreExecutionState { build_data: self.build_data.build_data, }; - return state.link()?.prepare_execution().await?.execute().await; - } - - // Add library deployment transactions to broadcastable transactions list. - if let Some(txs) = result.transactions.take() { - result.transactions = Some( - self.build_data - .predeploy_libraries - .iter() - .enumerate() - .map(|(i, bytes)| BroadcastableTransaction { - rpc: self.script_config.evm_opts.fork_url.clone(), - transaction: TransactionRequest { - from: Some(self.script_config.evm_opts.sender), - input: Some(bytes.clone()).into(), - nonce: Some(self.script_config.sender_nonce + i as u64), - ..Default::default() - }, - }) - .chain(txs) - .collect(), - ); + return state.link().await?.prepare_execution().await?.execute().await; } Ok(ExecutedState { @@ -200,7 +180,7 @@ impl PreExecutionState { if let Some(txs) = transactions { // If the user passed a `--sender` don't check anything. - if !self.build_data.predeploy_libraries.is_empty() && + if self.build_data.predeploy_libraries.libraries_count() > 0 && self.args.evm_opts.sender.is_none() { for tx in txs.iter() { diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 6445b8450..7b84802b1 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -228,7 +228,8 @@ impl ScriptArgs { } else { // Drive state machine to point at which we have everything needed for simulation. let pre_simulation = compiled - .link()? + .link() + .await? .prepare_execution() .await? .execute() @@ -600,11 +601,7 @@ impl ScriptConfig { }); } - Ok(ScriptRunner::new( - builder.build(env, db), - self.evm_opts.initial_balance, - self.evm_opts.sender, - )) + Ok(ScriptRunner::new(builder.build(env, db), self.evm_opts.clone())) } } diff --git a/crates/script/src/runner.rs b/crates/script/src/runner.rs index 901e5e58a..0321d0c5e 100644 --- a/crates/script/src/runner.rs +++ b/crates/script/src/runner.rs @@ -1,32 +1,36 @@ use super::ScriptResult; -use alloy_primitives::{Address, Bytes, U256}; +use crate::build::ScriptPredeployLibraries; +use alloy_primitives::{Address, Bytes, TxKind, U256}; +use alloy_rpc_types::TransactionRequest; use eyre::Result; +use foundry_cheatcodes::BroadcastableTransaction; use foundry_config::Config; use foundry_evm::{ - constants::CALLER, + constants::{CALLER, DEFAULT_CREATE2_DEPLOYER}, executors::{DeployResult, EvmError, ExecutionErr, Executor, RawCallResult}, + opts::EvmOpts, revm::interpreter::{return_ok, InstructionResult}, traces::{TraceKind, Traces}, }; +use std::collections::VecDeque; use yansi::Paint; /// Drives script execution #[derive(Debug)] pub struct ScriptRunner { pub executor: Executor, - pub initial_balance: U256, - pub sender: Address, + pub evm_opts: EvmOpts, } impl ScriptRunner { - pub fn new(executor: Executor, initial_balance: U256, sender: Address) -> Self { - Self { executor, initial_balance, sender } + pub fn new(executor: Executor, evm_opts: EvmOpts) -> Self { + Self { executor, evm_opts } } /// Deploys the libraries and broadcast contract. Calls setUp method if requested. pub fn setup( &mut self, - libraries: &[Bytes], + libraries: &ScriptPredeployLibraries, code: Bytes, setup: bool, sender_nonce: u64, @@ -36,9 +40,9 @@ impl ScriptRunner { trace!(target: "script", "executing setUP()"); if !is_broadcast { - if self.sender == Config::DEFAULT_SENDER { + if self.evm_opts.sender == Config::DEFAULT_SENDER { // We max out their balance so that they can deploy and make calls. - self.executor.set_balance(self.sender, U256::MAX)?; + self.executor.set_balance(self.evm_opts.sender, U256::MAX)?; } if need_create2_deployer { @@ -46,29 +50,86 @@ impl ScriptRunner { } } - self.executor.set_nonce(self.sender, sender_nonce)?; + self.executor.set_nonce(self.evm_opts.sender, sender_nonce)?; // We max out their balance so that they can deploy and make calls. self.executor.set_balance(CALLER, U256::MAX)?; + let mut library_transactions = VecDeque::new(); + let mut traces = Traces::default(); + // Deploy libraries - let mut traces: Traces = libraries - .iter() - .filter_map(|code| { - self.executor - .deploy(self.sender, code.clone(), U256::ZERO, None) + match libraries { + ScriptPredeployLibraries::Default(libraries) => libraries.iter().for_each(|code| { + let result = self + .executor + .deploy(self.evm_opts.sender, code.clone(), U256::ZERO, None) .expect("couldn't deploy library") - .raw - .traces - }) - .map(|traces| (TraceKind::Deployment, traces)) - .collect(); + .raw; + + if let Some(deploy_traces) = result.traces { + traces.push((TraceKind::Deployment, deploy_traces)); + } + + library_transactions.push_back(BroadcastableTransaction { + rpc: self.evm_opts.fork_url.clone(), + transaction: TransactionRequest { + from: Some(self.evm_opts.sender), + input: Some(code.clone()).into(), + nonce: Some(sender_nonce + library_transactions.len() as u64), + ..Default::default() + }, + }) + }), + ScriptPredeployLibraries::Create2(libraries, salt) => { + for library in libraries { + let address = + DEFAULT_CREATE2_DEPLOYER.create2_from_code(salt, library.as_ref()); + // Skip if already deployed + if !self.executor.is_empty_code(address)? { + continue; + } + let calldata = [salt.as_ref(), library.as_ref()].concat(); + let result = self + .executor + .call_raw_committing( + self.evm_opts.sender, + DEFAULT_CREATE2_DEPLOYER, + calldata.clone().into(), + U256::from(0), + ) + .expect("couldn't deploy library"); + + if let Some(deploy_traces) = result.traces { + traces.push((TraceKind::Deployment, deploy_traces)); + } + + library_transactions.push_back(BroadcastableTransaction { + rpc: self.evm_opts.fork_url.clone(), + transaction: TransactionRequest { + from: Some(self.evm_opts.sender), + input: Some(calldata.into()).into(), + nonce: Some(sender_nonce + library_transactions.len() as u64), + to: Some(TxKind::Call(DEFAULT_CREATE2_DEPLOYER)), + ..Default::default() + }, + }); + } + + // Sender nonce is not incremented when performing CALLs. We need to manually + // increase it. + self.executor.set_nonce( + self.evm_opts.sender, + sender_nonce + library_transactions.len() as u64, + )?; + } + }; let address = CALLER.create(self.executor.get_nonce(CALLER)?); // Set the contracts initial balance before deployment, so it is available during the // construction - self.executor.set_balance(address, self.initial_balance)?; + self.executor.set_balance(address, self.evm_opts.initial_balance)?; // Deploy an instance of the contract let DeployResult { @@ -85,9 +146,15 @@ impl ScriptRunner { // Optionally call the `setUp` function let (success, gas_used, labeled_addresses, transactions, debug) = if !setup { self.executor.backend.set_test_contract(address); - (true, 0, Default::default(), None, vec![constructor_debug].into_iter().collect()) + ( + true, + 0, + Default::default(), + Some(library_transactions), + vec![constructor_debug].into_iter().collect(), + ) } else { - match self.executor.setup(Some(self.sender), address, None) { + match self.executor.setup(Some(self.evm_opts.sender), address, None) { Ok(RawCallResult { reverted, traces: setup_traces, @@ -95,17 +162,21 @@ impl ScriptRunner { logs: setup_logs, debug, gas_used, - transactions, + transactions: setup_transactions, .. }) => { traces.extend(setup_traces.map(|traces| (TraceKind::Setup, traces))); logs.extend_from_slice(&setup_logs); + if let Some(txs) = setup_transactions { + library_transactions.extend(txs); + } + ( !reverted, gas_used, labels, - transactions, + Some(library_transactions), vec![constructor_debug, debug].into_iter().collect(), ) } @@ -123,11 +194,15 @@ impl ScriptRunner { traces.extend(setup_traces.map(|traces| (TraceKind::Setup, traces))); logs.extend_from_slice(&setup_logs); + if let Some(txs) = transactions { + library_transactions.extend(txs); + } + ( !reverted, gas_used, labels, - transactions, + Some(library_transactions), vec![constructor_debug, debug].into_iter().collect(), ) } @@ -154,7 +229,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.sender, address, calldata, U256::ZERO, false) + self.call(self.evm_opts.sender, address, calldata, U256::ZERO, false) } /// Runs a broadcastable transaction locally and persists its state. diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index b14ba2a54..8b625d6ee 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -4,11 +4,41 @@ use alloy_provider::Provider; use alloy_rpc_types::BlockId; use eyre::Result; use foundry_common::provider::{get_http_provider, RetryProvider}; -use std::{collections::BTreeMap, fs, path::Path, str::FromStr}; +use std::{ + collections::BTreeMap, + fs, + path::{Path, PathBuf}, + str::FromStr, +}; const BROADCAST_TEST_PATH: &str = "src/Broadcast.t.sol"; const TESTDATA: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../testdata"); +fn init_script_cmd( + cmd: &mut TestCommand, + project_root: &Path, + target_contract: &str, + endpoint: Option<&str>, +) { + cmd.forge_fuse(); + cmd.set_current_dir(project_root); + + cmd.args([ + "script", + "-R", + "ds-test/=lib/", + "-R", + "cheats/=cheats/", + target_contract, + "--root", + project_root.to_str().unwrap(), + "-vvvvv", + ]); + + if let Some(rpc_url) = endpoint { + cmd.args(["--fork-url", rpc_url]); + } +} /// A helper struct to test forge script scenarios pub struct ScriptTester { pub accounts_pub: Vec
, @@ -17,6 +47,9 @@ pub struct ScriptTester { pub nonces: BTreeMap, pub address_nonces: BTreeMap, pub cmd: TestCommand, + pub project_root: PathBuf, + pub target_contract: String, + pub endpoint: Option, } impl ScriptTester { @@ -29,23 +62,10 @@ impl ScriptTester { ) -> Self { init_tracing(); ScriptTester::copy_testdata(project_root).unwrap(); - cmd.set_current_dir(project_root); - - cmd.args([ - "script", - "-R", - "ds-test/=lib/", - "-R", - "cheats/=cheats/", - target_contract, - "--root", - project_root.to_str().unwrap(), - "-vvvvv", - ]); + init_script_cmd(&mut cmd, project_root, target_contract, endpoint); let mut provider = None; if let Some(endpoint) = endpoint { - cmd.args(["--fork-url", endpoint]); provider = Some(get_http_provider(endpoint)) } @@ -64,6 +84,9 @@ impl ScriptTester { nonces: BTreeMap::default(), address_nonces: BTreeMap::default(), cmd, + project_root: project_root.to_path_buf(), + target_contract: target_contract.to_string(), + endpoint: endpoint.map(|s| s.to_string()), } } @@ -243,6 +266,17 @@ impl ScriptTester { self.cmd.args(args); self } + + pub fn clear(&mut self) { + init_script_cmd( + &mut self.cmd, + &self.project_root, + &self.target_contract, + self.endpoint.as_deref(), + ); + self.nonces.clear(); + self.address_nonces.clear(); + } } /// Various `forge` script results From 12eaec8dbe04625381d974fa0ea2c8a03230d082 Mon Sep 17 00:00:00 2001 From: bixia <343224563@qq.com> Date: Wed, 15 May 2024 02:54:09 +0800 Subject: [PATCH 282/430] forge(verify): OKLink support (#7915) * update * Update provider.rs remove the requirement * cargo fmt --- crates/verify/src/provider.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/verify/src/provider.rs b/crates/verify/src/provider.rs index d5721018d..deac48229 100644 --- a/crates/verify/src/provider.rs +++ b/crates/verify/src/provider.rs @@ -33,6 +33,7 @@ impl FromStr for VerificationProviderType { "e" | "etherscan" => Ok(VerificationProviderType::Etherscan), "s" | "sourcify" => Ok(VerificationProviderType::Sourcify), "b" | "blockscout" => Ok(VerificationProviderType::Blockscout), + "o" | "oklink" => Ok(VerificationProviderType::Oklink), _ => Err(format!("Unknown provider: {s}")), } } @@ -50,6 +51,9 @@ impl fmt::Display for VerificationProviderType { VerificationProviderType::Blockscout => { write!(f, "blockscout")?; } + VerificationProviderType::Oklink => { + write!(f, "oklink")?; + } }; Ok(()) } @@ -61,6 +65,7 @@ pub enum VerificationProviderType { Etherscan, Sourcify, Blockscout, + Oklink, } impl VerificationProviderType { @@ -79,6 +84,7 @@ impl VerificationProviderType { VerificationProviderType::Blockscout => { Ok(Box::::default()) } + VerificationProviderType::Oklink => Ok(Box::::default()), } } } From 7469d79cca59e0bb5f23563ac5a6bd5f2ec8c5e4 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 14 May 2024 21:25:47 +0200 Subject: [PATCH 283/430] chore(deps): bump chains (#7927) --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 796e47fbd..52a36a589 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,9 +67,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "alloy-chains" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe6c2674230e94ea98767550b02853bf7024b46f784827be95acfc5f5f1a445f" +checksum = "03fd095a9d70f4b1c5c102c84a4c782867a5c6416dbf6dcd42a63e7c7a89d3c8" dependencies = [ "num_enum", "serde", From a470d635cfcdce68609e9dc5762a3584351bacc1 Mon Sep 17 00:00:00 2001 From: Azleal Date: Wed, 15 May 2024 17:40:59 +0800 Subject: [PATCH 284/430] fix: fuzz console log (#7781) * fix fuzz console * test case * fuzz console * remove clone --- crates/evm/evm/src/executors/fuzz/mod.rs | 16 ++++++++--- crates/evm/evm/src/executors/fuzz/types.rs | 4 ++- crates/forge/tests/cli/test_cmd.rs | 31 +++++++++++++++++++++- 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index d81ddbaca..1adabc5fd 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -1,7 +1,7 @@ use crate::executors::{Executor, RawCallResult}; use alloy_dyn_abi::JsonAbiExt; use alloy_json_abi::Function; -use alloy_primitives::{Address, Bytes, U256}; +use alloy_primitives::{Address, Bytes, Log, U256}; use eyre::Result; use foundry_config::FuzzConfig; use foundry_evm_core::{ @@ -87,6 +87,9 @@ impl FuzzedExecutor { dictionary_weight => fuzz_calldata_from_state(func.clone(), &state), ]; + //Stores logs for all fuzz cases + let logs: RefCell> = RefCell::default(); + debug!(func=?func.name, should_fail, "fuzzing"); let run_result = self.runner.clone().run(&strat, |calldata| { let fuzz_res = self.single_fuzz(address, should_fail, calldata)?; @@ -104,6 +107,7 @@ impl FuzzedExecutor { } traces.borrow_mut().push(call_traces); } + logs.borrow_mut().extend(case.logs); if let Some(prev) = coverage.take() { // Safety: If `Option::or` evaluates to `Some`, then `call.coverage` must @@ -127,6 +131,7 @@ impl FuzzedExecutor { // to run at least one more case to find a minimal failure // case. let call_res = _counterexample.1.result.clone(); + logs.borrow_mut().extend(_counterexample.1.logs.clone()); *counterexample.borrow_mut() = _counterexample; // HACK: we have to use an empty string here to denote `None` let reason = rd.maybe_decode(&call_res, Some(status)); @@ -140,14 +145,16 @@ impl FuzzedExecutor { let mut traces = traces.into_inner(); let last_run_traces = if run_result.is_ok() { traces.pop() } else { call.traces.clone() }; + let inner_logs = logs.into_inner(); + let mut result = FuzzTestResult { first_case: first_case.take().unwrap_or_default(), gas_by_case: gas_by_case.take(), success: run_result.is_ok(), reason: None, counterexample: None, - decoded_logs: decode_console_logs(&call.logs), - logs: call.logs, + decoded_logs: decode_console_logs(&inner_logs), + logs: inner_logs, labeled_addresses: call.labels, traces: last_run_traces, gas_report_traces: traces, @@ -207,7 +214,7 @@ impl FuzzedExecutor { // When the `assume` cheatcode is called it returns a special string if call.result.as_ref() == MAGIC_ASSUME { - return Err(TestCaseError::reject(FuzzError::AssumeReject)) + return Err(TestCaseError::reject(FuzzError::AssumeReject)); } let breakpoints = call @@ -229,6 +236,7 @@ impl FuzzedExecutor { coverage: call.coverage, debug: call.debug, breakpoints, + logs: call.logs, })) } else { Ok(FuzzOutcome::CounterExample(CounterExampleOutcome { diff --git a/crates/evm/evm/src/executors/fuzz/types.rs b/crates/evm/evm/src/executors/fuzz/types.rs index b15cf3faa..3791165ca 100644 --- a/crates/evm/evm/src/executors/fuzz/types.rs +++ b/crates/evm/evm/src/executors/fuzz/types.rs @@ -1,5 +1,5 @@ use crate::executors::RawCallResult; -use alloy_primitives::Bytes; +use alloy_primitives::{Bytes, Log}; use foundry_common::evm::Breakpoints; use foundry_evm_core::debug::DebugArena; use foundry_evm_coverage::HitMaps; @@ -20,6 +20,8 @@ pub struct CaseOutcome { pub debug: Option, /// Breakpoints char pc map pub breakpoints: Breakpoints, + /// logs of a single fuzz test case + pub logs: Vec, } /// Returned by a single fuzz when a counterexample has been discovered diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 2b0784d6a..5fe9b388a 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -1,6 +1,6 @@ //! Contains various tests for `forge test`. -use foundry_config::Config; +use foundry_config::{Config, FuzzConfig}; use foundry_test_utils::{ rpc, util::{OutputExt, OTHER_SOLC_VERSION, SOLC_VERSION}, @@ -544,3 +544,32 @@ contract Dummy { cmd.args(["test", "--match-path", "src/dummy.sol"]); cmd.assert_success() }); + +// tests that `forge test` for fuzz tests will display `console.log` info +forgetest_init!(can_test_fuzz_with_console_log, |prj, cmd| { + prj.wipe_contracts(); + + // run fuzz test 3 times + let config = + Config { fuzz: { FuzzConfig { runs: 3, ..Default::default() } }, ..Default::default() }; + prj.write_config(config); + let config = cmd.config(); + assert_eq!(config.fuzz.runs, 3); + + prj.add_test( + "ContractFuzz.t.sol", + r#"pragma solidity 0.8.24; + import {Test, console2} from "forge-std/Test.sol"; + + contract ContractFuzz is Test { + function testFuzzConsoleLog(uint256 x) public { + console2.log("inside fuzz test, x is:", x); + } + } + "#, + ) + .unwrap(); + cmd.args(["test", "-vv"]); + let stdout = cmd.stdout_lossy(); + assert!(stdout.contains("inside fuzz test, x is:"), "\n{stdout}"); +}); From 48e5d112f7675d1bb5b8fd3fa2d1ae7df4bd8245 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Thu, 16 May 2024 10:16:21 -0400 Subject: [PATCH 285/430] bump alloy f415827 --- Cargo.lock | 217 ++++++++++++++++++--------- Cargo.toml | 51 ++++--- crates/anvil/src/eth/backend/fork.rs | 19 ++- crates/cast/bin/cmd/estimate.rs | 2 +- crates/cast/bin/cmd/mktx.rs | 5 +- crates/cast/bin/cmd/send.rs | 10 +- crates/cast/bin/cmd/storage.rs | 9 +- crates/cast/bin/main.rs | 3 +- crates/cast/bin/tx.rs | 4 +- crates/cast/src/lib.rs | 42 ++++-- crates/evm/core/src/fork/backend.rs | 14 +- crates/forge/bin/cmd/create.rs | 4 +- crates/script/src/broadcast.rs | 7 +- crates/script/src/build.rs | 3 +- crates/test-utils/src/script.rs | 12 +- crates/verify/src/bytecode.rs | 13 +- 16 files changed, 270 insertions(+), 145 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 52a36a589..6eb52da31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -81,10 +81,23 @@ name = "alloy-consensus" version = "0.1.0" source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ - "alloy-eips", + "alloy-eips 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=899fc51)", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=899fc51)", + "c-kzg", + "serde", +] + +[[package]] +name = "alloy-consensus" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +dependencies = [ + "alloy-eips 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-primitives", + "alloy-rlp", + "alloy-serde 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "c-kzg", "serde", ] @@ -92,7 +105,7 @@ dependencies = [ [[package]] name = "alloy-contract" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -100,7 +113,7 @@ dependencies = [ "alloy-primitives", "alloy-provider", "alloy-pubsub", - "alloy-rpc-types", + "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-sol-types", "alloy-transport", "futures", @@ -136,7 +149,21 @@ source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1 dependencies = [ "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=899fc51)", + "c-kzg", + "once_cell", + "serde", + "sha2", +] + +[[package]] +name = "alloy-eips" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "alloy-serde 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "c-kzg", "derive_more", "once_cell", @@ -150,7 +177,18 @@ version = "0.1.0" source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-primitives", - "alloy-serde", + "alloy-serde 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=899fc51)", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-genesis" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +dependencies = [ + "alloy-primitives", + "alloy-serde 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "serde", "serde_json", ] @@ -170,7 +208,7 @@ dependencies = [ [[package]] name = "alloy-json-rpc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-primitives", "serde", @@ -182,13 +220,13 @@ dependencies = [ [[package]] name = "alloy-network" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ - "alloy-consensus", - "alloy-eips", + "alloy-consensus 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-eips 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-json-rpc", "alloy-primitives", - "alloy-rpc-types", + "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-signer", "alloy-sol-types", "async-trait", @@ -226,16 +264,16 @@ dependencies = [ [[package]] name = "alloy-provider" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ - "alloy-eips", + "alloy-eips 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-json-rpc", "alloy-network", "alloy-primitives", "alloy-pubsub", "alloy-rpc-client", - "alloy-rpc-types", - "alloy-rpc-types-trace", + "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-rpc-types-trace 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-transport", "alloy-transport-http", "alloy-transport-ipc", @@ -247,6 +285,7 @@ dependencies = [ "futures", "futures-utils-wasm", "lru", + "pin-project", "reqwest", "serde_json", "tokio", @@ -257,7 +296,7 @@ dependencies = [ [[package]] name = "alloy-pubsub" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -297,7 +336,7 @@ dependencies = [ [[package]] name = "alloy-rpc-client" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -323,12 +362,30 @@ name = "alloy-rpc-types" version = "0.1.0" source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-genesis", + "alloy-consensus 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=899fc51)", + "alloy-eips 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=899fc51)", + "alloy-genesis 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=899fc51)", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=899fc51)", + "alloy-sol-types", + "itertools 0.12.1", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "alloy-rpc-types" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +dependencies = [ + "alloy-consensus 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-eips 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-genesis 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-primitives", + "alloy-rlp", + "alloy-serde 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-sol-types", "itertools 0.12.1", "serde", @@ -339,14 +396,14 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ - "alloy-consensus", - "alloy-eips", + "alloy-consensus 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-eips 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-primitives", "alloy-rlp", - "alloy-rpc-types", - "alloy-serde", + "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-serde 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "jsonwebtoken", "rand", "serde", @@ -359,8 +416,20 @@ version = "0.1.0" source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-primitives", - "alloy-rpc-types", - "alloy-serde", + "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=899fc51)", + "alloy-serde 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=899fc51)", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-rpc-types-trace" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-serde 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "serde", "serde_json", ] @@ -375,10 +444,20 @@ dependencies = [ "serde_json", ] +[[package]] +name = "alloy-serde" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", +] + [[package]] name = "alloy-signer" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -393,9 +472,9 @@ dependencies = [ [[package]] name = "alloy-signer-aws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ - "alloy-consensus", + "alloy-consensus 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-network", "alloy-primitives", "alloy-signer", @@ -410,9 +489,9 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ - "alloy-consensus", + "alloy-consensus 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-dyn-abi", "alloy-network", "alloy-primitives", @@ -429,9 +508,9 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ - "alloy-consensus", + "alloy-consensus 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-network", "alloy-primitives", "alloy-signer", @@ -445,9 +524,9 @@ dependencies = [ [[package]] name = "alloy-signer-wallet" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ - "alloy-consensus", + "alloy-consensus 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-network", "alloy-primitives", "alloy-signer", @@ -522,7 +601,7 @@ dependencies = [ [[package]] name = "alloy-transport" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-json-rpc", "base64 0.22.1", @@ -540,7 +619,7 @@ dependencies = [ [[package]] name = "alloy-transport-http" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -554,7 +633,7 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -574,7 +653,7 @@ dependencies = [ [[package]] name = "alloy-transport-ws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -691,11 +770,11 @@ name = "anvil" version = "0.2.0" dependencies = [ "alloy-chains", - "alloy-consensus", + "alloy-consensus 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-contract", "alloy-dyn-abi", - "alloy-eips", - "alloy-genesis", + "alloy-eips 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-genesis 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-json-abi", "alloy-json-rpc", "alloy-network", @@ -704,8 +783,8 @@ dependencies = [ "alloy-pubsub", "alloy-rlp", "alloy-rpc-client", - "alloy-rpc-types", - "alloy-rpc-types-trace", + "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-rpc-types-trace 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-signer", "alloy-signer-wallet", "alloy-sol-types", @@ -759,15 +838,15 @@ dependencies = [ name = "anvil-core" version = "0.2.0" dependencies = [ - "alloy-consensus", + "alloy-consensus 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-dyn-abi", - "alloy-eips", + "alloy-eips 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-network", "alloy-primitives", "alloy-rlp", - "alloy-rpc-types", - "alloy-rpc-types-trace", - "alloy-serde", + "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-rpc-types-trace 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-serde 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-trie", "anvil-core", "bytes", @@ -1729,7 +1808,7 @@ name = "cast" version = "0.2.0" dependencies = [ "alloy-chains", - "alloy-consensus", + "alloy-consensus 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-contract", "alloy-dyn-abi", "alloy-json-abi", @@ -1738,7 +1817,7 @@ dependencies = [ "alloy-primitives", "alloy-provider", "alloy-rlp", - "alloy-rpc-types", + "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-signer", "alloy-signer-wallet", "alloy-sol-types", @@ -1822,7 +1901,7 @@ dependencies = [ "alloy-dyn-abi", "alloy-json-abi", "alloy-primitives", - "alloy-rpc-types", + "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "clap", "criterion", "dirs 5.0.1", @@ -3064,13 +3143,13 @@ name = "forge" version = "0.2.0" dependencies = [ "alloy-chains", - "alloy-consensus", + "alloy-consensus 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-dyn-abi", "alloy-json-abi", "alloy-network", "alloy-primitives", "alloy-provider", - "alloy-rpc-types", + "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-signer", "alloy-signer-wallet", "alloy-transport", @@ -3189,12 +3268,12 @@ version = "0.2.0" dependencies = [ "alloy-chains", "alloy-dyn-abi", - "alloy-eips", + "alloy-eips 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-json-abi", "alloy-network", "alloy-primitives", "alloy-provider", - "alloy-rpc-types", + "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-signer", "alloy-transport", "async-recursion", @@ -3233,7 +3312,7 @@ dependencies = [ "alloy-json-abi", "alloy-primitives", "alloy-provider", - "alloy-rpc-types", + "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "async-trait", "clap", "const-hex", @@ -3291,11 +3370,11 @@ name = "foundry-cheatcodes" version = "0.2.0" dependencies = [ "alloy-dyn-abi", - "alloy-genesis", + "alloy-genesis 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-json-abi", "alloy-primitives", "alloy-provider", - "alloy-rpc-types", + "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-signer", "alloy-signer-wallet", "alloy-sol-types", @@ -3376,7 +3455,7 @@ dependencies = [ name = "foundry-common" version = "0.2.0" dependencies = [ - "alloy-consensus", + "alloy-consensus 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-contract", "alloy-dyn-abi", "alloy-json-abi", @@ -3385,7 +3464,7 @@ dependencies = [ "alloy-provider", "alloy-pubsub", "alloy-rpc-client", - "alloy-rpc-types", + "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-rpc-types-engine", "alloy-sol-types", "alloy-transport", @@ -3543,11 +3622,11 @@ name = "foundry-evm-core" version = "0.2.0" dependencies = [ "alloy-dyn-abi", - "alloy-genesis", + "alloy-genesis 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-json-abi", "alloy-primitives", "alloy-provider", - "alloy-rpc-types", + "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-sol-types", "alloy-transport", "arrayvec", @@ -3667,7 +3746,7 @@ version = "0.2.0" dependencies = [ "alloy-primitives", "alloy-provider", - "alloy-rpc-types", + "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "eyre", "fd-lock 4.0.2", "foundry-common", @@ -3688,7 +3767,7 @@ dependencies = [ name = "foundry-wallets" version = "0.2.0" dependencies = [ - "alloy-consensus", + "alloy-consensus 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", "alloy-dyn-abi", "alloy-network", "alloy-primitives", @@ -6362,8 +6441,8 @@ version = "0.1.0" source = "git+https://github.com/paradigmxyz/evm-inspectors?rev=c1b5dd0#c1b5dd0d85dd46ef5ec5258aebd24adc041d103a" dependencies = [ "alloy-primitives", - "alloy-rpc-types", - "alloy-rpc-types-trace", + "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=899fc51)", + "alloy-rpc-types-trace 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=899fc51)", "alloy-sol-types", "anstyle", "colorchoice", diff --git a/Cargo.toml b/Cargo.toml index a807d1983..d4a3f6403 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -152,29 +152,29 @@ revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = ethers-contract-abigen = { version = "2.0.14", default-features = false } ## alloy -alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-signer-wallet = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-signer-wallet = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } alloy-primitives = { version = "0.7.1", features = ["getrandom"] } alloy-dyn-abi = "0.7.1" alloy-json-abi = "0.7.1" @@ -188,7 +188,10 @@ solang-parser = "=0.3.3" ## misc arrayvec = "0.7" base64 = "0.22" -chrono = { version = "0.4", default-features = false, features = ["clock", "std"] } +chrono = { version = "0.4", default-features = false, features = [ + "clock", + "std", +] } color-eyre = "0.6" derive_more = "0.99" evm-disassembler = "0.5" diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index a9cc1cd9d..dfaa92c62 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -163,7 +163,10 @@ impl ClientFork { keys: Vec, block_number: Option, ) -> Result { - self.provider().get_proof(address, keys, block_number.unwrap_or(BlockId::latest())).await + self.provider() + .get_proof(address, keys) + .block_id(block_number.unwrap_or(BlockId::latest())) + .await } /// Sends `eth_call` @@ -185,7 +188,7 @@ impl ClientFork { block: Option, ) -> Result { let block = block.unwrap_or(BlockNumber::Latest); - let res = self.provider().estimate_gas(request, block.into()).await?; + let res = self.provider().estimate_gas(request).block_id(block.into()).await?; Ok(res) } @@ -197,7 +200,8 @@ impl ClientFork { block: Option, ) -> Result { self.provider() - .create_access_list(request, block.unwrap_or(BlockNumber::Latest).into()) + .create_access_list(request) + .block_id(block.unwrap_or(BlockNumber::Latest).into()) .await } @@ -208,7 +212,8 @@ impl ClientFork { number: Option, ) -> Result { self.provider() - .get_storage_at(address, index, number.unwrap_or(BlockNumber::Latest).into()) + .get_storage_at(address, index) + .block_id(number.unwrap_or(BlockNumber::Latest).into()) .await } @@ -236,7 +241,7 @@ impl ClientFork { let block_id = BlockId::Number(blocknumber.into()); - let code = self.provider().get_code_at(address, block_id).await?; + let code = self.provider().get_code_at(address).block_id(block_id).await?; let mut storage = self.storage_write(); storage.code_at.insert((address, blocknumber), code.clone().0.into()); @@ -250,12 +255,12 @@ impl ClientFork { blocknumber: u64, ) -> Result { trace!(target: "backend::fork", "get_balance={:?}", address); - self.provider().get_balance(address, blocknumber.into()).await + self.provider().get_balance(address).block_id(blocknumber.into()).await } pub async fn get_nonce(&self, address: Address, block: u64) -> Result { trace!(target: "backend::fork", "get_nonce={:?}", address); - self.provider().get_transaction_count(address, block.into()).await + self.provider().get_transaction_count(address).block_id(block.into()).await } pub async fn transaction_by_block_number_and_index( diff --git a/crates/cast/bin/cmd/estimate.rs b/crates/cast/bin/cmd/estimate.rs index 33f375f9c..0627e4125 100644 --- a/crates/cast/bin/cmd/estimate.rs +++ b/crates/cast/bin/cmd/estimate.rs @@ -128,7 +128,7 @@ impl EstimateArgs { req.set_input(data); - let gas = provider.estimate_gas(&req, BlockId::latest()).await?; + let gas = provider.estimate_gas(&req).block_id(BlockId::latest()).await?; println!("{gas}"); Ok(()) } diff --git a/crates/cast/bin/cmd/mktx.rs b/crates/cast/bin/cmd/mktx.rs index e26115da3..bfef0a898 100644 --- a/crates/cast/bin/cmd/mktx.rs +++ b/crates/cast/bin/cmd/mktx.rs @@ -90,8 +90,9 @@ impl MakeTxArgs { tx::validate_from_address(eth.wallet.from, from)?; if resend { - tx.nonce = - Some(U64::from(provider.get_transaction_count(from, BlockId::latest()).await?)); + tx.nonce = Some(U64::from( + provider.get_transaction_count(from).block_id(BlockId::latest()).await?, + )); } let provider = get_provider(&config)?; diff --git a/crates/cast/bin/cmd/send.rs b/crates/cast/bin/cmd/send.rs index 7799f5226..2997bfb88 100644 --- a/crates/cast/bin/cmd/send.rs +++ b/crates/cast/bin/cmd/send.rs @@ -144,7 +144,10 @@ impl SendTxArgs { if resend { tx.nonce = Some(U64::from( - provider.get_transaction_count(config.sender, BlockId::latest()).await?, + provider + .get_transaction_count(config.sender) + .block_id(BlockId::latest()) + .await?, )); } @@ -175,8 +178,9 @@ impl SendTxArgs { tx::validate_from_address(eth.wallet.from, from)?; if resend { - tx.nonce = - Some(U64::from(provider.get_transaction_count(from, BlockId::latest()).await?)); + tx.nonce = Some(U64::from( + provider.get_transaction_count(from).block_id(BlockId::latest()).await?, + )); } let signer = EthereumSigner::from(signer); diff --git a/crates/cast/bin/cmd/storage.rs b/crates/cast/bin/cmd/storage.rs index 6197e40c9..a59d93c5d 100644 --- a/crates/cast/bin/cmd/storage.rs +++ b/crates/cast/bin/cmd/storage.rs @@ -95,7 +95,8 @@ impl StorageArgs { // No slot was provided // Get deployed bytecode at given address - let address_code = provider.get_code_at(address, block.unwrap_or_default()).await?; + let address_code = + provider.get_code_at(address).block_id(block.unwrap_or_default()).await?; if address_code.is_empty() { eyre::bail!("Provided address has no deployed code and thus no storage"); } @@ -237,8 +238,10 @@ async fn fetch_storage_slots, T: Transport + Clone>( ) -> Result> { let requests = layout.storage.iter().map(|storage_slot| async { let slot = B256::from(U256::from_str(&storage_slot.slot)?); - let raw_slot_value = - provider.get_storage_at(address, slot.into(), block.unwrap_or_default()).await?; + let raw_slot_value = provider + .get_storage_at(address, slot.into()) + .block_id(block.unwrap_or_default()) + .await?; let value = StorageValue { slot, raw_slot_value: raw_slot_value.into() }; diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index 30bb1205e..500c831d1 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -351,7 +351,8 @@ async fn main() -> Result<()> { let provider = utils::get_provider(&config)?; let address = address.resolve(&provider).await?; let value = provider - .get_proof(address, slots.into_iter().collect(), block.unwrap_or(BlockId::latest())) + .get_proof(address, slots.into_iter().collect()) + .block_id(block.unwrap_or(BlockId::latest())) .await?; println!("{}", serde_json::to_string(&value)?); } diff --git a/crates/cast/bin/tx.rs b/crates/cast/bin/tx.rs index 64b33b280..80ce6e04b 100644 --- a/crates/cast/bin/tx.rs +++ b/crates/cast/bin/tx.rs @@ -71,7 +71,7 @@ pub async fn build_tx< req.set_nonce(if let Some(nonce) = tx.nonce { nonce.to() } else { - provider.get_transaction_count(from, BlockId::latest()).await? + provider.get_transaction_count(from).block_id(BlockId::latest()).await? }); if tx.legacy || chain.is_legacy() { @@ -119,7 +119,7 @@ pub async fn build_tx< req.set_gas_limit(if let Some(gas_limit) = tx.gas_limit { gas_limit.to() } else { - provider.estimate_gas(&req, BlockId::latest()).await? + provider.estimate_gas(&req).block_id(BlockId::latest()).await? }); Ok((req, func)) diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index e2f706b74..e9c8cb7b2 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -133,8 +133,11 @@ where if res.is_empty() { // check that the recipient is a contract that can be called if let Some(TxKind::Call(addr)) = req.to { - if let Ok(code) = - self.provider.get_code_at(addr, block.unwrap_or_default()).await + if let Ok(code) = self + .provider + .get_code_at(addr) + .block_id(block.unwrap_or_default()) + .await { if code.is_empty() { eyre::bail!("contract {addr:?} does not have any code") @@ -197,8 +200,11 @@ where block: Option, to_json: bool, ) -> Result { - let access_list = - self.provider.create_access_list(req, block.unwrap_or(BlockId::latest())).await?; + let access_list = self + .provider + .create_access_list(req) + .block_id(block.unwrap_or(BlockId::latest())) + .await?; let res = if to_json { serde_json::to_string(&access_list)? } else { @@ -220,7 +226,7 @@ where } pub async fn balance(&self, who: Address, block: Option) -> Result { - Ok(self.provider.get_balance(who, block.unwrap_or(BlockId::latest())).await?) + Ok(self.provider.get_balance(who).block_id(block.unwrap_or(BlockId::latest())).await?) } /// Sends a transaction to the specified address @@ -472,7 +478,11 @@ where /// # } /// ``` pub async fn nonce(&self, who: Address, block: Option) -> Result { - Ok(self.provider.get_transaction_count(who, block.unwrap_or(BlockId::latest())).await?) + Ok(self + .provider + .get_transaction_count(who) + .block_id(block.unwrap_or(BlockId::latest())) + .await?) } /// # Example @@ -498,7 +508,8 @@ where B256::from_str("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc")?; let value = self .provider - .get_storage_at(who, slot.into(), block.unwrap_or(BlockId::latest())) + .get_storage_at(who, slot.into()) + .block_id(block.unwrap_or(BlockId::latest())) .await?; let addr = Address::from_word(value.into()); Ok(format!("{addr:?}")) @@ -527,7 +538,8 @@ where B256::from_str("0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103")?; let value = self .provider - .get_storage_at(who, slot.into(), block.unwrap_or(BlockId::latest())) + .get_storage_at(who, slot.into()) + .block_id(block.unwrap_or(BlockId::latest())) .await?; let addr = Address::from_word(value.into()); Ok(format!("{addr:?}")) @@ -581,10 +593,14 @@ where disassemble: bool, ) -> Result { if disassemble { - let code = self.provider.get_code_at(who, block.unwrap_or_default()).await?.to_vec(); + let code = + self.provider.get_code_at(who).block_id(block.unwrap_or_default()).await?.to_vec(); Ok(format_operations(disassemble_bytes(code)?)?) } else { - Ok(format!("{}", self.provider.get_code_at(who, block.unwrap_or_default()).await?)) + Ok(format!( + "{}", + self.provider.get_code_at(who).block_id(block.unwrap_or_default()).await? + )) } } @@ -607,7 +623,8 @@ where /// # } /// ``` pub async fn codesize(&self, who: Address, block: Option) -> Result { - let code = self.provider.get_code_at(who, block.unwrap_or_default()).await?.to_vec(); + let code = + self.provider.get_code_at(who).block_id(block.unwrap_or_default()).await?.to_vec(); Ok(format!("{}", code.len())) } @@ -773,7 +790,8 @@ where "{:?}", B256::from( self.provider - .get_storage_at(from, slot.into(), block.unwrap_or(BlockId::latest())) + .get_storage_at(from, slot.into()) + .block_id(block.unwrap_or_default()) .await? ) )) diff --git a/crates/evm/core/src/fork/backend.rs b/crates/evm/core/src/fork/backend.rs index 2ab9bdc9b..f2e4d15c3 100644 --- a/crates/evm/core/src/fork/backend.rs +++ b/crates/evm/core/src/fork/backend.rs @@ -22,6 +22,7 @@ use revm::{ use rustc_hash::FxHashMap; use std::{ collections::{hash_map::Entry, HashMap, VecDeque}, + future::IntoFuture, marker::PhantomData, pin::Pin, sync::{ @@ -189,8 +190,11 @@ where let provider = self.provider.clone(); let block_id = self.block_id.unwrap_or(BlockId::latest()); let fut = Box::pin(async move { - let storage = - provider.get_storage_at(address, idx, block_id).await.map_err(Into::into); + let storage = provider + .get_storage_at(address, idx) + .block_id(block_id) + .await + .map_err(Into::into); (storage, address, idx) }); self.pending_requests.push(ProviderRequest::Storage(fut)); @@ -204,9 +208,9 @@ where let provider = self.provider.clone(); let block_id = self.block_id.unwrap_or(BlockId::latest()); let fut = Box::pin(async move { - let balance = provider.get_balance(address, block_id); - let nonce = provider.get_transaction_count(address, block_id); - let code = provider.get_code_at(address, block_id); + let balance = provider.get_balance(address).block_id(block_id).into_future(); + let nonce = provider.get_transaction_count(address).block_id(block_id).into_future(); + let code = provider.get_code_at(address).block_id(block_id).into_future(); let resp = tokio::try_join!(balance, nonce, code).map_err(Into::into); (resp, address) }); diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index 460dbdea7..02e68213a 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -239,7 +239,7 @@ impl CreateArgs { deployer.tx.set_nonce(if let Some(nonce) = self.tx.nonce { Ok(nonce.to()) } else { - provider.get_transaction_count(deployer_address, BlockId::latest()).await + provider.get_transaction_count(deployer_address).block_id(BlockId::latest()).await }?); // set tx value if specified @@ -250,7 +250,7 @@ impl CreateArgs { deployer.tx.set_gas_limit(if let Some(gas_limit) = self.tx.gas_limit { Ok(gas_limit.to()) } else { - provider.estimate_gas(&deployer.tx, BlockId::latest()).await + provider.estimate_gas(&deployer.tx).block_id(BlockId::latest()).await }?); if is_legacy { diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index a278e44d7..39bf39289 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -44,7 +44,8 @@ where tx.set_gas_limit( provider - .estimate_gas(tx, BlockId::latest()) + .estimate_gas(tx) + .block_id(BlockId::latest()) .await .wrap_err("Failed to estimate gas for tx")? * estimate_multiplier as u128 / @@ -56,7 +57,7 @@ where pub async fn next_nonce(caller: Address, provider_url: &str) -> eyre::Result { let provider = try_get_http_provider(provider_url) .wrap_err_with(|| format!("bad fork_url provider: {provider_url}"))?; - Ok(provider.get_transaction_count(caller, BlockId::latest()).await?) + Ok(provider.get_transaction_count(caller).block_id(BlockId::latest()).await?) } pub async fn send_transaction( @@ -71,7 +72,7 @@ pub async fn send_transaction( let from = tx.from.expect("no sender"); if sequential_broadcast { - let nonce = provider.get_transaction_count(from, BlockId::latest()).await?; + let nonce = provider.get_transaction_count(from).block_id(BlockId::latest()).await?; let tx_nonce = tx.nonce.expect("no nonce"); if nonce != tx_nonce { diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index 8d9907ccf..64e1c7539 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -7,6 +7,7 @@ use crate::{ }; use alloy_primitives::{Bytes, B256}; use alloy_provider::Provider; +use alloy_rpc_types::BlockId; use eyre::{OptionExt, Result}; use foundry_cheatcodes::ScriptWallets; use foundry_common::{ @@ -46,7 +47,7 @@ impl BuildData { let can_use_create2 = if let Some(fork_url) = &script_config.evm_opts.fork_url { let provider = try_get_http_provider(fork_url)?; let deployer_code = - provider.get_code_at(DEFAULT_CREATE2_DEPLOYER, Default::default()).await?; + provider.get_code_at(DEFAULT_CREATE2_DEPLOYER).block_id(BlockId::latest()).await?; !deployer_code.is_empty() } else { diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index 8b625d6ee..c0331ed9f 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -141,7 +141,8 @@ impl ScriptTester { if let Some(provider) = &self.provider { let nonce = provider - .get_transaction_count(self.accounts_pub[index as usize], BlockId::latest()) + .get_transaction_count(self.accounts_pub[index as usize]) + .block_id(BlockId::latest()) .await .unwrap(); self.nonces.insert(index, nonce); @@ -156,7 +157,8 @@ impl ScriptTester { .provider .as_ref() .unwrap() - .get_transaction_count(address, BlockId::latest()) + .get_transaction_count(address) + .block_id(BlockId::latest()) .await .unwrap(); self.address_nonces.insert(address, nonce); @@ -202,7 +204,8 @@ impl ScriptTester { .provider .as_ref() .unwrap() - .get_transaction_count(addr, BlockId::latest()) + .get_transaction_count(addr) + .block_id(BlockId::latest()) .await .unwrap(); let prev_nonce = self.nonces.get(&private_key_slot).unwrap(); @@ -227,7 +230,8 @@ impl ScriptTester { .provider .as_ref() .unwrap() - .get_transaction_count(*address, BlockId::latest()) + .get_transaction_count(*address) + .block_id(BlockId::latest()) .await .unwrap(); let prev_nonce = self.address_nonces.get(address).unwrap(); diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index 53654a7d3..da0f805fd 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -109,7 +109,7 @@ impl VerifyBytecodeArgs { let config = self.load_config_emit_warnings(); let provider = ProviderBuilder::new(&config.get_rpc_url_or_localhost_http()?).build()?; - let code = provider.get_code_at(self.address, BlockId::latest()).await?; + let code = provider.get_code_at(self.address).block_id(BlockId::latest()).await?; if code.is_empty() { eyre::bail!("No bytecode found at address {}", self.address); } @@ -291,8 +291,10 @@ impl VerifyBytecodeArgs { // Workaround for the NonceTooHigh issue as we're not simulating prior txs of the same // block. let prev_block_id = BlockId::Number(BlockNumberOrTag::Number(simulation_block - 1)); - let prev_block_nonce = - provider.get_transaction_count(creation_data.contract_creator, prev_block_id).await?; + let prev_block_nonce = provider + .get_transaction_count(creation_data.contract_creator) + .block_id(prev_block_id) + .await?; transaction.nonce = prev_block_nonce; if let Some(ref block) = block { @@ -343,9 +345,8 @@ impl VerifyBytecodeArgs { ) })?; - let onchain_runtime_code = provider - .get_code_at(self.address, BlockId::Number(BlockNumberOrTag::Number(simulation_block))) - .await?; + let onchain_runtime_code = + provider.get_code_at(self.address).block_id(BlockId::number(simulation_block)).await?; // Compare the runtime bytecode with the locally built bytecode let (did_match, with_status) = try_match( From 20feac7ef7a8263d8aa7be5955a83dac9327c30a Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Thu, 16 May 2024 10:20:41 -0400 Subject: [PATCH 286/430] Revert "bump alloy f415827" This reverts commit 48e5d112f7675d1bb5b8fd3fa2d1ae7df4bd8245. --- Cargo.lock | 217 +++++++++------------------ Cargo.toml | 51 +++---- crates/anvil/src/eth/backend/fork.rs | 19 +-- crates/cast/bin/cmd/estimate.rs | 2 +- crates/cast/bin/cmd/mktx.rs | 5 +- crates/cast/bin/cmd/send.rs | 10 +- crates/cast/bin/cmd/storage.rs | 9 +- crates/cast/bin/main.rs | 3 +- crates/cast/bin/tx.rs | 4 +- crates/cast/src/lib.rs | 42 ++---- crates/evm/core/src/fork/backend.rs | 14 +- crates/forge/bin/cmd/create.rs | 4 +- crates/script/src/broadcast.rs | 7 +- crates/script/src/build.rs | 3 +- crates/test-utils/src/script.rs | 12 +- crates/verify/src/bytecode.rs | 13 +- 16 files changed, 145 insertions(+), 270 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6eb52da31..52a36a589 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -81,23 +81,10 @@ name = "alloy-consensus" version = "0.1.0" source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ - "alloy-eips 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=899fc51)", + "alloy-eips", "alloy-primitives", "alloy-rlp", - "alloy-serde 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=899fc51)", - "c-kzg", - "serde", -] - -[[package]] -name = "alloy-consensus" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" -dependencies = [ - "alloy-eips 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", - "alloy-primitives", - "alloy-rlp", - "alloy-serde 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-serde", "c-kzg", "serde", ] @@ -105,7 +92,7 @@ dependencies = [ [[package]] name = "alloy-contract" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -113,7 +100,7 @@ dependencies = [ "alloy-primitives", "alloy-provider", "alloy-pubsub", - "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-rpc-types", "alloy-sol-types", "alloy-transport", "futures", @@ -149,21 +136,7 @@ source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1 dependencies = [ "alloy-primitives", "alloy-rlp", - "alloy-serde 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=899fc51)", - "c-kzg", - "once_cell", - "serde", - "sha2", -] - -[[package]] -name = "alloy-eips" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" -dependencies = [ - "alloy-primitives", - "alloy-rlp", - "alloy-serde 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-serde", "c-kzg", "derive_more", "once_cell", @@ -177,18 +150,7 @@ version = "0.1.0" source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-primitives", - "alloy-serde 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=899fc51)", - "serde", - "serde_json", -] - -[[package]] -name = "alloy-genesis" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" -dependencies = [ - "alloy-primitives", - "alloy-serde 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-serde", "serde", "serde_json", ] @@ -208,7 +170,7 @@ dependencies = [ [[package]] name = "alloy-json-rpc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-primitives", "serde", @@ -220,13 +182,13 @@ dependencies = [ [[package]] name = "alloy-network" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ - "alloy-consensus 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", - "alloy-eips 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-consensus", + "alloy-eips", "alloy-json-rpc", "alloy-primitives", - "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-rpc-types", "alloy-signer", "alloy-sol-types", "async-trait", @@ -264,16 +226,16 @@ dependencies = [ [[package]] name = "alloy-provider" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ - "alloy-eips 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-eips", "alloy-json-rpc", "alloy-network", "alloy-primitives", "alloy-pubsub", "alloy-rpc-client", - "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", - "alloy-rpc-types-trace 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-rpc-types", + "alloy-rpc-types-trace", "alloy-transport", "alloy-transport-http", "alloy-transport-ipc", @@ -285,7 +247,6 @@ dependencies = [ "futures", "futures-utils-wasm", "lru", - "pin-project", "reqwest", "serde_json", "tokio", @@ -296,7 +257,7 @@ dependencies = [ [[package]] name = "alloy-pubsub" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -336,7 +297,7 @@ dependencies = [ [[package]] name = "alloy-rpc-client" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -362,30 +323,12 @@ name = "alloy-rpc-types" version = "0.1.0" source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ - "alloy-consensus 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=899fc51)", - "alloy-eips 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=899fc51)", - "alloy-genesis 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=899fc51)", + "alloy-consensus", + "alloy-eips", + "alloy-genesis", "alloy-primitives", "alloy-rlp", - "alloy-serde 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=899fc51)", - "alloy-sol-types", - "itertools 0.12.1", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "alloy-rpc-types" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" -dependencies = [ - "alloy-consensus 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", - "alloy-eips 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", - "alloy-genesis 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", - "alloy-primitives", - "alloy-rlp", - "alloy-serde 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-serde", "alloy-sol-types", "itertools 0.12.1", "serde", @@ -396,14 +339,14 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ - "alloy-consensus 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", - "alloy-eips 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-consensus", + "alloy-eips", "alloy-primitives", "alloy-rlp", - "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", - "alloy-serde 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-rpc-types", + "alloy-serde", "jsonwebtoken", "rand", "serde", @@ -416,20 +359,8 @@ version = "0.1.0" source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-primitives", - "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=899fc51)", - "alloy-serde 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=899fc51)", - "serde", - "serde_json", -] - -[[package]] -name = "alloy-rpc-types-trace" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" -dependencies = [ - "alloy-primitives", - "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", - "alloy-serde 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-rpc-types", + "alloy-serde", "serde", "serde_json", ] @@ -444,20 +375,10 @@ dependencies = [ "serde_json", ] -[[package]] -name = "alloy-serde" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" -dependencies = [ - "alloy-primitives", - "serde", - "serde_json", -] - [[package]] name = "alloy-signer" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -472,9 +393,9 @@ dependencies = [ [[package]] name = "alloy-signer-aws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ - "alloy-consensus 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-consensus", "alloy-network", "alloy-primitives", "alloy-signer", @@ -489,9 +410,9 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ - "alloy-consensus 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-consensus", "alloy-dyn-abi", "alloy-network", "alloy-primitives", @@ -508,9 +429,9 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ - "alloy-consensus 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-consensus", "alloy-network", "alloy-primitives", "alloy-signer", @@ -524,9 +445,9 @@ dependencies = [ [[package]] name = "alloy-signer-wallet" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ - "alloy-consensus 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-consensus", "alloy-network", "alloy-primitives", "alloy-signer", @@ -601,7 +522,7 @@ dependencies = [ [[package]] name = "alloy-transport" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-json-rpc", "base64 0.22.1", @@ -619,7 +540,7 @@ dependencies = [ [[package]] name = "alloy-transport-http" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -633,7 +554,7 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -653,7 +574,7 @@ dependencies = [ [[package]] name = "alloy-transport-ws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -770,11 +691,11 @@ name = "anvil" version = "0.2.0" dependencies = [ "alloy-chains", - "alloy-consensus 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-consensus", "alloy-contract", "alloy-dyn-abi", - "alloy-eips 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", - "alloy-genesis 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-eips", + "alloy-genesis", "alloy-json-abi", "alloy-json-rpc", "alloy-network", @@ -783,8 +704,8 @@ dependencies = [ "alloy-pubsub", "alloy-rlp", "alloy-rpc-client", - "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", - "alloy-rpc-types-trace 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-rpc-types", + "alloy-rpc-types-trace", "alloy-signer", "alloy-signer-wallet", "alloy-sol-types", @@ -838,15 +759,15 @@ dependencies = [ name = "anvil-core" version = "0.2.0" dependencies = [ - "alloy-consensus 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-consensus", "alloy-dyn-abi", - "alloy-eips 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-eips", "alloy-network", "alloy-primitives", "alloy-rlp", - "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", - "alloy-rpc-types-trace 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", - "alloy-serde 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-rpc-types", + "alloy-rpc-types-trace", + "alloy-serde", "alloy-trie", "anvil-core", "bytes", @@ -1808,7 +1729,7 @@ name = "cast" version = "0.2.0" dependencies = [ "alloy-chains", - "alloy-consensus 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-consensus", "alloy-contract", "alloy-dyn-abi", "alloy-json-abi", @@ -1817,7 +1738,7 @@ dependencies = [ "alloy-primitives", "alloy-provider", "alloy-rlp", - "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-rpc-types", "alloy-signer", "alloy-signer-wallet", "alloy-sol-types", @@ -1901,7 +1822,7 @@ dependencies = [ "alloy-dyn-abi", "alloy-json-abi", "alloy-primitives", - "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-rpc-types", "clap", "criterion", "dirs 5.0.1", @@ -3143,13 +3064,13 @@ name = "forge" version = "0.2.0" dependencies = [ "alloy-chains", - "alloy-consensus 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-consensus", "alloy-dyn-abi", "alloy-json-abi", "alloy-network", "alloy-primitives", "alloy-provider", - "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-rpc-types", "alloy-signer", "alloy-signer-wallet", "alloy-transport", @@ -3268,12 +3189,12 @@ version = "0.2.0" dependencies = [ "alloy-chains", "alloy-dyn-abi", - "alloy-eips 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-eips", "alloy-json-abi", "alloy-network", "alloy-primitives", "alloy-provider", - "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-rpc-types", "alloy-signer", "alloy-transport", "async-recursion", @@ -3312,7 +3233,7 @@ dependencies = [ "alloy-json-abi", "alloy-primitives", "alloy-provider", - "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-rpc-types", "async-trait", "clap", "const-hex", @@ -3370,11 +3291,11 @@ name = "foundry-cheatcodes" version = "0.2.0" dependencies = [ "alloy-dyn-abi", - "alloy-genesis 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-genesis", "alloy-json-abi", "alloy-primitives", "alloy-provider", - "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-rpc-types", "alloy-signer", "alloy-signer-wallet", "alloy-sol-types", @@ -3455,7 +3376,7 @@ dependencies = [ name = "foundry-common" version = "0.2.0" dependencies = [ - "alloy-consensus 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-consensus", "alloy-contract", "alloy-dyn-abi", "alloy-json-abi", @@ -3464,7 +3385,7 @@ dependencies = [ "alloy-provider", "alloy-pubsub", "alloy-rpc-client", - "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-rpc-types", "alloy-rpc-types-engine", "alloy-sol-types", "alloy-transport", @@ -3622,11 +3543,11 @@ name = "foundry-evm-core" version = "0.2.0" dependencies = [ "alloy-dyn-abi", - "alloy-genesis 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-genesis", "alloy-json-abi", "alloy-primitives", "alloy-provider", - "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-rpc-types", "alloy-sol-types", "alloy-transport", "arrayvec", @@ -3746,7 +3667,7 @@ version = "0.2.0" dependencies = [ "alloy-primitives", "alloy-provider", - "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-rpc-types", "eyre", "fd-lock 4.0.2", "foundry-common", @@ -3767,7 +3688,7 @@ dependencies = [ name = "foundry-wallets" version = "0.2.0" dependencies = [ - "alloy-consensus 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=f415827)", + "alloy-consensus", "alloy-dyn-abi", "alloy-network", "alloy-primitives", @@ -6441,8 +6362,8 @@ version = "0.1.0" source = "git+https://github.com/paradigmxyz/evm-inspectors?rev=c1b5dd0#c1b5dd0d85dd46ef5ec5258aebd24adc041d103a" dependencies = [ "alloy-primitives", - "alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=899fc51)", - "alloy-rpc-types-trace 0.1.0 (git+https://github.com/alloy-rs/alloy?rev=899fc51)", + "alloy-rpc-types", + "alloy-rpc-types-trace", "alloy-sol-types", "anstyle", "colorchoice", diff --git a/Cargo.toml b/Cargo.toml index d4a3f6403..a807d1983 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -152,29 +152,29 @@ revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = ethers-contract-abigen = { version = "2.0.14", default-features = false } ## alloy -alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-signer-wallet = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-signer-wallet = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } alloy-primitives = { version = "0.7.1", features = ["getrandom"] } alloy-dyn-abi = "0.7.1" alloy-json-abi = "0.7.1" @@ -188,10 +188,7 @@ solang-parser = "=0.3.3" ## misc arrayvec = "0.7" base64 = "0.22" -chrono = { version = "0.4", default-features = false, features = [ - "clock", - "std", -] } +chrono = { version = "0.4", default-features = false, features = ["clock", "std"] } color-eyre = "0.6" derive_more = "0.99" evm-disassembler = "0.5" diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index dfaa92c62..a9cc1cd9d 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -163,10 +163,7 @@ impl ClientFork { keys: Vec, block_number: Option, ) -> Result { - self.provider() - .get_proof(address, keys) - .block_id(block_number.unwrap_or(BlockId::latest())) - .await + self.provider().get_proof(address, keys, block_number.unwrap_or(BlockId::latest())).await } /// Sends `eth_call` @@ -188,7 +185,7 @@ impl ClientFork { block: Option, ) -> Result { let block = block.unwrap_or(BlockNumber::Latest); - let res = self.provider().estimate_gas(request).block_id(block.into()).await?; + let res = self.provider().estimate_gas(request, block.into()).await?; Ok(res) } @@ -200,8 +197,7 @@ impl ClientFork { block: Option, ) -> Result { self.provider() - .create_access_list(request) - .block_id(block.unwrap_or(BlockNumber::Latest).into()) + .create_access_list(request, block.unwrap_or(BlockNumber::Latest).into()) .await } @@ -212,8 +208,7 @@ impl ClientFork { number: Option, ) -> Result { self.provider() - .get_storage_at(address, index) - .block_id(number.unwrap_or(BlockNumber::Latest).into()) + .get_storage_at(address, index, number.unwrap_or(BlockNumber::Latest).into()) .await } @@ -241,7 +236,7 @@ impl ClientFork { let block_id = BlockId::Number(blocknumber.into()); - let code = self.provider().get_code_at(address).block_id(block_id).await?; + let code = self.provider().get_code_at(address, block_id).await?; let mut storage = self.storage_write(); storage.code_at.insert((address, blocknumber), code.clone().0.into()); @@ -255,12 +250,12 @@ impl ClientFork { blocknumber: u64, ) -> Result { trace!(target: "backend::fork", "get_balance={:?}", address); - self.provider().get_balance(address).block_id(blocknumber.into()).await + self.provider().get_balance(address, blocknumber.into()).await } pub async fn get_nonce(&self, address: Address, block: u64) -> Result { trace!(target: "backend::fork", "get_nonce={:?}", address); - self.provider().get_transaction_count(address).block_id(block.into()).await + self.provider().get_transaction_count(address, block.into()).await } pub async fn transaction_by_block_number_and_index( diff --git a/crates/cast/bin/cmd/estimate.rs b/crates/cast/bin/cmd/estimate.rs index 0627e4125..33f375f9c 100644 --- a/crates/cast/bin/cmd/estimate.rs +++ b/crates/cast/bin/cmd/estimate.rs @@ -128,7 +128,7 @@ impl EstimateArgs { req.set_input(data); - let gas = provider.estimate_gas(&req).block_id(BlockId::latest()).await?; + let gas = provider.estimate_gas(&req, BlockId::latest()).await?; println!("{gas}"); Ok(()) } diff --git a/crates/cast/bin/cmd/mktx.rs b/crates/cast/bin/cmd/mktx.rs index bfef0a898..e26115da3 100644 --- a/crates/cast/bin/cmd/mktx.rs +++ b/crates/cast/bin/cmd/mktx.rs @@ -90,9 +90,8 @@ impl MakeTxArgs { tx::validate_from_address(eth.wallet.from, from)?; if resend { - tx.nonce = Some(U64::from( - provider.get_transaction_count(from).block_id(BlockId::latest()).await?, - )); + tx.nonce = + Some(U64::from(provider.get_transaction_count(from, BlockId::latest()).await?)); } let provider = get_provider(&config)?; diff --git a/crates/cast/bin/cmd/send.rs b/crates/cast/bin/cmd/send.rs index 2997bfb88..7799f5226 100644 --- a/crates/cast/bin/cmd/send.rs +++ b/crates/cast/bin/cmd/send.rs @@ -144,10 +144,7 @@ impl SendTxArgs { if resend { tx.nonce = Some(U64::from( - provider - .get_transaction_count(config.sender) - .block_id(BlockId::latest()) - .await?, + provider.get_transaction_count(config.sender, BlockId::latest()).await?, )); } @@ -178,9 +175,8 @@ impl SendTxArgs { tx::validate_from_address(eth.wallet.from, from)?; if resend { - tx.nonce = Some(U64::from( - provider.get_transaction_count(from).block_id(BlockId::latest()).await?, - )); + tx.nonce = + Some(U64::from(provider.get_transaction_count(from, BlockId::latest()).await?)); } let signer = EthereumSigner::from(signer); diff --git a/crates/cast/bin/cmd/storage.rs b/crates/cast/bin/cmd/storage.rs index a59d93c5d..6197e40c9 100644 --- a/crates/cast/bin/cmd/storage.rs +++ b/crates/cast/bin/cmd/storage.rs @@ -95,8 +95,7 @@ impl StorageArgs { // No slot was provided // Get deployed bytecode at given address - let address_code = - provider.get_code_at(address).block_id(block.unwrap_or_default()).await?; + let address_code = provider.get_code_at(address, block.unwrap_or_default()).await?; if address_code.is_empty() { eyre::bail!("Provided address has no deployed code and thus no storage"); } @@ -238,10 +237,8 @@ async fn fetch_storage_slots, T: Transport + Clone>( ) -> Result> { let requests = layout.storage.iter().map(|storage_slot| async { let slot = B256::from(U256::from_str(&storage_slot.slot)?); - let raw_slot_value = provider - .get_storage_at(address, slot.into()) - .block_id(block.unwrap_or_default()) - .await?; + let raw_slot_value = + provider.get_storage_at(address, slot.into(), block.unwrap_or_default()).await?; let value = StorageValue { slot, raw_slot_value: raw_slot_value.into() }; diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index 500c831d1..30bb1205e 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -351,8 +351,7 @@ async fn main() -> Result<()> { let provider = utils::get_provider(&config)?; let address = address.resolve(&provider).await?; let value = provider - .get_proof(address, slots.into_iter().collect()) - .block_id(block.unwrap_or(BlockId::latest())) + .get_proof(address, slots.into_iter().collect(), block.unwrap_or(BlockId::latest())) .await?; println!("{}", serde_json::to_string(&value)?); } diff --git a/crates/cast/bin/tx.rs b/crates/cast/bin/tx.rs index 80ce6e04b..64b33b280 100644 --- a/crates/cast/bin/tx.rs +++ b/crates/cast/bin/tx.rs @@ -71,7 +71,7 @@ pub async fn build_tx< req.set_nonce(if let Some(nonce) = tx.nonce { nonce.to() } else { - provider.get_transaction_count(from).block_id(BlockId::latest()).await? + provider.get_transaction_count(from, BlockId::latest()).await? }); if tx.legacy || chain.is_legacy() { @@ -119,7 +119,7 @@ pub async fn build_tx< req.set_gas_limit(if let Some(gas_limit) = tx.gas_limit { gas_limit.to() } else { - provider.estimate_gas(&req).block_id(BlockId::latest()).await? + provider.estimate_gas(&req, BlockId::latest()).await? }); Ok((req, func)) diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index e9c8cb7b2..e2f706b74 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -133,11 +133,8 @@ where if res.is_empty() { // check that the recipient is a contract that can be called if let Some(TxKind::Call(addr)) = req.to { - if let Ok(code) = self - .provider - .get_code_at(addr) - .block_id(block.unwrap_or_default()) - .await + if let Ok(code) = + self.provider.get_code_at(addr, block.unwrap_or_default()).await { if code.is_empty() { eyre::bail!("contract {addr:?} does not have any code") @@ -200,11 +197,8 @@ where block: Option, to_json: bool, ) -> Result { - let access_list = self - .provider - .create_access_list(req) - .block_id(block.unwrap_or(BlockId::latest())) - .await?; + let access_list = + self.provider.create_access_list(req, block.unwrap_or(BlockId::latest())).await?; let res = if to_json { serde_json::to_string(&access_list)? } else { @@ -226,7 +220,7 @@ where } pub async fn balance(&self, who: Address, block: Option) -> Result { - Ok(self.provider.get_balance(who).block_id(block.unwrap_or(BlockId::latest())).await?) + Ok(self.provider.get_balance(who, block.unwrap_or(BlockId::latest())).await?) } /// Sends a transaction to the specified address @@ -478,11 +472,7 @@ where /// # } /// ``` pub async fn nonce(&self, who: Address, block: Option) -> Result { - Ok(self - .provider - .get_transaction_count(who) - .block_id(block.unwrap_or(BlockId::latest())) - .await?) + Ok(self.provider.get_transaction_count(who, block.unwrap_or(BlockId::latest())).await?) } /// # Example @@ -508,8 +498,7 @@ where B256::from_str("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc")?; let value = self .provider - .get_storage_at(who, slot.into()) - .block_id(block.unwrap_or(BlockId::latest())) + .get_storage_at(who, slot.into(), block.unwrap_or(BlockId::latest())) .await?; let addr = Address::from_word(value.into()); Ok(format!("{addr:?}")) @@ -538,8 +527,7 @@ where B256::from_str("0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103")?; let value = self .provider - .get_storage_at(who, slot.into()) - .block_id(block.unwrap_or(BlockId::latest())) + .get_storage_at(who, slot.into(), block.unwrap_or(BlockId::latest())) .await?; let addr = Address::from_word(value.into()); Ok(format!("{addr:?}")) @@ -593,14 +581,10 @@ where disassemble: bool, ) -> Result { if disassemble { - let code = - self.provider.get_code_at(who).block_id(block.unwrap_or_default()).await?.to_vec(); + let code = self.provider.get_code_at(who, block.unwrap_or_default()).await?.to_vec(); Ok(format_operations(disassemble_bytes(code)?)?) } else { - Ok(format!( - "{}", - self.provider.get_code_at(who).block_id(block.unwrap_or_default()).await? - )) + Ok(format!("{}", self.provider.get_code_at(who, block.unwrap_or_default()).await?)) } } @@ -623,8 +607,7 @@ where /// # } /// ``` pub async fn codesize(&self, who: Address, block: Option) -> Result { - let code = - self.provider.get_code_at(who).block_id(block.unwrap_or_default()).await?.to_vec(); + let code = self.provider.get_code_at(who, block.unwrap_or_default()).await?.to_vec(); Ok(format!("{}", code.len())) } @@ -790,8 +773,7 @@ where "{:?}", B256::from( self.provider - .get_storage_at(from, slot.into()) - .block_id(block.unwrap_or_default()) + .get_storage_at(from, slot.into(), block.unwrap_or(BlockId::latest())) .await? ) )) diff --git a/crates/evm/core/src/fork/backend.rs b/crates/evm/core/src/fork/backend.rs index f2e4d15c3..2ab9bdc9b 100644 --- a/crates/evm/core/src/fork/backend.rs +++ b/crates/evm/core/src/fork/backend.rs @@ -22,7 +22,6 @@ use revm::{ use rustc_hash::FxHashMap; use std::{ collections::{hash_map::Entry, HashMap, VecDeque}, - future::IntoFuture, marker::PhantomData, pin::Pin, sync::{ @@ -190,11 +189,8 @@ where let provider = self.provider.clone(); let block_id = self.block_id.unwrap_or(BlockId::latest()); let fut = Box::pin(async move { - let storage = provider - .get_storage_at(address, idx) - .block_id(block_id) - .await - .map_err(Into::into); + let storage = + provider.get_storage_at(address, idx, block_id).await.map_err(Into::into); (storage, address, idx) }); self.pending_requests.push(ProviderRequest::Storage(fut)); @@ -208,9 +204,9 @@ where let provider = self.provider.clone(); let block_id = self.block_id.unwrap_or(BlockId::latest()); let fut = Box::pin(async move { - let balance = provider.get_balance(address).block_id(block_id).into_future(); - let nonce = provider.get_transaction_count(address).block_id(block_id).into_future(); - let code = provider.get_code_at(address).block_id(block_id).into_future(); + let balance = provider.get_balance(address, block_id); + let nonce = provider.get_transaction_count(address, block_id); + let code = provider.get_code_at(address, block_id); let resp = tokio::try_join!(balance, nonce, code).map_err(Into::into); (resp, address) }); diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index 02e68213a..460dbdea7 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -239,7 +239,7 @@ impl CreateArgs { deployer.tx.set_nonce(if let Some(nonce) = self.tx.nonce { Ok(nonce.to()) } else { - provider.get_transaction_count(deployer_address).block_id(BlockId::latest()).await + provider.get_transaction_count(deployer_address, BlockId::latest()).await }?); // set tx value if specified @@ -250,7 +250,7 @@ impl CreateArgs { deployer.tx.set_gas_limit(if let Some(gas_limit) = self.tx.gas_limit { Ok(gas_limit.to()) } else { - provider.estimate_gas(&deployer.tx).block_id(BlockId::latest()).await + provider.estimate_gas(&deployer.tx, BlockId::latest()).await }?); if is_legacy { diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index 39bf39289..a278e44d7 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -44,8 +44,7 @@ where tx.set_gas_limit( provider - .estimate_gas(tx) - .block_id(BlockId::latest()) + .estimate_gas(tx, BlockId::latest()) .await .wrap_err("Failed to estimate gas for tx")? * estimate_multiplier as u128 / @@ -57,7 +56,7 @@ where pub async fn next_nonce(caller: Address, provider_url: &str) -> eyre::Result { let provider = try_get_http_provider(provider_url) .wrap_err_with(|| format!("bad fork_url provider: {provider_url}"))?; - Ok(provider.get_transaction_count(caller).block_id(BlockId::latest()).await?) + Ok(provider.get_transaction_count(caller, BlockId::latest()).await?) } pub async fn send_transaction( @@ -72,7 +71,7 @@ pub async fn send_transaction( let from = tx.from.expect("no sender"); if sequential_broadcast { - let nonce = provider.get_transaction_count(from).block_id(BlockId::latest()).await?; + let nonce = provider.get_transaction_count(from, BlockId::latest()).await?; let tx_nonce = tx.nonce.expect("no nonce"); if nonce != tx_nonce { diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index 64e1c7539..8d9907ccf 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -7,7 +7,6 @@ use crate::{ }; use alloy_primitives::{Bytes, B256}; use alloy_provider::Provider; -use alloy_rpc_types::BlockId; use eyre::{OptionExt, Result}; use foundry_cheatcodes::ScriptWallets; use foundry_common::{ @@ -47,7 +46,7 @@ impl BuildData { let can_use_create2 = if let Some(fork_url) = &script_config.evm_opts.fork_url { let provider = try_get_http_provider(fork_url)?; let deployer_code = - provider.get_code_at(DEFAULT_CREATE2_DEPLOYER).block_id(BlockId::latest()).await?; + provider.get_code_at(DEFAULT_CREATE2_DEPLOYER, Default::default()).await?; !deployer_code.is_empty() } else { diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index c0331ed9f..8b625d6ee 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -141,8 +141,7 @@ impl ScriptTester { if let Some(provider) = &self.provider { let nonce = provider - .get_transaction_count(self.accounts_pub[index as usize]) - .block_id(BlockId::latest()) + .get_transaction_count(self.accounts_pub[index as usize], BlockId::latest()) .await .unwrap(); self.nonces.insert(index, nonce); @@ -157,8 +156,7 @@ impl ScriptTester { .provider .as_ref() .unwrap() - .get_transaction_count(address) - .block_id(BlockId::latest()) + .get_transaction_count(address, BlockId::latest()) .await .unwrap(); self.address_nonces.insert(address, nonce); @@ -204,8 +202,7 @@ impl ScriptTester { .provider .as_ref() .unwrap() - .get_transaction_count(addr) - .block_id(BlockId::latest()) + .get_transaction_count(addr, BlockId::latest()) .await .unwrap(); let prev_nonce = self.nonces.get(&private_key_slot).unwrap(); @@ -230,8 +227,7 @@ impl ScriptTester { .provider .as_ref() .unwrap() - .get_transaction_count(*address) - .block_id(BlockId::latest()) + .get_transaction_count(*address, BlockId::latest()) .await .unwrap(); let prev_nonce = self.address_nonces.get(address).unwrap(); diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index da0f805fd..53654a7d3 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -109,7 +109,7 @@ impl VerifyBytecodeArgs { let config = self.load_config_emit_warnings(); let provider = ProviderBuilder::new(&config.get_rpc_url_or_localhost_http()?).build()?; - let code = provider.get_code_at(self.address).block_id(BlockId::latest()).await?; + let code = provider.get_code_at(self.address, BlockId::latest()).await?; if code.is_empty() { eyre::bail!("No bytecode found at address {}", self.address); } @@ -291,10 +291,8 @@ impl VerifyBytecodeArgs { // Workaround for the NonceTooHigh issue as we're not simulating prior txs of the same // block. let prev_block_id = BlockId::Number(BlockNumberOrTag::Number(simulation_block - 1)); - let prev_block_nonce = provider - .get_transaction_count(creation_data.contract_creator) - .block_id(prev_block_id) - .await?; + let prev_block_nonce = + provider.get_transaction_count(creation_data.contract_creator, prev_block_id).await?; transaction.nonce = prev_block_nonce; if let Some(ref block) = block { @@ -345,8 +343,9 @@ impl VerifyBytecodeArgs { ) })?; - let onchain_runtime_code = - provider.get_code_at(self.address).block_id(BlockId::number(simulation_block)).await?; + let onchain_runtime_code = provider + .get_code_at(self.address, BlockId::Number(BlockNumberOrTag::Number(simulation_block))) + .await?; // Compare the runtime bytecode with the locally built bytecode let (did_match, with_status) = try_match( From 467aff3056842e8d45bc58a353be17349f0e8651 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Thu, 16 May 2024 23:27:54 +0300 Subject: [PATCH 287/430] Revert "fix: fuzz console log (#7781)" (#7935) This reverts commit a470d635cfcdce68609e9dc5762a3584351bacc1. --- crates/evm/evm/src/executors/fuzz/mod.rs | 16 +++-------- crates/evm/evm/src/executors/fuzz/types.rs | 4 +-- crates/forge/tests/cli/test_cmd.rs | 31 +--------------------- 3 files changed, 6 insertions(+), 45 deletions(-) diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index 1adabc5fd..d81ddbaca 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -1,7 +1,7 @@ use crate::executors::{Executor, RawCallResult}; use alloy_dyn_abi::JsonAbiExt; use alloy_json_abi::Function; -use alloy_primitives::{Address, Bytes, Log, U256}; +use alloy_primitives::{Address, Bytes, U256}; use eyre::Result; use foundry_config::FuzzConfig; use foundry_evm_core::{ @@ -87,9 +87,6 @@ impl FuzzedExecutor { dictionary_weight => fuzz_calldata_from_state(func.clone(), &state), ]; - //Stores logs for all fuzz cases - let logs: RefCell> = RefCell::default(); - debug!(func=?func.name, should_fail, "fuzzing"); let run_result = self.runner.clone().run(&strat, |calldata| { let fuzz_res = self.single_fuzz(address, should_fail, calldata)?; @@ -107,7 +104,6 @@ impl FuzzedExecutor { } traces.borrow_mut().push(call_traces); } - logs.borrow_mut().extend(case.logs); if let Some(prev) = coverage.take() { // Safety: If `Option::or` evaluates to `Some`, then `call.coverage` must @@ -131,7 +127,6 @@ impl FuzzedExecutor { // to run at least one more case to find a minimal failure // case. let call_res = _counterexample.1.result.clone(); - logs.borrow_mut().extend(_counterexample.1.logs.clone()); *counterexample.borrow_mut() = _counterexample; // HACK: we have to use an empty string here to denote `None` let reason = rd.maybe_decode(&call_res, Some(status)); @@ -145,16 +140,14 @@ impl FuzzedExecutor { let mut traces = traces.into_inner(); let last_run_traces = if run_result.is_ok() { traces.pop() } else { call.traces.clone() }; - let inner_logs = logs.into_inner(); - let mut result = FuzzTestResult { first_case: first_case.take().unwrap_or_default(), gas_by_case: gas_by_case.take(), success: run_result.is_ok(), reason: None, counterexample: None, - decoded_logs: decode_console_logs(&inner_logs), - logs: inner_logs, + decoded_logs: decode_console_logs(&call.logs), + logs: call.logs, labeled_addresses: call.labels, traces: last_run_traces, gas_report_traces: traces, @@ -214,7 +207,7 @@ impl FuzzedExecutor { // When the `assume` cheatcode is called it returns a special string if call.result.as_ref() == MAGIC_ASSUME { - return Err(TestCaseError::reject(FuzzError::AssumeReject)); + return Err(TestCaseError::reject(FuzzError::AssumeReject)) } let breakpoints = call @@ -236,7 +229,6 @@ impl FuzzedExecutor { coverage: call.coverage, debug: call.debug, breakpoints, - logs: call.logs, })) } else { Ok(FuzzOutcome::CounterExample(CounterExampleOutcome { diff --git a/crates/evm/evm/src/executors/fuzz/types.rs b/crates/evm/evm/src/executors/fuzz/types.rs index 3791165ca..b15cf3faa 100644 --- a/crates/evm/evm/src/executors/fuzz/types.rs +++ b/crates/evm/evm/src/executors/fuzz/types.rs @@ -1,5 +1,5 @@ use crate::executors::RawCallResult; -use alloy_primitives::{Bytes, Log}; +use alloy_primitives::Bytes; use foundry_common::evm::Breakpoints; use foundry_evm_core::debug::DebugArena; use foundry_evm_coverage::HitMaps; @@ -20,8 +20,6 @@ pub struct CaseOutcome { pub debug: Option, /// Breakpoints char pc map pub breakpoints: Breakpoints, - /// logs of a single fuzz test case - pub logs: Vec, } /// Returned by a single fuzz when a counterexample has been discovered diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 5fe9b388a..2b0784d6a 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -1,6 +1,6 @@ //! Contains various tests for `forge test`. -use foundry_config::{Config, FuzzConfig}; +use foundry_config::Config; use foundry_test_utils::{ rpc, util::{OutputExt, OTHER_SOLC_VERSION, SOLC_VERSION}, @@ -544,32 +544,3 @@ contract Dummy { cmd.args(["test", "--match-path", "src/dummy.sol"]); cmd.assert_success() }); - -// tests that `forge test` for fuzz tests will display `console.log` info -forgetest_init!(can_test_fuzz_with_console_log, |prj, cmd| { - prj.wipe_contracts(); - - // run fuzz test 3 times - let config = - Config { fuzz: { FuzzConfig { runs: 3, ..Default::default() } }, ..Default::default() }; - prj.write_config(config); - let config = cmd.config(); - assert_eq!(config.fuzz.runs, 3); - - prj.add_test( - "ContractFuzz.t.sol", - r#"pragma solidity 0.8.24; - import {Test, console2} from "forge-std/Test.sol"; - - contract ContractFuzz is Test { - function testFuzzConsoleLog(uint256 x) public { - console2.log("inside fuzz test, x is:", x); - } - } - "#, - ) - .unwrap(); - cmd.args(["test", "-vv"]); - let stdout = cmd.stdout_lossy(); - assert!(stdout.contains("inside fuzz test, x is:"), "\n{stdout}"); -}); From 54d8510c0f2b0f791f4c5ef99866c6af99b7606a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 19 May 2024 10:46:09 +0200 Subject: [PATCH 288/430] chore(deps): weekly `cargo update` (#7946) Updating git repository `https://github.com/alloy-rs/alloy` Updating git repository `https://github.com/paradigmxyz/evm-inspectors` Locking 48 packages to latest compatible versions Updating alloy-dyn-abi v0.7.2 -> v0.7.4 Updating alloy-json-abi v0.7.2 -> v0.7.4 Updating alloy-primitives v0.7.2 -> v0.7.4 Updating alloy-sol-macro v0.7.2 -> v0.7.4 Adding alloy-sol-macro-expander v0.7.4 Updating alloy-sol-macro-input v0.7.2 -> v0.7.4 Updating alloy-sol-type-parser v0.7.2 -> v0.7.4 Updating alloy-sol-types v0.7.2 -> v0.7.4 Updating ammonia v3.3.0 -> v4.0.0 Updating anyhow v1.0.83 -> v1.0.86 Updating aws-sdk-kms v1.25.0 -> v1.26.0 Updating aws-sdk-sso v1.24.0 -> v1.25.0 Updating aws-sdk-ssooidc v1.25.0 -> v1.26.0 Updating aws-sdk-sts v1.24.0 -> v1.25.0 Updating bytemuck v1.15.0 -> v1.16.0 Updating camino v1.1.6 -> v1.1.7 Updating const-hex v1.11.3 -> v1.11.4 Updating darling v0.20.8 -> v0.20.9 Updating darling_core v0.20.8 -> v0.20.9 Updating darling_macro v0.20.8 -> v0.20.9 Adding dbus v0.9.7 Updating either v1.11.0 -> v1.12.0 Updating figment v0.10.18 -> v0.10.19 Updating html5ever v0.26.0 -> v0.27.0 Updating instant v0.1.12 -> v0.1.13 Updating libc v0.2.154 -> v0.2.155 Adding libdbus-sys v0.2.5 Updating linux-raw-sys v0.4.13 -> v0.4.14 (latest: v0.6.4) Updating markup5ever v0.11.0 -> v0.12.1 Updating mdbook v0.4.37 -> v0.4.40 Updating miniz_oxide v0.7.2 -> v0.7.3 Adding opener v0.7.1 Removing phf v0.10.1 Removing phf_codegen v0.10.0 Updating rustls-webpki v0.102.3 -> v0.102.4 Updating rustversion v1.0.16 -> v1.0.17 Updating schemars v0.8.19 -> v0.8.20 Updating schemars_derive v0.8.19 -> v0.8.20 Updating serde v1.0.201 -> v1.0.202 Updating serde_derive v1.0.201 -> v1.0.202 Updating serde_derive_internals v0.29.0 -> v0.29.1 Updating serde_spanned v0.6.5 -> v0.6.6 Updating syn v2.0.63 -> v2.0.64 Updating syn-solidity v0.7.2 -> v0.7.4 Updating thiserror v1.0.60 -> v1.0.61 Updating thiserror-impl v1.0.60 -> v1.0.61 Updating toml v0.8.12 -> v0.8.13 Updating toml_datetime v0.6.5 -> v0.6.6 Updating toml_edit v0.22.12 -> v0.22.13 Updating zip v1.2.3 -> v1.3.0 note: pass `--verbose` to see 139 unchanged dependencies behind latest Co-authored-by: mattsse --- Cargo.lock | 370 ++++++++++++++++++++++++++++------------------------- 1 file changed, 199 insertions(+), 171 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 52a36a589..440f2e3d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -110,9 +110,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545885d9b0b2c30fd344ae291439b4bfe59e48dd62fbc862f8503d98088967dc" +checksum = "8425a283510106b1a6ad25dd4bb648ecde7da3fd2baeb9400a85ad62f51ec90b" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -157,9 +157,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786689872ec4e7d354810ab0dffd48bb40b838c047522eb031cbd47d15634849" +checksum = "7e30946aa6173020259055a44971f5cf40a7d76c931d209caeb51b333263df4f" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -198,9 +198,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525448f6afc1b70dd0f9d0a8145631bf2f5e434678ab23ab18409ca264cae6b3" +checksum = "db8aa973e647ec336810a9356af8aea787249c9d00b1525359f3db29a68d231b" dependencies = [ "alloy-rlp", "arbitrary", @@ -291,7 +291,7 @@ checksum = "1a047897373be4bbb0224c1afdabca92648dc57a9c9ef6e7b0be3aff7a859c83" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -463,9 +463,23 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89c80a2cb97e7aa48611cbb63950336f9824a174cdf670527cc6465078a26ea1" +checksum = "7dbd17d67f3e89478c8a634416358e539e577899666c927bc3d2b1328ee9b6ca" +dependencies = [ + "alloy-sol-macro-expander", + "alloy-sol-macro-input", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.64", +] + +[[package]] +name = "alloy-sol-macro-expander" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6da95adcf4760bb4b108fefa51d50096c5e5fdd29ee72fed3e86ee414f2e34" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", @@ -475,16 +489,16 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", "syn-solidity", "tiny-keccak", ] [[package]] name = "alloy-sol-macro-input" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c58894b58ac50979eeac6249661991ac40b9d541830d9a725f7714cc9ef08c23" +checksum = "32c8da04c1343871fb6ce5a489218f9c85323c8340a36e9106b5fc98d4dd59d5" dependencies = [ "alloy-json-abi", "const-hex", @@ -493,24 +507,24 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.63", + "syn 2.0.64", "syn-solidity", ] [[package]] name = "alloy-sol-type-parser" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8e71ea68e780cc203919e03f69f59e7afe92d2696fb1dcb6662f61e4031b6" +checksum = "368cae4dc052cad1d8f72eb2ae0c38027116933eeb49213c200a9e9875f208d7" dependencies = [ "winnow 0.6.8", ] [[package]] name = "alloy-sol-types" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "399287f68d1081ed8b1f4903c49687658b95b142207d7cb4ae2f4813915343ef" +checksum = "40a64d2d2395c1ac636b62419a7b17ec39031d6b2367e66e9acbf566e6055e9c" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -605,9 +619,9 @@ dependencies = [ [[package]] name = "ammonia" -version = "3.3.0" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e6d1c7838db705c9b756557ee27c384ce695a1c51a6fe528784cb1c6840170" +checksum = "1ab99eae5ee58501ab236beb6f20f6ca39be615267b014899c89b2f0bc18a459" dependencies = [ "html5ever", "maplit", @@ -812,9 +826,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.83" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "arbitrary" @@ -991,7 +1005,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -1013,7 +1027,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -1024,7 +1038,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -1071,7 +1085,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -1148,9 +1162,9 @@ dependencies = [ [[package]] name = "aws-sdk-kms" -version = "1.25.0" +version = "1.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dca6ab350d6652bf85e38503758a67b2735b3d1ad38fd2f55181ef3c09afdb1c" +checksum = "b5a6b58615203957a253a180b81db9427cb4b623fc8bab8dcac42369239f7716" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1170,9 +1184,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.24.0" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd6a9b38fe2dcaa2422b42e2c667112872d03a83522533f8b4165fd2d9d4bd1" +checksum = "fef2d9ca2b43051224ed326ed9960a85e277b7d554a2cd0397e57c0553d86e64" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1192,9 +1206,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.25.0" +version = "1.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c99f342a364c8490b7715387244d2f7ebfc05f07bb6a0fc6e70b8e62b7078d06" +checksum = "c869d1f5c4ee7437b79c3c1664ddbf7a60231e893960cf82b2b299a5ccf2cc5d" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1214,9 +1228,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.24.0" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7207ca62206c93e470457babf0512d7b6b97170fdba929a2a2f5682f9f68407c" +checksum = "9e2b4a632a59e4fab7abf1db0d94a3136ad7871aba46bebd1fdb95c7054afcdb" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1643,9 +1657,9 @@ checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" [[package]] name = "bytemuck" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" +checksum = "78834c15cb5d5efe3452d58b1e8ba890dd62d21907f867f383358198e56ebca5" [[package]] name = "byteorder" @@ -1688,9 +1702,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" dependencies = [ "serde", ] @@ -1956,7 +1970,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -2149,9 +2163,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.11.3" +version = "1.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ba00838774b4ab0233e355d26710fbfc8327a05c017f6dc4873f876d1f79f78" +checksum = "70ff96486ccc291d36a958107caf2c0af8c78c0af7d31ae2f35ce055130de1a6" dependencies = [ "cfg-if", "cpufeatures", @@ -2352,9 +2366,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" dependencies = [ "darling_core", "darling_macro", @@ -2362,27 +2376,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "strsim 0.10.0", - "syn 2.0.63", + "strsim 0.11.1", + "syn 2.0.64", ] [[package]] name = "darling_macro" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ "darling_core", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -2404,6 +2418,17 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +[[package]] +name = "dbus" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" +dependencies = [ + "libc", + "libdbus-sys", + "winapi", +] + [[package]] name = "der" version = "0.7.9" @@ -2443,7 +2468,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -2464,7 +2489,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -2474,7 +2499,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" dependencies = [ "derive_builder_core", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -2594,7 +2619,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -2637,9 +2662,9 @@ dependencies = [ [[package]] name = "either" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "elasticlunr-rs" @@ -2702,7 +2727,7 @@ checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -2852,8 +2877,8 @@ dependencies = [ "regex", "serde", "serde_json", - "syn 2.0.63", - "toml 0.8.12", + "syn 2.0.64", + "toml 0.8.13", "walkdir", ] @@ -2880,7 +2905,7 @@ dependencies = [ "serde", "serde_json", "strum", - "syn 2.0.63", + "syn 2.0.64", "tempfile", "thiserror", "tiny-keccak", @@ -2983,16 +3008,16 @@ dependencies = [ [[package]] name = "figment" -version = "0.10.18" +version = "0.10.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d032832d74006f99547004d49410a4b4218e4c33382d56ca3ff89df74f86b953" +checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" dependencies = [ "atomic", "parking_lot", "pear", "serde", "tempfile", - "toml 0.8.12", + "toml 0.8.13", "uncased", "version_check", ] @@ -3110,7 +3135,7 @@ dependencies = [ "itertools 0.12.1", "mockall", "once_cell", - "opener", + "opener 0.6.1", "parking_lot", "paste", "path-slash", @@ -3132,8 +3157,8 @@ dependencies = [ "thiserror", "tikv-jemallocator", "tokio", - "toml 0.8.12", - "toml_edit 0.22.12", + "toml 0.8.13", + "toml_edit 0.22.13", "tower-http", "tracing", "tracing-subscriber", @@ -3163,7 +3188,7 @@ dependencies = [ "serde_json", "solang-parser", "thiserror", - "toml 0.8.12", + "toml 0.8.13", "tracing", ] @@ -3178,7 +3203,7 @@ dependencies = [ "similar-asserts", "solang-parser", "thiserror", - "toml 0.8.12", + "toml 0.8.13", "tracing", "tracing-subscriber", ] @@ -3319,7 +3344,7 @@ dependencies = [ "semver 1.0.23", "serde_json", "thiserror", - "toml 0.8.12", + "toml 0.8.13", "tracing", "walkdir", ] @@ -3488,8 +3513,8 @@ dependencies = [ "solang-parser", "tempfile", "thiserror", - "toml 0.8.12", - "toml_edit 0.22.12", + "toml 0.8.13", + "toml_edit 0.22.13", "tracing", "walkdir", ] @@ -3658,7 +3683,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -3818,7 +3843,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -4254,16 +4279,16 @@ dependencies = [ [[package]] name = "html5ever" -version = "0.26.0" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" +checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4" dependencies = [ "log", "mac", "markup5ever", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.64", ] [[package]] @@ -4652,9 +4677,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", ] @@ -4858,9 +4883,19 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.154" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libdbus-sys" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" +dependencies = [ + "cc", + "pkg-config", +] [[package]] name = "libm" @@ -4892,9 +4927,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" @@ -4935,13 +4970,13 @@ checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" [[package]] name = "markup5ever" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" +checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45" dependencies = [ "log", - "phf 0.10.1", - "phf_codegen 0.10.0", + "phf", + "phf_codegen", "string_cache", "string_cache_codegen", "tendril", @@ -4974,9 +5009,9 @@ dependencies = [ [[package]] name = "mdbook" -version = "0.4.37" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c33564061c3c640bed5ace7d6a2a1b65f2c64257d1ac930c15e94ed0fb561d3" +checksum = "b45a38e19bd200220ef07c892b0157ad3d2365e5b5a267ca01ad12182491eea5" dependencies = [ "ammonia", "anyhow", @@ -4989,7 +5024,7 @@ dependencies = [ "log", "memchr", "once_cell", - "opener", + "opener 0.7.1", "pulldown-cmark", "regex", "serde", @@ -5053,7 +5088,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -5080,9 +5115,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" dependencies = [ "adler", ] @@ -5123,7 +5158,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -5355,7 +5390,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -5443,6 +5478,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "opener" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8df34be653210fbe9ffaff41d3b92721c56ce82dfee58ee684f9afb5e3a90c0" +dependencies = [ + "bstr 1.9.1", + "dbus", + "normpath", + "windows-sys 0.52.0", +] + [[package]] name = "openssl" version = "0.10.64" @@ -5466,7 +5513,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -5623,7 +5670,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -5682,7 +5729,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -5716,15 +5763,6 @@ dependencies = [ "rustc_version 0.4.0", ] -[[package]] -name = "phf" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" -dependencies = [ - "phf_shared 0.10.0", -] - [[package]] name = "phf" version = "0.11.2" @@ -5735,16 +5773,6 @@ dependencies = [ "phf_shared 0.11.2", ] -[[package]] -name = "phf_codegen" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", -] - [[package]] name = "phf_codegen" version = "0.11.2" @@ -5785,7 +5813,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -5823,7 +5851,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -5939,7 +5967,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -6015,7 +6043,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", "version_check", "yansi", ] @@ -6609,7 +6637,7 @@ dependencies = [ "log", "ring", "rustls-pki-types", - "rustls-webpki 0.102.3", + "rustls-webpki 0.102.4", "subtle", "zeroize", ] @@ -6676,9 +6704,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.3" +version = "0.102.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3bce581c0dd41bce533ce695a1437fa16a7ab5ac3ccfa99fe1a620a7885eabf" +checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" dependencies = [ "ring", "rustls-pki-types", @@ -6687,9 +6715,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "092474d1a01ea8278f69e6a358998405fae5b8b963ddaeb2b0b04a128bf1dfb0" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "rusty-fork" @@ -6794,9 +6822,9 @@ dependencies = [ [[package]] name = "schemars" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6e7ed6919cb46507fb01ff1654309219f62b4d603822501b0b80d42f6f21ef" +checksum = "b0218ceea14babe24a4a5836f86ade86c1effbc198164e619194cb5069187e29" dependencies = [ "dyn-clone", "schemars_derive", @@ -6806,14 +6834,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "185f2b7aa7e02d418e453790dde16890256bbd2bcd04b7dc5348811052b53f49" +checksum = "3ed5a1ccce8ff962e31a165d41f6e2a2dd1245099dc4d594f5574a86cd90f4d3" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -6941,33 +6969,33 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.201" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" +checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.201" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" +checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] name = "serde_derive_internals" -version = "0.29.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -7010,14 +7038,14 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ "serde", ] @@ -7056,7 +7084,7 @@ checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -7240,7 +7268,7 @@ dependencies = [ "itertools 0.11.0", "lalrpop", "lalrpop-util", - "phf 0.11.2", + "phf", "thiserror", "unicode-xid", ] @@ -7274,7 +7302,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ff9eaf853dec4c8802325d8b6d3dffa86cc707fd7a1a4cdbf416e13b061787a" dependencies = [ "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -7346,7 +7374,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -7414,9 +7442,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.63" +version = "2.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" +checksum = "7ad3dee41f36859875573074334c200d1add8e4a87bb37113ebd31d926b7b11f" dependencies = [ "proc-macro2", "quote", @@ -7425,14 +7453,14 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa0cefd02f532035d83cfec82647c6eb53140b0485220760e669f4bad489e36" +checksum = "b8db114c44cf843a8bacd37a146e37987a0b823a0e8bc4fdc610c9c72ab397a5" dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -7506,8 +7534,8 @@ dependencies = [ "dirs 4.0.0", "fnv", "nom", - "phf 0.11.2", - "phf_codegen 0.11.2", + "phf", + "phf_codegen", ] [[package]] @@ -7518,22 +7546,22 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -7669,7 +7697,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -7766,22 +7794,22 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.12" +version = "0.8.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.12", + "toml_edit 0.22.13", ] [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ "serde", ] @@ -7799,9 +7827,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.12" +version = "0.22.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" +checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" dependencies = [ "indexmap", "serde", @@ -7889,7 +7917,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -8236,7 +8264,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", "wasm-bindgen-shared", ] @@ -8270,7 +8298,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -8714,7 +8742,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -8734,14 +8762,14 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] name = "zip" -version = "1.2.3" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c700ea425e148de30c29c580c1f9508b93ca57ad31c9f4e96b83c194c37a7a8f" +checksum = "f1f4a27345eb6f7aa7bd015ba7eb4175fa4e1b462a29874b779e0bbcf96c6ac7" dependencies = [ "arbitrary", "crc32fast", From 1ddea96f34a35fcc63238e02c44d2983df2f4fbb Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Mon, 20 May 2024 10:22:48 +0300 Subject: [PATCH 289/430] feat(invariant): fuzz with values from events and return values (#7666) * feat(invariant): scrape return values and add to fuzz dictionary * Perist mined values between runs * Refactor, add persistent samples * Apply weight to collected sample values * Add Function to BasicTxDetails (if has outputs), to be used for decoding. Decode results and persist per types. Use typed samples when fuzzing from state. * Fix clippy and fmt * Use prop-perturb take 1 * Decode logs using target abi, populate type samples * Fmt * Fix clippy, add calldetails type * Fix fmt test * Insert call sample once * Proper function naming * Generate state values bias using strategy * Add BasicTxDetails and CallTargetDetails struct, add Function always to call details and use it to generate counterexample * Tests cleanup * Code cleanup * Move args in CallDetails * Fallback to old impl if we are not able to decode logs * Refactor collect values fn * Get abi from FuzzedContracts * Lookup function from identified target abi. --- crates/evm/evm/src/executors/invariant/mod.rs | 44 +++++-- .../evm/evm/src/executors/invariant/replay.rs | 16 ++- .../evm/evm/src/executors/invariant/shrink.rs | 10 +- crates/evm/fuzz/src/inspector.rs | 14 +- .../evm/fuzz/src/invariant/call_override.rs | 12 +- crates/evm/fuzz/src/invariant/mod.rs | 31 ++++- crates/evm/fuzz/src/strategies/invariants.rs | 13 +- crates/evm/fuzz/src/strategies/param.rs | 28 +++- crates/evm/fuzz/src/strategies/state.rs | 121 ++++++++++++++---- crates/forge/tests/it/invariant.rs | 52 ++++++++ .../common/InvariantScrapeValues.t.sol | 69 ++++++++++ 11 files changed, 341 insertions(+), 69 deletions(-) create mode 100644 testdata/default/fuzz/invariant/common/InvariantScrapeValues.t.sol diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index a15f98f8b..ef7823342 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -205,11 +205,16 @@ impl<'a> InvariantExecutor<'a> { let mut assume_rejects_counter = 0; while current_run < self.config.depth { - let (sender, (address, calldata)) = inputs.last().expect("no input generated"); + let tx = inputs.last().expect("no input generated"); // Execute call from the randomly generated sequence and commit state changes. let call_result = executor - .call_raw_committing(*sender, *address, calldata.clone(), U256::ZERO) + .call_raw_committing( + tx.sender, + tx.call_details.target, + tx.call_details.calldata.clone(), + U256::ZERO, + ) .expect("could not make raw evm call"); if call_result.result.as_ref() == MAGIC_ASSUME { @@ -226,7 +231,16 @@ impl<'a> InvariantExecutor<'a> { let mut state_changeset = call_result.state_changeset.to_owned().expect("no changesets"); - collect_data(&mut state_changeset, sender, &call_result, &fuzz_state); + if !&call_result.reverted { + collect_data( + &mut state_changeset, + &targeted_contracts, + tx, + &call_result, + &fuzz_state, + self.config.depth, + ); + } // Collect created contracts and add to fuzz targets only if targeted contracts // are updatable. @@ -244,7 +258,7 @@ impl<'a> InvariantExecutor<'a> { } fuzz_runs.push(FuzzCase { - calldata: calldata.clone(), + calldata: tx.call_details.calldata.clone(), gas: call_result.gas_used, stipend: call_result.stipend, }); @@ -639,13 +653,16 @@ impl<'a> InvariantExecutor<'a> { /// randomly generated addresses. fn collect_data( state_changeset: &mut HashMap, - sender: &Address, + fuzzed_contracts: &FuzzRunIdentifiedContracts, + tx: &BasicTxDetails, call_result: &RawCallResult, fuzz_state: &EvmFuzzState, + run_depth: u32, ) { // Verify it has no code. let mut has_code = false; - if let Some(Some(code)) = state_changeset.get(sender).map(|account| account.info.code.as_ref()) + if let Some(Some(code)) = + state_changeset.get(&tx.sender).map(|account| account.info.code.as_ref()) { has_code = !code.is_empty(); } @@ -653,13 +670,22 @@ fn collect_data( // We keep the nonce changes to apply later. let mut sender_changeset = None; if !has_code { - sender_changeset = state_changeset.remove(sender); + sender_changeset = state_changeset.remove(&tx.sender); } - fuzz_state.collect_state_from_call(&call_result.logs, &*state_changeset); + // Collect values from fuzzed call result and add them to fuzz dictionary. + let (fuzzed_contract_abi, fuzzed_function) = fuzzed_contracts.fuzzed_artifacts(tx); + fuzz_state.collect_values_from_call( + fuzzed_contract_abi.as_ref(), + fuzzed_function.as_ref(), + &call_result.result, + &call_result.logs, + &*state_changeset, + run_depth, + ); // Re-add changes if let Some(changed) = sender_changeset { - state_changeset.insert(*sender, changed); + state_changeset.insert(tx.sender, changed); } } diff --git a/crates/evm/evm/src/executors/invariant/replay.rs b/crates/evm/evm/src/executors/invariant/replay.rs index e55581174..a225ccb2c 100644 --- a/crates/evm/evm/src/executors/invariant/replay.rs +++ b/crates/evm/evm/src/executors/invariant/replay.rs @@ -35,9 +35,13 @@ pub fn replay_run( let mut counterexample_sequence = vec![]; // Replay each call from the sequence, collect logs, traces and coverage. - for (sender, (addr, bytes)) in inputs.iter() { - let call_result = - executor.call_raw_committing(*sender, *addr, bytes.clone(), U256::ZERO)?; + for tx in inputs.iter() { + let call_result = executor.call_raw_committing( + tx.sender, + tx.call_details.target, + tx.call_details.calldata.clone(), + U256::ZERO, + )?; logs.extend(call_result.logs); traces.push((TraceKind::Execution, call_result.traces.clone().unwrap())); @@ -57,9 +61,9 @@ pub fn replay_run( // Create counter example to be used in failed case. counterexample_sequence.push(BaseCounterExample::create( - *sender, - *addr, - bytes, + tx.sender, + tx.call_details.target, + &tx.call_details.calldata, &ided_contracts, call_result.traces, )); diff --git a/crates/evm/evm/src/executors/invariant/shrink.rs b/crates/evm/evm/src/executors/invariant/shrink.rs index 7b78ae028..e6af9c1bc 100644 --- a/crates/evm/evm/src/executors/invariant/shrink.rs +++ b/crates/evm/evm/src/executors/invariant/shrink.rs @@ -120,9 +120,13 @@ fn check_sequence( let mut sequence_failed = false; // Apply the shrinked candidate sequence. for call_index in sequence { - let (sender, (addr, bytes)) = &calls[call_index]; - let call_result = - executor.call_raw_committing(*sender, *addr, bytes.clone(), U256::ZERO)?; + let tx = &calls[call_index]; + let call_result = executor.call_raw_committing( + tx.sender, + tx.call_details.target, + tx.call_details.calldata.clone(), + U256::ZERO, + )?; if call_result.reverted && failed_case.fail_on_revert { // Candidate sequence fails test. // We don't have to apply remaining calls to check sequence. diff --git a/crates/evm/fuzz/src/inspector.rs b/crates/evm/fuzz/src/inspector.rs index 5e7e644b1..eb089baf6 100644 --- a/crates/evm/fuzz/src/inspector.rs +++ b/crates/evm/fuzz/src/inspector.rs @@ -82,16 +82,14 @@ impl Fuzzer { !call_generator.used { // There's only a 30% chance that an override happens. - if let Some((sender, (contract, input))) = - call_generator.next(call.context.caller, call.contract) - { - *call.input = input.0; - call.context.caller = sender; - call.contract = contract; + if let Some(tx) = call_generator.next(call.context.caller, call.contract) { + *call.input = tx.call_details.calldata.0; + call.context.caller = tx.sender; + call.contract = tx.call_details.target; // TODO: in what scenarios can the following be problematic - call.context.code_address = contract; - call.context.address = contract; + call.context.code_address = tx.call_details.target; + call.context.address = tx.call_details.target; call_generator.used = true; } diff --git a/crates/evm/fuzz/src/invariant/call_override.rs b/crates/evm/fuzz/src/invariant/call_override.rs index 1d9edba5c..a98c50002 100644 --- a/crates/evm/fuzz/src/invariant/call_override.rs +++ b/crates/evm/fuzz/src/invariant/call_override.rs @@ -1,5 +1,5 @@ -use super::BasicTxDetails; -use alloy_primitives::{Address, Bytes}; +use super::{BasicTxDetails, CallDetails}; +use alloy_primitives::Address; use parking_lot::{Mutex, RwLock}; use proptest::{ option::weighted, @@ -17,7 +17,7 @@ pub struct RandomCallGenerator { /// Runner that will generate the call from the strategy. pub runner: Arc>, /// Strategy to be used to generate calls from `target_reference`. - pub strategy: SBoxedStrategy>, + pub strategy: SBoxedStrategy>, /// Reference to which contract we want a fuzzed calldata from. pub target_reference: Arc>, /// Flag to know if a call has been overridden. Don't allow nesting for now. @@ -33,7 +33,7 @@ impl RandomCallGenerator { pub fn new( test_address: Address, runner: TestRunner, - strategy: SBoxedStrategy<(Address, Bytes)>, + strategy: SBoxedStrategy, target_reference: Arc>, ) -> Self { let strategy = weighted(0.9, strategy).sboxed(); @@ -71,7 +71,7 @@ impl RandomCallGenerator { ) } else { // TODO: Do we want it to be 80% chance only too ? - let new_caller = original_target; + let sender = original_target; // Set which contract we mostly (80% chance) want to generate calldata from. *self.target_reference.write() = original_caller; @@ -82,7 +82,7 @@ impl RandomCallGenerator { .new_tree(&mut self.runner.lock()) .unwrap() .current() - .map(|(new_target, calldata)| (new_caller, (new_target, calldata))); + .map(|call_details| BasicTxDetails { sender, call_details }); self.last_sequence.write().push(choice.clone()); choice diff --git a/crates/evm/fuzz/src/invariant/mod.rs b/crates/evm/fuzz/src/invariant/mod.rs index d682041e9..7c3a1ffc0 100644 --- a/crates/evm/fuzz/src/invariant/mod.rs +++ b/crates/evm/fuzz/src/invariant/mod.rs @@ -26,10 +26,37 @@ impl FuzzRunIdentifiedContracts { pub fn new(targets: TargetedContracts, is_updatable: bool) -> Self { Self { targets: Arc::new(Mutex::new(targets)), is_updatable } } + + /// Returns fuzzed contract abi and fuzzed function from address and provided calldata. + /// Used to decode return values and logs in order to add values into fuzz dictionary. + pub fn fuzzed_artifacts(&self, tx: &BasicTxDetails) -> (Option, Option) { + match self.targets.lock().get(&tx.call_details.target) { + Some((_, abi, _)) => ( + Some(abi.to_owned()), + abi.functions().find(|f| f.selector() == tx.call_details.calldata[..4]).cloned(), + ), + None => (None, None), + } + } } -/// (Sender, (TargetContract, Calldata)) -pub type BasicTxDetails = (Address, (Address, Bytes)); +/// Details of a transaction generated by invariant strategy for fuzzing a target. +#[derive(Clone, Debug)] +pub struct BasicTxDetails { + // Transaction sender address. + pub sender: Address, + // Transaction call details. + pub call_details: CallDetails, +} + +/// Call details of a transaction generated to fuzz invariant target. +#[derive(Clone, Debug)] +pub struct CallDetails { + // Address of target contract. + pub target: Address, + // The data of the transaction. + pub calldata: Bytes, +} /// Test contract which is testing its invariants. #[derive(Clone, Debug)] diff --git a/crates/evm/fuzz/src/strategies/invariants.rs b/crates/evm/fuzz/src/strategies/invariants.rs index 08e53b2a0..49971613e 100644 --- a/crates/evm/fuzz/src/strategies/invariants.rs +++ b/crates/evm/fuzz/src/strategies/invariants.rs @@ -1,11 +1,11 @@ use super::{fuzz_calldata, fuzz_param_from_state}; use crate::{ - invariant::{BasicTxDetails, FuzzRunIdentifiedContracts, SenderFilters}, + invariant::{BasicTxDetails, CallDetails, FuzzRunIdentifiedContracts, SenderFilters}, strategies::{fuzz_calldata_from_state, fuzz_param, EvmFuzzState}, FuzzFixtures, }; use alloy_json_abi::{Function, JsonAbi}; -use alloy_primitives::{Address, Bytes}; +use alloy_primitives::Address; use parking_lot::RwLock; use proptest::prelude::*; use std::{rc::Rc, sync::Arc}; @@ -16,7 +16,7 @@ pub fn override_call_strat( contracts: FuzzRunIdentifiedContracts, target: Arc>, fuzz_fixtures: FuzzFixtures, -) -> SBoxedStrategy<(Address, Bytes)> { +) -> SBoxedStrategy { let contracts_ref = contracts.targets.clone(); proptest::prop_oneof![ 80 => proptest::strategy::LazyJust::new(move || *target.read()), @@ -101,6 +101,7 @@ fn generate_call( (sender, contract) }) }) + .prop_map(|(sender, call_details)| BasicTxDetails { sender, call_details }) .boxed() } @@ -166,9 +167,9 @@ fn select_random_function( pub fn fuzz_contract_with_calldata( fuzz_state: &EvmFuzzState, fuzz_fixtures: &FuzzFixtures, - contract: Address, + target: Address, func: Function, -) -> impl Strategy { +) -> impl Strategy { // We need to compose all the strategies generated for each parameter in all possible // combinations. // `prop_oneof!` / `TupleUnion` `Arc`s for cheap cloning. @@ -179,6 +180,6 @@ pub fn fuzz_contract_with_calldata( ] .prop_map(move |calldata| { trace!(input=?calldata); - (contract, calldata) + CallDetails { target, calldata } }) } diff --git a/crates/evm/fuzz/src/strategies/param.rs b/crates/evm/fuzz/src/strategies/param.rs index 137a04232..df7fb28c0 100644 --- a/crates/evm/fuzz/src/strategies/param.rs +++ b/crates/evm/fuzz/src/strategies/param.rs @@ -124,13 +124,27 @@ pub fn fuzz_param_from_state( // Value strategy that uses the state. let value = || { let state = state.clone(); - // Use `Index` instead of `Selector` to not iterate over the entire dictionary. - any::().prop_map(move |index| { - let state = state.dictionary_read(); - let values = state.values(); - let index = index.index(values.len()); - *values.iter().nth(index).unwrap() - }) + let param = param.clone(); + // Generate a bias and use it to pick samples or non-persistent values (50 / 50). + // Use `Index` instead of `Selector` when selecting a value to avoid iterating over the + // entire dictionary. + ((0..100).prop_flat_map(Just), any::()).prop_map( + move |(bias, index)| { + let state = state.dictionary_read(); + let values = match bias { + x if x < 50 => { + if let Some(sample_values) = state.samples(param.clone()) { + sample_values + } else { + state.values() + } + } + _ => state.values(), + }; + let index = index.index(values.len()); + *values.iter().nth(index).unwrap() + }, + ) }; // Convert the value based on the parameter type diff --git a/crates/evm/fuzz/src/strategies/state.rs b/crates/evm/fuzz/src/strategies/state.rs index 0a05028cc..1bb2a7e7a 100644 --- a/crates/evm/fuzz/src/strategies/state.rs +++ b/crates/evm/fuzz/src/strategies/state.rs @@ -1,5 +1,7 @@ use crate::invariant::{ArtifactFilters, FuzzRunIdentifiedContracts}; -use alloy_primitives::{Address, Log, B256, U256}; +use alloy_dyn_abi::{DynSolType, DynSolValue, EventExt, FunctionExt}; +use alloy_json_abi::{Function, JsonAbi}; +use alloy_primitives::{Address, Bytes, Log, B256, U256}; use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; use foundry_config::FuzzDictionaryConfig; use foundry_evm_core::utils::StateChangeset; @@ -10,7 +12,7 @@ use revm::{ interpreter::opcode::{self, spec_opcode_gas}, primitives::{AccountInfo, SpecId}, }; -use std::{fmt, sync::Arc}; +use std::{collections::HashMap, fmt, sync::Arc}; /// A set of arbitrary 32 byte data from the VM used to generate values for the strategy. /// @@ -41,9 +43,18 @@ impl EvmFuzzState { /// Collects state changes from a [StateChangeset] and logs into an [EvmFuzzState] according to /// the given [FuzzDictionaryConfig]. - pub fn collect_state_from_call(&self, logs: &[Log], state_changeset: &StateChangeset) { + pub fn collect_values_from_call( + &self, + target_abi: Option<&JsonAbi>, + target_function: Option<&Function>, + result: &Bytes, + logs: &[Log], + state_changeset: &StateChangeset, + run_depth: u32, + ) { let mut dict = self.inner.write(); - dict.insert_logs_values(logs); + dict.insert_result_values(target_function, result, run_depth); + dict.insert_logs_values(target_abi, logs, run_depth); dict.insert_state_values(state_changeset); } @@ -74,6 +85,8 @@ pub struct FuzzDictionary { new_values: IndexSet<[u8; 32]>, /// New addresses added to the dictionary since container initialization. new_addreses: IndexSet
, + /// Sample typed values that are collected from call result and used across invariant runs. + sample_values: HashMap>, } impl fmt::Debug for FuzzDictionary { @@ -114,6 +127,61 @@ impl FuzzDictionary { } } + /// Insert values collected from call result into fuzz dictionary. + fn insert_result_values( + &mut self, + function: Option<&Function>, + result: &Bytes, + run_depth: u32, + ) { + if let Some(function) = function { + if !function.outputs.is_empty() { + // Decode result and collect samples to be used in subsequent fuzz runs. + if let Ok(decoded_result) = function.abi_decode_output(result, false) { + self.insert_sample_values(decoded_result, run_depth); + } + } + } + } + + /// Insert values from call log topics and data into fuzz dictionary. + fn insert_logs_values(&mut self, abi: Option<&JsonAbi>, logs: &[Log], run_depth: u32) { + let mut samples = Vec::new(); + // Decode logs with known events and collect samples from indexed fields and event body. + for log in logs { + let mut log_decoded = false; + // Try to decode log with events from contract abi. + if let Some(abi) = abi { + for event in abi.events() { + if let Ok(decoded_event) = event.decode_log(log, false) { + samples.extend(decoded_event.indexed); + samples.extend(decoded_event.body); + log_decoded = true; + break; + } + } + } + + // If we weren't able to decode event then we insert raw data in fuzz dictionary. + if !log_decoded { + for topic in log.topics() { + self.insert_value(topic.0, true); + } + let chunks = log.data.data.chunks_exact(32); + let rem = chunks.remainder(); + for chunk in chunks { + self.insert_value(chunk.try_into().unwrap(), true); + } + if !rem.is_empty() { + self.insert_value(B256::right_padding_from(rem).0, true); + } + } + } + + // Insert samples collected from current call in fuzz dictionary. + self.insert_sample_values(samples, run_depth); + } + /// Insert values from call state changeset into fuzz dictionary. /// These values are removed at the end of current run. fn insert_state_values(&mut self, state_changeset: &StateChangeset) { @@ -131,24 +199,6 @@ impl FuzzDictionary { } } - /// Insert values from call log topics and data into fuzz dictionary. - /// These values are removed at the end of current run. - fn insert_logs_values(&mut self, logs: &[Log]) { - for log in logs { - for topic in log.topics() { - self.insert_value(topic.0, true); - } - let chunks = log.data.data.chunks_exact(32); - let rem = chunks.remainder(); - for chunk in chunks { - self.insert_value(chunk.try_into().unwrap(), true); - } - if !rem.is_empty() { - self.insert_value(B256::right_padding_from(rem).0, true); - } - } - } - /// Insert values from push bytes into fuzz dictionary. /// If values are newly collected then they are removed at the end of current run. fn insert_push_bytes_values( @@ -206,17 +256,44 @@ impl FuzzDictionary { } } + /// Insert sample values that are reused across multiple runs. + /// The number of samples is limited to invariant run depth. + /// If collected samples limit is reached then values are inserted as regular values. + pub fn insert_sample_values(&mut self, sample_values: Vec, limit: u32) { + for sample in sample_values { + let sample_type = sample.as_type().unwrap(); + let sample_value = sample.as_word().unwrap().into(); + + if let Some(values) = self.sample_values.get_mut(&sample_type) { + if values.len() < limit as usize { + values.insert(sample_value); + } else { + // Insert as state value (will be removed at the end of the run). + self.insert_value(sample_value, true); + } + } else { + self.sample_values.entry(sample_type).or_default().insert(sample_value); + } + } + } + #[inline] pub fn values(&self) -> &IndexSet<[u8; 32]> { &self.state_values } + #[inline] + pub fn samples(&self, param_type: DynSolType) -> Option<&IndexSet<[u8; 32]>> { + self.sample_values.get(¶m_type) + } + #[inline] pub fn addresses(&self) -> &IndexSet
{ &self.addresses } pub fn revert(&mut self) { + // Revert new values collected during the run. for key in self.new_values.iter() { self.state_values.swap_remove(key); } diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index 90bc4c9c5..cb39b6500 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -181,6 +181,26 @@ async fn test_invariant() { "default/fuzz/invariant/common/InvariantShrinkFailOnRevert.t.sol:ShrinkFailOnRevertTest", vec![("invariant_shrink_fail_on_revert()", true, None, None, None)], ), + ( + "default/fuzz/invariant/common/InvariantScrapeValues.t.sol:FindFromReturnValueTest", + vec![( + "invariant_value_not_found()", + false, + Some("revert: value from return found".into()), + None, + None, + )], + ), + ( + "default/fuzz/invariant/common/InvariantScrapeValues.t.sol:FindFromLogValueTest", + vec![( + "invariant_value_not_found()", + false, + Some("revert: value from logs found".into()), + None, + None, + )], + ) ]), ); } @@ -566,3 +586,35 @@ async fn test_invariant_fixtures() { )]), ); } + +#[tokio::test(flavor = "multi_thread")] +async fn test_invariant_scrape_values() { + let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantScrapeValues.t.sol"); + let mut runner = TEST_DATA_DEFAULT.runner(); + let results = runner.test_collect(&filter); + assert_multiple( + &results, + BTreeMap::from([ + ( + "default/fuzz/invariant/common/InvariantScrapeValues.t.sol:FindFromReturnValueTest", + vec![( + "invariant_value_not_found()", + false, + Some("revert: value from return found".into()), + None, + None, + )], + ), + ( + "default/fuzz/invariant/common/InvariantScrapeValues.t.sol:FindFromLogValueTest", + vec![( + "invariant_value_not_found()", + false, + Some("revert: value from logs found".into()), + None, + None, + )], + ), + ]), + ); +} diff --git a/testdata/default/fuzz/invariant/common/InvariantScrapeValues.t.sol b/testdata/default/fuzz/invariant/common/InvariantScrapeValues.t.sol new file mode 100644 index 000000000..40824a260 --- /dev/null +++ b/testdata/default/fuzz/invariant/common/InvariantScrapeValues.t.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract FindFromReturnValue { + bool public found = false; + + function seed() public returns (int256) { + int256 mystery = 13337; + return (1337 + mystery); + } + + function find(int256 i) public { + int256 mystery = 13337; + if (i == 1337 + mystery) { + found = true; + } + } +} + +contract FindFromReturnValueTest is DSTest { + FindFromReturnValue target; + + function setUp() public { + target = new FindFromReturnValue(); + } + + /// forge-config: default.invariant.runs = 50 + /// forge-config: default.invariant.depth = 300 + /// forge-config: default.invariant.fail-on-revert = true + function invariant_value_not_found() public view { + require(!target.found(), "value from return found"); + } +} + +contract FindFromLogValue { + event FindFromLog(int256 indexed mystery, bytes32 rand); + + bool public found = false; + + function seed() public { + int256 mystery = 13337; + emit FindFromLog(1337 + mystery, keccak256(abi.encodePacked("mystery"))); + } + + function find(int256 i) public { + int256 mystery = 13337; + if (i == 1337 + mystery) { + found = true; + } + } +} + +contract FindFromLogValueTest is DSTest { + FindFromLogValue target; + + function setUp() public { + target = new FindFromLogValue(); + } + + /// forge-config: default.invariant.runs = 50 + /// forge-config: default.invariant.depth = 300 + /// forge-config: default.invariant.fail-on-revert = true + function invariant_value_not_found() public view { + require(!target.found(), "value from logs found"); + } +} From 1b08ae4ece84a862d1d85a6e6b41bc64311bfb1e Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Mon, 20 May 2024 21:11:10 +0300 Subject: [PATCH 290/430] fix(fuzz) - consistent snapshot results between runs (#7951) * fix(fuzz) - consistent gas snapshot between runs * sort storage values before inserting * Revert "fix(fuzz) - consistent gas snapshot between runs" This reverts commit cf187fb2315c191782129c2fca0e3318a8a3a36d. --------- Co-authored-by: Arsenii Kulikov --- crates/evm/fuzz/src/strategies/state.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/evm/fuzz/src/strategies/state.rs b/crates/evm/fuzz/src/strategies/state.rs index 1bb2a7e7a..0bcf81bae 100644 --- a/crates/evm/fuzz/src/strategies/state.rs +++ b/crates/evm/fuzz/src/strategies/state.rs @@ -12,7 +12,11 @@ use revm::{ interpreter::opcode::{self, spec_opcode_gas}, primitives::{AccountInfo, SpecId}, }; -use std::{collections::HashMap, fmt, sync::Arc}; +use std::{ + collections::{BTreeMap, HashMap}, + fmt, + sync::Arc, +}; /// A set of arbitrary 32 byte data from the VM used to generate values for the strategy. /// @@ -113,7 +117,9 @@ impl FuzzDictionary { self.insert_push_bytes_values(address, &account.info, false); // Insert storage values. if self.config.include_storage { - for (slot, value) in &account.storage { + // Sort storage values before inserting to ensure deterministic dictionary. + let values = account.storage.iter().collect::>(); + for (slot, value) in values { self.insert_storage_value(slot, value, false); } } From c9ae920ae3e215a02e77384d1fc0b2eb4a5d0d96 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 21 May 2024 07:59:58 +0300 Subject: [PATCH 291/430] feature(invariant) - persist and replay failure (#7899) * feature(invariant) - persist and replay failure * Fix unit test * Changes after review: - replace test cache rm macro with closure - use commons for load / persist failure sequence * Changes after review: display proper message if replayed sequence reverts before checking invariant * Changes after review: simplify check sequence logic --- crates/config/src/invariant.rs | 37 ++++- crates/config/src/lib.rs | 27 ++-- crates/evm/evm/src/executors/fuzz/mod.rs | 13 +- crates/evm/evm/src/executors/invariant/mod.rs | 1 + .../evm/evm/src/executors/invariant/replay.rs | 46 +++--- .../evm/evm/src/executors/invariant/shrink.rs | 63 ++++---- crates/evm/fuzz/src/lib.rs | 44 ++++-- crates/forge/bin/cmd/coverage.rs | 2 +- crates/forge/bin/cmd/test/mod.rs | 2 +- crates/forge/src/runner.rs | 103 +++++++++++-- crates/forge/tests/cli/cmd.rs | 25 +++- crates/forge/tests/cli/config.rs | 6 +- crates/forge/tests/it/invariant.rs | 137 ++++++++++-------- crates/forge/tests/it/test_helpers.rs | 1 + 14 files changed, 343 insertions(+), 164 deletions(-) diff --git a/crates/config/src/invariant.rs b/crates/config/src/invariant.rs index fa9e5489f..869125324 100644 --- a/crates/config/src/invariant.rs +++ b/crates/config/src/invariant.rs @@ -8,9 +8,10 @@ use crate::{ }, }; use serde::{Deserialize, Serialize}; +use std::path::PathBuf; /// Contains for invariant testing -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct InvariantConfig { /// The number of runs that must execute for each invariant test group. pub runs: u32, @@ -31,6 +32,8 @@ pub struct InvariantConfig { pub max_assume_rejects: u32, /// Number of runs to execute and include in the gas report. pub gas_report_samples: u32, + /// Path where invariant failures are recorded and replayed. + pub failure_persist_dir: Option, } impl Default for InvariantConfig { @@ -44,10 +47,36 @@ impl Default for InvariantConfig { shrink_run_limit: 2usize.pow(18_u32), max_assume_rejects: 65536, gas_report_samples: 256, + failure_persist_dir: None, } } } +impl InvariantConfig { + /// Creates invariant configuration to write failures in `{PROJECT_ROOT}/cache/fuzz` dir. + pub fn new(cache_dir: PathBuf) -> Self { + InvariantConfig { + runs: 256, + depth: 15, + fail_on_revert: false, + call_override: false, + dictionary: FuzzDictionaryConfig { dictionary_weight: 80, ..Default::default() }, + shrink_run_limit: 2usize.pow(18_u32), + max_assume_rejects: 65536, + gas_report_samples: 256, + failure_persist_dir: Some(cache_dir), + } + } + + /// Returns path to failure dir of given invariant test contract. + pub fn failure_dir(self, contract_name: &str) -> PathBuf { + self.failure_persist_dir + .unwrap() + .join("failures") + .join(contract_name.split(':').last().unwrap()) + } +} + impl InlineConfigParser for InvariantConfig { fn config_key() -> String { INLINE_CONFIG_INVARIANT_KEY.into() @@ -60,8 +89,7 @@ impl InlineConfigParser for InvariantConfig { return Ok(None) } - // self is Copy. We clone it with dereference. - let mut conf_clone = *self; + let mut conf_clone = self.clone(); for pair in overrides { let key = pair.0; @@ -71,6 +99,9 @@ impl InlineConfigParser for InvariantConfig { "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)) + } _ => Err(InlineConfigParserError::InvalidConfigProperty(key.to_string()))?, } } diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 9c490ef03..e48f8d571 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -21,7 +21,7 @@ use foundry_compilers::{ }, cache::SOLIDITY_FILES_CACHE_FILENAME, compilers::{solc::SolcVersionManager, CompilerVersionManager}, - error::{SolcError, SolcIoError}, + error::SolcError, remappings::{RelativeRemapping, Remapping}, CompilerConfig, ConfigurableArtifacts, EvmVersion, Project, ProjectPathsConfig, Solc, SolcConfig, @@ -793,13 +793,17 @@ impl Config { pub fn cleanup(&self, project: &Project) -> Result<(), SolcError> { project.cleanup()?; - // Remove fuzz cache directory. - if let Some(fuzz_cache) = &self.fuzz.failure_persist_dir { - let path = project.root().join(fuzz_cache); - if path.exists() { - std::fs::remove_dir_all(&path).map_err(|e| SolcIoError::new(e, path))?; + // Remove fuzz and invariant cache directories. + let remove_test_dir = |test_dir: &Option| { + if let Some(test_dir) = test_dir { + let path = project.root().join(test_dir); + if path.exists() { + let _ = fs::remove_dir_all(&path); + } } - } + }; + remove_test_dir(&self.fuzz.failure_persist_dir); + remove_test_dir(&self.invariant.failure_persist_dir); Ok(()) } @@ -1958,7 +1962,7 @@ impl Default for Config { path_pattern: None, path_pattern_inverse: None, fuzz: FuzzConfig::new("cache/fuzz".into()), - invariant: Default::default(), + invariant: InvariantConfig::new("cache/invariant".into()), always_use_create_2_factory: false, ffi: false, prompt_timeout: 120, @@ -4461,7 +4465,12 @@ mod tests { let loaded = Config::load().sanitized(); assert_eq!( loaded.invariant, - InvariantConfig { runs: 512, depth: 10, ..Default::default() } + InvariantConfig { + runs: 512, + depth: 10, + failure_persist_dir: Some(PathBuf::from("cache/invariant")), + ..Default::default() + } ); Ok(()) diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index d81ddbaca..520a56cf2 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -175,15 +175,10 @@ impl FuzzedExecutor { } else { vec![] }; - result.counterexample = Some(CounterExample::Single(BaseCounterExample { - sender: None, - addr: None, - signature: None, - contract_name: None, - traces: call.traces, - calldata, - args, - })); + + 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 ef7823342..edf1f9291 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -40,6 +40,7 @@ mod result; pub use result::InvariantFuzzTestResult; mod shrink; +pub use shrink::check_sequence; sol! { interface IInvariantTest { diff --git a/crates/evm/evm/src/executors/invariant/replay.rs b/crates/evm/evm/src/executors/invariant/replay.rs index a225ccb2c..1da2a3ebd 100644 --- a/crates/evm/evm/src/executors/invariant/replay.rs +++ b/crates/evm/evm/src/executors/invariant/replay.rs @@ -8,7 +8,7 @@ use foundry_evm_core::constants::CALLER; use foundry_evm_coverage::HitMaps; use foundry_evm_fuzz::{ invariant::{BasicTxDetails, InvariantContract}, - BaseCounterExample, CounterExample, + BaseCounterExample, }; use foundry_evm_traces::{load_contracts, TraceKind, Traces}; use parking_lot::RwLock; @@ -28,7 +28,7 @@ pub fn replay_run( traces: &mut Traces, coverage: &mut Option, inputs: Vec, -) -> Result> { +) -> Result> { // We want traces for a failed case. executor.set_tracing(true); @@ -60,31 +60,34 @@ pub fn replay_run( )); // Create counter example to be used in failed case. - counterexample_sequence.push(BaseCounterExample::create( + counterexample_sequence.push(BaseCounterExample::from_invariant_call( tx.sender, tx.call_details.target, &tx.call_details.calldata, &ided_contracts, call_result.traces, )); - - // Replay invariant to collect logs and traces. - let error_call_result = executor.call_raw( - CALLER, - invariant_contract.address, - invariant_contract - .invariant_function - .abi_encode_input(&[]) - .expect("invariant should have no inputs") - .into(), - U256::ZERO, - )?; - traces.push((TraceKind::Execution, error_call_result.traces.clone().unwrap())); - logs.extend(error_call_result.logs); } - Ok((!counterexample_sequence.is_empty()) - .then_some(CounterExample::Sequence(counterexample_sequence))) + // Replay invariant to collect logs and traces. + // We do this only once at the end of the replayed sequence. + // Checking after each call doesn't add valuable info for passing scenario + // (invariant call result is always success) nor for failed scenarios + // (invariant call result is always success until the last call that breaks it). + let invariant_result = executor.call_raw( + CALLER, + invariant_contract.address, + invariant_contract + .invariant_function + .abi_encode_input(&[]) + .expect("invariant should have no inputs") + .into(), + U256::ZERO, + )?; + traces.push((TraceKind::Execution, invariant_result.traces.clone().unwrap())); + logs.extend(invariant_result.logs); + + Ok(counterexample_sequence) } /// Replays the error case, shrinks the failing sequence and collects all necessary traces. @@ -98,15 +101,16 @@ pub fn replay_error( logs: &mut Vec, traces: &mut Traces, coverage: &mut Option, -) -> Result> { +) -> Result> { match failed_case.test_error { // Don't use at the moment. - TestError::Abort(_) => Ok(None), + TestError::Abort(_) => Ok(vec![]), TestError::Fail(_, ref calls) => { // Shrink sequence of failed calls. let calls = shrink_sequence(failed_case, calls, &executor)?; set_up_inner_replay(&mut executor, &failed_case.inner_sequence); + // Replay calls to get the counterexample and to collect logs, traces and coverage. replay_run( invariant_contract, diff --git a/crates/evm/evm/src/executors/invariant/shrink.rs b/crates/evm/evm/src/executors/invariant/shrink.rs index e6af9c1bc..47711e11b 100644 --- a/crates/evm/evm/src/executors/invariant/shrink.rs +++ b/crates/evm/evm/src/executors/invariant/shrink.rs @@ -1,5 +1,5 @@ use crate::executors::{invariant::error::FailedInvariantCaseData, Executor}; -use alloy_primitives::U256; +use alloy_primitives::{Address, Bytes, U256}; use foundry_evm_core::constants::CALLER; use foundry_evm_fuzz::invariant::BasicTxDetails; use proptest::bits::{BitSetLike, VarBitSet}; @@ -97,12 +97,19 @@ pub(crate) fn shrink_sequence( let mut shrinker = CallSequenceShrinker::new(calls.len()); for _ in 0..failed_case.shrink_run_limit { // Check candidate sequence result. - match check_sequence(failed_case, executor.clone(), calls, shrinker.current().collect()) { + match check_sequence( + executor.clone(), + calls, + shrinker.current().collect(), + failed_case.addr, + failed_case.func.clone(), + failed_case.fail_on_revert, + ) { // If candidate sequence still fails then shrink more if possible. - Ok(false) if !shrinker.simplify() => break, + Ok((false, _)) if !shrinker.simplify() => break, // If candidate sequence pass then restore last removed call and shrink other // calls if possible. - Ok(true) if !shrinker.complicate() => break, + Ok((true, _)) if !shrinker.complicate() => break, _ => {} } } @@ -110,15 +117,19 @@ pub(crate) fn shrink_sequence( Ok(shrinker.current().map(|idx| &calls[idx]).cloned().collect()) } -/// Checks if the shrinked sequence fails test, if it does then we can try simplifying more. -fn check_sequence( - failed_case: &FailedInvariantCaseData, +/// Checks if the given call sequence breaks the invariant. +/// Used in shrinking phase for checking candidate sequences and in replay failures phase to test +/// persisted failures. +/// Returns the result of invariant check and if sequence was entirely applied. +pub fn check_sequence( mut executor: Executor, calls: &[BasicTxDetails], sequence: Vec, -) -> eyre::Result { - let mut sequence_failed = false; - // Apply the shrinked candidate sequence. + test_address: Address, + test_function: Bytes, + fail_on_revert: bool, +) -> eyre::Result<(bool, bool)> { + // Apply the call sequence. for call_index in sequence { let tx = &calls[call_index]; let call_result = executor.call_raw_committing( @@ -127,30 +138,22 @@ fn check_sequence( tx.call_details.calldata.clone(), U256::ZERO, )?; - if call_result.reverted && failed_case.fail_on_revert { + if call_result.reverted && fail_on_revert { // Candidate sequence fails test. // We don't have to apply remaining calls to check sequence. - sequence_failed = true; - break; + return Ok((false, false)); } } - // Return without checking the invariant if we already have failing sequence. - if sequence_failed { - return Ok(false); - }; - // Check the invariant for candidate sequence. - // If sequence fails then we can continue with shrinking - the removed call does not affect - // failure. - // - // If sequence doesn't fail then we have to restore last removed call and continue with next - // call - removed call is a required step for reproducing the failure. - let mut call_result = - executor.call_raw(CALLER, failed_case.addr, failed_case.func.clone(), U256::ZERO)?; - Ok(executor.is_raw_call_success( - failed_case.addr, - Cow::Owned(call_result.state_changeset.take().unwrap()), - &call_result, - false, + // Check the invariant for call sequence. + let mut call_result = executor.call_raw(CALLER, test_address, test_function, U256::ZERO)?; + Ok(( + executor.is_raw_call_success( + test_address, + Cow::Owned(call_result.state_changeset.take().unwrap()), + &call_result, + false, + ), + true, )) } diff --git a/crates/evm/fuzz/src/lib.rs b/crates/evm/fuzz/src/lib.rs index b2a058e5b..c6b5e0049 100644 --- a/crates/evm/fuzz/src/lib.rs +++ b/crates/evm/fuzz/src/lib.rs @@ -43,19 +43,20 @@ pub struct BaseCounterExample { pub addr: Option
, /// The data to provide pub calldata: Bytes, - /// Function signature if it exists - pub signature: Option, /// Contract name if it exists pub contract_name: Option, + /// Function signature if it exists + pub signature: Option, + /// Args used to call the function + pub args: Option, /// Traces #[serde(skip)] pub traces: Option, - #[serde(skip)] - pub args: Vec, } impl BaseCounterExample { - pub fn create( + /// Creates counter example representing a step from invariant call sequence. + pub fn from_invariant_call( sender: Address, addr: Address, bytes: &Bytes, @@ -70,10 +71,12 @@ impl BaseCounterExample { sender: Some(sender), addr: Some(addr), calldata: bytes.clone(), - signature: Some(func.signature()), contract_name: Some(name.clone()), + signature: Some(func.signature()), + args: Some( + foundry_common::fmt::format_tokens(&args).format(", ").to_string(), + ), traces, - args, }; } } @@ -83,10 +86,27 @@ impl BaseCounterExample { sender: Some(sender), addr: Some(addr), calldata: bytes.clone(), + contract_name: None, signature: None, + args: None, + traces, + } + } + + /// Creates counter example for a fuzz test failure. + pub fn from_fuzz_call( + bytes: Bytes, + args: Vec, + traces: Option, + ) -> Self { + BaseCounterExample { + sender: None, + addr: None, + calldata: bytes, contract_name: None, + signature: None, + args: Some(foundry_common::fmt::format_tokens(&args).format(", ").to_string()), traces, - args: vec![], } } } @@ -108,10 +128,14 @@ impl fmt::Display for BaseCounterExample { if let Some(sig) = &self.signature { write!(f, "calldata={sig}")? } else { - write!(f, "calldata={}", self.calldata)? + write!(f, "calldata={}", &self.calldata)? } - write!(f, " args=[{}]", foundry_common::fmt::format_tokens(&self.args).format(", ")) + if let Some(args) = &self.args { + write!(f, " args=[{}]", args) + } else { + write!(f, " args=[]") + } } } diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index d099035fb..d55c4db9a 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -312,7 +312,7 @@ impl CoverageArgs { .with_fork(evm_opts.get_fork(&config, env.clone())) .with_test_options(TestOptions { fuzz: config.fuzz.clone(), - invariant: config.invariant, + invariant: config.invariant.clone(), ..Default::default() }) .set_coverage(true) diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index cef234966..720d13284 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -257,7 +257,7 @@ impl TestArgs { let test_options: TestOptions = TestOptionsBuilder::default() .fuzz(config.fuzz.clone()) - .invariant(config.invariant) + .invariant(config.invariant.clone()) .profiles(profiles) .build(&output, project_root)?; diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 48fa10b6d..3f4824c22 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -1,6 +1,7 @@ //! The Forge test runner. use crate::{ + fuzz::{invariant::BasicTxDetails, BaseCounterExample}, multi_runner::{is_matching_test, TestContract}, result::{SuiteResult, TestKind, TestResult, TestSetup, TestStatus}, TestFilter, TestOptions, @@ -21,18 +22,23 @@ use foundry_evm::{ executors::{ fuzz::{CaseOutcome, CounterExampleOutcome, FuzzOutcome, FuzzedExecutor}, invariant::{ - replay_error, replay_run, InvariantExecutor, InvariantFuzzError, + check_sequence, replay_error, replay_run, InvariantExecutor, InvariantFuzzError, InvariantFuzzTestResult, }, CallResult, EvmError, ExecutionErr, Executor, RawCallResult, }, - fuzz::{fixture_name, invariant::InvariantContract, CounterExample, FuzzFixtures}, + fuzz::{ + fixture_name, + invariant::{CallDetails, InvariantContract}, + CounterExample, FuzzFixtures, + }, traces::{load_contracts, TraceKind}, }; use proptest::test_runner::TestRunner; use rayon::prelude::*; use std::{ borrow::Cow, + cmp::min, collections::{BTreeMap, HashMap}, sync::Arc, time::Instant, @@ -359,7 +365,7 @@ impl<'a> ContractRunner<'a> { self.run_invariant_test( runner, setup, - *invariant_config, + invariant_config.clone(), func, &known_contracts, identified_contracts.as_ref().unwrap(), @@ -548,14 +554,82 @@ impl<'a> ContractRunner<'a> { let mut evm = InvariantExecutor::new( self.executor.clone(), runner, - invariant_config, + invariant_config.clone(), identified_contracts, known_contracts, ); - let invariant_contract = InvariantContract { address, invariant_function: func, abi: &self.contract.abi }; + let mut logs = logs.clone(); + let mut traces = traces.clone(); + let mut coverage = coverage.clone(); + + let failure_dir = invariant_config.clone().failure_dir(self.name); + let failure_file = failure_dir.join(invariant_contract.invariant_function.clone().name); + + // Try to replay recorded failure if any. + if let Ok(call_sequence) = + foundry_common::fs::read_json_file::>(failure_file.as_path()) + { + // Create calls from failed sequence and check if invariant still broken. + let txes = call_sequence + .clone() + .into_iter() + .map(|seq| BasicTxDetails { + sender: seq.sender.unwrap_or_default(), + call_details: CallDetails { + target: seq.addr.unwrap_or_default(), + calldata: seq.calldata, + }, + }) + .collect::>(); + if let Ok((success, replayed_entirely)) = check_sequence( + self.executor.clone(), + &txes, + (0..min(txes.len(), invariant_config.depth as usize)).collect(), + invariant_contract.address, + invariant_contract.invariant_function.selector().to_vec().into(), + invariant_config.fail_on_revert, + ) { + if !success { + // If sequence still fails then replay error to collect traces and + // exit without executing new runs. + let _ = replay_run( + &invariant_contract, + self.executor.clone(), + known_contracts, + identified_contracts.clone(), + &mut logs, + &mut traces, + &mut coverage, + txes, + ); + return TestResult { + status: TestStatus::Failure, + reason: if replayed_entirely { + Some(format!( + "{} replay failure", + invariant_contract.invariant_function.name + )) + } else { + Some(format!( + "{} persisted failure revert", + invariant_contract.invariant_function.name + )) + }, + decoded_logs: decode_console_logs(&logs), + traces, + coverage, + counterexample: Some(CounterExample::Sequence(call_sequence)), + kind: TestKind::Invariant { runs: 1, calls: 1, reverts: 1 }, + duration: start.elapsed(), + ..Default::default() + } + } + } + } + let InvariantFuzzTestResult { error, cases, reverts, last_run_inputs, gas_report_traces } = match evm.invariant_fuzz(invariant_contract.clone(), &fuzz_fixtures) { Ok(x) => x, @@ -576,11 +650,9 @@ impl<'a> ContractRunner<'a> { }; let mut counterexample = None; - let mut logs = logs.clone(); - let mut traces = traces.clone(); let success = error.is_none(); let reason = error.as_ref().and_then(|err| err.revert_reason()); - let mut coverage = coverage.clone(); + match error { // If invariants were broken, replay the error to collect logs and traces Some(error) => match error { @@ -598,7 +670,20 @@ impl<'a> ContractRunner<'a> { &mut traces, &mut coverage, ) { - Ok(c) => counterexample = c, + Ok(call_sequence) => { + if !call_sequence.is_empty() { + // Persist error in invariant failure dir. + if let Err(err) = foundry_common::fs::create_dir_all(failure_dir) { + error!(%err, "Failed to create invariant failure dir"); + } else if let Err(err) = foundry_common::fs::write_json_file( + failure_file.as_path(), + &call_sequence, + ) { + error!(%err, "Failed to record call sequence"); + } + counterexample = Some(CounterExample::Sequence(call_sequence)) + } + } Err(err) => { error!(%err, "Failed to replay invariant error"); } diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index b4d8df8cc..de2c24241 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -3,7 +3,7 @@ use crate::constants::*; use foundry_compilers::{artifacts::Metadata, remappings::Remapping, ConfigurableContractArtifact}; use foundry_config::{ - parse_with_profile, BasicConfig, Chain, Config, FuzzConfig, SolidityErrorCode, + parse_with_profile, BasicConfig, Chain, Config, FuzzConfig, InvariantConfig, SolidityErrorCode, }; use foundry_test_utils::{ foundry_compilers::PathStyle, @@ -546,18 +546,27 @@ forgetest_init!(can_clean_config, |prj, cmd| { assert!(!artifact.exists()); }); -// checks that `clean` removes fuzz cache dir -forgetest_init!(can_clean_fuzz_cache, |prj, cmd| { - let config = Config { fuzz: FuzzConfig::new("cache/fuzz".into()), ..Default::default() }; +// checks that `clean` removes fuzz and invariant cache dirs +forgetest_init!(can_clean_test_cache, |prj, cmd| { + let config = Config { + fuzz: FuzzConfig::new("cache/fuzz".into()), + invariant: InvariantConfig::new("cache/invariant".into()), + ..Default::default() + }; prj.write_config(config); // default test contract is written in custom out directory - let cache_dir = prj.root().join("cache/fuzz"); - let _ = fs::create_dir(cache_dir.clone()); - assert!(cache_dir.exists()); + let fuzz_cache_dir = prj.root().join("cache/fuzz"); + let _ = fs::create_dir(fuzz_cache_dir.clone()); + let invariant_cache_dir = prj.root().join("cache/invariant"); + let _ = fs::create_dir(invariant_cache_dir.clone()); + + assert!(fuzz_cache_dir.exists()); + assert!(invariant_cache_dir.exists()); cmd.forge_fuse().arg("clean"); cmd.output(); - assert!(!cache_dir.exists()); + assert!(!fuzz_cache_dir.exists()); + assert!(!invariant_cache_dir.exists()); }); // checks that extra output works diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index c0856ce08..aec3b6dc2 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -72,7 +72,11 @@ forgetest!(can_extract_config_values, |prj, cmd| { failure_persist_file: Some("failures".to_string()), ..Default::default() }, - invariant: InvariantConfig { runs: 256, ..Default::default() }, + invariant: InvariantConfig { + runs: 256, + failure_persist_dir: Some("test-cache/fuzz".into()), + ..Default::default() + }, ffi: true, always_use_create_2_factory: false, prompt_timeout: 0, diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index cb39b6500..abdd6591a 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -2,14 +2,33 @@ use crate::{config::*, test_helpers::TEST_DATA_DEFAULT}; use alloy_primitives::U256; -use forge::{fuzz::CounterExample, result::TestStatus, TestOptions}; +use forge::{fuzz::CounterExample, TestOptions}; use foundry_test_utils::Filter; use std::collections::BTreeMap; +macro_rules! get_counterexample { + ($runner:ident, $filter:expr) => { + $runner + .test_collect($filter) + .values() + .last() + .expect("Invariant contract should be testable.") + .test_results + .values() + .last() + .expect("Invariant contract should be testable.") + .counterexample + .as_ref() + .expect("Invariant contract should have failed with a counterexample.") + }; +} + #[tokio::test(flavor = "multi_thread")] async fn test_invariant() { let filter = Filter::new(".*", ".*", ".*fuzz/invariant/(target|targetAbi|common)"); let mut runner = TEST_DATA_DEFAULT.runner(); + runner.test_options.invariant.failure_persist_dir = + Some(tempfile::tempdir().unwrap().into_path()); let results = runner.test_collect(&filter); assert_multiple( @@ -275,20 +294,8 @@ 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 results = runner.test_collect(&filter); - let results = - results.values().last().expect("`InvariantInnerContract.t.sol` should be testable."); - - let result = - results.test_results.values().last().expect("`InvariantInnerContract` should be testable."); - - let counter = result - .counterexample - .as_ref() - .expect("`InvariantInnerContract` should have failed with a counterexample."); - - match counter { + match get_counterexample!(runner, &filter) { CounterExample::Single(_) => panic!("CounterExample should be a sequence."), // `fuzz_seed` at 119 makes this sequence shrinkable from 4 to 2. CounterExample::Sequence(sequence) => { @@ -333,23 +340,8 @@ async fn test_shrink(opts: TestOptions, contract_pattern: &str) { ); let mut runner = TEST_DATA_DEFAULT.runner(); runner.test_options = opts.clone(); - let results = runner.test_collect(&filter); - let results = results.values().last().expect("`InvariantShrinkWithAssert` should be testable."); - - let result = results - .test_results - .values() - .last() - .expect("`InvariantShrinkWithAssert` should be testable."); - assert_eq!(result.status, TestStatus::Failure); - - let counter = result - .counterexample - .as_ref() - .expect("`InvariantShrinkWithAssert` should have failed with a counterexample."); - - match counter { + match get_counterexample!(runner, &filter) { CounterExample::Single(_) => panic!("CounterExample should be a sequence."), CounterExample::Sequence(sequence) => { assert!(sequence.len() <= 3); @@ -369,30 +361,67 @@ async fn test_shrink_big_sequence() { runner.test_options = opts.clone(); runner.test_options.invariant.runs = 1; runner.test_options.invariant.depth = 500; - let results = runner.test_collect(&filter); - let results = - results.values().last().expect("`InvariantShrinkBigSequence` should be testable."); - let result = results + let initial_counterexample = runner + .test_collect(&filter) + .values() + .last() + .expect("Invariant contract should be testable.") .test_results .values() .last() - .expect("`InvariantShrinkBigSequence` should be testable."); + .expect("Invariant contract should be testable.") + .counterexample + .clone() + .unwrap(); - assert_eq!(result.status, TestStatus::Failure); + let initial_sequence = match initial_counterexample { + CounterExample::Single(_) => panic!("CounterExample should be a sequence."), + CounterExample::Sequence(sequence) => sequence, + }; + // ensure shrinks to same sequence of 77 + assert_eq!(initial_sequence.len(), 77); - let counter = result + // test failure persistence + let results = runner.test_collect(&filter); + assert_multiple( + &results, + BTreeMap::from([( + "default/fuzz/invariant/common/InvariantShrinkBigSequence.t.sol:ShrinkBigSequenceTest", + vec![( + "invariant_shrink_big_sequence()", + false, + Some("invariant_shrink_big_sequence replay failure".into()), + None, + None, + )], + )]), + ); + let new_sequence = match results + .values() + .last() + .expect("Invariant contract should be testable.") + .test_results + .values() + .last() + .expect("Invariant contract should be testable.") .counterexample - .as_ref() - .expect("`InvariantShrinkBigSequence` should have failed with a counterexample."); - - match counter { + .clone() + .unwrap() + { CounterExample::Single(_) => panic!("CounterExample should be a sequence."), - CounterExample::Sequence(sequence) => { - // ensure shrinks to same sequence of 77 - assert_eq!(sequence.len(), 77); - } + CounterExample::Sequence(sequence) => sequence, }; + // ensure shrinks to same sequence of 77 + assert_eq!(new_sequence.len(), 77); + // ensure calls within failed sequence are the same as initial one + for index in 0..77 { + let new_call = new_sequence.get(index).unwrap(); + let initial_call = initial_sequence.get(index).unwrap(); + assert_eq!(new_call.sender, initial_call.sender); + assert_eq!(new_call.addr, initial_call.addr); + assert_eq!(new_call.calldata, initial_call.calldata); + } } #[tokio::test(flavor = "multi_thread")] @@ -408,24 +437,8 @@ async fn test_shrink_fail_on_revert() { runner.test_options.invariant.fail_on_revert = true; runner.test_options.invariant.runs = 1; runner.test_options.invariant.depth = 100; - let results = runner.test_collect(&filter); - let results = - results.values().last().expect("`InvariantShrinkFailOnRevert` should be testable."); - - let result = results - .test_results - .values() - .last() - .expect("`InvariantShrinkFailOnRevert` should be testable."); - - assert_eq!(result.status, TestStatus::Failure); - - let counter = result - .counterexample - .as_ref() - .expect("`InvariantShrinkFailOnRevert` should have failed with a counterexample."); - match counter { + match get_counterexample!(runner, &filter) { CounterExample::Single(_) => panic!("CounterExample should be a sequence."), CounterExample::Sequence(sequence) => { // ensure shrinks to sequence of 10 diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index be87b9ce9..b2993151d 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -107,6 +107,7 @@ impl ForgeTestProfile { shrink_run_limit: 2usize.pow(18u32), max_assume_rejects: 65536, gas_report_samples: 256, + failure_persist_dir: Some(tempfile::tempdir().unwrap().into_path()), }) .build(output, Path::new(self.project().root())) .expect("Config loaded") From 003d0889ddb2aba5af8ff3606ae692e660156ae8 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Tue, 21 May 2024 04:01:36 -0400 Subject: [PATCH 292/430] feat(cast): send 4844 support (#7823) * feat(cast-send): 4844 support boilerplate * send 4844 initial test success * add path arg * use coder ingest * bump alloy d78e79e - satisfy clippy * clap nit and use get_blob_base_fee * nits * fix: cast cli tests --- Cargo.toml | 5 ++++- crates/cast/Cargo.toml | 2 +- crates/cast/bin/cmd/mktx.rs | 2 +- crates/cast/bin/cmd/send.rs | 17 ++++++++++++++--- crates/cast/bin/tx.rs | 19 +++++++++++++++++++ crates/cli/src/opts/transaction.rs | 8 ++++++++ 6 files changed, 47 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a807d1983..55d368d4a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -188,7 +188,10 @@ solang-parser = "=0.3.3" ## misc arrayvec = "0.7" base64 = "0.22" -chrono = { version = "0.4", default-features = false, features = ["clock", "std"] } +chrono = { version = "0.4", default-features = false, features = [ + "clock", + "std", +] } color-eyre = "0.6" derive_more = "0.99" evm-disassembler = "0.5" diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index 59ac39e26..d6837b176 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -37,7 +37,7 @@ alloy-json-rpc.workspace = true alloy-signer.workspace = true alloy-signer-wallet = { workspace = true, features = ["mnemonic", "keystore"] } alloy-contract.workspace = true -alloy-consensus = { workspace = true, features = ["serde"] } +alloy-consensus = { workspace = true, features = ["serde", "kzg"] } alloy-network.workspace = true alloy-sol-types.workspace = true alloy-chains.workspace = true diff --git a/crates/cast/bin/cmd/mktx.rs b/crates/cast/bin/cmd/mktx.rs index e26115da3..6b7b08c6f 100644 --- a/crates/cast/bin/cmd/mktx.rs +++ b/crates/cast/bin/cmd/mktx.rs @@ -97,7 +97,7 @@ impl MakeTxArgs { let provider = get_provider(&config)?; let (tx, _) = - tx::build_tx(&provider, from, to, code, sig, args, tx, chain, api_key).await?; + tx::build_tx(&provider, from, to, code, sig, args, tx, chain, api_key, None).await?; let tx = tx.build(&EthereumSigner::new(signer)).await?; diff --git a/crates/cast/bin/cmd/send.rs b/crates/cast/bin/cmd/send.rs index 7799f5226..3a6de10a9 100644 --- a/crates/cast/bin/cmd/send.rs +++ b/crates/cast/bin/cmd/send.rs @@ -14,7 +14,7 @@ use foundry_cli::{ }; use foundry_common::{cli_warn, ens::NameOrAddress}; use foundry_config::{Chain, Config}; -use std::str::FromStr; +use std::{path::PathBuf, str::FromStr}; /// CLI arguments for `cast send`. #[derive(Debug, Parser)] @@ -59,6 +59,10 @@ pub struct SendTxArgs { #[command(flatten)] eth: EthereumOpts, + + /// The path of blob data to be sent. + #[arg(long, value_name = "BLOB_DATA_PATH", conflicts_with = "legacy", requires = "blob")] + path: Option, } #[derive(Debug, Parser)] @@ -91,8 +95,11 @@ impl SendTxArgs { resend, command, unlocked, + path, } = self; + let blob_data = if let Some(path) = path { Some(std::fs::read(path)?) } else { None }; + let code = if let Some(SendTxSubcommands::Create { code, sig: constructor_sig, @@ -161,6 +168,7 @@ impl SendTxArgs { cast_async, confirmations, to_json, + blob_data, ) .await // Case 2: @@ -196,6 +204,7 @@ impl SendTxArgs { cast_async, confirmations, to_json, + blob_data, ) .await } @@ -216,13 +225,15 @@ async fn cast_send, T: Transport + Clone>( cast_async: bool, confs: u64, to_json: bool, + blob_data: Option>, ) -> Result<()> { let (tx, _) = - tx::build_tx(&provider, from, to, code, sig, args, tx, chain, etherscan_api_key).await?; + tx::build_tx(&provider, from, to, code, sig, args, tx, chain, etherscan_api_key, blob_data) + .await?; let cast = Cast::new(provider); - let pending_tx = cast.send(tx).await?; + let tx_hash = pending_tx.inner().tx_hash(); if cast_async { diff --git a/crates/cast/bin/tx.rs b/crates/cast/bin/tx.rs index 64b33b280..82b1d8b4d 100644 --- a/crates/cast/bin/tx.rs +++ b/crates/cast/bin/tx.rs @@ -1,3 +1,4 @@ +use alloy_consensus::{SidecarBuilder, SimpleCoder}; use alloy_json_abi::Function; use alloy_network::{AnyNetwork, TransactionBuilder}; use alloy_primitives::{Address, Bytes, U256}; @@ -52,16 +53,34 @@ pub async fn build_tx< tx: TransactionOpts, chain: impl Into, etherscan_api_key: Option, + blob_data: Option>, ) -> Result<(WithOtherFields, Option)> { let chain = chain.into(); let from = from.into().resolve(provider).await?; + let sidecar = blob_data + .map(|data| { + let mut coder = SidecarBuilder::::default(); + coder.ingest(&data); + coder.build() + }) + .transpose()?; + let mut req = WithOtherFields::::default() .with_from(from) .with_value(tx.value.unwrap_or_default()) .with_chain_id(chain.id()); + if let Some(sidecar) = sidecar { + req.set_blob_sidecar(sidecar); + req.populate_blob_hashes(); + req.set_max_fee_per_blob_gas( + // If blob_base_fee is 0, uses 1 wei as minimum. + tx.blob_gas_price.map_or(provider.get_blob_base_fee().await?.max(1), |g| g.to()), + ); + } + if let Some(to) = to { req.set_to(to.into().resolve(provider).await?); } else { diff --git a/crates/cli/src/opts/transaction.rs b/crates/cli/src/opts/transaction.rs index 81d6107bd..d424626c4 100644 --- a/crates/cli/src/opts/transaction.rs +++ b/crates/cli/src/opts/transaction.rs @@ -45,6 +45,14 @@ pub struct TransactionOpts { /// This is automatically enabled for common networks without EIP1559. #[arg(long)] pub legacy: bool, + + /// Send a EIP-4844 blob transaction. + #[arg(long, conflicts_with = "legacy")] + pub blob: bool, + + /// Gas price for EIP-4844 blob transaction. + #[arg(long, conflicts_with = "legacy", value_parser = parse_ether_value, env = "ETH_BLOB_GAS_PRICE", value_name = "BLOB_PRICE")] + pub blob_gas_price: Option, } #[cfg(test)] From 0a5b22f07ba4f2ddf525089c8ee9cdcb05e44bd9 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 21 May 2024 13:13:10 +0300 Subject: [PATCH 293/430] fix(invariant): panic when decoding logs with None value (#7956) * fix(invariant): panic when decoding logs with None value * Changes after review: code cleanup --- crates/evm/fuzz/src/strategies/state.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/evm/fuzz/src/strategies/state.rs b/crates/evm/fuzz/src/strategies/state.rs index 0bcf81bae..791ca3d9a 100644 --- a/crates/evm/fuzz/src/strategies/state.rs +++ b/crates/evm/fuzz/src/strategies/state.rs @@ -267,18 +267,18 @@ impl FuzzDictionary { /// If collected samples limit is reached then values are inserted as regular values. pub fn insert_sample_values(&mut self, sample_values: Vec, limit: u32) { for sample in sample_values { - let sample_type = sample.as_type().unwrap(); - let sample_value = sample.as_word().unwrap().into(); - - if let Some(values) = self.sample_values.get_mut(&sample_type) { - if values.len() < limit as usize { - values.insert(sample_value); + if let (Some(sample_type), Some(sample_value)) = (sample.as_type(), sample.as_word()) { + let sample_value = sample_value.into(); + if let Some(values) = self.sample_values.get_mut(&sample_type) { + if values.len() < limit as usize { + values.insert(sample_value); + } else { + // Insert as state value (will be removed at the end of the run). + self.insert_value(sample_value, true); + } } else { - // Insert as state value (will be removed at the end of the run). - self.insert_value(sample_value, true); + self.sample_values.entry(sample_type).or_default().insert(sample_value); } - } else { - self.sample_values.entry(sample_type).or_default().insert(sample_value); } } } From 28ccb97e6a9133631f9b7f21a818e0f8c5bd0b5a Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 21 May 2024 21:29:03 +0300 Subject: [PATCH 294/430] chore: update interprocess to fix a todo (#7913) chore: remove interprocess TODO --- crates/anvil/server/src/ipc.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/anvil/server/src/ipc.rs b/crates/anvil/server/src/ipc.rs index eb2d2d4fd..a1ca67af8 100644 --- a/crates/anvil/server/src/ipc.rs +++ b/crates/anvil/server/src/ipc.rs @@ -47,7 +47,6 @@ impl IpcEndpoint { let name = to_name(path.as_ref())?; let listener = ls::ListenerOptions::new().name(name).create_tokio()?; - // TODO: https://github.com/kotauskas/interprocess/issues/64 let connections = futures::stream::unfold(listener, |listener| async move { let conn = listener.accept().await; Some((conn, listener)) From 23700c9233314e06ba65be4522daf3aa89130dc0 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Tue, 21 May 2024 22:22:43 +0300 Subject: [PATCH 295/430] feat: support solc 0.8.26 (#7968) --- Cargo.lock | 8 ++++---- crates/forge/tests/cli/svm.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 440f2e3d9..d291a6f9d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7398,9 +7398,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "svm-rs" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c7a55b859b986daa02c731cd07758d84b1db852665e45c5cfa6698c41d17cb" +checksum = "b19990faaaed6298d6b21984544ad941dc88d507330012fb4a6a8d4f1833de73" dependencies = [ "const-hex", "dirs 5.0.1", @@ -7418,9 +7418,9 @@ dependencies = [ [[package]] name = "svm-rs-builds" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bb4806c96207e7cde40fc238f9a1d570f3090f850a742e1e96665440615a31" +checksum = "68be542b0720275ac352f85ab67713ae22d56bcc58d83bdebdd3eda0473a1d7f" dependencies = [ "build_const", "const-hex", diff --git a/crates/forge/tests/cli/svm.rs b/crates/forge/tests/cli/svm.rs index cbdd56f9d..8403053d8 100644 --- a/crates/forge/tests/cli/svm.rs +++ b/crates/forge/tests/cli/svm.rs @@ -11,7 +11,7 @@ use svm::Platform; /// 3. svm bumped in foundry-compilers /// 4. foundry-compilers update with any breaking changes /// 5. upgrade the `LATEST_SOLC` -const LATEST_SOLC: Version = Version::new(0, 8, 25); +const LATEST_SOLC: Version = Version::new(0, 8, 26); macro_rules! ensure_svm_releases { ($($test:ident => $platform:ident),* $(,)?) => {$( From a539f3a234af763df59f91f80d622999ab630a2e Mon Sep 17 00:00:00 2001 From: aureliusbtc <82057759+aureliusbtc@users.noreply.github.com> Date: Wed, 22 May 2024 16:44:42 +0100 Subject: [PATCH 296/430] Nit: Fix EIP 1159 typo in comment (#7963) eip-1159 -> eip-1559 --- crates/anvil/src/eth/api.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 7fd4133a6..4fd9d090d 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -1242,7 +1242,7 @@ impl EthApi { Err(BlockchainError::RpcUnimplemented) } - /// Introduced in EIP-1159 for getting information on the appropriate priority fee to use. + /// Introduced in EIP-1559 for getting information on the appropriate priority fee to use. /// /// Handler for ETH RPC call: `eth_feeHistory` pub async fn fee_history( From 44d98ea9359070017819ba44f3d11e4fedb63420 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Wed, 22 May 2024 19:32:18 +0300 Subject: [PATCH 297/430] fix(fuzz): do not exceed configured runs (#7971) * fix(fuzz): do not exceed configured runs * Add test --- crates/forge/src/lib.rs | 3 ++ crates/forge/tests/cli/test_cmd.rs | 51 +++++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/crates/forge/src/lib.rs b/crates/forge/src/lib.rs index 98dc0aa47..6a9334b16 100644 --- a/crates/forge/src/lib.rs +++ b/crates/forge/src/lib.rs @@ -149,6 +149,9 @@ impl TestOptions { failure_persistence: file_failure_persistence, cases, max_global_rejects: self.fuzz.max_test_rejects, + // Disable proptest shrink: for fuzz tests we provide single counterexample, + // for invariant tests we shrink outside proptest. + max_shrink_iters: 0, ..Default::default() }; diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 2b0784d6a..22e928a43 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -1,6 +1,7 @@ //! Contains various tests for `forge test`. -use foundry_config::Config; +use alloy_primitives::U256; +use foundry_config::{Config, FuzzConfig}; use foundry_test_utils::{ rpc, util::{OutputExt, OTHER_SOLC_VERSION, SOLC_VERSION}, @@ -544,3 +545,51 @@ contract Dummy { cmd.args(["test", "--match-path", "src/dummy.sol"]); cmd.assert_success() }); + +forgetest_init!(should_not_shrink_fuzz_failure, |prj, cmd| { + prj.wipe_contracts(); + + // deterministic test so we always have 54 runs until test fails with overflow + let config = Config { + fuzz: { FuzzConfig { runs: 256, seed: Some(U256::from(100)), ..Default::default() } }, + ..Default::default() + }; + prj.write_config(config); + + prj.add_test( + "CounterFuzz.t.sol", + r#"pragma solidity 0.8.24; +import {Test} from "forge-std/Test.sol"; + +contract Counter { + uint256 public number = 0; + + function addOne(uint256 x) external pure returns (uint256) { + return x + 100_000_000; + } +} + +contract CounterTest is Test { + Counter public counter; + + function setUp() public { + counter = new Counter(); + } + + function testAddOne(uint256 x) public view { + assertEq(counter.addOne(x), x + 100_000_000); + } +} + "#, + ) + .unwrap(); + + cmd.args(["test"]); + let (stderr, _) = cmd.unchecked_output_lossy(); + let runs = stderr.find("runs:").and_then(|start_runs| { + let runs_split = &stderr[start_runs + 6..]; + runs_split.find(',').map(|end_runs| &runs_split[..end_runs]) + }); + // make sure there are only 54 runs (with proptest shrinking same test results in 292 runs) + assert_eq!(runs.unwrap().parse::().unwrap(), 54); +}); From 78aef64c735dcfe6a6a6f68c9ba92a5bc9d65d1a Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 22 May 2024 22:06:48 +0300 Subject: [PATCH 298/430] fix: apply `--no-match-test` when invoking compiler (#7941) * fix: apply --no-match-test when invoking compiler * fix filter * fix * fix --- crates/forge/bin/cmd/test/filter.rs | 15 +++------------ crates/forge/bin/cmd/test/mod.rs | 6 ++++-- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/crates/forge/bin/cmd/test/filter.rs b/crates/forge/bin/cmd/test/filter.rs index eb8baea46..c30b3434a 100644 --- a/crates/forge/bin/cmd/test/filter.rs +++ b/crates/forge/bin/cmd/test/filter.rs @@ -1,6 +1,5 @@ use clap::Parser; use forge::TestFilter; -use foundry_cli::utils::FoundryPathExt; use foundry_common::glob::GlobMatcher; use foundry_compilers::{FileFilter, ProjectPathsConfig}; use foundry_config::Config; @@ -93,16 +92,9 @@ impl fmt::Debug for FilterArgs { impl FileFilter for FilterArgs { /// Returns true if the file regex pattern match the `file` /// - /// If no file regex is set this returns true if the file ends with `.t.sol`, see - /// [`FoundryPathExt::is_sol_test()`]. + /// If no file regex is set this returns true by default fn is_match(&self, file: &Path) -> bool { - if let Some(glob) = &self.path_pattern { - return glob.is_match(file) - } - if let Some(glob) = &self.path_pattern_inverse { - return !glob.is_match(file) - } - file.is_sol_test() + self.matches_path(file) } } @@ -194,8 +186,7 @@ impl ProjectPathsAwareFilter { impl FileFilter for ProjectPathsAwareFilter { /// Returns true if the file regex pattern match the `file` /// - /// If no file regex is set this returns true if the file ends with `.t.sol`, see - /// [FoundryPathExr::is_sol_test()] + /// If no file regex is set this returns true by default fn is_match(&self, mut file: &Path) -> bool { file = file.strip_prefix(&self.paths.root).unwrap_or(file); self.args_filter.is_match(file) diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 720d13284..1bb70aa7e 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -20,7 +20,8 @@ use foundry_common::{ shell, }; use foundry_compilers::{ - artifacts::output_selection::OutputSelection, utils::source_files_iter, SOLC_EXTENSIONS, + artifacts::output_selection::OutputSelection, utils::source_files_iter, SolcSparseFileFilter, + SOLC_EXTENSIONS, }; use foundry_config::{ figment, @@ -153,7 +154,8 @@ impl TestArgs { let mut project = config.create_project(true, true)?; project.settings.output_selection = OutputSelection::common_output_selection(["abi".to_string()]); - let output = project.compile()?; + + let output = project.compile_sparse(Box::new(SolcSparseFileFilter::new(filter.clone())))?; if output.has_compiler_errors() { println!("{}", output); From 3e9385b65d5ff502095c7896aab6042127548c34 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 22 May 2024 22:28:07 +0300 Subject: [PATCH 299/430] feat: smarter verification (#7937) * fix: smarter verification * version build regex * rm println * fix tests * clippy * review fixes * fmt * ref -> & --- Cargo.lock | 1 + crates/cheatcodes/src/fs.rs | 4 +- crates/common/src/contracts.rs | 144 +++++++++-- crates/evm/traces/src/identifier/local.rs | 4 +- crates/forge/bin/cmd/create.rs | 8 +- crates/script/src/execute.rs | 17 +- crates/script/src/lib.rs | 4 +- crates/script/src/transaction.rs | 2 +- crates/script/src/verify.rs | 4 +- crates/verify/Cargo.toml | 1 + crates/verify/src/etherscan/flatten.rs | 18 +- crates/verify/src/etherscan/mod.rs | 257 ++++--------------- crates/verify/src/etherscan/standard_json.rs | 32 ++- crates/verify/src/lib.rs | 101 +++++++- crates/verify/src/provider.rs | 95 ++++++- crates/verify/src/sourcify.rs | 73 ++---- 16 files changed, 440 insertions(+), 325 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d291a6f9d..5472893a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3271,6 +3271,7 @@ dependencies = [ "foundry-evm", "foundry-test-utils", "futures", + "itertools 0.12.1", "once_cell", "regex", "reqwest", diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index 423ed2767..33e29475c 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -356,9 +356,9 @@ fn get_artifact_code(state: &Cheatcodes, path: &str, deployed: bool) -> Result +const CALL_PROTECTION_BYTECODE_PREFIX: [u8; 21] = + hex!("730000000000000000000000000000000000000000"); + /// Container for commonly used contract data. #[derive(Debug, Clone)] pub struct ContractData { @@ -20,9 +31,21 @@ pub struct ContractData { /// Contract ABI. pub abi: JsonAbi, /// Contract creation code. - pub bytecode: Option, + pub bytecode: Option, /// Contract runtime code. - pub deployed_bytecode: Option, + pub deployed_bytecode: Option, +} + +impl ContractData { + /// Returns reference to bytes of contract creation code, if present. + pub fn bytecode(&self) -> Option<&Bytes> { + self.bytecode.as_ref()?.bytes().filter(|b| !b.is_empty()) + } + + /// Returns reference to bytes of contract deployed code, if present. + pub fn deployed_bytecode(&self) -> Option<&Bytes> { + self.deployed_bytecode.as_ref()?.bytes().filter(|b| !b.is_empty()) + } } type ArtifactWithContractRef<'a> = (&'a ArtifactId, &'a ContractData); @@ -42,18 +65,10 @@ impl ContractsByArtifact { .into_iter() .filter_map(|(id, artifact)| { let name = id.name.clone(); - let bytecode = artifact.bytecode.and_then(|b| b.into_bytes())?; - let deployed_bytecode = - artifact.deployed_bytecode.and_then(|b| b.into_bytes())?; - - // Exclude artifacts with present but empty bytecode. Such artifacts are usually - // interfaces and abstract contracts. - let bytecode = (bytecode.len() > 0).then_some(bytecode); - let deployed_bytecode = - (deployed_bytecode.len() > 0).then_some(deployed_bytecode); - let abi = artifact.abi?; - - Some((id, ContractData { name, abi, bytecode, deployed_bytecode })) + + let CompactContractBytecode { abi, bytecode, deployed_bytecode } = artifact; + + Some((id, ContractData { name, abi: abi?, bytecode, deployed_bytecode })) }) .collect(), ) @@ -62,7 +77,7 @@ impl ContractsByArtifact { /// Finds a contract which has a similar bytecode as `code`. pub fn find_by_creation_code(&self, code: &[u8]) -> Option { self.iter().find(|(_, contract)| { - if let Some(bytecode) = &contract.bytecode { + if let Some(bytecode) = contract.bytecode() { bytecode_diff_score(bytecode.as_ref(), code) <= 0.1 } else { false @@ -73,7 +88,7 @@ impl ContractsByArtifact { /// Finds a contract which has a similar deployed bytecode as `code`. pub fn find_by_deployed_code(&self, code: &[u8]) -> Option { self.iter().find(|(_, contract)| { - if let Some(deployed_bytecode) = &contract.deployed_bytecode { + if let Some(deployed_bytecode) = contract.deployed_bytecode() { bytecode_diff_score(deployed_bytecode.as_ref(), code) <= 0.1 } else { false @@ -81,6 +96,99 @@ impl ContractsByArtifact { }) } + /// Finds a contract which deployed bytecode exactly matches the given code. Accounts for link + /// references and immutables. + pub fn find_by_deployed_code_exact(&self, code: &[u8]) -> Option { + self.iter().find(|(_, contract)| { + let Some(deployed_bytecode) = &contract.deployed_bytecode else { + return false; + }; + let Some(deployed_code) = &deployed_bytecode.bytecode else { + return false; + }; + + let len = match deployed_code.object { + BytecodeObject::Bytecode(ref bytes) => bytes.len(), + BytecodeObject::Unlinked(ref bytes) => bytes.len() / 2, + }; + + if len != code.len() { + return false; + } + + // Collect ignored offsets by chaining link and immutable references. + let mut ignored = deployed_bytecode + .immutable_references + .values() + .chain(deployed_code.link_references.values().flat_map(|v| v.values())) + .flatten() + .cloned() + .collect::>(); + + // For libraries solidity adds a call protection prefix to the bytecode. We need to + // ignore it as it includes library address determined at runtime. + // See https://docs.soliditylang.org/en/latest/contracts.html#call-protection-for-libraries and + // https://github.com/NomicFoundation/hardhat/blob/af7807cf38842a4f56e7f4b966b806e39631568a/packages/hardhat-verify/src/internal/solc/bytecode.ts#L172 + let has_call_protection = match deployed_code.object { + BytecodeObject::Bytecode(ref bytes) => { + bytes.starts_with(&CALL_PROTECTION_BYTECODE_PREFIX) + } + BytecodeObject::Unlinked(ref bytes) => { + if let Ok(bytes) = + Bytes::from_str(&bytes[..CALL_PROTECTION_BYTECODE_PREFIX.len() * 2]) + { + bytes.starts_with(&CALL_PROTECTION_BYTECODE_PREFIX) + } else { + false + } + } + }; + + if has_call_protection { + ignored.push(Offsets { start: 1, length: 20 }); + } + + ignored.sort_by_key(|o| o.start); + + let mut left = 0; + for offset in ignored { + let right = offset.start as usize; + + let matched = match deployed_code.object { + BytecodeObject::Bytecode(ref bytes) => bytes[left..right] == code[left..right], + BytecodeObject::Unlinked(ref bytes) => { + if let Ok(bytes) = Bytes::from_str(&bytes[left * 2..right * 2]) { + bytes == code[left..right] + } else { + false + } + } + }; + + if !matched { + return false; + } + + left = right + offset.length as usize; + } + + if left < code.len() { + match deployed_code.object { + BytecodeObject::Bytecode(ref bytes) => bytes[left..] == code[left..], + BytecodeObject::Unlinked(ref bytes) => { + if let Ok(bytes) = Bytes::from_str(&bytes[left * 2..]) { + bytes == code[left..] + } else { + false + } + } + } + } else { + true + } + }) + } + /// Finds a contract which has the same contract name or identifier as `id`. If more than one is /// found, return error. pub fn find_by_name_or_identifier(&self, id: &str) -> Result> { diff --git a/crates/evm/traces/src/identifier/local.rs b/crates/evm/traces/src/identifier/local.rs index 129656b95..e82d73378 100644 --- a/crates/evm/traces/src/identifier/local.rs +++ b/crates/evm/traces/src/identifier/local.rs @@ -19,7 +19,7 @@ impl<'a> LocalTraceIdentifier<'a> { pub fn new(known_contracts: &'a ContractsByArtifact) -> Self { let mut ordered_ids = known_contracts .iter() - .filter_map(|(id, contract)| Some((id, contract.deployed_bytecode.as_ref()?))) + .filter_map(|(id, contract)| Some((id, contract.deployed_bytecode()?))) .map(|(id, bytecode)| (id, bytecode.len())) .collect::>(); ordered_ids.sort_by_key(|(_, len)| *len); @@ -41,7 +41,7 @@ impl<'a> LocalTraceIdentifier<'a> { let mut check = |id| { let contract = self.known_contracts.get(id)?; - if let Some(deployed_bytecode) = &contract.deployed_bytecode { + if let Some(deployed_bytecode) = contract.deployed_bytecode() { let score = bytecode_diff_score(deployed_bytecode, code); if score == 0.0 { trace!(target: "evm::traces", "found exact match"); diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index 460dbdea7..71a1cffd7 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -169,7 +169,7 @@ impl CreateArgs { // since we don't know the address yet. let mut verify = forge_verify::VerifyArgs { address: Default::default(), - contract: self.contract.clone(), + contract: Some(self.contract.clone()), compiler_version: None, constructor_args, constructor_args_path: None, @@ -199,7 +199,9 @@ impl CreateArgs { verify.etherscan.key = config.get_etherscan_config_with_chain(Some(chain.into()))?.map(|c| c.key); - verify.verification_provider()?.preflight_check(verify).await?; + let context = verify.resolve_context().await?; + + verify.verification_provider()?.preflight_check(verify, context).await?; Ok(()) } @@ -318,7 +320,7 @@ impl CreateArgs { if self.opts.compiler.optimize { self.opts.compiler.optimizer_runs } else { None }; let verify = forge_verify::VerifyArgs { address, - contract: self.contract, + contract: Some(self.contract), compiler_version: None, constructor_args, constructor_args_path: None, diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index a0c8397ca..afba19f22 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -16,7 +16,7 @@ use foundry_cli::utils::{ensure_clean_constructor, needs_setup}; use foundry_common::{ fmt::{format_token, format_token_raw}, provider::get_http_provider, - shell, ContractData, ContractsByArtifact, + shell, ContractsByArtifact, }; use foundry_config::{Config, NamedChain}; use foundry_debugger::Debugger; @@ -61,20 +61,25 @@ impl LinkedState { pub async fn prepare_execution(self) -> Result { let Self { args, script_config, script_wallets, build_data } = self; - let ContractData { abi, bytecode, .. } = build_data.get_target_contract()?; + let target_contract = build_data.get_target_contract()?; - let bytecode = bytecode.ok_or_eyre("target contract has no bytecode")?; + let bytecode = target_contract.bytecode().ok_or_eyre("target contract has no bytecode")?; - let (func, calldata) = args.get_method_and_calldata(&abi)?; + let (func, calldata) = args.get_method_and_calldata(&target_contract.abi)?; - ensure_clean_constructor(&abi)?; + ensure_clean_constructor(&target_contract.abi)?; Ok(PreExecutionState { args, script_config, script_wallets, build_data, - execution_data: ExecutionData { func, calldata, bytecode, abi }, + execution_data: ExecutionData { + func, + calldata, + bytecode: bytecode.clone(), + abi: target_contract.abi, + }, }) } } diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 7b84802b1..88ab0782a 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -361,8 +361,8 @@ impl ScriptArgs { // From artifacts for (artifact, contract) in known_contracts.iter() { - let Some(bytecode) = &contract.bytecode else { continue }; - let Some(deployed_bytecode) = &contract.deployed_bytecode else { continue }; + let Some(bytecode) = contract.bytecode() else { continue }; + let Some(deployed_bytecode) = contract.deployed_bytecode() else { continue }; bytecodes.push((artifact.name.clone(), bytecode, deployed_bytecode)); } diff --git a/crates/script/src/transaction.rs b/crates/script/src/transaction.rs index c46301f9f..f5f9399d4 100644 --- a/crates/script/src/transaction.rs +++ b/crates/script/src/transaction.rs @@ -131,7 +131,7 @@ impl TransactionWithMetadata { let Some(data) = self.transaction.input.input() else { return Ok(()) }; let Some(info) = info else { return Ok(()) }; - let Some(bytecode) = info.bytecode.as_ref() else { return Ok(()) }; + let Some(bytecode) = info.bytecode() else { return Ok(()) }; // `create2` transactions are prefixed by a 32 byte salt. let creation_code = if is_create2 { diff --git a/crates/script/src/verify.rs b/crates/script/src/verify.rs index f6545c301..6437e9a5d 100644 --- a/crates/script/src/verify.rs +++ b/crates/script/src/verify.rs @@ -106,7 +106,7 @@ impl VerifyBundle { libraries: &[String], ) -> Option { for (artifact, contract) in self.known_contracts.iter() { - let Some(bytecode) = contract.bytecode.as_ref() else { continue }; + let Some(bytecode) = contract.bytecode() else { continue }; // If it's a CREATE2, the tx.data comes with a 32-byte salt in the beginning // of the transaction if data.split_at(create2_offset).1.starts_with(bytecode) { @@ -130,7 +130,7 @@ impl VerifyBundle { let verify = VerifyArgs { address: contract_address, - contract, + contract: Some(contract), compiler_version: Some(version.to_string()), constructor_args: Some(hex::encode(constructor_args)), constructor_args_path: None, diff --git a/crates/verify/Cargo.toml b/crates/verify/Cargo.toml index 62979edbc..54d886f7c 100644 --- a/crates/verify/Cargo.toml +++ b/crates/verify/Cargo.toml @@ -36,6 +36,7 @@ semver = "1" regex = { version = "1", default-features = false } once_cell = "1" yansi.workspace = true +itertools.workspace = true [dev-dependencies] tokio = { workspace = true, features = ["macros"] } diff --git a/crates/verify/src/etherscan/flatten.rs b/crates/verify/src/etherscan/flatten.rs index ee0f95d83..cebdfbd22 100644 --- a/crates/verify/src/etherscan/flatten.rs +++ b/crates/verify/src/etherscan/flatten.rs @@ -1,10 +1,11 @@ use super::{EtherscanSourceProvider, VerifyArgs}; +use crate::provider::VerificationContext; use eyre::{Context, Result}; use foundry_block_explorers::verify::CodeFormat; use foundry_compilers::{ artifacts::{BytecodeHash, Source}, compilers::{solc::SolcVersionManager, Compiler, CompilerVersionManager}, - AggregatedCompilerOutput, Project, SolcInput, + AggregatedCompilerOutput, SolcInput, }; use semver::{BuildMetadata, Version}; use std::{collections::BTreeMap, path::Path}; @@ -15,11 +16,9 @@ impl EtherscanSourceProvider for EtherscanFlattenedSource { fn source( &self, args: &VerifyArgs, - project: &Project, - target: &Path, - version: &Version, + context: &VerificationContext, ) -> Result<(String, String, CodeFormat)> { - let metadata = project.settings.metadata.as_ref(); + let metadata = context.project.settings.metadata.as_ref(); let bch = metadata.and_then(|m| m.bytecode_hash).unwrap_or_default(); eyre::ensure!( @@ -28,11 +27,13 @@ impl EtherscanSourceProvider for EtherscanFlattenedSource { bch, ); - let source = project.flatten(target).wrap_err("Failed to flatten contract")?; + let source = + context.project.flatten(&context.target_path).wrap_err("Failed to flatten contract")?; if !args.force { // solc dry run of flattened code - self.check_flattened(source.clone(), version, target).map_err(|err| { + self.check_flattened(source.clone(), &context.compiler_version, &context.target_path) + .map_err(|err| { eyre::eyre!( "Failed to compile the flattened code locally: `{}`\ To skip this solc dry, have a look at the `--force` flag of this command.", @@ -41,8 +42,7 @@ impl EtherscanSourceProvider for EtherscanFlattenedSource { })?; } - let name = args.contract.name.clone(); - Ok((source, name, CodeFormat::SingleFile)) + Ok((source, context.target_name.clone(), CodeFormat::SingleFile)) } } diff --git a/crates/verify/src/etherscan/mod.rs b/crates/verify/src/etherscan/mod.rs index 8ebe0dc46..b0698d58b 100644 --- a/crates/verify/src/etherscan/mod.rs +++ b/crates/verify/src/etherscan/mod.rs @@ -1,5 +1,5 @@ use super::{provider::VerificationProvider, VerifyArgs, VerifyCheckArgs}; -use crate::retry::RETRY_CHECK_ON_VERIFY; +use crate::{provider::VerificationContext, retry::RETRY_CHECK_ON_VERIFY}; use alloy_json_abi::Function; use alloy_provider::Provider; use eyre::{eyre, Context, OptionExt, Result}; @@ -9,25 +9,16 @@ use foundry_block_explorers::{ verify::{CodeFormat, VerifyContract}, Client, }; -use foundry_cli::utils::{self, get_cached_entry_by_name, read_constructor_args_file, LoadConfig}; -use foundry_common::{abi::encode_function_args, retry::Retry}; -use foundry_compilers::{ - artifacts::{BytecodeObject, CompactContract}, - cache::CacheEntry, - info::ContractInfo, - Artifact, Project, Solc, -}; -use foundry_config::{Chain, Config, SolcReq}; +use foundry_cli::utils::{self, read_constructor_args_file, LoadConfig}; +use foundry_common::{abi::encode_function_args, retry::Retry, shell}; +use foundry_compilers::{artifacts::BytecodeObject, Artifact}; +use foundry_config::{Chain, Config}; use foundry_evm::constants::DEFAULT_CREATE2_DEPLOYER; use futures::FutureExt; use once_cell::sync::Lazy; use regex::Regex; use semver::{BuildMetadata, Version}; -use std::{ - collections::HashSet, - fmt::Debug, - path::{Path, PathBuf}, -}; +use std::fmt::Debug; mod flatten; mod standard_json; @@ -37,10 +28,7 @@ pub static RE_BUILD_COMMIT: Lazy = #[derive(Clone, Debug, Default)] #[non_exhaustive] -pub struct EtherscanVerificationProvider { - /// Memoized cached entry of the target contract - cached_entry: Option<(PathBuf, CacheEntry, CompactContract)>, -} +pub struct EtherscanVerificationProvider; /// The contract source provider for [EtherscanVerificationProvider] /// @@ -49,21 +37,23 @@ trait EtherscanSourceProvider: Send + Sync + Debug { fn source( &self, args: &VerifyArgs, - project: &Project, - target: &Path, - version: &Version, + context: &VerificationContext, ) -> Result<(String, String, CodeFormat)>; } #[async_trait::async_trait] impl VerificationProvider for EtherscanVerificationProvider { - async fn preflight_check(&mut self, args: VerifyArgs) -> Result<()> { - let _ = self.prepare_request(&args).await?; + async fn preflight_check( + &mut self, + args: VerifyArgs, + context: VerificationContext, + ) -> Result<()> { + let _ = self.prepare_request(&args, &context).await?; Ok(()) } - async fn verify(&mut self, args: VerifyArgs) -> Result<()> { - let (etherscan, verify_args) = self.prepare_request(&args).await?; + async fn verify(&mut self, args: VerifyArgs, context: VerificationContext) -> Result<()> { + let (etherscan, verify_args) = self.prepare_request(&args, &context).await?; if !args.skip_is_verified_check && self.is_contract_verified(ðerscan, &verify_args).await? @@ -214,38 +204,12 @@ impl EtherscanVerificationProvider { } } - /// Return the memoized cache entry for the target contract. - /// Read the artifact from cache on first access. - fn cache_entry( - &mut self, - project: &Project, - contract: &ContractInfo, - ) -> Result<&(PathBuf, CacheEntry, CompactContract)> { - if let Some(ref entry) = self.cached_entry { - return Ok(entry) - } - - let cache = project.read_cache_file()?; - let (path, entry) = if let Some(path) = contract.path.as_ref() { - let path = project.root().join(path); - ( - path.clone(), - cache - .entry(&path) - .ok_or_else(|| { - eyre::eyre!(format!("Cache entry not found for {}", path.display())) - })? - .to_owned(), - ) - } else { - get_cached_entry_by_name(&cache, &contract.name)? - }; - let contract: CompactContract = cache.read_artifact(path.clone(), &contract.name)?; - Ok(self.cached_entry.insert((path, entry, contract))) - } - /// Configures the API request to the etherscan API using the given [`VerifyArgs`]. - async fn prepare_request(&mut self, args: &VerifyArgs) -> Result<(Client, VerifyContract)> { + async fn prepare_request( + &mut self, + args: &VerifyArgs, + context: &VerificationContext, + ) -> Result<(Client, VerifyContract)> { let config = args.try_load_config_emit_warnings()?; let etherscan = self.client( args.etherscan.chain.unwrap_or_default(), @@ -253,7 +217,7 @@ impl EtherscanVerificationProvider { args.etherscan.key().as_deref(), &config, )?; - let verify_args = self.create_verify_request(args, Some(config)).await?; + let verify_args = self.create_verify_request(args, context).await?; Ok((etherscan, verify_args)) } @@ -325,22 +289,20 @@ impl EtherscanVerificationProvider { pub async fn create_verify_request( &mut self, args: &VerifyArgs, - config: Option, + context: &VerificationContext, ) -> Result { - let mut config = - if let Some(config) = config { config } else { args.try_load_config_emit_warnings()? }; - - config.libraries.extend(args.libraries.clone()); - - let project = config.project()?; - - let contract_path = self.contract_path(args, &project)?; - let compiler_version = self.compiler_version(args, &config, &project)?; let (source, contract_name, code_format) = - self.source_provider(args).source(args, &project, &contract_path, &compiler_version)?; + self.source_provider(args).source(args, context)?; + + let mut compiler_version = context.compiler_version.clone(); + compiler_version.build = match RE_BUILD_COMMIT.captures(compiler_version.build.as_str()) { + Some(cap) => BuildMetadata::new(cap.name("commit").unwrap().as_str())?, + _ => BuildMetadata::EMPTY, + }; - let compiler_version = format!("v{}", ensure_solc_build_metadata(compiler_version).await?); - let constructor_args = self.constructor_args(args, &project, &config).await?; + let compiler_version = + format!("v{}", ensure_solc_build_metadata(context.compiler_version.clone()).await?); + let constructor_args = self.constructor_args(args, context).await?; let mut verify_args = VerifyContract::new(args.address, contract_name, source, compiler_version) .constructor_arguments(constructor_args) @@ -357,8 +319,8 @@ impl EtherscanVerificationProvider { if code_format == CodeFormat::SingleFile { verify_args = if let Some(optimizations) = args.num_of_optimizations { verify_args.optimized().runs(optimizations as u32) - } else if config.optimizer { - verify_args.optimized().runs(config.optimizer_runs.try_into()?) + } else if context.config.optimizer { + verify_args.optimized().runs(context.config.optimizer_runs.try_into()?) } else { verify_args.not_optimized() }; @@ -367,94 +329,16 @@ impl EtherscanVerificationProvider { Ok(verify_args) } - /// Get the target contract path. If it wasn't provided, attempt a lookup - /// in cache. Validate the path indeed exists on disk. - fn contract_path(&mut self, args: &VerifyArgs, project: &Project) -> Result { - let path = if let Some(path) = args.contract.path.as_ref() { - project.root().join(path) - } else { - let (path, _, _) = self.cache_entry(project, &args.contract).wrap_err( - "If cache is disabled, contract info must be provided in the format :", - )?; - path.to_owned() - }; - - // check that the provided contract is part of the source dir - if !path.exists() { - eyre::bail!("Contract {:?} does not exist.", path); - } - - Ok(path) - } - - /// Parse the compiler version. - /// The priority desc: - /// 1. Through CLI arg `--compiler-version` - /// 2. `solc` defined in foundry.toml - /// 3. The version contract was last compiled with. - fn compiler_version( - &mut self, - args: &VerifyArgs, - config: &Config, - project: &Project, - ) -> Result { - if let Some(ref version) = args.compiler_version { - return Ok(version.trim_start_matches('v').parse()?) - } - - if let Some(ref solc) = config.solc { - match solc { - SolcReq::Version(version) => return Ok(version.to_owned()), - SolcReq::Local(solc) => { - if solc.is_file() { - return Ok(Solc::new(solc).map(|solc| solc.version)?) - } - } - } - } - - let (_, entry, _) = self.cache_entry(project, &args.contract).wrap_err( - "If cache is disabled, compiler version must be either provided with `--compiler-version` option or set in foundry.toml" - )?; - let artifacts = entry.artifacts_versions().collect::>(); - - if artifacts.is_empty() { - eyre::bail!("No matching artifact found for {}", args.contract.name); - } - - // ensure we have a single version - let unique_versions = artifacts.iter().map(|a| a.0.to_string()).collect::>(); - if unique_versions.len() > 1 { - let versions = unique_versions.into_iter().collect::>(); - warn!("Ambiguous compiler versions found in cache: {}", versions.join(", ")); - eyre::bail!("Compiler version has to be set in `foundry.toml`. If the project was not deployed with foundry, specify the version through `--compiler-version` flag.") - } - - // we have a unique version - let mut version = artifacts[0].0.clone(); - version.build = match RE_BUILD_COMMIT.captures(version.build.as_str()) { - Some(cap) => BuildMetadata::new(cap.name("commit").unwrap().as_str())?, - _ => BuildMetadata::EMPTY, - }; - - Ok(version) - } - /// Return the optional encoded constructor arguments. If the path to /// constructor arguments was provided, read them and encode. Otherwise, /// return whatever was set in the [VerifyArgs] args. async fn constructor_args( &mut self, args: &VerifyArgs, - project: &Project, - config: &Config, + context: &VerificationContext, ) -> Result> { if let Some(ref constructor_args_path) = args.constructor_args_path { - let (_, _, contract) = self.cache_entry(project, &args.contract).wrap_err( - "Cache must be enabled in order to use the `--constructor-args-path` option", - )?; - let abi = - contract.abi.as_ref().ok_or_else(|| eyre!("Can't find ABI in cached artifact."))?; + let abi = context.get_target_abi()?; let constructor = abi .constructor() .ok_or_else(|| eyre!("Can't retrieve constructor info from artifact ABI."))?; @@ -473,7 +357,7 @@ impl EtherscanVerificationProvider { return Ok(Some(encoded_args[8..].into())) } if args.guess_constructor_args { - return Ok(Some(self.guess_constructor_args(args, project, config).await?)) + return Ok(Some(self.guess_constructor_args(args, context).await?)) } Ok(args.constructor_args.clone()) @@ -486,15 +370,14 @@ impl EtherscanVerificationProvider { async fn guess_constructor_args( &mut self, args: &VerifyArgs, - project: &Project, - config: &Config, + context: &VerificationContext, ) -> Result { - let provider = utils::get_provider(config)?; + let provider = utils::get_provider(&context.config)?; let client = self.client( args.etherscan.chain.unwrap_or_default(), args.verifier.verifier_url.as_deref(), args.etherscan.key.as_deref(), - config, + &context.config, )?; let creation_data = client.contract_creation_data(args.address).await?; @@ -507,20 +390,17 @@ impl EtherscanVerificationProvider { .await? .ok_or_eyre("Couldn't fetch transaction receipt from RPC")?; - let maybe_creation_code: &[u8]; - - if receipt.contract_address == Some(args.address) { - maybe_creation_code = &transaction.input; + let maybe_creation_code = if receipt.contract_address == Some(args.address) { + &transaction.input } else if transaction.to == Some(DEFAULT_CREATE2_DEPLOYER) { - maybe_creation_code = &transaction.input[32..]; + &transaction.input[32..] } else { eyre::bail!("Fetching of constructor arguments is not supported for contracts created by contracts") - } + }; - let contract_path = self.contract_path(args, project)?.to_string_lossy().into_owned(); - let output = project.compile()?; + let output = context.project.compile_file(&context.target_path)?; let artifact = output - .find(contract_path, &args.contract.name) + .find(context.target_path.to_string_lossy(), &context.target_name) .ok_or_eyre("Contract artifact wasn't found locally")?; let bytecode = artifact .get_bytecode_object() @@ -535,7 +415,9 @@ impl EtherscanVerificationProvider { if maybe_creation_code.starts_with(bytecode) { let constructor_args = &maybe_creation_code[bytecode.len()..]; - Ok(hex::encode(constructor_args)) + let constructor_args = hex::encode(constructor_args); + shell::println(format!("Identified constructor arguments: {constructor_args}"))?; + Ok(constructor_args) } else { eyre::bail!("Local bytecode doesn't match on-chain bytecode") } @@ -656,8 +538,6 @@ mod tests { let contract_path = format!("{src_dir}/Counter.sol"); fs::write(root.join(&contract_path), "").unwrap(); - let mut etherscan = EtherscanVerificationProvider::default(); - // No compiler argument let args = VerifyArgs::parse_from([ "foundry-cli", @@ -666,44 +546,12 @@ mod tests { "--root", root_path, ]); - - let result = etherscan.preflight_check(args).await; + let result = args.resolve_context().await; assert!(result.is_err()); assert_eq!( result.unwrap_err().to_string(), "If cache is disabled, compiler version must be either provided with `--compiler-version` option or set in foundry.toml" ); - - // No contract path - let args = - VerifyArgs::parse_from(["foundry-cli", address, contract_name, "--root", root_path]); - - let result = etherscan.preflight_check(args).await; - assert!(result.is_err()); - assert_eq!( - result.unwrap_err().to_string(), - "If cache is disabled, contract info must be provided in the format :" - ); - - // Constructor args path - let args = VerifyArgs::parse_from([ - "foundry-cli", - address, - &format!("{contract_path}:{contract_name}"), - "--constructor-args-path", - ".", - "--compiler-version", - "0.8.15", - "--root", - root_path, - ]); - - let result = etherscan.preflight_check(args).await; - assert!(result.is_err()); - assert_eq!( - result.unwrap_err().to_string(), - "Cache must be enabled in order to use the `--constructor-args-path` option", - ); } forgetest_async!(respects_path_for_duplicate, |prj, cmd| { @@ -719,8 +567,9 @@ mod tests { "--root", &prj.root().to_string_lossy(), ]); + let context = args.resolve_context().await.unwrap(); let mut etherscan = EtherscanVerificationProvider::default(); - etherscan.preflight_check(args).await.unwrap(); + etherscan.preflight_check(args, context).await.unwrap(); }); } diff --git a/crates/verify/src/etherscan/standard_json.rs b/crates/verify/src/etherscan/standard_json.rs index db4854355..944ee6b10 100644 --- a/crates/verify/src/etherscan/standard_json.rs +++ b/crates/verify/src/etherscan/standard_json.rs @@ -1,35 +1,35 @@ use super::{EtherscanSourceProvider, VerifyArgs}; +use crate::provider::VerificationContext; use eyre::{Context, Result}; use foundry_block_explorers::verify::CodeFormat; -use foundry_compilers::{artifacts::StandardJsonCompilerInput, Project}; -use semver::Version; -use std::path::Path; +use foundry_compilers::artifacts::StandardJsonCompilerInput; #[derive(Debug)] pub struct EtherscanStandardJsonSource; impl EtherscanSourceProvider for EtherscanStandardJsonSource { fn source( &self, - args: &VerifyArgs, - project: &Project, - target: &Path, - version: &Version, + _args: &VerifyArgs, + context: &VerificationContext, ) -> Result<(String, String, CodeFormat)> { - let mut input: StandardJsonCompilerInput = project - .standard_json_input(target) + let mut input: StandardJsonCompilerInput = context + .project + .standard_json_input(&context.target_path) .wrap_err("Failed to get standard json input")? - .normalize_evm_version(version); + .normalize_evm_version(&context.compiler_version); input.settings.libraries.libs = input .settings .libraries .libs .into_iter() - .map(|(f, libs)| (f.strip_prefix(project.root()).unwrap_or(&f).to_path_buf(), libs)) + .map(|(f, libs)| { + (f.strip_prefix(context.project.root()).unwrap_or(&f).to_path_buf(), libs) + }) .collect(); // remove all incompatible settings - input.settings.sanitize(version); + input.settings.sanitize(&context.compiler_version); let source = serde_json::to_string(&input).wrap_err("Failed to parse standard json input")?; @@ -38,8 +38,12 @@ impl EtherscanSourceProvider for EtherscanStandardJsonSource { let name = format!( "{}:{}", - target.strip_prefix(project.root()).unwrap_or(target).display(), - args.contract.name.clone() + context + .target_path + .strip_prefix(context.project.root()) + .unwrap_or(context.target_path.as_path()) + .display(), + context.target_name.clone() ); Ok((source, name, CodeFormat::StandardJsonInput)) } diff --git a/crates/verify/src/lib.rs b/crates/verify/src/lib.rs index ee6dd13f6..565579118 100644 --- a/crates/verify/src/lib.rs +++ b/crates/verify/src/lib.rs @@ -4,17 +4,20 @@ extern crate tracing; use alloy_primitives::Address; +use alloy_provider::Provider; use clap::{Parser, ValueHint}; use eyre::Result; use foundry_cli::{ opts::{EtherscanOpts, RpcOpts}, - utils, - utils::LoadConfig, + utils::{self, LoadConfig}, }; -use foundry_compilers::{info::ContractInfo, EvmVersion}; -use foundry_config::{figment, impl_figment_convert, impl_figment_convert_cast, Config}; +use foundry_common::{compile::ProjectCompiler, ContractsByArtifact}; +use foundry_compilers::{info::ContractInfo, EvmVersion, Solc}; +use foundry_config::{figment, impl_figment_convert, impl_figment_convert_cast, Config, SolcReq}; +use itertools::Itertools; use provider::VerificationProviderType; use reqwest::Url; +use revm_primitives::HashSet; use std::path::PathBuf; mod etherscan; @@ -29,6 +32,8 @@ mod sourcify; pub use retry::RetryArgs; +use crate::provider::VerificationContext; + /// Verification provider arguments #[derive(Clone, Debug, Parser)] pub struct VerifierArgs { @@ -54,7 +59,7 @@ pub struct VerifyArgs { pub address: Address, /// The contract identifier in the form `:`. - pub contract: ContractInfo, + pub contract: Option, /// The ABI-encoded constructor arguments. #[arg( @@ -192,19 +197,22 @@ impl VerifyArgs { None => config.chain.unwrap_or_default(), }; + let context = self.resolve_context().await?; + self.etherscan.chain = Some(chain); self.etherscan.key = config.get_etherscan_config_with_chain(Some(chain))?.map(|c| c.key); if self.show_standard_json_input { - let args = - EtherscanVerificationProvider::default().create_verify_request(&self, None).await?; + let args = EtherscanVerificationProvider::default() + .create_verify_request(&self, &context) + .await?; println!("{}", args.source); return Ok(()) } let verifier_url = self.verifier.verifier_url.clone(); println!("Start verifying contract `{}` deployed on {chain}", self.address); - self.verifier.verifier.client(&self.etherscan.key())?.verify(self).await.map_err(|err| { + 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) { Ok(url) => { @@ -230,6 +238,83 @@ impl VerifyArgs { pub fn verification_provider(&self) -> Result> { self.verifier.verifier.client(&self.etherscan.key()) } + + /// Resolves [VerificationContext] object either from entered contract name or by trying to + /// match bytecode located at given address. + pub async fn resolve_context(&self) -> Result { + let mut config = self.load_config_emit_warnings(); + config.libraries.extend(self.libraries.clone()); + + let project = config.project()?; + + if let Some(ref contract) = self.contract { + let contract_path = if let Some(ref path) = contract.path { + project.root().join(PathBuf::from(path)) + } else { + project.find_contract_path(&contract.name)? + }; + + let version = if let Some(ref version) = self.compiler_version { + version.trim_start_matches('v').parse()? + } else if let Some(ref solc) = config.solc { + match solc { + 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)) + { + let unique_versions = entry + .artifacts + .get(&contract.name) + .map(|artifacts| artifacts.keys().collect::>()) + .unwrap_or_default(); + + if unique_versions.is_empty() { + eyre::bail!("No matching artifact found for {}", contract.name); + } else if unique_versions.len() > 1 { + warn!( + "Ambiguous compiler versions found in cache: {}", + unique_versions.iter().join(", ") + ); + eyre::bail!("Compiler version has to be set in `foundry.toml`. If the project was not deployed with foundry, specify the version through `--compiler-version` flag.") + } + + unique_versions.into_iter().next().unwrap().to_owned() + } else { + 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) + } else { + if config.get_rpc_url().is_none() { + eyre::bail!("You have to provide a contract name or a valid RPC URL") + } + let provider = utils::get_provider(&config)?; + let code = provider.get_code_at(self.address, Default::default()).await?; + + let output = ProjectCompiler::new().compile(&project)?; + let contracts = ContractsByArtifact::new( + output.artifact_ids().map(|(id, artifact)| (id, artifact.clone().into())), + ); + + let Some((artifact_id, _)) = contracts.find_by_deployed_code_exact(&code) else { + eyre::bail!(format!( + "Bytecode at {} does not match any local contracts", + self.address + )) + }; + + VerificationContext::new( + artifact_id.source.clone(), + artifact_id.name.split('.').next().unwrap().to_owned(), + artifact_id.version.clone(), + config, + ) + } + } } /// Check verification status arguments diff --git a/crates/verify/src/provider.rs b/crates/verify/src/provider.rs index deac48229..210283c3a 100644 --- a/crates/verify/src/provider.rs +++ b/crates/verify/src/provider.rs @@ -2,9 +2,92 @@ use super::{ etherscan::EtherscanVerificationProvider, sourcify::SourcifyVerificationProvider, VerifyArgs, VerifyCheckArgs, }; +use alloy_json_abi::JsonAbi; use async_trait::async_trait; -use eyre::Result; -use std::{fmt, str::FromStr}; +use eyre::{OptionExt, Result}; +use foundry_common::compile::ProjectCompiler; +use foundry_compilers::{ + artifacts::{output_selection::OutputSelection, Metadata, Source}, + compilers::{solc::SolcVersionManager, CompilerVersionManager}, + CompilerConfig, Graph, Project, +}; +use foundry_config::Config; +use semver::Version; +use std::{fmt, path::PathBuf, str::FromStr}; + +/// Container with data required for contract verification. +#[derive(Debug, Clone)] +pub struct VerificationContext { + pub config: Config, + pub project: Project, + pub target_path: PathBuf, + pub target_name: String, + pub compiler_version: Version, +} + +impl VerificationContext { + pub fn new( + target_path: PathBuf, + target_name: String, + compiler_version: Version, + config: Config, + ) -> Result { + let mut project = config.project()?; + project.no_artifacts = true; + + // Set project's compiler to always use resolved version. + let vm = SolcVersionManager::default(); + let solc = vm.get_or_install(&compiler_version)?; + project.compiler_config = CompilerConfig::Specific(solc); + + Ok(Self { config, project, target_name, target_path, compiler_version }) + } + + /// Compiles target contract requesting only ABI and returns it. + pub fn get_target_abi(&self) -> Result { + let mut project = self.project.clone(); + project.settings.output_selection = + OutputSelection::common_output_selection(["abi".to_string()]); + + let output = ProjectCompiler::new() + .quiet(true) + .files([self.target_path.clone()]) + .compile(&project)?; + + let artifact = output + .find(self.target_path.to_string_lossy(), &self.target_name) + .ok_or_eyre("failed to find target artifact when compiling for abi")?; + + artifact.abi.clone().ok_or_eyre("target artifact does not have an ABI") + } + + /// Compiles target file requesting only metadata and returns it. + pub fn get_target_metadata(&self) -> Result { + let mut project = self.project.clone(); + project.settings.output_selection = + OutputSelection::common_output_selection(["metadata".to_string()]); + + let output = ProjectCompiler::new() + .quiet(true) + .files([self.target_path.clone()]) + .compile(&project)?; + + let artifact = output + .find(self.target_path.to_string_lossy(), &self.target_name) + .ok_or_eyre("failed to find target artifact when compiling for metadata")?; + + artifact.metadata.clone().ok_or_eyre("target artifact does not have an ABI") + } + + /// Returns [Vec] containing imports of the target file. + pub fn get_target_imports(&self) -> Result> { + let mut sources = self.project.paths.read_input_files()?; + sources.insert(self.target_path.clone(), Source::read(&self.target_path)?); + let graph = Graph::resolve_sources(&self.project.paths, sources)?; + + Ok(graph.imports(&self.target_path).into_iter().cloned().collect()) + } +} /// An abstraction for various verification providers such as etherscan, sourcify, blockscout #[async_trait] @@ -16,10 +99,14 @@ pub trait VerificationProvider { /// [`VerifyArgs`] are valid to begin with. This should prevent situations where there's a /// contract deployment that's executed before the verify request and the subsequent verify task /// fails due to misconfiguration. - async fn preflight_check(&mut self, args: VerifyArgs) -> Result<()>; + async fn preflight_check( + &mut self, + args: VerifyArgs, + context: VerificationContext, + ) -> Result<()>; /// Sends the actual verify request for the targeted contract. - async fn verify(&mut self, args: VerifyArgs) -> Result<()>; + async fn verify(&mut self, args: VerifyArgs, context: VerificationContext) -> Result<()>; /// Checks whether the contract is verified. async fn check(&self, args: VerifyCheckArgs) -> Result<()>; diff --git a/crates/verify/src/sourcify.rs b/crates/verify/src/sourcify.rs index 71fde6336..58cb2b4b9 100644 --- a/crates/verify/src/sourcify.rs +++ b/crates/verify/src/sourcify.rs @@ -1,13 +1,12 @@ use super::{provider::VerificationProvider, VerifyArgs, VerifyCheckArgs}; +use crate::provider::VerificationContext; use async_trait::async_trait; use eyre::Result; -use foundry_cli::utils::{get_cached_entry_by_name, LoadConfig}; use foundry_common::{fs, retry::Retry}; -use foundry_compilers::ConfigurableContractArtifact; use futures::FutureExt; use reqwest::Url; use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, path::PathBuf, str::FromStr}; +use std::{collections::HashMap, str::FromStr}; pub static SOURCIFY_URL: &str = "https://sourcify.dev/server/"; @@ -18,13 +17,17 @@ pub struct SourcifyVerificationProvider; #[async_trait] impl VerificationProvider for SourcifyVerificationProvider { - async fn preflight_check(&mut self, args: VerifyArgs) -> Result<()> { - let _ = self.prepare_request(&args)?; + async fn preflight_check( + &mut self, + args: VerifyArgs, + context: VerificationContext, + ) -> Result<()> { + let _ = self.prepare_request(&args, &context)?; Ok(()) } - async fn verify(&mut self, args: VerifyArgs) -> Result<()> { - let body = self.prepare_request(&args)?; + async fn verify(&mut self, args: VerifyArgs, context: VerificationContext) -> Result<()> { + let body = self.prepare_request(&args, &context)?; trace!("submitting verification request {:?}", body); @@ -36,7 +39,7 @@ impl VerificationProvider for SourcifyVerificationProvider { async { println!( "\nSubmitting verification for [{}] {:?}.", - args.contract.name, + context.target_name, args.address.to_string() ); let response = client @@ -99,54 +102,24 @@ impl VerificationProvider for SourcifyVerificationProvider { impl SourcifyVerificationProvider { /// Configures the API request to the sourcify API using the given [`VerifyArgs`]. - fn prepare_request(&self, args: &VerifyArgs) -> Result { - let mut config = args.try_load_config_emit_warnings()?; - config.libraries.extend(args.libraries.clone()); - - let project = config.project()?; - - if !config.cache { - eyre::bail!("Cache is required for sourcify verification.") - } - - let cache = project.read_cache_file()?; - let (path, entry) = get_cached_entry_by_name(&cache, &args.contract.name)?; + fn prepare_request( + &self, + args: &VerifyArgs, + context: &VerificationContext, + ) -> Result { + let metadata = context.get_target_metadata()?; + let imports = context.get_target_imports()?; - if entry.compiler_settings.metadata.is_none() { - eyre::bail!( - r#"Contract {} was compiled without the solc `metadata` setting. -Sourcify requires contract metadata for verification. -metadata output can be enabled via `extra_output = ["metadata"]` in `foundry.toml`"#, - args.contract.name - ) - } + let mut files = HashMap::with_capacity(2 + imports.len()); - let mut files = HashMap::with_capacity(2 + entry.imports.len()); - - // the metadata is included in the contract's artifact file - let artifact_path = entry - .find_artifact_path(&args.contract.name) - .ok_or_else(|| eyre::eyre!("No artifact found for contract {}", args.contract.name))?; - - let artifact: ConfigurableContractArtifact = fs::read_json_file(artifact_path)?; - if let Some(metadata) = artifact.metadata { - let metadata = serde_json::to_string_pretty(&metadata)?; - files.insert("metadata.json".to_string(), metadata); - } else { - eyre::bail!( - r#"No metadata found in artifact `{}` for contract {}. -Sourcify requires contract metadata for verification. -metadata output can be enabled via `extra_output = ["metadata"]` in `foundry.toml`"#, - artifact_path.display(), - args.contract.name - ) - } + let metadata = serde_json::to_string_pretty(&metadata)?; + files.insert("metadata.json".to_string(), metadata); - let contract_path = args.contract.path.clone().map_or(path, PathBuf::from); + let contract_path = context.target_path.clone(); let filename = contract_path.file_name().unwrap().to_string_lossy().to_string(); files.insert(filename, fs::read_to_string(&contract_path)?); - for import in entry.imports { + for import in imports { let import_entry = format!("{}", import.display()); files.insert(import_entry, fs::read_to_string(&import)?); } From 9c719d94997467363082520ab5437e2682b7036d Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 23 May 2024 17:12:50 +0300 Subject: [PATCH 300/430] fix: make clippy happy (#7975) --- crates/anvil/src/eth/otterscan/types.rs | 3 ++- crates/cheatcodes/src/test/expect.rs | 23 +++++++++++------------ crates/config/src/providers/remappings.rs | 1 + crates/evm/core/src/backend/mod.rs | 2 +- crates/evm/core/src/fork/backend.rs | 2 +- crates/evm/core/src/utils.rs | 1 + crates/evm/fuzz/src/strategies/int.rs | 8 ++++---- crates/evm/fuzz/src/strategies/mod.rs | 1 + crates/evm/fuzz/src/strategies/uint.rs | 8 ++++---- crates/forge/bin/cmd/clone.rs | 2 +- crates/forge/src/coverage.rs | 2 +- crates/forge/src/runner.rs | 3 +-- 12 files changed, 29 insertions(+), 27 deletions(-) diff --git a/crates/anvil/src/eth/otterscan/types.rs b/crates/anvil/src/eth/otterscan/types.rs index 72bcb0de1..fc0104066 100644 --- a/crates/anvil/src/eth/otterscan/types.rs +++ b/crates/anvil/src/eth/otterscan/types.rs @@ -135,7 +135,8 @@ impl OtsBlockDetails { /// This has two problems though: /// - It makes the endpoint too specific to Otterscan's implementation /// - It breaks the abstraction built in `OtsBlock` which computes `transaction_count` - /// based on the existing list. + /// based on the existing list. + /// /// Therefore we keep it simple by keeping the data in the response pub async fn build(block: Block, backend: &Backend) -> Result { if block.transactions.is_uncle() { diff --git a/crates/cheatcodes/src/test/expect.rs b/crates/cheatcodes/src/test/expect.rs index 2fdc99476..3b777bd8a 100644 --- a/crates/cheatcodes/src/test/expect.rs +++ b/crates/cheatcodes/src/test/expect.rs @@ -301,19 +301,18 @@ impl Cheatcode for expectSafeMemoryCallCall { /// /// It can handle calls in two ways: /// - If the cheatcode was used with a `count` argument, it will expect the call to be made exactly -/// `count` times. -/// e.g. `vm.expectCall(address(0xc4f3), abi.encodeWithSelector(0xd34db33f), 4)` will expect the -/// call to address(0xc4f3) with selector `0xd34db33f` to be made exactly 4 times. If the amount of -/// calls is less or more than 4, the test will fail. Note that the `count` argument cannot be -/// overwritten with another `vm.expectCall`. If this is attempted, `expectCall` will revert. +/// `count` times. e.g. `vm.expectCall(address(0xc4f3), abi.encodeWithSelector(0xd34db33f), 4)` +/// will expect the call to address(0xc4f3) with selector `0xd34db33f` to be made exactly 4 times. +/// If the amount of calls is less or more than 4, the test will fail. Note that the `count` +/// argument cannot be overwritten with another `vm.expectCall`. If this is attempted, +/// `expectCall` will revert. /// - If the cheatcode was used without a `count` argument, it will expect the call to be made at -/// least the amount of times the cheatcode -/// was called. This means that `vm.expectCall` without a count argument can be called many times, -/// but cannot be called with a `count` argument after it was called without one. If the latter -/// happens, `expectCall` will revert. e.g `vm.expectCall(address(0xc4f3), -/// abi.encodeWithSelector(0xd34db33f))` will expect the call to address(0xc4f3) and selector -/// `0xd34db33f` to be made at least once. If the amount of calls is 0, the test will fail. If the -/// call is made more than once, the test will pass. +/// least the amount of times the cheatcode was called. This means that `vm.expectCall` without a +/// count argument can be called many times, but cannot be called with a `count` argument after it +/// was called without one. If the latter happens, `expectCall` will revert. e.g +/// `vm.expectCall(address(0xc4f3), abi.encodeWithSelector(0xd34db33f))` will expect the call to +/// address(0xc4f3) and selector `0xd34db33f` to be made at least once. If the amount of calls is +/// 0, the test will fail. If the call is made more than once, the test will pass. #[allow(clippy::too_many_arguments)] // It is what it is fn expect_call( state: &mut Cheatcodes, diff --git a/crates/config/src/providers/remappings.rs b/crates/config/src/providers/remappings.rs index 67ae449fb..2bfbf4ca4 100644 --- a/crates/config/src/providers/remappings.rs +++ b/crates/config/src/providers/remappings.rs @@ -102,6 +102,7 @@ impl<'a> RemappingsProvider<'a> { trace!("get all remappings from {:?}", self.root); /// prioritizes remappings that are closer: shorter `path` /// - ("a", "1/2") over ("a", "1/2/3") + /// /// grouped by remapping context fn insert_closest( mappings: &mut BTreeMap, BTreeMap>, diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index 90e27444b..fd4eeba7f 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -398,7 +398,7 @@ pub struct Backend { /// In a way the `JournaledState` is something like a cache that /// 1. check if account is already loaded (hot) /// 2. if not load from the `Database` (this will then retrieve the account via RPC in forking - /// mode) + /// mode) /// /// To properly initialize we store the `JournaledState` before the first fork is selected /// ([`DatabaseExt::select_fork`]). diff --git a/crates/evm/core/src/fork/backend.rs b/crates/evm/core/src/fork/backend.rs index 2ab9bdc9b..960f3779f 100644 --- a/crates/evm/core/src/fork/backend.rs +++ b/crates/evm/core/src/fork/backend.rs @@ -134,7 +134,7 @@ where /// We always check: /// 1. if the requested value is already stored in the cache, then answer the sender /// 2. otherwise, fetch it via the provider but check if a request for that value is already in - /// progress (e.g. another Sender just requested the same account) + /// progress (e.g. another Sender just requested the same account) fn on_request(&mut self, req: BackendRequest) { match req { BackendRequest::Basic(addr, sender) => { diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index bf9664bd8..d02889052 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -23,6 +23,7 @@ pub use revm::primitives::State as StateChangeset; /// /// - checks for prevrandao mixhash after merge /// - applies chain specifics: on Arbitrum `block.number` is the L1 block +/// /// Should be called with proper chain id (retrieved from provider if not provided). pub fn apply_chain_and_block_specific_env_changes(env: &mut revm::primitives::Env, block: &Block) { if let Ok(chain) = NamedChain::try_from(env.cfg.chain_id) { diff --git a/crates/evm/fuzz/src/strategies/int.rs b/crates/evm/fuzz/src/strategies/int.rs index b27f62f28..fc8f88ed6 100644 --- a/crates/evm/fuzz/src/strategies/int.rs +++ b/crates/evm/fuzz/src/strategies/int.rs @@ -81,15 +81,15 @@ impl ValueTree for IntValueTree { /// Value tree for signed ints (up to int256). /// The strategy combines 3 different strategies, each assigned a specific weight: /// 1. Generate purely random value in a range. This will first choose bit size uniformly (up `bits` -/// param). Then generate a value for this bit size. +/// param). Then generate a value for this bit size. /// 2. Generate a random value around the edges (+/- 3 around min, 0 and max possible value) /// 3. Generate a value from a predefined fixtures set /// /// To define int fixtures: -/// - return an array of possible values for a parameter named `amount` declare a function -/// `function fixture_amount() public returns (int32[] memory)`. +/// - return an array of possible values for a parameter named `amount` declare a function `function +/// fixture_amount() public returns (int32[] memory)`. /// - use `amount` named parameter in fuzzed test in order to include fixtures in fuzzed values -/// `function testFuzz_int32(int32 amount)`. +/// `function testFuzz_int32(int32 amount)`. /// /// If fixture is not a valid int type then error is raised and random value generated. #[derive(Debug)] diff --git a/crates/evm/fuzz/src/strategies/mod.rs b/crates/evm/fuzz/src/strategies/mod.rs index 4e1120b58..b49adf16c 100644 --- a/crates/evm/fuzz/src/strategies/mod.rs +++ b/crates/evm/fuzz/src/strategies/mod.rs @@ -19,6 +19,7 @@ pub use invariants::{fuzz_contract_with_calldata, invariant_strat, override_call /// Macro to create strategy with fixtures. /// 1. A default strategy if no fixture defined for current parameter. /// 2. A weighted strategy that use fixtures and default strategy values for current parameter. +/// /// If fixture is not of the same type as fuzzed parameter then value is rejected and error raised. macro_rules! fixture_strategy { ($fixtures:ident, $strategy_value:expr, $default_strategy:expr) => { diff --git a/crates/evm/fuzz/src/strategies/uint.rs b/crates/evm/fuzz/src/strategies/uint.rs index 1b1eb2540..77a080f8c 100644 --- a/crates/evm/fuzz/src/strategies/uint.rs +++ b/crates/evm/fuzz/src/strategies/uint.rs @@ -69,15 +69,15 @@ impl ValueTree for UintValueTree { /// Value tree for unsigned ints (up to uint256). /// The strategy combines 3 different strategies, each assigned a specific weight: /// 1. Generate purely random value in a range. This will first choose bit size uniformly (up `bits` -/// param). Then generate a value for this bit size. +/// param). Then generate a value for this bit size. /// 2. Generate a random value around the edges (+/- 3 around 0 and max possible value) /// 3. Generate a value from a predefined fixtures set /// /// To define uint fixtures: -/// - return an array of possible values for a parameter named `amount` declare a function -/// `function fixture_amount() public returns (uint32[] memory)`. +/// - return an array of possible values for a parameter named `amount` declare a function `function +/// fixture_amount() public returns (uint32[] memory)`. /// - use `amount` named parameter in fuzzed test in order to include fixtures in fuzzed values -/// `function testFuzz_uint32(uint32 amount)`. +/// `function testFuzz_uint32(uint32 amount)`. /// /// If fixture is not a valid uint type then error is raised and random value generated. #[derive(Debug)] diff --git a/crates/forge/bin/cmd/clone.rs b/crates/forge/bin/cmd/clone.rs index c3315e198..61e4d637f 100644 --- a/crates/forge/bin/cmd/clone.rs +++ b/crates/forge/bin/cmd/clone.rs @@ -470,7 +470,7 @@ fn dump_sources(meta: &Metadata, root: &PathBuf, no_reorg: bool) -> Result ContractRunner<'a> { /// /// Fixtures can be defined: /// - as storage arrays in test contract, prefixed with `fixture` - /// - as functions prefixed with `fixture` and followed by parameter name to be - /// fuzzed + /// - as functions prefixed with `fixture` and followed by parameter name to be fuzzed /// /// Storage array fixtures: /// `uint256[] public fixture_amount = [1, 2, 3];` From 299902e34d32c4ea77bf2c8b9b13c68f45113f13 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Thu, 23 May 2024 10:33:42 -0400 Subject: [PATCH 301/430] chore: bump to latest alloy and revm (#7934) * revert to alloy f415827 * revm-inspectors bump 098ab30 * deny.toml nit * satisfy AccessListInspector trait bounds * bump revm * map Opcode * nit * fix: revm `CallInputs` for cheatcodes/inspector * remove: `BlockId::latest()` calls * use unwrap_or_default * fix: evm/coverage * fix: evm/fuzz * fix: cheatcodes * fix: evm executors and inspectors * fix: anvil & verify * fix: add `AnvilAccessListInspector` to satisfy trait bounds * fix: anvil tests * clippy: rm unused * fmt * fix: cheatcodes inspector revm `CallInputs` * rm spec arg * use OPCODE_INFO_JUMPTABLE * nit * rm wrapped inspectors * fix: anvil test can_sign_tx * fix: evm deser cache tests * deprecate gas memory recording * nits * nit * nits * ci nits * fix: use `call.bytecode_address` instead of `call.target_address` * nit Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix arg * rm forge-std --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: Matthias Seitz --- Cargo.lock | 72 +++++------ Cargo.toml | 52 ++++---- crates/anvil/core/src/eth/transaction/mod.rs | 4 +- crates/anvil/src/eth/api.rs | 4 + crates/anvil/src/eth/backend/db.rs | 6 +- crates/anvil/src/eth/backend/fork.rs | 21 ++-- crates/anvil/src/eth/backend/mem/fork_db.rs | 3 +- .../anvil/src/eth/backend/mem/in_memory_db.rs | 7 +- crates/anvil/src/eth/backend/mem/mod.rs | 9 +- crates/anvil/src/eth/error.rs | 8 +- crates/anvil/tests/it/anvil_api.rs | 22 ++-- crates/anvil/tests/it/api.rs | 2 +- crates/anvil/tests/it/fork.rs | 115 ++++++++---------- crates/anvil/tests/it/genesis.rs | 3 +- crates/anvil/tests/it/optimism.rs | 10 +- crates/anvil/tests/it/sign.rs | 2 +- crates/anvil/tests/it/transaction.rs | 56 +++++---- crates/anvil/tests/it/wsapi.rs | 3 +- crates/cast/bin/cmd/estimate.rs | 4 +- crates/cast/bin/cmd/mktx.rs | 4 +- crates/cast/bin/cmd/send.rs | 8 +- crates/cast/bin/cmd/storage.rs | 9 +- crates/cast/bin/main.rs | 3 +- crates/cast/bin/tx.rs | 6 +- crates/cast/src/lib.rs | 33 +++-- crates/cheatcodes/assets/cheatcodes.json | 2 +- crates/cheatcodes/spec/src/vm.rs | 2 +- crates/cheatcodes/src/evm.rs | 2 +- crates/cheatcodes/src/evm/mapping.rs | 5 +- crates/cheatcodes/src/evm/mock.rs | 2 +- crates/cheatcodes/src/inspector.rs | 83 ++++++------- crates/debugger/src/tui/mod.rs | 5 +- crates/evm/core/src/backend/mod.rs | 10 +- crates/evm/core/src/fork/backend.rs | 20 +-- crates/evm/core/src/fork/cache.rs | 44 ++++--- crates/evm/core/src/ic.rs | 21 ++-- crates/evm/core/src/utils.rs | 23 ++-- crates/evm/coverage/src/anchors.rs | 9 +- crates/evm/coverage/src/inspector.rs | 6 +- crates/evm/evm/Cargo.toml | 7 +- crates/evm/evm/src/executors/mod.rs | 8 +- crates/evm/evm/src/inspectors/debugger.rs | 19 ++- crates/evm/evm/src/inspectors/logs.rs | 2 +- crates/evm/evm/src/inspectors/stack.rs | 18 +-- crates/evm/fuzz/src/inspector.rs | 16 ++- crates/evm/fuzz/src/strategies/state.rs | 9 +- crates/forge/bin/cmd/coverage.rs | 6 +- crates/forge/bin/cmd/create.rs | 6 +- crates/script/src/broadcast.rs | 11 +- crates/script/src/build.rs | 3 +- crates/test-utils/Cargo.toml | 1 - crates/test-utils/src/script.rs | 29 +---- crates/verify/src/bytecode.rs | 17 +-- crates/verify/src/lib.rs | 2 +- deny.toml | 5 +- testdata/default/cheats/LastCallGas.t.sol | 24 +--- 56 files changed, 417 insertions(+), 466 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5472893a8..b03d115c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,7 +79,7 @@ dependencies = [ [[package]] name = "alloy-consensus" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-eips", "alloy-primitives", @@ -92,7 +92,7 @@ dependencies = [ [[package]] name = "alloy-contract" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -132,7 +132,7 @@ dependencies = [ [[package]] name = "alloy-eips" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -147,7 +147,7 @@ dependencies = [ [[package]] name = "alloy-genesis" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-primitives", "alloy-serde", @@ -170,7 +170,7 @@ dependencies = [ [[package]] name = "alloy-json-rpc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-primitives", "serde", @@ -182,7 +182,7 @@ dependencies = [ [[package]] name = "alloy-network" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-consensus", "alloy-eips", @@ -226,7 +226,7 @@ dependencies = [ [[package]] name = "alloy-provider" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-eips", "alloy-json-rpc", @@ -247,6 +247,7 @@ dependencies = [ "futures", "futures-utils-wasm", "lru", + "pin-project", "reqwest", "serde_json", "tokio", @@ -257,7 +258,7 @@ dependencies = [ [[package]] name = "alloy-pubsub" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -297,7 +298,7 @@ dependencies = [ [[package]] name = "alloy-rpc-client" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -321,7 +322,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-consensus", "alloy-eips", @@ -339,7 +340,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-consensus", "alloy-eips", @@ -356,7 +357,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -368,7 +369,7 @@ dependencies = [ [[package]] name = "alloy-serde" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-primitives", "serde", @@ -378,7 +379,7 @@ dependencies = [ [[package]] name = "alloy-signer" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -393,7 +394,7 @@ dependencies = [ [[package]] name = "alloy-signer-aws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-consensus", "alloy-network", @@ -410,7 +411,7 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -429,7 +430,7 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-consensus", "alloy-network", @@ -445,7 +446,7 @@ dependencies = [ [[package]] name = "alloy-signer-wallet" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-consensus", "alloy-network", @@ -536,7 +537,7 @@ dependencies = [ [[package]] name = "alloy-transport" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-json-rpc", "base64 0.22.1", @@ -554,7 +555,7 @@ dependencies = [ [[package]] name = "alloy-transport-http" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -568,7 +569,7 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -588,7 +589,7 @@ dependencies = [ [[package]] name = "alloy-transport-ws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=899fc51#899fc51af8b5f4de6df1605ca3ffe8d8d6fa8c69" +source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -3693,7 +3694,6 @@ version = "0.2.0" dependencies = [ "alloy-primitives", "alloy-provider", - "alloy-rpc-types", "eyre", "fd-lock 4.0.2", "foundry-common", @@ -6372,9 +6372,9 @@ dependencies = [ [[package]] name = "revm" -version = "8.0.0" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a454c1c650b2b2e23f0c461af09e6c31e1d15e1cbebe905a701c46b8a50afc" +checksum = "3a2c336f9921588e50871c00024feb51a521eca50ce6d01494bb9c50f837c8ed" dependencies = [ "auto_impl", "cfg-if", @@ -6388,7 +6388,7 @@ dependencies = [ [[package]] name = "revm-inspectors" version = "0.1.0" -source = "git+https://github.com/paradigmxyz/evm-inspectors?rev=c1b5dd0#c1b5dd0d85dd46ef5ec5258aebd24adc041d103a" +source = "git+https://github.com/paradigmxyz/revm-inspectors?rev=098ab30#098ab30faf3254c8ba3159b1af006d71d3577d95" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -6404,9 +6404,9 @@ dependencies = [ [[package]] name = "revm-interpreter" -version = "4.0.0" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d322f2730cd300e99d271a1704a2dfb8973d832428f5aa282aaa40e2473b5eec" +checksum = "a58182c7454179826f9dad2ca577661963092ce9d0fd0c9d682c1e9215a72e70" dependencies = [ "revm-primitives", "serde", @@ -6414,9 +6414,9 @@ dependencies = [ [[package]] name = "revm-precompile" -version = "6.0.0" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "931f692f3f4fc72ec39d5d270f8e9d208c4a6008de7590ee96cf948e3b6d3f8d" +checksum = "dc8af9aa737eef0509a50d9f3cc1a631557a00ef2e70a3aa8a75d9ee0ed275bb" dependencies = [ "aurora-engine-modexp", "c-kzg", @@ -6431,9 +6431,9 @@ dependencies = [ [[package]] name = "revm-primitives" -version = "3.1.1" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbbc9640790cebcb731289afb7a7d96d16ad94afeb64b5d0b66443bd151e79d6" +checksum = "b9bf5d465e64b697da6a111cb19e798b5b2ebb18e5faf2ad48e9e8d47c64add2" dependencies = [ "alloy-primitives", "auto_impl", @@ -6895,9 +6895,9 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.28.2" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10" +checksum = "0e0cc0f1cf93f4969faf3ea1c7d8a9faed25918d96affa959720823dfe86d4f3" dependencies = [ "rand", "secp256k1-sys", @@ -6905,9 +6905,9 @@ dependencies = [ [[package]] name = "secp256k1-sys" -version = "0.9.2" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb" +checksum = "1433bd67156263443f14d603720b082dd3121779323fce20cba2aa07b874bc1b" dependencies = [ "cc", ] diff --git a/Cargo.toml b/Cargo.toml index 55d368d4a..d55304817 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -142,9 +142,9 @@ foundry-compilers = { version = "0.4.3", default-features = false } ## revm # no default features to avoid c-kzg -revm = { version = "8", default-features = false } -revm-primitives = { version = "3", default-features = false } -revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "c1b5dd0", features = [ +revm = { version = "9", default-features = false } +revm-primitives = { version = "4", default-features = false } +revm-inspectors = { git = "https://github.com/paradigmxyz/revm-inspectors", rev = "098ab30", features = [ "serde", ] } @@ -152,29 +152,29 @@ revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = ethers-contract-abigen = { version = "2.0.14", default-features = false } ## alloy -alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-signer-wallet = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } -alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "899fc51", default-features = false } +alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-signer-wallet = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } alloy-primitives = { version = "0.7.1", features = ["getrandom"] } alloy-dyn-abi = "0.7.1" alloy-json-abi = "0.7.1" diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index c7bd58054..eced8fe33 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -16,7 +16,7 @@ use bytes::BufMut; use foundry_evm::traces::CallTraceNode; use revm::{ interpreter::InstructionResult, - primitives::{CreateScheme, OptimismFields, TransactTo, TxEnv}, + primitives::{OptimismFields, TransactTo, TxEnv}, }; use serde::{Deserialize, Serialize}; use std::ops::{Deref, Mul}; @@ -447,7 +447,7 @@ impl PendingTransaction { fn transact_to(kind: &TxKind) -> TransactTo { match kind { TxKind::Call(c) => TransactTo::Call(*c), - TxKind::Create => TransactTo::Create(CreateScheme::Create), + TxKind::Create => TransactTo::Create, } } diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 4fd9d090d..70641043f 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -2668,6 +2668,10 @@ impl TryFrom, u128, State)>> for GasEs InstructionResult::FatalExternalError | InstructionResult::OutOfFunds | InstructionResult::CallTooDeep => Ok(Self::EvmError(exit)), + // Handle Revm EOF InstructionResults: Not supported yet + InstructionResult::ReturnContractInNotInitEOF | + InstructionResult::EOFOpcodeDisabledInLegacy | + InstructionResult::EOFFunctionStackOverflow => Ok(Self::EvmError(exit)), }, } } diff --git a/crates/anvil/src/eth/backend/db.rs b/crates/anvil/src/eth/backend/db.rs index 6149c0108..34ad343b5 100644 --- a/crates/anvil/src/eth/backend/db.rs +++ b/crates/anvil/src/eth/backend/db.rs @@ -100,7 +100,7 @@ pub trait Db: B256::from_slice(&keccak256(code.as_ref())[..]) }; info.code_hash = code_hash; - info.code = Some(Bytecode::new_raw(alloy_primitives::Bytes(code.0)).to_checked()); + info.code = Some(Bytecode::new_raw(alloy_primitives::Bytes(code.0))); self.insert_account(address, info); Ok(()) } @@ -137,9 +137,7 @@ pub trait Db: code: if account.code.0.is_empty() { None } else { - Some( - Bytecode::new_raw(alloy_primitives::Bytes(account.code.0)).to_checked(), - ) + Some(Bytecode::new_raw(alloy_primitives::Bytes(account.code.0))) }, nonce, }, diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index a9cc1cd9d..b193a6fa7 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -163,7 +163,7 @@ impl ClientFork { keys: Vec, block_number: Option, ) -> Result { - self.provider().get_proof(address, keys, block_number.unwrap_or(BlockId::latest())).await + self.provider().get_proof(address, keys).block_id(block_number.unwrap_or_default()).await } /// Sends `eth_call` @@ -184,8 +184,8 @@ impl ClientFork { request: &WithOtherFields, block: Option, ) -> Result { - let block = block.unwrap_or(BlockNumber::Latest); - let res = self.provider().estimate_gas(request, block.into()).await?; + let block = block.unwrap_or_default(); + let res = self.provider().estimate_gas(request).block_id(block.into()).await?; Ok(res) } @@ -196,9 +196,7 @@ impl ClientFork { request: &WithOtherFields, block: Option, ) -> Result { - self.provider() - .create_access_list(request, block.unwrap_or(BlockNumber::Latest).into()) - .await + self.provider().create_access_list(request).block_id(block.unwrap_or_default().into()).await } pub async fn storage_at( @@ -208,7 +206,8 @@ impl ClientFork { number: Option, ) -> Result { self.provider() - .get_storage_at(address, index, number.unwrap_or(BlockNumber::Latest).into()) + .get_storage_at(address, index) + .block_id(number.unwrap_or_default().into()) .await } @@ -234,9 +233,9 @@ impl ClientFork { return Ok(code); } - let block_id = BlockId::Number(blocknumber.into()); + let block_id = BlockId::number(blocknumber); - let code = self.provider().get_code_at(address, block_id).await?; + let code = self.provider().get_code_at(address).block_id(block_id).await?; let mut storage = self.storage_write(); storage.code_at.insert((address, blocknumber), code.clone().0.into()); @@ -250,12 +249,12 @@ impl ClientFork { blocknumber: u64, ) -> Result { trace!(target: "backend::fork", "get_balance={:?}", address); - self.provider().get_balance(address, blocknumber.into()).await + self.provider().get_balance(address).block_id(blocknumber.into()).await } pub async fn get_nonce(&self, address: Address, block: u64) -> Result { trace!(target: "backend::fork", "get_nonce={:?}", address); - self.provider().get_transaction_count(address, block.into()).await + self.provider().get_transaction_count(address).block_id(block.into()).await } pub async fn transaction_by_block_number_and_index( diff --git a/crates/anvil/src/eth/backend/mem/fork_db.rs b/crates/anvil/src/eth/backend/mem/fork_db.rs index d99aeb5ed..93070ef88 100644 --- a/crates/anvil/src/eth/backend/mem/fork_db.rs +++ b/crates/anvil/src/eth/backend/mem/fork_db.rs @@ -48,8 +48,7 @@ impl Db for ForkedDatabase { code } else { db.code_by_hash(v.info.code_hash)? - } - .to_checked(); + }; Ok(( k, SerializableAccountRecord { diff --git a/crates/anvil/src/eth/backend/mem/in_memory_db.rs b/crates/anvil/src/eth/backend/mem/in_memory_db.rs index 1c96a0eb5..4a1a429f0 100644 --- a/crates/anvil/src/eth/backend/mem/in_memory_db.rs +++ b/crates/anvil/src/eth/backend/mem/in_memory_db.rs @@ -48,8 +48,7 @@ impl Db for MemDb { code } else { self.inner.code_by_hash_ref(v.info.code_hash)? - } - .to_checked(); + }; Ok(( k, SerializableAccountRecord { @@ -147,7 +146,7 @@ mod tests { let mut dump_db = MemDb::default(); - let contract_code = Bytecode::new_raw(Bytes::from("fake contract code")).to_checked(); + let contract_code = Bytecode::new_raw(Bytes::from("fake contract code")); dump_db.insert_account( test_addr, @@ -184,7 +183,7 @@ mod tests { let test_addr2: Address = Address::from_str("0x70997970c51812dc3a010c7d01b50e0d17dc79c8").unwrap(); - let contract_code = Bytecode::new_raw(Bytes::from("fake contract code")).to_checked(); + let contract_code = Bytecode::new_raw(Bytes::from("fake contract code")); let mut db = MemDb::default(); diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index d49c99d91..861bad0a5 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -67,8 +67,8 @@ use foundry_evm::{ db::CacheDB, interpreter::InstructionResult, primitives::{ - BlockEnv, CfgEnvWithHandlerCfg, CreateScheme, EnvWithHandlerCfg, ExecutionResult, - Output, SpecId, TransactTo, TxEnv, KECCAK_EMPTY, + BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, ExecutionResult, Output, SpecId, + TransactTo, TxEnv, KECCAK_EMPTY, }, }, utils::new_evm_with_inspector_ref, @@ -1160,7 +1160,7 @@ impl Backend { max_fee_per_blob_gas: max_fee_per_blob_gas.map(U256::from), transact_to: match to { Some(addr) => TransactTo::Call(*addr), - None => TransactTo::Create(CreateScheme::Create), + None => TransactTo::Create, }, value: value.unwrap_or_default(), data: input.into_input().unwrap_or_default(), @@ -1169,6 +1169,7 @@ impl Backend { access_list: access_list.unwrap_or_default().flattened(), blob_hashes: blob_versioned_hashes.unwrap_or_default(), optimism: OptimismFields { enveloped_tx: Some(Bytes::new()), ..Default::default() }, + ..Default::default() }; if env.block.basefee == revm::primitives::U256::ZERO { @@ -2401,7 +2402,7 @@ impl TransactionValidator for Backend { // Ensure the tx does not exceed the max blobs per block. if blob_count > MAX_BLOBS_PER_BLOCK { - return Err(InvalidTransactionError::TooManyBlobs) + return Err(InvalidTransactionError::TooManyBlobs(MAX_BLOBS_PER_BLOCK, blob_count)) } // Check for any blob validation errors diff --git a/crates/anvil/src/eth/error.rs b/crates/anvil/src/eth/error.rs index 61333822c..417ec77a0 100644 --- a/crates/anvil/src/eth/error.rs +++ b/crates/anvil/src/eth/error.rs @@ -214,8 +214,8 @@ pub enum InvalidTransactionError { /// Thrown when there are no `blob_hashes` in the transaction, and it is an EIP-4844 tx. #[error("`blob_hashes` are required for EIP-4844 transactions")] NoBlobHashes, - #[error("too many blobs in one transaction")] - TooManyBlobs, + #[error("too many blobs in one transaction, max: {0}, have: {1}")] + TooManyBlobs(usize, usize), /// Thrown when there's a blob validation error #[error(transparent)] BlobTransactionValidationError(#[from] alloy_consensus::BlobTransactionValidationError), @@ -283,7 +283,9 @@ impl From for InvalidTransactionError { InvalidTransactionError::BlobVersionNotSupported } InvalidTransaction::EmptyBlobs => InvalidTransactionError::EmptyBlobs, - InvalidTransaction::TooManyBlobs => InvalidTransactionError::TooManyBlobs, + InvalidTransaction::TooManyBlobs { max, have } => { + InvalidTransactionError::TooManyBlobs(max, have) + } _ => todo!(), } } diff --git a/crates/anvil/tests/it/anvil_api.rs b/crates/anvil/tests/it/anvil_api.rs index 4f46d7525..0ebffe94a 100644 --- a/crates/anvil/tests/it/anvil_api.rs +++ b/crates/anvil/tests/it/anvil_api.rs @@ -87,10 +87,10 @@ async fn can_impersonate_account() { let res = provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); assert_eq!(res.from, impersonate); - let nonce = provider.get_transaction_count(impersonate, BlockId::latest()).await.unwrap(); + let nonce = provider.get_transaction_count(impersonate).await.unwrap(); assert_eq!(nonce, 1); - let balance = provider.get_balance(to, BlockId::latest()).await.unwrap(); + let balance = provider.get_balance(to).await.unwrap(); assert_eq!(balance, val); api.anvil_stop_impersonating_account(impersonate).await.unwrap(); @@ -125,10 +125,10 @@ async fn can_auto_impersonate_account() { let res = provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); assert_eq!(res.from, impersonate); - let nonce = provider.get_transaction_count(impersonate, BlockId::latest()).await.unwrap(); + let nonce = provider.get_transaction_count(impersonate).await.unwrap(); assert_eq!(nonce, 1); - let balance = provider.get_balance(to, BlockId::latest()).await.unwrap(); + let balance = provider.get_balance(to).await.unwrap(); assert_eq!(balance, val); api.anvil_auto_impersonate_account(false).await.unwrap(); @@ -169,7 +169,7 @@ async fn can_impersonate_contract() { let res = provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); assert_eq!(res.from, impersonate); - let balance = provider.get_balance(to, BlockId::latest()).await.unwrap(); + let balance = provider.get_balance(to).await.unwrap(); assert_eq!(balance, val); api.anvil_stop_impersonating_account(impersonate).await.unwrap(); @@ -188,24 +188,24 @@ async fn can_impersonate_gnosis_safe() { // let safe = address!("A063Cb7CFd8E57c30c788A0572CBbf2129ae56B6"); - let code = provider.get_code_at(safe, BlockId::default()).await.unwrap(); + let code = provider.get_code_at(safe).await.unwrap(); assert!(!code.is_empty()); api.anvil_impersonate_account(safe).await.unwrap(); - let code = provider.get_code_at(safe, BlockId::default()).await.unwrap(); + let code = provider.get_code_at(safe).await.unwrap(); assert!(!code.is_empty()); let balance = U256::from(1e18 as u64); // fund the impersonated account api.anvil_set_balance(safe, balance).await.unwrap(); - let on_chain_balance = provider.get_balance(safe, BlockId::latest()).await.unwrap(); + let on_chain_balance = provider.get_balance(safe).await.unwrap(); assert_eq!(on_chain_balance, balance); api.anvil_stop_impersonating_account(safe).await.unwrap(); - let code = provider.get_code_at(safe, BlockId::default()).await.unwrap(); + let code = provider.get_code_at(safe).await.unwrap(); // code is added back after stop impersonating assert!(!code.is_empty()); } @@ -234,7 +234,7 @@ async fn can_impersonate_multiple_accounts() { let res0 = provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); assert_eq!(res0.from, impersonate0); - let nonce = provider.get_transaction_count(impersonate0, BlockId::latest()).await.unwrap(); + let nonce = provider.get_transaction_count(impersonate0).await.unwrap(); assert_eq!(nonce, 1); let receipt = provider.get_transaction_receipt(res0.transaction_hash).await.unwrap().unwrap(); @@ -250,7 +250,7 @@ async fn can_impersonate_multiple_accounts() { assert_eq!(res1.from, impersonate1); - let nonce = provider.get_transaction_count(impersonate1, BlockId::latest()).await.unwrap(); + let nonce = provider.get_transaction_count(impersonate1).await.unwrap(); assert_eq!(nonce, 1); let receipt = provider.get_transaction_receipt(res1.transaction_hash).await.unwrap().unwrap(); diff --git a/crates/anvil/tests/it/api.rs b/crates/anvil/tests/it/api.rs index 9619e1388..8dd9b570c 100644 --- a/crates/anvil/tests/it/api.rs +++ b/crates/anvil/tests/it/api.rs @@ -34,7 +34,7 @@ async fn can_dev_get_balance() { let genesis_balance = handle.genesis_balance(); for acc in handle.genesis_accounts() { - let balance = provider.get_balance(acc, BlockId::latest()).await.unwrap(); + let balance = provider.get_balance(acc).await.unwrap(); assert_eq!(balance, genesis_balance); } } diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index 067934d8a..87a1b6985 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -75,7 +75,7 @@ async fn test_fork_eth_get_balance() { for _ in 0..10 { let addr = Address::random(); let balance = api.balance(addr, None).await.unwrap(); - let provider_balance = provider.get_balance(addr, BlockId::latest()).await.unwrap(); + let provider_balance = provider.get_balance(addr).await.unwrap(); assert_eq!(balance, provider_balance) } } @@ -91,11 +91,11 @@ async fn test_fork_eth_get_balance_after_mine() { let address = Address::random(); - let _balance = provider.get_balance(address, BlockId::Number(number.into())).await.unwrap(); + let _balance = provider.get_balance(address).await.unwrap(); api.evm_mine(None).await.unwrap(); - let _balance = provider.get_balance(address, BlockId::Number(number.into())).await.unwrap(); + let _balance = provider.get_balance(address).await.unwrap(); } // @@ -109,11 +109,11 @@ async fn test_fork_eth_get_code_after_mine() { let address = Address::random(); - let _code = provider.get_code_at(address, BlockId::Number(1.into())).await.unwrap(); + let _code = provider.get_code_at(address).block_id(BlockId::number(1)).await.unwrap(); api.evm_mine(None).await.unwrap(); - let _code = provider.get_code_at(address, BlockId::Number(1.into())).await.unwrap(); + let _code = provider.get_code_at(address).block_id(BlockId::number(1)).await.unwrap(); } #[tokio::test(flavor = "multi_thread")] @@ -123,7 +123,7 @@ async fn test_fork_eth_get_code() { for _ in 0..10 { let addr = Address::random(); let code = api.get_code(addr, None).await.unwrap(); - let provider_code = provider.get_code_at(addr, BlockId::latest()).await.unwrap(); + let provider_code = provider.get_code_at(addr).await.unwrap(); assert_eq!(code, provider_code) } @@ -140,7 +140,7 @@ async fn test_fork_eth_get_code() { .await .unwrap(); let code = api.get_code(address, None).await.unwrap(); - let provider_code = provider.get_code_at(address, BlockId::latest()).await.unwrap(); + let provider_code = provider.get_code_at(address).await.unwrap(); assert_eq!(code, prev_code); assert_eq!(code, provider_code); assert!(!code.as_ref().is_empty()); @@ -155,13 +155,13 @@ async fn test_fork_eth_get_nonce() { for _ in 0..10 { let addr = Address::random(); let api_nonce = api.transaction_count(addr, None).await.unwrap().to::(); - let provider_nonce = provider.get_transaction_count(addr, BlockId::latest()).await.unwrap(); + let provider_nonce = provider.get_transaction_count(addr).await.unwrap(); assert_eq!(api_nonce, provider_nonce); } let addr = Config::DEFAULT_SENDER; let api_nonce = api.transaction_count(addr, None).await.unwrap().to::(); - let provider_nonce = provider.get_transaction_count(addr, BlockId::latest()).await.unwrap(); + let provider_nonce = provider.get_transaction_count(addr).await.unwrap(); assert_eq!(api_nonce, provider_nonce); } @@ -186,20 +186,20 @@ async fn test_fork_reset() { let from = accounts[0].address(); let to = accounts[1].address(); let block_number = provider.get_block_number().await.unwrap(); - let balance_before = provider.get_balance(to, BlockId::latest()).await.unwrap(); + let balance_before = provider.get_balance(to).await.unwrap(); let amount = handle.genesis_balance().checked_div(U256::from(2u64)).unwrap(); - let initial_nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); + let initial_nonce = provider.get_transaction_count(from).await.unwrap(); let tx = TransactionRequest::default().to(to).value(amount).from(from); let tx = WithOtherFields::new(tx); let tx = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); assert_eq!(tx.transaction_index, Some(0)); - let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); + let nonce = provider.get_transaction_count(from).await.unwrap(); assert_eq!(nonce, initial_nonce + 1); - let to_balance = provider.get_balance(to, BlockId::latest()).await.unwrap(); + let to_balance = provider.get_balance(to).await.unwrap(); assert_eq!(balance_before.saturating_add(amount), to_balance); api.anvil_reset(Some(Forking { json_rpc_url: None, block_number: Some(block_number) })) .await @@ -208,11 +208,11 @@ async fn test_fork_reset() { // reset block number assert_eq!(block_number, provider.get_block_number().await.unwrap()); - let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); + let nonce = provider.get_transaction_count(from).await.unwrap(); assert_eq!(nonce, initial_nonce); - let balance = provider.get_balance(from, BlockId::latest()).await.unwrap(); + let balance = provider.get_balance(from).await.unwrap(); assert_eq!(balance, handle.genesis_balance()); - let balance = provider.get_balance(to, BlockId::latest()).await.unwrap(); + let balance = provider.get_balance(to).await.unwrap(); assert_eq!(balance, handle.genesis_balance()); // reset to latest @@ -232,7 +232,7 @@ async fn test_fork_reset_setup() { let block_number = provider.get_block_number().await.unwrap(); assert_eq!(block_number, 0); - let local_balance = provider.get_balance(dead_addr, BlockId::latest()).await.unwrap(); + let local_balance = provider.get_balance(dead_addr).await.unwrap(); assert_eq!(local_balance, U256::ZERO); api.anvil_reset(Some(Forking { @@ -245,7 +245,7 @@ async fn test_fork_reset_setup() { let block_number = provider.get_block_number().await.unwrap(); assert_eq!(block_number, BLOCK_NUMBER); - let remote_balance = provider.get_balance(dead_addr, BlockId::latest()).await.unwrap(); + let remote_balance = provider.get_balance(dead_addr).await.unwrap(); assert_eq!(remote_balance, U256::from(DEAD_BALANCE_AT_BLOCK_NUMBER)); } @@ -260,8 +260,8 @@ async fn test_fork_snapshotting() { let to = accounts[1].address(); let block_number = provider.get_block_number().await.unwrap(); - let initial_nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); - let balance_before = provider.get_balance(to, BlockId::latest()).await.unwrap(); + let initial_nonce = provider.get_transaction_count(from).await.unwrap(); + let balance_before = provider.get_balance(to).await.unwrap(); let amount = handle.genesis_balance().checked_div(U256::from(2u64)).unwrap(); let provider = handle.http_provider(); @@ -272,18 +272,18 @@ async fn test_fork_snapshotting() { let provider = handle.http_provider(); - let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); + let nonce = provider.get_transaction_count(from).await.unwrap(); assert_eq!(nonce, initial_nonce + 1); - let to_balance = provider.get_balance(to, BlockId::latest()).await.unwrap(); + let to_balance = provider.get_balance(to).await.unwrap(); assert_eq!(balance_before.saturating_add(amount), to_balance); assert!(api.evm_revert(snapshot).await.unwrap()); - let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); + let nonce = provider.get_transaction_count(from).await.unwrap(); assert_eq!(nonce, initial_nonce); - let balance = provider.get_balance(from, BlockId::latest()).await.unwrap(); + let balance = provider.get_balance(from).await.unwrap(); assert_eq!(balance, handle.genesis_balance()); - let balance = provider.get_balance(to, BlockId::latest()).await.unwrap(); + let balance = provider.get_balance(to).await.unwrap(); assert_eq!(balance, handle.genesis_balance()); assert_eq!(block_number, provider.get_block_number().await.unwrap()); } @@ -300,8 +300,8 @@ async fn test_fork_snapshotting_repeated() { let to = accounts[1].address(); let block_number = provider.get_block_number().await.unwrap(); - let initial_nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); - let balance_before = provider.get_balance(to, BlockId::latest()).await.unwrap(); + let initial_nonce = provider.get_transaction_count(from).await.unwrap(); + let balance_before = provider.get_balance(to).await.unwrap(); let amount = handle.genesis_balance().checked_div(U256::from(92u64)).unwrap(); let tx = TransactionRequest::default().to(to).value(amount).from(from); @@ -309,20 +309,20 @@ async fn test_fork_snapshotting_repeated() { let tx_provider = handle.http_provider(); let _ = tx_provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); - let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); + let nonce = provider.get_transaction_count(from).await.unwrap(); assert_eq!(nonce, initial_nonce + 1); - let to_balance = provider.get_balance(to, BlockId::latest()).await.unwrap(); + let to_balance = provider.get_balance(to).await.unwrap(); assert_eq!(balance_before.saturating_add(amount), to_balance); let _second_snapshot = api.evm_snapshot().await.unwrap(); assert!(api.evm_revert(snapshot).await.unwrap()); - let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); + let nonce = provider.get_transaction_count(from).await.unwrap(); assert_eq!(nonce, initial_nonce); - let balance = provider.get_balance(from, BlockId::latest()).await.unwrap(); + let balance = provider.get_balance(from).await.unwrap(); assert_eq!(balance, handle.genesis_balance()); - let balance = provider.get_balance(to, BlockId::latest()).await.unwrap(); + let balance = provider.get_balance(to).await.unwrap(); assert_eq!(balance, handle.genesis_balance()); assert_eq!(block_number, provider.get_block_number().await.unwrap()); @@ -348,8 +348,8 @@ async fn test_fork_snapshotting_blocks() { let to = accounts[1].address(); let block_number = provider.get_block_number().await.unwrap(); - let initial_nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); - let balance_before = provider.get_balance(to, BlockId::latest()).await.unwrap(); + let initial_nonce = provider.get_transaction_count(from).await.unwrap(); + let balance_before = provider.get_balance(to).await.unwrap(); let amount = handle.genesis_balance().checked_div(U256::from(2u64)).unwrap(); // send the transaction @@ -360,29 +360,26 @@ async fn test_fork_snapshotting_blocks() { let block_number_after = provider.get_block_number().await.unwrap(); assert_eq!(block_number_after, block_number + 1); - let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); + let nonce = provider.get_transaction_count(from).await.unwrap(); assert_eq!(nonce, initial_nonce + 1); - let to_balance = provider.get_balance(to, BlockId::latest()).await.unwrap(); + let to_balance = provider.get_balance(to).await.unwrap(); assert_eq!(balance_before.saturating_add(amount), to_balance); // revert snapshot assert!(api.evm_revert(snapshot).await.unwrap()); - assert_eq!( - initial_nonce, - provider.get_transaction_count(from, BlockId::latest()).await.unwrap() - ); + assert_eq!(initial_nonce, provider.get_transaction_count(from).await.unwrap()); let block_number_after = provider.get_block_number().await.unwrap(); assert_eq!(block_number_after, block_number); // repeat transaction let _ = provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); - let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); + let nonce = provider.get_transaction_count(from).await.unwrap(); assert_eq!(nonce, initial_nonce + 1); // revert again: nothing to revert since snapshot gone assert!(!api.evm_revert(snapshot).await.unwrap()); - let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); + let nonce = provider.get_transaction_count(from).await.unwrap(); assert_eq!(nonce, initial_nonce + 1); let block_number_after = provider.get_block_number().await.unwrap(); assert_eq!(block_number_after, block_number + 1); @@ -398,11 +395,11 @@ async fn test_separate_states() { let addr: Address = "000000000000000000000000000000000000dEaD".parse().unwrap(); - let remote_balance = provider.get_balance(addr, BlockId::latest()).await.unwrap(); + let remote_balance = provider.get_balance(addr).await.unwrap(); assert_eq!(remote_balance, U256::from(12556104082473169733500u128)); api.anvil_set_balance(addr, U256::from(1337u64)).await.unwrap(); - let balance = provider.get_balance(addr, BlockId::latest()).await.unwrap(); + let balance = provider.get_balance(addr).await.unwrap(); assert_eq!(balance, U256::from(1337u64)); let fork = api.get_fork().unwrap(); @@ -448,44 +445,32 @@ async fn can_reset_properly() { let origin_nonce = 1u64; origin_api.anvil_set_nonce(account, U256::from(origin_nonce)).await.unwrap(); - assert_eq!( - origin_nonce, - origin_provider.get_transaction_count(account, BlockId::latest()).await.unwrap() - ); + assert_eq!(origin_nonce, origin_provider.get_transaction_count(account).await.unwrap()); let (fork_api, fork_handle) = spawn(NodeConfig::test().with_eth_rpc_url(Some(origin_handle.http_endpoint()))).await; let fork_provider = fork_handle.http_provider(); let fork_tx_provider = http_provider(&fork_handle.http_endpoint()); - assert_eq!( - origin_nonce, - fork_provider.get_transaction_count(account, BlockId::latest()).await.unwrap() - ); + assert_eq!(origin_nonce, fork_provider.get_transaction_count(account).await.unwrap()); let to = Address::random(); - let to_balance = fork_provider.get_balance(to, BlockId::latest()).await.unwrap(); + let to_balance = fork_provider.get_balance(to).await.unwrap(); let tx = TransactionRequest::default().from(account).to(to).value(U256::from(1337u64)); let tx = WithOtherFields::new(tx); let tx = fork_tx_provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); // nonce incremented by 1 - assert_eq!( - origin_nonce + 1, - fork_provider.get_transaction_count(account, BlockId::latest()).await.unwrap() - ); + assert_eq!(origin_nonce + 1, fork_provider.get_transaction_count(account).await.unwrap()); // resetting to origin state fork_api.anvil_reset(Some(Forking::default())).await.unwrap(); // nonce reset to origin - assert_eq!( - origin_nonce, - fork_provider.get_transaction_count(account, BlockId::latest()).await.unwrap() - ); + assert_eq!(origin_nonce, fork_provider.get_transaction_count(account).await.unwrap()); // balance is reset - assert_eq!(to_balance, fork_provider.get_balance(to, BlockId::latest()).await.unwrap()); + assert_eq!(to_balance, fork_provider.get_balance(to).await.unwrap()); // tx does not exist anymore assert!(fork_tx_provider.get_transaction_by_hash(tx.transaction_hash).await.unwrap().is_none()) @@ -587,7 +572,7 @@ async fn test_fork_can_send_tx() { api.anvil_set_balance(signer, U256::MAX).await.unwrap(); api.anvil_impersonate_account(signer).await.unwrap(); // Added until SignerFiller for alloy-provider is fixed. - let balance = provider.get_balance(signer, BlockId::latest()).await.unwrap(); + let balance = provider.get_balance(signer).await.unwrap(); assert_eq!(balance, U256::MAX); let addr = Address::random(); @@ -597,7 +582,7 @@ async fn test_fork_can_send_tx() { // broadcast it via the eth_sendTransaction API let _ = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); - let balance = provider.get_balance(addr, BlockId::latest()).await.unwrap(); + let balance = provider.get_balance(addr).await.unwrap(); assert_eq!(balance, val); } @@ -963,7 +948,7 @@ async fn can_impersonate_in_fork() { let status = res.inner.inner.inner.receipt.status; assert!(status); - let balance = provider.get_balance(to, BlockId::latest()).await.unwrap(); + let balance = provider.get_balance(to).await.unwrap(); assert_eq!(balance, val); api.anvil_stop_impersonating_account(token_holder).await.unwrap(); diff --git a/crates/anvil/tests/it/genesis.rs b/crates/anvil/tests/it/genesis.rs index bb4e2d24b..139a2fe20 100644 --- a/crates/anvil/tests/it/genesis.rs +++ b/crates/anvil/tests/it/genesis.rs @@ -3,7 +3,6 @@ use alloy_genesis::Genesis; use alloy_primitives::{Address, U256}; use alloy_provider::Provider; -use alloy_rpc_types::BlockId; use anvil::{spawn, NodeConfig}; use std::str::FromStr; @@ -44,7 +43,7 @@ async fn can_apply_genesis() { assert_eq!(provider.get_chain_id().await.unwrap(), 19763u64); let addr: Address = Address::from_str("71562b71999873db5b286df957af199ec94617f7").unwrap(); - let balance = provider.get_balance(addr, BlockId::latest()).await.unwrap(); + let balance = provider.get_balance(addr).await.unwrap(); let expected: U256 = U256::from_str_radix("ffffffffffffffffffffffffff", 16).unwrap(); assert_eq!(balance, expected); diff --git a/crates/anvil/tests/it/optimism.rs b/crates/anvil/tests/it/optimism.rs index 7b38a3e5c..60d506aee 100644 --- a/crates/anvil/tests/it/optimism.rs +++ b/crates/anvil/tests/it/optimism.rs @@ -1,7 +1,7 @@ //! Tests for OP chain support. use crate::utils::http_provider_with_signer; -use alloy_eips::{eip2718::Encodable2718, BlockId}; +use alloy_eips::eip2718::Encodable2718; use alloy_network::{EthereumSigner, TransactionBuilder}; use alloy_primitives::{b256, U128, U256}; use alloy_provider::Provider; @@ -53,7 +53,7 @@ async fn test_send_value_deposit_transaction() { let provider = http_provider_with_signer(&handle.http_endpoint(), signer); let send_value = U256::from(1234); - let before_balance_to = provider.get_balance(to, BlockId::latest()).await.unwrap(); + let before_balance_to = provider.get_balance(to).await.unwrap(); let tx = TransactionRequest::default() .with_from(from) @@ -83,7 +83,7 @@ async fn test_send_value_deposit_transaction() { assert_eq!(receipt.to, Some(to)); // the recipient should have received the value - let after_balance_to = provider.get_balance(to, BlockId::latest()).await.unwrap(); + let after_balance_to = provider.get_balance(to).await.unwrap(); assert_eq!(after_balance_to, before_balance_to + send_value); } @@ -101,7 +101,7 @@ async fn test_send_value_raw_deposit_transaction() { let provider = http_provider_with_signer(&handle.http_endpoint(), signer.clone()); let send_value = U256::from(1234); - let before_balance_to = provider.get_balance(to, BlockId::latest()).await.unwrap(); + let before_balance_to = provider.get_balance(to).await.unwrap(); let tx = TransactionRequest::default() .with_chain_id(31337) @@ -140,6 +140,6 @@ async fn test_send_value_raw_deposit_transaction() { assert_eq!(receipt.to, Some(to)); // the recipient should have received the value - let after_balance_to = provider.get_balance(to, BlockId::latest()).await.unwrap(); + let after_balance_to = provider.get_balance(to).await.unwrap(); assert_eq!(after_balance_to, before_balance_to + send_value); } diff --git a/crates/anvil/tests/it/sign.rs b/crates/anvil/tests/it/sign.rs index 1417fec52..c279619f2 100644 --- a/crates/anvil/tests/it/sign.rs +++ b/crates/anvil/tests/it/sign.rs @@ -303,7 +303,7 @@ async fn can_sign_transaction() { // sign it via the eth_signTransaction API let signed_tx = api.sign_transaction(tx).await.unwrap(); - assert_eq!(signed_tx, "0x02f868827a690a65648252089470997970c51812dc3a010c7d01b50e0d17dc79c88203e980c082f4f6a0e4de88aefcf87ccb04466e60de66a83192e46aa26177d5ea35efbfd43fd0ecdca00e3148e0e8e0b9a6f9b329efd6e30c4a461920f3a27497be3dbefaba996601da"); + assert_eq!(signed_tx, "0x02f866827a690a65648252089470997970c51812dc3a010c7d01b50e0d17dc79c88203e980c001a0e4de88aefcf87ccb04466e60de66a83192e46aa26177d5ea35efbfd43fd0ecdca00e3148e0e8e0b9a6f9b329efd6e30c4a461920f3a27497be3dbefaba996601da"); } #[tokio::test(flavor = "multi_thread")] diff --git a/crates/anvil/tests/it/transaction.rs b/crates/anvil/tests/it/transaction.rs index eac152a51..cf9845d6f 100644 --- a/crates/anvil/tests/it/transaction.rs +++ b/crates/anvil/tests/it/transaction.rs @@ -25,10 +25,10 @@ async fn can_transfer_eth() { let from = accounts[0].address(); let to = accounts[1].address(); - let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); + let nonce = provider.get_transaction_count(from).await.unwrap(); assert!(nonce == 0); - let balance_before = provider.get_balance(to, BlockId::latest()).await.unwrap(); + let balance_before = provider.get_balance(to).await.unwrap(); let amount = handle.genesis_balance().checked_div(U256::from(2u64)).unwrap(); @@ -44,11 +44,11 @@ async fn can_transfer_eth() { assert_eq!(tx.block_number, Some(1)); assert_eq!(tx.transaction_index, Some(0)); - let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); + let nonce = provider.get_transaction_count(from).await.unwrap(); assert_eq!(nonce, 1); - let to_balance = provider.get_balance(to, BlockId::latest()).await.unwrap(); + let to_balance = provider.get_balance(to).await.unwrap(); assert_eq!(balance_before.saturating_add(amount), to_balance); } @@ -104,7 +104,7 @@ async fn can_respect_nonces() { let from = accounts[0].address(); let to = accounts[1].address(); - let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); + let nonce = provider.get_transaction_count(from).await.unwrap(); let amount = handle.genesis_balance().checked_div(U256::from(3u64)).unwrap(); let tx = TransactionRequest::default().to(to).value(amount).from(from).nonce(nonce + 1); @@ -150,7 +150,7 @@ async fn can_replace_transaction() { let from = accounts[0].address(); let to = accounts[1].address(); - let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); + let nonce = provider.get_transaction_count(from).await.unwrap(); let gas_price = provider.get_gas_price().await.unwrap(); let amount = handle.genesis_balance().checked_div(U256::from(3u64)).unwrap(); @@ -240,7 +240,7 @@ async fn can_reject_underpriced_replacement() { let from = accounts[0].address(); let to = accounts[1].address(); - let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); + let nonce = provider.get_transaction_count(from).await.unwrap(); let gas_price = provider.get_gas_price().await.unwrap(); let amount = handle.genesis_balance().checked_div(U256::from(3u64)).unwrap(); @@ -446,7 +446,7 @@ async fn can_deploy_get_code() { .await .unwrap(); - let code = provider.get_code_at(greeter_addr, BlockId::latest()).await.unwrap(); + let code = provider.get_code_at(greeter_addr).await.unwrap(); assert!(!code.as_ref().is_empty()); } @@ -551,7 +551,7 @@ async fn can_handle_multiple_concurrent_transfers_with_same_nonce() { let from = accounts[0].address(); let to = accounts[1].address(); - let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); + let nonce = provider.get_transaction_count(from).await.unwrap(); // explicitly set the nonce let tx = TransactionRequest::default() @@ -578,7 +578,7 @@ async fn can_handle_multiple_concurrent_transfers_with_same_nonce() { join_all(tasks).await.into_iter().filter(|res| res.as_ref().is_ok()).count(); assert_eq!(successful_tx, 1); - assert_eq!(provider.get_transaction_count(from, BlockId::latest()).await.unwrap(), 1u64); + assert_eq!(provider.get_transaction_count(from).await.unwrap(), 1u64); } #[tokio::test(flavor = "multi_thread")] @@ -588,7 +588,7 @@ async fn can_handle_multiple_concurrent_deploys_with_same_nonce() { let wallet = handle.dev_wallets().next().unwrap(); let from = wallet.address(); - let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); + let nonce = provider.get_transaction_count(from).await.unwrap(); let mut tasks = Vec::new(); @@ -617,7 +617,7 @@ async fn can_handle_multiple_concurrent_deploys_with_same_nonce() { let successful_tx = join_all(tasks).await.into_iter().filter(|res| res.as_ref().unwrap().is_ok()).count(); assert_eq!(successful_tx, 1); - assert_eq!(provider.get_transaction_count(from, BlockId::latest()).await.unwrap(), 1u64); + assert_eq!(provider.get_transaction_count(from).await.unwrap(), 1u64); } #[tokio::test(flavor = "multi_thread")] @@ -631,7 +631,7 @@ async fn can_handle_multiple_concurrent_transactions_with_same_nonce() { let greeter_contract = Greeter::deploy(provider.clone(), "Hello World!".to_string()).await.unwrap(); - let nonce = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); + let nonce = provider.get_transaction_count(from).await.unwrap(); let mut tasks = Vec::new(); @@ -675,7 +675,7 @@ async fn can_handle_multiple_concurrent_transactions_with_same_nonce() { let successful_tx = join_all(tasks).await.into_iter().filter(|res| res.as_ref().unwrap().is_ok()).count(); assert_eq!(successful_tx, 1); - assert_eq!(provider.get_transaction_count(from, BlockId::latest()).await.unwrap(), nonce + 1); + assert_eq!(provider.get_transaction_count(from).await.unwrap(), nonce + 1); } #[tokio::test(flavor = "multi_thread")] async fn can_get_pending_transaction() { @@ -709,7 +709,7 @@ async fn test_first_noce_is_zero() { let provider = handle.http_provider(); let from = handle.dev_wallets().next().unwrap().address(); - let nonce = provider.get_transaction_count(from, BlockId::pending()).await.unwrap(); + let nonce = provider.get_transaction_count(from).block_id(BlockId::pending()).await.unwrap(); assert_eq!(nonce, 0); } @@ -736,7 +736,7 @@ async fn can_handle_different_sender_nonce_calculation() { let tx_from_first = WithOtherFields::new(tx_from_first); let _tx = provider.send_transaction(tx_from_first).await.unwrap(); let nonce_from_first = - provider.get_transaction_count(from_first, BlockId::pending()).await.unwrap(); + provider.get_transaction_count(from_first).block_id(BlockId::pending()).await.unwrap(); assert_eq!(nonce_from_first, idx); let tx_from_second = TransactionRequest::default() @@ -746,7 +746,7 @@ async fn can_handle_different_sender_nonce_calculation() { let tx_from_second = WithOtherFields::new(tx_from_second); let _tx = provider.send_transaction(tx_from_second).await.unwrap(); let nonce_from_second = - provider.get_transaction_count(from_second, BlockId::pending()).await.unwrap(); + provider.get_transaction_count(from_second).block_id(BlockId::pending()).await.unwrap(); assert_eq!(nonce_from_second, idx); } } @@ -768,12 +768,13 @@ async fn includes_pending_tx_for_transaction_count() { TransactionRequest::default().from(from).value(U256::from(1337)).to(Address::random()); let tx = WithOtherFields::new(tx); let _tx = provider.send_transaction(tx).await.unwrap(); - let nonce = provider.get_transaction_count(from, BlockId::pending()).await.unwrap(); + let nonce = + provider.get_transaction_count(from).block_id(BlockId::pending()).await.unwrap(); assert_eq!(nonce, idx); } api.mine_one().await; - let nonce = provider.get_transaction_count(from, BlockId::pending()).await.unwrap(); + let nonce = provider.get_transaction_count(from).block_id(BlockId::pending()).await.unwrap(); assert_eq!(nonce, tx_count); } @@ -792,19 +793,20 @@ async fn can_get_historic_info() { let tx = provider.send_transaction(tx).await.unwrap(); let _ = tx.get_receipt().await.unwrap(); - let nonce_pre = provider.get_transaction_count(from, BlockId::Number(0.into())).await.unwrap(); + let nonce_pre = + provider.get_transaction_count(from).block_id(BlockId::number(0)).await.unwrap(); - let nonce_post = provider.get_transaction_count(from, BlockId::latest()).await.unwrap(); + let nonce_post = provider.get_transaction_count(from).await.unwrap(); assert!(nonce_pre < nonce_post); - let balance_pre = provider.get_balance(from, BlockId::Number(0.into())).await.unwrap(); + let balance_pre = provider.get_balance(from).block_id(BlockId::number(0)).await.unwrap(); - let balance_post = provider.get_balance(from, BlockId::latest()).await.unwrap(); + let balance_post = provider.get_balance(from).await.unwrap(); assert!(balance_post < balance_pre); - let to_balance = provider.get_balance(to, BlockId::latest()).await.unwrap(); + let to_balance = provider.get_balance(to).await.unwrap(); assert_eq!(balance_pre.saturating_add(amount), to_balance); } @@ -962,7 +964,7 @@ async fn test_tx_access_list() { .to(*simple_storage.address()) .with_input(set_value_calldata.to_owned()); let set_value_tx = WithOtherFields::new(set_value_tx); - let access_list = provider.create_access_list(&set_value_tx, BlockId::latest()).await.unwrap(); + let access_list = provider.create_access_list(&set_value_tx).await.unwrap(); // let set_value_tx = simple_storage.set_value("bar".to_string()).from(sender).tx; // let access_list = client.create_access_list(&set_value_tx, None).await.unwrap(); assert_access_list_eq( @@ -989,7 +991,7 @@ async fn test_tx_access_list() { .to(*multicall.address()) .with_input(call_tx_data.to_owned()); let call_tx = WithOtherFields::new(call_tx); - let access_list = provider.create_access_list(&call_tx, BlockId::latest()).await.unwrap(); + let access_list = provider.create_access_list(&call_tx).await.unwrap(); assert_access_list_eq( access_list.access_list, AccessList::from(vec![AccessListItem { address: other_acc, storage_keys: vec![] }]), @@ -1009,7 +1011,7 @@ async fn test_tx_access_list() { .to(*multicall.address()) .with_input(subcall_tx_calldata.to_owned()); let subcall_tx = WithOtherFields::new(subcall_tx); - let access_list = provider.create_access_list(&subcall_tx, BlockId::latest()).await.unwrap(); + let access_list = provider.create_access_list(&subcall_tx).await.unwrap(); assert_access_list_eq( access_list.access_list, // H256::from_uint(&(1u64.into())), diff --git a/crates/anvil/tests/it/wsapi.rs b/crates/anvil/tests/it/wsapi.rs index f68b15312..ebe853a7d 100644 --- a/crates/anvil/tests/it/wsapi.rs +++ b/crates/anvil/tests/it/wsapi.rs @@ -2,7 +2,6 @@ use alloy_primitives::U256; use alloy_provider::Provider; -use alloy_rpc_types::BlockId; use anvil::{spawn, NodeConfig}; #[tokio::test(flavor = "multi_thread")] @@ -24,7 +23,7 @@ async fn can_dev_get_balance_ws() { let genesis_balance = handle.genesis_balance(); for acc in handle.genesis_accounts() { - let balance = provider.get_balance(acc, BlockId::latest()).await.unwrap(); + let balance = provider.get_balance(acc).await.unwrap(); assert_eq!(balance, genesis_balance); } } diff --git a/crates/cast/bin/cmd/estimate.rs b/crates/cast/bin/cmd/estimate.rs index 33f375f9c..c000a79c1 100644 --- a/crates/cast/bin/cmd/estimate.rs +++ b/crates/cast/bin/cmd/estimate.rs @@ -1,7 +1,7 @@ use alloy_network::TransactionBuilder; use alloy_primitives::U256; use alloy_provider::Provider; -use alloy_rpc_types::{BlockId, TransactionRequest, WithOtherFields}; +use alloy_rpc_types::{TransactionRequest, WithOtherFields}; use clap::Parser; use eyre::Result; use foundry_cli::{ @@ -128,7 +128,7 @@ impl EstimateArgs { req.set_input(data); - let gas = provider.estimate_gas(&req, BlockId::latest()).await?; + let gas = provider.estimate_gas(&req).await?; println!("{gas}"); Ok(()) } diff --git a/crates/cast/bin/cmd/mktx.rs b/crates/cast/bin/cmd/mktx.rs index 6b7b08c6f..f8f6d9fb2 100644 --- a/crates/cast/bin/cmd/mktx.rs +++ b/crates/cast/bin/cmd/mktx.rs @@ -2,7 +2,6 @@ use crate::tx; use alloy_network::{eip2718::Encodable2718, EthereumSigner, TransactionBuilder}; use alloy_primitives::U64; use alloy_provider::Provider; -use alloy_rpc_types::BlockId; use alloy_signer::Signer; use clap::Parser; use eyre::Result; @@ -90,8 +89,7 @@ impl MakeTxArgs { tx::validate_from_address(eth.wallet.from, from)?; if resend { - tx.nonce = - Some(U64::from(provider.get_transaction_count(from, BlockId::latest()).await?)); + tx.nonce = Some(U64::from(provider.get_transaction_count(from).await?)); } let provider = get_provider(&config)?; diff --git a/crates/cast/bin/cmd/send.rs b/crates/cast/bin/cmd/send.rs index 3a6de10a9..06c8b99f2 100644 --- a/crates/cast/bin/cmd/send.rs +++ b/crates/cast/bin/cmd/send.rs @@ -2,7 +2,6 @@ use crate::tx; use alloy_network::{AnyNetwork, EthereumSigner}; use alloy_primitives::{Address, U64}; use alloy_provider::{Provider, ProviderBuilder}; -use alloy_rpc_types::BlockId; use alloy_signer::Signer; use alloy_transport::Transport; use cast::Cast; @@ -150,9 +149,7 @@ impl SendTxArgs { } if resend { - tx.nonce = Some(U64::from( - provider.get_transaction_count(config.sender, BlockId::latest()).await?, - )); + tx.nonce = Some(U64::from(provider.get_transaction_count(config.sender).await?)); } cast_send( @@ -183,8 +180,7 @@ impl SendTxArgs { tx::validate_from_address(eth.wallet.from, from)?; if resend { - tx.nonce = - Some(U64::from(provider.get_transaction_count(from, BlockId::latest()).await?)); + tx.nonce = Some(U64::from(provider.get_transaction_count(from).await?)); } let signer = EthereumSigner::from(signer); diff --git a/crates/cast/bin/cmd/storage.rs b/crates/cast/bin/cmd/storage.rs index 6197e40c9..a59d93c5d 100644 --- a/crates/cast/bin/cmd/storage.rs +++ b/crates/cast/bin/cmd/storage.rs @@ -95,7 +95,8 @@ impl StorageArgs { // No slot was provided // Get deployed bytecode at given address - let address_code = provider.get_code_at(address, block.unwrap_or_default()).await?; + let address_code = + provider.get_code_at(address).block_id(block.unwrap_or_default()).await?; if address_code.is_empty() { eyre::bail!("Provided address has no deployed code and thus no storage"); } @@ -237,8 +238,10 @@ async fn fetch_storage_slots, T: Transport + Clone>( ) -> Result> { let requests = layout.storage.iter().map(|storage_slot| async { let slot = B256::from(U256::from_str(&storage_slot.slot)?); - let raw_slot_value = - provider.get_storage_at(address, slot.into(), block.unwrap_or_default()).await?; + let raw_slot_value = provider + .get_storage_at(address, slot.into()) + .block_id(block.unwrap_or_default()) + .await?; let value = StorageValue { slot, raw_slot_value: raw_slot_value.into() }; diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index 30bb1205e..0af23e200 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -351,7 +351,8 @@ async fn main() -> Result<()> { let provider = utils::get_provider(&config)?; let address = address.resolve(&provider).await?; let value = provider - .get_proof(address, slots.into_iter().collect(), block.unwrap_or(BlockId::latest())) + .get_proof(address, slots.into_iter().collect()) + .block_id(block.unwrap_or_default()) .await?; println!("{}", serde_json::to_string(&value)?); } diff --git a/crates/cast/bin/tx.rs b/crates/cast/bin/tx.rs index 82b1d8b4d..d4c9e205c 100644 --- a/crates/cast/bin/tx.rs +++ b/crates/cast/bin/tx.rs @@ -3,7 +3,7 @@ use alloy_json_abi::Function; use alloy_network::{AnyNetwork, TransactionBuilder}; use alloy_primitives::{Address, Bytes, U256}; use alloy_provider::Provider; -use alloy_rpc_types::{BlockId, TransactionRequest, WithOtherFields}; +use alloy_rpc_types::{TransactionRequest, WithOtherFields}; use alloy_transport::Transport; use eyre::Result; use foundry_cli::{opts::TransactionOpts, utils::parse_function_args}; @@ -90,7 +90,7 @@ pub async fn build_tx< req.set_nonce(if let Some(nonce) = tx.nonce { nonce.to() } else { - provider.get_transaction_count(from, BlockId::latest()).await? + provider.get_transaction_count(from).await? }); if tx.legacy || chain.is_legacy() { @@ -138,7 +138,7 @@ pub async fn build_tx< req.set_gas_limit(if let Some(gas_limit) = tx.gas_limit { gas_limit.to() } else { - provider.estimate_gas(&req, BlockId::latest()).await? + provider.estimate_gas(&req).await? }); Ok((req, func)) diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index e2f706b74..c83fe9aa1 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -133,8 +133,11 @@ where if res.is_empty() { // check that the recipient is a contract that can be called if let Some(TxKind::Call(addr)) = req.to { - if let Ok(code) = - self.provider.get_code_at(addr, block.unwrap_or_default()).await + if let Ok(code) = self + .provider + .get_code_at(addr) + .block_id(block.unwrap_or_default()) + .await { if code.is_empty() { eyre::bail!("contract {addr:?} does not have any code") @@ -198,7 +201,7 @@ where to_json: bool, ) -> Result { let access_list = - self.provider.create_access_list(req, block.unwrap_or(BlockId::latest())).await?; + self.provider.create_access_list(req).block_id(block.unwrap_or_default()).await?; let res = if to_json { serde_json::to_string(&access_list)? } else { @@ -220,7 +223,7 @@ where } pub async fn balance(&self, who: Address, block: Option) -> Result { - Ok(self.provider.get_balance(who, block.unwrap_or(BlockId::latest())).await?) + Ok(self.provider.get_balance(who).block_id(block.unwrap_or_default()).await?) } /// Sends a transaction to the specified address @@ -472,7 +475,7 @@ where /// # } /// ``` pub async fn nonce(&self, who: Address, block: Option) -> Result { - Ok(self.provider.get_transaction_count(who, block.unwrap_or(BlockId::latest())).await?) + Ok(self.provider.get_transaction_count(who).block_id(block.unwrap_or_default()).await?) } /// # Example @@ -498,7 +501,8 @@ where B256::from_str("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc")?; let value = self .provider - .get_storage_at(who, slot.into(), block.unwrap_or(BlockId::latest())) + .get_storage_at(who, slot.into()) + .block_id(block.unwrap_or_default()) .await?; let addr = Address::from_word(value.into()); Ok(format!("{addr:?}")) @@ -527,7 +531,8 @@ where B256::from_str("0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103")?; let value = self .provider - .get_storage_at(who, slot.into(), block.unwrap_or(BlockId::latest())) + .get_storage_at(who, slot.into()) + .block_id(block.unwrap_or_default()) .await?; let addr = Address::from_word(value.into()); Ok(format!("{addr:?}")) @@ -581,10 +586,14 @@ where disassemble: bool, ) -> Result { if disassemble { - let code = self.provider.get_code_at(who, block.unwrap_or_default()).await?.to_vec(); + let code = + self.provider.get_code_at(who).block_id(block.unwrap_or_default()).await?.to_vec(); Ok(format_operations(disassemble_bytes(code)?)?) } else { - Ok(format!("{}", self.provider.get_code_at(who, block.unwrap_or_default()).await?)) + Ok(format!( + "{}", + self.provider.get_code_at(who).block_id(block.unwrap_or_default()).await? + )) } } @@ -607,7 +616,8 @@ where /// # } /// ``` pub async fn codesize(&self, who: Address, block: Option) -> Result { - let code = self.provider.get_code_at(who, block.unwrap_or_default()).await?.to_vec(); + let code = + self.provider.get_code_at(who).block_id(block.unwrap_or_default()).await?.to_vec(); Ok(format!("{}", code.len())) } @@ -773,7 +783,8 @@ where "{:?}", B256::from( self.provider - .get_storage_at(from, slot.into(), block.unwrap_or(BlockId::latest())) + .get_storage_at(from, slot.into()) + .block_id(block.unwrap_or_default()) .await? ) )) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index a461f614d..5606a10b1 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -475,7 +475,7 @@ { "name": "gasMemoryUsed", "ty": "uint64", - "description": "The amount of gas used for memory expansion." + "description": "DEPRECATED: The amount of gas used for memory expansion. Ref: " }, { "name": "gasRefunded", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 6e2fbd278..e8d24e3c5 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -102,7 +102,7 @@ interface Vm { uint64 gasLimit; /// The total gas used. uint64 gasTotalUsed; - /// The amount of gas used for memory expansion. + /// DEPRECATED: The amount of gas used for memory expansion. Ref: uint64 gasMemoryUsed; /// The amount of gas refunded. int64 gasRefunded; diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index d40fae0ad..7eae7331b 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -402,7 +402,7 @@ impl Cheatcode for etchCall { let Self { target, newRuntimeBytecode } = self; ensure_not_precompile!(target, ccx); ccx.ecx.load_account(*target)?; - let bytecode = Bytecode::new_raw(Bytes::copy_from_slice(newRuntimeBytecode)).to_checked(); + let bytecode = Bytecode::new_raw(Bytes::copy_from_slice(newRuntimeBytecode)); ccx.ecx.journaled_state.set_code(*target, bytecode); Ok(Default::default()) } diff --git a/crates/cheatcodes/src/evm/mapping.rs b/crates/cheatcodes/src/evm/mapping.rs index f5acc4966..b506d2058 100644 --- a/crates/cheatcodes/src/evm/mapping.rs +++ b/crates/cheatcodes/src/evm/mapping.rs @@ -117,7 +117,7 @@ pub(crate) fn step(mapping_slots: &mut HashMap, interpret match interpreter.current_opcode() { opcode::KECCAK256 => { if interpreter.stack.peek(1) == Ok(U256::from(0x40)) { - let address = interpreter.contract.address; + let address = interpreter.contract.target_address; let offset = interpreter.stack.peek(0).expect("stack size > 1").saturating_to(); let data = interpreter.shared_memory.slice(offset, 0x40); let low = B256::from_slice(&data[..0x20]); @@ -128,7 +128,8 @@ pub(crate) fn step(mapping_slots: &mut HashMap, interpret } } opcode::SSTORE => { - if let Some(mapping_slots) = mapping_slots.get_mut(&interpreter.contract.address) { + if let Some(mapping_slots) = mapping_slots.get_mut(&interpreter.contract.target_address) + { if let Ok(slot) = interpreter.stack.peek(0) { mapping_slots.insert(slot.into()); } diff --git a/crates/cheatcodes/src/evm/mock.rs b/crates/cheatcodes/src/evm/mock.rs index 6a266a410..b2c46f116 100644 --- a/crates/cheatcodes/src/evm/mock.rs +++ b/crates/cheatcodes/src/evm/mock.rs @@ -56,7 +56,7 @@ impl Cheatcode for mockCall_0Call { // check Solidity might perform. let empty_bytecode = acc.info.code.as_ref().map_or(true, Bytecode::is_empty); if empty_bytecode { - let code = Bytecode::new_raw(Bytes::from_static(&[0u8])).to_checked(); + let code = Bytecode::new_raw(Bytes::from_static(&[0u8])); ccx.ecx.journaled_state.set_code(*callee, code); } diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 25e34313c..855e0374f 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -236,7 +236,7 @@ impl Cheatcodes { } e })?; - let caller = call.context.caller; + let caller = call.caller; // ensure the caller is allowed to execute cheatcodes, // but only if the backend is in forking mode @@ -391,7 +391,7 @@ impl Inspector for Cheatcodes { let key = try_or_continue!(interpreter.stack().peek(0)); storage_accesses .reads - .entry(interpreter.contract().address) + .entry(interpreter.contract().target_address) .or_default() .push(key); } @@ -401,12 +401,12 @@ impl Inspector for Cheatcodes { // An SSTORE does an SLOAD internally storage_accesses .reads - .entry(interpreter.contract().address) + .entry(interpreter.contract().target_address) .or_default() .push(key); storage_accesses .writes - .entry(interpreter.contract().address) + .entry(interpreter.contract().target_address) .or_default() .push(key); } @@ -420,7 +420,7 @@ impl Inspector for Cheatcodes { let target = try_or_continue!(interpreter.stack().peek(0)); // load balance of this account let value = ecx - .balance(interpreter.contract().address) + .balance(interpreter.contract().target_address) .map(|(b, _)| b) .unwrap_or(U256::ZERO); let account = Address::from_word(B256::from(target)); @@ -439,7 +439,7 @@ impl Inspector for Cheatcodes { forkId: ecx.db.active_fork_id().unwrap_or_default(), chainId: U256::from(ecx.env.cfg.chain_id), }, - accessor: interpreter.contract().address, + accessor: interpreter.contract().target_address, account, kind: crate::Vm::AccountAccessKind::SelfDestruct, initialized, @@ -464,7 +464,7 @@ impl Inspector for Cheatcodes { match interpreter.current_opcode() { opcode::SLOAD => { let key = try_or_continue!(interpreter.stack().peek(0)); - let address = interpreter.contract().address; + let address = interpreter.contract().target_address; // Try to include present value for informational purposes, otherwise assume // it's not set (zero value) @@ -476,7 +476,7 @@ impl Inspector for Cheatcodes { } } let access = crate::Vm::StorageAccess { - account: interpreter.contract().address, + account: interpreter.contract().target_address, slot: key.into(), isWrite: false, previousValue: present_value.into(), @@ -492,7 +492,7 @@ impl Inspector for Cheatcodes { opcode::SSTORE => { let key = try_or_continue!(interpreter.stack().peek(0)); let value = try_or_continue!(interpreter.stack().peek(1)); - let address = interpreter.contract().address; + let address = interpreter.contract().target_address; // Try to load the account and the slot's previous value, otherwise, assume it's // not set (zero value) let mut previous_value = U256::ZERO; @@ -546,7 +546,7 @@ impl Inspector for Cheatcodes { forkId: ecx.db.active_fork_id().unwrap_or_default(), chainId: U256::from(ecx.env.cfg.chain_id), }, - accessor: interpreter.contract().address, + accessor: interpreter.contract().target_address, account: address, kind, initialized, @@ -790,7 +790,7 @@ impl Inspector for Cheatcodes { } } - if call.contract == CHEATCODE_ADDRESS { + if call.target_address == CHEATCODE_ADDRESS { return match self.apply_cheatcode(ecx, call) { Ok(retdata) => Some(CallOutcome { result: InterpreterResult { @@ -813,14 +813,15 @@ impl Inspector for Cheatcodes { let ecx = &mut ecx.inner; - if call.contract == HARDHAT_CONSOLE_ADDRESS { + if call.target_address == HARDHAT_CONSOLE_ADDRESS { return None } // Handle expected calls // Grab the different calldatas expected. - if let Some(expected_calls_for_target) = self.expected_calls.get_mut(&(call.contract)) { + if let Some(expected_calls_for_target) = self.expected_calls.get_mut(&(call.target_address)) + { // Match every partial/full calldata for (calldata, (expected, actual_count)) in expected_calls_for_target { // Increment actual times seen if... @@ -831,7 +832,7 @@ impl Inspector for Cheatcodes { // The value matches, if provided expected .value - .map_or(true, |value| value == call.transfer.value) && + .map_or(true, |value| Some(value) == call.transfer_value()) && // The gas matches, if provided expected.gas.map_or(true, |gas| gas == call.gas_limit) && // The minimum gas matches, if provided @@ -843,17 +844,15 @@ impl Inspector for Cheatcodes { } // Handle mocked calls - if let Some(mocks) = self.mocked_calls.get(&call.contract) { - let ctx = MockCallDataContext { - calldata: call.input.clone(), - value: Some(call.transfer.value), - }; + if let Some(mocks) = self.mocked_calls.get(&call.target_address) { + let ctx = + MockCallDataContext { calldata: call.input.clone(), value: call.transfer_value() }; if let Some(return_data) = mocks.get(&ctx).or_else(|| { mocks .iter() .find(|(mock, _)| { call.input.get(..mock.calldata.len()) == Some(&mock.calldata[..]) && - mock.value.map_or(true, |value| value == call.transfer.value) + mock.value.map_or(true, |value| Some(value) == call.transfer_value()) }) .map(|(_, v)| v) }) { @@ -870,15 +869,12 @@ impl Inspector for Cheatcodes { // Apply our prank if let Some(prank) = &self.prank { - if ecx.journaled_state.depth() >= prank.depth && - call.context.caller == prank.prank_caller - { + if ecx.journaled_state.depth() >= prank.depth && call.caller == prank.prank_caller { let mut prank_applied = false; // At the target depth we set `msg.sender` if ecx.journaled_state.depth() == prank.depth { - call.context.caller = prank.new_caller; - call.transfer.source = prank.new_caller; + call.caller = prank.new_caller; prank_applied = true; } @@ -904,15 +900,14 @@ impl Inspector for Cheatcodes { // We do this because any subsequent contract calls *must* exist on chain and // we only want to grab *this* call, not internal ones if ecx.journaled_state.depth() == broadcast.depth && - call.context.caller == broadcast.original_caller + call.caller == broadcast.original_caller { // At the target depth we set `msg.sender` & tx.origin. // We are simulating the caller as being an EOA, so *both* must be set to the // broadcast.origin. ecx.env.tx.caller = broadcast.new_origin; - call.context.caller = broadcast.new_origin; - call.transfer.source = broadcast.new_origin; + call.caller = broadcast.new_origin; // Add a `legacy` transaction to the VecDeque. We use a legacy transaction here // because we only need the from, to, value, and data. We can later change this // into 1559, in the cli package, relatively easily once we @@ -938,8 +933,8 @@ impl Inspector for Cheatcodes { rpc: ecx.db.active_fork_url(), transaction: TransactionRequest { from: Some(broadcast.new_origin), - to: Some(TxKind::from(Some(call.contract))), - value: Some(call.transfer.value), + 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 { @@ -979,14 +974,15 @@ impl Inspector for Cheatcodes { let initialized; let old_balance; // TODO: use ecx.load_account - if let Ok((acc, _)) = ecx.journaled_state.load_account(call.contract, &mut ecx.db) { + if let Ok((acc, _)) = ecx.journaled_state.load_account(call.target_address, &mut ecx.db) + { initialized = acc.info.exists(); old_balance = acc.info.balance; } else { initialized = false; old_balance = U256::ZERO; } - let kind = match call.context.scheme { + let kind = match call.scheme { CallScheme::Call => crate::Vm::AccountAccessKind::Call, CallScheme::CallCode => crate::Vm::AccountAccessKind::CallCode, CallScheme::DelegateCall => crate::Vm::AccountAccessKind::DelegateCall, @@ -1002,13 +998,13 @@ impl Inspector for Cheatcodes { forkId: ecx.db.active_fork_id().unwrap_or_default(), chainId: U256::from(ecx.env.cfg.chain_id), }, - accessor: call.context.caller, - account: call.contract, + accessor: call.caller, + account: call.bytecode_address, kind, initialized, oldBalance: old_balance, newBalance: U256::ZERO, // updated on call_end - value: call.transfer.value, + value: call.call_value(), data: call.input.clone(), reverted: false, deployedCode: Bytes::new(), @@ -1027,8 +1023,8 @@ impl Inspector for Cheatcodes { mut outcome: CallOutcome, ) -> CallOutcome { let ecx = &mut ecx.inner; - let cheatcode_call = - call.contract == CHEATCODE_ADDRESS || call.contract == HARDHAT_CONSOLE_ADDRESS; + let cheatcode_call = call.target_address == CHEATCODE_ADDRESS || + call.target_address == HARDHAT_CONSOLE_ADDRESS; // Clean up pranks/broadcasts if it's not a cheatcode call end. We shouldn't do // it for cheatcode calls because they are not appplied for cheatcodes in the `call` hook. @@ -1115,15 +1111,10 @@ impl Inspector for Cheatcodes { // retrieve the gas usage of the last call. let gas = outcome.result.gas; self.last_call_gas = Some(crate::Vm::Gas { - // The gas limit of the call. gasLimit: gas.limit(), - // The total gas used. gasTotalUsed: gas.spent(), - // The amount of gas used for memory expansion. - gasMemoryUsed: gas.memory(), - // The amount of gas refunded. + gasMemoryUsed: 0, gasRefunded: gas.refunded(), - // The amount of gas remaining. gasRemaining: gas.remaining(), }); @@ -1152,7 +1143,7 @@ impl Inspector for Cheatcodes { if call_access.depth == ecx.journaled_state.depth() { // TODO: use ecx.load_account if let Ok((acc, _)) = - ecx.journaled_state.load_account(call.contract, &mut ecx.db) + ecx.journaled_state.load_account(call.target_address, &mut ecx.db) { debug_assert!(access_is_call(call_access.kind)); call_access.newBalance = acc.info.balance; @@ -1220,10 +1211,10 @@ impl Inspector for Cheatcodes { // `Stop` we check if the contract actually exists on the active fork if ecx.db.is_forked_mode() && outcome.result.result == InstructionResult::Stop && - call.contract != test_contract + call.target_address != test_contract { self.fork_revert_diagnostic = - ecx.db.diagnose_revert(call.contract, &ecx.journaled_state); + ecx.db.diagnose_revert(call.target_address, &ecx.journaled_state); } } diff --git a/crates/debugger/src/tui/mod.rs b/crates/debugger/src/tui/mod.rs index 1fac3d051..d57c6e7a2 100644 --- a/crates/debugger/src/tui/mod.rs +++ b/crates/debugger/src/tui/mod.rs @@ -13,7 +13,6 @@ use ratatui::{ backend::{Backend, CrosstermBackend}, Terminal, }; -use revm::primitives::SpecId; use std::{ collections::{BTreeMap, HashMap}, io, @@ -71,8 +70,8 @@ impl Debugger { Some(( contract_name.to_owned(), ( - PcIcMap::new(SpecId::LATEST, contract.bytecode.bytes()?), - PcIcMap::new(SpecId::LATEST, contract.deployed_bytecode.bytes()?), + PcIcMap::new(contract.bytecode.bytes()?), + PcIcMap::new(contract.deployed_bytecode.bytes()?), ), )) }) diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index fd4eeba7f..baeb1145b 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -17,7 +17,7 @@ use revm::{ inspectors::NoOpInspector, precompile::{PrecompileSpecId, Precompiles}, primitives::{ - Account, AccountInfo, Bytecode, CreateScheme, Env, EnvWithHandlerCfg, HashMap as Map, Log, + Account, AccountInfo, Bytecode, Env, EnvWithHandlerCfg, HashMap as Map, Log, ResultAndState, SpecId, State, StorageSlot, TransactTo, KECCAK_EMPTY, }, Database, DatabaseCommit, JournaledState, @@ -761,13 +761,7 @@ impl Backend { let test_contract = match env.tx.transact_to { TransactTo::Call(to) => to, - TransactTo::Create(CreateScheme::Create) => { - env.tx.caller.create(env.tx.nonce.unwrap_or_default()) - } - TransactTo::Create(CreateScheme::Create2 { salt }) => { - let code_hash = B256::from_slice(keccak256(&env.tx.data).as_slice()); - env.tx.caller.create2(B256::from(salt), code_hash) - } + TransactTo::Create => env.tx.caller.create(env.tx.nonce.unwrap_or_default()), }; self.set_test_contract(test_contract); } diff --git a/crates/evm/core/src/fork/backend.rs b/crates/evm/core/src/fork/backend.rs index 960f3779f..4dda2a994 100644 --- a/crates/evm/core/src/fork/backend.rs +++ b/crates/evm/core/src/fork/backend.rs @@ -22,6 +22,7 @@ use revm::{ use rustc_hash::FxHashMap; use std::{ collections::{hash_map::Entry, HashMap, VecDeque}, + future::IntoFuture, marker::PhantomData, pin::Pin, sync::{ @@ -187,10 +188,13 @@ where trace!(target: "backendhandler", %address, %idx, "preparing storage request"); entry.insert(vec![listener]); let provider = self.provider.clone(); - let block_id = self.block_id.unwrap_or(BlockId::latest()); + let block_id = self.block_id.unwrap_or_default(); let fut = Box::pin(async move { - let storage = - provider.get_storage_at(address, idx, block_id).await.map_err(Into::into); + let storage = provider + .get_storage_at(address, idx) + .block_id(block_id) + .await + .map_err(Into::into); (storage, address, idx) }); self.pending_requests.push(ProviderRequest::Storage(fut)); @@ -202,11 +206,11 @@ where fn get_account_req(&self, address: Address) -> ProviderRequest { trace!(target: "backendhandler", "preparing account request, address={:?}", address); let provider = self.provider.clone(); - let block_id = self.block_id.unwrap_or(BlockId::latest()); + let block_id = self.block_id.unwrap_or_default(); let fut = Box::pin(async move { - let balance = provider.get_balance(address, block_id); - let nonce = provider.get_transaction_count(address, block_id); - let code = provider.get_code_at(address, block_id); + let balance = provider.get_balance(address).block_id(block_id).into_future(); + let nonce = provider.get_transaction_count(address).block_id(block_id).into_future(); + let code = provider.get_code_at(address).block_id(block_id).into_future(); let resp = tokio::try_join!(balance, nonce, code).map_err(Into::into); (resp, address) }); @@ -358,7 +362,7 @@ where let acc = AccountInfo { nonce, balance, - code: Some(Bytecode::new_raw(code).to_checked()), + code: Some(Bytecode::new_raw(code)), code_hash, }; pin.db.accounts().write().insert(addr, acc.clone()); diff --git a/crates/evm/core/src/fork/cache.rs b/crates/evm/core/src/fork/cache.rs index 607eced68..ce13f69c1 100644 --- a/crates/evm/core/src/fork/cache.rs +++ b/crates/evm/core/src/fork/cache.rs @@ -487,12 +487,12 @@ mod tests { "meta": { "cfg_env": { "chain_id": 1337, - "spec_id": "LATEST", - "perf_all_precompiles_have_balance": false, - "disable_coinbase_tip": false, "perf_analyse_created_bytecodes": "Analyse", "limit_contract_code_size": 18446744073709551615, - "memory_limit": 4294967295 + "memory_limit": 4294967295, + "disable_block_gas_limit": false, + "disable_eip3607": false, + "disable_base_fee": false }, "block_env": { "number": "0xed3ddf", @@ -500,7 +500,8 @@ mod tests { "timestamp": "0x6324bc3f", "difficulty": "0x0", "basefee": "0x2e5fda223", - "gas_limit": "0x1c9c380" + "gas_limit": "0x1c9c380", + "prevrandao": "0x0000000000000000000000000000000000000000000000000000000000000000" }, "hosts": [ "eth-mainnet.alchemyapi.io" @@ -512,11 +513,17 @@ mod tests { "nonce": 10, "code_hash": "0x3ac64c95eedf82e5d821696a12daac0e1b22c8ee18a9fd688b00cfaf14550aad", "code": { - "bytecode": "0x60806040526004361061006c5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416634555d5c9811461012b5780634558850c1461015257806348a0c8dd146101965780635c60da1b146101bf57806386070cfe146101d4575b6127107f665fd576fbbe6f247aff98f5c94a561e3f71ec2d3c988d56f12d342396c50cea6000825a10156100e15760003411361583541616156100dc576040513381523460208201527f15eeaa57c7bd188c1388020bcadc2c436ec60d647d36ef5b9eb3c742217ddee1604082a1005b600080fd5b6100e96101e9565b9050610126816000368080601f0160208091040260200160405190810160405280939291908181526020018383808284375061026c945050505050565b505050005b34801561013757600080fd5b506101406102ad565b60408051918252519081900360200190f35b34801561015e57600080fd5b5061016d6004356024356102b2565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b3480156101a257600080fd5b506101ab6102e2565b604080519115158252519081900360200190f35b3480156101cb57600080fd5b5061016d6101e9565b3480156101e057600080fd5b50610140610312565b7f3b4bf6bf3ad5000ecf0f989d5befde585c6860fea3e574a4fab4c49d1c177d9c6000527fc67454ed56db7ff90a4bb32fc9a8de1ab3174b221e5fecea22b7503a3111791f6020527f8e2ed18767e9c33b25344c240cdf92034fae56be99e2c07f3d9946d949ffede45473ffffffffffffffffffffffffffffffffffffffff1690565b600061027783610318565b151561028257600080fd5b612710905060008083516020850186855a03f43d604051816000823e8280156102a9578282f35b8282fd5b600290565b600060208181529281526040808220909352908152205473ffffffffffffffffffffffffffffffffffffffff1681565b600061030d7f665fd576fbbe6f247aff98f5c94a561e3f71ec2d3c988d56f12d342396c50cea610352565b905090565b60015481565b60008073ffffffffffffffffffffffffffffffffffffffff83161515610341576000915061034c565b823b90506000811191505b50919050565b54905600a165627a7a72305820968d404e148c1ec7bb58c8df6cbdcaad4978b93a804e00a1f0e97a5e789eacd40029000000000000000000000000000000000000000000000000000000000000000000", - "hash": "0x3ac64c95eedf82e5d821696a12daac0e1b22c8ee18a9fd688b00cfaf14550aad", - "state": { - "Checked": { - "len": 898 + "LegacyAnalyzed": { + "bytecode": "0x00", + "original_len": 0, + "jump_table": { + "order": "bitvec::order::Lsb0", + "head": { + "width": 8, + "index": 0 + }, + "bits": 1, + "data": [0] } } } @@ -557,7 +564,7 @@ mod tests { "meta": { "cfg_env": { "chain_id": 1, - "spec_id": "LATEST", + "kzg_settings": "Default", "perf_analyse_created_bytecodes": "Analyse", "limit_contract_code_size": 18446744073709551615, "memory_limit": 134217728, @@ -589,10 +596,17 @@ mod tests { "nonce": 128912, "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "code": { - "bytecode": "0x000000000000000000000000000000000000000000000000000000000000000000", - "state": { - "Checked": { - "len": 0 + "LegacyAnalyzed": { + "bytecode": "0x00", + "original_len": 0, + "jump_table": { + "order": "bitvec::order::Lsb0", + "head": { + "width": 8, + "index": 0 + }, + "bits": 1, + "data": [0] } } } diff --git a/crates/evm/core/src/ic.rs b/crates/evm/core/src/ic.rs index 9f679fc36..fa2b5efd5 100644 --- a/crates/evm/core/src/ic.rs +++ b/crates/evm/core/src/ic.rs @@ -1,7 +1,4 @@ -use revm::{ - interpreter::{opcode, opcode::spec_opcode_gas}, - primitives::SpecId, -}; +use revm::interpreter::opcode::{PUSH0, PUSH1, PUSH32}; use rustc_hash::FxHashMap; /// Maps from program counter to instruction counter. @@ -13,8 +10,8 @@ pub struct PcIcMap { impl PcIcMap { /// Creates a new `PcIcMap` for the given code. - pub fn new(spec: SpecId, code: &[u8]) -> Self { - Self { inner: make_map::(spec, code) } + pub fn new(code: &[u8]) -> Self { + Self { inner: make_map::(code) } } /// Returns the instruction counter for the given program counter. @@ -32,8 +29,8 @@ pub struct IcPcMap { impl IcPcMap { /// Creates a new `IcPcMap` for the given code. - pub fn new(spec: SpecId, code: &[u8]) -> Self { - Self { inner: make_map::(spec, code) } + pub fn new(code: &[u8]) -> Self { + Self { inner: make_map::(code) } } /// Returns the program counter for the given instruction counter. @@ -42,8 +39,7 @@ impl IcPcMap { } } -fn make_map(spec: SpecId, code: &[u8]) -> FxHashMap { - let opcode_infos = spec_opcode_gas(spec); +fn make_map(code: &[u8]) -> FxHashMap { let mut map = FxHashMap::default(); let mut pc = 0; @@ -56,10 +52,9 @@ fn make_map(spec: SpecId, code: &[u8]) -> FxHashMap u64 { fn get_create2_factory_call_inputs(salt: U256, inputs: CreateInputs) -> CallInputs { let calldata = [&salt.to_be_bytes::<32>()[..], &inputs.init_code[..]].concat(); CallInputs { - contract: DEFAULT_CREATE2_DEPLOYER, - transfer: Transfer { - source: inputs.caller, - target: DEFAULT_CREATE2_DEPLOYER, - value: inputs.value, - }, + caller: inputs.caller, + bytecode_address: DEFAULT_CREATE2_DEPLOYER, + target_address: DEFAULT_CREATE2_DEPLOYER, + scheme: CallScheme::Call, + value: CallValue::Transfer(inputs.value), input: calldata.into(), gas_limit: inputs.gas_limit, - context: CallContext { - caller: inputs.caller, - address: DEFAULT_CREATE2_DEPLOYER, - code_address: DEFAULT_CREATE2_DEPLOYER, - apparent_value: inputs.value, - scheme: CallScheme::Call, - }, is_static: false, return_memory_offset: 0..0, + is_eof: false, } } diff --git a/crates/evm/coverage/src/anchors.rs b/crates/evm/coverage/src/anchors.rs index d45fdae57..a1992d47a 100644 --- a/crates/evm/coverage/src/anchors.rs +++ b/crates/evm/coverage/src/anchors.rs @@ -5,8 +5,8 @@ use alloy_primitives::Bytes; use foundry_compilers::sourcemap::{SourceElement, SourceMap}; use foundry_evm_core::utils::IcPcMap; use revm::{ - interpreter::opcode::{self, spec_opcode_gas}, - primitives::{HashSet, SpecId}, + interpreter::opcode::{self, PUSH1, PUSH32}, + primitives::HashSet, }; /// Attempts to find anchors for the given items using the given source map and bytecode. @@ -114,7 +114,6 @@ pub fn find_anchor_branch( ) -> eyre::Result<(ItemAnchor, ItemAnchor)> { // NOTE(onbjerg): We use `SpecId::LATEST` here since it does not matter; the only difference // is the gas cost. - let opcode_infos = spec_opcode_gas(SpecId::LATEST); let mut anchors: Option<(ItemAnchor, ItemAnchor)> = None; let mut pc = 0; @@ -124,7 +123,9 @@ pub fn find_anchor_branch( // We found a push, so we do some PC -> IC translation accounting, but we also check if // this push is coupled with the JUMPI we are interested in. - if opcode_infos[op as usize].is_push() { + + // Check if Opcode is PUSH + if (PUSH1..=PUSH32).contains(&op) { let element = if let Some(element) = source_map.get(pc - cumulative_push_size) { element } else { diff --git a/crates/evm/coverage/src/inspector.rs b/crates/evm/coverage/src/inspector.rs index 3c3af1ba2..4e25ddb51 100644 --- a/crates/evm/coverage/src/inspector.rs +++ b/crates/evm/coverage/src/inspector.rs @@ -10,15 +10,15 @@ pub struct CoverageCollector { impl Inspector for CoverageCollector { #[inline] fn initialize_interp(&mut self, interp: &mut Interpreter, _context: &mut EvmContext) { - let hash = interp.contract.hash; + let hash = interp.contract.hash.expect("Contract hash is None"); self.maps .entry(hash) - .or_insert_with(|| HitMap::new(interp.contract.bytecode.original_bytecode())); + .or_insert_with(|| HitMap::new(interp.contract.bytecode.original_bytes())); } #[inline] fn step(&mut self, interp: &mut Interpreter, _context: &mut EvmContext) { - let hash = interp.contract.hash; + let hash = interp.contract.hash.expect("Contract hash is None"); self.maps.entry(hash).and_modify(|map| map.hit(interp.program_counter())); } } diff --git a/crates/evm/evm/Cargo.toml b/crates/evm/evm/Cargo.toml index be3324c10..72f4a6d17 100644 --- a/crates/evm/evm/Cargo.toml +++ b/crates/evm/evm/Cargo.toml @@ -22,7 +22,12 @@ foundry-evm-traces.workspace = true alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } alloy-json-abi.workspace = true -alloy-primitives = { workspace = true, features = ["serde", "getrandom", "arbitrary", "rlp"] } +alloy-primitives = { workspace = true, features = [ + "serde", + "getrandom", + "arbitrary", + "rlp", +] } alloy-sol-types.workspace = true revm = { workspace = true, default-features = false, features = [ "std", diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 699800641..940e223b2 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -26,7 +26,7 @@ use foundry_evm_coverage::HitMaps; use foundry_evm_traces::CallTraceArena; use revm::{ db::{DatabaseCommit, DatabaseRef}, - interpreter::{return_ok, CreateScheme, InstructionResult}, + interpreter::{return_ok, InstructionResult}, primitives::{ BlockEnv, Bytecode, Env, EnvWithHandlerCfg, ExecutionResult, Output, ResultAndState, SpecId, TransactTo, TxEnv, @@ -92,7 +92,7 @@ impl Executor { backend.insert_account_info( CHEATCODE_ADDRESS, revm::primitives::AccountInfo { - code: Some(Bytecode::new_raw(Bytes::from_static(&[0])).to_checked()), + code: Some(Bytecode::new_raw(Bytes::from_static(&[0]))), ..Default::default() }, ); @@ -386,7 +386,7 @@ impl Executor { rd: Option<&RevertDecoder>, ) -> Result { assert!( - matches!(env.tx.transact_to, TransactTo::Create(_)), + matches!(env.tx.transact_to, TransactTo::Create), "Expected create transaction, got {:?}", env.tx.transact_to ); @@ -419,7 +419,7 @@ impl Executor { value: U256, rd: Option<&RevertDecoder>, ) -> Result { - let env = self.build_test_env(from, TransactTo::Create(CreateScheme::Create), code, value); + let env = self.build_test_env(from, TransactTo::Create, code, value); self.deploy_with_env(env, rd) } diff --git a/crates/evm/evm/src/inspectors/debugger.rs b/crates/evm/evm/src/inspectors/debugger.rs index d68a14bf6..c6b8a7cc5 100644 --- a/crates/evm/evm/src/inspectors/debugger.rs +++ b/crates/evm/evm/src/inspectors/debugger.rs @@ -8,9 +8,8 @@ use foundry_evm_core::{ }; use revm::{ interpreter::{ - opcode::{self, spec_opcode_gas}, - CallInputs, CallOutcome, CreateInputs, CreateOutcome, Gas, InstructionResult, Interpreter, - InterpreterResult, + opcode, CallInputs, CallOutcome, CreateInputs, CreateOutcome, Gas, InstructionResult, + Interpreter, InterpreterResult, }, EvmContext, Inspector, }; @@ -50,12 +49,12 @@ impl Inspector for Debugger { let pc = interp.program_counter(); let op = interp.current_opcode(); - // Get opcode information - let opcode_infos = spec_opcode_gas(ecx.spec_id()); - let opcode_info = &opcode_infos[op as usize]; - // Extract the push bytes - let push_size = if opcode_info.is_push() { (op - opcode::PUSH0) as usize } else { 0 }; + let push_size = if (opcode::PUSH1..=opcode::PUSH32).contains(&op) { + (op - opcode::PUSH0) as usize + } else { + 0 + }; let push_bytes = (push_size > 0).then(|| { let start = pc + 1; let end = start + push_size; @@ -95,8 +94,8 @@ impl Inspector for Debugger { fn call(&mut self, ecx: &mut EvmContext, inputs: &mut CallInputs) -> Option { self.enter( ecx.journaled_state.depth() as usize, - inputs.context.code_address, - inputs.context.scheme.into(), + inputs.bytecode_address, + inputs.scheme.into(), ); None diff --git a/crates/evm/evm/src/inspectors/logs.rs b/crates/evm/evm/src/inspectors/logs.rs index a8e4a063c..f2bf3a114 100644 --- a/crates/evm/evm/src/inspectors/logs.rs +++ b/crates/evm/evm/src/inspectors/logs.rs @@ -48,7 +48,7 @@ impl Inspector for LogCollector { _context: &mut EvmContext, inputs: &mut CallInputs, ) -> Option { - if inputs.contract == HARDHAT_CONSOLE_ADDRESS { + if inputs.target_address == HARDHAT_CONSOLE_ADDRESS { let (res, out) = self.hardhat_log(inputs.input.to_vec()); if res != InstructionResult::Continue { return Some(CallOutcome { diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index bcb1e5517..10108c891 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -486,7 +486,7 @@ impl InspectorStack { sender: ecx.env.tx.caller, original_origin: cached_env.tx.caller, original_sender_nonce: nonce, - is_create: matches!(transact_to, TransactTo::Create(_)), + is_create: matches!(transact_to, TransactTo::Create), }); self.in_inner_context = true; @@ -551,7 +551,7 @@ impl InspectorStack { let (result, address, output) = match res.result { ExecutionResult::Success { reason, gas_used, gas_refunded, logs: _, output } => { gas.set_refund(gas_refunded as i64); - gas.record_cost(gas_used); + let _ = gas.record_cost(gas_used); let address = match output { Output::Create(_, address) => address, Output::Call(_) => None, @@ -559,11 +559,11 @@ impl InspectorStack { (reason.into(), address, output.into_data()) } ExecutionResult::Halt { reason, gas_used } => { - gas.record_cost(gas_used); + let _ = gas.record_cost(gas_used); (reason.into(), None, Bytes::new()) } ExecutionResult::Revert { gas_used, output } => { - gas.record_cost(gas_used); + let _ = gas.record_cost(gas_used); (InstructionResult::Revert, None, output) } }; @@ -678,17 +678,17 @@ impl Inspector<&mut DB> for InspectorStack { ); if self.enable_isolation && - call.context.scheme == CallScheme::Call && + call.scheme == CallScheme::Call && !self.in_inner_context && ecx.journaled_state.depth == 1 { let (result, _) = self.transact_inner( ecx, - TransactTo::Call(call.contract), - call.context.caller, + TransactTo::Call(call.target_address), + call.caller, call.input.clone(), call.gas_limit, - call.transfer.value, + call.value.get(), ); return Some(CallOutcome { result, memory_offset: call.return_memory_offset.clone() }) } @@ -741,7 +741,7 @@ impl Inspector<&mut DB> for InspectorStack { if self.enable_isolation && !self.in_inner_context && ecx.journaled_state.depth == 1 { let (result, address) = self.transact_inner( ecx, - TransactTo::Create(create.scheme), + TransactTo::Create, create.caller, create.init_code.clone(), create.gas_limit, diff --git a/crates/evm/fuzz/src/inspector.rs b/crates/evm/fuzz/src/inspector.rs index eb089baf6..fe5c13456 100644 --- a/crates/evm/fuzz/src/inspector.rs +++ b/crates/evm/fuzz/src/inspector.rs @@ -29,7 +29,7 @@ impl Inspector for Fuzzer { #[inline] fn call(&mut self, ecx: &mut EvmContext, inputs: &mut CallInputs) -> Option { // We don't want to override the very first call made to the test contract. - if self.call_generator.is_some() && ecx.env.tx.caller != inputs.context.caller { + if self.call_generator.is_some() && ecx.env.tx.caller != inputs.caller { self.override_call(inputs); } @@ -77,20 +77,18 @@ impl Fuzzer { fn override_call(&mut self, call: &mut CallInputs) { if let Some(ref mut call_generator) = self.call_generator { // We only override external calls which are not coming from the test contract. - if call.context.caller != call_generator.test_address && - call.context.scheme == CallScheme::Call && + if call.caller != call_generator.test_address && + call.scheme == CallScheme::Call && !call_generator.used { // There's only a 30% chance that an override happens. - if let Some(tx) = call_generator.next(call.context.caller, call.contract) { + if let Some(tx) = call_generator.next(call.caller, call.target_address) { *call.input = tx.call_details.calldata.0; - call.context.caller = tx.sender; - call.contract = tx.call_details.target; + call.caller = tx.sender; + call.target_address = tx.call_details.target; // TODO: in what scenarios can the following be problematic - call.context.code_address = tx.call_details.target; - call.context.address = tx.call_details.target; - + call.bytecode_address = tx.call_details.target; call_generator.used = true; } } diff --git a/crates/evm/fuzz/src/strategies/state.rs b/crates/evm/fuzz/src/strategies/state.rs index 791ca3d9a..81aa93a5a 100644 --- a/crates/evm/fuzz/src/strategies/state.rs +++ b/crates/evm/fuzz/src/strategies/state.rs @@ -9,8 +9,8 @@ use indexmap::IndexSet; use parking_lot::{lock_api::RwLockReadGuard, RawRwLock, RwLock}; use revm::{ db::{CacheDB, DatabaseRef, DbAccount}, - interpreter::opcode::{self, spec_opcode_gas}, - primitives::{AccountInfo, SpecId}, + interpreter::opcode, + primitives::AccountInfo, }; use std::{ collections::{BTreeMap, HashMap}, @@ -217,7 +217,7 @@ impl FuzzDictionary { // Insert push bytes if let Some(code) = account_info.code.clone() { self.insert_address(*address, collected); - for push_byte in collect_push_bytes(code.bytes()) { + for push_byte in collect_push_bytes(&code.bytes()) { self.insert_value(push_byte, collected); } } @@ -323,11 +323,10 @@ fn collect_push_bytes(code: &[u8]) -> Vec<[u8; 32]> { let mut bytes: Vec<[u8; 32]> = Vec::new(); // We use [SpecId::LATEST] since we do not really care what spec it is - we are not interested // in gas costs. - let opcode_infos = spec_opcode_gas(SpecId::LATEST); let mut i = 0; while i < code.len().min(PUSH_BYTE_ANALYSIS_LIMIT) { let op = code[i]; - if opcode_infos[op as usize].is_push() { + if (opcode::PUSH1..=opcode::PUSH32).contains(&op) { let push_size = (op - opcode::PUSH1 + 1) as usize; let push_start = i + 1; let push_end = push_start + push_size; diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index d55c4db9a..380aa4927 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -8,7 +8,6 @@ use forge::{ CoverageReport, CoverageReporter, DebugReporter, LcovReporter, SummaryReporter, }, opts::EvmOpts, - revm::primitives::SpecId, utils::IcPcMap, MultiContractRunnerBuilder, TestOptions, }; @@ -231,10 +230,7 @@ impl CoverageArgs { // TODO: Creation bytecode as well ( id.clone(), - ( - IcPcMap::new(SpecId::LATEST, bytecodes.0.as_ref()), - IcPcMap::new(SpecId::LATEST, bytecodes.1.as_ref()), - ), + (IcPcMap::new(bytecodes.0.as_ref()), IcPcMap::new(bytecodes.1.as_ref())), ) }) .collect(); diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index 71a1cffd7..6ef37cf10 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -4,7 +4,7 @@ use alloy_json_abi::{Constructor, JsonAbi}; use alloy_network::{AnyNetwork, EthereumSigner, TransactionBuilder}; use alloy_primitives::{Address, Bytes}; use alloy_provider::{Provider, ProviderBuilder}; -use alloy_rpc_types::{AnyTransactionReceipt, BlockId, TransactionRequest, WithOtherFields}; +use alloy_rpc_types::{AnyTransactionReceipt, TransactionRequest, WithOtherFields}; use alloy_signer::Signer; use alloy_transport::{Transport, TransportError}; use clap::{Parser, ValueHint}; @@ -241,7 +241,7 @@ impl CreateArgs { deployer.tx.set_nonce(if let Some(nonce) = self.tx.nonce { Ok(nonce.to()) } else { - provider.get_transaction_count(deployer_address, BlockId::latest()).await + provider.get_transaction_count(deployer_address).await }?); // set tx value if specified @@ -252,7 +252,7 @@ impl CreateArgs { deployer.tx.set_gas_limit(if let Some(gas_limit) = self.tx.gas_limit { Ok(gas_limit.to()) } else { - provider.estimate_gas(&deployer.tx, BlockId::latest()).await + provider.estimate_gas(&deployer.tx).await }?); if is_legacy { diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index a278e44d7..3a2e66370 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -8,7 +8,7 @@ use alloy_eips::eip2718::Encodable2718; use alloy_network::{AnyNetwork, EthereumSigner, TransactionBuilder}; use alloy_primitives::{utils::format_units, Address, TxHash}; use alloy_provider::{utils::Eip1559Estimation, Provider}; -use alloy_rpc_types::{BlockId, TransactionRequest, WithOtherFields}; +use alloy_rpc_types::{TransactionRequest, WithOtherFields}; use alloy_transport::Transport; use eyre::{bail, Context, Result}; use forge_verify::provider::VerificationProviderType; @@ -43,10 +43,7 @@ where tx.gas = None; tx.set_gas_limit( - provider - .estimate_gas(tx, BlockId::latest()) - .await - .wrap_err("Failed to estimate gas for tx")? * + provider.estimate_gas(tx).await.wrap_err("Failed to estimate gas for tx")? * estimate_multiplier as u128 / 100, ); @@ -56,7 +53,7 @@ where pub async fn next_nonce(caller: Address, provider_url: &str) -> eyre::Result { let provider = try_get_http_provider(provider_url) .wrap_err_with(|| format!("bad fork_url provider: {provider_url}"))?; - Ok(provider.get_transaction_count(caller, BlockId::latest()).await?) + Ok(provider.get_transaction_count(caller).await?) } pub async fn send_transaction( @@ -71,7 +68,7 @@ pub async fn send_transaction( let from = tx.from.expect("no sender"); if sequential_broadcast { - let nonce = provider.get_transaction_count(from, BlockId::latest()).await?; + let nonce = provider.get_transaction_count(from).await?; let tx_nonce = tx.nonce.expect("no nonce"); if nonce != tx_nonce { diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index 8d9907ccf..3bf9e154b 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -45,8 +45,7 @@ impl BuildData { pub async fn link(self, script_config: &ScriptConfig) -> Result { let can_use_create2 = if let Some(fork_url) = &script_config.evm_opts.fork_url { let provider = try_get_http_provider(fork_url)?; - let deployer_code = - provider.get_code_at(DEFAULT_CREATE2_DEPLOYER, Default::default()).await?; + let deployer_code = provider.get_code_at(DEFAULT_CREATE2_DEPLOYER).await?; !deployer_code.is_empty() } else { diff --git a/crates/test-utils/Cargo.toml b/crates/test-utils/Cargo.toml index c2ee04cbe..c34faf250 100644 --- a/crates/test-utils/Cargo.toml +++ b/crates/test-utils/Cargo.toml @@ -18,7 +18,6 @@ foundry-config.workspace = true alloy-primitives.workspace = true alloy-provider.workspace = true -alloy-rpc-types.workspace = true eyre.workspace = true fd-lock = "4.0.0" diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index 8b625d6ee..232417f87 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -1,7 +1,6 @@ use crate::{init_tracing, TestCommand}; use alloy_primitives::Address; use alloy_provider::Provider; -use alloy_rpc_types::BlockId; use eyre::Result; use foundry_common::provider::{get_http_provider, RetryProvider}; use std::{ @@ -141,7 +140,7 @@ impl ScriptTester { if let Some(provider) = &self.provider { let nonce = provider - .get_transaction_count(self.accounts_pub[index as usize], BlockId::latest()) + .get_transaction_count(self.accounts_pub[index as usize]) .await .unwrap(); self.nonces.insert(index, nonce); @@ -152,13 +151,8 @@ impl ScriptTester { pub async fn load_addresses(&mut self, addresses: &[Address]) -> &mut Self { for &address in addresses { - let nonce = self - .provider - .as_ref() - .unwrap() - .get_transaction_count(address, BlockId::latest()) - .await - .unwrap(); + let nonce = + self.provider.as_ref().unwrap().get_transaction_count(address).await.unwrap(); self.address_nonces.insert(address, nonce); } self @@ -198,13 +192,7 @@ impl ScriptTester { pub async fn assert_nonce_increment(&mut self, keys_indexes: &[(u32, u32)]) -> &mut Self { for &(private_key_slot, expected_increment) in keys_indexes { let addr = self.accounts_pub[private_key_slot as usize]; - let nonce = self - .provider - .as_ref() - .unwrap() - .get_transaction_count(addr, BlockId::latest()) - .await - .unwrap(); + let nonce = self.provider.as_ref().unwrap().get_transaction_count(addr).await.unwrap(); let prev_nonce = self.nonces.get(&private_key_slot).unwrap(); assert_eq!( @@ -223,13 +211,8 @@ impl ScriptTester { address_indexes: &[(Address, u32)], ) -> &mut Self { for (address, expected_increment) in address_indexes { - let nonce = self - .provider - .as_ref() - .unwrap() - .get_transaction_count(*address, BlockId::latest()) - .await - .unwrap(); + let nonce = + self.provider.as_ref().unwrap().get_transaction_count(*address).await.unwrap(); let prev_nonce = self.address_nonces.get(address).unwrap(); assert_eq!(nonce, *prev_nonce + *expected_increment as u64); diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index 53654a7d3..829efd426 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -109,7 +109,7 @@ impl VerifyBytecodeArgs { let config = self.load_config_emit_warnings(); let provider = ProviderBuilder::new(&config.get_rpc_url_or_localhost_http()?).build()?; - let code = provider.get_code_at(self.address, BlockId::latest()).await?; + let code = provider.get_code_at(self.address).await?; if code.is_empty() { eyre::bail!("No bytecode found at address {}", self.address); } @@ -290,9 +290,11 @@ impl VerifyBytecodeArgs { // Workaround for the NonceTooHigh issue as we're not simulating prior txs of the same // block. - let prev_block_id = BlockId::Number(BlockNumberOrTag::Number(simulation_block - 1)); - let prev_block_nonce = - provider.get_transaction_count(creation_data.contract_creator, prev_block_id).await?; + let prev_block_id = BlockId::number(simulation_block - 1); + let prev_block_nonce = provider + .get_transaction_count(creation_data.contract_creator) + .block_id(prev_block_id) + .await?; transaction.nonce = prev_block_nonce; if let Some(ref block) = block { @@ -343,13 +345,12 @@ impl VerifyBytecodeArgs { ) })?; - let onchain_runtime_code = provider - .get_code_at(self.address, BlockId::Number(BlockNumberOrTag::Number(simulation_block))) - .await?; + let onchain_runtime_code = + provider.get_code_at(self.address).block_id(BlockId::number(simulation_block)).await?; // Compare the runtime bytecode with the locally built bytecode let (did_match, with_status) = try_match( - &fork_runtime_code.bytecode, + fork_runtime_code.bytecode(), &onchain_runtime_code, &constructor_args, &verification_type, diff --git a/crates/verify/src/lib.rs b/crates/verify/src/lib.rs index 565579118..200abbf3d 100644 --- a/crates/verify/src/lib.rs +++ b/crates/verify/src/lib.rs @@ -293,7 +293,7 @@ impl VerifyArgs { eyre::bail!("You have to provide a contract name or a valid RPC URL") } let provider = utils::get_provider(&config)?; - let code = provider.get_code_at(self.address, Default::default()).await?; + let code = provider.get_code_at(self.address).await?; let output = ProjectCompiler::new().compile(&project)?; let contracts = ContractsByArtifact::new( diff --git a/deny.toml b/deny.toml index 299c1452a..d69e73391 100644 --- a/deny.toml +++ b/deny.toml @@ -85,4 +85,7 @@ unknown-registry = "warn" # Lint level for what to happen when a crate from a git repository that is not # in the allow list is encountered unknown-git = "deny" -allow-git = ["https://github.com/alloy-rs/alloy", "https://github.com/paradigmxyz/evm-inspectors"] +allow-git = [ + "https://github.com/alloy-rs/alloy", + "https://github.com/paradigmxyz/revm-inspectors", +] diff --git a/testdata/default/cheats/LastCallGas.t.sol b/testdata/default/cheats/LastCallGas.t.sol index ec8c6ba0a..0f5b65e35 100644 --- a/testdata/default/cheats/LastCallGas.t.sol +++ b/testdata/default/cheats/LastCallGas.t.sol @@ -53,10 +53,6 @@ abstract contract LastCallGasFixture is DSTest { (success,) = address(target).call(""); } - function _performExpandMemory() internal view { - target.expandMemory(1000); - } - function _performRefund() internal { target.setValue(1); target.resetValue(); @@ -84,12 +80,6 @@ contract LastCallGasIsolatedTest is LastCallGasFixture { _assertGas(vm.lastCallGas(), Gas({gasTotalUsed: 21064, gasMemoryUsed: 0, gasRefunded: 0})); } - function testRecordGasMemory() public { - _setup(); - _performExpandMemory(); - _assertGas(vm.lastCallGas(), Gas({gasTotalUsed: 186470, gasMemoryUsed: 4994, gasRefunded: 0})); - } - function testRecordGasRefund() public { _setup(); _performRefund(); @@ -102,24 +92,18 @@ contract LastCallGasDefaultTest is LastCallGasFixture { function testRecordLastCallGas() public { _setup(); _performCall(); - _assertGas(vm.lastCallGas(), Gas({gasTotalUsed: 64, gasMemoryUsed: 9, gasRefunded: 0})); + _assertGas(vm.lastCallGas(), Gas({gasTotalUsed: 64, gasMemoryUsed: 0, gasRefunded: 0})); _performCall(); - _assertGas(vm.lastCallGas(), Gas({gasTotalUsed: 64, gasMemoryUsed: 9, gasRefunded: 0})); + _assertGas(vm.lastCallGas(), Gas({gasTotalUsed: 64, gasMemoryUsed: 0, gasRefunded: 0})); _performCall(); - _assertGas(vm.lastCallGas(), Gas({gasTotalUsed: 64, gasMemoryUsed: 9, gasRefunded: 0})); - } - - function testRecordGasMemory() public { - _setup(); - _performExpandMemory(); - _assertGas(vm.lastCallGas(), Gas({gasTotalUsed: 186470, gasMemoryUsed: 4994, gasRefunded: 0})); + _assertGas(vm.lastCallGas(), Gas({gasTotalUsed: 64, gasMemoryUsed: 0, gasRefunded: 0})); } function testRecordGasRefund() public { _setup(); _performRefund(); - _assertGas(vm.lastCallGas(), Gas({gasTotalUsed: 216, gasMemoryUsed: 9, gasRefunded: 19900})); + _assertGas(vm.lastCallGas(), Gas({gasTotalUsed: 216, gasMemoryUsed: 0, gasRefunded: 19900})); } } From 8fd3eb9fb803dc546f57d1d0ca3bffac7d3b794a Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Thu, 23 May 2024 17:51:09 +0300 Subject: [PATCH 302/430] chore(fuzz): add unit test for #1168 (#7974) --- crates/forge/tests/it/fuzz.rs | 24 ++++++++++++- .../default/fuzz/FuzzScrapeBytecode.t.sol | 35 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 testdata/default/fuzz/FuzzScrapeBytecode.t.sol diff --git a/crates/forge/tests/it/fuzz.rs b/crates/forge/tests/it/fuzz.rs index c6369e896..46f8115ab 100644 --- a/crates/forge/tests/it/fuzz.rs +++ b/crates/forge/tests/it/fuzz.rs @@ -12,7 +12,7 @@ use std::collections::BTreeMap; #[tokio::test(flavor = "multi_thread")] async fn test_fuzz() { let filter = Filter::new(".*", ".*", ".*fuzz/") - .exclude_tests(r"invariantCounter|testIncrement\(address\)|testNeedle\(uint256\)|testSuccessChecker\(uint256\)|testSuccessChecker2\(int256\)|testSuccessChecker3\(uint32\)") + .exclude_tests(r"invariantCounter|testIncrement\(address\)|testNeedle\(uint256\)|testSuccessChecker\(uint256\)|testSuccessChecker2\(int256\)|testSuccessChecker3\(uint32\)|testStorageOwner\(address\)|testImmutableOwner\(address\)") .exclude_paths("invariant"); let mut runner = TEST_DATA_DEFAULT.runner(); let suite_result = runner.test_collect(&filter); @@ -153,3 +153,25 @@ async fn test_persist_fuzz_failure() { // empty file is used to load failure so new calldata is generated assert_ne!(initial_calldata, new_calldata); } + +#[tokio::test(flavor = "multi_thread")] +async fn test_scrape_bytecode() { + let filter = Filter::new(".*", ".*", ".*fuzz/FuzzScrapeBytecode.t.sol"); + let mut runner = TEST_DATA_DEFAULT.runner(); + runner.test_options.fuzz.runs = 750; + runner.test_options.fuzz.seed = Some(U256::from(6u32)); + let suite_result = runner.test_collect(&filter); + + assert!(!suite_result.is_empty()); + + for (_, SuiteResult { test_results, .. }) in suite_result { + for (test_name, result) in test_results { + match test_name.as_str() { + "testImmutableOwner(address)" | "testStorageOwner(address)" => { + assert_eq!(result.status, TestStatus::Failure) + } + _ => {} + } + } + } +} diff --git a/testdata/default/fuzz/FuzzScrapeBytecode.t.sol b/testdata/default/fuzz/FuzzScrapeBytecode.t.sol new file mode 100644 index 000000000..ffded67f0 --- /dev/null +++ b/testdata/default/fuzz/FuzzScrapeBytecode.t.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; + +// https://github.com/foundry-rs/foundry/issues/1168 +contract FuzzerDict { + // Immutables should get added to the dictionary. + address public immutable immutableOwner; + // Regular storage variables should also get added to the dictionary. + address public storageOwner; + + constructor(address _immutableOwner, address _storageOwner) { + immutableOwner = _immutableOwner; + storageOwner = _storageOwner; + } +} + +contract FuzzerDictTest is DSTest { + FuzzerDict fuzzerDict; + + function setUp() public { + fuzzerDict = new FuzzerDict(address(100), address(200)); + } + + // Fuzzer should try `fuzzerDict.immutableOwner()` as input, causing this to fail + function testImmutableOwner(address who) public { + assertTrue(who != fuzzerDict.immutableOwner()); + } + + // Fuzzer should try `fuzzerDict.storageOwner()` as input, causing this to fail + function testStorageOwner(address who) public { + assertTrue(who != fuzzerDict.storageOwner()); + } +} From 10b9baa3a162f5742e561be2bc8048eceb03a3da Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 23 May 2024 17:59:30 +0300 Subject: [PATCH 303/430] feat: Vyper support for forge build (#7953) * feat: Vyper support for forge build * clippy * fix doc * fmt * fix * rm patch * fmt * review fixes * update test * clippy + fmt --------- Co-authored-by: Matthias Seitz --- Cargo.lock | 9 +- Cargo.toml | 4 +- crates/common/src/compile.rs | 36 ++++++-- crates/config/src/language.rs | 39 +++++++++ crates/config/src/lib.rs | 131 +++++++++++++++++++++++------ crates/forge/bin/cmd/build.rs | 50 ++++++++--- crates/forge/bin/cmd/coverage.rs | 6 +- crates/forge/bin/cmd/doc/mod.rs | 4 +- crates/forge/bin/cmd/fmt.rs | 4 +- crates/forge/bin/cmd/geiger/mod.rs | 4 +- crates/forge/bin/cmd/tree.rs | 4 +- crates/forge/src/multi_runner.rs | 8 +- crates/forge/tests/cli/config.rs | 7 +- crates/test-utils/src/util.rs | 4 +- 14 files changed, 244 insertions(+), 66 deletions(-) create mode 100644 crates/config/src/language.rs diff --git a/Cargo.lock b/Cargo.lock index b03d115c6..1496b3379 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3297,9 +3297,9 @@ dependencies = [ [[package]] name = "foundry-block-explorers" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "351c6be2db0090c6c5ae214a7e768a1a61d7d46ff70ab9e7ee45c8f34e6c3c60" +checksum = "7101c78b700b8c84294aee0319289af724201181a9b757d8a9a2fea991f3ce4e" dependencies = [ "alloy-chains", "alloy-json-abi", @@ -3451,9 +3451,9 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.4.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74a0bb9a4a8da5ded9ddbf2aba3d25bf26d2c4e8a3dfb4f20164d47d4da8761a" +checksum = "136f5e0af939b12cf072ac6577818a87cb7a408b269070f5d6f52ba23454660e" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3484,6 +3484,7 @@ dependencies = [ "tokio", "tracing", "walkdir", + "winnow 0.6.8", "yansi", ] diff --git a/Cargo.toml b/Cargo.toml index d55304817..4f5b962b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -137,8 +137,8 @@ foundry-wallets = { path = "crates/wallets" } foundry-linking = { path = "crates/linking" } # solc & compilation utilities -foundry-block-explorers = { version = "0.2.7", default-features = false } -foundry-compilers = { version = "0.4.3", default-features = false } +foundry-block-explorers = { version = "0.2.8", default-features = false } +foundry-compilers = { version = "0.5", default-features = false } ## revm # no default features to avoid c-kzg diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index 3141a6978..329e53213 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -6,11 +6,15 @@ use eyre::{Context, Result}; use foundry_block_explorers::contract::Metadata; use foundry_compilers::{ artifacts::{BytecodeObject, ContractBytecodeSome, Libraries}, - compilers::{solc::SolcVersionManager, Compiler, CompilerVersionManager}, + compilers::{ + solc::SolcVersionManager, vyper::parser::VyperParsedSource, Compiler, + CompilerVersionManager, + }, remappings::Remapping, report::{BasicStdoutReporter, NoReporter, Report}, - Artifact, ArtifactId, CompilerConfig, ConfigurableArtifacts, FileFilter, Project, - ProjectCompileOutput, ProjectPathsConfig, SolcConfig, SparseOutputFileFilter, + resolver::{parse::SolData, GraphEdges}, + Artifact, ArtifactId, CompilerConfig, FileFilter, Project, ProjectCompileOutput, + ProjectPathsConfig, SolcConfig, SolcSparseFileFilter, SparseOutputFileFilter, }; use foundry_linking::Linker; use num_format::{Locale, ToFormattedString}; @@ -138,7 +142,7 @@ impl ProjectCompiler { /// Compiles the project. pub fn compile( mut self, - project: &Project, + project: &Project, ) -> Result> { // TODO: Avoid process::exit if !project.paths.has_input_files() && self.files.is_empty() { @@ -476,7 +480,7 @@ pub struct ContractInfo { /// **Note:** this expects the `target_path` to be absolute pub fn compile_target( target_path: &Path, - project: &Project, + project: &Project, quiet: bool, ) -> Result> { ProjectCompiler::::new().quiet(quiet).files([target_path.into()]).compile(project) @@ -585,6 +589,28 @@ impl FileFilter for SkipBuildFilters { } } +impl FileFilter for &SkipBuildFilters { + fn is_match(&self, file: &Path) -> bool { + (*self).is_match(file) + } +} + +impl SparseOutputFileFilter for SkipBuildFilters { + fn sparse_sources(&self, file: &Path, graph: &GraphEdges) -> Vec { + SolcSparseFileFilter::new(self).sparse_sources(file, graph) + } +} + +impl SparseOutputFileFilter for SkipBuildFilters { + fn sparse_sources(&self, file: &Path, _graph: &GraphEdges) -> Vec { + if self.is_match(file) { + vec![file.to_path_buf()] + } else { + vec![] + } + } +} + impl SkipBuildFilters { /// Creates a new `SkipBuildFilters` from multiple `SkipBuildFilter`. pub fn new( diff --git a/crates/config/src/language.rs b/crates/config/src/language.rs new file mode 100644 index 000000000..dd1105e2d --- /dev/null +++ b/crates/config/src/language.rs @@ -0,0 +1,39 @@ +use serde::{Deserialize, Serialize}; +use std::str::FromStr; + +/// Compilers supported by foundry. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub enum Language { + /// Solidity + Solidity, + /// Vyper + Vyper, +} + +impl Language { + /// Returns the language name as a string. + pub const fn as_str(&self) -> &'static str { + match self { + Self::Solidity => "solidity", + Self::Vyper => "vyper", + } + } +} + +impl std::fmt::Display for Language { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.as_str()) + } +} + +impl FromStr for Language { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "solidity" => Ok(Self::Solidity), + "vyper" => Ok(Self::Vyper), + s => Err(format!("Unknown language: {s}")), + } + } +} diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index e48f8d571..aff1ec88b 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -15,16 +15,21 @@ use figment::{ }; use foundry_compilers::{ artifacts::{ - output_selection::ContractOutputSelection, serde_helpers, BytecodeHash, DebuggingSettings, - Libraries, ModelCheckerSettings, ModelCheckerTarget, Optimizer, OptimizerDetails, - RevertStrings, Settings, SettingsMetadata, Severity, + output_selection::{ContractOutputSelection, OutputSelection}, + serde_helpers, BytecodeHash, DebuggingSettings, Libraries, ModelCheckerSettings, + ModelCheckerTarget, Optimizer, OptimizerDetails, RevertStrings, Settings, SettingsMetadata, + Severity, }, cache::SOLIDITY_FILES_CACHE_FILENAME, - compilers::{solc::SolcVersionManager, CompilerVersionManager}, + compilers::{ + solc::SolcVersionManager, + vyper::{Vyper, VyperSettings}, + Compiler, CompilerVersionManager, + }, error::SolcError, remappings::{RelativeRemapping, Remapping}, - CompilerConfig, ConfigurableArtifacts, EvmVersion, Project, ProjectPathsConfig, Solc, - SolcConfig, + CompilerConfig, ConfigurableArtifacts, EvmVersion, Project, ProjectBuilder, ProjectPathsConfig, + Solc, SolcConfig, }; use inflector::Inflector; use regex::Regex; @@ -82,6 +87,10 @@ pub use figment; /// config providers pub mod providers; +/// compilers supported by foundry +pub mod language; +pub use language::Language; + use crate::{ error::ExtractConfigError, etherscan::{EtherscanConfigError, EtherscanConfigs, ResolvedEtherscanConfig}, @@ -397,6 +406,10 @@ pub struct Config { /// CREATE2 salt to use for the library deployment in scripts. pub create2_library_salt: B256, + /// Compiler to use + #[serde(with = "from_str_lowercase")] + pub lang: Language, + /// The root path where the config detection started from, `Config::with_root` #[doc(hidden)] // We're skipping serialization here, so it won't be included in the [`Config::to_string()`] @@ -757,6 +770,11 @@ impl Config { self.create_project(self.cache, false) } + /// Configures [Project] with [Vyper] compiler. + pub fn vyper_project(&self) -> Result, SolcError> { + self.create_vyper_project(self.cache, false) + } + /// Same as [`Self::project()`] but sets configures the project to not emit artifacts and ignore /// cache. pub fn ephemeral_no_artifacts_project(&self) -> Result { @@ -765,10 +783,38 @@ impl Config { /// Creates a [Project] with the given `cached` and `no_artifacts` flags pub fn create_project(&self, cached: bool, no_artifacts: bool) -> Result { - let project = Project::builder() + let compiler_config = self.solc_config()?; + let settings = SolcConfig::builder().settings(self.solc_settings()?).build().settings; + + self.create_project_with_compiler(cached, no_artifacts, compiler_config, settings) + } + + /// Creates a [Project] with the given `cached` and `no_artifacts` flags + pub fn create_vyper_project( + &self, + cached: bool, + no_artifacts: bool, + ) -> Result, SolcError> { + self.create_project_with_compiler( + cached, + no_artifacts, + self.vyper_config()?, + self.vyper_settings()?, + ) + } + + /// Creates a [Project] with a given [CompilerConfig]. + pub fn create_project_with_compiler( + &self, + cached: bool, + no_artifacts: bool, + compiler_config: CompilerConfig, + settings: C::Settings, + ) -> Result, SolcError> { + let project = ProjectBuilder::::new(Default::default()) .artifacts(self.configured_artifacts_handler()) .paths(self.project_paths()) - .settings(SolcConfig::builder().settings(self.solc_settings()?).build().settings) + .settings(settings) .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 { @@ -780,7 +826,7 @@ impl Config { .set_cached(cached && !self.build_info) .set_build_info(!no_artifacts && self.build_info) .set_no_artifacts(no_artifacts) - .build(self.compiler_config()?)?; + .build(compiler_config)?; if self.force { self.cleanup(&project)?; @@ -790,7 +836,7 @@ impl Config { } /// Cleans the project. - pub fn cleanup(&self, project: &Project) -> Result<(), SolcError> { + pub fn cleanup(&self, project: &Project) -> Result<(), SolcError> { project.cleanup()?; // Remove fuzz and invariant cache directories. @@ -830,15 +876,7 @@ impl Config { version_manager.install(version)? } } - SolcReq::Local(solc) => { - if !solc.is_file() { - return Err(SolcError::msg(format!( - "`solc` {} does not exist", - solc.display() - ))) - } - Solc::new(solc)? - } + SolcReq::Local(solc) => Solc::new(solc)?, }; return Ok(Some(solc)) } @@ -881,11 +919,12 @@ impl Config { /// # Example /// /// ``` + /// use foundry_compilers::Solc; /// use foundry_config::Config; /// let config = Config::load_with_root(".").sanitized(); - /// let paths = config.project_paths(); + /// let paths = config.project_paths::(); /// ``` - pub fn project_paths(&self) -> ProjectPathsConfig { + pub fn project_paths(&self) -> ProjectPathsConfig { let mut builder = ProjectPathsConfig::builder() .cache(self.cache_path.join(SOLIDITY_FILES_CACHE_FILENAME)) .sources(&self.src) @@ -907,7 +946,7 @@ impl Config { } /// Returns configuration for a compiler to use when setting up a [Project]. - pub fn compiler_config(&self) -> Result, SolcError> { + pub fn solc_config(&self) -> Result, SolcError> { if let Some(solc) = self.ensure_solc()? { Ok(CompilerConfig::Specific(solc)) } else { @@ -915,6 +954,15 @@ impl Config { } } + /// Returns configuration for a compiler to use when setting up a [Project]. + pub fn vyper_config(&self) -> Result, SolcError> { + if let Some(SolcReq::Local(path)) = &self.solc { + Ok(CompilerConfig::Specific(Vyper::new(path)?)) + } else { + Ok(CompilerConfig::Specific(Vyper::new("vyper")?)) + } + } + /// Returns all configured [`Remappings`] /// /// **Note:** this will add an additional `/=` remapping here, see @@ -1237,6 +1285,23 @@ impl Config { Ok(settings) } + /// Returns the configured [VyperSettings] that includes: + /// - evm version + pub fn vyper_settings(&self) -> Result { + Ok(VyperSettings { + evm_version: Some(self.evm_version), + optimize: None, + bytecode_metadata: None, + // TODO: We don't yet have a way to deserialize other outputs correctly, so request only + // those for now. It should be enough to run tests and deploy contracts. + output_selection: OutputSelection::common_output_selection([ + "abi".to_string(), + "evm.bytecode".to_string(), + "evm.deployedBytecode".to_string(), + ]), + }) + } + /// Returns the default figment /// /// The default figment reads from the following sources, in ascending @@ -2017,6 +2082,7 @@ impl Default for Config { labels: Default::default(), unchecked_cheatcode_artifacts: false, create2_library_salt: Config::DEFAULT_CREATE2_LIBRARY_SALT, + lang: Language::Solidity, __non_exhaustive: (), __warnings: vec![], } @@ -2709,6 +2775,23 @@ fn canonic(path: impl Into) -> PathBuf { foundry_compilers::utils::canonicalize(&path).unwrap_or(path) } +/// Executes the given closure with a [Project] configured via the given [Config]. +#[macro_export] +macro_rules! with_resolved_project { + ($config:ident, |$prj:ident| $e:expr) => { + match $config.lang { + foundry_config::Language::Solidity => { + let $prj = $config.project(); + $e + } + foundry_config::Language::Vyper => { + let $prj = $config.vyper_project(); + $e + } + } + }; +} + #[cfg(test)] mod tests { use super::*; @@ -2849,7 +2932,7 @@ mod tests { fn test_default_test_path() { figment::Jail::expect_with(|_| { let config = Config::default(); - let paths_config = config.project_paths(); + let paths_config = config.project_paths::(); assert_eq!(paths_config.tests, PathBuf::from(r"test")); Ok(()) }); @@ -2916,7 +2999,7 @@ mod tests { )?; let config = Config::load(); - let paths_config = config.project_paths(); + let paths_config = config.project_paths::(); assert_eq!(paths_config.tests, PathBuf::from(r"mytest")); Ok(()) }); diff --git a/crates/forge/bin/cmd/build.rs b/crates/forge/bin/cmd/build.rs index 673198fdc..c4bbbb8a2 100644 --- a/crates/forge/bin/cmd/build.rs +++ b/crates/forge/bin/cmd/build.rs @@ -3,7 +3,9 @@ use clap::Parser; use eyre::Result; use foundry_cli::{opts::CoreBuildArgs, utils::LoadConfig}; use foundry_common::compile::{ProjectCompiler, SkipBuildFilter, SkipBuildFilters}; -use foundry_compilers::{Project, ProjectCompileOutput, SolcSparseFileFilter}; +use foundry_compilers::{ + compilers::Compiler, Project, ProjectCompileOutput, SparseOutputFileFilter, +}; use foundry_config::{ figment::{ self, @@ -11,7 +13,7 @@ use foundry_config::{ value::{Dict, Map, Value}, Metadata, Profile, Provider, }, - Config, + with_resolved_project, Config, }; use serde::Serialize; use watchexec::config::{InitConfig, RuntimeConfig}; @@ -75,32 +77,54 @@ pub struct BuildArgs { } impl BuildArgs { - pub fn run(self) -> Result { + pub fn run(self) -> Result<()> { let mut config = self.try_load_config_emit_warnings()?; - let mut project = config.project()?; if install::install_missing_dependencies(&mut config, self.args.silent) && config.auto_detect_remappings { // need to re-configure here to also catch additional remappings config = self.load_config(); - project = config.project()?; } + with_resolved_project!(config, |project| { + let project = project?; + + let filter = if let Some(ref skip) = self.skip { + if !skip.is_empty() { + let filter = SkipBuildFilters::new(skip.clone(), project.root().clone())?; + Some(filter) + } else { + None + } + } else { + None + }; + + self.run_with_project(project, filter)?; + }); + + Ok(()) + } + + pub fn run_with_project( + &self, + project: Project, + filter: Option + 'static>, + ) -> Result> + where + C::CompilationError: Clone, + { let mut compiler = ProjectCompiler::new() .print_names(self.names) .print_sizes(self.sizes) .quiet(self.format_json) .bail(!self.format_json); - if let Some(skip) = self.skip { - if !skip.is_empty() { - let filter = SolcSparseFileFilter::new(SkipBuildFilters::new( - skip.clone(), - project.root().to_path_buf(), - )?); - compiler = compiler.filter(Box::new(filter)); - } + + if let Some(filter) = filter { + compiler = compiler.filter(Box::new(filter)); } + let output = compiler.compile(&project)?; if self.format_json { diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index 380aa4927..89765e9da 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -88,7 +88,7 @@ impl CoverageArgs { let (project, output) = self.build(&config)?; p_println!(!self.test.build_args().silent => "Analysing contracts..."); - let report = self.prepare(&config, output.clone())?; + let report = self.prepare(&project, output.clone())?; p_println!(!self.test.build_args().silent => "Running tests..."); self.collect(project, output, report, Arc::new(config), evm_opts).await @@ -141,8 +141,8 @@ impl CoverageArgs { /// Builds the coverage report. #[instrument(name = "prepare", skip_all)] - fn prepare(&self, config: &Config, output: ProjectCompileOutput) -> Result { - let project_paths = config.project_paths(); + fn prepare(&self, project: &Project, output: ProjectCompileOutput) -> Result { + let project_paths = &project.paths; // Extract artifacts let (artifacts, sources) = output.into_artifacts_with_sources(); diff --git a/crates/forge/bin/cmd/doc/mod.rs b/crates/forge/bin/cmd/doc/mod.rs index 30de31e5a..e561dcc5c 100644 --- a/crates/forge/bin/cmd/doc/mod.rs +++ b/crates/forge/bin/cmd/doc/mod.rs @@ -95,8 +95,8 @@ impl DocArgs { let mut builder = DocBuilder::new( root.clone(), - config.project_paths().sources, - config.project_paths().libraries, + project.paths.sources, + project.paths.libraries, self.include_libraries, ) .with_should_build(self.build) diff --git a/crates/forge/bin/cmd/fmt.rs b/crates/forge/bin/cmd/fmt.rs index 6ff5387fa..71ac7ceb0 100644 --- a/crates/forge/bin/cmd/fmt.rs +++ b/crates/forge/bin/cmd/fmt.rs @@ -3,7 +3,7 @@ use eyre::Result; use forge_fmt::{format_to, parse, print_diagnostics_report}; use foundry_cli::utils::{FoundryPathExt, LoadConfig}; use foundry_common::{fs, glob::expand_globs, term::cli_warn}; -use foundry_compilers::SOLC_EXTENSIONS; +use foundry_compilers::{Solc, SOLC_EXTENSIONS}; use foundry_config::impl_figment_convert_basic; use rayon::prelude::*; use similar::{ChangeTag, TextDiff}; @@ -60,7 +60,7 @@ impl FmtArgs { [] => { // Retrieve the project paths, and filter out the ignored ones. let project_paths: Vec = config - .project_paths() + .project_paths::() .input_files_iter() .filter(|p| !(ignored.contains(p) || ignored.contains(&cwd.join(p)))) .collect(); diff --git a/crates/forge/bin/cmd/geiger/mod.rs b/crates/forge/bin/cmd/geiger/mod.rs index 451c29dbd..0b08f2d0d 100644 --- a/crates/forge/bin/cmd/geiger/mod.rs +++ b/crates/forge/bin/cmd/geiger/mod.rs @@ -1,7 +1,7 @@ use clap::{Parser, ValueHint}; use eyre::{Result, WrapErr}; use foundry_cli::utils::LoadConfig; -use foundry_compilers::{resolver::parse::SolData, Graph}; +use foundry_compilers::{resolver::parse::SolData, Graph, Solc}; use foundry_config::{impl_figment_convert_basic, Config}; use itertools::Itertools; use rayon::prelude::*; @@ -62,7 +62,7 @@ impl GeigerArgs { let mut sources: Vec = { if self.paths.is_empty() { - Graph::::resolve(&config.project_paths())? + Graph::::resolve(&config.project_paths::())? .files() .keys() .cloned() diff --git a/crates/forge/bin/cmd/tree.rs b/crates/forge/bin/cmd/tree.rs index 088975d87..e2f958034 100644 --- a/crates/forge/bin/cmd/tree.rs +++ b/crates/forge/bin/cmd/tree.rs @@ -3,7 +3,7 @@ use eyre::Result; use foundry_cli::{opts::ProjectPathsArgs, utils::LoadConfig}; use foundry_compilers::{ resolver::{parse::SolData, Charset, TreeOptions}, - Graph, + Graph, Solc, }; /// CLI arguments for `forge tree`. @@ -28,7 +28,7 @@ foundry_config::impl_figment_convert!(TreeArgs, opts); impl TreeArgs { pub fn run(self) -> Result<()> { let config = self.try_load_config_emit_warnings()?; - let graph = Graph::::resolve(&config.project_paths())?; + let graph = Graph::::resolve(&config.project_paths::())?; let opts = TreeOptions { charset: self.charset, no_dedupe: self.no_dedupe }; graph.print_with_options(opts); diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 1e4f77388..3653a0e3f 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -5,7 +5,7 @@ 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_compilers::{artifacts::Libraries, Artifact, ArtifactId, ProjectCompileOutput}; +use foundry_compilers::{artifacts::Libraries, Artifact, ArtifactId, ProjectCompileOutput, Solc}; use foundry_config::Config; use foundry_evm::{ backend::Backend, decode::RevertDecoder, executors::ExecutorBuilder, fork::CreateFork, @@ -181,8 +181,10 @@ impl MultiContractRunner { let identifier = artifact_id.identifier(); let mut span_name = identifier.as_str(); - let linker = - Linker::new(self.config.project_paths().root, self.output.artifact_ids().collect()); + let linker = Linker::new( + self.config.project_paths::().root, + self.output.artifact_ids().collect(), + ); let linked_contracts = linker.get_linked_artifacts(&contract.libraries).unwrap_or_default(); let known_contracts = Arc::new(ContractsByArtifact::new(linked_contracts)); diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index aec3b6dc2..54e7caff7 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -9,7 +9,7 @@ use foundry_compilers::{ use foundry_config::{ cache::{CachedChains, CachedEndpoints, StorageCachingConfig}, fs_permissions::{FsAccessPermission, PathPermission}, - Config, FsPermissions, FuzzConfig, InvariantConfig, SolcReq, + Config, FsPermissions, FuzzConfig, InvariantConfig, Language, SolcReq, }; use foundry_evm::opts::EvmOpts; use foundry_test_utils::{ @@ -135,6 +135,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { isolate: true, unchecked_cheatcode_artifacts: false, create2_library_salt: Config::DEFAULT_CREATE2_LIBRARY_SALT, + lang: Language::Solidity, __non_exhaustive: (), __warnings: vec![], }; @@ -361,7 +362,9 @@ contract Foo {} // fails to use solc that does not exist cmd.forge_fuse().args(["build", "--use", "this/solc/does/not/exist"]); - assert!(cmd.stderr_lossy().contains("this/solc/does/not/exist does not exist")); + assert!(cmd + .stderr_lossy() + .contains(r#""this/solc/does/not/exist": No such file or directory"#)); // `OTHER_SOLC_VERSION` was installed in previous step, so we can use the path to this directly let local_solc = diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index be7e13af1..cba0104a7 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -5,7 +5,7 @@ use foundry_compilers::{ cache::CompilerCache, error::Result as SolcResult, project_util::{copy_dir, TempProject}, - ArtifactOutput, ConfigurableArtifacts, PathStyle, ProjectPathsConfig, + ArtifactOutput, ConfigurableArtifacts, PathStyle, ProjectPathsConfig, Solc, }; use foundry_config::Config; use once_cell::sync::Lazy; @@ -408,7 +408,7 @@ pub struct TestProject { /// The directory in which this test executable is running. exe_root: PathBuf, /// The project in which the test should run. - inner: Arc>, + inner: Arc>, } impl TestProject { From f8ad354e9d61933d5ed8f299b88d724043440448 Mon Sep 17 00:00:00 2001 From: Federico Gimenez Date: Thu, 23 May 2024 21:05:35 +0200 Subject: [PATCH 304/430] feat: add tmp prague config value (#7697) --- crates/config/src/lib.rs | 15 ++++++++++++--- crates/forge/tests/cli/config.rs | 1 + 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index aff1ec88b..d57dc68a6 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -388,6 +388,11 @@ pub struct Config { /// Should be removed once EvmVersion Cancun is supported by solc pub cancun: bool, + /// Temporary config to enable [SpecId::PRAGUE] + /// + /// Should be removed once EvmVersion Prague is supported by solc + pub prague: bool, + /// Whether to enable call isolation. /// /// Useful for more correct gas accounting and EVM behavior in general. @@ -890,6 +895,9 @@ impl Config { if self.cancun { return SpecId::CANCUN } + if self.prague { + return SpecId::PRAGUE + } evm_spec_id(&self.evm_version) } @@ -1993,6 +2001,7 @@ impl Default for Config { profile: Self::DEFAULT_PROFILE, fs_permissions: FsPermissions::new([PathPermission::read("out")]), cancun: false, + prague: false, isolate: false, __root: Default::default(), src: "src".into(), @@ -2966,7 +2975,7 @@ mod tests { test = "defaulttest" src = "defaultsrc" libs = ['lib', 'node_modules'] - + [profile.custom] src = "customsrc" "#, @@ -3788,7 +3797,7 @@ mod tests { tx_origin = '0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38' verbosity = 0 via_ir = false - + [profile.default.rpc_storage_caching] chains = 'all' endpoints = 'all' @@ -4256,7 +4265,7 @@ mod tests { './src/SizeAuction.sol:Math:0x902f6cf364b8d9470d5793a9b2b2e86bddd21e0c', './src/test/ChainlinkTWAP.t.sol:ChainlinkTWAP:0xffedba5e171c4f15abaaabc86e8bd01f9b54dae5', './src/SizeAuctionDiscount.sol:Math:0x902f6cf364b8d9470d5793a9b2b2e86bddd21e0c', - ] + ] ", )?; let config = Config::load(); diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 54e7caff7..a00a50ba4 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -132,6 +132,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { fs_permissions: Default::default(), labels: Default::default(), cancun: true, + prague: true, isolate: true, unchecked_cheatcode_artifacts: false, create2_library_salt: Config::DEFAULT_CREATE2_LIBRARY_SALT, From f4102c1458fe6cf5223a5018591f58b8dd4763e6 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 23 May 2024 23:01:33 +0200 Subject: [PATCH 305/430] chore: remove cancun setting (#7977) --- crates/config/src/lib.rs | 10 ---------- crates/forge/tests/cli/config.rs | 1 - 2 files changed, 11 deletions(-) diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index d57dc68a6..4b622662a 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -382,12 +382,6 @@ pub struct Config { /// This includes what operations can be executed (read, write) pub fs_permissions: FsPermissions, - /// Temporary config to enable [SpecId::CANCUN] - /// - /// - /// Should be removed once EvmVersion Cancun is supported by solc - pub cancun: bool, - /// Temporary config to enable [SpecId::PRAGUE] /// /// Should be removed once EvmVersion Prague is supported by solc @@ -892,9 +886,6 @@ impl Config { /// Returns the [SpecId] derived from the configured [EvmVersion] #[inline] pub fn evm_spec_id(&self) -> SpecId { - if self.cancun { - return SpecId::CANCUN - } if self.prague { return SpecId::PRAGUE } @@ -2000,7 +1991,6 @@ impl Default for Config { Self { profile: Self::DEFAULT_PROFILE, fs_permissions: FsPermissions::new([PathPermission::read("out")]), - cancun: false, prague: false, isolate: false, __root: Default::default(), diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index a00a50ba4..d439cf68d 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -131,7 +131,6 @@ forgetest!(can_extract_config_values, |prj, cmd| { doc: Default::default(), fs_permissions: Default::default(), labels: Default::default(), - cancun: true, prague: true, isolate: true, unchecked_cheatcode_artifacts: false, From 2b5af1b07d439fc46a9ef0d34711afedef5a573d Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 24 May 2024 00:20:10 +0300 Subject: [PATCH 306/430] chore: make aws-kms signer support optional (#7976) --- Cargo.lock | 49 ----------------------------- crates/cast/Cargo.toml | 5 +-- crates/wallets/Cargo.toml | 10 +++--- crates/wallets/src/error.rs | 15 +++++++-- crates/wallets/src/multi_wallet.rs | 2 ++ crates/wallets/src/wallet.rs | 2 +- crates/wallets/src/wallet_signer.rs | 24 ++++++++++---- 7 files changed, 42 insertions(+), 65 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1496b3379..131c6351d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1103,8 +1103,6 @@ checksum = "40ddbfb5db93d62521f47b3f223da0884a2f02741ff54cb9cda192a0e73ba08b" dependencies = [ "aws-credential-types", "aws-runtime", - "aws-sdk-sso", - "aws-sdk-ssooidc", "aws-sdk-sts", "aws-smithy-async", "aws-smithy-http", @@ -1115,15 +1113,12 @@ dependencies = [ "aws-types", "bytes", "fastrand", - "hex", "http 0.2.12", "hyper 0.14.28", - "ring", "time", "tokio", "tracing", "url", - "zeroize", ] [[package]] @@ -1183,50 +1178,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "aws-sdk-sso" -version = "1.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fef2d9ca2b43051224ed326ed9960a85e277b7d554a2cd0397e57c0553d86e64" -dependencies = [ - "aws-credential-types", - "aws-runtime", - "aws-smithy-async", - "aws-smithy-http", - "aws-smithy-json", - "aws-smithy-runtime", - "aws-smithy-runtime-api", - "aws-smithy-types", - "aws-types", - "bytes", - "http 0.2.12", - "once_cell", - "regex-lite", - "tracing", -] - -[[package]] -name = "aws-sdk-ssooidc" -version = "1.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c869d1f5c4ee7437b79c3c1664ddbf7a60231e893960cf82b2b299a5ccf2cc5d" -dependencies = [ - "aws-credential-types", - "aws-runtime", - "aws-smithy-async", - "aws-smithy-http", - "aws-smithy-json", - "aws-smithy-runtime", - "aws-smithy-runtime-api", - "aws-smithy-types", - "aws-types", - "bytes", - "http 0.2.12", - "once_cell", - "regex-lite", - "tracing", -] - [[package]] name = "aws-sdk-sts" version = "1.25.0" diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index d6837b176..bf180b9b5 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -52,8 +52,8 @@ rayon = "1" serde_json.workspace = true serde.workspace = true -# aws -aws-sdk-kms = { version = "1", default-features = false } +# aws-kms +aws-sdk-kms = { version = "1", default-features = false, optional = true } # bin foundry-cli.workspace = true @@ -88,6 +88,7 @@ rustls = ["foundry-cli/rustls", "foundry-wallets/rustls"] openssl = ["foundry-cli/openssl"] asm-keccak = ["alloy-primitives/asm-keccak"] jemalloc = ["dep:tikv-jemallocator"] +aws-kms = ["foundry-wallets/aws-kms", "dep:aws-sdk-kms"] [[bench]] name = "vanity" diff --git a/crates/wallets/Cargo.toml b/crates/wallets/Cargo.toml index a0495c51d..c7c4038cd 100644 --- a/crates/wallets/Cargo.toml +++ b/crates/wallets/Cargo.toml @@ -13,7 +13,6 @@ repository.workspace = true alloy-primitives.workspace = true alloy-signer = { workspace = true, features = ["eip712"] } alloy-signer-wallet = { workspace = true, features = ["mnemonic", "keystore"] } -alloy-signer-aws = { workspace = true, features = ["eip712"] } alloy-signer-ledger = { workspace = true, features = ["eip712"] } alloy-signer-trezor.workspace = true alloy-network.workspace = true @@ -21,8 +20,10 @@ alloy-consensus.workspace = true alloy-sol-types.workspace = true alloy-dyn-abi.workspace = true -aws-sdk-kms = { version = "1", default-features = false } -aws-config = "1" +# aws-kms +alloy-signer-aws = { workspace = true, features = ["eip712"], optional = true } +aws-config = { version = "1", default-features = false, optional = true } +aws-sdk-kms = { version = "1", default-features = false, optional = true } foundry-config.workspace = true foundry-common.workspace = true @@ -43,4 +44,5 @@ tokio = { workspace = true, features = ["macros"] } [features] default = ["rustls"] -rustls = ["aws-sdk-kms/rustls"] +rustls = ["aws-sdk-kms?/rustls"] +aws-kms = ["dep:alloy-signer-aws", "dep:aws-config", "dep:aws-sdk-kms"] diff --git a/crates/wallets/src/error.rs b/crates/wallets/src/error.rs index b9a5e34f5..8b341cc3d 100644 --- a/crates/wallets/src/error.rs +++ b/crates/wallets/src/error.rs @@ -1,10 +1,12 @@ use alloy_signer::k256::ecdsa; -use alloy_signer_aws::AwsSignerError; use alloy_signer_ledger::LedgerError; use alloy_signer_trezor::TrezorError; use alloy_signer_wallet::WalletError; use hex::FromHexError; +#[cfg(feature = "aws-kms")] +use alloy_signer_aws::AwsSignerError; + #[derive(Debug, thiserror::Error)] pub enum PrivateKeyError { #[error("Failed to create wallet from private key. Private key is invalid hex: {0}")] @@ -22,6 +24,7 @@ pub enum WalletSignerError { #[error(transparent)] Trezor(#[from] TrezorError), #[error(transparent)] + #[cfg(feature = "aws-kms")] Aws(#[from] AwsSignerError), #[error(transparent)] Io(#[from] std::io::Error), @@ -29,6 +32,12 @@ pub enum WalletSignerError { InvalidHex(#[from] FromHexError), #[error(transparent)] Ecdsa(#[from] ecdsa::Error), - #[error("{0} cannot sign raw hashes")] - CannotSignRawHash(&'static str), + #[error("foundry was not built with support for {0} signer")] + UnsupportedSigner(&'static str), +} + +impl WalletSignerError { + pub fn aws_unsupported() -> Self { + WalletSignerError::UnsupportedSigner("AWS KMS") + } } diff --git a/crates/wallets/src/multi_wallet.rs b/crates/wallets/src/multi_wallet.rs index c95bb8d0e..7dc78c48e 100644 --- a/crates/wallets/src/multi_wallet.rs +++ b/crates/wallets/src/multi_wallet.rs @@ -375,6 +375,7 @@ impl MultiWalletOpts { } pub async fn aws_signers(&self) -> Result>> { + #[cfg(feature = "aws-kms")] if self.aws { let mut wallets = vec![]; let aws_keys = std::env::var("AWS_KMS_KEY_IDS") @@ -390,6 +391,7 @@ impl MultiWalletOpts { return Ok(Some(wallets)); } + Ok(None) } } diff --git a/crates/wallets/src/wallet.rs b/crates/wallets/src/wallet.rs index 5773e57d8..b2a188b91 100644 --- a/crates/wallets/src/wallet.rs +++ b/crates/wallets/src/wallet.rs @@ -78,7 +78,7 @@ pub struct WalletOpts { pub trezor: bool, /// Use AWS Key Management Service. - #[arg(long, help_heading = "Wallet options - AWS KMS")] + #[arg(long, help_heading = "Wallet options - AWS KMS", hide = !cfg!(feature = "aws-kms"))] pub aws: bool, } diff --git a/crates/wallets/src/wallet_signer.rs b/crates/wallets/src/wallet_signer.rs index 7b1f698b7..15e450289 100644 --- a/crates/wallets/src/wallet_signer.rs +++ b/crates/wallets/src/wallet_signer.rs @@ -4,16 +4,16 @@ use alloy_dyn_abi::TypedData; use alloy_network::TxSigner; use alloy_primitives::{Address, ChainId, B256}; use alloy_signer::{Signature, Signer}; -use alloy_signer_aws::AwsSigner; use alloy_signer_ledger::{HDPath as LedgerHDPath, LedgerSigner}; use alloy_signer_trezor::{HDPath as TrezorHDPath, TrezorSigner}; use alloy_signer_wallet::{coins_bip39::English, LocalWallet, MnemonicBuilder}; use alloy_sol_types::{Eip712Domain, SolStruct}; use async_trait::async_trait; -use aws_config::BehaviorVersion; -use aws_sdk_kms::Client as AwsClient; use std::path::PathBuf; +#[cfg(feature = "aws-kms")] +use {alloy_signer_aws::AwsSigner, aws_config::BehaviorVersion, aws_sdk_kms::Client as AwsClient}; + pub type Result = std::result::Result; /// Wrapper enum around different signers. @@ -26,6 +26,7 @@ pub enum WalletSigner { /// Wrapper around Trezor signer. Trezor(TrezorSigner), /// Wrapper around AWS KMS signer. + #[cfg(feature = "aws-kms")] Aws(AwsSigner), } @@ -41,10 +42,19 @@ impl WalletSigner { } pub async fn from_aws(key_id: String) -> Result { - let config = aws_config::load_defaults(BehaviorVersion::latest()).await; - let client = AwsClient::new(&config); + #[cfg(feature = "aws-kms")] + { + let config = aws_config::load_defaults(BehaviorVersion::latest()).await; + let client = AwsClient::new(&config); + + Ok(Self::Aws(AwsSigner::new(client, key_id, None).await?)) + } - Ok(Self::Aws(AwsSigner::new(client, key_id, None).await?)) + #[cfg(not(feature = "aws-kms"))] + { + let _ = key_id; + Err(WalletSignerError::aws_unsupported()) + } } pub fn from_private_key(private_key: impl AsRef<[u8]>) -> Result { @@ -89,6 +99,7 @@ impl WalletSigner { } } } + #[cfg(feature = "aws-kms")] WalletSigner::Aws(aws) => { senders.push(alloy_signer::Signer::address(aws)); } @@ -124,6 +135,7 @@ macro_rules! delegate { Self::Local($inner) => $e, Self::Ledger($inner) => $e, Self::Trezor($inner) => $e, + #[cfg(feature = "aws-kms")] Self::Aws($inner) => $e, } }; From 30c93a22e359b9b4df11e29e921c1a781eeaf37e Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 24 May 2024 00:44:12 +0300 Subject: [PATCH 307/430] fix(config): deprecated key warnings (#7978) --- crates/config/src/providers/mod.rs | 58 +++++++++++++++++------------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/crates/config/src/providers/mod.rs b/crates/config/src/providers/mod.rs index 239ee0c74..9dbbf38ea 100644 --- a/crates/config/src/providers/mod.rs +++ b/crates/config/src/providers/mod.rs @@ -3,6 +3,7 @@ use figment::{ value::{Dict, Map, Value}, Error, Figment, Metadata, Profile, Provider, }; +use std::collections::BTreeMap; /// Remappings provider pub mod remappings; @@ -43,15 +44,15 @@ impl

WarningsProvider

{ impl WarningsProvider

{ /// Collects all warnings. pub fn collect_warnings(&self) -> Result, Error> { + let data = self.provider.data().unwrap_or_default(); + let mut out = self.old_warnings.clone()?; - // add warning for unknown sections + + // Add warning for unknown sections. out.extend( - self.provider - .data() - .unwrap_or_default() - .keys() + data.keys() .filter(|k| { - k != &Config::PROFILE_SECTION && + **k != Config::PROFILE_SECTION && !Config::STANDALONE_SECTIONS.iter().any(|s| s == k) }) .map(|unknown_section| { @@ -59,26 +60,33 @@ impl WarningsProvider

{ Warning::UnknownSection { unknown_section: unknown_section.clone(), source } }), ); - // add warning for deprecated keys - out.extend( - self.provider - .data() - .unwrap_or_default() - .iter() - .flat_map(|(profile, dict)| dict.keys().map(move |key| format!("{profile}.{key}"))) - .filter_map(|key| { - DEPRECATIONS.iter().find_map(|(deprecated_key, new_value)| { - if key == *deprecated_key { - Some(Warning::DeprecatedKey { - old: deprecated_key.to_string(), - new: new_value.to_string(), - }) - } else { - None - } + + // Add warning for deprecated keys. + let deprecated_key_warning = |key| { + DEPRECATIONS.iter().find_map(|(deprecated_key, new_value)| { + if key == *deprecated_key { + Some(Warning::DeprecatedKey { + old: deprecated_key.to_string(), + new: new_value.to_string(), }) - }), + } else { + None + } + }) + }; + let profiles = data + .iter() + .filter(|(profile, _)| **profile == Config::PROFILE_SECTION) + .map(|(_, dict)| dict); + out.extend(profiles.clone().flat_map(BTreeMap::keys).filter_map(deprecated_key_warning)); + out.extend( + profiles + .filter_map(|dict| dict.get(self.profile.as_str().as_str())) + .filter_map(Value::as_dict) + .flat_map(BTreeMap::keys) + .filter_map(deprecated_key_warning), ); + Ok(out) } } @@ -91,6 +99,7 @@ impl Provider for WarningsProvider

{ Metadata::named("Warnings") } } + fn data(&self) -> Result, Error> { Ok(Map::from([( self.profile.clone(), @@ -100,6 +109,7 @@ impl Provider for WarningsProvider

{ )]), )])) } + fn profile(&self) -> Option { Some(self.profile.clone()) } From b1f4684d5bdcfc9eb777256633af523f7eed1cd7 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 24 May 2024 01:11:01 +0300 Subject: [PATCH 308/430] chore: hide aws flags when not enabled (#7979) --- crates/cast/bin/cmd/wallet/list.rs | 2 +- crates/wallets/src/multi_wallet.rs | 2 +- crates/wallets/src/wallet.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/cast/bin/cmd/wallet/list.rs b/crates/cast/bin/cmd/wallet/list.rs index e534d5713..88b948605 100644 --- a/crates/cast/bin/cmd/wallet/list.rs +++ b/crates/cast/bin/cmd/wallet/list.rs @@ -22,7 +22,7 @@ pub struct ListArgs { trezor: bool, /// List accounts from AWS KMS. - #[arg(long)] + #[arg(long, hide = !cfg!(feature = "aws-kms"))] aws: bool, /// List all configured accounts. diff --git a/crates/wallets/src/multi_wallet.rs b/crates/wallets/src/multi_wallet.rs index 7dc78c48e..459074aaa 100644 --- a/crates/wallets/src/multi_wallet.rs +++ b/crates/wallets/src/multi_wallet.rs @@ -219,7 +219,7 @@ pub struct MultiWalletOpts { pub trezor: bool, /// Use AWS Key Management Service. - #[arg(long, help_heading = "Wallet options - remote")] + #[arg(long, help_heading = "Wallet options - remote", hide = !cfg!(feature = "aws-kms"))] pub aws: bool, } diff --git a/crates/wallets/src/wallet.rs b/crates/wallets/src/wallet.rs index b2a188b91..5b984d701 100644 --- a/crates/wallets/src/wallet.rs +++ b/crates/wallets/src/wallet.rs @@ -78,7 +78,7 @@ pub struct WalletOpts { pub trezor: bool, /// Use AWS Key Management Service. - #[arg(long, help_heading = "Wallet options - AWS KMS", hide = !cfg!(feature = "aws-kms"))] + #[arg(long, help_heading = "Wallet options - remote", hide = !cfg!(feature = "aws-kms"))] pub aws: bool, } From fecac4c49a6eefe678ce7b290e55d5dc2ed5db44 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Fri, 24 May 2024 10:24:32 +0300 Subject: [PATCH 309/430] fix: unchecked getCode (#7982) * fix: unchecked getCode * fix windows test * fix --- crates/cheatcodes/src/fs.rs | 29 ++++++++++++++++------------- crates/config/src/lib.rs | 10 +++++++++- crates/forge/tests/cli/config.rs | 4 +--- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index 33e29475c..308d86641 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -364,19 +364,22 @@ fn get_artifact_code(state: &Cheatcodes, path: &str, deployed: bool) -> Result { - PathBuf::from(format!("{file}/{contract_name}.json")) - } - (None, Some(contract_name)) => { - PathBuf::from(format!("{contract_name}.sol/{contract_name}.json")) - } - (Some(file), None) => { - let name = file.replace(".sol", ""); - PathBuf::from(format!("{file}/{name}.json")) - } - _ => return Err(fmt_err!("Invalid artifact path")), - } + let path_in_artifacts = + match (file.map(|f| f.to_string_lossy().to_string()), contract_name) { + (Some(file), Some(contract_name)) => { + PathBuf::from(format!("{file}/{contract_name}.json")) + } + (None, Some(contract_name)) => { + PathBuf::from(format!("{contract_name}.sol/{contract_name}.json")) + } + (Some(file), None) => { + let name = file.replace(".sol", ""); + PathBuf::from(format!("{file}/{name}.json")) + } + _ => return Err(fmt_err!("Invalid artifact path")), + }; + + state.config.paths.artifacts.join(path_in_artifacts) } }; diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 4b622662a..ec2118c16 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -875,7 +875,15 @@ impl Config { version_manager.install(version)? } } - SolcReq::Local(solc) => Solc::new(solc)?, + SolcReq::Local(solc) => { + if !solc.is_file() { + return Err(SolcError::msg(format!( + "`solc` {} does not exist", + solc.display() + ))) + } + Solc::new(solc)? + } }; return Ok(Some(solc)) } diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index d439cf68d..f463a437e 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -362,9 +362,7 @@ contract Foo {} // fails to use solc that does not exist cmd.forge_fuse().args(["build", "--use", "this/solc/does/not/exist"]); - assert!(cmd - .stderr_lossy() - .contains(r#""this/solc/does/not/exist": No such file or directory"#)); + assert!(cmd.stderr_lossy().contains("`solc` this/solc/does/not/exist does not exist")); // `OTHER_SOLC_VERSION` was installed in previous step, so we can use the path to this directly let local_solc = From 31c13dce772cccb4d4b7adfb2f366c9d8276ebc5 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Fri, 24 May 2024 13:03:31 +0300 Subject: [PATCH 310/430] fix(forge): load state on roll fork for non persisting accounts (#7983) * fix(forge): load state on roll fork for non persisting accounts * Add roll fork invariant test * Nested is_created check --- crates/evm/core/src/backend/mod.rs | 22 +++++++---- crates/forge/tests/it/invariant.rs | 37 +++++++++++++++++++ crates/forge/tests/it/repros.rs | 3 ++ .../invariant/common/InvariantRollFork.t.sol | 29 +++++++++++++++ testdata/default/repros/Issue5739.t.sol | 35 ++++++++++++++++++ 5 files changed, 118 insertions(+), 8 deletions(-) create mode 100644 testdata/default/fuzz/invariant/common/InvariantRollFork.t.sol create mode 100644 testdata/default/repros/Issue5739.t.sol diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index baeb1145b..88a4b060a 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -1176,16 +1176,22 @@ impl DatabaseExt for Backend { merge_journaled_state_data(addr, journaled_state, &mut active.journaled_state); } - // ensure all previously loaded accounts are present in the journaled state to + // Ensure all previously loaded accounts are present in the journaled state to // prevent issues in the new journalstate, e.g. assumptions that accounts are loaded - // if the account is not touched, we reload it, if it's touched we clone it + // if the account is not touched, we reload it, if it's touched we clone it. + // + // Special case for accounts that are not created: we don't merge their state but + // load it in order to reflect their state at the new block (they should explicitly + // be marked as persistent if it is desired to keep state between fork rolls). for (addr, acc) in journaled_state.state.iter() { - if acc.is_touched() { - merge_journaled_state_data( - *addr, - journaled_state, - &mut active.journaled_state, - ); + if acc.is_created() { + if acc.is_touched() { + merge_journaled_state_data( + *addr, + journaled_state, + &mut active.journaled_state, + ); + } } else { let _ = active.journaled_state.load_account(*addr, &mut active.db); } diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index abdd6591a..62e76c274 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -219,6 +219,16 @@ async fn test_invariant() { None, None, )], + ), + ( + "default/fuzz/invariant/common/InvariantRollFork.t.sol:InvariantRollForkTest", + vec![( + "invariant_fork_handler_block()", + false, + Some("revert: too many blocks mined".into()), + None, + None, + )], ) ]), ); @@ -631,3 +641,30 @@ async fn test_invariant_scrape_values() { ]), ); } + +#[tokio::test(flavor = "multi_thread")] +async fn test_invariant_roll_fork_handler() { + let mut opts = TEST_DATA_DEFAULT.test_opts.clone(); + opts.fuzz.seed = Some(U256::from(119u32)); + + let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantRollFork.t.sol"); + let mut runner = TEST_DATA_DEFAULT.runner(); + runner.test_options = opts.clone(); + runner.test_options.invariant.failure_persist_dir = + Some(tempfile::tempdir().unwrap().into_path()); + + let results = runner.test_collect(&filter); + assert_multiple( + &results, + BTreeMap::from([( + "default/fuzz/invariant/common/InvariantRollFork.t.sol:InvariantRollForkTest", + vec![( + "invariant_fork_handler_block()", + false, + Some("revert: too many blocks mined".into()), + None, + None, + )], + )]), + ); +} diff --git a/crates/forge/tests/it/repros.rs b/crates/forge/tests/it/repros.rs index 6f9576765..75856c5e4 100644 --- a/crates/forge/tests/it/repros.rs +++ b/crates/forge/tests/it/repros.rs @@ -326,3 +326,6 @@ test_repro!(6634; |config| { }); test_repro!(7481); + +// https://github.com/foundry-rs/foundry/issues/5739 +test_repro!(5739); diff --git a/testdata/default/fuzz/invariant/common/InvariantRollFork.t.sol b/testdata/default/fuzz/invariant/common/InvariantRollFork.t.sol new file mode 100644 index 000000000..7934a2916 --- /dev/null +++ b/testdata/default/fuzz/invariant/common/InvariantRollFork.t.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract RollForkHandler is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + function work() external { + vm.rollFork(block.number + 1); + } +} + +contract InvariantRollForkTest is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + RollForkHandler forkHandler; + + function setUp() public { + vm.createSelectFork("rpcAlias", 19812632); + forkHandler = new RollForkHandler(); + } + + /// forge-config: default.invariant.runs = 2 + /// forge-config: default.invariant.depth = 4 + function invariant_fork_handler_block() public { + require(block.number < 19812634, "too many blocks mined"); + } +} diff --git a/testdata/default/repros/Issue5739.t.sol b/testdata/default/repros/Issue5739.t.sol new file mode 100644 index 000000000..2864c0cbf --- /dev/null +++ b/testdata/default/repros/Issue5739.t.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +interface IERC20 { + function totalSupply() external view returns (uint256 supply); +} + +// https://github.com/foundry-rs/foundry/issues/5739 +contract Issue5739Test is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + IERC20 dai; + + function setUp() public { + vm.createSelectFork("rpcAlias", 19000000); + dai = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); + } + + function testRollForkStateUpdated() public { + // dai not persistent so state should be updated between rolls + assertEq(dai.totalSupply(), 3723031040751006502480211083); + vm.rollFork(19925849); + assertEq(dai.totalSupply(), 3320242279303699674318705475); + } + + function testRollForkStatePersisted() public { + // make dai persistent so state is preserved between rolls + vm.makePersistent(address(dai)); + assertEq(dai.totalSupply(), 3723031040751006502480211083); + vm.rollFork(19925849); + assertEq(dai.totalSupply(), 3723031040751006502480211083); + } +} From a03fed9aeb9986e809f78e7204a5b8e979702b5e Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 24 May 2024 22:09:12 +0300 Subject: [PATCH 311/430] chore(deps): bump alloy (#7990) --- Cargo.lock | 86 +++++++++++--------- Cargo.toml | 57 ++++++------- crates/anvil/core/src/eth/transaction/mod.rs | 3 +- crates/anvil/src/eth/api.rs | 2 +- crates/anvil/src/eth/backend/mem/mod.rs | 6 +- 5 files changed, 82 insertions(+), 72 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 131c6351d..f5fffa400 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,7 +79,7 @@ dependencies = [ [[package]] name = "alloy-consensus" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" dependencies = [ "alloy-eips", "alloy-primitives", @@ -92,7 +92,7 @@ dependencies = [ [[package]] name = "alloy-contract" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -132,7 +132,7 @@ dependencies = [ [[package]] name = "alloy-eips" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -147,7 +147,7 @@ dependencies = [ [[package]] name = "alloy-genesis" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" dependencies = [ "alloy-primitives", "alloy-serde", @@ -170,7 +170,7 @@ dependencies = [ [[package]] name = "alloy-json-rpc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" dependencies = [ "alloy-primitives", "serde", @@ -182,7 +182,7 @@ dependencies = [ [[package]] name = "alloy-network" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" dependencies = [ "alloy-consensus", "alloy-eips", @@ -192,6 +192,7 @@ dependencies = [ "alloy-signer", "alloy-sol-types", "async-trait", + "auto_impl", "futures-utils-wasm", "thiserror", ] @@ -226,8 +227,9 @@ dependencies = [ [[package]] name = "alloy-provider" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" dependencies = [ + "alloy-chains", "alloy-eips", "alloy-json-rpc", "alloy-network", @@ -249,6 +251,7 @@ dependencies = [ "lru", "pin-project", "reqwest", + "serde", "serde_json", "tokio", "tracing", @@ -258,7 +261,7 @@ dependencies = [ [[package]] name = "alloy-pubsub" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -298,7 +301,7 @@ dependencies = [ [[package]] name = "alloy-rpc-client" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -322,7 +325,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" dependencies = [ "alloy-consensus", "alloy-eips", @@ -340,7 +343,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" dependencies = [ "alloy-consensus", "alloy-eips", @@ -357,7 +360,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -369,7 +372,7 @@ dependencies = [ [[package]] name = "alloy-serde" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" dependencies = [ "alloy-primitives", "serde", @@ -379,7 +382,7 @@ dependencies = [ [[package]] name = "alloy-signer" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -394,7 +397,7 @@ dependencies = [ [[package]] name = "alloy-signer-aws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" dependencies = [ "alloy-consensus", "alloy-network", @@ -411,7 +414,7 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -430,7 +433,7 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" dependencies = [ "alloy-consensus", "alloy-network", @@ -446,7 +449,7 @@ dependencies = [ [[package]] name = "alloy-signer-wallet" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" dependencies = [ "alloy-consensus", "alloy-network", @@ -537,7 +540,7 @@ dependencies = [ [[package]] name = "alloy-transport" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" dependencies = [ "alloy-json-rpc", "base64 0.22.1", @@ -555,7 +558,7 @@ dependencies = [ [[package]] name = "alloy-transport-http" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -569,7 +572,7 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -589,7 +592,7 @@ dependencies = [ [[package]] name = "alloy-transport-ws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=f415827#f41582792950a3bf4c334c588a5497468e1f8149" +source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -604,9 +607,9 @@ dependencies = [ [[package]] name = "alloy-trie" -version = "0.3.1" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beb28aa4ecd32fdfa1b1bdd111ff7357dd562c6b2372694cf9e613434fcba659" +checksum = "03704f265cbbb943b117ecb5055fd46e8f41e7dc8a58b1aed20bcd40ace38c15" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -750,7 +753,7 @@ dependencies = [ "foundry-test-utils", "futures", "hyper 1.3.1", - "itertools 0.12.1", + "itertools 0.13.0", "k256", "parking_lot", "rand", @@ -1732,7 +1735,7 @@ dependencies = [ "foundry-wallets", "futures", "indicatif", - "itertools 0.12.1", + "itertools 0.13.0", "rand", "rayon", "regex", @@ -3084,7 +3087,7 @@ dependencies = [ "humantime-serde", "hyper 1.3.1", "indicatif", - "itertools 0.12.1", + "itertools 0.13.0", "mockall", "once_cell", "opener 0.6.1", @@ -3131,7 +3134,7 @@ dependencies = [ "foundry-common", "foundry-compilers", "foundry-config", - "itertools 0.12.1", + "itertools 0.13.0", "mdbook", "once_cell", "rayon", @@ -3151,7 +3154,7 @@ dependencies = [ "alloy-primitives", "ariadne", "foundry-config", - "itertools 0.12.1", + "itertools 0.13.0", "similar-asserts", "solang-parser", "thiserror", @@ -3192,7 +3195,7 @@ dependencies = [ "foundry-wallets", "futures", "indicatif", - "itertools 0.12.1", + "itertools 0.13.0", "parking_lot", "revm-inspectors", "semver 1.0.23", @@ -3223,7 +3226,7 @@ dependencies = [ "foundry-evm", "foundry-test-utils", "futures", - "itertools 0.12.1", + "itertools 0.13.0", "once_cell", "regex", "reqwest", @@ -3287,7 +3290,7 @@ dependencies = [ "foundry-config", "foundry-evm-core", "foundry-wallets", - "itertools 0.12.1", + "itertools 0.13.0", "jsonpath_lib", "k256", "p256", @@ -3540,7 +3543,7 @@ dependencies = [ "foundry-macros", "foundry-test-utils", "futures", - "itertools 0.12.1", + "itertools 0.13.0", "once_cell", "parking_lot", "revm", @@ -3584,7 +3587,7 @@ dependencies = [ "foundry-evm-coverage", "foundry-evm-traces", "indexmap", - "itertools 0.12.1", + "itertools 0.13.0", "parking_lot", "proptest", "rand", @@ -3610,7 +3613,7 @@ dependencies = [ "foundry-config", "foundry-evm-core", "futures", - "itertools 0.12.1", + "itertools 0.13.0", "once_cell", "revm-inspectors", "serde", @@ -3685,7 +3688,7 @@ dependencies = [ "eyre", "foundry-common", "foundry-config", - "itertools 0.12.1", + "itertools 0.13.0", "rpassword", "serde", "thiserror", @@ -4701,6 +4704,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -6340,7 +6352,7 @@ dependencies = [ [[package]] name = "revm-inspectors" version = "0.1.0" -source = "git+https://github.com/paradigmxyz/revm-inspectors?rev=098ab30#098ab30faf3254c8ba3159b1af006d71d3577d95" +source = "git+https://github.com/paradigmxyz/revm-inspectors?rev=0d3f1f4#0d3f1f4e41bfa2e1e56d37994494934edf0a11ef" dependencies = [ "alloy-primitives", "alloy-rpc-types", diff --git a/Cargo.toml b/Cargo.toml index 4f5b962b2..fc19e062d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -144,7 +144,7 @@ foundry-compilers = { version = "0.5", default-features = false } # no default features to avoid c-kzg revm = { version = "9", default-features = false } revm-primitives = { version = "4", default-features = false } -revm-inspectors = { git = "https://github.com/paradigmxyz/revm-inspectors", rev = "098ab30", features = [ +revm-inspectors = { git = "https://github.com/paradigmxyz/revm-inspectors", rev = "0d3f1f4", features = [ "serde", ] } @@ -152,52 +152,49 @@ revm-inspectors = { git = "https://github.com/paradigmxyz/revm-inspectors", rev ethers-contract-abigen = { version = "2.0.14", default-features = false } ## alloy -alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-signer-wallet = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } -alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "f415827", default-features = false } +alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } +alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } +alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } +alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } +alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } +alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } +alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } +alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } +alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } +alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } +alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } +alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } +alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } +alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } +alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } +alloy-signer-wallet = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } +alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } +alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } +alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } +alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } +alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } +alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } +alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } alloy-primitives = { version = "0.7.1", features = ["getrandom"] } alloy-dyn-abi = "0.7.1" alloy-json-abi = "0.7.1" alloy-sol-types = "0.7.1" syn-solidity = "0.7.1" alloy-chains = "0.1" -alloy-trie = "0.3.1" +alloy-trie = "0.4.1" alloy-rlp = "0.3.3" solang-parser = "=0.3.3" ## misc arrayvec = "0.7" base64 = "0.22" -chrono = { version = "0.4", default-features = false, features = [ - "clock", - "std", -] } +chrono = { version = "0.4", default-features = false, features = ["clock", "std"] } color-eyre = "0.6" derive_more = "0.99" evm-disassembler = "0.5" eyre = "0.6" hex = { package = "const-hex", version = "1.6", features = ["hex"] } -itertools = "0.12" +itertools = "0.13" jsonpath_lib = "0.3" k256 = "0.13" similar-asserts = "1.5" diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index eced8fe33..6710ce1bc 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -2,8 +2,9 @@ use crate::eth::transaction::optimism::{DepositTransaction, DepositTransactionRequest}; use alloy_consensus::{ + transaction::eip4844::{TxEip4844, TxEip4844Variant, TxEip4844WithSidecar}, AnyReceiptEnvelope, Receipt, ReceiptEnvelope, ReceiptWithBloom, Signed, TxEip1559, TxEip2930, - TxEip4844, TxEip4844Variant, TxEip4844WithSidecar, TxEnvelope, TxLegacy, TxReceipt, + TxEnvelope, TxLegacy, TxReceipt, }; use alloy_eips::eip2718::{Decodable2718, Encodable2718}; use alloy_primitives::{Address, Bloom, Bytes, Log, Signature, TxHash, TxKind, B256, U256}; diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 70641043f..98c68abf3 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -31,7 +31,7 @@ use crate::{ revm::primitives::{BlobExcessGasAndPrice, Output}, ClientFork, LoggingManager, Miner, MiningMode, StorageInfo, }; -use alloy_consensus::TxEip4844Variant; +use alloy_consensus::transaction::eip4844::TxEip4844Variant; use alloy_dyn_abi::TypedData; use alloy_eips::eip2718::Encodable2718; use alloy_network::eip2718::Decodable2718; diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 861bad0a5..3668c4e3c 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -44,7 +44,7 @@ use alloy_rpc_types_trace::{ geth::{DefaultFrame, GethDebugTracingOptions, GethDefaultTracingOptions, GethTrace}, parity::LocalizedTransactionTrace, }; -use alloy_trie::{HashBuilder, Nibbles}; +use alloy_trie::{proof::ProofRetainer, HashBuilder, Nibbles}; use anvil_core::{ eth::{ block::{Block, BlockInfo}, @@ -2226,7 +2226,7 @@ impl Backend { let account = db.get(&address).cloned().unwrap_or_default(); let mut builder = HashBuilder::default() - .with_proof_retainer(vec![Nibbles::unpack(keccak256(address))]); + .with_proof_retainer(ProofRetainer::new(vec![Nibbles::unpack(keccak256(address))])); for (key, account) in trie_accounts(db) { builder.add_leaf(key, &account); @@ -2508,7 +2508,7 @@ pub fn transaction_build( pub fn prove_storage(storage: &HashMap, keys: &[B256]) -> Vec> { let keys: Vec<_> = keys.iter().map(|key| Nibbles::unpack(keccak256(key))).collect(); - let mut builder = HashBuilder::default().with_proof_retainer(keys.clone()); + let mut builder = HashBuilder::default().with_proof_retainer(ProofRetainer::new(keys.clone())); for (key, value) in trie_storage(storage) { builder.add_leaf(key, &value); From 10f1402b9dc7c4d0fdffd47d5c842cf416f656ea Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 24 May 2024 23:22:52 +0300 Subject: [PATCH 312/430] fix(debugger): don't panic on pc-ic / sourcemap mismatch (#7991) --- crates/debugger/src/tui/draw.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/debugger/src/tui/draw.rs b/crates/debugger/src/tui/draw.rs index ffe348f47..ad1645d27 100644 --- a/crates/debugger/src/tui/draw.rs +++ b/crates/debugger/src/tui/draw.rs @@ -350,17 +350,18 @@ impl DebuggerContext<'_> { } else { contract_source.deployed_bytecode.bytecode.as_ref()? }; - let mut source_map = bytecode.source_map()?.ok()?; + let source_map = bytecode.source_map()?.ok()?; let pc_ic_map = if is_create { create_map } else { rt_map }; let ic = pc_ic_map.get(pc)?; - let source_element = source_map.swap_remove(ic); + let source_element = source_map.get(ic)?; // if the source element has an index, find the sourcemap for that index source_element .index - .and_then(|index| // if index matches current file_id, return current source code - (index == file_id).then(|| (source_element.clone(), source_code))) + .and_then(|index| { + (index == file_id).then(|| (source_element.clone(), source_code)) + }) .or_else(|| { // otherwise find the source code for the element's index self.debugger From 83e6aec038760a58dbab1acd992ed5ce18b9d90b Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sat, 25 May 2024 11:43:09 +0300 Subject: [PATCH 313/430] refactor: clean-up cast send (#7967) * refactor: clean-up cast build_tx * doc * wip * refactor access-list and send * fixes --- crates/cast/bin/cmd/access_list.rs | 106 ++------- crates/cast/bin/cmd/call.rs | 177 ++++++---------- crates/cast/bin/cmd/estimate.rs | 114 ++++------ crates/cast/bin/cmd/mktx.rs | 29 +-- crates/cast/bin/cmd/send.rs | 94 ++------ crates/cast/bin/tx.rs | 330 +++++++++++++++++++++-------- crates/cli/src/utils/abi.rs | 4 +- 7 files changed, 401 insertions(+), 453 deletions(-) diff --git a/crates/cast/bin/cmd/access_list.rs b/crates/cast/bin/cmd/access_list.rs index 0e8fd042d..0ec110bae 100644 --- a/crates/cast/bin/cmd/access_list.rs +++ b/crates/cast/bin/cmd/access_list.rs @@ -1,17 +1,15 @@ -use alloy_network::{AnyNetwork, TransactionBuilder}; -use alloy_primitives::{Address, Bytes}; -use alloy_provider::Provider; -use alloy_rpc_types::{BlockId, TransactionRequest, WithOtherFields}; -use alloy_transport::Transport; +use crate::tx::CastTxBuilder; +use alloy_primitives::TxKind; +use alloy_rpc_types::BlockId; use cast::Cast; use clap::Parser; use eyre::Result; use foundry_cli::{ opts::{EthereumOpts, TransactionOpts}, - utils::{self, parse_function_args}, + utils, }; use foundry_common::ens::NameOrAddress; -use foundry_config::{Chain, Config}; +use foundry_config::Config; use std::str::FromStr; /// CLI arguments for `cast access-list`. @@ -32,14 +30,6 @@ pub struct AccessListArgs { #[arg(value_name = "ARGS")] args: Vec, - /// The data for the transaction. - #[arg( - long, - value_name = "DATA", - conflicts_with_all = &["sig", "args"] - )] - data: Option, - /// The block height to query at. /// /// Can also be the tags earliest, finalized, safe, latest, or pending. @@ -59,86 +49,32 @@ pub struct AccessListArgs { impl AccessListArgs { pub async fn run(self) -> Result<()> { - let AccessListArgs { to, sig, args, data, tx, eth, block, json: to_json } = self; + let AccessListArgs { to, sig, args, tx, eth, block, json: to_json } = self; let config = Config::from(ð); let provider = utils::get_provider(&config)?; - let chain = utils::get_chain(config.chain, &provider).await?; let sender = eth.wallet.sender().await; - let etherscan_api_key = config.get_etherscan_api_key(Some(chain)); - let to = match to { - Some(to) => Some(to.resolve(&provider).await?), - None => None, + let tx_kind = if let Some(to) = to { + TxKind::Call(to.resolve(&provider).await?) + } else { + TxKind::Create }; - access_list( - &provider, - etherscan_api_key.as_deref(), - sender, - to, - sig, - args, - data, - tx, - chain, - block, - to_json, - ) - .await?; - Ok(()) - } -} + let (tx, _) = CastTxBuilder::new(&provider, tx, &config) + .await? + .with_tx_kind(tx_kind) + .with_code_sig_and_args(None, sig, args) + .await? + .build_raw(sender) + .await?; -#[allow(clippy::too_many_arguments)] -async fn access_list, T: Transport + Clone>( - provider: P, - etherscan_api_key: Option<&str>, - from: Address, - to: Option

, - sig: Option, - args: Vec, - data: Option, - tx: TransactionOpts, - chain: Chain, - block: Option, - to_json: bool, -) -> Result<()> { - let mut req = WithOtherFields::::default() - .with_from(from) - .with_value(tx.value.unwrap_or_default()) - .with_chain_id(chain.id()); + let cast = Cast::new(&provider); - if let Some(to) = to { - req.set_to(to); - } else { - req.set_kind(alloy_primitives::TxKind::Create); - } + let access_list: String = cast.access_list(&tx, block, to_json).await?; - if let Some(gas_limit) = tx.gas_limit { - req.set_gas_limit(gas_limit.to()); - } + println!("{}", access_list); - if let Some(nonce) = tx.nonce { - req.set_nonce(nonce.to()); + Ok(()) } - - let data = if let Some(sig) = sig { - parse_function_args(&sig, args, to, chain, &provider, etherscan_api_key).await?.0 - } else if let Some(data) = data { - // Note: `sig+args` and `data` are mutually exclusive - hex::decode(data)? - } else { - Vec::new() - }; - - req.set_input::(data.into()); - - let cast = Cast::new(&provider); - - let access_list: String = cast.access_list(&req, block, to_json).await?; - - println!("{}", access_list); - - Ok(()) } diff --git a/crates/cast/bin/cmd/call.rs b/crates/cast/bin/cmd/call.rs index 129e3da30..10a97ea00 100644 --- a/crates/cast/bin/cmd/call.rs +++ b/crates/cast/bin/cmd/call.rs @@ -1,12 +1,12 @@ -use alloy_network::TransactionBuilder; +use crate::tx::CastTxBuilder; use alloy_primitives::{TxKind, U256}; -use alloy_rpc_types::{BlockId, TransactionRequest, WithOtherFields}; +use alloy_rpc_types::BlockId; use cast::Cast; use clap::Parser; use eyre::Result; use foundry_cli::{ opts::{EthereumOpts, TransactionOpts}, - utils::{self, handle_traces, parse_ether_value, parse_function_args, TraceResult}, + utils::{self, handle_traces, parse_ether_value, TraceResult}, }; use foundry_common::ens::NameOrAddress; use foundry_compilers::EvmVersion; @@ -97,10 +97,9 @@ impl CallArgs { pub async fn run(self) -> Result<()> { let CallArgs { to, - sig, - args, - data, - tx, + mut sig, + mut args, + mut tx, eth, command, block, @@ -108,132 +107,78 @@ impl CallArgs { evm_version, debug, labels, + data, } = self; + if let Some(data) = data { + sig = Some(data); + } + let config = Config::from(ð); let provider = utils::get_provider(&config)?; - let chain = utils::get_chain(config.chain, &provider).await?; let sender = eth.wallet.sender().await; - let etherscan_api_key = config.get_etherscan_api_key(Some(chain)); - let to = match to { - Some(to) => Some(to.resolve(&provider).await?), - None => None, + let tx_kind = if let Some(to) = to { + TxKind::Call(to.resolve(&provider).await?) + } else { + TxKind::Create }; - let mut req = WithOtherFields::::default() - .with_from(sender) - .with_value(tx.value.unwrap_or_default()); - - if let Some(to) = to { - req.set_to(to); + let code = if let Some(CallSubcommands::Create { + code, + sig: create_sig, + args: create_args, + value, + }) = command + { + sig = create_sig; + args = create_args; + if let Some(value) = value { + tx.value = Some(value); + } + Some(code) } else { - req.set_kind(alloy_primitives::TxKind::Create); - } - - if let Some(nonce) = tx.nonce { - req.set_nonce(nonce.to()); - } - - let (data, func) = match command { - Some(CallSubcommands::Create { code, sig, args, value }) => { - if let Some(value) = value { - req.set_value(value); - } - - let mut data = hex::decode(code)?; - - if let Some(s) = sig { - let (mut constructor_args, _) = parse_function_args( - &s, - args, - None, - chain, - &provider, - etherscan_api_key.as_deref(), - ) - .await?; - data.append(&mut constructor_args); - } - - if trace { - let figment = Config::figment_with_root(find_project_root_path(None).unwrap()) - .merge(eth.rpc); - - let evm_opts = figment.extract::()?; - - let (env, fork, chain) = - TracingExecutor::get_fork_material(&config, evm_opts).await?; - - let mut executor = TracingExecutor::new(env, fork, evm_version, debug); + None + }; - let trace = match executor.deploy( - sender, - data.into(), - req.value.unwrap_or_default(), - None, - ) { + let (tx, func) = CastTxBuilder::new(&provider, tx, &config) + .await? + .with_tx_kind(tx_kind) + .with_code_sig_and_args(code, sig, args) + .await? + .build_raw(sender) + .await?; + + if trace { + let figment = + Config::figment_with_root(find_project_root_path(None).unwrap()).merge(eth.rpc); + let evm_opts = figment.extract::()?; + let (env, fork, chain) = TracingExecutor::get_fork_material(&config, evm_opts).await?; + let mut executor = TracingExecutor::new(env, fork, evm_version, debug); + + let value = tx.value.unwrap_or_default(); + let input = tx.inner.input.into_input().unwrap_or_default(); + + let trace = match tx_kind { + TxKind::Create => { + let deploy_result = executor.deploy(sender, input, value, None); + + match deploy_result { Ok(deploy_result) => TraceResult::from(deploy_result), Err(evm_err) => TraceResult::try_from(evm_err)?, - }; - - handle_traces(trace, &config, chain, labels, debug).await?; - - return Ok(()); + } } - - (data, None) - } - _ => { - // fill first here because we need to use the builder in the conditional - let (data, func) = if let Some(sig) = sig { - parse_function_args( - &sig, - args, - to, - chain, - &provider, - etherscan_api_key.as_deref(), - ) - .await? - } else if let Some(data) = data { - // Note: `sig+args` and `data` are mutually exclusive - (hex::decode(data)?, None) - } else { - (Vec::new(), None) - }; - - if trace { - let figment = Config::figment_with_root(find_project_root_path(None).unwrap()) - .merge(eth.rpc); - - let evm_opts = figment.extract::()?; - - let (env, fork, chain) = - TracingExecutor::get_fork_material(&config, evm_opts).await?; - - let mut executor = TracingExecutor::new(env, fork, evm_version, debug); - - let to = if let Some(TxKind::Call(to)) = req.to { Some(to) } else { None }; - let trace = TraceResult::from(executor.call_raw_committing( - sender, - to.expect("an address to be here"), - data.into(), - req.value.unwrap_or_default(), - )?); - - handle_traces(trace, &config, chain, labels, debug).await?; - - return Ok(()); + TxKind::Call(to) => { + TraceResult::from(executor.call_raw_committing(sender, to, input, value)?) } + }; - (data, func) - } - }; + handle_traces(trace, &config, chain, labels, debug).await?; - req.set_input(data); + return Ok(()); + } - println!("{}", Cast::new(provider).call(&req, func.as_ref(), block).await?); + println!("{}", Cast::new(provider).call(&tx, func.as_ref(), block).await?); Ok(()) } diff --git a/crates/cast/bin/cmd/estimate.rs b/crates/cast/bin/cmd/estimate.rs index c000a79c1..5e60c9796 100644 --- a/crates/cast/bin/cmd/estimate.rs +++ b/crates/cast/bin/cmd/estimate.rs @@ -1,15 +1,15 @@ -use alloy_network::TransactionBuilder; -use alloy_primitives::U256; +use crate::tx::CastTxBuilder; +use alloy_primitives::{TxKind, U256}; use alloy_provider::Provider; -use alloy_rpc_types::{TransactionRequest, WithOtherFields}; +use alloy_rpc_types::BlockId; use clap::Parser; use eyre::Result; use foundry_cli::{ - opts::{EtherscanOpts, RpcOpts}, - utils::{self, parse_ether_value, parse_function_args}, + opts::{EthereumOpts, TransactionOpts}, + utils::{self, parse_ether_value}, }; use foundry_common::ens::NameOrAddress; -use foundry_config::{figment::Figment, Config}; +use foundry_config::Config; use std::str::FromStr; /// CLI arguments for `cast estimate`. @@ -25,32 +25,20 @@ pub struct EstimateArgs { /// The arguments of the function to call. args: Vec, - /// The sender account. - #[arg( - short, - long, - value_parser = NameOrAddress::from_str, - default_value = "0x0000000000000000000000000000000000000000", - env = "ETH_FROM", - )] - from: NameOrAddress, - - /// Ether to send in the transaction. + /// The block height to query at. /// - /// Either specified in wei, or as a string with a unit type: - /// - /// Examples: 1ether, 10gwei, 0.01ether - #[arg(long, value_parser = parse_ether_value)] - value: Option, + /// Can also be the tags earliest, finalized, safe, latest, or pending. + #[arg(long, short = 'B')] + block: Option, - #[command(flatten)] - rpc: RpcOpts, + #[command(subcommand)] + command: Option, #[command(flatten)] - etherscan: EtherscanOpts, + tx: TransactionOpts, - #[command(subcommand)] - command: Option, + #[command(flatten)] + eth: EthereumOpts, } #[derive(Debug, Parser)] @@ -79,56 +67,44 @@ pub enum EstimateSubcommands { impl EstimateArgs { pub async fn run(self) -> Result<()> { - let EstimateArgs { from, to, sig, args, value, rpc, etherscan, command } = self; + let EstimateArgs { to, mut sig, mut args, mut tx, block, eth, command } = self; - let figment = Figment::from(Config::figment()).merge(etherscan).merge(rpc); - let config = Config::try_from(figment)?; + let config = Config::from(ð); let provider = utils::get_provider(&config)?; - let chain = utils::get_chain(config.chain, &provider).await?; - let api_key = config.get_etherscan_api_key(Some(chain)); - - let from = from.resolve(&provider).await?; - let to = match to { - Some(to) => Some(to.resolve(&provider).await?), - None => None, - }; - - let mut req = WithOtherFields::::default() - .with_from(from) - .with_value(value.unwrap_or_default()); + let sender = eth.wallet.sender().await; - if let Some(to) = to { - req.set_to(to); + let tx_kind = if let Some(to) = to { + TxKind::Call(to.resolve(&provider).await?) } else { - req.set_kind(alloy_primitives::TxKind::Create); - } - - let data = match command { - Some(EstimateSubcommands::Create { code, sig, args, value }) => { - if let Some(value) = value { - req.set_value(value); - } - - let mut data = hex::decode(code)?; - - if let Some(s) = sig { - let (mut constructor_args, _) = - parse_function_args(&s, args, to, chain, &provider, api_key.as_deref()) - .await?; - data.append(&mut constructor_args); - } + TxKind::Create + }; - data - } - _ => { - let sig = sig.ok_or_else(|| eyre::eyre!("Function signature must be provided."))?; - parse_function_args(&sig, args, to, chain, &provider, api_key.as_deref()).await?.0 + let code = if let Some(EstimateSubcommands::Create { + code, + sig: create_sig, + args: create_args, + value, + }) = command + { + sig = create_sig; + args = create_args; + if let Some(value) = value { + tx.value = Some(value); } + Some(code) + } else { + None }; - req.set_input(data); + let (tx, _) = CastTxBuilder::new(&provider, tx, &config) + .await? + .with_tx_kind(tx_kind) + .with_code_sig_and_args(code, sig, args) + .await? + .build_raw(sender) + .await?; - let gas = provider.estimate_gas(&req).await?; + let gas = provider.estimate_gas(&tx).block_id(block.unwrap_or_default()).await?; println!("{gas}"); Ok(()) } @@ -141,6 +117,6 @@ mod tests { #[test] fn parse_estimate_value() { let args: EstimateArgs = EstimateArgs::parse_from(["foundry-cli", "--value", "100"]); - assert!(args.value.is_some()); + assert!(args.tx.value.is_some()); } } diff --git a/crates/cast/bin/cmd/mktx.rs b/crates/cast/bin/cmd/mktx.rs index f8f6d9fb2..999688b19 100644 --- a/crates/cast/bin/cmd/mktx.rs +++ b/crates/cast/bin/cmd/mktx.rs @@ -1,7 +1,5 @@ -use crate::tx; +use crate::tx::{self, CastTxBuilder}; use alloy_network::{eip2718::Encodable2718, EthereumSigner, TransactionBuilder}; -use alloy_primitives::U64; -use alloy_provider::Provider; use alloy_signer::Signer; use clap::Parser; use eyre::Result; @@ -28,10 +26,6 @@ pub struct MakeTxArgs { /// The arguments of the function to call. args: Vec, - /// Reuse the latest nonce for the sender account. - #[arg(long, conflicts_with = "nonce")] - resend: bool, - #[command(subcommand)] command: Option, @@ -60,7 +54,7 @@ pub enum MakeTxSubcommands { impl MakeTxArgs { pub async fn run(self) -> Result<()> { - let MakeTxArgs { to, mut sig, mut args, resend, command, mut tx, eth } = self; + let MakeTxArgs { to, mut sig, mut args, command, tx, eth } = self; let code = if let Some(MakeTxSubcommands::Create { code, @@ -75,12 +69,10 @@ impl MakeTxArgs { None }; - tx::validate_to_address(&code, &to)?; - let config = Config::from(ð); let provider = utils::get_provider(&config)?; - let chain = utils::get_chain(config.chain, &provider).await?; - let api_key = config.get_etherscan_api_key(Some(chain)); + + let tx_kind = tx::resolve_tx_kind(&provider, &code, &to).await?; // Retrieve the signer, and bail if it can't be constructed. let signer = eth.wallet.signer().await?; @@ -88,14 +80,15 @@ impl MakeTxArgs { tx::validate_from_address(eth.wallet.from, from)?; - if resend { - tx.nonce = Some(U64::from(provider.get_transaction_count(from).await?)); - } - let provider = get_provider(&config)?; - let (tx, _) = - tx::build_tx(&provider, from, to, code, sig, args, tx, chain, api_key, None).await?; + let (tx, _) = CastTxBuilder::new(provider, tx, &config) + .await? + .with_tx_kind(tx_kind) + .with_code_sig_and_args(code, sig, args) + .await? + .build(from) + .await?; let tx = tx.build(&EthereumSigner::new(signer)).await?; diff --git a/crates/cast/bin/cmd/send.rs b/crates/cast/bin/cmd/send.rs index 06c8b99f2..ea9cfcdcd 100644 --- a/crates/cast/bin/cmd/send.rs +++ b/crates/cast/bin/cmd/send.rs @@ -1,7 +1,7 @@ -use crate::tx; +use crate::tx::{self, CastTxBuilder}; use alloy_network::{AnyNetwork, EthereumSigner}; -use alloy_primitives::{Address, U64}; use alloy_provider::{Provider, ProviderBuilder}; +use alloy_rpc_types::{TransactionRequest, WithOtherFields}; use alloy_signer::Signer; use alloy_transport::Transport; use cast::Cast; @@ -12,7 +12,7 @@ use foundry_cli::{ utils, }; use foundry_common::{cli_warn, ens::NameOrAddress}; -use foundry_config::{Chain, Config}; +use foundry_config::Config; use std::{path::PathBuf, str::FromStr}; /// CLI arguments for `cast send`. @@ -42,10 +42,6 @@ pub struct SendTxArgs { #[arg(long, short, help_heading = "Display options")] json: bool, - /// Reuse the latest nonce for the sender account. - #[arg(long, conflicts_with = "nonce")] - resend: bool, - #[command(subcommand)] command: Option, @@ -88,10 +84,9 @@ impl SendTxArgs { mut sig, cast_async, mut args, - mut tx, + tx, confirmations, json: to_json, - resend, command, unlocked, path, @@ -112,17 +107,16 @@ impl SendTxArgs { None }; - tx::validate_to_address(&code, &to)?; - let config = Config::from(ð); let provider = utils::get_provider(&config)?; - let chain = utils::get_chain(config.chain, &provider).await?; - let api_key = config.get_etherscan_api_key(Some(chain)); + let tx_kind = tx::resolve_tx_kind(&provider, &code, &to).await?; - let to = match to { - Some(to) => Some(to.resolve(&provider).await?), - None => None, - }; + let builder = CastTxBuilder::new(&provider, tx, &config) + .await? + .with_tx_kind(tx_kind) + .with_code_sig_and_args(code, sig, args) + .await? + .with_blob_data(blob_data)?; // Case 1: // Default to sending via eth_sendTransaction if the --unlocked flag is passed. @@ -148,26 +142,9 @@ impl SendTxArgs { } } - if resend { - tx.nonce = Some(U64::from(provider.get_transaction_count(config.sender).await?)); - } + let (tx, _) = builder.build(config.sender).await?; - cast_send( - provider, - config.sender, - to, - code, - sig, - args, - tx, - chain, - api_key, - cast_async, - confirmations, - to_json, - blob_data, - ) - .await + cast_send(provider, tx, cast_async, confirmations, to_json).await // Case 2: // An option to use a local signer was provided. // If we cannot successfully instantiate a local signer, then we will assume we don't have @@ -179,54 +156,25 @@ impl SendTxArgs { tx::validate_from_address(eth.wallet.from, from)?; - if resend { - tx.nonce = Some(U64::from(provider.get_transaction_count(from).await?)); - } - let signer = EthereumSigner::from(signer); - let provider = - ProviderBuilder::<_, _, AnyNetwork>::default().signer(signer).on_provider(provider); - - cast_send( - provider, - from, - to, - code, - sig, - args, - tx, - chain, - api_key, - cast_async, - confirmations, - to_json, - blob_data, - ) - .await + let provider = ProviderBuilder::<_, _, AnyNetwork>::default() + .signer(signer) + .on_provider(&provider); + + let (tx, _) = builder.build(from).await?; + + cast_send(provider, tx, cast_async, confirmations, to_json).await } } } -#[allow(clippy::too_many_arguments)] async fn cast_send, T: Transport + Clone>( provider: P, - from: Address, - to: Option
, - code: Option, - sig: Option, - args: Vec, - tx: TransactionOpts, - chain: Chain, - etherscan_api_key: Option, + tx: WithOtherFields, cast_async: bool, confs: u64, to_json: bool, - blob_data: Option>, ) -> Result<()> { - let (tx, _) = - tx::build_tx(&provider, from, to, code, sig, args, tx, chain, etherscan_api_key, blob_data) - .await?; - let cast = Cast::new(provider); let pending_tx = cast.send(tx).await?; diff --git a/crates/cast/bin/tx.rs b/crates/cast/bin/tx.rs index d4c9e205c..77ed93bab 100644 --- a/crates/cast/bin/tx.rs +++ b/crates/cast/bin/tx.rs @@ -1,14 +1,17 @@ use alloy_consensus::{SidecarBuilder, SimpleCoder}; use alloy_json_abi::Function; use alloy_network::{AnyNetwork, TransactionBuilder}; -use alloy_primitives::{Address, Bytes, U256}; +use alloy_primitives::{Address, TxKind}; use alloy_provider::Provider; use alloy_rpc_types::{TransactionRequest, WithOtherFields}; use alloy_transport::Transport; use eyre::Result; -use foundry_cli::{opts::TransactionOpts, utils::parse_function_args}; +use foundry_cli::{ + opts::TransactionOpts, + utils::{self, parse_function_args}, +}; use foundry_common::ens::NameOrAddress; -use foundry_config::Chain; +use foundry_config::{Chain, Config}; /// Prevents a misconfigured hwlib from sending a transaction that defies user-specified --from pub fn validate_from_address( @@ -30,116 +33,263 @@ corresponds to the sender, or let foundry automatically detect it by not specify } /// Ensures the transaction is either a contract deployment or a recipient address is specified -pub fn validate_to_address(code: &Option, to: &Option) -> Result<()> { - if code.is_none() && to.is_none() { +pub async fn resolve_tx_kind, T: Transport + Clone>( + provider: &P, + code: &Option, + to: &Option, +) -> Result { + if code.is_some() { + Ok(TxKind::Create) + } else if let Some(to) = to { + Ok(TxKind::Call(to.resolve(provider).await?)) + } else { eyre::bail!("Must specify a recipient address or contract code to deploy"); } - Ok(()) } -#[allow(clippy::too_many_arguments)] -pub async fn build_tx< +/// Initial state. +#[derive(Debug)] +pub struct InitState; + +/// State with known [TxKind]. +#[derive(Debug)] +pub struct TxKindState { + kind: TxKind, +} + +/// State with known input for the transaction. +#[derive(Debug)] +pub struct InputState { + kind: TxKind, + input: Vec, + func: Option, +} + +/// Builder type constructing [TransactionRequest] from cast send/mktx inputs. +/// +/// It is implemented as a stateful builder with expected state transition of [InitState] -> +/// [TxKindState] -> [InputState]. +#[derive(Debug)] +pub struct CastTxBuilder { + provider: P, + tx: WithOtherFields, + legacy: bool, + blob: bool, + chain: Chain, + etherscan_api_key: Option, + state: S, + _t: std::marker::PhantomData, +} + +impl CastTxBuilder +where P: Provider, T: Transport + Clone, - F: Into, - TO: Into, ->( - provider: &P, - from: F, - to: Option, - code: Option, - sig: Option, - args: Vec, - tx: TransactionOpts, - chain: impl Into, - etherscan_api_key: Option, - blob_data: Option>, -) -> Result<(WithOtherFields, Option)> { - let chain = chain.into(); +{ + /// Creates a new instance of [CastTxBuilder] filling transaction with fields present in + /// provided [TransactionOpts]. + pub async fn new(provider: P, tx_opts: TransactionOpts, config: &Config) -> Result { + let mut tx = WithOtherFields::::default(); + + let chain = utils::get_chain(config.chain, &provider).await?; + let etherscan_api_key = config.get_etherscan_api_key(Some(chain)); + + if let Some(gas_limit) = tx_opts.gas_limit { + tx.set_gas_limit(gas_limit.to()); + } + + if let Some(value) = tx_opts.value { + tx.set_value(value); + } - let from = from.into().resolve(provider).await?; + if let Some(gas_price) = tx_opts.gas_price { + if tx_opts.legacy { + tx.set_gas_price(gas_price.to()); + } else { + tx.set_max_fee_per_gas(gas_price.to()); + } + } - let sidecar = blob_data - .map(|data| { - let mut coder = SidecarBuilder::::default(); - coder.ingest(&data); - coder.build() + if !tx_opts.legacy { + if let Some(priority_fee) = tx_opts.priority_gas_price { + tx.set_max_priority_fee_per_gas(priority_fee.to()); + } + } + + if let Some(max_blob_fee) = tx_opts.blob_gas_price { + tx.set_max_fee_per_blob_gas(max_blob_fee.to()) + } + + if let Some(nonce) = tx_opts.nonce { + tx.set_nonce(nonce.to()); + } + + Ok(Self { + provider, + tx, + legacy: tx_opts.legacy || chain.is_legacy(), + blob: tx_opts.blob, + chain, + etherscan_api_key, + state: InitState, + _t: std::marker::PhantomData, }) - .transpose()?; - - let mut req = WithOtherFields::::default() - .with_from(from) - .with_value(tx.value.unwrap_or_default()) - .with_chain_id(chain.id()); - - if let Some(sidecar) = sidecar { - req.set_blob_sidecar(sidecar); - req.populate_blob_hashes(); - req.set_max_fee_per_blob_gas( - // If blob_base_fee is 0, uses 1 wei as minimum. - tx.blob_gas_price.map_or(provider.get_blob_base_fee().await?.max(1), |g| g.to()), - ); } - if let Some(to) = to { - req.set_to(to.into().resolve(provider).await?); - } else { - req.set_kind(alloy_primitives::TxKind::Create); + /// Sets [TxKind] for this builder and changes state to [TxKindState]. + pub fn with_tx_kind(self, kind: TxKind) -> CastTxBuilder { + CastTxBuilder { + provider: self.provider, + tx: self.tx, + legacy: self.legacy, + blob: self.blob, + chain: self.chain, + etherscan_api_key: self.etherscan_api_key, + state: TxKindState { kind }, + _t: self._t, + } } +} - req.set_nonce(if let Some(nonce) = tx.nonce { - nonce.to() - } else { - provider.get_transaction_count(from).await? - }); +impl CastTxBuilder +where + P: Provider, + T: Transport + Clone, +{ + /// Accepts user-provided code, sig and args params and constructs calldata for the transaction. + /// If code is present, input will be set to code + encoded constructor arguments. If no code is + /// present, input is set to just provided arguments. + pub async fn with_code_sig_and_args( + self, + code: Option, + sig: Option, + args: Vec, + ) -> Result> { + let (mut args, func) = if let Some(sig) = sig { + parse_function_args( + &sig, + args, + self.state.kind.to().cloned(), + self.chain, + &self.provider, + self.etherscan_api_key.as_deref(), + ) + .await? + } else { + (Vec::new(), None) + }; - if tx.legacy || chain.is_legacy() { - req.set_gas_price(if let Some(gas_price) = tx.gas_price { - gas_price.to() + let input = if let Some(code) = code { + let mut code = hex::decode(code)?; + code.append(&mut args); + code } else { - provider.get_gas_price().await? - }); - } else { - let (max_fee, priority_fee) = match (tx.gas_price, tx.priority_gas_price) { - (Some(gas_price), Some(priority_gas_price)) => (gas_price, priority_gas_price), - (_, _) => { - let estimate = provider.estimate_eip1559_fees(None).await?; - ( - tx.gas_price.unwrap_or(U256::from(estimate.max_fee_per_gas)), - tx.priority_gas_price.unwrap_or(U256::from(estimate.max_priority_fee_per_gas)), - ) - } + args }; - req.set_max_fee_per_gas(max_fee.to()); - req.set_max_priority_fee_per_gas(priority_fee.to()); + Ok(CastTxBuilder { + provider: self.provider, + tx: self.tx, + legacy: self.legacy, + blob: self.blob, + chain: self.chain, + etherscan_api_key: self.etherscan_api_key, + state: InputState { kind: self.state.kind, input, func }, + _t: self._t, + }) } +} - let params = sig.as_deref().map(|sig| (sig, args)); - let (data, func) = if let Some(code) = code { - let mut data = hex::decode(code)?; +impl CastTxBuilder +where + P: Provider, + T: Transport + Clone, +{ + /// Builds [TransactionRequest] and fiils missing fields. Returns a transaction which is ready + /// to be broadcasted. + pub async fn build( + self, + from: impl Into, + ) -> Result<(WithOtherFields, Option)> { + self._build(from, true).await + } - if let Some((sig, args)) = params { - let (mut sigdata, _) = - parse_function_args(sig, args, None, chain, provider, etherscan_api_key.as_deref()) - .await?; - data.append(&mut sigdata); + /// Builds [TransactionRequest] without filling missing fields. Used for read-only calls such as + /// eth_call, eth_estimateGas, etc + pub async fn build_raw( + self, + from: impl Into, + ) -> Result<(WithOtherFields, Option)> { + self._build(from, false).await + } + + async fn _build( + mut self, + from: impl Into, + fill: bool, + ) -> Result<(WithOtherFields, Option)> { + let from = from.into().resolve(&self.provider).await?; + + self.tx.set_kind(self.state.kind); + self.tx.set_input(self.state.input); + self.tx.set_from(from); + self.tx.set_chain_id(self.chain.id()); + + if !fill { + return Ok((self.tx, self.state.func)); } - (data, None) - } else if let Some((sig, args)) = params { - parse_function_args(sig, args, None, chain, provider, etherscan_api_key.as_deref()).await? - } else { - (Vec::new(), None) - }; + if self.legacy && self.tx.gas_price.is_none() { + self.tx.gas_price = Some(self.provider.get_gas_price().await?); + } - req.set_input::(data.into()); + if self.blob && self.tx.max_fee_per_blob_gas.is_none() { + self.tx.max_fee_per_blob_gas = Some(self.provider.get_blob_base_fee().await?) + } - req.set_gas_limit(if let Some(gas_limit) = tx.gas_limit { - gas_limit.to() - } else { - provider.estimate_gas(&req).await? - }); + if !self.legacy && + (self.tx.max_fee_per_gas.is_none() || self.tx.max_priority_fee_per_gas.is_none()) + { + let estimate = self.provider.estimate_eip1559_fees(None).await?; - Ok((req, func)) + if !self.legacy { + if self.tx.max_fee_per_gas.is_none() { + self.tx.max_fee_per_gas = Some(estimate.max_fee_per_gas); + } + + if self.tx.max_priority_fee_per_gas.is_none() { + self.tx.max_priority_fee_per_gas = Some(estimate.max_priority_fee_per_gas); + } + } + } + + if self.tx.gas.is_none() { + self.tx.gas = Some(self.provider.estimate_gas(&self.tx).await?); + } + + if self.tx.nonce.is_none() { + self.tx.nonce = Some(self.provider.get_transaction_count(from).await?); + } + + Ok((self.tx, self.state.func)) + } +} + +impl CastTxBuilder +where + P: Provider, + T: Transport + Clone, +{ + pub fn with_blob_data(mut self, blob_data: Option>) -> Result { + let Some(blob_data) = blob_data else { return Ok(self) }; + + let mut coder = SidecarBuilder::::default(); + coder.ingest(&blob_data); + let sidecar = coder.build()?; + + self.tx.set_blob_sidecar(sidecar); + self.tx.populate_blob_hashes(); + + Ok(self) + } } diff --git a/crates/cli/src/utils/abi.rs b/crates/cli/src/utils/abi.rs index 634b889ca..a52bdc158 100644 --- a/crates/cli/src/utils/abi.rs +++ b/crates/cli/src/utils/abi.rs @@ -42,8 +42,8 @@ pub async fn parse_function_args Date: Sat, 25 May 2024 22:42:50 +0300 Subject: [PATCH 314/430] fix(invariant) - do not panic when evm call fails (#7994) fix(invariant) - remove expect when evm call --- crates/evm/evm/src/executors/invariant/mod.rs | 11 +++++++---- crates/evm/evm/src/executors/invariant/replay.rs | 6 +----- crates/evm/evm/src/executors/invariant/result.rs | 2 +- crates/evm/evm/src/executors/mod.rs | 3 --- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index edf1f9291..be8dbf2ba 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -206,7 +206,9 @@ impl<'a> InvariantExecutor<'a> { let mut assume_rejects_counter = 0; while current_run < self.config.depth { - let tx = inputs.last().expect("no input generated"); + let tx = inputs.last().ok_or_else(|| { + TestCaseError::fail("No input generated to call fuzzed target.") + })?; // Execute call from the randomly generated sequence and commit state changes. let call_result = executor @@ -216,7 +218,9 @@ impl<'a> InvariantExecutor<'a> { tx.call_details.calldata.clone(), U256::ZERO, ) - .expect("could not make raw evm call"); + .map_err(|e| { + TestCaseError::fail(format!("Could not make raw evm call: {}", e)) + })?; if call_result.result.as_ref() == MAGIC_ASSUME { inputs.pop(); @@ -229,8 +233,7 @@ impl<'a> InvariantExecutor<'a> { } } else { // Collect data for fuzzing from the state changeset. - let mut state_changeset = - call_result.state_changeset.to_owned().expect("no changesets"); + let mut state_changeset = call_result.state_changeset.to_owned().unwrap(); if !&call_result.reverted { collect_data( diff --git a/crates/evm/evm/src/executors/invariant/replay.rs b/crates/evm/evm/src/executors/invariant/replay.rs index 1da2a3ebd..ff4fbc89d 100644 --- a/crates/evm/evm/src/executors/invariant/replay.rs +++ b/crates/evm/evm/src/executors/invariant/replay.rs @@ -77,11 +77,7 @@ pub fn replay_run( let invariant_result = executor.call_raw( CALLER, invariant_contract.address, - invariant_contract - .invariant_function - .abi_encode_input(&[]) - .expect("invariant should have no inputs") - .into(), + invariant_contract.invariant_function.abi_encode_input(&[])?.into(), U256::ZERO, )?; traces.push((TraceKind::Execution, invariant_result.traces.clone().unwrap())); diff --git a/crates/evm/evm/src/executors/invariant/result.rs b/crates/evm/evm/src/executors/invariant/result.rs index 8b2acc56c..12d745de5 100644 --- a/crates/evm/evm/src/executors/invariant/result.rs +++ b/crates/evm/evm/src/executors/invariant/result.rs @@ -64,7 +64,7 @@ pub(crate) fn assert_invariants( let mut call_result = executor.call_raw( CALLER, invariant_contract.address, - func.abi_encode_input(&[]).expect("invariant should have no inputs").into(), + func.abi_encode_input(&[])?.into(), U256::ZERO, )?; diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 940e223b2..2e4c4b229 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -674,9 +674,6 @@ pub struct RawCallResult { /// Scripted transactions generated from this call pub transactions: Option, /// The changeset of the state. - /// - /// This is only present if the changed state was not committed to the database (i.e. if you - /// used `call` and `call_raw` not `call_committing` or `call_raw_committing`). pub state_changeset: Option, /// The `revm::Env` after the call pub env: EnvWithHandlerCfg, From ea2eff95b5c17edd3ffbdfc6daab5ce5cc80afc0 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Sun, 26 May 2024 19:39:17 +0300 Subject: [PATCH 315/430] fix(forge): consistently apply `no_storage_caching` config (#7997) * fix(forge): consistently apply no_storage_caching config * Settings cleanup, Add test --- crates/cheatcodes/src/config.rs | 4 ++++ crates/cheatcodes/src/evm/fork.rs | 3 ++- crates/config/src/cache.rs | 8 ++++---- crates/config/src/lib.rs | 2 +- crates/forge/tests/it/fork.rs | 33 +++++++++++++++++++++++++++++- testdata/default/cheats/Fork.t.sol | 5 +++++ 6 files changed, 48 insertions(+), 7 deletions(-) diff --git a/crates/cheatcodes/src/config.rs b/crates/cheatcodes/src/config.rs index 64e02070a..66aef3cff 100644 --- a/crates/cheatcodes/src/config.rs +++ b/crates/cheatcodes/src/config.rs @@ -29,6 +29,8 @@ pub struct CheatsConfig { pub prompt_timeout: Duration, /// RPC storage caching settings determines what chains and endpoints to cache pub rpc_storage_caching: StorageCachingConfig, + /// Disables storage caching entirely. + pub no_storage_caching: bool, /// All known endpoints and their aliases pub rpc_endpoints: ResolvedRpcEndpoints, /// Project's paths as configured @@ -78,6 +80,7 @@ impl CheatsConfig { always_use_create_2_factory: evm_opts.always_use_create_2_factory, prompt_timeout: Duration::from_secs(config.prompt_timeout), rpc_storage_caching: config.rpc_storage_caching.clone(), + no_storage_caching: config.no_storage_caching, rpc_endpoints, paths: config.project_paths(), fs_permissions: config.fs_permissions.clone().joined(&config.__root), @@ -204,6 +207,7 @@ impl Default for CheatsConfig { always_use_create_2_factory: false, prompt_timeout: Duration::from_secs(120), rpc_storage_caching: Default::default(), + no_storage_caching: false, rpc_endpoints: Default::default(), paths: ProjectPathsConfig::builder().build_with_root("./"), fs_permissions: Default::default(), diff --git a/crates/cheatcodes/src/evm/fork.rs b/crates/cheatcodes/src/evm/fork.rs index 81b364aaf..4c91a2261 100644 --- a/crates/cheatcodes/src/evm/fork.rs +++ b/crates/cheatcodes/src/evm/fork.rs @@ -338,7 +338,8 @@ fn create_fork_request( let mut evm_opts = ccx.state.config.evm_opts.clone(); evm_opts.fork_block_number = block; let fork = CreateFork { - enable_caching: ccx.state.config.rpc_storage_caching.enable_for_endpoint(&url), + enable_caching: !ccx.state.config.no_storage_caching && + ccx.state.config.rpc_storage_caching.enable_for_endpoint(&url), url, env: (*ccx.ecx.env).clone(), evm_opts, diff --git a/crates/config/src/cache.rs b/crates/config/src/cache.rs index ed1475c7e..58b3b8cbe 100644 --- a/crates/config/src/cache.rs +++ b/crates/config/src/cache.rs @@ -5,12 +5,12 @@ use number_prefix::NumberPrefix; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::{fmt, fmt::Formatter, str::FromStr}; -/// Settings to configure caching of remote +/// Settings to configure caching of remote. #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct StorageCachingConfig { - /// chains to cache + /// Chains to cache. pub chains: CachedChains, - /// endpoints to cache + /// Endpoints to cache. pub endpoints: CachedEndpoints, } @@ -258,7 +258,7 @@ mod tests { Chain::optimism_mainnet(), Chain::from_id(999999) ]), - endpoints: CachedEndpoints::All + endpoints: CachedEndpoints::All, } ) } diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index ec2118c16..d6a157ecf 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -3689,7 +3689,7 @@ mod tests { Chain::optimism_mainnet(), Chain::from_id(999999) ]), - endpoints: CachedEndpoints::All + endpoints: CachedEndpoints::All, }, use_literal_content: false, bytecode_hash: BytecodeHash::Ipfs, diff --git a/crates/forge/tests/it/fork.rs b/crates/forge/tests/it/fork.rs index d3f81ff67..3f3576ac4 100644 --- a/crates/forge/tests/it/fork.rs +++ b/crates/forge/tests/it/fork.rs @@ -4,9 +4,11 @@ use crate::{ config::*, test_helpers::{RE_PATH_SEPARATOR, TEST_DATA_DEFAULT}, }; +use alloy_chains::Chain; use forge::result::SuiteResult; -use foundry_config::{fs_permissions::PathPermission, FsPermissions}; +use foundry_config::{fs_permissions::PathPermission, Config, FsPermissions}; use foundry_test_utils::Filter; +use std::fs; /// Executes reverting fork test #[tokio::test(flavor = "multi_thread")] @@ -96,3 +98,32 @@ async fn test_create_same_fork() { let filter = Filter::new(".*", ".*", &format!(".*fork{RE_PATH_SEPARATOR}ForkSame")); TestConfig::with_filter(runner, filter).run().await; } + +/// 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 cache_dir = Config::foundry_block_cache_dir(Chain::mainnet(), 19800000); + assert!(!cache_dir.unwrap().exists()); + + // 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"); + TestConfig::with_filter(runner, filter).run().await; + let cache_dir = Config::foundry_block_cache_dir(Chain::mainnet(), 19800000).unwrap(); + assert!(cache_dir.exists()); + + // cleanup cached storage so subsequent tests does not fail + let _ = fs::remove_file(cache_dir); +} diff --git a/testdata/default/cheats/Fork.t.sol b/testdata/default/cheats/Fork.t.sol index 950865eac..cb6a4e3ff 100644 --- a/testdata/default/cheats/Fork.t.sol +++ b/testdata/default/cheats/Fork.t.sol @@ -117,4 +117,9 @@ contract ForkTest is DSTest { vm.createSelectFork("https://polygon-pokt.nodies.app"); // Polygon mainnet RPC URL assertEq(block.chainid, 137); } + + // ensures forks storage is cached at block + function testStorageCaching() public { + vm.createSelectFork("https://eth-mainnet.alchemyapi.io/v2/Lc7oIGYeL_QvInzI0Wiu_pOZZDEKBrdf", 19800000); + } } From 93a6c3a9e50d149e9058e25ba0589e0acad4d82b Mon Sep 17 00:00:00 2001 From: NIC Lin Date: Wed, 29 May 2024 17:17:50 +0800 Subject: [PATCH 316/430] Fix missing error code (#7999) * Fix string identifier of `UnnamedReturnVariable` error code * Add missing error code in `from_str` and update the order of error codes --- crates/config/src/error.rs | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/crates/config/src/error.rs b/crates/config/src/error.rs index 00692d67d..448553cdc 100644 --- a/crates/config/src/error.rs +++ b/crates/config/src/error.rs @@ -149,6 +149,7 @@ impl SolidityErrorCode { pub fn as_str(&self) -> Result<&'static str, u64> { let s = match self { SolidityErrorCode::SpdxLicenseNotProvided => "license", + SolidityErrorCode::VisibilityForConstructorIsIgnored => "constructor-visibility", SolidityErrorCode::ContractExceeds24576Bytes => "code-size", SolidityErrorCode::ContractInitCodeSizeExceeds49152Bytes => "init-code-size", SolidityErrorCode::FunctionStateMutabilityCanBeRestricted => "func-mutability", @@ -162,9 +163,8 @@ impl SolidityErrorCode { SolidityErrorCode::UnnamedReturnVariable => "unnamed-return", SolidityErrorCode::Unreachable => "unreachable", SolidityErrorCode::PragmaSolidity => "pragma-solidity", - SolidityErrorCode::Other(code) => return Err(*code), - SolidityErrorCode::VisibilityForConstructorIsIgnored => "constructor-visibility", SolidityErrorCode::TransientStorageUsed => "transient-storage", + SolidityErrorCode::Other(code) => return Err(*code), }; Ok(s) } @@ -174,7 +174,9 @@ impl From for u64 { fn from(code: SolidityErrorCode) -> u64 { match code { SolidityErrorCode::SpdxLicenseNotProvided => 1878, + SolidityErrorCode::VisibilityForConstructorIsIgnored => 2462, SolidityErrorCode::ContractExceeds24576Bytes => 5574, + SolidityErrorCode::ContractInitCodeSizeExceeds49152Bytes => 3860, SolidityErrorCode::FunctionStateMutabilityCanBeRestricted => 2018, SolidityErrorCode::UnusedLocalVariable => 2072, SolidityErrorCode::UnusedFunctionParameter => 5667, @@ -186,8 +188,6 @@ impl From for u64 { SolidityErrorCode::UnnamedReturnVariable => 6321, SolidityErrorCode::Unreachable => 5740, SolidityErrorCode::PragmaSolidity => 3420, - SolidityErrorCode::ContractInitCodeSizeExceeds49152Bytes => 3860, - SolidityErrorCode::VisibilityForConstructorIsIgnored => 2462, SolidityErrorCode::TransientStorageUsed => 2394, SolidityErrorCode::Other(code) => code, } @@ -208,20 +208,21 @@ impl FromStr for SolidityErrorCode { fn from_str(s: &str) -> Result { let code = match s { - "unreachable" => SolidityErrorCode::Unreachable, - "unused-return" => SolidityErrorCode::UnnamedReturnVariable, - "unused-param" => SolidityErrorCode::UnusedFunctionParameter, - "unused-var" => SolidityErrorCode::UnusedLocalVariable, + "license" => SolidityErrorCode::SpdxLicenseNotProvided, + "constructor-visibility" => SolidityErrorCode::VisibilityForConstructorIsIgnored, "code-size" => SolidityErrorCode::ContractExceeds24576Bytes, "init-code-size" => SolidityErrorCode::ContractInitCodeSizeExceeds49152Bytes, - "shadowing" => SolidityErrorCode::ShadowsExistingDeclaration, "func-mutability" => SolidityErrorCode::FunctionStateMutabilityCanBeRestricted, - "license" => SolidityErrorCode::SpdxLicenseNotProvided, - "pragma-solidity" => SolidityErrorCode::PragmaSolidity, + "unused-var" => SolidityErrorCode::UnusedLocalVariable, + "unused-param" => SolidityErrorCode::UnusedFunctionParameter, + "unused-return" => SolidityErrorCode::ReturnValueOfCallsNotUsed, "virtual-interfaces" => SolidityErrorCode::InterfacesExplicitlyVirtual, "missing-receive-ether" => SolidityErrorCode::PayableNoReceiveEther, + "shadowing" => SolidityErrorCode::ShadowsExistingDeclaration, "same-varname" => SolidityErrorCode::DeclarationSameNameAsAnother, - "constructor-visibility" => SolidityErrorCode::VisibilityForConstructorIsIgnored, + "unnamed-return" => SolidityErrorCode::UnnamedReturnVariable, + "unreachable" => SolidityErrorCode::Unreachable, + "pragma-solidity" => SolidityErrorCode::PragmaSolidity, "transient-storage" => SolidityErrorCode::TransientStorageUsed, _ => return Err(format!("Unknown variant {s}")), }; @@ -234,6 +235,7 @@ impl From for SolidityErrorCode { fn from(code: u64) -> Self { match code { 1878 => SolidityErrorCode::SpdxLicenseNotProvided, + 2462 => SolidityErrorCode::VisibilityForConstructorIsIgnored, 5574 => SolidityErrorCode::ContractExceeds24576Bytes, 3860 => SolidityErrorCode::ContractInitCodeSizeExceeds49152Bytes, 2018 => SolidityErrorCode::FunctionStateMutabilityCanBeRestricted, @@ -245,9 +247,8 @@ impl From for SolidityErrorCode { 2519 => SolidityErrorCode::ShadowsExistingDeclaration, 8760 => SolidityErrorCode::DeclarationSameNameAsAnother, 6321 => SolidityErrorCode::UnnamedReturnVariable, - 3420 => SolidityErrorCode::PragmaSolidity, 5740 => SolidityErrorCode::Unreachable, - 2462 => SolidityErrorCode::VisibilityForConstructorIsIgnored, + 3420 => SolidityErrorCode::PragmaSolidity, 2394 => SolidityErrorCode::TransientStorageUsed, other => SolidityErrorCode::Other(other), } From 82e7406af08e1e7b8719757e80e37d9625e794f1 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Wed, 29 May 2024 14:48:32 +0530 Subject: [PATCH 317/430] feat(cheatcodes): randomUint (#7960) * feat(cheatcodes): randomUint * fix: cargo cheats * add vm.randomUint test * nit cargo cheats * add randomUint range and randomAddress * use U256::rand * cargo cheats nit --- Cargo.lock | 1 + Cargo.toml | 7 ++- crates/cheatcodes/Cargo.toml | 6 ++- crates/cheatcodes/assets/cheatcodes.json | 60 ++++++++++++++++++++++++ crates/cheatcodes/spec/src/vm.rs | 12 +++++ crates/cheatcodes/src/utils.rs | 30 ++++++++++++ testdata/cheats/Vm.sol | 3 ++ testdata/default/cheats/RandomUint.t.sol | 15 ++++++ 8 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 testdata/default/cheats/RandomUint.t.sol diff --git a/Cargo.lock b/Cargo.lock index f5fffa400..cc0e58724 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3295,6 +3295,7 @@ dependencies = [ "k256", "p256", "parking_lot", + "rand", "revm", "rustc-hash", "semver 1.0.23", diff --git a/Cargo.toml b/Cargo.toml index fc19e062d..d5b4bdb41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -175,7 +175,7 @@ alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } -alloy-primitives = { version = "0.7.1", features = ["getrandom"] } +alloy-primitives = { version = "0.7.1", features = ["getrandom", "rand"] } alloy-dyn-abi = "0.7.1" alloy-json-abi = "0.7.1" alloy-sol-types = "0.7.1" @@ -188,7 +188,10 @@ solang-parser = "=0.3.3" ## misc arrayvec = "0.7" base64 = "0.22" -chrono = { version = "0.4", default-features = false, features = ["clock", "std"] } +chrono = { version = "0.4", default-features = false, features = [ + "clock", + "std", +] } color-eyre = "0.6" derive_more = "0.99" evm-disassembler = "0.5" diff --git a/crates/cheatcodes/Cargo.toml b/crates/cheatcodes/Cargo.toml index fa2daa876..f712b7766 100644 --- a/crates/cheatcodes/Cargo.toml +++ b/crates/cheatcodes/Cargo.toml @@ -27,7 +27,10 @@ alloy-sol-types.workspace = true alloy-provider.workspace = true alloy-rpc-types.workspace = true alloy-signer.workspace = true -alloy-signer-wallet = { workspace = true, features = ["mnemonic-all-languages", "keystore"] } +alloy-signer-wallet = { workspace = true, features = [ + "mnemonic-all-languages", + "keystore", +] } parking_lot = "0.12" eyre.workspace = true @@ -46,3 +49,4 @@ thiserror = "1" semver = "1" rustc-hash.workspace = true dialoguer = "0.11.0" +rand = "0.8" diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 5606a10b1..3f2f3cb33 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -6391,6 +6391,66 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "randomAddress", + "description": "Returns a random `address`.", + "declaration": "function randomAddress() external returns (address);", + "visibility": "external", + "mutability": "", + "signature": "randomAddress()", + "selector": "0xd5bee9f5", + "selectorBytes": [ + 213, + 190, + 233, + 245 + ] + }, + "group": "utilities", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "randomUint_0", + "description": "Returns a random uint256 value.", + "declaration": "function randomUint() external returns (uint256);", + "visibility": "external", + "mutability": "", + "signature": "randomUint()", + "selector": "0x25124730", + "selectorBytes": [ + 37, + 18, + 71, + 48 + ] + }, + "group": "utilities", + "status": "stable", + "safety": "safe" + }, + { + "func": { + "id": "randomUint_1", + "description": "Returns random uin256 value between the provided range (min..=max).", + "declaration": "function randomUint(uint256 min, uint256 max) external returns (uint256);", + "visibility": "external", + "mutability": "", + "signature": "randomUint(uint256,uint256)", + "selector": "0xd61b051b", + "selectorBytes": [ + 214, + 27, + 5, + 27 + ] + }, + "group": "utilities", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "readCallers", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index e8d24e3c5..d1123b448 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -2139,6 +2139,18 @@ interface Vm { /// Returns ENS namehash for provided string. #[cheatcode(group = Utilities)] function ensNamehash(string calldata name) external pure returns (bytes32); + + /// Returns a random uint256 value. + #[cheatcode(group = Utilities)] + function randomUint() external returns (uint256); + + /// Returns random uin256 value between the provided range (min..=max). + #[cheatcode(group = Utilities)] + function randomUint(uint256 min, uint256 max) external returns (uint256); + + /// Returns a random `address`. + #[cheatcode(group = Utilities)] + function randomAddress() external returns (address); } } diff --git a/crates/cheatcodes/src/utils.rs b/crates/cheatcodes/src/utils.rs index df1061346..fb750fa91 100644 --- a/crates/cheatcodes/src/utils.rs +++ b/crates/cheatcodes/src/utils.rs @@ -19,6 +19,7 @@ use k256::{ Secp256k1, }; use p256::ecdsa::{signature::hazmat::PrehashSigner, Signature, SigningKey as P256SigningKey}; +use rand::Rng; /// The BIP32 default derivation path prefix. const DEFAULT_DERIVATION_PATH_PREFIX: &str = "m/44'/60'/0'/0/"; @@ -145,6 +146,35 @@ impl Cheatcode for ensNamehashCall { } } +impl Cheatcode for randomUint_0Call { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self {} = self; + // Use thread_rng to get a random number + let mut rng = rand::thread_rng(); + let random_number: U256 = rng.gen(); + Ok(random_number.abi_encode()) + } +} + +impl Cheatcode for randomUint_1Call { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { min, max } = self; + // Generate random between range min..=max + let mut rng = rand::thread_rng(); + let range = *max - *min + U256::from(1); + let random_number = rng.gen::() % range + *min; + Ok(random_number.abi_encode()) + } +} + +impl Cheatcode for randomAddressCall { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self {} = self; + let addr = Address::random(); + Ok(addr.abi_encode()) + } +} + /// Using a given private key, return its public ETH address, its public key affine x and y /// coordinates, and its private key (see the 'Wallet' struct) /// diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 45c720e6c..e1551362a 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -315,6 +315,9 @@ interface Vm { function promptAddress(string calldata promptText) external returns (address); function promptSecret(string calldata promptText) external returns (string memory input); function promptUint(string calldata promptText) external returns (uint256); + function randomAddress() external returns (address); + function randomUint() external returns (uint256); + function randomUint(uint256 min, uint256 max) external returns (uint256); function readCallers() external returns (CallerMode callerMode, address msgSender, address txOrigin); function readDir(string calldata path) external view returns (DirEntry[] memory entries); function readDir(string calldata path, uint64 maxDepth) external view returns (DirEntry[] memory entries); diff --git a/testdata/default/cheats/RandomUint.t.sol b/testdata/default/cheats/RandomUint.t.sol new file mode 100644 index 000000000..5c5b1024a --- /dev/null +++ b/testdata/default/cheats/RandomUint.t.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract RandomUint is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + + function testRandomUint() public { + uint256 rand = vm.randomUint(); + + assertTrue(rand > 0); + } +} From 25b24554a1effd98c7c32f4c0c26911a6066f84b Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Wed, 29 May 2024 12:19:08 +0300 Subject: [PATCH 318/430] chore(invariant): config defaults, add max test threads config (#7957) * chore(invariant): default depth / shrink run limit, config num of threads * Limit depth in testing at 15 * Set depth for ext integrations to 15 * Make max threads global config, propagate error if thread pool creation fails * Run invariant gas report with depth of 15 * Typo, move set threads at top of execute --- crates/config/README.md | 3 ++- crates/config/src/invariant.rs | 11 +++++----- crates/config/src/lib.rs | 3 ++- .../evm/evm/src/executors/invariant/error.rs | 2 +- crates/forge/bin/cmd/test/mod.rs | 21 ++++++++++++++++++- crates/forge/tests/it/test_helpers.rs | 2 +- crates/test-utils/src/util.rs | 1 + testdata/foundry.toml | 2 +- 8 files changed, 34 insertions(+), 11 deletions(-) diff --git a/crates/config/README.md b/crates/config/README.md index 04fd53bb6..25100a5df 100644 --- a/crates/config/README.md +++ b/crates/config/README.md @@ -189,12 +189,13 @@ include_push_bytes = true [invariant] runs = 256 -depth = 15 +depth = 500 fail_on_revert = false call_override = false dictionary_weight = 80 include_storage = true include_push_bytes = true +shrink_run_limit = 5000 [fmt] line_length = 100 diff --git a/crates/config/src/invariant.rs b/crates/config/src/invariant.rs index 869125324..8c90e3cc3 100644 --- a/crates/config/src/invariant.rs +++ b/crates/config/src/invariant.rs @@ -26,7 +26,7 @@ pub struct InvariantConfig { #[serde(flatten)] pub dictionary: FuzzDictionaryConfig, /// The maximum number of attempts to shrink the sequence - pub shrink_run_limit: usize, + pub shrink_run_limit: u32, /// The maximum number of rejects via `vm.assume` which can be encountered during a single /// invariant run. pub max_assume_rejects: u32, @@ -40,11 +40,11 @@ impl Default for InvariantConfig { fn default() -> Self { InvariantConfig { runs: 256, - depth: 15, + depth: 500, fail_on_revert: false, call_override: false, dictionary: FuzzDictionaryConfig { dictionary_weight: 80, ..Default::default() }, - shrink_run_limit: 2usize.pow(18_u32), + shrink_run_limit: 5000, max_assume_rejects: 65536, gas_report_samples: 256, failure_persist_dir: None, @@ -57,11 +57,11 @@ impl InvariantConfig { pub fn new(cache_dir: PathBuf) -> Self { InvariantConfig { runs: 256, - depth: 15, + depth: 500, fail_on_revert: false, call_override: false, dictionary: FuzzDictionaryConfig { dictionary_weight: 80, ..Default::default() }, - shrink_run_limit: 2usize.pow(18_u32), + shrink_run_limit: 5000, max_assume_rejects: 65536, gas_report_samples: 256, failure_persist_dir: Some(cache_dir), @@ -102,6 +102,7 @@ impl InlineConfigParser for InvariantConfig { "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)?, _ => Err(InlineConfigParserError::InvalidConfigProperty(key.to_string()))?, } } diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index d6a157ecf..81dbff8a4 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -3812,9 +3812,10 @@ mod tests { [invariant] runs = 256 - depth = 15 + depth = 500 fail_on_revert = false call_override = false + shrink_run_limit = 5000 "#, )?; diff --git a/crates/evm/evm/src/executors/invariant/error.rs b/crates/evm/evm/src/executors/invariant/error.rs index 6e8dc929b..b0004c8cb 100644 --- a/crates/evm/evm/src/executors/invariant/error.rs +++ b/crates/evm/evm/src/executors/invariant/error.rs @@ -64,7 +64,7 @@ pub struct FailedInvariantCaseData { /// Inner fuzzing Sequence coming from overriding calls. pub inner_sequence: Vec>, /// Shrink run limit - pub shrink_run_limit: usize, + pub shrink_run_limit: u32, /// Fail on revert, used to check sequence when shrinking. pub fail_on_revert: bool, } diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 1bb70aa7e..152d21db5 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -110,6 +110,11 @@ 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)] + pub max_threads: Option, + #[command(flatten)] filter: FilterArgs, @@ -217,6 +222,13 @@ impl TestArgs { /// /// Returns the test results for all matching tests. pub async fn execute_tests(self) -> Result { + // 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) = self.max_threads { + trace!(target: "forge::test", "execute tests with {} max threads", test_threads); + rayon::ThreadPoolBuilder::new().num_threads(test_threads as usize).build_global()?; + } + // Merge all configs let (mut config, mut evm_opts) = self.load_config_and_evm_opts_emit_warnings()?; @@ -627,7 +639,7 @@ fn list( #[cfg(test)] mod tests { use super::*; - use foundry_config::Chain; + use foundry_config::{Chain, InvariantConfig}; use foundry_test_utils::forgetest_async; #[test] @@ -664,6 +676,13 @@ mod tests { } forgetest_async!(gas_report_fuzz_invariant, |prj, _cmd| { + // speed up test by running with depth of 15 + let config = Config { + invariant: { InvariantConfig { depth: 15, ..Default::default() } }, + ..Default::default() + }; + prj.write_config(config); + prj.insert_ds_test(); prj.add_source( "Contracts.sol", diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index b2993151d..cd54526d6 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -104,7 +104,7 @@ impl ForgeTestProfile { max_fuzz_dictionary_addresses: 10_000, max_fuzz_dictionary_values: 10_000, }, - shrink_run_limit: 2usize.pow(18u32), + shrink_run_limit: 5000, max_assume_rejects: 65536, gas_report_samples: 256, failure_persist_dir: Some(tempfile::tempdir().unwrap().into_path()), diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index cba0104a7..37b927e19 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -198,6 +198,7 @@ impl ExtTester { test_cmd.env("FOUNDRY_ETH_RPC_URL", crate::rpc::next_http_archive_rpc_endpoint()); test_cmd.env("FOUNDRY_FORK_BLOCK_NUMBER", fork_block.to_string()); } + test_cmd.env("FOUNDRY_INVARIANT_DEPTH", "15"); test_cmd.assert_non_empty_stdout(); } diff --git a/testdata/foundry.toml b/testdata/foundry.toml index 1fd8c610f..e9189bb00 100644 --- a/testdata/foundry.toml +++ b/testdata/foundry.toml @@ -16,7 +16,7 @@ ffi = false force = false invariant_fail_on_revert = false invariant_call_override = false -invariant_preserve_state = false +invariant_shrink_run_limit = 5000 gas_limit = 9223372036854775807 gas_price = 0 gas_reports = ["*"] From 5494c33bc7977b3537bd296e375431d938d44ca3 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Wed, 29 May 2024 17:47:18 +0530 Subject: [PATCH 319/430] fix(cast): set --block value as fork_block_number while tracing (#8009) fix(cast): set --block value as fork_block_number while Tracing --- crates/cast/bin/cmd/call.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/crates/cast/bin/cmd/call.rs b/crates/cast/bin/cmd/call.rs index 10a97ea00..b8e6330ba 100644 --- a/crates/cast/bin/cmd/call.rs +++ b/crates/cast/bin/cmd/call.rs @@ -1,6 +1,6 @@ use crate::tx::CastTxBuilder; use alloy_primitives::{TxKind, U256}; -use alloy_rpc_types::BlockId; +use alloy_rpc_types::{BlockId, BlockNumberOrTag}; use cast::Cast; use clap::Parser; use eyre::Result; @@ -114,7 +114,7 @@ impl CallArgs { sig = Some(data); } - let config = Config::from(ð); + let mut config = Config::from(ð); let provider = utils::get_provider(&config)?; let sender = eth.wallet.sender().await; @@ -153,6 +153,11 @@ impl CallArgs { let figment = Config::figment_with_root(find_project_root_path(None).unwrap()).merge(eth.rpc); let evm_opts = figment.extract::()?; + if let Some(BlockId::Number(BlockNumberOrTag::Number(block_number))) = self.block { + // Override Config `fork_block_number` (if set) with CLI value. + config.fork_block_number = Some(block_number); + } + let (env, fork, chain) = TracingExecutor::get_fork_material(&config, evm_opts).await?; let mut executor = TracingExecutor::new(env, fork, evm_version, debug); From 7c52ecda14a81a93471437aab08edff1c2ced415 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Fri, 31 May 2024 19:52:13 +0530 Subject: [PATCH 320/430] fix(cheatcodes): inspector `call` (#8019) * use `call.bytecode_address` instead of `call.target_address` * nit Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * add test --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- crates/cheatcodes/src/inspector.rs | 2 +- testdata/default/cheats/ExpectCall.t.sol | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 855e0374f..968f2b4d7 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -820,7 +820,7 @@ impl Inspector for Cheatcodes { // Handle expected calls // Grab the different calldatas expected. - if let Some(expected_calls_for_target) = self.expected_calls.get_mut(&(call.target_address)) + if let Some(expected_calls_for_target) = self.expected_calls.get_mut(&call.bytecode_address) { // Match every partial/full calldata for (calldata, (expected, actual_count)) in expected_calls_for_target { diff --git a/testdata/default/cheats/ExpectCall.t.sol b/testdata/default/cheats/ExpectCall.t.sol index 86a5290a9..7d757101a 100644 --- a/testdata/default/cheats/ExpectCall.t.sol +++ b/testdata/default/cheats/ExpectCall.t.sol @@ -50,6 +50,16 @@ contract NestedContract { } } +contract SimpleCall { + function call() public {} +} + +contract ProxyWithDelegateCall { + function delegateCall(SimpleCall simpleCall) public { + address(simpleCall).delegatecall(abi.encodeWithSignature("call()")); + } +} + contract ExpectCallTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); @@ -249,6 +259,14 @@ contract ExpectCallTest is DSTest { vm.expectCall(address(target), abi.encodeWithSelector(target.add.selector)); this.exposed_callTargetNTimes(target, 5, 5, 1); } + + /// Ensure expectCall works for Proxy DelegateCalls. Ref: + function testExpectCallForProxyDelegateCall() public { + ProxyWithDelegateCall proxyWithDelegateCall = new ProxyWithDelegateCall(); + SimpleCall simpleCall = new SimpleCall(); + vm.expectCall(address(simpleCall), abi.encodeWithSignature("call()")); + proxyWithDelegateCall.delegateCall(simpleCall); + } } contract ExpectCallCountTest is DSTest { From 08ef489e9ce73304b898058d031c8a6ac27759c0 Mon Sep 17 00:00:00 2001 From: Colin Kennedy Date: Fri, 31 May 2024 18:53:37 -0300 Subject: [PATCH 321/430] feat(foundryup): add a `--jobs` flag while building from source (#8021) * feat: add --jobs flag to foundryup this flag is passed on to cargo build. It's useful for people building on lower powered machines (<=16 GB Ram). * doc: clarify build / binary options * fix: rename jobs variable --------- Co-authored-by: Colin Kennedy --- foundryup/foundryup | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/foundryup/foundryup b/foundryup/foundryup index f1e40b648..033e68d6f 100755 --- a/foundryup/foundryup +++ b/foundryup/foundryup @@ -6,6 +6,8 @@ FOUNDRY_DIR=${FOUNDRY_DIR:-"$BASE_DIR/.foundry"} FOUNDRY_BIN_DIR="$FOUNDRY_DIR/bin" FOUNDRY_MAN_DIR="$FOUNDRY_DIR/share/man/man1" +FOUNDRYUP_JOBS=$(nproc) + BINS=(forge cast anvil chisel) export RUSTFLAGS="${RUSTFLAGS:--C target-cpu=native}" @@ -24,6 +26,7 @@ main() { -p|--path) shift; FOUNDRYUP_LOCAL_REPO=$1;; -P|--pr) shift; FOUNDRYUP_PR=$1;; -C|--commit) shift; FOUNDRYUP_COMMIT=$1;; + -j|--jobs) shift; FOUNDRYUP_JOBS=$1;; --arch) shift; FOUNDRYUP_ARCH=$1;; --platform) shift; FOUNDRYUP_PLATFORM=$1;; -h|--help) @@ -60,7 +63,7 @@ main() { # Enter local repo and build say "installing from $FOUNDRYUP_LOCAL_REPO" cd "$FOUNDRYUP_LOCAL_REPO" - ensure cargo build --bins --release # need 4 speed + ensure cargo build --bins --release -j "$FOUNDRYUP_JOBS" # need 4 speed for bin in "${BINS[@]}"; do # Remove prior installations if they exist @@ -197,7 +200,7 @@ EOF fi # Build the repo and install the binaries locally to the .foundry bin directory. - ensure cargo build --bins --release + ensure cargo build --bins --release -j "$FOUNDRYUP_JOBS" for bin in "${BINS[@]}"; do for try_path in target/release/$bin target/release/$bin.exe; do if [ -f "$try_path" ]; then @@ -224,17 +227,20 @@ The installer for Foundry. Update or revert to a specific Foundry version with ease. +By default, the latest nightly version is installed from built binaries. + USAGE: foundryup OPTIONS: -h, --help Print help information - -v, --version Install a specific version - -b, --branch Install a specific branch - -P, --pr Install a specific Pull Request - -C, --commit Install a specific commit - -r, --repo Install from a remote GitHub repo (uses default branch if no other options are set) - -p, --path Install a local repository + -v, --version Install a specific version from built binaries + -b, --branch Build and install a specific branch + -P, --pr Build and install a specific Pull Request + -C, --commit Build and install a specific commit + -r, --repo Build and install from a remote GitHub repo (uses default branch if no other options are set) + -p, --path Build and install a local repository + -j, --jobs Number of CPUs to use for building Foundry (default: all CPUs) --arch Install a specific architecture (supports amd64 and arm64) --platform Install a specific platform (supports win32, linux, and darwin) EOF From f479e945c6be78bb902df12f9d683c3bb55e3fb0 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 1 Jun 2024 01:07:01 +0300 Subject: [PATCH 322/430] chore: simplify debug traces (#8020) --- crates/cast/bin/cmd/call.rs | 15 +++---- crates/cast/bin/cmd/run.rs | 9 ++-- crates/cli/src/utils/cmd.rs | 53 ++++++++--------------- crates/evm/core/src/debug.rs | 13 +++++- crates/evm/evm/src/inspectors/debugger.rs | 6 +-- crates/evm/evm/src/inspectors/stack.rs | 4 +- 6 files changed, 43 insertions(+), 57 deletions(-) diff --git a/crates/cast/bin/cmd/call.rs b/crates/cast/bin/cmd/call.rs index b8e6330ba..3b4e8da36 100644 --- a/crates/cast/bin/cmd/call.rs +++ b/crates/cast/bin/cmd/call.rs @@ -1,7 +1,7 @@ use crate::tx::CastTxBuilder; use alloy_primitives::{TxKind, U256}; use alloy_rpc_types::{BlockId, BlockNumberOrTag}; -use cast::Cast; +use cast::{traces::TraceKind, Cast}; use clap::Parser; use eyre::Result; use foundry_cli::{ @@ -167,15 +167,12 @@ impl CallArgs { let trace = match tx_kind { TxKind::Create => { let deploy_result = executor.deploy(sender, input, value, None); - - match deploy_result { - Ok(deploy_result) => TraceResult::from(deploy_result), - Err(evm_err) => TraceResult::try_from(evm_err)?, - } - } - TxKind::Call(to) => { - TraceResult::from(executor.call_raw_committing(sender, to, input, value)?) + TraceResult::try_from(deploy_result)? } + TxKind::Call(to) => TraceResult::from_raw( + executor.call_raw_committing(sender, to, input, value)?, + TraceKind::Execution, + ), }; handle_traces(trace, &config, chain, labels, debug).await?; diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index 35ab17ec4..32bd4fea6 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -1,7 +1,7 @@ use alloy_primitives::U256; use alloy_provider::Provider; use alloy_rpc_types::BlockTransactions; -use cast::revm::primitives::EnvWithHandlerCfg; +use cast::{revm::primitives::EnvWithHandlerCfg, traces::TraceKind}; use clap::Parser; use eyre::{Result, WrapErr}; use foundry_cli::{ @@ -215,13 +215,10 @@ impl RunArgs { if let Some(to) = tx.to { trace!(tx=?tx.hash, to=?to, "executing call transaction"); - TraceResult::from(executor.commit_tx_with_env(env)?) + TraceResult::from_raw(executor.commit_tx_with_env(env)?, TraceKind::Execution) } else { trace!(tx=?tx.hash, "executing create transaction"); - match executor.deploy_with_env(env, None) { - Ok(res) => TraceResult::from(res), - Err(err) => TraceResult::try_from(err)?, - } + TraceResult::try_from(executor.deploy_with_env(env, None))? } }; diff --git a/crates/cli/src/utils/cmd.rs b/crates/cli/src/utils/cmd.rs index 96c9e2e50..9e8f99241 100644 --- a/crates/cli/src/utils/cmd.rs +++ b/crates/cli/src/utils/cmd.rs @@ -323,19 +323,19 @@ pub fn read_constructor_args_file(constructor_args_path: PathBuf) -> Result, + pub debug: Option, pub gas_used: u64, } -impl From for TraceResult { - fn from(result: RawCallResult) -> Self { - let RawCallResult { gas_used, traces, reverted, debug, .. } = result; - +impl TraceResult { + /// Create a new [`TraceResult`] from a [`RawCallResult`]. + pub fn from_raw(raw: RawCallResult, trace_kind: TraceKind) -> Self { + let RawCallResult { gas_used, traces, reverted, debug, .. } = raw; Self { success: !reverted, - traces: vec![(TraceKind::Execution, traces.expect("traces is None"))], - debug: debug.unwrap_or_default(), + traces: traces.map(|arena| vec![(trace_kind, arena)]), + debug, gas_used, } } @@ -343,31 +343,18 @@ impl From for TraceResult { impl From for TraceResult { fn from(result: DeployResult) -> Self { - let RawCallResult { gas_used, traces, debug, .. } = result.raw; - Self { - success: true, - traces: vec![(TraceKind::Execution, traces.expect("traces is None"))], - debug: debug.unwrap_or_default(), - gas_used, - } + Self::from_raw(result.raw, TraceKind::Deployment) } } -impl TryFrom for TraceResult { +impl TryFrom> for TraceResult { type Error = EvmError; - fn try_from(err: EvmError) -> Result { - match err { - EvmError::Execution(err) => { - let RawCallResult { reverted, gas_used, traces, debug: run_debug, .. } = err.raw; - Ok(TraceResult { - success: !reverted, - traces: vec![(TraceKind::Execution, traces.expect("traces is None"))], - debug: run_debug.unwrap_or_default(), - gas_used, - }) - } - _ => Err(err), + fn try_from(value: Result) -> Result { + match value { + Ok(result) => Ok(Self::from(result)), + Err(EvmError::Execution(err)) => Ok(Self::from_raw(err.raw, TraceKind::Deployment)), + Err(err) => Err(err), } } } @@ -401,7 +388,7 @@ pub async fn handle_traces( let mut etherscan_identifier = EtherscanIdentifier::new(config, chain)?; if let Some(etherscan_identifier) = &mut etherscan_identifier { - for (_, trace) in &mut result.traces { + for (_, trace) in result.traces.as_deref_mut().unwrap_or_default() { decoder.identify(trace, etherscan_identifier); } } @@ -413,7 +400,7 @@ pub async fn handle_traces( Default::default() }; let mut debugger = Debugger::builder() - .debug_arena(&result.debug) + .debug_arena(result.debug.as_ref().expect("missing debug arena")) .decoder(&decoder) .sources(sources) .build(); @@ -426,12 +413,10 @@ pub async fn handle_traces( } pub async fn print_traces(result: &mut TraceResult, decoder: &CallTraceDecoder) -> Result<()> { - if result.traces.is_empty() { - panic!("No traces found") - } + let traces = result.traces.as_ref().expect("No traces found"); println!("Traces:"); - for (_, arena) in &result.traces { + for (_, arena) in traces { println!("{}", render_trace_arena(arena, decoder).await?); } println!(); diff --git a/crates/evm/core/src/debug.rs b/crates/evm/core/src/debug.rs index 056bde54c..21705f128 100644 --- a/crates/evm/core/src/debug.rs +++ b/crates/evm/core/src/debug.rs @@ -6,13 +6,24 @@ use revm_inspectors::tracing::types::CallKind; use serde::{Deserialize, Serialize}; /// An arena of [DebugNode]s -#[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct DebugArena { /// The arena of nodes pub arena: Vec, } +impl Default for DebugArena { + fn default() -> Self { + Self::new() + } +} + impl DebugArena { + /// Creates a new debug arena. + pub const fn new() -> Self { + Self { arena: Vec::new() } + } + /// Pushes a new debug node into the arena pub fn push_node(&mut self, mut new_node: DebugNode) -> usize { fn recursively_push( diff --git a/crates/evm/evm/src/inspectors/debugger.rs b/crates/evm/evm/src/inspectors/debugger.rs index c6b8a7cc5..c970cd671 100644 --- a/crates/evm/evm/src/inspectors/debugger.rs +++ b/crates/evm/evm/src/inspectors/debugger.rs @@ -37,9 +37,7 @@ impl Debugger { pub fn exit(&mut self) { if let Some(parent_id) = self.arena.arena[self.head].parent { let DebugNode { depth, address, kind, .. } = self.arena.arena[parent_id]; - self.context = address; - self.head = - self.arena.push_node(DebugNode { depth, address, kind, ..Default::default() }); + self.enter(depth, address, kind); } } } @@ -59,7 +57,7 @@ impl Inspector for Debugger { let start = pc + 1; let end = start + push_size; let slice = &interp.contract.bytecode.bytecode()[start..end]; - assert!(slice.len() <= 32); + debug_assert!(slice.len() <= 32); let mut array = ArrayVec::new(); array.try_extend_from_slice(slice).unwrap(); array diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 10108c891..022586ae2 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -399,9 +399,7 @@ impl InspectorStack { labels: self .cheatcodes .as_ref() - .map(|cheatcodes| { - cheatcodes.labels.clone().into_iter().map(|l| (l.0, l.1)).collect() - }) + .map(|cheatcodes| cheatcodes.labels.clone()) .unwrap_or_default(), traces: self.tracer.map(|tracer| tracer.get_traces().clone()), debug: self.debugger.map(|debugger| debugger.arena), From a3071e5225122fd7c3d7a1759cabdfd17e314ded Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 1 Jun 2024 06:12:53 +0300 Subject: [PATCH 323/430] fix(foundryup): nproc does not exist on macos (#8024) --- foundryup/foundryup | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/foundryup/foundryup b/foundryup/foundryup index 033e68d6f..2dc94bfb4 100755 --- a/foundryup/foundryup +++ b/foundryup/foundryup @@ -6,7 +6,7 @@ FOUNDRY_DIR=${FOUNDRY_DIR:-"$BASE_DIR/.foundry"} FOUNDRY_BIN_DIR="$FOUNDRY_DIR/bin" FOUNDRY_MAN_DIR="$FOUNDRY_DIR/share/man/man1" -FOUNDRYUP_JOBS=$(nproc) +FOUNDRYUP_JOBS="" BINS=(forge cast anvil chisel) @@ -40,6 +40,12 @@ main() { esac; shift done + CARGO_BUILD_ARGS=(--release) + + if [ -n "$FOUNDRYUP_JOBS" ]; then + CARGO_BUILD_ARGS+=(--jobs "$FOUNDRYUP_JOBS") + fi + # Print the banner after successfully parsing args banner @@ -63,7 +69,7 @@ main() { # Enter local repo and build say "installing from $FOUNDRYUP_LOCAL_REPO" cd "$FOUNDRYUP_LOCAL_REPO" - ensure cargo build --bins --release -j "$FOUNDRYUP_JOBS" # need 4 speed + ensure cargo build --bins "${CARGO_BUILD_ARGS[@]}" for bin in "${BINS[@]}"; do # Remove prior installations if they exist @@ -200,7 +206,7 @@ EOF fi # Build the repo and install the binaries locally to the .foundry bin directory. - ensure cargo build --bins --release -j "$FOUNDRYUP_JOBS" + ensure cargo build --bins "${CARGO_BUILD_ARGS[@]}" for bin in "${BINS[@]}"; do for try_path in target/release/$bin target/release/$bin.exe; do if [ -f "$try_path" ]; then From bdf05a8bb4aabc5683508c33e90f421b46a27261 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 1 Jun 2024 12:38:55 +0300 Subject: [PATCH 324/430] perf: prefill fuzz dictionary with 0 (#8027) --- Cargo.lock | 1 - crates/evm/evm/Cargo.toml | 1 - crates/evm/evm/src/executors/invariant/mod.rs | 9 +- crates/evm/fuzz/src/invariant/mod.rs | 20 ++-- crates/evm/fuzz/src/strategies/state.rs | 113 ++++++++++-------- 5 files changed, 80 insertions(+), 64 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cc0e58724..59daf146b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3503,7 +3503,6 @@ dependencies = [ "alloy-primitives", "alloy-sol-types", "arrayvec", - "const-hex", "eyre", "foundry-cheatcodes", "foundry-common", diff --git a/crates/evm/evm/Cargo.toml b/crates/evm/evm/Cargo.toml index 72f4a6d17..5168c35e0 100644 --- a/crates/evm/evm/Cargo.toml +++ b/crates/evm/evm/Cargo.toml @@ -43,7 +43,6 @@ revm-inspectors.workspace = true arrayvec.workspace = true eyre = "0.6" -hex.workspace = true parking_lot = "0.12" proptest = "1" thiserror = "1" diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index be8dbf2ba..9d4a9f4c0 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -321,8 +321,8 @@ impl<'a> InvariantExecutor<'a> { Ok(()) }); - trace!(target: "forge::test::invariant::fuzz_fixtures", "{:?}", fuzz_fixtures); - trace!(target: "forge::test::invariant::dictionary", "{:?}", fuzz_state.dictionary_read().values().iter().map(hex::encode).collect::>()); + trace!(?fuzz_fixtures); + trace!(state_len = fuzz_state.dictionary_read().len()); let (reverts, error) = failures.into_inner().into_inner(); @@ -678,10 +678,9 @@ fn collect_data( } // Collect values from fuzzed call result and add them to fuzz dictionary. - let (fuzzed_contract_abi, fuzzed_function) = fuzzed_contracts.fuzzed_artifacts(tx); fuzz_state.collect_values_from_call( - fuzzed_contract_abi.as_ref(), - fuzzed_function.as_ref(), + fuzzed_contracts, + tx, &call_result.result, &call_result.logs, &*state_changeset, diff --git a/crates/evm/fuzz/src/invariant/mod.rs b/crates/evm/fuzz/src/invariant/mod.rs index 7c3a1ffc0..e83c59549 100644 --- a/crates/evm/fuzz/src/invariant/mod.rs +++ b/crates/evm/fuzz/src/invariant/mod.rs @@ -28,15 +28,21 @@ impl FuzzRunIdentifiedContracts { } /// Returns fuzzed contract abi and fuzzed function from address and provided calldata. + /// /// Used to decode return values and logs in order to add values into fuzz dictionary. - pub fn fuzzed_artifacts(&self, tx: &BasicTxDetails) -> (Option, Option) { - match self.targets.lock().get(&tx.call_details.target) { - Some((_, abi, _)) => ( - Some(abi.to_owned()), - abi.functions().find(|f| f.selector() == tx.call_details.calldata[..4]).cloned(), - ), + pub fn with_fuzzed_artifacts( + &self, + tx: &BasicTxDetails, + f: impl FnOnce(Option<&JsonAbi>, Option<&Function>), + ) { + let targets = self.targets.lock(); + let (abi, abi_f) = match targets.get(&tx.call_details.target) { + Some((_, abi, _)) => { + (Some(abi), abi.functions().find(|f| f.selector() == tx.call_details.calldata[..4])) + } None => (None, None), - } + }; + f(abi, abi_f); } } diff --git a/crates/evm/fuzz/src/strategies/state.rs b/crates/evm/fuzz/src/strategies/state.rs index 81aa93a5a..a138f4507 100644 --- a/crates/evm/fuzz/src/strategies/state.rs +++ b/crates/evm/fuzz/src/strategies/state.rs @@ -1,4 +1,4 @@ -use crate::invariant::{ArtifactFilters, FuzzRunIdentifiedContracts}; +use crate::invariant::{ArtifactFilters, BasicTxDetails, FuzzRunIdentifiedContracts}; use alloy_dyn_abi::{DynSolType, DynSolValue, EventExt, FunctionExt}; use alloy_json_abi::{Function, JsonAbi}; use alloy_primitives::{Address, Bytes, Log, B256, U256}; @@ -18,6 +18,12 @@ use std::{ sync::Arc, }; +/// The maximum number of bytes we will look at in bytecodes to find push bytes (24 KiB). +/// +/// This is to limit the performance impact of fuzz tests that might deploy arbitrarily sized +/// bytecode (as is the case with Solmate). +const PUSH_BYTE_ANALYSIS_LIMIT: usize = 24 * 1024; + /// A set of arbitrary 32 byte data from the VM used to generate values for the strategy. /// /// Wrapped in a shareable container. @@ -49,16 +55,18 @@ impl EvmFuzzState { /// the given [FuzzDictionaryConfig]. pub fn collect_values_from_call( &self, - target_abi: Option<&JsonAbi>, - target_function: Option<&Function>, + fuzzed_contracts: &FuzzRunIdentifiedContracts, + tx: &BasicTxDetails, result: &Bytes, logs: &[Log], state_changeset: &StateChangeset, run_depth: u32, ) { let mut dict = self.inner.write(); - dict.insert_result_values(target_function, result, run_depth); - dict.insert_logs_values(target_abi, logs, run_depth); + fuzzed_contracts.with_fuzzed_artifacts(tx, |target_abi, target_function| { + dict.insert_logs_values(target_abi, logs, run_depth); + dict.insert_result_values(target_function, result, run_depth); + }); dict.insert_state_values(state_changeset); } @@ -104,7 +112,14 @@ impl fmt::Debug for FuzzDictionary { impl FuzzDictionary { pub fn new(config: FuzzDictionaryConfig) -> Self { - Self { config, ..Default::default() } + let mut dictionary = Self { config, ..Default::default() }; + dictionary.prefill(); + dictionary + } + + /// Insert common values into the dictionary at initialization. + fn prefill(&mut self) { + self.insert_value([0; 32], false); } /// Insert values from initial db state into fuzz dictionary. @@ -217,10 +232,42 @@ impl FuzzDictionary { // Insert push bytes if let Some(code) = account_info.code.clone() { self.insert_address(*address, collected); - for push_byte in collect_push_bytes(&code.bytes()) { - self.insert_value(push_byte, collected); + self.collect_push_bytes(code.bytes_slice(), collected); + } + } + } + + fn collect_push_bytes(&mut self, code: &[u8], collected: bool) { + let mut i = 0; + let len = code.len().min(PUSH_BYTE_ANALYSIS_LIMIT); + while i < len { + let op = code[i]; + if (opcode::PUSH1..=opcode::PUSH32).contains(&op) { + let push_size = (op - opcode::PUSH1 + 1) as usize; + let push_start = i + 1; + let push_end = push_start + push_size; + // As a precaution, if a fuzz test deploys malformed bytecode (such as using + // `CREATE2`) this will terminate the loop early. + if push_start > code.len() || push_end > code.len() { + break; + } + + let push_value = U256::try_from_be_slice(&code[push_start..push_end]).unwrap(); + // Also add the value below and above the push value to the dictionary. + if push_value != U256::ZERO { + // Never add 0 to the dictionary as it's always present, and it's a pretty + // common value that this is worth it. + self.insert_value(push_value.to_be_bytes(), collected); + + self.insert_value((push_value - U256::from(1)).to_be_bytes(), collected); } + if push_value != U256::MAX { + self.insert_value((push_value + U256::from(1)).to_be_bytes(), collected); + } + + i += push_size; } + i += 1; } } @@ -283,11 +330,18 @@ impl FuzzDictionary { } } - #[inline] pub fn values(&self) -> &IndexSet<[u8; 32]> { &self.state_values } + pub fn len(&self) -> usize { + self.state_values.len() + } + + pub fn is_empty(&self) -> bool { + self.state_values.is_empty() + } + #[inline] pub fn samples(&self, param_type: DynSolType) -> Option<&IndexSet<[u8; 32]>> { self.sample_values.get(¶m_type) @@ -312,47 +366,6 @@ impl FuzzDictionary { } } -/// The maximum number of bytes we will look at in bytecodes to find push bytes (24 KiB). -/// -/// This is to limit the performance impact of fuzz tests that might deploy arbitrarily sized -/// bytecode (as is the case with Solmate). -const PUSH_BYTE_ANALYSIS_LIMIT: usize = 24 * 1024; - -/// Collects all push bytes from the given bytecode. -fn collect_push_bytes(code: &[u8]) -> Vec<[u8; 32]> { - let mut bytes: Vec<[u8; 32]> = Vec::new(); - // We use [SpecId::LATEST] since we do not really care what spec it is - we are not interested - // in gas costs. - let mut i = 0; - while i < code.len().min(PUSH_BYTE_ANALYSIS_LIMIT) { - let op = code[i]; - if (opcode::PUSH1..=opcode::PUSH32).contains(&op) { - let push_size = (op - opcode::PUSH1 + 1) as usize; - let push_start = i + 1; - let push_end = push_start + push_size; - // As a precaution, if a fuzz test deploys malformed bytecode (such as using `CREATE2`) - // this will terminate the loop early. - if push_start > code.len() || push_end > code.len() { - return bytes; - } - - let push_value = U256::try_from_be_slice(&code[push_start..push_end]).unwrap(); - bytes.push(push_value.to_be_bytes()); - // also add the value below and above the push value to the dictionary. - if push_value != U256::ZERO { - bytes.push((push_value - U256::from(1)).to_be_bytes()); - } - if push_value != U256::MAX { - bytes.push((push_value + U256::from(1)).to_be_bytes()); - } - - i += push_size; - } - i += 1; - } - bytes -} - /// Collects all created contracts from a StateChangeset which haven't been discovered yet. Stores /// them at `targeted_contracts` and `created_contracts`. pub fn collect_created_contracts( From 80986a7eff10ae4ddba7e757fc073afb5e079569 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 1 Jun 2024 12:42:14 +0300 Subject: [PATCH 325/430] refactor(coverage): refactor coverage analysis (#8025) --- Cargo.lock | 2 + Cargo.toml | 5 +- crates/anvil/Cargo.toml | 21 ++- crates/cast/Cargo.toml | 10 +- crates/chisel/Cargo.toml | 20 +- crates/doc/Cargo.toml | 2 +- crates/evm/core/src/decode.rs | 5 +- crates/evm/core/src/ic.rs | 20 ++ crates/evm/coverage/Cargo.toml | 1 + crates/evm/coverage/src/analysis.rs | 232 ++++++++++++----------- crates/evm/coverage/src/anchors.rs | 89 +++++---- crates/evm/coverage/src/lib.rs | 28 ++- crates/evm/traces/Cargo.toml | 8 +- crates/evm/traces/src/decoder/mod.rs | 3 +- crates/forge/Cargo.toml | 14 +- crates/forge/bin/cmd/coverage.rs | 263 +++++++++++++-------------- crates/forge/src/coverage.rs | 2 +- 17 files changed, 396 insertions(+), 329 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 59daf146b..b77ed1a33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3566,6 +3566,7 @@ dependencies = [ "foundry-common", "foundry-compilers", "foundry-evm-core", + "rayon", "revm", "rustc-hash", "semver 1.0.23", @@ -3616,6 +3617,7 @@ dependencies = [ "itertools 0.13.0", "once_cell", "revm-inspectors", + "rustc-hash", "serde", "tempfile", "tokio", diff --git a/Cargo.toml b/Cargo.toml index d5b4bdb41..8d0264d45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,17 +82,17 @@ scrypt.opt-level = 3 [profile.local] inherits = "dev" opt-level = 1 +debug-assertions = false strip = "debuginfo" panic = "abort" codegen-units = 16 # Like release, but with full debug symbols and with stack unwinds. Useful for e.g. `perf`. [profile.debug-fast] -inherits = "release" +inherits = "local" debug = true strip = "none" panic = "unwind" -incremental = false # Optimized release profile. [profile.release] @@ -216,6 +216,7 @@ num-format = "0.4.4" yansi = { version = "1.0", features = ["detect-tty", "detect-env"] } tempfile = "3.10" tokio = "1" +rayon = "1" axum = "0.7" hyper = "1.0" diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index 9ca41a520..96c0a2071 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -16,7 +16,11 @@ path = "src/anvil.rs" required-features = ["cli"] [build-dependencies] -vergen = { workspace = true, default-features = false, features = ["build", "git", "gitcl"] } +vergen = { workspace = true, default-features = false, features = [ + "build", + "git", + "gitcl", +] } [dependencies] # foundry internal @@ -31,7 +35,12 @@ foundry-evm.workspace = true # evm support bytes = "1.4.0" k256.workspace = true -revm = { workspace = true, features = ["std", "serde", "memory_limit", "c-kzg"] } +revm = { workspace = true, features = [ + "std", + "serde", + "memory_limit", + "c-kzg", +] } alloy-primitives = { workspace = true, features = ["serde"] } alloy-consensus = { workspace = true, features = ["k256", "kzg"] } alloy-contract = { workspace = true, features = ["pubsub"] } @@ -78,7 +87,11 @@ rand = "0.8" eyre.workspace = true # cli -clap = { version = "4", features = ["derive", "env", "wrap_help"], optional = true } +clap = { version = "4", features = [ + "derive", + "env", + "wrap_help", +], optional = true } clap_complete = { version = "4", optional = true } chrono.workspace = true auto_impl = "1" @@ -101,7 +114,7 @@ similar-asserts.workspace = true tokio = { workspace = true, features = ["full"] } [features] -default = ["cli"] +default = ["cli", "jemalloc"] cmd = ["clap", "clap_complete", "ctrlc", "anvil-server/clap"] cli = ["tokio/full", "cmd", "fdlimit"] asm-keccak = ["alloy-primitives/asm-keccak"] diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index bf180b9b5..443e5992c 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -15,7 +15,11 @@ name = "cast" path = "bin/main.rs" [build-dependencies] -vergen = { workspace = true, default-features = false, features = ["build", "git", "gitcl"] } +vergen = { workspace = true, default-features = false, features = [ + "build", + "git", + "gitcl", +] } [dependencies] # lib @@ -48,7 +52,7 @@ eyre.workspace = true futures = "0.3" hex.workspace = true rand.workspace = true -rayon = "1" +rayon.workspace = true serde_json.workspace = true serde.workspace = true @@ -83,7 +87,7 @@ async-trait = "0.1" criterion = "0.5" [features] -default = ["rustls"] +default = ["rustls", "jemalloc"] rustls = ["foundry-cli/rustls", "foundry-wallets/rustls"] openssl = ["foundry-cli/openssl"] asm-keccak = ["alloy-primitives/asm-keccak"] diff --git a/crates/chisel/Cargo.toml b/crates/chisel/Cargo.toml index fc8a2a782..f5e9b7b02 100644 --- a/crates/chisel/Cargo.toml +++ b/crates/chisel/Cargo.toml @@ -1,6 +1,9 @@ [package] name = "chisel" -authors = ["clabby ", "asnared "] +authors = [ + "clabby ", + "asnared ", +] description = "Fast, utilitarian, and verbose Solidity REPL" version.workspace = true @@ -15,7 +18,11 @@ name = "chisel" path = "bin/main.rs" [build-dependencies] -vergen = { workspace = true, default-features = false, features = ["build", "git", "gitcl"] } +vergen = { workspace = true, default-features = false, features = [ + "build", + "git", + "gitcl", +] } [dependencies] # forge @@ -28,7 +35,12 @@ foundry-config.workspace = true foundry-evm.workspace = true alloy-dyn-abi = { workspace = true, features = ["arbitrary"] } -alloy-primitives = { workspace = true, features = ["serde", "getrandom", "arbitrary", "rlp"] } +alloy-primitives = { workspace = true, features = [ + "serde", + "getrandom", + "arbitrary", + "rlp", +] } alloy-json-abi.workspace = true alloy-rpc-types.workspace = true @@ -59,7 +71,7 @@ serial_test = "3" tracing-subscriber.workspace = true [features] -default = ["rustls"] +default = ["rustls", "jemalloc"] rustls = ["reqwest/rustls-tls", "reqwest/rustls-tls-native-roots"] openssl = ["foundry-compilers/openssl", "reqwest/default-tls"] asm-keccak = ["alloy-primitives/asm-keccak"] diff --git a/crates/doc/Cargo.toml b/crates/doc/Cargo.toml index 73368bd9f..1965befb8 100644 --- a/crates/doc/Cargo.toml +++ b/crates/doc/Cargo.toml @@ -24,7 +24,7 @@ eyre.workspace = true itertools.workspace = true mdbook = { version = "0.4", default-features = false, features = ["search"] } once_cell = "1" -rayon = "1" +rayon.workspace = true serde_json.workspace = true serde.workspace = true solang-parser.workspace = true diff --git a/crates/evm/core/src/decode.rs b/crates/evm/core/src/decode.rs index bf82c2d34..8b09316e3 100644 --- a/crates/evm/core/src/decode.rs +++ b/crates/evm/core/src/decode.rs @@ -8,7 +8,8 @@ use alloy_sol_types::{SolCall, SolError, SolEventInterface, SolInterface, SolVal use foundry_common::SELECTOR_LEN; use itertools::Itertools; use revm::interpreter::InstructionResult; -use std::{collections::HashMap, sync::OnceLock}; +use rustc_hash::FxHashMap; +use std::sync::OnceLock; /// Decode a set of logs, only returning logs from DSTest logging events and Hardhat's `console.log` pub fn decode_console_logs(logs: &[Log]) -> Vec { @@ -27,7 +28,7 @@ pub fn decode_console_log(log: &Log) -> Option { #[derive(Clone, Debug, Default)] pub struct RevertDecoder { /// The custom errors to use for decoding. - pub errors: HashMap>, + pub errors: FxHashMap>, } impl Default for &RevertDecoder { diff --git a/crates/evm/core/src/ic.rs b/crates/evm/core/src/ic.rs index fa2b5efd5..c2792ab87 100644 --- a/crates/evm/core/src/ic.rs +++ b/crates/evm/core/src/ic.rs @@ -14,6 +14,16 @@ impl PcIcMap { Self { inner: make_map::(code) } } + /// Returns the length of the map. + pub fn len(&self) -> usize { + self.inner.len() + } + + /// Returns `true` if the map is empty. + pub fn is_empty(&self) -> bool { + self.inner.is_empty() + } + /// Returns the instruction counter for the given program counter. pub fn get(&self, pc: usize) -> Option { self.inner.get(&pc).copied() @@ -33,6 +43,16 @@ impl IcPcMap { Self { inner: make_map::(code) } } + /// Returns the length of the map. + pub fn len(&self) -> usize { + self.inner.len() + } + + /// Returns `true` if the map is empty. + pub fn is_empty(&self) -> bool { + self.inner.is_empty() + } + /// Returns the program counter for the given instruction counter. pub fn get(&self, ic: usize) -> Option { self.inner.get(&ic).copied() diff --git a/crates/evm/coverage/Cargo.toml b/crates/evm/coverage/Cargo.toml index 6a3dc92ff..57a7a9f2b 100644 --- a/crates/evm/coverage/Cargo.toml +++ b/crates/evm/coverage/Cargo.toml @@ -21,3 +21,4 @@ revm.workspace = true semver = "1" tracing = "0.1" rustc-hash.workspace = true +rayon.workspace = true diff --git a/crates/evm/coverage/src/analysis.rs b/crates/evm/coverage/src/analysis.rs index 040da5a0f..cfcb7406f 100644 --- a/crates/evm/coverage/src/analysis.rs +++ b/crates/evm/coverage/src/analysis.rs @@ -1,9 +1,9 @@ -use super::{ContractId, CoverageItem, CoverageItemKind, SourceLocation}; +use super::{CoverageItem, CoverageItemKind, SourceLocation}; use foundry_common::TestFunctionExt; use foundry_compilers::artifacts::ast::{self, Ast, Node, NodeType}; +use rayon::prelude::*; use rustc_hash::FxHashMap; -use semver::Version; -use std::collections::HashMap; +use std::sync::Arc; /// A visitor that walks the AST of a single contract and finds coverage items. #[derive(Clone, Debug)] @@ -14,7 +14,7 @@ pub struct ContractVisitor<'a> { source: &'a str, /// The name of the contract being walked. - contract_name: String, + contract_name: &'a Arc, /// The current branch ID branch_id: usize, @@ -26,14 +26,14 @@ pub struct ContractVisitor<'a> { } impl<'a> ContractVisitor<'a> { - pub fn new(source_id: usize, source: &'a str, contract_name: String) -> Self { + pub fn new(source_id: usize, source: &'a str, contract_name: &'a Arc) -> Self { Self { source_id, source, contract_name, branch_id: 0, last_line: 0, items: Vec::new() } } - pub fn visit(mut self, contract_ast: Node) -> eyre::Result { + pub fn visit_contract(&mut self, node: &Node) -> eyre::Result<()> { // Find all functions and walk their AST - for node in contract_ast.nodes { - match &node.node_type { + for node in &node.nodes { + match node.node_type { NodeType::FunctionDefinition => { self.visit_function_definition(node)?; } @@ -43,11 +43,10 @@ impl<'a> ContractVisitor<'a> { _ => {} } } - - Ok(self) + Ok(()) } - fn visit_function_definition(&mut self, mut node: Node) -> eyre::Result<()> { + fn visit_function_definition(&mut self, node: &Node) -> eyre::Result<()> { let name: String = node.attribute("name").ok_or_else(|| eyre::eyre!("Function has no name"))?; @@ -58,46 +57,47 @@ impl<'a> ContractVisitor<'a> { return Ok(()) } - match node.body.take() { + match &node.body { Some(body) => { self.push_item(CoverageItem { kind: CoverageItemKind::Function { name }, loc: self.source_location_for(&node.src), hits: 0, }); - self.visit_block(*body) + self.visit_block(body) } _ => Ok(()), } } - fn visit_modifier_definition(&mut self, mut node: Node) -> eyre::Result<()> { + fn visit_modifier_definition(&mut self, node: &Node) -> eyre::Result<()> { let name: String = node.attribute("name").ok_or_else(|| eyre::eyre!("Modifier has no name"))?; - match node.body.take() { + match &node.body { Some(body) => { self.push_item(CoverageItem { kind: CoverageItemKind::Function { name }, loc: self.source_location_for(&node.src), hits: 0, }); - self.visit_block(*body) + self.visit_block(body) } _ => Ok(()), } } - fn visit_block(&mut self, node: Node) -> eyre::Result<()> { + fn visit_block(&mut self, node: &Node) -> eyre::Result<()> { let statements: Vec = node.attribute("statements").unwrap_or_default(); - for statement in statements { + for statement in &statements { self.visit_statement(statement)?; } Ok(()) } - fn visit_statement(&mut self, node: Node) -> eyre::Result<()> { + + fn visit_statement(&mut self, node: &Node) -> eyre::Result<()> { // TODO: YulSwitch, YulForLoop, YulFunctionDefinition, YulVariableDeclaration match node.node_type { // Blocks @@ -106,7 +106,8 @@ impl<'a> ContractVisitor<'a> { } // Inline assembly block NodeType::InlineAssembly => self.visit_block( - node.attribute("AST") + &node + .attribute("AST") .ok_or_else(|| eyre::eyre!("inline assembly block with no AST attribute"))?, ), // Simple statements @@ -137,7 +138,7 @@ impl<'a> ContractVisitor<'a> { hits: 0, }); if let Some(expr) = node.attribute("expression") { - self.visit_expression(expr)?; + self.visit_expression(&expr)?; } Ok(()) } @@ -150,47 +151,54 @@ impl<'a> ContractVisitor<'a> { hits: 0, }); if let Some(expr) = node.attribute("initialValue") { - self.visit_expression(expr)?; + self.visit_expression(&expr)?; } Ok(()) } // While loops NodeType::DoWhileStatement | NodeType::WhileStatement => { self.visit_expression( - node.attribute("condition") + &node + .attribute("condition") .ok_or_else(|| eyre::eyre!("while statement had no condition"))?, )?; - let body = - node.body.ok_or_else(|| eyre::eyre!("while statement had no body node"))?; - self.visit_block_or_statement(*body) + let body = node + .body + .as_deref() + .ok_or_else(|| eyre::eyre!("while statement had no body node"))?; + self.visit_block_or_statement(body) } // For loops NodeType::ForStatement => { if let Some(stmt) = node.attribute("initializationExpression") { - self.visit_statement(stmt)?; + self.visit_statement(&stmt)?; } if let Some(expr) = node.attribute("condition") { - self.visit_expression(expr)?; + self.visit_expression(&expr)?; } if let Some(stmt) = node.attribute("loopExpression") { - self.visit_statement(stmt)?; + self.visit_statement(&stmt)?; } - let body = - node.body.ok_or_else(|| eyre::eyre!("for statement had no body node"))?; - self.visit_block_or_statement(*body) + let body = node + .body + .as_deref() + .ok_or_else(|| eyre::eyre!("for statement had no body node"))?; + self.visit_block_or_statement(body) } // Expression statement NodeType::ExpressionStatement | NodeType::YulExpressionStatement => self .visit_expression( - node.attribute("expression") + &node + .attribute("expression") .ok_or_else(|| eyre::eyre!("expression statement had no expression"))?, ), // If statement NodeType::IfStatement => { self.visit_expression( - node.attribute("condition") + &node + .attribute("condition") .ok_or_else(|| eyre::eyre!("while statement had no condition"))?, )?; @@ -224,22 +232,25 @@ impl<'a> ContractVisitor<'a> { ); // Process the true branch - self.visit_block_or_statement(true_body)?; + self.visit_block_or_statement(&true_body)?; // Process the false branch - let false_body: Option = node.attribute("falseBody"); - if let Some(false_body) = false_body { - self.visit_block_or_statement(false_body)?; + if let Some(false_body) = node.attribute("falseBody") { + self.visit_block_or_statement(&false_body)?; } Ok(()) } NodeType::YulIf => { self.visit_expression( - node.attribute("condition") + &node + .attribute("condition") .ok_or_else(|| eyre::eyre!("yul if statement had no condition"))?, )?; - let body = node.body.ok_or_else(|| eyre::eyre!("yul if statement had no body"))?; + let body = node + .body + .as_deref() + .ok_or_else(|| eyre::eyre!("yul if statement had no body"))?; // We need to store the current branch ID here since visiting the body of either of // the if blocks may increase `self.branch_id` in the case of nested if statements. @@ -254,7 +265,7 @@ impl<'a> ContractVisitor<'a> { loc: self.source_location_for(&node.src), hits: 0, }); - self.visit_block(*body)?; + self.visit_block(body)?; Ok(()) } @@ -263,7 +274,8 @@ impl<'a> ContractVisitor<'a> { // TODO: Clauses // TODO: This is branching, right? self.visit_expression( - node.attribute("externalCall") + &node + .attribute("externalCall") .ok_or_else(|| eyre::eyre!("try statement had no call"))?, ) } @@ -274,7 +286,7 @@ impl<'a> ContractVisitor<'a> { } } - fn visit_expression(&mut self, node: Node) -> eyre::Result<()> { + fn visit_expression(&mut self, node: &Node) -> eyre::Result<()> { // TODO // elementarytypenameexpression // memberaccess @@ -301,11 +313,11 @@ impl<'a> ContractVisitor<'a> { // There could possibly a function call in the left or right expression // e.g: callFunc(a) + callFunc(b) if let Some(expr) = node.attribute("leftExpression") { - self.visit_expression(expr)?; + self.visit_expression(&expr)?; } if let Some(expr) = node.attribute("rightExpression") { - self.visit_expression(expr)?; + self.visit_expression(&expr)?; } Ok(()) @@ -352,7 +364,7 @@ impl<'a> ContractVisitor<'a> { } } - fn visit_block_or_statement(&mut self, node: Node) -> eyre::Result<()> { + fn visit_block_or_statement(&mut self, node: &Node) -> eyre::Result<()> { match node.node_type { NodeType::Block => self.visit_block(node), NodeType::Break | @@ -420,6 +432,7 @@ impl<'a> ContractVisitor<'a> { } } +/// [`SourceAnalyzer`] result type. #[derive(Debug)] pub struct SourceAnalysis { /// A collection of coverage items. @@ -427,47 +440,15 @@ pub struct SourceAnalysis { } /// Analyzes a set of sources to find coverage items. -#[derive(Clone, Debug, Default)] -pub struct SourceAnalyzer { - /// A map of source IDs to their source code - sources: FxHashMap, - /// A map of contract IDs to their AST nodes. - contracts: HashMap, - /// A collection of coverage items. - items: Vec, +#[derive(Debug)] +pub struct SourceAnalyzer<'a> { + sources: &'a SourceFiles<'a>, } -impl SourceAnalyzer { +impl<'a> SourceAnalyzer<'a> { /// Creates a new source analyzer. - /// - /// The source analyzer expects all given sources to belong to the same compilation job - /// (defined by `version`). - pub fn new( - version: Version, - asts: FxHashMap, - sources: FxHashMap, - ) -> eyre::Result { - let mut analyzer = SourceAnalyzer { sources, ..Default::default() }; - - // TODO: Skip interfaces - for (source_id, ast) in asts.into_iter() { - for child in ast.nodes { - if !matches!(child.node_type, NodeType::ContractDefinition) { - continue - } - - let contract_id = ContractId { - version: version.clone(), - source_id, - contract_name: child - .attribute("name") - .ok_or_else(|| eyre::eyre!("Contract has no name"))?, - }; - analyzer.contracts.insert(contract_id, child); - } - } - - Ok(analyzer) + pub fn new(data: &'a SourceFiles<'a>) -> Self { + Self { sources: data } } /// Analyzes contracts in the sources held by the source analyzer. @@ -483,33 +464,64 @@ impl SourceAnalyzer { /// Note: Source IDs are only unique per compilation job; that is, a code base compiled with /// two different solc versions will produce overlapping source IDs if the compiler version is /// not taken into account. - pub fn analyze(mut self) -> eyre::Result { - for (contract_id, ast) in self.contracts { - let ContractVisitor { items, .. } = ContractVisitor::new( - contract_id.source_id, - self.sources.get(&contract_id.source_id).ok_or_else(|| { - eyre::eyre!( - "We should have the source code for source ID {}", - contract_id.source_id - ) - })?, - contract_id.contract_name.clone(), - ) - .visit(ast)?; - - let is_test = items.iter().any(|item| { - if let CoverageItemKind::Function { name } = &item.kind { - name.is_test() - } else { - false - } - }); + pub fn analyze(&self) -> eyre::Result { + let items = self + .sources + .sources + .par_iter() + .flat_map_iter(|(&source_id, SourceFile { source, ast })| { + ast.nodes.iter().map(move |node| { + if !matches!(node.node_type, NodeType::ContractDefinition) { + return Ok(vec![]); + } - if !is_test { - self.items.extend(items); - } - } + // Skip interfaces which have no function implementations. + let contract_kind: String = node + .attribute("contractKind") + .ok_or_else(|| eyre::eyre!("Contract has no kind"))?; + if contract_kind == "interface" { + return Ok(vec![]); + } - Ok(SourceAnalysis { items: self.items }) + let name = node + .attribute("name") + .ok_or_else(|| eyre::eyre!("Contract has no name"))?; + + let mut visitor = ContractVisitor::new(source_id, source, &name); + visitor.visit_contract(node)?; + let mut items = visitor.items; + + let is_test = items.iter().any(|item| { + if let CoverageItemKind::Function { name } = &item.kind { + name.is_test() + } else { + false + } + }); + if is_test { + items.clear(); + } + + Ok(items) + }) + }) + .collect::>>>()?; + Ok(SourceAnalysis { items: items.concat() }) } } + +/// A list of versioned sources and their ASTs. +#[derive(Debug, Default)] +pub struct SourceFiles<'a> { + /// The versioned sources. + pub sources: FxHashMap>, +} + +/// The source code and AST of a file. +#[derive(Debug)] +pub struct SourceFile<'a> { + /// The source code. + pub source: String, + /// The AST of the source code. + pub ast: &'a Ast, +} diff --git a/crates/evm/coverage/src/anchors.rs b/crates/evm/coverage/src/anchors.rs index a1992d47a..f05f7c287 100644 --- a/crates/evm/coverage/src/anchors.rs +++ b/crates/evm/coverage/src/anchors.rs @@ -1,52 +1,47 @@ -use std::collections::HashMap; - use super::{CoverageItem, CoverageItemKind, ItemAnchor, SourceLocation}; -use alloy_primitives::Bytes; +use eyre::ensure; use foundry_compilers::sourcemap::{SourceElement, SourceMap}; use foundry_evm_core::utils::IcPcMap; -use revm::{ - interpreter::opcode::{self, PUSH1, PUSH32}, - primitives::HashSet, -}; +use revm::interpreter::opcode; +use rustc_hash::{FxHashMap, FxHashSet}; /// Attempts to find anchors for the given items using the given source map and bytecode. pub fn find_anchors( - bytecode: &Bytes, + bytecode: &[u8], source_map: &SourceMap, ic_pc_map: &IcPcMap, items: &[CoverageItem], - items_by_source_id: &HashMap>, + items_by_source_id: &FxHashMap>, ) -> Vec { - // Prepare coverage items from all sources referenced in the source map - let potential_item_ids = source_map + let mut seen = FxHashSet::default(); + source_map .iter() .filter_map(|element| items_by_source_id.get(&(element.index? as usize))) .flatten() - .collect::>(); - - potential_item_ids - .into_iter() - .filter_map(|item_id| { - let item = &items[*item_id]; + .filter_map(|&item_id| { + if !seen.insert(item_id) { + return None; + } + let item = &items[item_id]; match item.kind { CoverageItemKind::Branch { path_id, .. } => { - match find_anchor_branch(bytecode, source_map, *item_id, &item.loc) { + match find_anchor_branch(bytecode, source_map, item_id, &item.loc) { Ok(anchors) => match path_id { 0 => Some(anchors.0), 1 => Some(anchors.1), _ => panic!("Too many paths for branch"), }, Err(e) => { - warn!("Could not find anchor for item: {}, error: {e}", item); + warn!("Could not find anchor for item {item}: {e}"); None } } } - _ => match find_anchor_simple(source_map, ic_pc_map, *item_id, &item.loc) { + _ => match find_anchor_simple(source_map, ic_pc_map, item_id, &item.loc) { Ok(anchor) => Some(anchor), Err(e) => { - warn!("Could not find anchor for item: {}, error: {e}", item); + warn!("Could not find anchor for item {item}: {e}"); None } }, @@ -62,13 +57,10 @@ pub fn find_anchor_simple( item_id: usize, loc: &SourceLocation, ) -> eyre::Result { - let instruction = source_map - .iter() - .enumerate() - .find_map(|(ic, element)| is_in_source_range(element, loc).then_some(ic)) - .ok_or_else(|| { - eyre::eyre!("Could not find anchor: No matching instruction in range {}", loc) - })?; + let instruction = + source_map.iter().position(|element| is_in_source_range(element, loc)).ok_or_else( + || eyre::eyre!("Could not find anchor: No matching instruction in range {loc}"), + )?; Ok(ItemAnchor { instruction: ic_pc_map.get(instruction).ok_or_else(|| { @@ -107,7 +99,7 @@ pub fn find_anchor_simple( /// counter of the first branch, and return an item for that program counter, and the /// program counter immediately after the JUMPI instruction. pub fn find_anchor_branch( - bytecode: &Bytes, + bytecode: &[u8], source_map: &SourceMap, item_id: usize, loc: &SourceLocation, @@ -118,14 +110,14 @@ pub fn find_anchor_branch( let mut anchors: Option<(ItemAnchor, ItemAnchor)> = None; let mut pc = 0; let mut cumulative_push_size = 0; - while pc < bytecode.0.len() { - let op = bytecode.0[pc]; + while pc < bytecode.len() { + let op = bytecode[pc]; // We found a push, so we do some PC -> IC translation accounting, but we also check if // this push is coupled with the JUMPI we are interested in. // Check if Opcode is PUSH - if (PUSH1..=PUSH32).contains(&op) { + if (opcode::PUSH1..=opcode::PUSH32).contains(&op) { let element = if let Some(element) = source_map.get(pc - cumulative_push_size) { element } else { @@ -141,29 +133,25 @@ pub fn find_anchor_branch( // Check if we are in the source range we are interested in, and if the next opcode // is a JUMPI - if is_in_source_range(element, loc) && bytecode.0[pc + 1] == opcode::JUMPI { + if is_in_source_range(element, loc) && bytecode[pc + 1] == opcode::JUMPI { // We do not support program counters bigger than usize. This is also an // assumption in REVM, so this is just a sanity check. - if push_size > 8 { - panic!("We found the anchor for the branch, but it refers to a program counter bigger than 64 bits."); - } + ensure!(push_size <= 8, "jump destination overflow"); // Convert the push bytes for the second branch's PC to a usize let push_bytes_start = pc - push_size + 1; - let mut pc_bytes: [u8; 8] = [0; 8]; - for (i, push_byte) in - bytecode.0[push_bytes_start..push_bytes_start + push_size].iter().enumerate() - { - pc_bytes[8 - push_size + i] = *push_byte; - } - + let push_bytes = &bytecode[push_bytes_start..push_bytes_start + push_size]; + let mut pc_bytes = [0u8; 8]; + pc_bytes[8 - push_size..].copy_from_slice(push_bytes); + let pc = u64::from_be_bytes(pc_bytes); + let pc = usize::try_from(pc).expect("PC is too big"); anchors = Some(( ItemAnchor { item_id, // The first branch is the opcode directly after JUMPI instruction: pc + 2, }, - ItemAnchor { item_id, instruction: usize::from_be_bytes(pc_bytes) }, + ItemAnchor { item_id, instruction: pc }, )); } } @@ -175,14 +163,25 @@ 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); + if !source_ids_match { + return false; + } // Needed because some source ranges in the source map mark the entire contract... let is_within_start = element.offset >= location.start; + if !is_within_start { + return false; + } let start_of_ranges = location.start.max(element.offset); let end_of_ranges = (location.start + location.length.unwrap_or_default()).min(element.offset + element.length); + let within_ranges = start_of_ranges <= end_of_ranges; + if !within_ranges { + return false; + } - source_ids_match && is_within_start && start_of_ranges <= end_of_ranges + true } diff --git a/crates/evm/coverage/src/lib.rs b/crates/evm/coverage/src/lib.rs index a563764b8..45b89b2d9 100644 --- a/crates/evm/coverage/src/lib.rs +++ b/crates/evm/coverage/src/lib.rs @@ -8,13 +8,14 @@ extern crate tracing; use alloy_primitives::{Bytes, B256}; -use foundry_compilers::sourcemap::SourceElement; +use foundry_compilers::sourcemap::SourceMap; use semver::Version; use std::{ collections::{BTreeMap, HashMap}, fmt::Display, ops::{AddAssign, Deref, DerefMut}, path::PathBuf, + sync::Arc, }; use eyre::{Context, Result}; @@ -42,7 +43,7 @@ pub struct CoverageReport { /// All the bytecode hits for the codebase pub bytecode_hits: HashMap, /// The bytecode -> source mappings - pub source_maps: HashMap, Vec)>, + pub source_maps: HashMap, } impl CoverageReport { @@ -53,27 +54,27 @@ impl CoverageReport { } /// Get the source ID for a specific source file path. - pub fn get_source_id(&self, version: Version, path: PathBuf) -> Option<&usize> { - self.source_paths_to_ids.get(&(version, path)) + pub fn get_source_id(&self, version: Version, path: PathBuf) -> Option { + self.source_paths_to_ids.get(&(version, path)).copied() } /// Add the source maps pub fn add_source_maps( &mut self, - source_maps: HashMap, Vec)>, + source_maps: impl IntoIterator, ) { self.source_maps.extend(source_maps); } /// Add coverage items to this report - pub fn add_items(&mut self, version: Version, items: Vec) { + pub fn add_items(&mut self, version: Version, items: impl IntoIterator) { self.items.entry(version).or_default().extend(items); } /// Add anchors to this report pub fn add_anchors( &mut self, - anchors: HashMap, Vec)>, + anchors: impl IntoIterator, Vec))>, ) { self.anchors.extend(anchors); } @@ -130,18 +131,13 @@ impl CoverageReport { .bytecode_hits .entry(contract_id.clone()) .or_insert_with(|| HitMap::new(hit_map.bytecode.clone())); - e.merge(hit_map).context(format!( - "contract_id {:?}, hash {}, hash {}", - contract_id, - e.bytecode.clone(), - hit_map.bytecode.clone(), - ))?; + e.merge(hit_map).wrap_err_with(|| format!("{contract_id:?}"))?; // Add source level hits if let Some(anchors) = self.anchors.get(contract_id) { let anchors = if is_deployed_code { &anchors.1 } else { &anchors.0 }; for anchor in anchors { - if let Some(hits) = hit_map.hits.get(&anchor.instruction) { + if let Some(&hits) = hit_map.hits.get(&anchor.instruction) { self.items .get_mut(&contract_id.version) .and_then(|items| items.get_mut(anchor.item_id)) @@ -233,7 +229,7 @@ impl HitMap { pub struct ContractId { pub version: Version, pub source_id: usize, - pub contract_name: String, + pub contract_name: Arc, } impl Display for ContractId { @@ -321,7 +317,7 @@ pub struct SourceLocation { /// The source ID. pub source_id: usize, /// The contract this source range is in. - pub contract_name: String, + pub contract_name: Arc, /// Start byte in the source code. pub start: usize, /// Number of bytes in the source code. diff --git a/crates/evm/traces/Cargo.toml b/crates/evm/traces/Cargo.toml index 496a4e998..0d6192aef 100644 --- a/crates/evm/traces/Cargo.toml +++ b/crates/evm/traces/Cargo.toml @@ -19,7 +19,12 @@ foundry-evm-core.workspace = true alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } alloy-json-abi.workspace = true -alloy-primitives = { workspace = true, features = ["serde", "getrandom", "arbitrary", "rlp"] } +alloy-primitives = { workspace = true, features = [ + "serde", + "getrandom", + "arbitrary", + "rlp", +] } alloy-sol-types.workspace = true revm-inspectors.workspace = true @@ -32,6 +37,7 @@ serde = "1" tokio = { workspace = true, features = ["time", "macros"] } tracing = "0.1" yansi.workspace = true +rustc-hash.workspace = true [dev-dependencies] tempfile.workspace = true diff --git a/crates/evm/traces/src/decoder/mod.rs b/crates/evm/traces/src/decoder/mod.rs index cbbec53f7..73bf52bdd 100644 --- a/crates/evm/traces/src/decoder/mod.rs +++ b/crates/evm/traces/src/decoder/mod.rs @@ -20,6 +20,7 @@ use foundry_evm_core::{ }; use itertools::Itertools; use once_cell::sync::OnceCell; +use rustc_hash::FxHashMap; use std::collections::{hash_map::Entry, BTreeMap, HashMap}; mod precompiles; @@ -108,7 +109,7 @@ pub struct CallTraceDecoder { pub receive_contracts: Vec
, /// All known functions. - pub functions: HashMap>, + pub functions: FxHashMap>, /// All known events. pub events: BTreeMap<(B256, usize), Vec>, /// Revert decoder. Contains all known custom errors. diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index f58968bf0..9ca39877d 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -15,7 +15,11 @@ name = "forge" path = "bin/main.rs" [build-dependencies] -vergen = { workspace = true, default-features = false, features = ["build", "git", "gitcl"] } +vergen = { workspace = true, default-features = false, features = [ + "build", + "git", + "gitcl", +] } [dependencies] # lib @@ -34,7 +38,7 @@ revm-inspectors.workspace = true comfy-table = "7" eyre.workspace = true proptest = "1" -rayon = "1" +rayon.workspace = true serde.workspace = true tracing.workspace = true yansi.workspace = true @@ -105,14 +109,16 @@ globset = "0.4" paste = "1.0" path-slash = "0.2" similar-asserts.workspace = true -svm = { package = "svm-rs", version = "0.5", default-features = false, features = ["rustls"] } +svm = { package = "svm-rs", version = "0.5", default-features = false, features = [ + "rustls", +] } tempfile.workspace = true tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] } alloy-signer-wallet.workspace = true [features] -default = ["rustls"] +default = ["rustls", "jemalloc"] rustls = [ "foundry-cli/rustls", "foundry-wallets/rustls", diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index 89765e9da..efdcc0e38 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -4,8 +4,10 @@ use clap::{Parser, ValueEnum, ValueHint}; use eyre::{Context, Result}; use forge::{ coverage::{ - analysis::SourceAnalyzer, anchors::find_anchors, BytecodeReporter, ContractId, - CoverageReport, CoverageReporter, DebugReporter, LcovReporter, SummaryReporter, + analysis::{SourceAnalysis, SourceAnalyzer, SourceFile, SourceFiles}, + anchors::find_anchors, + BytecodeReporter, ContractId, CoverageReport, CoverageReporter, DebugReporter, ItemAnchor, + LcovReporter, SummaryReporter, }, opts::EvmOpts, utils::IcPcMap, @@ -17,19 +19,17 @@ use foundry_cli::{ }; use foundry_common::{compile::ProjectCompiler, fs}; use foundry_compilers::{ - artifacts::{contract::CompactContractBytecode, Ast, CompactBytecode, CompactDeployedBytecode}, + artifacts::{CompactBytecode, CompactDeployedBytecode}, sourcemap::SourceMap, - Artifact, Project, ProjectCompileOutput, + Artifact, ArtifactId, Project, ProjectCompileOutput, }; use foundry_config::{Config, SolcReq}; +use rayon::prelude::*; use rustc_hash::FxHashMap; use semver::Version; use std::{collections::HashMap, path::PathBuf, sync::Arc}; use yansi::Paint; -/// A map, keyed by contract ID, to a tuple of the deployment source map and the runtime source map. -type SourceMaps = HashMap; - // Loads project's figment and merges the build cli arguments into it foundry_config::impl_figment_convert!(CoverageArgs, test); @@ -88,7 +88,7 @@ impl CoverageArgs { let (project, output) = self.build(&config)?; p_println!(!self.test.build_args().silent => "Analysing contracts..."); - let report = self.prepare(&project, output.clone())?; + let report = self.prepare(&project, &output)?; p_println!(!self.test.build_args().silent => "Running tests..."); self.collect(project, output, report, Arc::new(config), evm_opts).await @@ -141,148 +141,80 @@ impl CoverageArgs { /// Builds the coverage report. #[instrument(name = "prepare", skip_all)] - fn prepare(&self, project: &Project, output: ProjectCompileOutput) -> Result { - let project_paths = &project.paths; - - // Extract artifacts - let (artifacts, sources) = output.into_artifacts_with_sources(); + fn prepare(&self, project: &Project, output: &ProjectCompileOutput) -> Result { let mut report = CoverageReport::default(); - // Collect ASTs and sources - let mut versioned_asts: HashMap> = HashMap::new(); - let mut versioned_sources: HashMap> = HashMap::new(); - for (path, mut source_file, version) in sources.into_sources_with_version() { + // Collect source files. + let project_paths = &project.paths; + let mut versioned_sources = HashMap::::new(); + for (path, source_file, version) in output.output().sources.sources_with_version() { report.add_source(version.clone(), source_file.id as usize, path.clone()); // Filter out dependencies - if !self.include_libs && project_paths.has_library_ancestor(std::path::Path::new(&path)) - { - continue + if !self.include_libs && project_paths.has_library_ancestor(path) { + continue; } - if let Some(ast) = source_file.ast.take() { - versioned_asts - .entry(version.clone()) - .or_default() - .insert(source_file.id as usize, ast); - - let file = project_paths.root.join(&path); + if let Some(ast) = &source_file.ast { + let file = project_paths.root.join(path); trace!(root=?project_paths.root, ?file, "reading source file"); - versioned_sources.entry(version.clone()).or_default().insert( - source_file.id as usize, - fs::read_to_string(&file) + let source = SourceFile { + ast, + source: fs::read_to_string(&file) .wrap_err("Could not read source code for analysis")?, - ); + }; + versioned_sources + .entry(version.clone()) + .or_default() + .sources + .insert(source_file.id as usize, source); } } // Get source maps and bytecodes - let (source_maps, bytecodes): (SourceMaps, HashMap) = artifacts - .into_iter() - .map(|(id, artifact)| (id, CompactContractBytecode::from(artifact))) + let artifacts: Vec = output + .artifact_ids() + .par_bridge() .filter_map(|(id, artifact)| { - let contract_id = ContractId { - version: id.version.clone(), - source_id: *report.get_source_id(id.version, id.source)?, - contract_name: id.name, - }; - let source_maps = ( - contract_id.clone(), - ( - artifact.get_source_map()?.ok()?, - artifact - .get_deployed_bytecode() - .as_ref()? - .bytecode - .as_ref()? - .source_map()? - .ok()?, - ), - ); - let bytecodes = ( - contract_id, - ( - artifact - .get_bytecode() - .and_then(|bytecode| dummy_link_bytecode(bytecode.into_owned()))?, - artifact.get_deployed_bytecode().and_then(|bytecode| { - dummy_link_deployed_bytecode(bytecode.into_owned()) - })?, - ), - ); - - Some((source_maps, bytecodes)) - }) - .unzip(); - - // Build IC -> PC mappings - // - // The source maps are indexed by *instruction counters*, which are the indexes of - // instructions in the bytecode *minus any push bytes*. - // - // Since our coverage inspector collects hit data using program counters, the anchors also - // need to be based on program counters. - // TODO: Index by contract ID - let ic_pc_maps: HashMap = bytecodes - .iter() - .map(|(id, bytecodes)| { - // TODO: Creation bytecode as well - ( - id.clone(), - (IcPcMap::new(bytecodes.0.as_ref()), IcPcMap::new(bytecodes.1.as_ref())), - ) + let source_id = report.get_source_id(id.version.clone(), id.source.clone())?; + ArtifactData::new(&id, source_id, artifact) }) .collect(); // Add coverage items - for (version, asts) in versioned_asts.into_iter() { - let source_analysis = SourceAnalyzer::new( - version.clone(), - asts, - versioned_sources.remove(&version).ok_or_else(|| { - eyre::eyre!( - "File tree is missing source code, cannot perform coverage analysis" - ) - })?, - )? - .analyze()?; + for (version, sources) in &versioned_sources { + let source_analysis = SourceAnalyzer::new(sources).analyze()?; // Build helper mapping used by `find_anchors` - let mut items_by_source_id: HashMap<_, Vec<_>> = - HashMap::with_capacity(source_analysis.items.len()); + let mut items_by_source_id = FxHashMap::<_, Vec<_>>::with_capacity_and_hasher( + source_analysis.items.len(), + Default::default(), + ); for (item_id, item) in source_analysis.items.iter().enumerate() { items_by_source_id.entry(item.loc.source_id).or_default().push(item_id); } - let anchors = source_maps - .iter() - .filter(|(contract_id, _)| contract_id.version == version) - .filter_map(|(contract_id, (creation_source_map, deployed_source_map))| { - let creation_code_anchors = find_anchors( - &bytecodes.get(contract_id)?.0, - creation_source_map, - &ic_pc_maps.get(contract_id)?.0, - &source_analysis.items, - &items_by_source_id, - ); - let deployed_code_anchors = find_anchors( - &bytecodes.get(contract_id)?.1, - deployed_source_map, - &ic_pc_maps.get(contract_id)?.1, - &source_analysis.items, - &items_by_source_id, - ); - // TODO: Creation source map/bytecode as well - Some((contract_id.clone(), (creation_code_anchors, deployed_code_anchors))) + let anchors = artifacts + .par_iter() + .filter(|artifact| artifact.contract_id.version == *version) + .map(|artifact| { + let creation_code_anchors = + artifact.creation.find_anchors(&source_analysis, &items_by_source_id); + let deployed_code_anchors = + artifact.deployed.find_anchors(&source_analysis, &items_by_source_id); + (artifact.contract_id.clone(), (creation_code_anchors, deployed_code_anchors)) }) - .collect(); - report.add_items(version, source_analysis.items); + .collect::>(); + report.add_anchors(anchors); + report.add_items(version.clone(), source_analysis.items); } - report.add_source_maps(source_maps); + report.add_source_maps(artifacts.into_iter().map(|artifact| { + (artifact.contract_id, (artifact.creation.source_map, artifact.deployed.source_map)) + })); Ok(report) } @@ -320,39 +252,36 @@ impl CoverageArgs { .await?; // Add hit data to the coverage report - let data = outcome.results.into_iter().flat_map(|(_, suite)| { + let data = outcome.results.iter().flat_map(|(_, suite)| { let mut hits = Vec::new(); - for (_, mut result) in suite.test_results { - let Some(hit_maps) = result.coverage.take() else { continue }; - - for map in hit_maps.0.into_values() { + for result in suite.test_results.values() { + let Some(hit_maps) = result.coverage.as_ref() else { continue }; + for map in hit_maps.0.values() { if let Some((id, _)) = - suite.known_contracts.find_by_deployed_code(map.bytecode.as_ref()) + suite.known_contracts.find_by_deployed_code(&map.bytecode) { - hits.push((id.clone(), map, true)); + hits.push((id, map, true)); } else if let Some((id, _)) = - suite.known_contracts.find_by_creation_code(map.bytecode.as_ref()) + suite.known_contracts.find_by_creation_code(&map.bytecode) { - hits.push((id.clone(), map, false)); + hits.push((id, map, false)); } } } - hits }); - for (artifact_id, hits, is_deployed_code) in data { + for (artifact_id, map, is_deployed_code) in data { if let Some(source_id) = - report.get_source_id(artifact_id.version.clone(), artifact_id.source) + report.get_source_id(artifact_id.version.clone(), artifact_id.source.clone()) { - let source_id = *source_id; report.add_hit_map( &ContractId { version: artifact_id.version.clone(), source_id, - contract_name: artifact_id.name.clone(), + contract_name: artifact_id.name.as_str().into(), }, - &hits, + map, is_deployed_code, )?; } @@ -414,3 +343,67 @@ fn dummy_link_bytecode(mut obj: CompactBytecode) -> Option { fn dummy_link_deployed_bytecode(obj: CompactDeployedBytecode) -> Option { obj.bytecode.and_then(dummy_link_bytecode) } + +pub struct ArtifactData { + pub contract_id: ContractId, + pub creation: BytecodeData, + pub deployed: BytecodeData, +} + +impl ArtifactData { + pub fn new(id: &ArtifactId, source_id: usize, artifact: &impl Artifact) -> Option { + Some(Self { + contract_id: ContractId { + version: id.version.clone(), + source_id, + contract_name: id.name.as_str().into(), + }, + creation: BytecodeData::new( + artifact.get_source_map()?.ok()?, + artifact + .get_bytecode() + .and_then(|bytecode| dummy_link_bytecode(bytecode.into_owned()))?, + ), + deployed: BytecodeData::new( + artifact.get_source_map_deployed()?.ok()?, + artifact + .get_deployed_bytecode() + .and_then(|bytecode| dummy_link_deployed_bytecode(bytecode.into_owned()))?, + ), + }) + } +} + +pub struct BytecodeData { + source_map: SourceMap, + bytecode: Bytes, + /// The instruction counter to program counter mapping. + /// + /// The source maps are indexed by *instruction counters*, which are the indexes of + /// instructions in the bytecode *minus any push bytes*. + /// + /// Since our coverage inspector collects hit data using program counters, the anchors + /// also need to be based on program counters. + ic_pc_map: IcPcMap, +} + +impl BytecodeData { + fn new(source_map: SourceMap, bytecode: Bytes) -> Self { + let ic_pc_map = IcPcMap::new(&bytecode); + Self { source_map, bytecode, ic_pc_map } + } + + pub fn find_anchors( + &self, + source_analysis: &SourceAnalysis, + items_by_source_id: &FxHashMap>, + ) -> Vec { + find_anchors( + &self.bytecode, + &self.source_map, + &self.ic_pc_map, + &source_analysis.items, + items_by_source_id, + ) + } +} diff --git a/crates/forge/src/coverage.rs b/crates/forge/src/coverage.rs index 920c847be..cef6714f3 100644 --- a/crates/forge/src/coverage.rs +++ b/crates/forge/src/coverage.rs @@ -256,7 +256,7 @@ impl CoverageReporter for BytecodeReporter { } } fs::write( - self.destdir.join(contract_id.contract_name.clone()).with_extension("asm"), + self.destdir.join(&*contract_id.contract_name).with_extension("asm"), formatted, )?; } From 8e9fca8d79912f5d9dd9bb40d1ed692e97188b1c Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sat, 1 Jun 2024 11:43:25 +0200 Subject: [PATCH 326/430] perf: exclude source maps from `ContractData` (#8022) perf: exclude source maps from ContractData --- crates/common/src/contracts.rs | 62 +++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 9 deletions(-) diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index e523c3813..6e9de9791 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -23,6 +23,42 @@ use std::{ const CALL_PROTECTION_BYTECODE_PREFIX: [u8; 21] = hex!("730000000000000000000000000000000000000000"); +/// Subset of [CompactBytecode] excluding sourcemaps. +#[allow(missing_docs)] +#[derive(Debug, Clone)] +pub struct BytecodeData { + pub object: Option, + pub link_references: BTreeMap>>, + pub immutable_references: BTreeMap>, +} + +impl BytecodeData { + fn bytes(&self) -> Option<&Bytes> { + self.object.as_ref().and_then(|b| b.as_bytes()) + } +} + +impl From for BytecodeData { + fn from(bytecode: CompactBytecode) -> Self { + Self { + object: Some(bytecode.object), + link_references: bytecode.link_references, + immutable_references: BTreeMap::new(), + } + } +} + +impl From for BytecodeData { + fn from(bytecode: CompactDeployedBytecode) -> Self { + let (object, link_references) = if let Some(compact) = bytecode.bytecode { + (Some(compact.object), compact.link_references) + } else { + (None, BTreeMap::new()) + }; + Self { object, link_references, immutable_references: bytecode.immutable_references } + } +} + /// Container for commonly used contract data. #[derive(Debug, Clone)] pub struct ContractData { @@ -31,9 +67,9 @@ pub struct ContractData { /// Contract ABI. pub abi: JsonAbi, /// Contract creation code. - pub bytecode: Option, + pub bytecode: Option, /// Contract runtime code. - pub deployed_bytecode: Option, + pub deployed_bytecode: Option, } impl ContractData { @@ -68,7 +104,15 @@ impl ContractsByArtifact { let CompactContractBytecode { abi, bytecode, deployed_bytecode } = artifact; - Some((id, ContractData { name, abi: abi?, bytecode, deployed_bytecode })) + Some(( + id, + ContractData { + name, + abi: abi?, + bytecode: bytecode.map(Into::into), + deployed_bytecode: deployed_bytecode.map(Into::into), + }, + )) }) .collect(), ) @@ -103,11 +147,11 @@ impl ContractsByArtifact { let Some(deployed_bytecode) = &contract.deployed_bytecode else { return false; }; - let Some(deployed_code) = &deployed_bytecode.bytecode else { + let Some(deployed_code) = &deployed_bytecode.object else { return false; }; - let len = match deployed_code.object { + let len = match deployed_code { BytecodeObject::Bytecode(ref bytes) => bytes.len(), BytecodeObject::Unlinked(ref bytes) => bytes.len() / 2, }; @@ -120,7 +164,7 @@ impl ContractsByArtifact { let mut ignored = deployed_bytecode .immutable_references .values() - .chain(deployed_code.link_references.values().flat_map(|v| v.values())) + .chain(deployed_bytecode.link_references.values().flat_map(|v| v.values())) .flatten() .cloned() .collect::>(); @@ -129,7 +173,7 @@ impl ContractsByArtifact { // ignore it as it includes library address determined at runtime. // See https://docs.soliditylang.org/en/latest/contracts.html#call-protection-for-libraries and // https://github.com/NomicFoundation/hardhat/blob/af7807cf38842a4f56e7f4b966b806e39631568a/packages/hardhat-verify/src/internal/solc/bytecode.ts#L172 - let has_call_protection = match deployed_code.object { + let has_call_protection = match deployed_code { BytecodeObject::Bytecode(ref bytes) => { bytes.starts_with(&CALL_PROTECTION_BYTECODE_PREFIX) } @@ -154,7 +198,7 @@ impl ContractsByArtifact { for offset in ignored { let right = offset.start as usize; - let matched = match deployed_code.object { + let matched = match deployed_code { BytecodeObject::Bytecode(ref bytes) => bytes[left..right] == code[left..right], BytecodeObject::Unlinked(ref bytes) => { if let Ok(bytes) = Bytes::from_str(&bytes[left * 2..right * 2]) { @@ -173,7 +217,7 @@ impl ContractsByArtifact { } if left < code.len() { - match deployed_code.object { + match deployed_code { BytecodeObject::Bytecode(ref bytes) => bytes[left..] == code[left..], BytecodeObject::Unlinked(ref bytes) => { if let Ok(bytes) = Bytes::from_str(&bytes[left * 2..]) { From 2b95250d7858e660de30a8c195f5fc60007524ce Mon Sep 17 00:00:00 2001 From: sodamntired Date: Sat, 1 Jun 2024 13:13:22 +0300 Subject: [PATCH 327/430] feat: Priority fee suggestion (#7984) * Suggested tip cap * Clippy warnings resolved * Reviews updated * Tests * review updated * Subtle refactorings --- crates/anvil/src/eth/api.rs | 42 ++++++++++++++++++------- crates/anvil/src/eth/backend/mem/mod.rs | 14 ++------- crates/anvil/src/eth/fees.rs | 26 +++------------ crates/anvil/tests/it/transaction.rs | 4 +-- 4 files changed, 41 insertions(+), 45 deletions(-) diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 98c68abf3..25a6b69bb 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -544,12 +544,16 @@ impl EthApi { /// Returns the current gas price fn eth_gas_price(&self) -> Result { node_info!("eth_gasPrice"); - self.gas_price() + Ok(U256::from(self.gas_price())) } /// Returns the current gas price - pub fn gas_price(&self) -> Result { - Ok(U256::from(self.backend.gas_price())) + pub fn gas_price(&self) -> u128 { + if self.backend.is_eip1559() { + self.backend.base_fee().saturating_add(self.lowest_suggestion_tip()) + } else { + self.backend.fees().raw_gas_price() + } } /// Returns the excess blob gas and current blob gas price @@ -562,7 +566,7 @@ impl EthApi { /// /// Handler for ETH RPC call: `eth_maxPriorityFeePerGas` pub fn gas_max_priority_fee_per_gas(&self) -> Result { - Ok(U256::from(self.backend.max_priority_fee_per_gas())) + self.max_priority_fee_per_gas() } /// Returns the base fee per blob required to send a EIP-4844 tx. @@ -1351,7 +1355,24 @@ impl EthApi { /// Handler for ETH RPC call: `eth_maxPriorityFeePerGas` pub fn max_priority_fee_per_gas(&self) -> Result { node_info!("eth_maxPriorityFeePerGas"); - Ok(U256::from(self.backend.max_priority_fee_per_gas())) + Ok(U256::from(self.lowest_suggestion_tip())) + } + + /// Returns the suggested fee cap. + fn lowest_suggestion_tip(&self) -> u128 { + let block_number = self.backend.best_number(); + let latest_cached_block = self.fee_history_cache.lock().get(&block_number).cloned(); + + match latest_cached_block { + Some(block) => block.rewards.iter().copied().min().unwrap_or(1e9 as u128), + None => self + .fee_history_cache + .lock() + .values() + .flat_map(|b| b.rewards.clone()) + .min() + .unwrap_or(1e9 as u128), + } } /// Creates a filter object, based on filter options, to notify when the state changes (logs). @@ -1744,7 +1765,7 @@ impl EthApi { base_fee: self.backend.base_fee(), chain_id: self.backend.chain_id().to::(), gas_limit: self.backend.gas_limit(), - gas_price: self.backend.gas_price(), + gas_price: self.gas_price(), }, fork_config: fork_config .map(|fork| { @@ -2427,7 +2448,7 @@ impl EthApi { m.chain_id = Some(chain_id); m.gas_limit = gas_limit; if gas_price.is_none() { - m.gas_price = self.backend.gas_price() + m.gas_price = self.gas_price(); } TypedTransactionRequest::Legacy(m) } @@ -2436,7 +2457,7 @@ impl EthApi { m.chain_id = chain_id; m.gas_limit = gas_limit; if gas_price.is_none() { - m.gas_price = self.backend.gas_price(); + m.gas_price = self.gas_price(); } TypedTransactionRequest::EIP2930(m) } @@ -2445,7 +2466,7 @@ impl EthApi { m.chain_id = chain_id; m.gas_limit = gas_limit; if max_fee_per_gas.is_none() { - m.max_fee_per_gas = self.backend.gas_price(); + m.max_fee_per_gas = self.gas_price(); } TypedTransactionRequest::EIP1559(m) } @@ -2457,8 +2478,7 @@ impl EthApi { m.tx.chain_id = chain_id; m.tx.gas_limit = gas_limit; if max_fee_per_gas.is_none() { - m.tx.max_fee_per_gas = - self.gas_price().unwrap_or_default().to::(); + m.tx.max_fee_per_gas = self.gas_price(); } if max_fee_per_blob_gas.is_none() { m.tx.max_fee_per_blob_gas = self diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 3668c4e3c..25aa47cef 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -650,16 +650,6 @@ impl Backend { self.fees.set_base_fee(basefee) } - /// Returns the current gas price - pub fn gas_price(&self) -> u128 { - self.fees.gas_price() - } - - /// Returns the suggested fee cap - pub fn max_priority_fee_per_gas(&self) -> u128 { - self.fees.max_priority_fee_per_gas() - } - /// Sets the gas price pub fn set_gas_price(&self, price: u128) { self.fees.set_gas_price(price) @@ -1149,7 +1139,9 @@ impl Backend { env.block.basefee = U256::from(base); } - let gas_price = gas_price.or(max_fee_per_gas).unwrap_or_else(|| self.gas_price()); + let gas_price = gas_price + .or(max_fee_per_gas) + .unwrap_or_else(|| self.fees().raw_gas_price().saturating_add(1e9 as u128)); let caller = from.unwrap_or_default(); let to = to.as_ref().and_then(TxKind::to); env.tx = TxEnv { diff --git a/crates/anvil/src/eth/fees.rs b/crates/anvil/src/eth/fees.rs index f0a614c35..11f84ea5e 100644 --- a/crates/anvil/src/eth/fees.rs +++ b/crates/anvil/src/eth/fees.rs @@ -86,15 +86,6 @@ impl FeeManager { (self.spec_id as u8) >= (SpecId::CANCUN as u8) } - /// Calculates the current gas price - pub fn gas_price(&self) -> u128 { - if self.is_eip1559() { - self.base_fee().saturating_add(self.suggested_priority_fee()) - } else { - *self.gas_price.read() - } - } - /// Calculates the current blob gas price pub fn blob_gas_price(&self) -> u128 { if self.is_eip4844() { @@ -104,11 +95,6 @@ impl FeeManager { } } - /// Suggested priority fee to add to the base fee - pub fn suggested_priority_fee(&self) -> u128 { - 1e9 as u128 - } - pub fn base_fee(&self) -> u128 { if self.is_eip1559() { *self.base_fee.read() @@ -117,6 +103,11 @@ impl FeeManager { } } + /// Raw base gas price + pub fn raw_gas_price(&self) -> u128 { + *self.gas_price.read() + } + pub fn excess_blob_gas_and_price(&self) -> Option { if self.is_eip4844() { Some(self.blob_excess_gas_and_price.read().clone()) @@ -133,13 +124,6 @@ impl FeeManager { } } - /// Returns the suggested fee cap - /// - /// Note: This currently returns a constant value: [Self::suggested_priority_fee] - pub fn max_priority_fee_per_gas(&self) -> u128 { - self.suggested_priority_fee() - } - /// Returns the current gas price pub fn set_gas_price(&self, price: u128) { let mut gas = self.gas_price.write(); diff --git a/crates/anvil/tests/it/transaction.rs b/crates/anvil/tests/it/transaction.rs index cf9845d6f..b8ef827a7 100644 --- a/crates/anvil/tests/it/transaction.rs +++ b/crates/anvil/tests/it/transaction.rs @@ -515,7 +515,7 @@ async fn call_past_state() { let value = contract.getValue().call().await.unwrap(); assert_eq!(value._0, "initial value"); - let gas_price = api.gas_price().unwrap().to::(); + let gas_price = api.gas_price(); let set_tx = contract.setValue("hi".to_string()).gas_price(gas_price + 1); let _receipt = set_tx.send().await.unwrap().get_receipt().await.unwrap(); @@ -1136,7 +1136,7 @@ async fn test_reject_eip1559_pre_london() { let provider = handle.http_provider(); let gas_limit = api.gas_limit().to::(); - let gas_price = api.gas_price().unwrap().to::(); + let gas_price = api.gas_price(); let unsupported_call_builder = Greeter::deploy_builder(provider.clone(), "Hello World!".to_string()); From 489453c7b955441179b805a2e15ac107682220a4 Mon Sep 17 00:00:00 2001 From: Andre Miras Date: Sat, 1 Jun 2024 13:56:18 +0100 Subject: [PATCH 328/430] nit: Minor help style consistency (#8029) Follow up https://github.com/foundry-rs/book/pull/1210 --- crates/anvil/src/cmd.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index 4a4b329da..f36b20013 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -46,21 +46,21 @@ pub struct NodeArgs { pub timestamp: Option, /// BIP39 mnemonic phrase used for generating accounts. - /// Cannot be used if `mnemonic_random` or `mnemonic_seed` are used + /// Cannot be used if `mnemonic_random` or `mnemonic_seed` are used. #[arg(long, short, conflicts_with_all = &["mnemonic_seed", "mnemonic_random"])] pub mnemonic: Option, /// Automatically generates a BIP39 mnemonic phrase, and derives accounts from it. - /// Cannot be used with other `mnemonic` options + /// Cannot be used with other `mnemonic` options. /// You can specify the number of words you want in the mnemonic. /// [default: 12] #[arg(long, conflicts_with_all = &["mnemonic", "mnemonic_seed"], default_missing_value = "12", num_args(0..=1))] pub mnemonic_random: Option, /// Generates a BIP39 mnemonic phrase from a given seed - /// Cannot be used with other `mnemonic` options + /// Cannot be used with other `mnemonic` options. /// - /// CAREFUL: this is NOT SAFE and should only be used for testing. + /// CAREFUL: This is NOT SAFE and should only be used for testing. /// Never use the private keys generated in production. #[arg(long = "mnemonic-seed-unsafe", conflicts_with_all = &["mnemonic", "mnemonic_random"])] pub mnemonic_seed: Option, From 399a42df7ea1f77ad4d4a0ffb2337dc36f5cc8d0 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 1 Jun 2024 18:16:31 +0300 Subject: [PATCH 329/430] chore(deps): bump foundry-compilers to 0.5.1 (#8030) --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- crates/chisel/src/executor.rs | 6 +++--- crates/config/src/lib.rs | 2 +- crates/debugger/src/tui/draw.rs | 8 ++++---- crates/evm/coverage/src/analysis.rs | 4 ++-- crates/evm/coverage/src/anchors.rs | 12 ++++++------ crates/evm/coverage/src/lib.rs | 4 ++-- crates/forge/src/coverage.rs | 8 ++++---- 9 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b77ed1a33..4260c729c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3406,9 +3406,9 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136f5e0af939b12cf072ac6577818a87cb7a408b269070f5d6f52ba23454660e" +checksum = "cd571882cd635dd2a11169d5b696e51644fc803487989e2b5fd7ff6bdd008f1c" dependencies = [ "alloy-json-abi", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index 8d0264d45..d9db1d72f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -138,7 +138,7 @@ foundry-linking = { path = "crates/linking" } # solc & compilation utilities foundry-block-explorers = { version = "0.2.8", default-features = false } -foundry-compilers = { version = "0.5", default-features = false } +foundry-compilers = { version = "0.5.2", default-features = false } ## revm # no default features to avoid c-kzg diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index fc1e7c84a..3ce6a7d19 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -91,15 +91,15 @@ impl SessionSource { // Map the source location of the final statement of the `run()` function to its // corresponding runtime program counter let final_pc = { - let offset = source_loc.start(); - let length = source_loc.end() - source_loc.start(); + let offset = source_loc.start() as u32; + let length = (source_loc.end() - source_loc.start()) as u32; contract .get_source_map_deployed() .unwrap() .unwrap() .into_iter() .zip(InstructionIter::new(&deployed_bytecode)) - .filter(|(s, _)| s.offset == offset && s.length == length) + .filter(|(s, _)| s.offset() == offset && s.length() == length) .map(|(_, i)| i.pc) .max() .unwrap_or_default() diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 81dbff8a4..f2957c6a4 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -810,7 +810,7 @@ impl Config { compiler_config: CompilerConfig, settings: C::Settings, ) -> Result, SolcError> { - let project = ProjectBuilder::::new(Default::default()) + let project = ProjectBuilder::::new(Default::default()) .artifacts(self.configured_artifacts_handler()) .paths(self.project_paths()) .settings(settings) diff --git a/crates/debugger/src/tui/draw.rs b/crates/debugger/src/tui/draw.rs index ad1645d27..9a33e6a05 100644 --- a/crates/debugger/src/tui/draw.rs +++ b/crates/debugger/src/tui/draw.rs @@ -215,8 +215,8 @@ impl DebuggerContext<'_> { // currently being executed. This includes an offset and length. // This vector is in instruction pointer order, meaning the location of the instruction // minus `sum(push_bytes[..pc])`. - let offset = source_element.offset; - let len = source_element.length; + let offset = source_element.offset() as usize; + let len = source_element.length() as usize; let max = source_code.len(); // Split source into before, relevant, and after chunks, split by line, for formatting. @@ -357,7 +357,7 @@ impl DebuggerContext<'_> { let source_element = source_map.get(ic)?; // if the source element has an index, find the sourcemap for that index source_element - .index + .index() // if index matches current file_id, return current source code .and_then(|index| { (index == file_id).then(|| (source_element.clone(), source_code)) @@ -367,7 +367,7 @@ impl DebuggerContext<'_> { self.debugger .contracts_sources .sources_by_id - .get(&(source_element.index?)) + .get(&source_element.index()?) .map(|source_code| (source_element.clone(), source_code.as_ref())) }) }) diff --git a/crates/evm/coverage/src/analysis.rs b/crates/evm/coverage/src/analysis.rs index cfcb7406f..bbfaa1897 100644 --- a/crates/evm/coverage/src/analysis.rs +++ b/crates/evm/coverage/src/analysis.rs @@ -412,8 +412,8 @@ impl<'a> ContractVisitor<'a> { SourceLocation { source_id: self.source_id, contract_name: self.contract_name.clone(), - start: loc.start, - length: loc.length, + start: loc.start as u32, + length: loc.length.map(|x| x as u32), line: self.source[..loc.start].lines().count(), } } diff --git a/crates/evm/coverage/src/anchors.rs b/crates/evm/coverage/src/anchors.rs index f05f7c287..f717e24ab 100644 --- a/crates/evm/coverage/src/anchors.rs +++ b/crates/evm/coverage/src/anchors.rs @@ -16,7 +16,7 @@ pub fn find_anchors( let mut seen = FxHashSet::default(); source_map .iter() - .filter_map(|element| items_by_source_id.get(&(element.index? as usize))) + .filter_map(|element| items_by_source_id.get(&(element.index()? as usize))) .flatten() .filter_map(|&item_id| { if !seen.insert(item_id) { @@ -164,20 +164,20 @@ 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().map_or(false, |a| a as usize == location.source_id); if !source_ids_match { return false; } // Needed because some source ranges in the source map mark the entire contract... - let is_within_start = element.offset >= location.start; + let is_within_start = element.offset() >= location.start; if !is_within_start { return false; } - let start_of_ranges = location.start.max(element.offset); - let end_of_ranges = - (location.start + location.length.unwrap_or_default()).min(element.offset + element.length); + let start_of_ranges = location.start.max(element.offset()); + let end_of_ranges = (location.start + location.length.unwrap_or_default()) + .min(element.offset() + element.length()); let within_ranges = start_of_ranges <= end_of_ranges; if !within_ranges { return false; diff --git a/crates/evm/coverage/src/lib.rs b/crates/evm/coverage/src/lib.rs index 45b89b2d9..53ca928f6 100644 --- a/crates/evm/coverage/src/lib.rs +++ b/crates/evm/coverage/src/lib.rs @@ -319,9 +319,9 @@ pub struct SourceLocation { /// The contract this source range is in. pub contract_name: Arc, /// Start byte in the source code. - pub start: usize, + pub start: u32, /// Number of bytes in the source code. - pub length: Option, + pub length: Option, /// The line in the source code. pub line: usize, } diff --git a/crates/forge/src/coverage.rs b/crates/forge/src/coverage.rs index cef6714f3..29920d735 100644 --- a/crates/forge/src/coverage.rs +++ b/crates/forge/src/coverage.rs @@ -220,14 +220,14 @@ impl CoverageReporter for BytecodeReporter { .get(&(code.offset as usize)) .map(|h| format!("[{:03}]", h)) .unwrap_or(" ".to_owned()); - let source_id = source_element.index; + let source_id = source_element.index(); let source_path = source_id.and_then(|i| { report.source_paths.get(&(contract_id.version.clone(), i as usize)) }); - let code = format!("{:?}", code); - let start = source_element.offset; - let end = source_element.offset + source_element.length; + let code = format!("{code:?}"); + let start = source_element.offset() as usize; + let end = (source_element.offset() + source_element.length()) as usize; if let Some(source_path) = source_path { let (sline, spos) = line_number_cache.get_position(source_path, start)?; From 5ac78a9cd4b94dc53d1fe5e0f42372b28b5a7559 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 1 Jun 2024 18:37:02 +0300 Subject: [PATCH 330/430] chore: Arc ContractsByArtifact internally (#8026) * chore: Arc ContractsByArtifact internally * perf: clear from test suite result * chore: clear only when not coverage --- crates/cheatcodes/src/config.rs | 5 ++- crates/common/src/contracts.rs | 58 +++++++++++++----------------- crates/forge/bin/cmd/test/mod.rs | 15 ++++---- crates/forge/src/multi_runner.rs | 2 +- crates/forge/src/result.rs | 17 ++++++--- crates/forge/src/runner.rs | 3 +- crates/forge/tests/cli/coverage.rs | 12 +++---- crates/script/src/build.rs | 3 +- crates/script/src/execute.rs | 4 +-- crates/script/src/lib.rs | 4 +-- 10 files changed, 58 insertions(+), 65 deletions(-) diff --git a/crates/cheatcodes/src/config.rs b/crates/cheatcodes/src/config.rs index 66aef3cff..54ba1dad1 100644 --- a/crates/cheatcodes/src/config.rs +++ b/crates/cheatcodes/src/config.rs @@ -12,7 +12,6 @@ use semver::Version; use std::{ collections::HashMap, path::{Path, PathBuf}, - sync::Arc, time::Duration, }; @@ -50,7 +49,7 @@ pub struct CheatsConfig { /// Artifacts which are guaranteed to be fresh (either recompiled or cached). /// If Some, `vm.getDeployedCode` invocations are validated to be in scope of this list. /// If None, no validation is performed. - pub available_artifacts: Option>, + pub available_artifacts: Option, /// Version of the script/test contract which is currently running. pub running_version: Option, } @@ -60,7 +59,7 @@ impl CheatsConfig { pub fn new( config: &Config, evm_opts: EvmOpts, - available_artifacts: Option>, + available_artifacts: Option, script_wallets: Option, running_version: Option, ) -> Self { diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index 6e9de9791..3e6b2ce10 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -10,11 +10,7 @@ use foundry_compilers::{ }, ArtifactId, }; -use std::{ - collections::BTreeMap, - ops::{Deref, DerefMut}, - str::FromStr, -}; +use std::{collections::BTreeMap, ops::Deref, str::FromStr, sync::Arc}; /// Libraries' runtime code always starts with the following instruction: /// `PUSH20 0x0000000000000000000000000000000000000000` @@ -60,7 +56,7 @@ impl From for BytecodeData { } /// Container for commonly used contract data. -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct ContractData { /// Contract name. pub name: String, @@ -88,7 +84,7 @@ type ArtifactWithContractRef<'a> = (&'a ArtifactId, &'a ContractData); /// Wrapper type that maps an artifact to a contract ABI and bytecode. #[derive(Clone, Default, Debug)] -pub struct ContractsByArtifact(pub BTreeMap); +pub struct ContractsByArtifact(Arc>); impl ContractsByArtifact { /// Creates a new instance by collecting all artifacts with present bytecode from an iterator. @@ -96,26 +92,28 @@ impl ContractsByArtifact { /// It is recommended to use this method with an output of /// [foundry_linking::Linker::get_linked_artifacts]. pub fn new(artifacts: impl IntoIterator) -> Self { - Self( - artifacts - .into_iter() - .filter_map(|(id, artifact)| { - let name = id.name.clone(); - - let CompactContractBytecode { abi, bytecode, deployed_bytecode } = artifact; - - Some(( - id, - ContractData { - name, - abi: abi?, - bytecode: bytecode.map(Into::into), - deployed_bytecode: deployed_bytecode.map(Into::into), - }, - )) - }) - .collect(), - ) + let map = artifacts + .into_iter() + .filter_map(|(id, artifact)| { + let name = id.name.clone(); + let CompactContractBytecode { abi, bytecode, deployed_bytecode } = artifact; + Some(( + id, + ContractData { + name, + abi: abi?, + bytecode: bytecode.map(Into::into), + deployed_bytecode: deployed_bytecode.map(Into::into), + }, + )) + }) + .collect(); + Self(Arc::new(map)) + } + + /// Clears all contracts. + pub fn clear(&mut self) { + *self = Self::default(); } /// Finds a contract which has a similar bytecode as `code`. @@ -276,12 +274,6 @@ impl Deref for ContractsByArtifact { } } -impl DerefMut for ContractsByArtifact { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - /// Wrapper type that maps an address to a contract identifier and contract ABI. pub type ContractsByAddress = BTreeMap; diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 152d21db5..8f1df029c 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -392,12 +392,12 @@ impl TestArgs { let mut outcome = TestOutcome::empty(self.allow_failure); let mut any_test_failed = false; - for (contract_name, suite_result) in rx { + for (contract_name, mut suite_result) in rx { let tests = &suite_result.test_results; // Set up trace identifiers. - let known_contracts = suite_result.known_contracts.clone(); - let mut identifier = TraceIdentifiers::new().with_local(&known_contracts); + let known_contracts = &suite_result.known_contracts; + let mut identifier = TraceIdentifiers::new().with_local(known_contracts); // Avoid using etherscan for gas report as we decode more traces and this will be // expensive. @@ -407,7 +407,7 @@ impl TestArgs { // Build the trace decoder. let mut builder = CallTraceDecoderBuilder::new() - .with_known_contracts(&known_contracts) + .with_known_contracts(known_contracts) .with_verbosity(verbosity); // Signatures are of no value for gas reports. if !self.gas_report { @@ -453,10 +453,6 @@ impl TestArgs { // processing the remaining tests and print the suite summary. any_test_failed |= result.status == TestStatus::Failure; - if result.traces.is_empty() { - continue; - } - // Clear the addresses and labels from previous runs. decoder.clear_addresses(); decoder @@ -524,6 +520,9 @@ impl TestArgs { // Print suite summary. shell::println(suite_result.summary())?; + // Free memory if it's not needed. + suite_result.clear_unneeded(); + // Add the suite result to the outcome. outcome.results.insert(contract_name, suite_result); outcome.last_run_decoder = Some(decoder); diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 3653a0e3f..d910215e9 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -186,7 +186,7 @@ impl MultiContractRunner { self.output.artifact_ids().collect(), ); let linked_contracts = linker.get_linked_artifacts(&contract.libraries).unwrap_or_default(); - let known_contracts = Arc::new(ContractsByArtifact::new(linked_contracts)); + let known_contracts = ContractsByArtifact::new(linked_contracts); let cheats_config = CheatsConfig::new( &self.config, diff --git a/crates/forge/src/result.rs b/crates/forge/src/result.rs index 2dd5508e8..ab289db54 100644 --- a/crates/forge/src/result.rs +++ b/crates/forge/src/result.rs @@ -1,5 +1,6 @@ //! Test outcomes. +use crate::gas_report::GasReport; use alloy_primitives::{Address, Log}; use foundry_common::{ evm::Breakpoints, get_contract_name, get_file_name, shell, ContractsByArtifact, @@ -16,13 +17,10 @@ use serde::{Deserialize, Serialize}; use std::{ collections::{BTreeMap, HashMap}, fmt::{self, Write}, - sync::Arc, time::Duration, }; use yansi::Paint; -use crate::gas_report::GasReport; - /// The aggregated result of a test run. #[derive(Clone, Debug)] pub struct TestOutcome { @@ -201,8 +199,10 @@ pub struct SuiteResult { /// Libraries used to link test contract. pub libraries: Libraries, /// Contracts linked with correct libraries. + /// + /// This is cleared at the end of the test run if coverage is not enabled. #[serde(skip)] - pub known_contracts: Arc, + pub known_contracts: ContractsByArtifact, } impl SuiteResult { @@ -211,11 +211,18 @@ impl SuiteResult { test_results: BTreeMap, warnings: Vec, libraries: Libraries, - known_contracts: Arc, + known_contracts: ContractsByArtifact, ) -> Self { Self { duration, test_results, warnings, libraries, known_contracts } } + /// Frees memory that is not used for the final output. + pub fn clear_unneeded(&mut self) { + if !self.test_results.values().any(|r| r.coverage.is_some()) { + ContractsByArtifact::clear(&mut self.known_contracts); + } + } + /// Returns an iterator over all individual succeeding tests and their names. pub fn successes(&self) -> impl Iterator { self.tests().filter(|(_, t)| t.status == TestStatus::Success) diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 33a2ce64a..ea6b6efe7 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -40,7 +40,6 @@ use std::{ borrow::Cow, cmp::min, collections::{BTreeMap, HashMap}, - sync::Arc, time::Instant, }; @@ -258,7 +257,7 @@ impl<'a> ContractRunner<'a> { mut self, filter: &dyn TestFilter, test_options: &TestOptions, - known_contracts: Arc, + known_contracts: ContractsByArtifact, handle: &tokio::runtime::Handle, ) -> SuiteResult { info!("starting tests"); diff --git a/crates/forge/tests/cli/coverage.rs b/crates/forge/tests/cli/coverage.rs index df44a1cf6..546a11038 100644 --- a/crates/forge/tests/cli/coverage.rs +++ b/crates/forge/tests/cli/coverage.rs @@ -70,11 +70,9 @@ contract AContractTest is DSTest { let lcov_data = std::fs::read_to_string(lcov_info).unwrap(); // AContract.init must be hit at least once let re = Regex::new(r"FNDA:(\d+),AContract\.init").unwrap(); - assert!(lcov_data.lines().any(|line| re.captures(line).map_or(false, |caps| caps - .get(1) - .unwrap() - .as_str() - .parse::() - .unwrap() > - 0))); + let valid_line = |line| { + re.captures(line) + .map_or(false, |caps| caps.get(1).unwrap().as_str().parse::().unwrap() > 0) + }; + assert!(lcov_data.lines().any(valid_line), "{lcov_data}"); }); diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index 3bf9e154b..a2462e4b2 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -146,10 +146,9 @@ impl LinkedBuildData { } /// Fetches target bytecode from linked contracts. - pub fn get_target_contract(&self) -> Result { + pub fn get_target_contract(&self) -> Result<&ContractData> { self.known_contracts .get(&self.build_data.target) - .cloned() .ok_or_eyre("target not found in linked artifacts") } } diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index afba19f22..2b57468e2 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -73,13 +73,13 @@ impl LinkedState { args, script_config, script_wallets, - build_data, execution_data: ExecutionData { func, calldata, bytecode: bytecode.clone(), - abi: target_contract.abi, + abi: target_contract.abi.clone(), }, + build_data, }) } } diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 88ab0782a..39059ed21 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -44,7 +44,7 @@ use foundry_evm::{ }; use foundry_wallets::MultiWalletOpts; use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, sync::Arc}; +use std::collections::HashMap; use yansi::Paint; mod broadcast; @@ -591,7 +591,7 @@ impl ScriptConfig { CheatsConfig::new( &self.config, self.evm_opts.clone(), - Some(Arc::new(known_contracts)), + Some(known_contracts), Some(script_wallets), Some(target.version), ) From 741377fc391cc10fff5fb15f8e23213046db49d8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 2 Jun 2024 03:38:30 +0200 Subject: [PATCH 331/430] chore(deps): weekly `cargo update` (#8033) Updating git repository `https://github.com/alloy-rs/alloy` Updating git repository `https://github.com/paradigmxyz/revm-inspectors` Locking 43 packages to latest compatible versions Updating alloy-rlp v0.3.4 -> v0.3.5 Updating alloy-rlp-derive v0.3.4 -> v0.3.5 Updating aws-config v1.4.0 -> v1.5.0 Updating aws-sdk-kms v1.26.0 -> v1.29.0 Updating aws-sdk-sts v1.25.0 -> v1.28.0 Updating aws-smithy-runtime v1.5.0 -> v1.5.4 Updating aws-smithy-runtime-api v1.6.0 -> v1.6.1 Updating aws-smithy-types v1.1.9 -> v1.1.10 Updating aws-types v1.2.1 -> v1.3.0 Updating blst v0.3.11 -> v0.3.12 Updating cc v1.0.97 -> v1.0.98 Updating const-hex v1.11.4 -> v1.12.0 Updating crc32fast v1.4.0 -> v1.4.2 Updating crossbeam-channel v0.5.12 -> v0.5.13 Updating crossbeam-utils v0.8.19 -> v0.8.20 Adding doctest-file v1.0.0 Updating ethereum_ssz v0.5.3 -> v0.5.4 Updating hyper-util v0.1.3 -> v0.1.5 Removing indoc v2.0.5 Updating interprocess v2.1.0 -> v2.1.1 Adding lockfree-object-pool v0.1.6 Updating native-tls v0.2.11 -> v0.2.12 Updating parking_lot v0.12.2 -> v0.12.3 Updating plotters v0.3.5 -> v0.3.6 Updating plotters-backend v0.3.5 -> v0.3.6 Updating plotters-svg v0.3.5 -> v0.3.6 Updating proc-macro2 v1.0.82 -> v1.0.84 Updating ratatui v0.26.2 -> v0.26.3 Updating schemars v0.8.20 -> v0.8.21 Updating schemars_derive v0.8.20 -> v0.8.21 Updating serde v1.0.202 -> v1.0.203 Updating serde_derive v1.0.202 -> v1.0.203 Adding simd-adler32 v0.3.7 Updating strum_macros v0.26.2 -> v0.26.3 Updating svm-rs v0.5.3 -> v0.5.4 Updating svm-rs-builds v0.5.3 -> v0.5.4 Updating syn v2.0.64 -> v2.0.66 Updating tokio v1.37.0 -> v1.38.0 Updating tokio-macros v2.2.0 -> v2.3.0 Adding unicode-truncate v1.0.0 Updating winnow v0.6.8 -> v0.6.9 Updating zeroize v1.7.0 -> v1.8.1 Updating zip v1.3.0 -> v2.1.1 Adding zopfli v0.8.1 note: pass `--verbose` to see 137 unchanged dependencies behind latest Co-authored-by: mattsse --- Cargo.lock | 308 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 173 insertions(+), 135 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4260c729c..0e7bd7cf0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -126,7 +126,7 @@ dependencies = [ "proptest", "serde", "serde_json", - "winnow 0.6.8", + "winnow 0.6.9", ] [[package]] @@ -278,9 +278,9 @@ dependencies = [ [[package]] name = "alloy-rlp" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d58d9f5da7b40e9bfff0b7e7816700be4019db97d4b6359fe7f94a9e22e42ac" +checksum = "b155716bab55763c95ba212806cf43d05bcc70e5f35b02bad20cf5ec7fe11fed" dependencies = [ "alloy-rlp-derive", "arrayvec", @@ -289,13 +289,13 @@ dependencies = [ [[package]] name = "alloy-rlp-derive" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a047897373be4bbb0224c1afdabca92648dc57a9c9ef6e7b0be3aff7a859c83" +checksum = "8037e03c7f462a063f28daec9fda285a9a89da003c552f8637a80b9c8fd96241" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -476,7 +476,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -493,7 +493,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", "syn-solidity", "tiny-keccak", ] @@ -511,7 +511,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.64", + "syn 2.0.66", "syn-solidity", ] @@ -521,7 +521,7 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "368cae4dc052cad1d8f72eb2ae0c38027116933eeb49213c200a9e9875f208d7" dependencies = [ - "winnow 0.6.8", + "winnow 0.6.9", ] [[package]] @@ -1009,7 +1009,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -1031,7 +1031,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -1042,7 +1042,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -1089,7 +1089,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -1100,9 +1100,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "aws-config" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ddbfb5db93d62521f47b3f223da0884a2f02741ff54cb9cda192a0e73ba08b" +checksum = "1234b742ac4a40a7d3459c6e3c99818271976a5a6ae3732cb415f4a9a94da7b6" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1161,9 +1161,9 @@ dependencies = [ [[package]] name = "aws-sdk-kms" -version = "1.26.0" +version = "1.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a6b58615203957a253a180b81db9427cb4b623fc8bab8dcac42369239f7716" +checksum = "53d5f3e0faac32aa4edbcdcc10330e7b6b1fdbb2e83b44439f5696281e18d278" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1183,9 +1183,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.25.0" +version = "1.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e2b4a632a59e4fab7abf1db0d94a3136ad7871aba46bebd1fdb95c7054afcdb" +checksum = "a422d2f3080421ed23630ada0e474c76e4279c18b4a379bff2f1062e05cef466" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1279,9 +1279,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.5.0" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9ac79e9f3a4d576f3cd4a470a0275b138d9e7b11b1cd514a6858ae0a79dd5bb" +checksum = "607e8b53aeb2bc23fb332159d72a69650cd9643c161d76cd3b7f88ac00b5a1bb" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -1305,9 +1305,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime-api" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04ec42c2f5c0e7796a2848dde4d9f3bf8ce12ccbb3d5aa40c52fa0cdd61a1c47" +checksum = "5b7d790d553d163c7d80a4e06e2906bf24b9172c9ebe045fc3a274e9358ab7bb" dependencies = [ "aws-smithy-async", "aws-smithy-types", @@ -1322,9 +1322,9 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf98d97bba6ddaba180f1b1147e202d8fe04940403a95a3f826c790f931bbd1" +checksum = "5b6764ba7e1c5ede1c9f9e4046645534f06c2581402461c559b481a420330a83" dependencies = [ "base64-simd", "bytes", @@ -1354,9 +1354,9 @@ dependencies = [ [[package]] name = "aws-types" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a807d90cd50a969b3d95e4e7ad1491fcae13c6e83948d8728363ecc09d66343a" +checksum = "02fa328e19c849b20ef7ada4c9b581dd12351ff35ecc7642d06e69de4f98407c" dependencies = [ "aws-credential-types", "aws-smithy-async", @@ -1541,9 +1541,9 @@ dependencies = [ [[package]] name = "blst" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c94087b935a822949d3291a9989ad2b2051ea141eda0fd4e478a75f6aa3e604b" +checksum = "62dc83a094a71d43eeadd254b1ec2d24cb6a0bb6cadce00df51f0db594711a32" dependencies = [ "cc", "glob", @@ -1768,9 +1768,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" +checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" [[package]] name = "cfg-if" @@ -1925,7 +1925,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -2118,9 +2118,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.11.4" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ff96486ccc291d36a958107caf2c0af8c78c0af7d31ae2f35ce055130de1a6" +checksum = "94fb8a24a26d37e1ffd45343323dc9fe6654ceea44c12f2fcb3d7ac29e610bc6" dependencies = [ "cfg-if", "cpufeatures", @@ -2168,9 +2168,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] @@ -2215,9 +2215,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ "crossbeam-utils", ] @@ -2243,9 +2243,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crossterm" @@ -2340,7 +2340,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -2351,7 +2351,7 @@ checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ "darling_core", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -2423,7 +2423,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -2444,7 +2444,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -2454,7 +2454,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" dependencies = [ "derive_builder_core", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -2574,9 +2574,15 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] +[[package]] +name = "doctest-file" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac81fa3e28d21450aa4d2ac065992ba96a1d7303efbce51a95f4fd175b67562" + [[package]] name = "dotenvy" version = "0.15.7" @@ -2682,7 +2688,7 @@ checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -2806,9 +2812,9 @@ dependencies = [ [[package]] name = "ethereum_ssz" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e61ffea29f26e8249d35128a82ec8d3bd4fbc80179ea5f5e5e3daafef6a80fcb" +checksum = "7d3627f83d8b87b432a5fad9934b4565260722a141a2c40f371f8080adec9425" dependencies = [ "ethereum-types", "itertools 0.10.5", @@ -2832,7 +2838,7 @@ dependencies = [ "regex", "serde", "serde_json", - "syn 2.0.64", + "syn 2.0.66", "toml 0.8.13", "walkdir", ] @@ -2860,7 +2866,7 @@ dependencies = [ "serde", "serde_json", "strum", - "syn 2.0.64", + "syn 2.0.66", "tempfile", "thiserror", "tiny-keccak", @@ -3439,7 +3445,7 @@ dependencies = [ "tokio", "tracing", "walkdir", - "winnow 0.6.8", + "winnow 0.6.9", "yansi", ] @@ -3642,7 +3648,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -3801,7 +3807,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -4246,7 +4252,7 @@ dependencies = [ "markup5ever", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -4434,9 +4440,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +checksum = "7b875924a60b96e5d7b9ae7b066540b1dd1cbd90d1828f54c92e02a283351c56" dependencies = [ "bytes", "futures-channel", @@ -4592,12 +4598,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "indoc" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" - [[package]] name = "inlinable_string" version = "0.1.15" @@ -4644,10 +4644,11 @@ dependencies = [ [[package]] name = "interprocess" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4d0250d41da118226e55b3d50ca3f0d9e0a0f6829b92f543ac0054aeea1572" +checksum = "13f2533e1f1a70bec71ea7a85d1c0a4dab141c314035ce76e51a19a2f48be708" dependencies = [ + "doctest-file", "futures-core", "libc", "recvmsg", @@ -4908,6 +4909,12 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "lockfree-object-pool" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" + [[package]] name = "log" version = "0.4.21" @@ -5055,7 +5062,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -5125,16 +5132,15 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -5357,7 +5363,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -5480,7 +5486,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -5565,9 +5571,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -5637,7 +5643,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -5696,7 +5702,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -5780,7 +5786,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -5818,7 +5824,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -5851,9 +5857,9 @@ checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "plotters" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" +checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" dependencies = [ "num-traits", "plotters-backend", @@ -5864,15 +5870,15 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" +checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" [[package]] name = "plotters-svg" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" +checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" dependencies = [ "plotters-backend", ] @@ -5934,7 +5940,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -5995,9 +6001,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.82" +version = "1.0.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" dependencies = [ "unicode-ident", ] @@ -6010,7 +6016,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", "version_check", "yansi", ] @@ -6167,21 +6173,21 @@ dependencies = [ [[package]] name = "ratatui" -version = "0.26.2" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a564a852040e82671dc50a37d88f3aa83bbc690dfc6844cfe7a2591620206a80" +checksum = "f44c9e68fd46eda15c646fbb85e1040b657a58cdc8c98db1d97a55930d991eef" dependencies = [ "bitflags 2.5.0", "cassowary", "compact_str", "crossterm", - "indoc", "itertools 0.12.1", "lru", "paste", "stability", "strum", "unicode-segmentation", + "unicode-truncate", "unicode-width", ] @@ -6789,9 +6795,9 @@ dependencies = [ [[package]] name = "schemars" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0218ceea14babe24a4a5836f86ade86c1effbc198164e619194cb5069187e29" +checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" dependencies = [ "dyn-clone", "schemars_derive", @@ -6801,14 +6807,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed5a1ccce8ff962e31a165d41f6e2a2dd1245099dc4d594f5574a86cd90f4d3" +checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -6936,22 +6942,22 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.202" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.202" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -6962,7 +6968,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -7005,7 +7011,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -7051,7 +7057,7 @@ checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -7163,6 +7169,12 @@ dependencies = [ "rand_core", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "similar" version = "2.5.0" @@ -7269,7 +7281,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ff9eaf853dec4c8802325d8b6d3dffa86cc707fd7a1a4cdbf416e13b061787a" dependencies = [ "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -7333,15 +7345,15 @@ dependencies = [ [[package]] name = "strum_macros" -version = "0.26.2" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +checksum = "f7993a8e3a9e88a00351486baae9522c91b123a088f76469e5bd5cc17198ea87" dependencies = [ - "heck 0.4.1", + "heck 0.5.0", "proc-macro2", "quote", "rustversion", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -7365,9 +7377,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "svm-rs" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b19990faaaed6298d6b21984544ad941dc88d507330012fb4a6a8d4f1833de73" +checksum = "af5910befd515534a92e9424f250d952fe6f6dba6a92bd001dfeba1fb4a2f87c" dependencies = [ "const-hex", "dirs 5.0.1", @@ -7385,9 +7397,9 @@ dependencies = [ [[package]] name = "svm-rs-builds" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68be542b0720275ac352f85ab67713ae22d56bcc58d83bdebdd3eda0473a1d7f" +checksum = "3d5ea000fdbeab0b2739315f9093c75ea63030e5c44f92daa72401d11b48adda" dependencies = [ "build_const", "const-hex", @@ -7409,9 +7421,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.64" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ad3dee41f36859875573074334c200d1add8e4a87bb37113ebd31d926b7b11f" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", @@ -7427,7 +7439,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -7528,7 +7540,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -7639,9 +7651,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.37.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ "backtrace", "bytes", @@ -7658,13 +7670,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -7802,7 +7814,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.8", + "winnow 0.6.9", ] [[package]] @@ -7884,7 +7896,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -8077,6 +8089,16 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +[[package]] +name = "unicode-truncate" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5fbabedabe362c618c714dbefda9927b5afc8e2a8102f47f081089a9019226" +dependencies = [ + "itertools 0.12.1", + "unicode-width", +] + [[package]] name = "unicode-width" version = "0.1.12" @@ -8231,7 +8253,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", "wasm-bindgen-shared", ] @@ -8265,7 +8287,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -8632,9 +8654,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" +checksum = "86c949fede1d13936a99f14fafd3e76fd642b556dd2ce96287fbe2e0151bfac6" dependencies = [ "memchr", ] @@ -8709,14 +8731,14 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "zeroize_derive", ] @@ -8729,14 +8751,14 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] name = "zip" -version = "1.3.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f4a27345eb6f7aa7bd015ba7eb4175fa4e1b462a29874b779e0bbcf96c6ac7" +checksum = "1dd56a4d5921bc2f99947ac5b3abe5f510b1be7376fdc5e9fce4a23c6a93e87c" dependencies = [ "arbitrary", "crc32fast", @@ -8744,5 +8766,21 @@ dependencies = [ "displaydoc", "flate2", "indexmap", + "memchr", "thiserror", + "zopfli", +] + +[[package]] +name = "zopfli" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946" +dependencies = [ + "bumpalo", + "crc32fast", + "lockfree-object-pool", + "log", + "once_cell", + "simd-adler32", ] From fd4236868e02b41063a063c2f31da127c38a8cdb Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sun, 2 Jun 2024 17:23:56 +0300 Subject: [PATCH 332/430] fix: enable providers in ethers-contract-abigen (#8032) --- crates/forge/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 9ca39877d..940acb087 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -31,7 +31,7 @@ foundry-evm.workspace = true foundry-wallets.workspace = true foundry-linking.workspace = true -ethers-contract-abigen.workspace = true +ethers-contract-abigen = { workspace = true, features = ["providers"] } revm-inspectors.workspace = true From 0fc39763d46b8ffab5fa4eaeb2f65ae078fa07de Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sun, 2 Jun 2024 19:55:54 +0200 Subject: [PATCH 333/430] feat: better run feedback for scripts (#8023) * feat: better feedback for scripts * fix tests * review fixes --- crates/cast/bin/cmd/run.rs | 10 +- crates/cli/src/utils/cmd.rs | 37 ++--- crates/script/src/broadcast.rs | 47 +++---- crates/script/src/lib.rs | 1 + crates/script/src/progress.rs | 245 +++++++++++++++++++++++++++++++++ crates/script/src/receipts.rs | 114 +-------------- 6 files changed, 294 insertions(+), 160 deletions(-) create mode 100644 crates/script/src/progress.rs diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index 32bd4fea6..0f3ca609d 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -5,10 +5,8 @@ use cast::{revm::primitives::EnvWithHandlerCfg, traces::TraceKind}; use clap::Parser; use eyre::{Result, WrapErr}; use foundry_cli::{ - init_progress, opts::RpcOpts, - update_progress, - utils::{handle_traces, TraceResult}, + utils::{handle_traces, init_progress, TraceResult}, }; use foundry_common::{is_known_system_sender, SYSTEM_TRANSACTION_TYPE}; use foundry_compilers::EvmVersion; @@ -153,7 +151,7 @@ impl RunArgs { println!("Executing previous transactions from the block."); if let Some(block) = block { - let pb = init_progress!(block.transactions, "tx"); + let pb = init_progress(block.transactions.len() as u64, "tx"); pb.set_position(0); let BlockTransactions::Full(txs) = block.transactions else { @@ -167,7 +165,7 @@ impl RunArgs { if is_known_system_sender(tx.from) || tx.transaction_type == Some(SYSTEM_TRANSACTION_TYPE) { - update_progress!(pb, index); + pb.set_position((index + 1) as u64); continue; } if tx.hash == tx_hash { @@ -202,7 +200,7 @@ impl RunArgs { } } - update_progress!(pb, index); + pb.set_position((index + 1) as u64); } } } diff --git a/crates/cli/src/utils/cmd.rs b/crates/cli/src/utils/cmd.rs index 9e8f99241..b9020c045 100644 --- a/crates/cli/src/utils/cmd.rs +++ b/crates/cli/src/utils/cmd.rs @@ -141,29 +141,20 @@ pub fn eta_key(state: &indicatif::ProgressState, f: &mut dyn Write) { write!(f, "{:.1}s", state.eta().as_secs_f64()).unwrap() } -#[macro_export] -macro_rules! init_progress { - ($local:expr, $label:expr) => {{ - let pb = indicatif::ProgressBar::new($local.len() as u64); - let mut template = - "{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {pos}/{len} ".to_string(); - template += $label; - template += " ({eta})"; - pb.set_style( - indicatif::ProgressStyle::with_template(&template) - .unwrap() - .with_key("eta", $crate::utils::eta_key) - .progress_chars("#>-"), - ); - pb - }}; -} - -#[macro_export] -macro_rules! update_progress { - ($pb:ident, $index:expr) => { - $pb.set_position(($index + 1) as u64); - }; +pub fn init_progress(len: u64, label: &str) -> indicatif::ProgressBar { + let pb = indicatif::ProgressBar::new(len); + let mut template = + "{prefix}{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {pos}/{len} " + .to_string(); + write!(template, "{}", label).unwrap(); + template += " ({eta})"; + pb.set_style( + indicatif::ProgressStyle::with_template(&template) + .unwrap() + .with_key("eta", crate::utils::eta_key) + .progress_chars("#>-"), + ); + pb } /// True if the network calculates gas costs differently. diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index 3a2e66370..63b2de818 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -1,7 +1,6 @@ -use super::receipts; use crate::{ - build::LinkedBuildData, sequence::ScriptSequenceKind, verify::BroadcastedState, ScriptArgs, - ScriptConfig, + build::LinkedBuildData, progress::ScriptProgress, sequence::ScriptSequenceKind, + verify::BroadcastedState, ScriptArgs, ScriptConfig, }; use alloy_chains::Chain; use alloy_eips::eip2718::Encodable2718; @@ -13,10 +12,7 @@ use alloy_transport::Transport; use eyre::{bail, Context, Result}; use forge_verify::provider::VerificationProviderType; use foundry_cheatcodes::ScriptWallets; -use foundry_cli::{ - init_progress, update_progress, - utils::{has_batch_support, has_different_gas_calc}, -}; +use foundry_cli::utils::{has_batch_support, has_different_gas_calc}; use foundry_common::{ provider::{get_http_provider, try_get_http_provider, RetryProvider}, shell, @@ -160,14 +156,17 @@ pub struct BundledState { impl BundledState { pub async fn wait_for_pending(mut self) -> Result { + let progress = ScriptProgress::default(); + let progress_ref = &progress; let futs = self .sequence .sequences_mut() .iter_mut() - .map(|sequence| async move { + .enumerate() + .map(|(sequence_idx, sequence)| async move { let rpc_url = sequence.rpc_url(); let provider = Arc::new(get_http_provider(rpc_url)); - receipts::wait_for_pending(provider, sequence).await + progress_ref.wait_for_pending(sequence_idx, sequence, &provider).await }) .collect::>(); @@ -229,12 +228,16 @@ impl BundledState { SendTransactionsKind::Raw(signers) }; + let progress = ScriptProgress::default(); + for i in 0..self.sequence.sequences().len() { let mut sequence = self.sequence.sequences_mut().get_mut(i).unwrap(); let provider = Arc::new(try_get_http_provider(sequence.rpc_url())?); let already_broadcasted = sequence.receipts.len(); + let seq_progress = progress.get_sequence_progress(i, sequence); + if already_broadcasted < sequence.transactions.len() { let is_legacy = Chain::from(sequence.chain).is_legacy() || self.args.legacy; // Make a one-time gas price estimation @@ -313,8 +316,6 @@ impl BundledState { send_kind.signers_count() != 1 || !has_batch_support(sequence.chain); - let pb = init_progress!(transactions, "txes"); - // We send transactions and wait for receipts in batches. let batch_size = if sequential_broadcast { 1 } else { self.args.batch_size }; let mut index = already_broadcasted; @@ -322,11 +323,11 @@ impl BundledState { for (batch_number, batch) in transactions.chunks(batch_size).enumerate() { let mut pending_transactions = vec![]; - shell::println(format!( - "##\nSending transactions [{} - {}].", + seq_progress.inner.write().set_status(&format!( + "Sending transactions [{} - {}]", batch_number * batch_size, batch_number * batch_size + std::cmp::min(batch_size, batch.len()) - 1 - ))?; + )); for (tx, kind, is_fixed_gas_limit) in batch { let fut = send_transaction( provider.clone(), @@ -351,7 +352,7 @@ impl BundledState { self.sequence.save(true, false)?; sequence = self.sequence.sequences_mut().get_mut(i).unwrap(); - update_progress!(pb, index - already_broadcasted); + seq_progress.inner.write().tx_sent(tx_hash); index += 1; } @@ -359,8 +360,7 @@ impl BundledState { self.sequence.save(true, false)?; sequence = self.sequence.sequences_mut().get_mut(i).unwrap(); - shell::println("##\nWaiting for receipts.")?; - receipts::clear_pendings(provider.clone(), sequence, None).await?; + progress.wait_for_pending(i, sequence, &provider).await? } // Checkpoint save self.sequence.save(true, false)?; @@ -368,9 +368,6 @@ impl BundledState { } } - shell::println("\n\n==========================")?; - shell::println("\nONCHAIN EXECUTION COMPLETE & SUCCESSFUL.")?; - let (total_gas, total_gas_price, total_paid) = sequence.receipts.iter().fold((0, 0, 0), |acc, receipt| { let gas_used = receipt.gas_used; @@ -381,14 +378,18 @@ impl BundledState { let avg_gas_price = format_units(total_gas_price / sequence.receipts.len() as u128, 9) .unwrap_or_else(|_| "N/A".to_string()); - shell::println(format!( - "Total Paid: {} ETH ({} gas * avg {} gwei)", + seq_progress.inner.write().set_status(&format!( + "Total Paid: {} ETH ({} gas * avg {} gwei)\n", paid.trim_end_matches('0'), total_gas, avg_gas_price.trim_end_matches('0').trim_end_matches('.') - ))?; + )); + seq_progress.inner.write().finish(); } + shell::println("\n\n==========================")?; + shell::println("\nONCHAIN EXECUTION COMPLETE & SUCCESSFUL.")?; + Ok(BroadcastedState { args: self.args, script_config: self.script_config, diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 39059ed21..95f88dd8d 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -51,6 +51,7 @@ mod broadcast; mod build; mod execute; mod multi_sequence; +mod progress; mod providers; mod receipts; mod runner; diff --git a/crates/script/src/progress.rs b/crates/script/src/progress.rs new file mode 100644 index 000000000..c8fd065cb --- /dev/null +++ b/crates/script/src/progress.rs @@ -0,0 +1,245 @@ +use crate::{ + receipts::{check_tx_status, format_receipt, TxStatus}, + sequence::ScriptSequence, +}; +use alloy_chains::Chain; +use alloy_primitives::B256; +use eyre::Result; +use foundry_cli::utils::init_progress; +use foundry_common::provider::RetryProvider; +use futures::StreamExt; +use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; +use parking_lot::RwLock; +use std::{collections::HashMap, fmt::Write, sync::Arc, time::Duration}; +use yansi::Paint; + +/// State of [ProgressBar]s displayed for the given [ScriptSequence]. +#[derive(Debug)] +pub struct SequenceProgressState { + /// The top spinner with containt of the format "Sequence #{id} on {network} | {status}"" + top_spinner: ProgressBar, + /// Progress bar with the count of transactions. + txs: ProgressBar, + /// Progress var with the count of confirmed transactions. + receipts: ProgressBar, + /// Standalone spinners for pending transactions. + tx_spinners: HashMap, + /// Copy of the main [MultiProgress] instance. + multi: MultiProgress, +} + +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 = SequenceProgressState { + top_spinner, + txs, + receipts, + tx_spinners: Default::default(), + multi, + }; + + for tx_hash in sequence.pending.iter() { + state.tx_sent(*tx_hash); + } + + state + } + + /// Called when a new transaction is sent. Displays a spinner with a hash of the transaction and + /// advances the sent transactions progress bar. + 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)); + + self.tx_spinners.insert(tx_hash, spinner); + } + self.txs.inc(1); + } + + /// Removes the pending transaction spinner and advances confirmed transactions progress bar. + pub fn finish_tx_spinner(&mut self, tx_hash: B256) { + if let Some(spinner) = self.tx_spinners.remove(&tx_hash) { + spinner.finish_and_clear(); + } + self.receipts.inc(1); + } + + /// 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)?; + + Ok(()) + } + + /// Sets status for the current sequence progress. + pub fn set_status(&mut self, status: &str) { + self.top_spinner.set_message(format!(" | {status}")); + } + + /// Hides transactions and receipts progress bar, leaving only top line with the latest set + /// status. + pub fn finish(&self) { + self.top_spinner.finish(); + self.txs.finish_and_clear(); + self.receipts.finish_and_clear(); + } +} + +/// Clonable wrapper around [SequenceProgressState]. +#[derive(Debug, Clone)] +pub struct SequenceProgress { + pub inner: Arc>, +} + +impl SequenceProgress { + pub fn new(sequence_idx: usize, sequence: &ScriptSequence, multi: MultiProgress) -> Self { + Self { + inner: Arc::new(RwLock::new(SequenceProgressState::new(sequence_idx, sequence, multi))), + } + } +} + +/// Container for multiple [SequenceProgress] instances keyed by sequence index. +#[derive(Debug, Clone, Default)] +pub struct ScriptProgress { + state: Arc>>, + multi: MultiProgress, +} + +impl ScriptProgress { + /// Returns a [SequenceProgress] instance for the given sequence index. If it doesn't exist, + /// creates one. + pub fn get_sequence_progress( + &self, + sequence_idx: usize, + sequence: &ScriptSequence, + ) -> SequenceProgress { + if let Some(progress) = self.state.read().get(&sequence_idx) { + return progress.clone(); + } + let progress = SequenceProgress::new(sequence_idx, sequence, self.multi.clone()); + self.state.write().insert(sequence_idx, progress.clone()); + progress + } + + /// Traverses a set of pendings and either finds receipts, or clears them from + /// the deployment sequence. + /// + /// For each `tx_hash`, we check if it has confirmed. If it has + /// confirmed, we push the receipt (if successful) or push an error (if + /// revert). If the transaction has not confirmed, but can be found in the + /// node's mempool, we wait for its receipt to be available. If the transaction + /// has not confirmed, and cannot be found in the mempool, we remove it from + /// the `deploy_sequence.pending` vector so that it will be rebroadcast in + /// later steps. + pub async fn wait_for_pending( + &self, + sequence_idx: usize, + deployment_sequence: &mut ScriptSequence, + provider: &RetryProvider, + ) -> Result<()> { + if deployment_sequence.pending.is_empty() { + return Ok(()); + } + + let count = deployment_sequence.pending.len(); + let seq_progress = self.get_sequence_progress(sequence_idx, deployment_sequence); + + seq_progress.inner.write().set_status("Waiting for pending transactions"); + + trace!("Checking status of {count} pending transactions"); + + let futs = + deployment_sequence.pending.clone().into_iter().map(|tx| check_tx_status(provider, tx)); + let mut tasks = futures::stream::iter(futs).buffer_unordered(10); + + let mut errors: Vec = vec![]; + + while let Some((tx_hash, result)) = tasks.next().await { + match result { + Err(err) => { + errors.push(format!("Failure on receiving a receipt for {tx_hash:?}:\n{err}")); + + seq_progress.inner.write().finish_tx_spinner(tx_hash); + } + Ok(TxStatus::Dropped) => { + // We want to remove it from pending so it will be re-broadcast. + deployment_sequence.remove_pending(tx_hash); + errors.push(format!("Transaction dropped from the mempool: {tx_hash:?}")); + + seq_progress.inner.write().finish_tx_spinner(tx_hash); + } + Ok(TxStatus::Success(receipt)) => { + trace!(tx_hash=?tx_hash, "received tx receipt"); + + let msg = format_receipt(deployment_sequence.chain.into(), &receipt); + seq_progress.inner.write().finish_tx_spinner_with_msg(tx_hash, &msg)?; + + deployment_sequence.remove_pending(receipt.transaction_hash); + deployment_sequence.add_receipt(receipt); + } + Ok(TxStatus::Revert(receipt)) => { + // consider: + // if this is not removed from pending, then the script becomes + // un-resumable. Is this desirable on reverts? + warn!(tx_hash=?tx_hash, "Transaction Failure"); + deployment_sequence.remove_pending(receipt.transaction_hash); + + let msg = format_receipt(deployment_sequence.chain.into(), &receipt); + seq_progress.inner.write().finish_tx_spinner_with_msg(tx_hash, &msg)?; + + errors.push(format!("Transaction Failure: {:?}", receipt.transaction_hash)); + } + } + } + + // print any errors + if !errors.is_empty() { + let mut error_msg = errors.join("\n"); + if !deployment_sequence.pending.is_empty() { + error_msg += "\n\n Add `--resume` to your command to try and continue broadcasting + the transactions." + } + eyre::bail!(error_msg); + } + + Ok(()) + } +} diff --git a/crates/script/src/receipts.rs b/crates/script/src/receipts.rs index 5686f18f6..57c3e94a3 100644 --- a/crates/script/src/receipts.rs +++ b/crates/script/src/receipts.rs @@ -1,16 +1,12 @@ -use super::sequence::ScriptSequence; use alloy_chains::Chain; use alloy_primitives::{utils::format_units, TxHash, U256}; use alloy_provider::{PendingTransactionBuilder, Provider}; use alloy_rpc_types::AnyTransactionReceipt; use eyre::Result; -use foundry_cli::{init_progress, update_progress}; use foundry_common::provider::RetryProvider; -use futures::StreamExt; -use std::sync::Arc; /// Convenience enum for internal signalling of transaction status -enum TxStatus { +pub enum TxStatus { Dropped, Success(AnyTransactionReceipt), Revert(AnyTransactionReceipt), @@ -26,106 +22,9 @@ impl From for TxStatus { } } -/// Gets the receipts of previously pending transactions, or removes them from -/// the deploy sequence's pending vector -pub async fn wait_for_pending( - provider: Arc, - deployment_sequence: &mut ScriptSequence, -) -> Result<()> { - if deployment_sequence.pending.is_empty() { - return Ok(()); - } - println!("##\nChecking previously pending transactions."); - clear_pendings(provider, deployment_sequence, None).await -} - -/// Traverses a set of pendings and either finds receipts, or clears them from -/// the deployment sequence. -/// -/// If no `tx_hashes` are provided, then `deployment_sequence.pending` will be -/// used. For each `tx_hash`, we check if it has confirmed. If it has -/// confirmed, we push the receipt (if successful) or push an error (if -/// revert). If the transaction has not confirmed, but can be found in the -/// node's mempool, we wait for its receipt to be available. If the transaction -/// has not confirmed, and cannot be found in the mempool, we remove it from -/// the `deploy_sequence.pending` vector so that it will be rebroadcast in -/// later steps. -pub async fn clear_pendings( - provider: Arc, - deployment_sequence: &mut ScriptSequence, - tx_hashes: Option>, -) -> Result<()> { - let to_query = tx_hashes.unwrap_or_else(|| deployment_sequence.pending.clone()); - - let count = deployment_sequence.pending.len(); - - trace!("Checking status of {count} pending transactions"); - - let futs = to_query.iter().copied().map(|tx| check_tx_status(&provider, tx)); - let mut tasks = futures::stream::iter(futs).buffer_unordered(10); - - let mut errors: Vec = vec![]; - let mut receipts = Vec::::with_capacity(count); - - // set up progress bar - let mut pos = 0; - let pb = init_progress!(deployment_sequence.pending, "receipts"); - pb.set_position(pos); - - while let Some((tx_hash, result)) = tasks.next().await { - match result { - Err(err) => { - errors.push(format!("Failure on receiving a receipt for {tx_hash:?}:\n{err}")) - } - Ok(TxStatus::Dropped) => { - // We want to remove it from pending so it will be re-broadcast. - deployment_sequence.remove_pending(tx_hash); - errors.push(format!("Transaction dropped from the mempool: {tx_hash:?}")); - } - Ok(TxStatus::Success(receipt)) => { - trace!(tx_hash=?tx_hash, "received tx receipt"); - deployment_sequence.remove_pending(receipt.transaction_hash); - receipts.push(receipt); - } - Ok(TxStatus::Revert(receipt)) => { - // consider: - // if this is not removed from pending, then the script becomes - // un-resumable. Is this desirable on reverts? - warn!(tx_hash=?tx_hash, "Transaction Failure"); - deployment_sequence.remove_pending(receipt.transaction_hash); - errors.push(format!("Transaction Failure: {:?}", receipt.transaction_hash)); - } - } - // update the progress bar - update_progress!(pb, pos); - pos += 1; - } - - // sort receipts by blocks asc and index - receipts.sort_by_key(|r| (r.block_number, r.transaction_index)); - - // print all receipts - for receipt in receipts { - print_receipt(deployment_sequence.chain.into(), &receipt); - deployment_sequence.add_receipt(receipt); - } - - // print any errors - if !errors.is_empty() { - let mut error_msg = errors.join("\n"); - if !deployment_sequence.pending.is_empty() { - error_msg += "\n\n Add `--resume` to your command to try and continue broadcasting - the transactions." - } - eyre::bail!(error_msg); - } - - Ok(()) -} - /// Checks the status of a txhash by first polling for a receipt, then for /// mempool inclusion. Returns the tx hash, and a status -async fn check_tx_status( +pub async fn check_tx_status( provider: &RetryProvider, hash: TxHash, ) -> (TxHash, Result) { @@ -151,11 +50,11 @@ async fn check_tx_status( } /// Prints parts of the receipt to stdout -pub fn print_receipt(chain: Chain, receipt: &AnyTransactionReceipt) { +pub fn format_receipt(chain: Chain, receipt: &AnyTransactionReceipt) -> String { let gas_used = receipt.gas_used; let gas_price = receipt.effective_gas_price; - foundry_common::shell::println(format!( - "\n##### {chain}\n{status}Hash: {tx_hash:?}{caddr}\nBlock: {bn}\n{gas}\n", + format!( + "\n##### {chain}\n{status}Hash: {tx_hash:?}{caddr}\nBlock: {bn}\n{gas}\n\n", status = if !receipt.inner.inner.inner.receipt.status { "❌ [Failed]" } else { @@ -180,6 +79,5 @@ pub fn print_receipt(chain: Chain, receipt: &AnyTransactionReceipt) { gas_price.trim_end_matches('0').trim_end_matches('.') ) }, - )) - .expect("could not print receipt"); + ) } From 9eebb37588e2bda4393157fd157f5468f603803b Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 3 Jun 2024 13:43:01 +0200 Subject: [PATCH 334/430] fix: use timeouts for script transactions (#8037) --- crates/script/src/receipts.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/script/src/receipts.rs b/crates/script/src/receipts.rs index 57c3e94a3..16f97536b 100644 --- a/crates/script/src/receipts.rs +++ b/crates/script/src/receipts.rs @@ -4,6 +4,7 @@ use alloy_provider::{PendingTransactionBuilder, Provider}; use alloy_rpc_types::AnyTransactionReceipt; use eyre::Result; use foundry_common::provider::RetryProvider; +use std::time::Duration; /// Convenience enum for internal signalling of transaction status pub enum TxStatus { @@ -40,6 +41,7 @@ pub async fn check_tx_status( // If the tx is present in the mempool, run the pending tx future, and // assume the next drop is really really real Ok(PendingTransactionBuilder::new(provider, hash) + .with_timeout(Some(Duration::from_secs(120))) .get_receipt() .await .map_or(TxStatus::Dropped, |r| r.into())) From 3b175cb852cbac6ab29fa7f470fe5461277a6e1d Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 3 Jun 2024 13:45:35 +0200 Subject: [PATCH 335/430] refactor: deploy all libraries when running tests (#8034) * refactor: deploy all libraries when running tests * fix test * fix tests * fix traces test * review fixes * fix * review fixes --- crates/evm/core/src/backend/mod.rs | 5 --- crates/forge/bin/cmd/coverage.rs | 8 ++-- crates/forge/bin/cmd/test/mod.rs | 59 +++++++++++++------------ crates/forge/src/multi_runner.rs | 71 ++++++++++++++---------------- crates/forge/src/result.rs | 23 +--------- crates/forge/src/runner.rs | 34 +++++++------- crates/forge/tests/it/core.rs | 6 +-- crates/forge/tests/it/fuzz.rs | 2 +- crates/forge/tests/it/repros.rs | 2 +- crates/linking/src/lib.rs | 8 ++-- crates/script/src/build.rs | 2 +- 11 files changed, 98 insertions(+), 122 deletions(-) diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index 88a4b060a..357a79f43 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -536,11 +536,6 @@ impl Backend { /// This will also grant cheatcode access to the test account pub fn set_test_contract(&mut self, acc: Address) -> &mut Self { trace!(?acc, "setting test account"); - // toggle the previous sender - if let Some(current) = self.inner.test_contract_address.take() { - self.remove_persistent_account(¤t); - self.revoke_cheatcode_access(&acc); - } self.add_persistent_account(acc); self.allow_cheatcode_access(acc); diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index efdcc0e38..22dbab67c 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -246,6 +246,8 @@ impl CoverageArgs { .set_coverage(true) .build(&root, output, env, evm_opts)?; + let known_contracts = runner.known_contracts.clone(); + let outcome = self .test .run_tests(runner, config.clone(), verbosity, &self.test.filter(&config)) @@ -257,12 +259,10 @@ impl CoverageArgs { for result in suite.test_results.values() { let Some(hit_maps) = result.coverage.as_ref() else { continue }; for map in hit_maps.0.values() { - if let Some((id, _)) = - suite.known_contracts.find_by_deployed_code(&map.bytecode) - { + if let Some((id, _)) = known_contracts.find_by_deployed_code(&map.bytecode) { hits.push((id, map, true)); } else if let Some((id, _)) = - suite.known_contracts.find_by_creation_code(&map.bytecode) + known_contracts.find_by_creation_code(&map.bytecode) { hits.push((id, map, false)); } diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 8f1df029c..03d6266d7 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -312,11 +312,12 @@ impl TestArgs { *test_pattern = Some(debug_test_pattern.clone()); } + let libraries = runner.libraries.clone(); let outcome = self.run_tests(runner, config, verbosity, &filter).await?; if should_debug { // Get first non-empty suite result. We will have only one such entry - let Some((suite_result, test_result)) = outcome + let Some((_, test_result)) = outcome .results .iter() .find(|(_, r)| !r.test_results.is_empty()) @@ -328,7 +329,7 @@ impl TestArgs { let sources = ContractSources::from_project_output( output_clone.as_ref().unwrap(), project.root(), - &suite_result.libraries, + &libraries, )?; // Run the debugger. @@ -376,6 +377,7 @@ impl TestArgs { } let remote_chain_id = runner.evm_opts.get_remote_chain_id().await; + let known_contracts = runner.known_contracts.clone(); // Run tests. let (tx, rx) = channel::<(String, SuiteResult)>(); @@ -385,6 +387,28 @@ impl TestArgs { move || runner.test(&filter, tx) }); + // Set up trace identifiers. + let mut identifier = TraceIdentifiers::new().with_local(&known_contracts); + + // Avoid using etherscan for gas report as we decode more traces and this will be + // expensive. + if !self.gas_report { + identifier = identifier.with_etherscan(&config, remote_chain_id)?; + } + + // Build the trace decoder. + let mut builder = CallTraceDecoderBuilder::new() + .with_known_contracts(&known_contracts) + .with_verbosity(verbosity); + // Signatures are of no value for gas reports. + if !self.gas_report { + builder = builder.with_signature_identifier(SignaturesIdentifier::new( + Config::foundry_cache_dir(), + config.offline, + )?); + } + let mut decoder = builder.build(); + let mut gas_report = self .gas_report .then(|| GasReport::new(config.gas_reports.clone(), config.gas_reports_ignore.clone())); @@ -392,31 +416,11 @@ impl TestArgs { let mut outcome = TestOutcome::empty(self.allow_failure); let mut any_test_failed = false; - for (contract_name, mut suite_result) in rx { + for (contract_name, suite_result) in rx { let tests = &suite_result.test_results; - // Set up trace identifiers. - let known_contracts = &suite_result.known_contracts; - let mut identifier = TraceIdentifiers::new().with_local(known_contracts); - - // Avoid using etherscan for gas report as we decode more traces and this will be - // expensive. - if !self.gas_report { - identifier = identifier.with_etherscan(&config, remote_chain_id)?; - } - - // Build the trace decoder. - let mut builder = CallTraceDecoderBuilder::new() - .with_known_contracts(known_contracts) - .with_verbosity(verbosity); - // Signatures are of no value for gas reports. - if !self.gas_report { - builder = builder.with_signature_identifier(SignaturesIdentifier::new( - Config::foundry_cache_dir(), - config.offline, - )?); - } - let mut decoder = builder.build(); + // Clear the addresses and labels from previous test. + decoder.clear_addresses(); // We identify addresses if we're going to print *any* trace or gas report. let identify_addresses = verbosity >= 3 || self.gas_report || self.debug.is_some(); @@ -520,18 +524,15 @@ impl TestArgs { // Print suite summary. shell::println(suite_result.summary())?; - // Free memory if it's not needed. - suite_result.clear_unneeded(); - // Add the suite result to the outcome. outcome.results.insert(contract_name, suite_result); - outcome.last_run_decoder = Some(decoder); // Stop processing the remaining suites if any test failed and `fail_fast` is set. if self.fail_fast && any_test_failed { break; } } + outcome.last_run_decoder = Some(decoder); let duration = timer.elapsed(); trace!(target: "forge::test", len=outcome.results.len(), %any_test_failed, "done with results"); diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index d910215e9..4e671a77a 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -1,11 +1,13 @@ //! Forge test runner for multiple contracts. -use crate::{result::SuiteResult, ContractRunner, TestFilter, TestOptions}; +use crate::{ + result::SuiteResult, runner::LIBRARY_DEPLOYER, ContractRunner, TestFilter, TestOptions, +}; 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_compilers::{artifacts::Libraries, Artifact, ArtifactId, ProjectCompileOutput, Solc}; +use foundry_compilers::{artifacts::Libraries, Artifact, ArtifactId, ProjectCompileOutput}; use foundry_config::Config; use foundry_evm::{ backend::Backend, decode::RevertDecoder, executors::ExecutorBuilder, fork::CreateFork, @@ -27,8 +29,6 @@ use std::{ pub struct TestContract { pub abi: JsonAbi, pub bytecode: Bytes, - pub libs_to_deploy: Vec, - pub libraries: Libraries, } pub type DeployableContracts = BTreeMap; @@ -61,8 +61,12 @@ pub struct MultiContractRunner { pub test_options: TestOptions, /// Whether to enable call isolation pub isolation: bool, - /// Output of the project compilation - pub output: ProjectCompileOutput, + /// Known contracts linked with computed library addresses. + pub known_contracts: ContractsByArtifact, + /// Libraries to deploy. + pub libs_to_deploy: Vec, + /// Library addresses used to link contracts. + pub libraries: Libraries, } impl MultiContractRunner { @@ -181,17 +185,10 @@ impl MultiContractRunner { let identifier = artifact_id.identifier(); let mut span_name = identifier.as_str(); - let linker = Linker::new( - self.config.project_paths::().root, - self.output.artifact_ids().collect(), - ); - let linked_contracts = linker.get_linked_artifacts(&contract.libraries).unwrap_or_default(); - let known_contracts = ContractsByArtifact::new(linked_contracts); - let cheats_config = CheatsConfig::new( &self.config, self.evm_opts.clone(), - Some(known_contracts.clone()), + Some(self.known_contracts.clone()), None, Some(artifact_id.version.clone()), ); @@ -220,12 +217,13 @@ impl MultiContractRunner { &identifier, executor, contract, + &self.libs_to_deploy, self.evm_opts.initial_balance, self.sender, &self.revert_decoder, self.debug, ); - let r = runner.run_tests(filter, &self.test_options, known_contracts, handle); + let r = runner.run_tests(filter, &self.test_options, self.known_contracts.clone(), handle); debug!(duration=?r.duration, "executed all tests in contract"); @@ -332,10 +330,19 @@ impl MultiContractRunnerBuilder { .filter_map(|(_, contract)| contract.abi.as_ref().map(|abi| abi.borrow())); let revert_decoder = RevertDecoder::new().with_abis(abis); + let LinkOutput { libraries, libs_to_deploy } = linker.link_with_nonce_or_address( + Default::default(), + LIBRARY_DEPLOYER, + 0, + linker.contracts.keys(), + )?; + + let linked_contracts = linker.get_linked_artifacts(&libraries)?; + // Create a mapping of name => (abi, deployment code, Vec) let mut deployable_contracts = DeployableContracts::default(); - for (id, contract) in linker.contracts.iter() { + for (id, contract) in linked_contracts.iter() { let Some(abi) = contract.abi.as_ref() else { continue; }; @@ -344,35 +351,19 @@ impl MultiContractRunnerBuilder { if abi.constructor.as_ref().map(|c| c.inputs.is_empty()).unwrap_or(true) && abi.functions().any(|func| func.name.is_test() || func.name.is_invariant_test()) { - let LinkOutput { libs_to_deploy, libraries } = linker.link_with_nonce_or_address( - Default::default(), - evm_opts.sender, - 1, - id, - )?; - - let linked_contract = linker.link(id, &libraries)?; - - let Some(bytecode) = linked_contract - .get_bytecode_bytes() - .map(|b| b.into_owned()) - .filter(|b| !b.is_empty()) + let Some(bytecode) = + contract.get_bytecode_bytes().map(|b| b.into_owned()).filter(|b| !b.is_empty()) else { continue; }; - deployable_contracts.insert( - id.clone(), - TestContract { - abi: abi.clone().into_owned(), - bytecode, - libs_to_deploy, - libraries, - }, - ); + deployable_contracts + .insert(id.clone(), TestContract { abi: abi.clone(), bytecode }); } } + let known_contracts = ContractsByArtifact::new(linked_contracts); + Ok(MultiContractRunner { contracts: deployable_contracts, evm_opts, @@ -386,7 +377,9 @@ impl MultiContractRunnerBuilder { debug: self.debug, test_options: self.test_options.unwrap_or_default(), isolation: self.isolation, - output, + known_contracts, + libs_to_deploy, + libraries, }) } } diff --git a/crates/forge/src/result.rs b/crates/forge/src/result.rs index ab289db54..cd168c1d7 100644 --- a/crates/forge/src/result.rs +++ b/crates/forge/src/result.rs @@ -2,10 +2,7 @@ use crate::gas_report::GasReport; use alloy_primitives::{Address, Log}; -use foundry_common::{ - evm::Breakpoints, get_contract_name, get_file_name, shell, ContractsByArtifact, -}; -use foundry_compilers::artifacts::Libraries; +use foundry_common::{evm::Breakpoints, get_contract_name, get_file_name, shell}; use foundry_evm::{ coverage::HitMaps, debug::DebugArena, @@ -196,13 +193,6 @@ pub struct SuiteResult { pub test_results: BTreeMap, /// Generated warnings. pub warnings: Vec, - /// Libraries used to link test contract. - pub libraries: Libraries, - /// Contracts linked with correct libraries. - /// - /// This is cleared at the end of the test run if coverage is not enabled. - #[serde(skip)] - pub known_contracts: ContractsByArtifact, } impl SuiteResult { @@ -210,17 +200,8 @@ impl SuiteResult { duration: Duration, test_results: BTreeMap, warnings: Vec, - libraries: Libraries, - known_contracts: ContractsByArtifact, ) -> Self { - Self { duration, test_results, warnings, libraries, known_contracts } - } - - /// Frees memory that is not used for the final output. - pub fn clear_unneeded(&mut self) { - if !self.test_results.values().any(|r| r.coverage.is_some()) { - ContractsByArtifact::clear(&mut self.known_contracts); - } + Self { duration, test_results, warnings } } /// Returns an iterator over all individual succeeding tests and their names. diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index ea6b6efe7..72f8b03c4 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -8,7 +8,7 @@ use crate::{ }; use alloy_dyn_abi::DynSolValue; use alloy_json_abi::Function; -use alloy_primitives::{Address, U256}; +use alloy_primitives::{address, Address, Bytes, U256}; use eyre::Result; use foundry_common::{ contracts::{ContractsByAddress, ContractsByArtifact}, @@ -43,12 +43,21 @@ use std::{ time::Instant, }; +/// When running tests, we deploy all external libraries present in the project. To avoid additional +/// libraries affecting nonces of senders used in tests, we are using separate address to +/// predeploy libraries. +/// +/// `address(uint160(uint256(keccak256("foundry library deployer"))))` +pub const LIBRARY_DEPLOYER: Address = address!("1F95D37F27EA0dEA9C252FC09D5A6eaA97647353"); + /// A type that executes all tests of a contract #[derive(Clone, Debug)] pub struct ContractRunner<'a> { pub name: &'a str, /// The data of the contract being ran. pub contract: &'a TestContract, + /// The libraries that need to be deployed before the contract. + pub libs_to_deploy: &'a Vec, /// The executor used by the runner. pub executor: Executor, /// Revert decoder. Contains all known errors. @@ -62,10 +71,12 @@ pub struct ContractRunner<'a> { } impl<'a> ContractRunner<'a> { + #[allow(clippy::too_many_arguments)] pub fn new( name: &'a str, executor: Executor, contract: &'a TestContract, + libs_to_deploy: &'a Vec, initial_balance: U256, sender: Option
, revert_decoder: &'a RevertDecoder, @@ -75,6 +86,7 @@ impl<'a> ContractRunner<'a> { name, executor, contract, + libs_to_deploy, initial_balance, sender: sender.unwrap_or_default(), revert_decoder, @@ -104,11 +116,13 @@ impl<'a> ContractRunner<'a> { self.executor.set_nonce(self.sender, 1)?; // Deploy libraries + self.executor.set_balance(LIBRARY_DEPLOYER, U256::MAX)?; + let mut logs = Vec::new(); - let mut traces = Vec::with_capacity(self.contract.libs_to_deploy.len()); - for code in self.contract.libs_to_deploy.iter() { + let mut traces = Vec::with_capacity(self.libs_to_deploy.len()); + for code in self.libs_to_deploy.iter() { match self.executor.deploy( - self.sender, + LIBRARY_DEPLOYER, code.clone(), U256::ZERO, Some(self.revert_decoder), @@ -286,8 +300,6 @@ impl<'a> ContractRunner<'a> { [("setUp()".to_string(), TestResult::fail("multiple setUp functions".to_string()))] .into(), warnings, - self.contract.libraries.clone(), - known_contracts, ) } @@ -324,8 +336,6 @@ impl<'a> ContractRunner<'a> { )] .into(), warnings, - self.contract.libraries.clone(), - known_contracts, ) } @@ -383,13 +393,7 @@ impl<'a> ContractRunner<'a> { .collect::>(); let duration = start.elapsed(); - let suite_result = SuiteResult::new( - duration, - test_results, - warnings, - self.contract.libraries.clone(), - known_contracts, - ); + let suite_result = SuiteResult::new(duration, test_results, warnings); info!( duration=?suite_result.duration, "done. {}/{} successful", diff --git a/crates/forge/tests/it/core.rs b/crates/forge/tests/it/core.rs index 34cdc0d17..b229f151f 100644 --- a/crates/forge/tests/it/core.rs +++ b/crates/forge/tests/it/core.rs @@ -710,12 +710,12 @@ async fn test_trace() { result.traces.iter().filter(|(kind, _)| *kind == TraceKind::Deployment); let setup_traces = result.traces.iter().filter(|(kind, _)| *kind == TraceKind::Setup); let execution_traces = - result.traces.iter().filter(|(kind, _)| *kind == TraceKind::Deployment); + result.traces.iter().filter(|(kind, _)| *kind == TraceKind::Execution); assert_eq!( deployment_traces.count(), - 1, - "Test {test_name} did not have exactly 1 deployment trace." + 12, + "Test {test_name} did not have exactly 12 deployment trace." ); assert!(setup_traces.count() <= 1, "Test {test_name} had more than 1 setup trace."); assert_eq!( diff --git a/crates/forge/tests/it/fuzz.rs b/crates/forge/tests/it/fuzz.rs index 46f8115ab..4f8a6d412 100644 --- a/crates/forge/tests/it/fuzz.rs +++ b/crates/forge/tests/it/fuzz.rs @@ -158,7 +158,7 @@ async fn test_persist_fuzz_failure() { async fn test_scrape_bytecode() { let filter = Filter::new(".*", ".*", ".*fuzz/FuzzScrapeBytecode.t.sol"); let mut runner = TEST_DATA_DEFAULT.runner(); - runner.test_options.fuzz.runs = 750; + runner.test_options.fuzz.runs = 2000; runner.test_options.fuzz.seed = Some(U256::from(6u32)); let suite_result = runner.test_collect(&filter); diff --git a/crates/forge/tests/it/repros.rs b/crates/forge/tests/it/repros.rs index 75856c5e4..aeae1175a 100644 --- a/crates/forge/tests/it/repros.rs +++ b/crates/forge/tests/it/repros.rs @@ -254,7 +254,7 @@ test_repro!(6501, false, None, |res| { assert_eq!(test.status, TestStatus::Success); assert_eq!(test.decoded_logs, ["a".to_string(), "1".to_string(), "b 2".to_string()]); - let (kind, traces) = test.traces[1].clone(); + let (kind, traces) = test.traces.last().unwrap().clone(); let nodes = traces.into_nodes(); assert_eq!(kind, TraceKind::Execution); diff --git a/crates/linking/src/lib.rs b/crates/linking/src/lib.rs index 8c0c6f5fe..8d9791a18 100644 --- a/crates/linking/src/lib.rs +++ b/crates/linking/src/lib.rs @@ -138,14 +138,16 @@ impl<'a> Linker<'a> { libraries: Libraries, sender: Address, mut nonce: u64, - target: &'a ArtifactId, + targets: impl IntoIterator, ) -> Result { // Library paths in `link_references` keys are always stripped, so we have to strip // user-provided paths to be able to match them correctly. let mut libraries = libraries.with_stripped_file_prefixes(self.root.as_path()); let mut needed_libraries = BTreeSet::new(); - self.collect_dependencies(target, &mut needed_libraries)?; + for target in targets { + self.collect_dependencies(target, &mut needed_libraries)?; + } let mut libs_to_deploy = Vec::new(); @@ -324,7 +326,7 @@ mod tests { let linker = Linker::new(self.project.root(), self.output.artifact_ids().collect()); for (id, identifier) in self.iter_linking_targets(&linker) { let output = linker - .link_with_nonce_or_address(Default::default(), sender, initial_nonce, id) + .link_with_nonce_or_address(Default::default(), sender, initial_nonce, [id]) .expect("Linking failed"); self.validate_assertions(identifier, output); } diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index a2462e4b2..90e72c66c 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -81,7 +81,7 @@ impl BuildData { known_libraries, script_config.evm_opts.sender, script_config.sender_nonce, - &self.target, + [&self.target], )?; (output.libraries, ScriptPredeployLibraries::Default(output.libs_to_deploy)) From 590f5669df6d69766fc8ad7c288e83d821c367e2 Mon Sep 17 00:00:00 2001 From: Alisina Bahadori Date: Mon, 3 Jun 2024 14:03:25 -0400 Subject: [PATCH 336/430] Fix shadowed branch coverage PC variable (#8040) --- crates/evm/coverage/src/anchors.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/evm/coverage/src/anchors.rs b/crates/evm/coverage/src/anchors.rs index f717e24ab..a31ef0237 100644 --- a/crates/evm/coverage/src/anchors.rs +++ b/crates/evm/coverage/src/anchors.rs @@ -143,15 +143,15 @@ pub fn find_anchor_branch( let push_bytes = &bytecode[push_bytes_start..push_bytes_start + push_size]; let mut pc_bytes = [0u8; 8]; pc_bytes[8 - push_size..].copy_from_slice(push_bytes); - let pc = u64::from_be_bytes(pc_bytes); - let pc = usize::try_from(pc).expect("PC is too big"); + let pc_jump = u64::from_be_bytes(pc_bytes); + let pc_jump = usize::try_from(pc_jump).expect("PC is too big"); anchors = Some(( ItemAnchor { item_id, // The first branch is the opcode directly after JUMPI instruction: pc + 2, }, - ItemAnchor { item_id, instruction: pc }, + ItemAnchor { item_id, instruction: pc_jump }, )); } } From fbad377ab26a432e48444cf324feee1195a30960 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 3 Jun 2024 23:11:15 +0200 Subject: [PATCH 337/430] bump compilers (#8011) * patch compilers * patch block-explorers * update patch * add println * add printlm * RUST_LOG * update patch * update patch * fix * update patch * update patch * skip vyper contracts * fix * update patch * rm patch * fix generics --- Cargo.lock | 10 +-- Cargo.toml | 4 +- crates/cast/bin/cmd/storage.rs | 28 ++++--- crates/cheatcodes/src/fs.rs | 2 +- crates/chisel/benches/session_source.rs | 8 +- crates/chisel/src/executor.rs | 13 +-- crates/chisel/src/session_source.rs | 25 +++--- crates/cli/src/opts/build/core.rs | 5 +- crates/common/src/compile.rs | 40 +++------ crates/config/src/lib.rs | 107 ++++++++---------------- crates/forge/bin/cmd/build.rs | 45 +++------- crates/forge/bin/cmd/coverage.rs | 12 +-- crates/forge/bin/cmd/flatten.rs | 6 +- crates/forge/bin/cmd/fmt.rs | 4 +- crates/forge/bin/cmd/geiger/mod.rs | 4 +- crates/forge/bin/cmd/test/mod.rs | 17 ++-- crates/forge/bin/cmd/tree.rs | 4 +- crates/forge/src/multi_runner.rs | 4 +- crates/forge/tests/cli/config.rs | 6 +- crates/forge/tests/cli/test_cmd.rs | 1 + crates/script/src/verify.rs | 8 +- crates/test-utils/src/util.rs | 5 +- crates/verify/src/bytecode.rs | 7 +- crates/verify/src/etherscan/flatten.rs | 34 +++++--- crates/verify/src/provider.rs | 23 ++--- 25 files changed, 175 insertions(+), 247 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0e7bd7cf0..375391847 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3257,9 +3257,9 @@ dependencies = [ [[package]] name = "foundry-block-explorers" -version = "0.2.8" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7101c78b700b8c84294aee0319289af724201181a9b757d8a9a2fea991f3ce4e" +checksum = "673c42208fee48238ef6833cf55295c9e9b5546383caf426da72d849fb43dc24" dependencies = [ "alloy-chains", "alloy-json-abi", @@ -3412,9 +3412,9 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.5.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd571882cd635dd2a11169d5b696e51644fc803487989e2b5fd7ff6bdd008f1c" +checksum = "fe70a3860ec9f1861e5d82cbd4ffc55756975c0826dacf8ae4d6d696df8f7f53" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3425,7 +3425,7 @@ dependencies = [ "fs_extra", "futures-util", "home", - "itertools 0.12.1", + "itertools 0.13.0", "md-5", "memmap2 0.9.4", "once_cell", diff --git a/Cargo.toml b/Cargo.toml index d9db1d72f..3818778fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -137,8 +137,8 @@ foundry-wallets = { path = "crates/wallets" } foundry-linking = { path = "crates/linking" } # solc & compilation utilities -foundry-block-explorers = { version = "0.2.8", default-features = false } -foundry-compilers = { version = "0.5.2", default-features = false } +foundry-block-explorers = { version = "0.3.0", default-features = false } +foundry-compilers = { version = "0.6.0", default-features = false } ## revm # no default features to avoid c-kzg diff --git a/crates/cast/bin/cmd/storage.rs b/crates/cast/bin/cmd/storage.rs index a59d93c5d..aedf84471 100644 --- a/crates/cast/bin/cmd/storage.rs +++ b/crates/cast/bin/cmd/storage.rs @@ -20,15 +20,15 @@ use foundry_common::{ }; use foundry_compilers::{ artifacts::StorageLayout, - compilers::{solc::SolcVersionManager, CompilerVersionManager}, - Artifact, CompilerConfig, ConfigurableContractArtifact, Project, + compilers::{solc::SolcCompiler, Compiler, CompilerSettings}, + Artifact, ConfigurableContractArtifact, Project, Solc, }; use foundry_config::{ figment::{self, value::Dict, Metadata, Profile}, impl_figment_convert_cast, Config, }; use semver::Version; -use std::{str::FromStr, sync::Arc}; +use std::str::FromStr; /// The minimum Solc version for outputting storage layouts. /// @@ -142,11 +142,10 @@ impl StorageArgs { let mut project = etherscan_project(metadata, root_path)?; add_storage_layout_output(&mut project); - let vm = SolcVersionManager::default(); - project.compiler_config = if auto_detect { - CompilerConfig::AutoDetect(Arc::new(vm)) + project.compiler = if auto_detect { + SolcCompiler::AutoDetect } else { - CompilerConfig::Specific(vm.get_or_install(&version)?) + SolcCompiler::Specific(Solc::find_or_install(&version)?) }; // Compile @@ -160,8 +159,8 @@ impl StorageArgs { if is_storage_layout_empty(&artifact.storage_layout) && auto_detect { // try recompiling with the minimum version eprintln!("The requested contract was compiled with {version} while the minimum version for storage layouts is {MIN_SOLC} and as a result the output may be empty."); - let solc = SolcVersionManager::default().get_or_install(&MIN_SOLC)?; - project.compiler_config = CompilerConfig::Specific(solc); + let solc = Solc::find_or_install(&MIN_SOLC)?; + project.compiler = SolcCompiler::Specific(solc); if let Ok(output) = ProjectCompiler::new().quiet(true).compile(&project) { out = output; let (_, new_artifact) = out @@ -284,10 +283,15 @@ fn print_storage(layout: StorageLayout, values: Vec, pretty: bool) Ok(()) } -fn add_storage_layout_output(project: &mut Project) { +fn add_storage_layout_output(project: &mut Project) { project.artifacts.additional_values.storage_layout = true; - let output_selection = project.artifacts.output_selection(); - project.settings.push_all(output_selection); + project.settings.update_output_selection(|selection| { + selection.0.values_mut().for_each(|contract_selection| { + contract_selection + .values_mut() + .for_each(|selection| selection.push("storageLayout".to_string())) + }); + }) } fn is_storage_layout_empty(storage_layout: &Option) -> bool { diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index 308d86641..fd4234d21 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -283,7 +283,7 @@ fn get_artifact_code(state: &Cheatcodes, path: &str, deployed: bool) -> Result = - Lazy::new(|| SolcVersionManager::default().get_or_install(&Version::new(0, 8, 19)).unwrap()); +static SOLC: Lazy = Lazy::new(|| Solc::find_or_install(&Version::new(0, 8, 19)).unwrap()); /// Benchmark for the `clone_with_new_line` function in [SessionSource] fn clone_with_new_line(c: &mut Criterion) { diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index 3ce6a7d19..eea595486 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -1401,11 +1401,7 @@ impl<'a> Iterator for InstructionIter<'a> { #[cfg(test)] mod tests { use super::*; - use foundry_compilers::{ - compilers::{solc::SolcVersionManager, CompilerVersionManager}, - error::SolcError, - Solc, - }; + use foundry_compilers::{error::SolcError, Solc}; use semver::Version; use std::sync::Mutex; @@ -1690,8 +1686,7 @@ mod tests { for _ in 0..3 { let mut is_preinstalled = PRE_INSTALL_SOLC_LOCK.lock().unwrap(); if !*is_preinstalled { - let solc = SolcVersionManager::default() - .get_or_install(&version.parse().unwrap()) + let solc = Solc::find_or_install(&version.parse().unwrap()) .map(|solc| (solc.version.clone(), solc)); match solc { Ok((v, solc)) => { @@ -1712,9 +1707,7 @@ mod tests { } } - let solc = SolcVersionManager::default() - .get_or_install(&Version::new(0, 8, 19)) - .expect("could not install solc"); + let solc = Solc::find_or_install(&Version::new(0, 8, 19)).expect("could not install solc"); SessionSource::new(solc, Default::default()) } diff --git a/crates/chisel/src/session_source.rs b/crates/chisel/src/session_source.rs index 21fa3d834..6882a63aa 100644 --- a/crates/chisel/src/session_source.rs +++ b/crates/chisel/src/session_source.rs @@ -8,7 +8,6 @@ use eyre::Result; use forge_fmt::solang_ext::SafeUnwrap; use foundry_compilers::{ artifacts::{Settings, Source, Sources}, - compilers::{solc::SolcVersionManager, CompilerInput, CompilerVersionManager}, CompilerOutput, Solc, SolcInput, }; use foundry_config::{Config, SolcReq}; @@ -104,8 +103,6 @@ impl SessionSourceConfig { SolcReq::Version(Version::new(0, 8, 19)) }; - let vm = SolcVersionManager::default(); - match solc_req { SolcReq::Version(version) => { // Validate that the requested evm version is supported by the solc version @@ -118,15 +115,16 @@ impl SessionSourceConfig { } } - let solc = if let Ok(solc) = vm.get_installed(&version) { - solc - } else { - if self.foundry_config.offline { - eyre::bail!("can't install missing solc {version} in offline mode") - } - println!("{}", format!("Installing solidity version {version}...").green()); - vm.install(&version)? - }; + let solc = + if let Some(solc) = Solc::find_svm_installed_version(version.to_string())? { + solc + } else { + if self.foundry_config.offline { + eyre::bail!("can't install missing solc {version} in offline mode") + } + println!("{}", format!("Installing solidity version {version}...").green()); + Solc::blocking_install(&version)? + }; Ok(solc) } SolcReq::Local(solc) => { @@ -329,9 +327,10 @@ impl SessionSource { }; // we only care about the solidity source, so we can safely unwrap - SolcInput::build(sources, settings, &self.solc.version) + SolcInput::resolve_and_build(sources, settings) .into_iter() .next() + .map(|i| i.sanitized(&self.solc.version)) .expect("Solidity source not found") } diff --git a/crates/cli/src/opts/build/core.rs b/crates/cli/src/opts/build/core.rs index 99caaea35..85575bccc 100644 --- a/crates/cli/src/opts/build/core.rs +++ b/crates/cli/src/opts/build/core.rs @@ -3,7 +3,8 @@ use crate::{opts::CompilerArgs, utils::LoadConfig}; use clap::{Parser, ValueHint}; use eyre::Result; use foundry_compilers::{ - artifacts::RevertStrings, remappings::Remapping, utils::canonicalized, Project, + artifacts::RevertStrings, compilers::multi::MultiCompiler, remappings::Remapping, + utils::canonicalized, Project, }; use foundry_config::{ figment, @@ -132,7 +133,7 @@ impl CoreBuildArgs { /// This loads the `foundry_config::Config` for the current workspace (see /// [`utils::find_project_root_path`] and merges the cli `BuildArgs` into it before returning /// [`foundry_config::Config::project()`] - pub fn project(&self) -> Result { + pub fn project(&self) -> Result> { let config = self.try_load_config_emit_warnings()?; Ok(config.project()?) } diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index 329e53213..8e8cff368 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -6,15 +6,11 @@ use eyre::{Context, Result}; use foundry_block_explorers::contract::Metadata; use foundry_compilers::{ artifacts::{BytecodeObject, ContractBytecodeSome, Libraries}, - compilers::{ - solc::SolcVersionManager, vyper::parser::VyperParsedSource, Compiler, - CompilerVersionManager, - }, + compilers::{solc::SolcCompiler, Compiler}, remappings::Remapping, report::{BasicStdoutReporter, NoReporter, Report}, - resolver::{parse::SolData, GraphEdges}, - Artifact, ArtifactId, CompilerConfig, FileFilter, Project, ProjectCompileOutput, - ProjectPathsConfig, SolcConfig, SolcSparseFileFilter, SparseOutputFileFilter, + Artifact, ArtifactId, FileFilter, Project, ProjectBuilder, ProjectCompileOutput, + ProjectPathsConfig, Solc, SolcConfig, SparseOutputFileFilter, }; use foundry_linking::Linker; use num_format::{Locale, ToFormattedString}; @@ -521,7 +517,10 @@ pub async fn compile_from_source( } /// Creates a [Project] from an Etherscan source. -pub fn etherscan_project(metadata: &Metadata, target_path: impl AsRef) -> Result { +pub fn etherscan_project( + metadata: &Metadata, + target_path: impl AsRef, +) -> Result> { let target_path = dunce::canonicalize(target_path.as_ref())?; let sources_path = target_path.join(&metadata.contract_name); metadata.source_tree().write_to(&target_path)?; @@ -553,17 +552,16 @@ pub fn etherscan_project(metadata: &Metadata, target_path: impl AsRef) -> .build_with_root(sources_path); let v = metadata.compiler_version()?; - let vm = SolcVersionManager::default(); - let solc = vm.get_or_install(&v)?; + let solc = Solc::find_or_install(&v)?; - let compiler_config = CompilerConfig::Specific(solc); + let compiler = SolcCompiler::Specific(solc); - Ok(Project::builder() + Ok(ProjectBuilder::::default() .settings(SolcConfig::builder().settings(settings).build().settings) .paths(paths) .ephemeral() .no_artifacts() - .build(compiler_config)?) + .build(compiler)?) } /// Bundles multiple `SkipBuildFilter` into a single `FileFilter` @@ -595,22 +593,6 @@ impl FileFilter for &SkipBuildFilters { } } -impl SparseOutputFileFilter for SkipBuildFilters { - fn sparse_sources(&self, file: &Path, graph: &GraphEdges) -> Vec { - SolcSparseFileFilter::new(self).sparse_sources(file, graph) - } -} - -impl SparseOutputFileFilter for SkipBuildFilters { - fn sparse_sources(&self, file: &Path, _graph: &GraphEdges) -> Vec { - if self.is_match(file) { - vec![file.to_path_buf()] - } else { - vec![] - } - } -} - impl SkipBuildFilters { /// Creates a new `SkipBuildFilters` from multiple `SkipBuildFilter`. pub fn new( diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index f2957c6a4..cc2cfc1b6 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -22,14 +22,14 @@ use foundry_compilers::{ }, cache::SOLIDITY_FILES_CACHE_FILENAME, compilers::{ - solc::SolcVersionManager, + multi::{MultiCompiler, MultiCompilerSettings}, + solc::SolcCompiler, vyper::{Vyper, VyperSettings}, - Compiler, CompilerVersionManager, + Compiler, }, error::SolcError, remappings::{RelativeRemapping, Remapping}, - CompilerConfig, ConfigurableArtifacts, EvmVersion, Project, ProjectBuilder, ProjectPathsConfig, - Solc, SolcConfig, + ConfigurableArtifacts, EvmVersion, Project, ProjectPathsConfig, Solc, }; use inflector::Inflector; use regex::Regex; @@ -42,7 +42,6 @@ use std::{ fs, path::{Path, PathBuf}, str::FromStr, - sync::Arc, }; // Macros useful for creating a figment. @@ -193,6 +192,8 @@ pub struct Config { /// **Note** for backwards compatibility reasons this also accepts solc_version from the toml /// file, see [`BackwardsCompatProvider`] pub solc: Option, + /// The Vyper instance to use if any. + pub vyper: Option, /// whether to autodetect the solc compiler version to use pub auto_detect_solc: bool, /// Offline mode, if set, network access (downloading solc) is disallowed. @@ -765,55 +766,22 @@ impl Config { /// let config = Config::load_with_root(".").sanitized(); /// let project = config.project(); /// ``` - pub fn project(&self) -> Result { + pub fn project(&self) -> Result, SolcError> { self.create_project(self.cache, false) } - /// Configures [Project] with [Vyper] compiler. - pub fn vyper_project(&self) -> Result, SolcError> { - self.create_vyper_project(self.cache, false) - } - /// Same as [`Self::project()`] but sets configures the project to not emit artifacts and ignore /// cache. - pub fn ephemeral_no_artifacts_project(&self) -> Result { + pub fn ephemeral_no_artifacts_project(&self) -> Result, SolcError> { self.create_project(false, true) } /// Creates a [Project] with the given `cached` and `no_artifacts` flags pub fn create_project(&self, cached: bool, no_artifacts: bool) -> Result { - let compiler_config = self.solc_config()?; - let settings = SolcConfig::builder().settings(self.solc_settings()?).build().settings; - - self.create_project_with_compiler(cached, no_artifacts, compiler_config, settings) - } - - /// Creates a [Project] with the given `cached` and `no_artifacts` flags - pub fn create_vyper_project( - &self, - cached: bool, - no_artifacts: bool, - ) -> Result, SolcError> { - self.create_project_with_compiler( - cached, - no_artifacts, - self.vyper_config()?, - self.vyper_settings()?, - ) - } - - /// Creates a [Project] with a given [CompilerConfig]. - pub fn create_project_with_compiler( - &self, - cached: bool, - no_artifacts: bool, - compiler_config: CompilerConfig, - settings: C::Settings, - ) -> Result, SolcError> { - let project = ProjectBuilder::::new(Default::default()) + let project = Project::builder() .artifacts(self.configured_artifacts_handler()) .paths(self.project_paths()) - .settings(settings) + .settings(self.compiler_settings()?) .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 { @@ -825,7 +793,7 @@ impl Config { .set_cached(cached && !self.build_info) .set_build_info(!no_artifacts && self.build_info) .set_no_artifacts(no_artifacts) - .build(compiler_config)?; + .build(self.compiler()?)?; if self.force { self.cleanup(&project)?; @@ -861,10 +829,9 @@ impl Config { /// If `solc` is [`SolcReq::Local`] then this will ensure that the path exists. fn ensure_solc(&self) -> Result, SolcError> { if let Some(ref solc) = self.solc { - let version_manager = SolcVersionManager::default(); let solc = match solc { SolcReq::Version(version) => { - if let Ok(solc) = version_manager.get_installed(version) { + if let Some(solc) = Solc::find_svm_installed_version(version.to_string())? { solc } else { if self.offline { @@ -872,7 +839,7 @@ impl Config { "can't install missing solc {version} in offline mode" ))) } - version_manager.install(version)? + Solc::blocking_install(version)? } } SolcReq::Local(solc) => { @@ -953,21 +920,33 @@ impl Config { } /// Returns configuration for a compiler to use when setting up a [Project]. - pub fn solc_config(&self) -> Result, SolcError> { + pub fn solc_compiler(&self) -> Result { if let Some(solc) = self.ensure_solc()? { - Ok(CompilerConfig::Specific(solc)) + Ok(SolcCompiler::Specific(solc)) } else { - Ok(CompilerConfig::AutoDetect(Arc::new(SolcVersionManager::default()))) + Ok(SolcCompiler::AutoDetect) } } - /// Returns configuration for a compiler to use when setting up a [Project]. - pub fn vyper_config(&self) -> Result, SolcError> { - if let Some(SolcReq::Local(path)) = &self.solc { - Ok(CompilerConfig::Specific(Vyper::new(path)?)) + /// Returns configured [Vyper] compiler. + pub fn vyper_compiler(&self) -> Result, SolcError> { + let vyper = if let Some(path) = &self.vyper { + Some(Vyper::new(path)?) } else { - Ok(CompilerConfig::Specific(Vyper::new("vyper")?)) - } + Vyper::new("vyper").ok() + }; + + Ok(vyper) + } + + /// Returns configuration for a compiler to use when setting up a [Project]. + pub fn compiler(&self) -> Result { + Ok(MultiCompiler { solc: self.solc_compiler()?, vyper: self.vyper_compiler()? }) + } + + /// Returns configured [MultiCompilerSettings]. + pub fn compiler_settings(&self) -> Result { + Ok(MultiCompilerSettings { solc: self.solc_settings()?, vyper: self.vyper_settings()? }) } /// Returns all configured [`Remappings`] @@ -2017,6 +1996,7 @@ impl Default for Config { gas_reports: vec!["*".to_string()], gas_reports_ignore: vec![], solc: None, + vyper: None, auto_detect_solc: true, offline: false, optimizer: true, @@ -2782,23 +2762,6 @@ fn canonic(path: impl Into) -> PathBuf { foundry_compilers::utils::canonicalize(&path).unwrap_or(path) } -/// Executes the given closure with a [Project] configured via the given [Config]. -#[macro_export] -macro_rules! with_resolved_project { - ($config:ident, |$prj:ident| $e:expr) => { - match $config.lang { - foundry_config::Language::Solidity => { - let $prj = $config.project(); - $e - } - foundry_config::Language::Vyper => { - let $prj = $config.vyper_project(); - $e - } - } - }; -} - #[cfg(test)] mod tests { use super::*; diff --git a/crates/forge/bin/cmd/build.rs b/crates/forge/bin/cmd/build.rs index c4bbbb8a2..c2fc8088f 100644 --- a/crates/forge/bin/cmd/build.rs +++ b/crates/forge/bin/cmd/build.rs @@ -3,9 +3,7 @@ use clap::Parser; use eyre::Result; use foundry_cli::{opts::CoreBuildArgs, utils::LoadConfig}; use foundry_common::compile::{ProjectCompiler, SkipBuildFilter, SkipBuildFilters}; -use foundry_compilers::{ - compilers::Compiler, Project, ProjectCompileOutput, SparseOutputFileFilter, -}; +use foundry_compilers::{Project, ProjectCompileOutput}; use foundry_config::{ figment::{ self, @@ -13,7 +11,7 @@ use foundry_config::{ value::{Dict, Map, Value}, Metadata, Profile, Provider, }, - with_resolved_project, Config, + Config, }; use serde::Serialize; use watchexec::config::{InitConfig, RuntimeConfig}; @@ -77,7 +75,7 @@ pub struct BuildArgs { } impl BuildArgs { - pub fn run(self) -> Result<()> { + pub fn run(self) -> Result { let mut config = self.try_load_config_emit_warnings()?; if install::install_missing_dependencies(&mut config, self.args.silent) && @@ -87,43 +85,20 @@ impl BuildArgs { config = self.load_config(); } - with_resolved_project!(config, |project| { - let project = project?; - - let filter = if let Some(ref skip) = self.skip { - if !skip.is_empty() { - let filter = SkipBuildFilters::new(skip.clone(), project.root().clone())?; - Some(filter) - } else { - None - } - } else { - None - }; - - self.run_with_project(project, filter)?; - }); + let project = config.project()?; - Ok(()) - } - - pub fn run_with_project( - &self, - project: Project, - filter: Option + 'static>, - ) -> Result> - where - C::CompilationError: Clone, - { let mut compiler = ProjectCompiler::new() .print_names(self.names) .print_sizes(self.sizes) .quiet(self.format_json) .bail(!self.format_json); - if let Some(filter) = filter { - compiler = compiler.filter(Box::new(filter)); - } + if let Some(ref skip) = self.skip { + if !skip.is_empty() { + let filter = SkipBuildFilters::new(skip.clone(), project.root().clone())?; + compiler = compiler.filter(Box::new(filter)); + } + }; let output = compiler.compile(&project)?; diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index 22dbab67c..2fcfbb1dc 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -97,7 +97,7 @@ impl CoverageArgs { /// Builds the project. fn build(&self, config: &Config) -> Result<(Project, ProjectCompileOutput)> { // Set up the project - let mut project = config.ephemeral_no_artifacts_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 ? @@ -124,12 +124,12 @@ impl CoverageArgs { // https://github.com/ethereum/solidity/issues/12533#issuecomment-1013073350 // And also in new releases of solidity: // https://github.com/ethereum/solidity/issues/13972#issuecomment-1628632202 - project.settings = project.settings.with_via_ir_minimum_optimization() + project.settings.solc = project.settings.solc.with_via_ir_minimum_optimization() } else { - project.settings.optimizer.disable(); - project.settings.optimizer.runs = None; - project.settings.optimizer.details = None; - project.settings.via_ir = None; + project.settings.solc.optimizer.disable(); + project.settings.solc.optimizer.runs = None; + project.settings.solc.optimizer.details = None; + project.settings.solc.via_ir = None; } let output = ProjectCompiler::default() diff --git a/crates/forge/bin/cmd/flatten.rs b/crates/forge/bin/cmd/flatten.rs index c1351d06d..0c9c6f23a 100644 --- a/crates/forge/bin/cmd/flatten.rs +++ b/crates/forge/bin/cmd/flatten.rs @@ -5,7 +5,7 @@ use foundry_cli::{ utils::LoadConfig, }; use foundry_common::{compile::compile_target, fs}; -use foundry_compilers::{error::SolcError, flatten::Flattener}; +use foundry_compilers::{compilers::solc::SolcLanguage, error::SolcError, flatten::Flattener}; use std::path::PathBuf; /// CLI arguments for `forge flatten`. @@ -39,7 +39,7 @@ impl FlattenArgs { let mut config = build_args.try_load_config_emit_warnings()?; // `Flattener` uses the typed AST for better flattening results. config.ast = true; - let project = config.ephemeral_no_artifacts_project()?; + let project = config.create_project(false, true)?; let target_path = dunce::canonicalize(target_path)?; let compiler_output = compile_target(&target_path, &project, false); @@ -52,7 +52,7 @@ impl FlattenArgs { // Fallback to the old flattening implementation if we couldn't compile the target // successfully. This would be the case if the target has invalid // syntax. (e.g. Solang) - project.paths.flatten(&target_path) + project.paths.clone().with_language::().flatten(&target_path) } } .map_err(|err: SolcError| eyre::eyre!("Failed to flatten: {err}"))?; diff --git a/crates/forge/bin/cmd/fmt.rs b/crates/forge/bin/cmd/fmt.rs index 71ac7ceb0..b3207bcb7 100644 --- a/crates/forge/bin/cmd/fmt.rs +++ b/crates/forge/bin/cmd/fmt.rs @@ -3,7 +3,7 @@ use eyre::Result; use forge_fmt::{format_to, parse, print_diagnostics_report}; use foundry_cli::utils::{FoundryPathExt, LoadConfig}; use foundry_common::{fs, glob::expand_globs, term::cli_warn}; -use foundry_compilers::{Solc, SOLC_EXTENSIONS}; +use foundry_compilers::{compilers::solc::SolcLanguage, SOLC_EXTENSIONS}; use foundry_config::impl_figment_convert_basic; use rayon::prelude::*; use similar::{ChangeTag, TextDiff}; @@ -60,7 +60,7 @@ impl FmtArgs { [] => { // Retrieve the project paths, and filter out the ignored ones. let project_paths: Vec = config - .project_paths::() + .project_paths::() .input_files_iter() .filter(|p| !(ignored.contains(p) || ignored.contains(&cwd.join(p)))) .collect(); diff --git a/crates/forge/bin/cmd/geiger/mod.rs b/crates/forge/bin/cmd/geiger/mod.rs index 0b08f2d0d..451c29dbd 100644 --- a/crates/forge/bin/cmd/geiger/mod.rs +++ b/crates/forge/bin/cmd/geiger/mod.rs @@ -1,7 +1,7 @@ use clap::{Parser, ValueHint}; use eyre::{Result, WrapErr}; use foundry_cli::utils::LoadConfig; -use foundry_compilers::{resolver::parse::SolData, Graph, Solc}; +use foundry_compilers::{resolver::parse::SolData, Graph}; use foundry_config::{impl_figment_convert_basic, Config}; use itertools::Itertools; use rayon::prelude::*; @@ -62,7 +62,7 @@ impl GeigerArgs { let mut sources: Vec = { if self.paths.is_empty() { - Graph::::resolve(&config.project_paths::())? + Graph::::resolve(&config.project_paths())? .files() .keys() .cloned() diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 03d6266d7..a441b8e66 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -20,8 +20,9 @@ use foundry_common::{ shell, }; use foundry_compilers::{ - artifacts::output_selection::OutputSelection, utils::source_files_iter, SolcSparseFileFilter, - SOLC_EXTENSIONS, + artifacts::output_selection::OutputSelection, + compilers::{multi::MultiCompilerLanguage, CompilerSettings, Language}, + utils::source_files_iter, }; use foundry_config::{ figment, @@ -157,10 +158,11 @@ impl TestArgs { filter: &ProjectPathsAwareFilter, ) -> Result> { let mut project = config.create_project(true, true)?; - project.settings.output_selection = - OutputSelection::common_output_selection(["abi".to_string()]); + project.settings.update_output_selection(|selection| { + *selection = OutputSelection::common_output_selection(["abi".to_string()]); + }); - let output = project.compile_sparse(Box::new(SolcSparseFileFilter::new(filter.clone())))?; + let output = project.compile_sparse(Box::new(filter.clone()))?; if output.has_compiler_errors() { println!("{}", output); @@ -210,7 +212,10 @@ impl TestArgs { } // Always recompile all sources to ensure that `getCode` cheatcode can use any artifact. - test_sources.extend(source_files_iter(project.paths.sources, SOLC_EXTENSIONS)); + test_sources.extend(source_files_iter( + project.paths.sources, + MultiCompilerLanguage::FILE_EXTENSIONS, + )); Ok(test_sources) } diff --git a/crates/forge/bin/cmd/tree.rs b/crates/forge/bin/cmd/tree.rs index e2f958034..088975d87 100644 --- a/crates/forge/bin/cmd/tree.rs +++ b/crates/forge/bin/cmd/tree.rs @@ -3,7 +3,7 @@ use eyre::Result; use foundry_cli::{opts::ProjectPathsArgs, utils::LoadConfig}; use foundry_compilers::{ resolver::{parse::SolData, Charset, TreeOptions}, - Graph, Solc, + Graph, }; /// CLI arguments for `forge tree`. @@ -28,7 +28,7 @@ foundry_config::impl_figment_convert!(TreeArgs, opts); impl TreeArgs { pub fn run(self) -> Result<()> { let config = self.try_load_config_emit_warnings()?; - let graph = Graph::::resolve(&config.project_paths::())?; + let graph = Graph::::resolve(&config.project_paths())?; let opts = TreeOptions { charset: self.charset, no_dedupe: self.no_dedupe }; graph.print_with_options(opts); diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 4e671a77a..8508abbe5 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -313,10 +313,10 @@ impl MultiContractRunnerBuilder { /// Given an EVM, proceeds to return a runner which is able to execute all tests /// against that evm - pub fn build( + pub fn build( self, root: &Path, - output: ProjectCompileOutput, + output: ProjectCompileOutput, env: revm::primitives::Env, evm_opts: EvmOpts, ) -> Result { diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index f463a437e..e8812dce1 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -4,7 +4,7 @@ use alloy_primitives::{Address, B256, U256}; use foundry_cli::utils as forge_utils; use foundry_compilers::{ artifacts::{BytecodeHash, OptimizerDetails, RevertStrings, YulDetails}, - compilers::{solc::SolcVersionManager, CompilerVersionManager}, + Solc, }; use foundry_config::{ cache::{CachedChains, CachedEndpoints, StorageCachingConfig}, @@ -43,6 +43,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { gas_reports: vec!["Contract".to_string()], gas_reports_ignore: vec![], solc: Some(SolcReq::Local(PathBuf::from("custom-solc"))), + vyper: Some(PathBuf::from("custom-vyper")), auto_detect_solc: false, auto_detect_remappings: true, offline: true, @@ -365,8 +366,7 @@ contract Foo {} assert!(cmd.stderr_lossy().contains("`solc` this/solc/does/not/exist does not exist")); // `OTHER_SOLC_VERSION` was installed in previous step, so we can use the path to this directly - let local_solc = - SolcVersionManager::default().get_or_install(&OTHER_SOLC_VERSION.parse().unwrap()).unwrap(); + let local_solc = Solc::find_or_install(&OTHER_SOLC_VERSION.parse().unwrap()).unwrap(); cmd.forge_fuse().args(["build", "--force", "--use"]).arg(local_solc.solc).root_arg(); let stdout = cmd.stdout_lossy(); assert!(stdout.contains("Compiler run successful")); diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 22e928a43..c9f76a3aa 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -266,6 +266,7 @@ contract ContractTest is DSTest { // tests that libraries are handled correctly in multiforking mode forgetest_init!(can_use_libs_in_multi_fork, |prj, cmd| { prj.wipe_contracts(); + prj.add_source( "Contract.sol", r" diff --git a/crates/script/src/verify.rs b/crates/script/src/verify.rs index 6437e9a5d..11dab9575 100644 --- a/crates/script/src/verify.rs +++ b/crates/script/src/verify.rs @@ -112,10 +112,12 @@ 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")) { + warn!("Skipping verification of Vyper contract: {}", artifact.name); + } + let contract = ContractInfo { - path: Some( - artifact.source.to_str().expect("There should be an artifact.").to_string(), - ), + path: Some(artifact.source.to_string_lossy().to_string()), name: artifact.name.clone(), }; diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index 37b927e19..0ada068fd 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -3,9 +3,10 @@ use eyre::{Result, WrapErr}; use foundry_compilers::{ artifacts::Settings, cache::CompilerCache, + compilers::multi::MultiCompiler, error::Result as SolcResult, project_util::{copy_dir, TempProject}, - ArtifactOutput, ConfigurableArtifacts, PathStyle, ProjectPathsConfig, Solc, + ArtifactOutput, ConfigurableArtifacts, PathStyle, ProjectPathsConfig, }; use foundry_config::Config; use once_cell::sync::Lazy; @@ -409,7 +410,7 @@ pub struct TestProject { /// The directory in which this test executable is running. exe_root: PathBuf, /// The project in which the test should run. - inner: Arc>, + inner: Arc>, } impl TestProject { diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index 829efd426..8a2a49f9c 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -15,7 +15,7 @@ use foundry_common::{ use foundry_compilers::{ artifacts::{BytecodeHash, BytecodeObject, CompactContractBytecode}, info::ContractInfo, - Artifact, EvmVersion, SolcSparseFileFilter, + Artifact, EvmVersion, }; use foundry_config::{figment, impl_figment_convert, Chain, Config}; use foundry_evm::{ @@ -378,10 +378,7 @@ impl VerifyBytecodeArgs { if let Some(skip) = &self.skip { if !skip.is_empty() { - let filter = SolcSparseFileFilter::new(SkipBuildFilters::new( - skip.to_owned(), - project.root().to_path_buf(), - )?); + let filter = SkipBuildFilters::new(skip.to_owned(), project.root().to_path_buf())?; compiler = compiler.filter(Box::new(filter)); } } diff --git a/crates/verify/src/etherscan/flatten.rs b/crates/verify/src/etherscan/flatten.rs index cebdfbd22..1542f3a0e 100644 --- a/crates/verify/src/etherscan/flatten.rs +++ b/crates/verify/src/etherscan/flatten.rs @@ -4,8 +4,11 @@ use eyre::{Context, Result}; use foundry_block_explorers::verify::CodeFormat; use foundry_compilers::{ artifacts::{BytecodeHash, Source}, - compilers::{solc::SolcVersionManager, Compiler, CompilerVersionManager}, - AggregatedCompilerOutput, SolcInput, + compilers::{ + solc::{SolcCompiler, SolcLanguage, SolcVersionedInput}, + Compiler, CompilerInput, + }, + AggregatedCompilerOutput, Solc, }; use semver::{BuildMetadata, Version}; use std::{collections::BTreeMap, path::Path}; @@ -18,7 +21,7 @@ impl EtherscanSourceProvider for EtherscanFlattenedSource { args: &VerifyArgs, context: &VerificationContext, ) -> Result<(String, String, CodeFormat)> { - let metadata = context.project.settings.metadata.as_ref(); + let metadata = context.project.settings.solc.metadata.as_ref(); let bch = metadata.and_then(|m| m.bytecode_hash).unwrap_or_default(); eyre::ensure!( @@ -27,8 +30,13 @@ impl EtherscanSourceProvider for EtherscanFlattenedSource { bch, ); - let source = - context.project.flatten(&context.target_path).wrap_err("Failed to flatten contract")?; + let source = context + .project + .paths + .clone() + .with_language::() + .flatten(&context.target_path) + .wrap_err("Failed to flatten contract")?; if !args.force { // solc dry run of flattened code @@ -67,17 +75,17 @@ impl EtherscanFlattenedSource { version: &Version, contract_path: &Path, ) -> Result<()> { - let vm = SolcVersionManager::default(); let version = strip_build_meta(version.clone()); - let solc = vm.get_or_install(&version)?; + let solc = Solc::find_or_install(&version)?; - let input = SolcInput { - language: "Solidity".to_string(), - sources: BTreeMap::from([("contract.sol".into(), Source::new(content))]), - settings: Default::default(), - }; + let input = SolcVersionedInput::build( + BTreeMap::from([("contract.sol".into(), Source::new(content))]), + Default::default(), + SolcLanguage::Solidity, + version.clone(), + ); - let out = Compiler::compile(&solc, &input)?; + let out = SolcCompiler::Specific(solc).compile(&input)?; if out.errors.iter().any(|e| e.is_error()) { let mut o = AggregatedCompilerOutput::default(); o.extend(version, out); diff --git a/crates/verify/src/provider.rs b/crates/verify/src/provider.rs index 210283c3a..cc65078d3 100644 --- a/crates/verify/src/provider.rs +++ b/crates/verify/src/provider.rs @@ -8,8 +8,8 @@ use eyre::{OptionExt, Result}; use foundry_common::compile::ProjectCompiler; use foundry_compilers::{ artifacts::{output_selection::OutputSelection, Metadata, Source}, - compilers::{solc::SolcVersionManager, CompilerVersionManager}, - CompilerConfig, Graph, Project, + compilers::{multi::MultiCompilerParsedSource, solc::SolcCompiler, CompilerSettings}, + Graph, Project, Solc, }; use foundry_config::Config; use semver::Version; @@ -35,10 +35,8 @@ impl VerificationContext { let mut project = config.project()?; project.no_artifacts = true; - // Set project's compiler to always use resolved version. - let vm = SolcVersionManager::default(); - let solc = vm.get_or_install(&compiler_version)?; - project.compiler_config = CompilerConfig::Specific(solc); + let solc = Solc::find_or_install(&compiler_version)?; + project.compiler.solc = SolcCompiler::Specific(solc); Ok(Self { config, project, target_name, target_path, compiler_version }) } @@ -46,8 +44,9 @@ impl VerificationContext { /// Compiles target contract requesting only ABI and returns it. pub fn get_target_abi(&self) -> Result { let mut project = self.project.clone(); - project.settings.output_selection = - OutputSelection::common_output_selection(["abi".to_string()]); + project.settings.update_output_selection(|selection| { + *selection = OutputSelection::common_output_selection(["abi".to_string()]) + }); let output = ProjectCompiler::new() .quiet(true) @@ -64,8 +63,9 @@ 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.output_selection = - OutputSelection::common_output_selection(["metadata".to_string()]); + project.settings.update_output_selection(|selection| { + *selection = OutputSelection::common_output_selection(["metadata".to_string()]); + }); let output = ProjectCompiler::new() .quiet(true) @@ -83,7 +83,8 @@ impl VerificationContext { pub fn get_target_imports(&self) -> Result> { let mut sources = self.project.paths.read_input_files()?; sources.insert(self.target_path.clone(), Source::read(&self.target_path)?); - let graph = Graph::resolve_sources(&self.project.paths, sources)?; + let graph = + Graph::::resolve_sources(&self.project.paths, sources)?; Ok(graph.imports(&self.target_path).into_iter().cloned().collect()) } From 8c7e28a0c1047d1fa43be9c31d7ce38a2dff07fd Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 4 Jun 2024 09:54:51 +0300 Subject: [PATCH 338/430] perf(fuzz): store dict values once, track newly inserted indexes (#8043) * perf(fuzz): store values only once, track newly inserted indexes * Update crates/evm/fuzz/src/strategies/state.rs Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * Update crates/evm/fuzz/src/strategies/state.rs Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- crates/evm/fuzz/src/strategies/state.rs | 40 ++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/crates/evm/fuzz/src/strategies/state.rs b/crates/evm/fuzz/src/strategies/state.rs index a138f4507..c469e2910 100644 --- a/crates/evm/fuzz/src/strategies/state.rs +++ b/crates/evm/fuzz/src/strategies/state.rs @@ -93,10 +93,10 @@ pub struct FuzzDictionary { addresses: IndexSet
, /// Configuration for the dictionary. config: FuzzDictionaryConfig, - /// New keys added to the dictionary since container initialization. - new_values: IndexSet<[u8; 32]>, - /// New addresses added to the dictionary since container initialization. - new_addreses: IndexSet
, + /// New key indexes added to the dictionary since container initialization. + new_values: Vec, + /// New address indexes added to the dictionary since container initialization. + new_addreses: Vec, /// Sample typed values that are collected from call result and used across invariant runs. sample_values: HashMap>, } @@ -288,24 +288,24 @@ impl FuzzDictionary { } /// Insert address into fuzz dictionary. - /// If address is newly collected then it is removed at the end of current run. + /// If address is newly collected then it is removed by index at the end of current run. fn insert_address(&mut self, address: Address, collected: bool) { - if self.addresses.len() < self.config.max_fuzz_dictionary_addresses && - self.addresses.insert(address) && - collected - { - self.new_addreses.insert(address); + if self.addresses.len() < self.config.max_fuzz_dictionary_addresses { + let (index, new_address) = self.addresses.insert_full(address); + if new_address && collected { + self.new_addreses.push(index); + } } } /// Insert raw value into fuzz dictionary. - /// If value is newly collected then it is removed at the end of current run. + /// If value is newly collected then it is removed by index at the end of current run. fn insert_value(&mut self, value: [u8; 32], collected: bool) { - if self.state_values.len() < self.config.max_fuzz_dictionary_values && - self.state_values.insert(value) && - collected - { - self.new_values.insert(value); + if self.state_values.len() < self.config.max_fuzz_dictionary_values { + let (index, new_value) = self.state_values.insert_full(value); + if new_value && collected { + self.new_values.push(index); + } } } @@ -354,11 +354,11 @@ impl FuzzDictionary { pub fn revert(&mut self) { // Revert new values collected during the run. - for key in self.new_values.iter() { - self.state_values.swap_remove(key); + for &value_index in &self.new_values { + self.state_values.swap_remove_index(value_index); } - for address in self.new_addreses.iter() { - self.addresses.swap_remove(address); + for &address_index in &self.new_addreses { + self.addresses.swap_remove_index(address_index); } self.new_values.clear(); From 4f30c3fdd758bc7ff82b67a26949fb27b4a45f11 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:49:13 +0200 Subject: [PATCH 339/430] perf(invariant): only compute success when necessary (#8045) --- crates/evm/evm/src/executors/invariant/result.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/crates/evm/evm/src/executors/invariant/result.rs b/crates/evm/evm/src/executors/invariant/result.rs index 12d745de5..f4ba88e2a 100644 --- a/crates/evm/evm/src/executors/invariant/result.rs +++ b/crates/evm/evm/src/executors/invariant/result.rs @@ -110,13 +110,14 @@ pub(crate) fn can_continue( ) -> Result { let mut call_results = None; - // Detect handler assertion failures first. - let handlers_failed = targeted_contracts.targets.lock().iter().any(|contract| { - !executor.is_success(*contract.0, false, Cow::Borrowed(state_changeset), false) - }); + let handlers_succeeded = || { + targeted_contracts.targets.lock().iter().all(|(address, ..)| { + executor.is_success(*address, false, Cow::Borrowed(state_changeset), false) + }) + }; - // Assert invariants IF the call did not revert and the handlers did not fail. - if !call_result.reverted && !handlers_failed { + // Assert invariants if the call did not revert and the handlers did not fail. + if !call_result.reverted && handlers_succeeded() { if let Some(traces) = call_result.traces { run_traces.push(traces); } From 2466e31e73180b4c5a4dcecf2e7c36ff692e09f8 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 4 Jun 2024 14:51:24 +0300 Subject: [PATCH 340/430] fix(forge) - reset lib deployer balance to initial (#8046) * fix(forge) - reset lib deployer balance to initial * Update crates/forge/src/runner.rs Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- crates/forge/src/runner.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 72f8b03c4..7c05b573c 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -160,9 +160,10 @@ impl<'a> ContractRunner<'a> { } }; - // Reset `self.sender`s and `CALLER`s balance to the initial balance we want + // Reset `self.sender`s, `CALLER`s and `LIBRARY_DEPLOYER`'s balance to the initial balance. self.executor.set_balance(self.sender, self.initial_balance)?; self.executor.set_balance(CALLER, self.initial_balance)?; + self.executor.set_balance(LIBRARY_DEPLOYER, self.initial_balance)?; self.executor.deploy_create2_deployer()?; From a4ebf4143d5ee5501851a761ad7d9b268dc1dedd Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:54:34 +0200 Subject: [PATCH 341/430] perf: manually set code hash when inserting cheatcodes account (#8044) --- crates/anvil/src/eth/backend/mem/mod.rs | 2 +- crates/evm/core/src/backend/in_memory_db.rs | 1 + crates/evm/core/src/backend/mod.rs | 35 +++++++++++++------ crates/evm/core/src/constants.rs | 12 +++++-- crates/evm/core/src/utils.rs | 14 +++++++- crates/evm/evm/src/executors/invariant/mod.rs | 2 +- crates/evm/evm/src/executors/mod.rs | 6 +++- 7 files changed, 55 insertions(+), 17 deletions(-) diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 25aa47cef..c705ec376 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -839,7 +839,7 @@ impl Backend { let db = self.db.read().await; let mut inspector = Inspector::default(); - let mut evm = self.new_evm_with_inspector_ref(&*db, env, &mut inspector); + let mut evm = self.new_evm_with_inspector_ref(&**db, env, &mut inspector); let ResultAndState { result, state } = evm.transact()?; let (exit_reason, gas_used, out, logs) = match result { ExecutionResult::Success { reason, gas_used, logs, output, .. } => { diff --git a/crates/evm/core/src/backend/in_memory_db.rs b/crates/evm/core/src/backend/in_memory_db.rs index 3b19d0fce..a3cb3aab4 100644 --- a/crates/evm/core/src/backend/in_memory_db.rs +++ b/crates/evm/core/src/backend/in_memory_db.rs @@ -29,6 +29,7 @@ impl Default for MemDb { impl DatabaseRef for MemDb { type Error = DatabaseError; + fn basic_ref(&self, address: Address) -> Result, Self::Error> { DatabaseRef::basic_ref(&self.inner, address) } diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index 357a79f43..1e648491d 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -496,15 +496,11 @@ impl Backend { slot: U256, value: U256, ) -> Result<(), DatabaseError> { - let ret = if let Some(db) = self.active_fork_db_mut() { + if let Some(db) = self.active_fork_db_mut() { db.insert_account_storage(address, slot, value) } else { self.mem_db.insert_account_storage(address, slot, value) - }; - - debug_assert!(self.storage(address, slot).unwrap() == value); - - ret + } } /// Completely replace an account's storage without overriding account info. @@ -598,11 +594,11 @@ impl Backend { /// Instead, it stores whether an `assert` failed in a boolean variable that we can read pub fn is_failed_test_contract(&self, address: Address) -> bool { /* - contract DSTest { + contract DSTest { bool public IS_TEST = true; // slot 0 offset 1 => second byte of slot0 bool private _failed; - } + } */ let value = self.storage_ref(address, U256::ZERO).unwrap_or_default(); value.as_le_bytes()[1] != 0 @@ -670,9 +666,8 @@ impl Backend { active_journaled_state: &mut JournaledState, target_fork: &mut Fork, ) { - if let Some((_, fork_idx)) = self.active_fork_ids.as_ref() { - let active = self.inner.get_fork(*fork_idx); - merge_account_data(accounts, &active.db, active_journaled_state, target_fork) + if let Some(db) = self.active_fork_db() { + merge_account_data(accounts, db, active_journaled_state, target_fork) } else { merge_account_data(accounts, &self.mem_db, active_journaled_state, target_fork) } @@ -713,6 +708,24 @@ impl Backend { self.active_fork_mut().map(|f| &mut f.db) } + /// Returns the current database implementation as a `&dyn` value. + #[inline(always)] + pub fn db(&self) -> &dyn Database { + match self.active_fork_db() { + Some(fork_db) => fork_db, + None => &self.mem_db, + } + } + + /// Returns the current database implementation as a `&mut dyn` value. + #[inline(always)] + pub fn db_mut(&mut self) -> &mut dyn Database { + match self.active_fork_ids.map(|(_, idx)| &mut self.inner.get_fork_mut(idx).db) { + Some(fork_db) => fork_db, + None => &mut self.mem_db, + } + } + /// Creates a snapshot of the currently active database pub(crate) fn create_db_snapshot(&self) -> BackendDatabaseSnapshot { if let Some((id, idx)) = self.active_fork_ids { diff --git a/crates/evm/core/src/constants.rs b/crates/evm/core/src/constants.rs index 3e8941d27..67fa2d338 100644 --- a/crates/evm/core/src/constants.rs +++ b/crates/evm/core/src/constants.rs @@ -1,12 +1,20 @@ -use alloy_primitives::{address, hex, Address}; +use alloy_primitives::{address, b256, hex, Address, B256}; /// The cheatcode handler address. /// /// This is the same address as the one used in DappTools's HEVM. -/// It is calculated as: +/// +/// This is calculated as: /// `address(bytes20(uint160(uint256(keccak256('hevm cheat code')))))` pub const CHEATCODE_ADDRESS: Address = address!("7109709ECfa91a80626fF3989D68f67F5b1DD12D"); +/// The contract hash at [`CHEATCODE_ADDRESS`]. +/// +/// This is calculated as: +/// `keccak256(abi.encodePacked(CHEATCODE_ADDRESS))`. +pub const CHEATCODE_CONTRACT_HASH: B256 = + b256!("b0450508e5a2349057c3b4c9c84524d62be4bb17e565dbe2df34725a26872291"); + /// The Hardhat console address. /// /// See: diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index d68a9d754..faacee68b 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -235,9 +235,21 @@ where DB: revm::Database, I: InspectorExt, { + let revm::primitives::EnvWithHandlerCfg { env, handler_cfg } = env; + // NOTE: We could use `revm::Evm::builder()` here, but on the current patch it has some // performance issues. - let revm::primitives::EnvWithHandlerCfg { env, handler_cfg } = env; + /* + revm::Evm::builder() + .with_db(db) + .with_env(env) + .with_external_context(inspector) + .with_handler_cfg(handler_cfg) + .append_handler_register(revm::inspector_handle_register) + .append_handler_register(create2_handler_register) + .build() + */ + let context = revm::Context::new(revm::EvmContext::new_with_env(db, env), inspector); let mut handler = revm::Handler::new(handler_cfg); handler.append_handler_register_plain(revm::inspector_handle_register); diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 9d4a9f4c0..c6f2a44ec 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -235,7 +235,7 @@ impl<'a> InvariantExecutor<'a> { // Collect data for fuzzing from the state changeset. let mut state_changeset = call_result.state_changeset.to_owned().unwrap(); - if !&call_result.reverted { + if !call_result.reverted { collect_data( &mut state_changeset, &targeted_contracts, diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 2e4c4b229..5c830c256 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -16,7 +16,8 @@ use alloy_sol_types::{sol, SolCall}; use foundry_evm_core::{ backend::{Backend, CowBackend, DatabaseError, DatabaseExt, DatabaseResult}, constants::{ - CALLER, CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, DEFAULT_CREATE2_DEPLOYER_CODE, + CALLER, CHEATCODE_ADDRESS, CHEATCODE_CONTRACT_HASH, DEFAULT_CREATE2_DEPLOYER, + DEFAULT_CREATE2_DEPLOYER_CODE, }, debug::DebugArena, decode::RevertDecoder, @@ -93,6 +94,9 @@ impl Executor { CHEATCODE_ADDRESS, revm::primitives::AccountInfo { code: Some(Bytecode::new_raw(Bytes::from_static(&[0]))), + // Also set the code hash manually so that it's not computed later. + // The code hash value does not matter, as long as it's not zero or `KECCAK_EMPTY`. + code_hash: CHEATCODE_CONTRACT_HASH, ..Default::default() }, ); From 23cb7e09a0bc8811ea3c2f50b5a5b0243575c7af Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 4 Jun 2024 16:17:41 +0200 Subject: [PATCH 342/430] chore(fuzz): some cleanup (#8050) --- crates/evm/core/src/utils.rs | 16 +++--- crates/evm/coverage/src/lib.rs | 10 ++-- crates/evm/evm/src/executors/fuzz/mod.rs | 21 +++----- .../evm/evm/src/executors/invariant/error.rs | 16 +++--- crates/evm/evm/src/executors/invariant/mod.rs | 53 +++++++------------ .../evm/evm/src/executors/invariant/replay.rs | 2 +- .../evm/evm/src/executors/invariant/result.rs | 4 +- .../evm/evm/src/executors/invariant/shrink.rs | 8 +-- crates/evm/fuzz/src/invariant/filters.rs | 10 +++- crates/forge/src/runner.rs | 2 +- 10 files changed, 65 insertions(+), 77 deletions(-) diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index faacee68b..230c068e6 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -1,9 +1,8 @@ pub use crate::ic::*; use crate::{constants::DEFAULT_CREATE2_DEPLOYER, InspectorExt}; use alloy_json_abi::{Function, JsonAbi}; -use alloy_primitives::{Address, FixedBytes, U256}; +use alloy_primitives::{Address, Selector, U256}; use alloy_rpc_types::{Block, Transaction}; -use eyre::ContextCompat; use foundry_config::NamedChain; use revm::{ db::WrapDatabaseRef, @@ -61,15 +60,14 @@ pub fn apply_chain_and_block_specific_env_changes(env: &mut revm::primitives::En } /// Given an ABI and selector, it tries to find the respective function. -pub fn get_function( +pub fn get_function<'a>( contract_name: &str, - selector: &FixedBytes<4>, - abi: &JsonAbi, -) -> eyre::Result { + selector: Selector, + abi: &'a JsonAbi, +) -> eyre::Result<&'a Function> { abi.functions() - .find(|func| func.selector().as_slice() == selector.as_slice()) - .cloned() - .wrap_err(format!("{contract_name} does not have the selector {selector:?}")) + .find(|func| func.selector() == selector) + .ok_or_else(|| eyre::eyre!("{contract_name} does not have the selector {selector}")) } /// Configures the env for the transaction diff --git a/crates/evm/coverage/src/lib.rs b/crates/evm/coverage/src/lib.rs index 53ca928f6..9496c0aca 100644 --- a/crates/evm/coverage/src/lib.rs +++ b/crates/evm/coverage/src/lib.rs @@ -155,15 +155,19 @@ impl CoverageReport { pub struct HitMaps(pub HashMap); impl HitMaps { - pub fn merge(mut self, other: HitMaps) -> Self { - for (code_hash, hit_map) in other.0.into_iter() { + pub fn merge(&mut self, other: HitMaps) { + for (code_hash, hit_map) in other.0 { if let Some(HitMap { hits: extra_hits, .. }) = self.insert(code_hash, hit_map) { - for (pc, hits) in extra_hits.into_iter() { + for (pc, hits) in extra_hits { self.entry(code_hash) .and_modify(|map| *map.hits.entry(pc).or_default() += hits); } } } + } + + pub fn merged(mut self, other: HitMaps) -> Self { + self.merge(other); self } } diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index 520a56cf2..d0ff89297 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -105,31 +105,26 @@ impl FuzzedExecutor { traces.borrow_mut().push(call_traces); } - if let Some(prev) = coverage.take() { - // Safety: If `Option::or` evaluates to `Some`, then `call.coverage` must - // necessarily also be `Some` - coverage.replace(Some(prev.merge(case.coverage.unwrap()))); - } else { - coverage.replace(case.coverage); + match &mut *coverage.borrow_mut() { + Some(prev) => prev.merge(case.coverage.unwrap()), + opt => *opt = case.coverage, } Ok(()) } FuzzOutcome::CounterExample(CounterExampleOutcome { - exit_reason, - counterexample: _counterexample, + exit_reason: status, + counterexample: outcome, .. }) => { - let status = exit_reason; // We cannot use the calldata returned by the test runner in `TestError::Fail`, // since that input represents the last run case, which may not correspond with // our failure - when a fuzz case fails, proptest will try // to run at least one more case to find a minimal failure // case. - let call_res = _counterexample.1.result.clone(); - *counterexample.borrow_mut() = _counterexample; - // HACK: we have to use an empty string here to denote `None` - let reason = rd.maybe_decode(&call_res, Some(status)); + let reason = rd.maybe_decode(&outcome.1.result, Some(status)); + *counterexample.borrow_mut() = outcome; + // HACK: we have to use an empty string here to denote `None`. Err(TestCaseError::fail(reason.unwrap_or_default())) } } diff --git a/crates/evm/evm/src/executors/invariant/error.rs b/crates/evm/evm/src/executors/invariant/error.rs index b0004c8cb..74f8d92cc 100644 --- a/crates/evm/evm/src/executors/invariant/error.rs +++ b/crates/evm/evm/src/executors/invariant/error.rs @@ -59,8 +59,8 @@ pub struct FailedInvariantCaseData { pub revert_reason: String, /// Address of the invariant asserter. pub addr: Address, - /// Function data for invariant check. - pub func: Bytes, + /// Function calldata for invariant check. + pub calldata: Bytes, /// Inner fuzzing Sequence coming from overriding calls. pub inner_sequence: Vec>, /// Shrink run limit @@ -79,17 +79,13 @@ impl FailedInvariantCaseData { inner_sequence: &[Option], ) -> Self { // Collect abis of fuzzed and invariant contracts to decode custom error. - let targets = targeted_contracts.targets.lock(); - let abis = targets - .iter() - .map(|contract| &contract.1 .1) - .chain(std::iter::once(invariant_contract.abi)); - let revert_reason = RevertDecoder::new() - .with_abis(abis) + .with_abis(targeted_contracts.targets.lock().iter().map(|(_, (_, abi, _))| abi)) + .with_abi(invariant_contract.abi) .decode(call_result.result.as_ref(), Some(call_result.exit_reason)); let func = invariant_contract.invariant_function; + debug_assert!(func.inputs.is_empty()); let origin = func.name.as_str(); Self { test_error: proptest::test_runner::TestError::Fail( @@ -99,7 +95,7 @@ impl FailedInvariantCaseData { return_reason: "".into(), revert_reason, addr: invariant_contract.address, - func: func.selector().to_vec().into(), + calldata: func.selector().to_vec().into(), inner_sequence: inner_sequence.to_vec(), shrink_run_limit: invariant_config.shrink_run_limit, fail_on_revert: invariant_config.fail_on_revert, diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index c6f2a44ec..fba994cba 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -2,7 +2,7 @@ use crate::{ executors::{Executor, RawCallResult}, inspectors::Fuzzer, }; -use alloy_primitives::{Address, FixedBytes, U256}; +use alloy_primitives::{Address, FixedBytes, Selector, U256}; use alloy_sol_types::{sol, SolCall}; use eyre::{eyre, ContextCompat, Result}; use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; @@ -491,6 +491,8 @@ impl<'a> InvariantExecutor<'a> { self.call_sol_default(to, &IInvariantTest::targetSendersCall {}).targetedSenders; let excluded_senders = self.call_sol_default(to, &IInvariantTest::excludeSendersCall {}).excludedSenders; + let sender_filters = SenderFilters::new(targeted_senders, excluded_senders); + let selected = self.call_sol_default(to, &IInvariantTest::targetContractsCall {}).targetedContracts; let excluded = @@ -498,20 +500,16 @@ impl<'a> InvariantExecutor<'a> { let mut contracts: TargetedContracts = self .setup_contracts - .clone() - .into_iter() - .filter(|(addr, (identifier, _))| { + .iter() + .filter(|&(addr, (identifier, _))| { *addr != to && *addr != CHEATCODE_ADDRESS && *addr != HARDHAT_CONSOLE_ADDRESS && (selected.is_empty() || selected.contains(addr)) && - (self.artifact_filters.targeted.is_empty() || - self.artifact_filters.targeted.contains_key(identifier)) && (excluded.is_empty() || !excluded.contains(addr)) && - (self.artifact_filters.excluded.is_empty() || - !self.artifact_filters.excluded.contains(identifier)) + self.artifact_filters.matches(identifier) }) - .map(|(addr, (identifier, abi))| (addr, (identifier, abi, vec![]))) + .map(|(addr, (identifier, abi))| (*addr, (identifier.clone(), abi.clone(), vec![]))) .collect(); self.target_interfaces(to, &mut contracts)?; @@ -523,10 +521,7 @@ impl<'a> InvariantExecutor<'a> { eyre::bail!("No contracts to fuzz."); } - Ok(( - SenderFilters::new(targeted_senders, excluded_senders), - FuzzRunIdentifiedContracts::new(contracts, selected.is_empty()), - )) + Ok((sender_filters, FuzzRunIdentifiedContracts::new(contracts, selected.is_empty()))) } /// Extends the contracts and selectors to fuzz with the addresses and ABIs specified in @@ -586,26 +581,18 @@ impl<'a> InvariantExecutor<'a> { address: Address, targeted_contracts: &mut TargetedContracts, ) -> Result<()> { - let some_abi_selectors = self - .artifact_filters - .targeted - .iter() - .filter(|(_, selectors)| !selectors.is_empty()) - .collect::>(); - for (address, (identifier, _)) in self.setup_contracts.iter() { - if let Some(selectors) = some_abi_selectors.get(identifier) { - self.add_address_with_functions( - *address, - (*selectors).clone(), - targeted_contracts, - )?; + if let Some(selectors) = self.artifact_filters.targeted.get(identifier) { + if selectors.is_empty() { + continue; + } + self.add_address_with_functions(*address, selectors, targeted_contracts)?; } } let selectors = self.call_sol_default(address, &IInvariantTest::targetSelectorsCall {}); for IInvariantTest::FuzzSelector { addr, selectors } in selectors.targetedSelectors { - self.add_address_with_functions(addr, selectors, targeted_contracts)?; + self.add_address_with_functions(addr, &selectors, targeted_contracts)?; } Ok(()) } @@ -614,14 +601,14 @@ impl<'a> InvariantExecutor<'a> { fn add_address_with_functions( &self, address: Address, - bytes4_array: Vec>, + selectors: &[Selector], targeted_contracts: &mut TargetedContracts, ) -> eyre::Result<()> { if let Some((name, abi, address_selectors)) = targeted_contracts.get_mut(&address) { // The contract is already part of our filter, and all we do is specify that we're // only looking at specific functions coming from `bytes4_array`. - for selector in bytes4_array { - address_selectors.push(get_function(name, &selector, abi)?); + for &selector in selectors { + address_selectors.push(get_function(name, selector, abi).cloned()?); } } else { let (name, abi) = self.setup_contracts.get(&address).ok_or_else(|| { @@ -630,9 +617,9 @@ impl<'a> InvariantExecutor<'a> { ) })?; - let functions = bytes4_array - .into_iter() - .map(|selector| get_function(name, &selector, abi)) + let functions = selectors + .iter() + .map(|&selector| get_function(name, selector, abi).cloned()) .collect::, _>>()?; targeted_contracts.insert(address, (name.to_string(), abi.clone(), functions)); diff --git a/crates/evm/evm/src/executors/invariant/replay.rs b/crates/evm/evm/src/executors/invariant/replay.rs index ff4fbc89d..b6c2c6d9e 100644 --- a/crates/evm/evm/src/executors/invariant/replay.rs +++ b/crates/evm/evm/src/executors/invariant/replay.rs @@ -47,7 +47,7 @@ pub fn replay_run( if let Some(new_coverage) = call_result.coverage { if let Some(old_coverage) = coverage { - *coverage = Some(std::mem::take(old_coverage).merge(new_coverage)); + *coverage = Some(std::mem::take(old_coverage).merged(new_coverage)); } else { *coverage = Some(new_coverage); } diff --git a/crates/evm/evm/src/executors/invariant/result.rs b/crates/evm/evm/src/executors/invariant/result.rs index f4ba88e2a..2d08efb12 100644 --- a/crates/evm/evm/src/executors/invariant/result.rs +++ b/crates/evm/evm/src/executors/invariant/result.rs @@ -68,13 +68,13 @@ pub(crate) fn assert_invariants( U256::ZERO, )?; - let is_err = !executor.is_raw_call_success( + let success = executor.is_raw_call_success( invariant_contract.address, Cow::Owned(call_result.state_changeset.take().unwrap()), &call_result, false, ); - if is_err { + if !success { // We only care about invariants which we haven't broken yet. if invariant_failures.error.is_none() { let case_data = FailedInvariantCaseData::new( diff --git a/crates/evm/evm/src/executors/invariant/shrink.rs b/crates/evm/evm/src/executors/invariant/shrink.rs index 47711e11b..9d397bfe1 100644 --- a/crates/evm/evm/src/executors/invariant/shrink.rs +++ b/crates/evm/evm/src/executors/invariant/shrink.rs @@ -89,7 +89,7 @@ pub(crate) fn shrink_sequence( // Special case test: the invariant is *unsatisfiable* - it took 0 calls to // break the invariant -- consider emitting a warning. let error_call_result = - executor.call_raw(CALLER, failed_case.addr, failed_case.func.clone(), U256::ZERO)?; + executor.call_raw(CALLER, failed_case.addr, failed_case.calldata.clone(), U256::ZERO)?; if error_call_result.reverted { return Ok(vec![]); } @@ -102,7 +102,7 @@ pub(crate) fn shrink_sequence( calls, shrinker.current().collect(), failed_case.addr, - failed_case.func.clone(), + failed_case.calldata.clone(), failed_case.fail_on_revert, ) { // If candidate sequence still fails then shrink more if possible. @@ -126,7 +126,7 @@ pub fn check_sequence( calls: &[BasicTxDetails], sequence: Vec, test_address: Address, - test_function: Bytes, + calldata: Bytes, fail_on_revert: bool, ) -> eyre::Result<(bool, bool)> { // Apply the call sequence. @@ -146,7 +146,7 @@ pub fn check_sequence( } // Check the invariant for call sequence. - let mut call_result = executor.call_raw(CALLER, test_address, test_function, U256::ZERO)?; + let mut call_result = executor.call_raw(CALLER, test_address, calldata, U256::ZERO)?; Ok(( executor.is_raw_call_success( test_address, diff --git a/crates/evm/fuzz/src/invariant/filters.rs b/crates/evm/fuzz/src/invariant/filters.rs index c56450c27..2925c6e45 100644 --- a/crates/evm/fuzz/src/invariant/filters.rs +++ b/crates/evm/fuzz/src/invariant/filters.rs @@ -14,7 +14,14 @@ pub struct ArtifactFilters { /// List of `contract_path:contract_name` which are to be excluded. pub excluded: Vec, } + impl ArtifactFilters { + /// Returns `true` if the given identifier matches this filter. + pub fn matches(&self, identifier: &str) -> bool { + (self.targeted.is_empty() || self.targeted.contains_key(identifier)) && + (self.excluded.is_empty() || !self.excluded.iter().any(|id| id == identifier)) + } + /// Gets all the targeted functions from `artifact`. Returns error, if selectors do not match /// the `artifact`. /// @@ -28,7 +35,7 @@ impl ArtifactFilters { if let Some(selectors) = self.targeted.get(&artifact.identifier()) { let functions = selectors .iter() - .map(|selector| get_function(&artifact.name, selector, abi)) + .map(|selector| get_function(&artifact.name, *selector, abi).cloned()) .collect::>>()?; // targetArtifactSelectors > excludeArtifacts > targetArtifacts if functions.is_empty() && self.excluded.contains(&artifact.identifier()) { @@ -44,6 +51,7 @@ impl ArtifactFilters { Ok(None) } } + /// Filter for acceptable senders to use for invariant testing. Exclusion takes priority if /// clashing. /// diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 7c05b573c..c6588b582 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -877,7 +877,7 @@ impl<'a> ContractRunner<'a> { fn merge_coverages(mut coverage: Option, other: Option) -> Option { let old_coverage = std::mem::take(&mut coverage); match (old_coverage, other) { - (Some(old_coverage), Some(other)) => Some(old_coverage.merge(other)), + (Some(old_coverage), Some(other)) => Some(old_coverage.merged(other)), (None, Some(other)) => Some(other), (Some(old_coverage), None) => Some(old_coverage), (None, None) => None, From cffc812075412a1e6d63575d8d8f2fef00ada3ef Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 4 Jun 2024 16:33:49 +0200 Subject: [PATCH 343/430] fix: use revm to calculate gas stipend (#8048) --- crates/evm/evm/src/executors/mod.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 5c830c256..7e770f54f 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -785,12 +785,6 @@ impl std::ops::DerefMut for CallResult { } } -/// Calculates the initial gas stipend for a transaction -fn calc_stipend(calldata: &[u8], spec: SpecId) -> u64 { - let non_zero_data_cost = if SpecId::enabled(spec, SpecId::ISTANBUL) { 16 } else { 68 }; - calldata.iter().fold(21000, |sum, byte| sum + if *byte == 0 { 4 } else { non_zero_data_cost }) -} - /// Converts the data aggregated in the `inspector` and `call` to a `RawCallResult` fn convert_executed_result( env: EnvWithHandlerCfg, @@ -809,7 +803,13 @@ fn convert_executed_result( } ExecutionResult::Halt { reason, gas_used } => (reason.into(), 0_u64, gas_used, None), }; - let stipend = calc_stipend(&env.tx.data, env.handler_cfg.spec_id); + let stipend = revm::interpreter::gas::validate_initial_tx_gas( + env.spec_id(), + &env.tx.data, + env.tx.transact_to.is_create(), + &env.tx.access_list, + &env.tx.eof_initcodes, + ); let result = match &out { Some(Output::Call(data)) => data.clone(), From 99a12d1f2c1146823d27032c94247d1b501b8429 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 4 Jun 2024 16:49:51 +0200 Subject: [PATCH 344/430] perf(cheatcodes): don't recover verify signature after signing (#8051) --- crates/cheatcodes/src/utils.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/crates/cheatcodes/src/utils.rs b/crates/cheatcodes/src/utils.rs index fb750fa91..7356d7133 100644 --- a/crates/cheatcodes/src/utils.rs +++ b/crates/cheatcodes/src/utils.rs @@ -204,12 +204,8 @@ fn encode_vrs(sig: alloy_primitives::Signature) -> Vec { pub(super) 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)?; - let recovered = sig.recover_address_from_prehash(digest)?; - - assert_eq!(recovered, wallet.address()); - + debug_assert_eq!(sig.recover_address_from_prehash(digest)?, wallet.address()); Ok(encode_vrs(sig)) } @@ -243,8 +239,10 @@ pub(super) fn sign_with_wallet( let sig = foundry_common::block_on(wallet.sign_hash(digest)).map_err(|err| fmt_err!("{err}"))?; - let recovered = sig.recover_address_from_prehash(digest).map_err(|err| fmt_err!("{err}"))?; - assert_eq!(recovered, signer); + debug_assert_eq!( + sig.recover_address_from_prehash(digest).map_err(|err| fmt_err!("{err}"))?, + signer + ); Ok(encode_vrs(sig)) } From 2193a274e568cfd6ce64e6823c036d778e0ebbea Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 4 Jun 2024 17:50:32 +0300 Subject: [PATCH 345/430] fix(forge): ensure contract managing fork is persistent (#8041) * fix(forge): ensure contract managing fork is persistent * Review changes: rename to persist_caller, always add (instead check + add) --- crates/cheatcodes/src/evm/fork.rs | 16 +++++ crates/forge/tests/it/repros.rs | 3 + testdata/default/repros/Issue8004.t.sol | 94 +++++++++++++++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 testdata/default/repros/Issue8004.t.sol diff --git a/crates/cheatcodes/src/evm/fork.rs b/crates/cheatcodes/src/evm/fork.rs index 4c91a2261..a76752028 100644 --- a/crates/cheatcodes/src/evm/fork.rs +++ b/crates/cheatcodes/src/evm/fork.rs @@ -62,6 +62,7 @@ impl Cheatcode for createSelectFork_2Call { impl Cheatcode for rollFork_0Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { blockNumber } = self; + persist_caller(ccx); ccx.ecx.db.roll_fork( None, (*blockNumber).to(), @@ -75,6 +76,7 @@ impl Cheatcode for rollFork_0Call { impl Cheatcode for rollFork_1Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { txHash } = self; + persist_caller(ccx); ccx.ecx.db.roll_fork_to_transaction( None, *txHash, @@ -88,6 +90,7 @@ impl Cheatcode for rollFork_1Call { impl Cheatcode for rollFork_2Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { forkId, blockNumber } = self; + persist_caller(ccx); ccx.ecx.db.roll_fork( Some(*forkId), (*blockNumber).to(), @@ -101,6 +104,7 @@ impl Cheatcode for rollFork_2Call { impl Cheatcode for rollFork_3Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { forkId, txHash } = self; + persist_caller(ccx); ccx.ecx.db.roll_fork_to_transaction( Some(*forkId), *txHash, @@ -114,6 +118,7 @@ impl Cheatcode for rollFork_3Call { impl Cheatcode for selectForkCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { forkId } = self; + persist_caller(ccx); check_broadcast(ccx.state)?; ccx.ecx.db.select_fork(*forkId, &mut ccx.ecx.env, &mut ccx.ecx.journaled_state)?; @@ -334,6 +339,8 @@ fn create_fork_request( url_or_alias: &str, block: Option, ) -> Result { + persist_caller(ccx); + let url = ccx.state.config.rpc_url(url_or_alias)?; let mut evm_opts = ccx.state.config.evm_opts.clone(); evm_opts.fork_block_number = block; @@ -355,3 +362,12 @@ fn check_broadcast(state: &Cheatcodes) -> Result<()> { Err(fmt_err!("cannot select forks during a broadcast")) } } + +// Helper to add the caller of fork cheat code as persistent account (in order to make sure that the +// state of caller contract is not lost when fork changes). +// Applies to create, select and roll forks actions. +// https://github.com/foundry-rs/foundry/issues/8004 +#[inline] +fn persist_caller(ccx: &mut CheatsCtxt) { + ccx.ecx.db.add_persistent_account(ccx.caller); +} diff --git a/crates/forge/tests/it/repros.rs b/crates/forge/tests/it/repros.rs index aeae1175a..0c0f1bc50 100644 --- a/crates/forge/tests/it/repros.rs +++ b/crates/forge/tests/it/repros.rs @@ -329,3 +329,6 @@ test_repro!(7481); // https://github.com/foundry-rs/foundry/issues/5739 test_repro!(5739); + +// https://github.com/foundry-rs/foundry/issues/8004 +test_repro!(8004); diff --git a/testdata/default/repros/Issue8004.t.sol b/testdata/default/repros/Issue8004.t.sol new file mode 100644 index 000000000..3e077e2a2 --- /dev/null +++ b/testdata/default/repros/Issue8004.t.sol @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +contract NonPersistentHelper is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + uint256 public curState; + + function createSelectFork() external { + vm.createSelectFork("rpcAlias"); + curState += 1; + } + + function createSelectForkAtBlock() external { + vm.createSelectFork("rpcAlias", 19000000); + curState += 1; + } + + function createSelectForkAtTx() external { + vm.createSelectFork( + "rpcAlias", vm.parseBytes32("0xb5c978f15d01fcc9b4d78967e8189e35ecc21ff4e78315ea5d616f3467003c84") + ); + curState += 1; + } + + function selectFork(uint256 forkId) external { + vm.selectFork(forkId); + curState += 1; + } + + function rollForkAtBlock() external { + vm.rollFork(19000000); + curState += 1; + } + + function rollForkIdAtBlock(uint256 forkId) external { + vm.rollFork(forkId, 19000000); + curState += 1; + } +} + +// https://github.com/foundry-rs/foundry/issues/8004 +contract Issue8004CreateSelectForkTest is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + NonPersistentHelper helper; + + function setUp() public { + helper = new NonPersistentHelper(); + } + + function testNonPersistentHelperCreateFork() external { + helper.createSelectFork(); + assertEq(helper.curState(), 1); + } + + function testNonPersistentHelperCreateForkAtBlock() external { + helper.createSelectForkAtBlock(); + assertEq(helper.curState(), 1); + } + + function testNonPersistentHelperCreateForkAtTx() external { + helper.createSelectForkAtBlock(); + assertEq(helper.curState(), 1); + } + + function testNonPersistentHelperSelectFork() external { + uint256 forkId = vm.createFork("rpcAlias"); + helper.selectFork(forkId); + assertEq(helper.curState(), 1); + } +} + +contract Issue8004RollForkTest is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + NonPersistentHelper helper; + uint256 forkId; + + function setUp() public { + forkId = vm.createSelectFork("rpcAlias"); + helper = new NonPersistentHelper(); + } + + function testNonPersistentHelperRollForkAtBlock() external { + helper.rollForkAtBlock(); + assertEq(helper.curState(), 1); + } + + function testNonPersistentHelperRollForkIdAtBlock() external { + helper.rollForkIdAtBlock(forkId); + assertEq(helper.curState(), 1); + } +} From f9cbe69300b896514694d13f9bf2cd2f787b203b Mon Sep 17 00:00:00 2001 From: sam bacha Date: Tue, 4 Jun 2024 09:07:14 -0700 Subject: [PATCH 346/430] fix(docker build): update index checksum (#8003) fixes https://github.com/foundry-rs/foundry/issues/7925 --- Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Dockerfile b/Dockerfile index e5ce88c01..e900ee1e8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,6 +15,9 @@ RUN [[ "$TARGETARCH" = "arm64" ]] && echo "export CFLAGS=-mno-outline-atomics" > WORKDIR /opt/foundry COPY . . +# see +RUN git update-index --force-write-index + RUN --mount=type=cache,target=/root/.cargo/registry --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/opt/foundry/target \ source $HOME/.profile && cargo build --release \ && mkdir out \ From 9e95c5537ec13e66d770d4d10dd18891f85332a5 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 4 Jun 2024 18:27:57 +0200 Subject: [PATCH 347/430] ci: switch macos test runner to use aarch64 (#8055) * ci: switch macos test runner to use aarch64 * tmp: run it * Revert "ci: switch macos test runner to use aarch64" This reverts commit 844d375d933e108ba746fd668822490e2297dc98. * Revert "tmp: run it" This reverts commit 0eb13b77de39e6989ab40171909640fae5c1f895. * Reapply "ci: switch macos test runner to use aarch64" This reverts commit f6da78534404ceb3ce8335996c9ddff643097ee2. --- .github/scripts/matrices.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/matrices.py b/.github/scripts/matrices.py index 3fdcea115..a71ff3683 100755 --- a/.github/scripts/matrices.py +++ b/.github/scripts/matrices.py @@ -69,7 +69,7 @@ def __init__( t_linux_x86 = Target("ubuntu-latest", "x86_64-unknown-linux-gnu", "linux-amd64") # TODO: Figure out how to make this work # t_linux_arm = Target("ubuntu-latest", "aarch64-unknown-linux-gnu", "linux-aarch64") -t_macos = Target("macos-latest", "x86_64-apple-darwin", "macosx-amd64") +t_macos = Target("macos-latest", "aarch64-apple-darwin", "macosx-aarch64") t_windows = Target("windows-latest", "x86_64-pc-windows-msvc", "windows-amd64") targets = [t_linux_x86, t_windows] if is_pr else [t_linux_x86, t_macos, t_windows] From 5668e4699c1adc2dc39526dcfb319bdad78f98ea Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 4 Jun 2024 18:28:06 +0200 Subject: [PATCH 348/430] ci: release x86_64 darwin binaries with macos-12 runner (#8056) --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c7422bfb6..3db72dfcf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -88,7 +88,7 @@ jobs: svm_target_platform: linux-aarch64 platform: linux arch: arm64 - - runner: macos-latest-large + - runner: macos-12-large target: x86_64-apple-darwin svm_target_platform: macosx-amd64 platform: darwin From ec89c4f84e195ac155eb022dd1c522aff8e9967f Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 4 Jun 2024 20:28:56 +0200 Subject: [PATCH 349/430] fix: re-enable aws-config default-features (#8058) --- Cargo.lock | 49 +++++++++++++++++++++++++++++++++++++++ crates/wallets/Cargo.toml | 2 +- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 375391847..b802db9cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1106,6 +1106,8 @@ checksum = "1234b742ac4a40a7d3459c6e3c99818271976a5a6ae3732cb415f4a9a94da7b6" dependencies = [ "aws-credential-types", "aws-runtime", + "aws-sdk-sso", + "aws-sdk-ssooidc", "aws-sdk-sts", "aws-smithy-async", "aws-smithy-http", @@ -1116,12 +1118,15 @@ dependencies = [ "aws-types", "bytes", "fastrand", + "hex", "http 0.2.12", "hyper 0.14.28", + "ring", "time", "tokio", "tracing", "url", + "zeroize", ] [[package]] @@ -1181,6 +1186,50 @@ dependencies = [ "tracing", ] +[[package]] +name = "aws-sdk-sso" +version = "1.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef53f254e16c00cfbfd69fa6eca4d858b7c161878db2cd248410af402d1951e" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-ssooidc" +version = "1.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1b673b2972c38463955e27d76c9d2ebb0452a9ce8059a0e2c9ed67efe69ef35" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + [[package]] name = "aws-sdk-sts" version = "1.28.0" diff --git a/crates/wallets/Cargo.toml b/crates/wallets/Cargo.toml index c7c4038cd..a8c7658de 100644 --- a/crates/wallets/Cargo.toml +++ b/crates/wallets/Cargo.toml @@ -22,7 +22,7 @@ alloy-dyn-abi.workspace = true # aws-kms alloy-signer-aws = { workspace = true, features = ["eip712"], optional = true } -aws-config = { version = "1", default-features = false, optional = true } +aws-config = { version = "1", optional = true } # default-features are necessary aws-sdk-kms = { version = "1", default-features = false, optional = true } foundry-config.workspace = true From 1c6bd3274430b96ea5c0c1f6bf81bb68912e9813 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 4 Jun 2024 21:18:02 +0200 Subject: [PATCH 350/430] fix: panic in WalletSigner::from_private_key (#8052) * fix: panic in WalletSigner::from_private_key * stuff --- crates/cheatcodes/src/fs.rs | 2 +- crates/cheatcodes/src/script.rs | 20 ++++++++++++-------- crates/cheatcodes/src/utils.rs | 20 +++++++------------- crates/wallets/src/wallet_signer.rs | 7 +++---- 4 files changed, 23 insertions(+), 26 deletions(-) diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index fd4234d21..75e0d2467 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -376,7 +376,7 @@ fn get_artifact_code(state: &Cheatcodes, path: &str, deployed: bool) -> Result return Err(fmt_err!("Invalid artifact path")), + _ => bail!("invalid artifact path"), }; state.config.paths.artifacts.join(path_in_artifacts) diff --git a/crates/cheatcodes/src/script.rs b/crates/cheatcodes/src/script.rs index 19922ad34..8eacb52a8 100644 --- a/crates/cheatcodes/src/script.rs +++ b/crates/cheatcodes/src/script.rs @@ -1,7 +1,7 @@ //! Implementations of [`Scripting`](crate::Group::Scripting) cheatcodes. use crate::{Cheatcode, CheatsCtxt, DatabaseExt, Result, Vm::*}; -use alloy_primitives::{Address, U256}; +use alloy_primitives::{Address, B256, U256}; use alloy_signer_wallet::LocalWallet; use foundry_wallets::{multi_wallet::MultiWallet, WalletSigner}; use parking_lot::Mutex; @@ -106,9 +106,14 @@ impl ScriptWallets { } /// Locks inner Mutex and adds a signer to the [MultiWallet]. - pub fn add_signer(&self, private_key: impl AsRef<[u8]>) -> Result { - self.inner.lock().multi_wallet.add_signer(WalletSigner::from_private_key(private_key)?); - Ok(Default::default()) + pub fn add_private_key(&self, private_key: &B256) -> Result<()> { + self.add_local_signer(LocalWallet::from_bytes(private_key)?); + Ok(()) + } + + /// Locks inner Mutex and adds a signer to the [MultiWallet]. + pub fn add_local_signer(&self, wallet: LocalWallet) { + self.inner.lock().multi_wallet.add_signer(WalletSigner::Local(wallet)); } /// Locks inner Mutex and returns all signer addresses in the [MultiWallet]. @@ -166,14 +171,13 @@ fn broadcast_key( private_key: &U256, single_call: bool, ) -> Result { - let key = super::utils::parse_private_key(private_key)?; - let new_origin = LocalWallet::from(key.clone()).address(); + let wallet = super::utils::parse_wallet(private_key)?; + let new_origin = wallet.address(); let result = broadcast(ccx, Some(&new_origin), single_call); - if result.is_ok() { if let Some(script_wallets) = &ccx.state.script_wallets { - script_wallets.add_signer(key.to_bytes())?; + script_wallets.add_local_signer(wallet); } } result diff --git a/crates/cheatcodes/src/utils.rs b/crates/cheatcodes/src/utils.rs index 7356d7133..95a7c6d32 100644 --- a/crates/cheatcodes/src/utils.rs +++ b/crates/cheatcodes/src/utils.rs @@ -90,10 +90,10 @@ impl Cheatcode for deriveKey_3Call { impl Cheatcode for rememberKeyCall { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { privateKey } = self; - let key = parse_private_key(privateKey)?; - let address = LocalWallet::from(key.clone()).address(); + let wallet = parse_wallet(privateKey)?; + let address = wallet.address(); if let Some(script_wallets) = &ccx.state.script_wallets { - script_wallets.add_signer(key.to_bytes())?; + script_wallets.add_local_signer(wallet); } Ok(address.abi_encode()) } @@ -215,7 +215,7 @@ pub(super) fn sign_with_wallet( digest: &B256, ) -> Result { let Some(script_wallets) = &ccx.state.script_wallets else { - return Err("no wallets are available".into()); + bail!("no wallets are available"); }; let mut script_wallets = script_wallets.inner.lock(); @@ -229,21 +229,15 @@ pub(super) fn sign_with_wallet( } else if signers.len() == 1 { *signers.keys().next().unwrap() } else { - return Err("could not determine signer".into()); + bail!("could not determine signer"); }; let wallet = signers .get(&signer) .ok_or_else(|| fmt_err!("signer with address {signer} is not available"))?; - let sig = - foundry_common::block_on(wallet.sign_hash(digest)).map_err(|err| fmt_err!("{err}"))?; - - debug_assert_eq!( - sig.recover_address_from_prehash(digest).map_err(|err| fmt_err!("{err}"))?, - signer - ); - + let sig = foundry_common::block_on(wallet.sign_hash(digest))?; + debug_assert_eq!(sig.recover_address_from_prehash(digest)?, signer); Ok(encode_vrs(sig)) } diff --git a/crates/wallets/src/wallet_signer.rs b/crates/wallets/src/wallet_signer.rs index 15e450289..9ab30e1ad 100644 --- a/crates/wallets/src/wallet_signer.rs +++ b/crates/wallets/src/wallet_signer.rs @@ -57,9 +57,8 @@ impl WalletSigner { } } - pub fn from_private_key(private_key: impl AsRef<[u8]>) -> Result { - let wallet = LocalWallet::from_bytes(&B256::from_slice(private_key.as_ref()))?; - Ok(Self::Local(wallet)) + pub fn from_private_key(private_key: &B256) -> Result { + Ok(Self::Local(LocalWallet::from_bytes(private_key)?)) } /// Returns a list of addresses available to use with current signer @@ -213,7 +212,7 @@ impl PendingSigner { } Self::Interactive => { let private_key = rpassword::prompt_password("Enter private key:")?; - Ok(WalletSigner::from_private_key(hex::decode(private_key)?)?) + Ok(WalletSigner::from_private_key(&hex::FromHex::from_hex(private_key)?)?) } } } From fb86e5d3bf41f9cef3ccc7fbd04e1a422f20c29e Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 5 Jun 2024 01:12:17 +0200 Subject: [PATCH 351/430] perf(link): keep around the cow for a bit longer (#8059) --- crates/common/src/compile.rs | 2 +- crates/forge/src/multi_runner.rs | 4 +--- crates/linking/src/lib.rs | 40 +++++++++++++++++--------------- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index 8e8cff368..4b1cd7874 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -316,7 +316,7 @@ impl ContractSources { format!("failed to read artifact source file for `{}`", id.identifier()) })?; let linked = linker.link(&id, libraries)?; - let contract = compact_to_contract(linked)?; + let contract = compact_to_contract(linked.into_contract_bytecode())?; sources.insert(&id, file_id, source_code, contract); } else { warn!(id = id.identifier(), "source not found"); diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 8508abbe5..0b56f3500 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -343,9 +343,7 @@ impl MultiContractRunnerBuilder { let mut deployable_contracts = DeployableContracts::default(); for (id, contract) in linked_contracts.iter() { - let Some(abi) = contract.abi.as_ref() else { - continue; - }; + let Some(abi) = &contract.abi else { continue }; // if it's a test, link it and add to deployable contracts if abi.constructor.as_ref().map(|c| c.inputs.is_empty()).unwrap_or(true) && diff --git a/crates/linking/src/lib.rs b/crates/linking/src/lib.rs index 8d9791a18..980dfc8fd 100644 --- a/crates/linking/src/lib.rs +++ b/crates/linking/src/lib.rs @@ -2,13 +2,13 @@ use alloy_primitives::{Address, Bytes, B256}; use foundry_compilers::{ - artifacts::{CompactContractBytecode, CompactContractBytecodeCow, Libraries}, + artifacts::{CompactContractBytecodeCow, Libraries}, contracts::ArtifactContracts, Artifact, ArtifactId, }; use semver::Version; use std::{ - collections::{BTreeMap, BTreeSet, HashMap}, + collections::{BTreeMap, BTreeSet}, path::{Path, PathBuf}, str::FromStr, }; @@ -202,7 +202,7 @@ impl<'a> Linker<'a> { let bytecode = self.link(id, &libraries).unwrap().bytecode.unwrap(); (id, bytecode) }) - .collect::>(); + .collect::>(); let mut libs_to_deploy = Vec::new(); @@ -212,22 +212,22 @@ impl<'a> Linker<'a> { // Find any library which is fully linked. let deployable = needed_libraries .iter() - .find(|(_, bytecode)| !bytecode.object.is_unlinked()) - .map(|(id, _)| *id); + .enumerate() + .find(|(_, (_, bytecode))| !bytecode.object.is_unlinked()); // If we haven't found any deployable library, it means we have a cyclic dependency. - let Some(id) = deployable else { + let Some((index, &(id, _))) = deployable else { return Err(LinkerError::CyclicDependency); }; - let bytecode = needed_libraries.remove(id).unwrap(); - let code = bytecode.into_bytes().unwrap(); - let address = sender.create2_from_code(salt, code.as_ref()); - libs_to_deploy.push(code); + let (_, bytecode) = needed_libraries.swap_remove(index); + let code = bytecode.bytes().unwrap(); + let address = sender.create2_from_code(salt, code); + libs_to_deploy.push(code.clone()); let (file, name) = self.convert_artifact_id_to_lib_path(id); - for (_, bytecode) in needed_libraries.iter_mut() { - bytecode.link(file.to_string_lossy(), name.clone(), address); + for (_, bytecode) in &mut needed_libraries { + bytecode.to_mut().link(file.to_string_lossy(), name.clone(), address); } libraries.libs.entry(file).or_default().insert(name, address.to_checksum(None)); @@ -241,7 +241,7 @@ impl<'a> Linker<'a> { &self, target: &ArtifactId, libraries: &Libraries, - ) -> Result { + ) -> Result, LinkerError> { let mut contract = self.contracts.get(target).ok_or(LinkerError::MissingTargetArtifact)?.clone(); for (file, libs) in &libraries.libs { @@ -257,12 +257,7 @@ impl<'a> Linker<'a> { } } } - - Ok(CompactContractBytecode { - abi: contract.abi.map(|a| a.into_owned()), - bytecode: contract.bytecode.map(|b| b.into_owned()), - deployed_bytecode: contract.deployed_bytecode.map(|b| b.into_owned()), - }) + Ok(contract) } pub fn get_linked_artifacts( @@ -271,6 +266,13 @@ impl<'a> Linker<'a> { ) -> Result { self.contracts.keys().map(|id| Ok((id.clone(), self.link(id, libraries)?))).collect() } + + pub fn get_linked_artifacts_cow( + &self, + libraries: &Libraries, + ) -> Result>, LinkerError> { + self.contracts.keys().map(|id| Ok((id.clone(), self.link(id, libraries)?))).collect() + } } #[cfg(test)] From b164c206659ab410ea67d6e13b1968b79e5447bc Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 5 Jun 2024 13:01:46 +0200 Subject: [PATCH 352/430] chore: add fuzz dictionary stats (#8060) --- crates/evm/evm/src/executors/fuzz/mod.rs | 2 ++ crates/evm/evm/src/executors/invariant/mod.rs | 2 +- crates/evm/fuzz/src/strategies/state.rs | 21 +++++++++++ crates/forge/src/multi_runner.rs | 2 +- crates/forge/src/runner.rs | 35 ++++++------------- 5 files changed, 36 insertions(+), 26 deletions(-) diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index d0ff89297..163d5444d 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -178,6 +178,8 @@ impl FuzzedExecutor { _ => {} } + state.log_stats(); + result } diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index fba994cba..69ef106f6 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -322,7 +322,7 @@ impl<'a> InvariantExecutor<'a> { }); trace!(?fuzz_fixtures); - trace!(state_len = fuzz_state.dictionary_read().len()); + fuzz_state.log_stats(); let (reverts, error) = failures.into_inner().into_inner(); diff --git a/crates/evm/fuzz/src/strategies/state.rs b/crates/evm/fuzz/src/strategies/state.rs index c469e2910..3717f370e 100644 --- a/crates/evm/fuzz/src/strategies/state.rs +++ b/crates/evm/fuzz/src/strategies/state.rs @@ -81,6 +81,11 @@ impl EvmFuzzState { pub fn dictionary_read(&self) -> RwLockReadGuard<'_, RawRwLock, FuzzDictionary> { self.inner.read() } + + /// Logs stats about the current state. + pub fn log_stats(&self) { + self.inner.read().log_stats(); + } } // We're using `IndexSet` to have a stable element order when restoring persisted state, as well as @@ -99,6 +104,9 @@ pub struct FuzzDictionary { new_addreses: Vec, /// Sample typed values that are collected from call result and used across invariant runs. sample_values: HashMap>, + + misses: usize, + hits: usize, } impl fmt::Debug for FuzzDictionary { @@ -303,6 +311,8 @@ impl FuzzDictionary { fn insert_value(&mut self, value: [u8; 32], collected: bool) { if self.state_values.len() < self.config.max_fuzz_dictionary_values { let (index, new_value) = self.state_values.insert_full(value); + let counter = if new_value { &mut self.misses } else { &mut self.hits }; + *counter += 1; if new_value && collected { self.new_values.push(index); } @@ -364,6 +374,17 @@ impl FuzzDictionary { self.new_values.clear(); self.new_addreses.clear(); } + + pub fn log_stats(&self) { + trace!( + addresses.len = self.addresses.len(), + sample.len = self.sample_values.len(), + state.len = self.state_values.len(), + state.misses = self.misses, + state.hits = self.hits, + "FuzzDictionary stats", + ); + } } /// Collects all created contracts from a StateChangeset which haven't been discovered yet. Stores diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 0b56f3500..3c3eb2ad4 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -209,7 +209,7 @@ impl MultiContractRunner { if !enabled!(tracing::Level::TRACE) { span_name = get_contract_name(&identifier); } - let _guard = info_span!("run_tests", name = span_name).entered(); + let _guard = debug_span!("run_suite", name = span_name).entered(); debug!("start executing all tests in contract"); diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index c6588b582..dadd51963 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -365,6 +365,14 @@ impl<'a> ContractRunner<'a> { let _guard = handle.enter(); let sig = func.signature(); + let span = debug_span!("test", name = tracing::field::Empty).entered(); + if !span.is_disabled() { + if enabled!(tracing::Level::TRACE) { + span.record("name", &sig); + } else { + span.record("name", &func.name); + } + } let setup = setup.clone(); let should_fail = func.is_test_fail(); @@ -410,18 +418,8 @@ impl<'a> ContractRunner<'a> { /// /// State modifications are not committed to the evm database but discarded after the call, /// similar to `eth_call`. + #[instrument(level = "debug", name = "normal", skip_all)] pub fn run_test(&self, func: &Function, should_fail: bool, setup: TestSetup) -> TestResult { - let span = info_span!("test", %should_fail); - if !span.is_disabled() { - let sig = &func.signature()[..]; - if enabled!(tracing::Level::TRACE) { - span.record("sig", sig); - } else { - span.record("sig", sig.split('(').next().unwrap()); - } - } - let _guard = span.enter(); - let TestSetup { address, mut logs, mut traces, mut labeled_addresses, mut coverage, .. } = setup; @@ -517,7 +515,7 @@ impl<'a> ContractRunner<'a> { } } - #[instrument(name = "invariant_test", skip_all)] + #[instrument(level = "debug", name = "invariant", skip_all)] pub fn run_invariant_test( &self, runner: TestRunner, @@ -736,7 +734,7 @@ impl<'a> ContractRunner<'a> { } } - #[instrument(name = "fuzz_test", skip_all, fields(name = %func.signature(), %should_fail))] + #[instrument(level = "debug", name = "fuzz", skip_all)] pub fn run_fuzz_test( &self, func: &Function, @@ -745,17 +743,6 @@ impl<'a> ContractRunner<'a> { setup: TestSetup, fuzz_config: FuzzConfig, ) -> TestResult { - let span = info_span!("fuzz_test", %should_fail); - if !span.is_disabled() { - let sig = &func.signature()[..]; - if enabled!(tracing::Level::TRACE) { - span.record("test", sig); - } else { - span.record("test", sig.split('(').next().unwrap()); - } - } - let _guard = span.enter(); - let TestSetup { address, mut logs, From 487892dd05730a4120b8ed2639b38d9c0bee5fe6 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Wed, 5 Jun 2024 15:55:53 +0300 Subject: [PATCH 353/430] perf(invariant): collect push bytes only once (#8063) --- crates/evm/fuzz/src/strategies/state.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/evm/fuzz/src/strategies/state.rs b/crates/evm/fuzz/src/strategies/state.rs index 3717f370e..6ff36f8e4 100644 --- a/crates/evm/fuzz/src/strategies/state.rs +++ b/crates/evm/fuzz/src/strategies/state.rs @@ -229,6 +229,7 @@ impl FuzzDictionary { } /// Insert values from push bytes into fuzz dictionary. + /// Values are collected only once for a given address. /// If values are newly collected then they are removed at the end of current run. fn insert_push_bytes_values( &mut self, @@ -236,7 +237,7 @@ impl FuzzDictionary { account_info: &AccountInfo, collected: bool, ) { - if self.config.include_push_bytes { + if self.config.include_push_bytes && !self.addresses.contains(address) { // Insert push bytes if let Some(code) = account_info.code.clone() { self.insert_address(*address, collected); From 29a47350b6e12a0fea61640ff7a3dff923034b54 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 5 Jun 2024 16:04:29 +0200 Subject: [PATCH 354/430] chore: remove '=== impl' comments (#8068) Sorry @mattsse but these are completely useless :D --- crates/anvil/core/src/eth/subscription.rs | 4 ---- crates/anvil/server/src/config.rs | 2 -- crates/anvil/server/src/pubsub.rs | 4 ---- crates/anvil/src/config.rs | 4 ---- crates/anvil/src/eth/api.rs | 4 ---- crates/anvil/src/eth/backend/cheats.rs | 2 -- crates/anvil/src/eth/backend/db.rs | 4 ---- crates/anvil/src/eth/backend/fork.rs | 6 ------ crates/anvil/src/eth/backend/genesis.rs | 2 -- crates/anvil/src/eth/backend/info.rs | 2 -- crates/anvil/src/eth/backend/mem/inspector.rs | 2 -- crates/anvil/src/eth/backend/mem/storage.rs | 10 ---------- crates/anvil/src/eth/backend/time.rs | 2 -- crates/anvil/src/eth/fees.rs | 4 ---- crates/anvil/src/eth/miner.rs | 10 ---------- crates/anvil/src/eth/pool/transactions.rs | 4 ---- crates/anvil/src/filter.rs | 6 ------ crates/anvil/src/logging.rs | 4 ---- crates/anvil/src/pubsub.rs | 6 ------ crates/anvil/src/server/handler.rs | 2 -- crates/anvil/src/service.rs | 2 -- crates/anvil/src/tasks/mod.rs | 2 -- crates/anvil/tests/it/fork.rs | 1 - crates/common/src/shell.rs | 6 ------ crates/config/src/endpoints.rs | 6 ------ crates/config/src/error.rs | 2 -- crates/config/src/etherscan.rs | 10 ---------- crates/config/src/fs_permissions.rs | 6 ------ crates/config/src/resolve.rs | 2 -- crates/evm/core/src/backend/mod.rs | 8 -------- crates/evm/core/src/backend/snapshot.rs | 2 -- crates/evm/core/src/fork/database.rs | 2 -- crates/evm/core/src/fork/multi.rs | 6 ------ crates/forge/bin/cmd/fmt.rs | 2 -- crates/forge/bin/cmd/test/filter.rs | 2 -- crates/script/src/lib.rs | 2 -- 36 files changed, 145 deletions(-) diff --git a/crates/anvil/core/src/eth/subscription.rs b/crates/anvil/core/src/eth/subscription.rs index cc162cc4d..cec95ba31 100644 --- a/crates/anvil/core/src/eth/subscription.rs +++ b/crates/anvil/core/src/eth/subscription.rs @@ -14,8 +14,6 @@ pub enum SubscriptionId { String(String), } -// === impl SubscriptionId === - impl SubscriptionId { /// Generates a new random hex identifier pub fn random_hex() -> Self { @@ -47,8 +45,6 @@ pub struct HexIdProvider { len: usize, } -// === impl HexIdProvider === - impl HexIdProvider { /// Generates a random hex encoded Id pub fn gen(&self) -> String { diff --git a/crates/anvil/server/src/config.rs b/crates/anvil/server/src/config.rs index f968a6da3..29b0e8812 100644 --- a/crates/anvil/server/src/config.rs +++ b/crates/anvil/server/src/config.rs @@ -25,8 +25,6 @@ pub struct ServerConfig { pub no_cors: bool, } -// === impl ServerConfig === - impl ServerConfig { /// Sets the "allow origin" header for cors pub fn with_allow_origin(mut self, allow_origin: impl Into) -> Self { diff --git a/crates/anvil/server/src/pubsub.rs b/crates/anvil/server/src/pubsub.rs index 3c9be85bc..2fe4358ef 100644 --- a/crates/anvil/server/src/pubsub.rs +++ b/crates/anvil/server/src/pubsub.rs @@ -40,8 +40,6 @@ pub struct PubSubContext { subscriptions: Subscriptions, } -// === impl PubSubContext === - impl PubSubContext { /// Adds new active subscription /// @@ -125,8 +123,6 @@ pub struct PubSubConnection { pending: VecDeque, } -// === impl PubSubConnection === - impl PubSubConnection { pub fn new(connection: Connection, handler: Handler) -> Self { Self { diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 9cac52cfe..7ee7d5e34 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -350,8 +350,6 @@ Genesis Timestamp } } -// === impl NodeConfig === - impl NodeConfig { /// Returns a new config intended to be used in tests, which does not print and binds to a /// random, free port by setting it to `0` @@ -1179,8 +1177,6 @@ pub struct PruneStateHistoryConfig { pub max_memory_history: Option, } -// === impl PruneStateHistoryConfig === - impl PruneStateHistoryConfig { /// Returns `true` if writing state history is supported pub fn is_state_history_supported(&self) -> bool { diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 25a6b69bb..6d4da2af4 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -116,8 +116,6 @@ pub struct EthApi { instance_id: Arc>, } -// === impl Eth RPC API === - impl EthApi { /// Creates a new instance #[allow(clippy::too_many_arguments)] @@ -2114,8 +2112,6 @@ impl EthApi { } } -// === impl EthApi utility functions === - impl EthApi { /// Executes the future on a new blocking task. async fn on_blocking_task(&self, c: C) -> Result diff --git a/crates/anvil/src/eth/backend/cheats.rs b/crates/anvil/src/eth/backend/cheats.rs index 0dc3189b0..dbc58670f 100644 --- a/crates/anvil/src/eth/backend/cheats.rs +++ b/crates/anvil/src/eth/backend/cheats.rs @@ -14,8 +14,6 @@ pub struct CheatsManager { state: Arc>, } -// === impl CheatsManager === - impl CheatsManager { /// Sets the account to impersonate /// diff --git a/crates/anvil/src/eth/backend/db.rs b/crates/anvil/src/eth/backend/db.rs index 34ad343b5..852956d73 100644 --- a/crates/anvil/src/eth/backend/db.rs +++ b/crates/anvil/src/eth/backend/db.rs @@ -266,8 +266,6 @@ impl> MaybeForkedDatabase for CacheDB { /// Represents a state at certain point pub struct StateDb(pub(crate) Box); -// === impl StateDB === - impl StateDb { pub fn new(db: impl MaybeFullDatabase + Send + Sync + 'static) -> Self { Self(Box::new(db)) @@ -322,8 +320,6 @@ pub struct SerializableState { pub best_block_number: Option, } -// === impl SerializableState === - impl SerializableState { /// Loads the `Genesis` object from the given json file path pub fn load(path: impl AsRef) -> Result { diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index b193a6fa7..04d62f48b 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -39,8 +39,6 @@ pub struct ClientFork { pub database: Arc>>, } -// === impl ClientFork === - impl ClientFork { /// Creates a new instance of the fork pub fn new(config: ClientForkConfig, database: Arc>>) -> Self { @@ -599,8 +597,6 @@ pub struct ClientForkConfig { pub total_difficulty: U256, } -// === impl ClientForkConfig === - impl ClientForkConfig { /// Updates the provider URL /// @@ -659,8 +655,6 @@ pub struct ForkedStorage { pub code_at: HashMap<(Address, u64), Bytes>, } -// === impl ForkedStorage === - impl ForkedStorage { /// Clears all data pub fn clear(&mut self) { diff --git a/crates/anvil/src/eth/backend/genesis.rs b/crates/anvil/src/eth/backend/genesis.rs index bbfbda55e..ebe6e6f6e 100644 --- a/crates/anvil/src/eth/backend/genesis.rs +++ b/crates/anvil/src/eth/backend/genesis.rs @@ -32,8 +32,6 @@ pub struct GenesisConfig { pub genesis_init: Option, } -// === impl GenesisConfig === - impl GenesisConfig { /// Returns fresh `AccountInfo`s for the configured `accounts` pub fn account_infos(&self) -> impl Iterator + '_ { diff --git a/crates/anvil/src/eth/backend/info.rs b/crates/anvil/src/eth/backend/info.rs index 7b4c5d9c7..448dc660a 100644 --- a/crates/anvil/src/eth/backend/info.rs +++ b/crates/anvil/src/eth/backend/info.rs @@ -16,8 +16,6 @@ pub struct StorageInfo { backend: Arc, } -// === impl StorageInfo === - impl StorageInfo { pub(crate) fn new(backend: Arc) -> Self { Self { backend } diff --git a/crates/anvil/src/eth/backend/mem/inspector.rs b/crates/anvil/src/eth/backend/mem/inspector.rs index 892fa1778..0fe0f26af 100644 --- a/crates/anvil/src/eth/backend/mem/inspector.rs +++ b/crates/anvil/src/eth/backend/mem/inspector.rs @@ -23,8 +23,6 @@ pub struct Inspector { pub log_collector: LogCollector, } -// === impl Inspector === - impl Inspector { /// Called after the inspecting the evm /// diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index 916b07f64..1f97addb4 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -35,8 +35,6 @@ const MIN_HISTORY_LIMIT: usize = 10; // 1hr of up-time at lowest 1s interval const MAX_ON_DISK_HISTORY_LIMIT: usize = 3_600; -// === impl DiskStateCache === - /// Represents the complete state of single block pub struct InMemoryBlockStates { /// The states at a certain block @@ -59,8 +57,6 @@ pub struct InMemoryBlockStates { disk_cache: DiskStateCache, } -// === impl InMemoryBlockStates === - impl InMemoryBlockStates { /// Creates a new instance with limited slots pub fn new(limit: usize) -> Self { @@ -296,8 +292,6 @@ impl BlockchainStorage { } } -// === impl BlockchainStorage === - impl BlockchainStorage { /// Returns the hash for [BlockNumberOrTag] pub fn hash(&self, number: BlockNumberOrTag) -> Option { @@ -334,8 +328,6 @@ pub struct Blockchain { pub storage: Arc>, } -// === impl BlockchainStorage === - impl Blockchain { /// Creates a new storage with a genesis block pub fn new(env: &Env, base_fee: Option, timestamp: u64) -> Self { @@ -395,8 +387,6 @@ pub struct MinedTransaction { pub block_number: u64, } -// === impl MinedTransaction === - impl MinedTransaction { /// Returns the traces of the transaction for `trace_transaction` pub fn parity_traces(&self) -> Vec { diff --git a/crates/anvil/src/eth/backend/time.rs b/crates/anvil/src/eth/backend/time.rs index ce6900eb4..5cad24f2b 100644 --- a/crates/anvil/src/eth/backend/time.rs +++ b/crates/anvil/src/eth/backend/time.rs @@ -25,8 +25,6 @@ pub struct TimeManager { interval: Arc>>, } -// === impl TimeManager === - impl TimeManager { pub fn new(start_timestamp: u64) -> TimeManager { let time_manager = TimeManager { diff --git a/crates/anvil/src/eth/fees.rs b/crates/anvil/src/eth/fees.rs index 11f84ea5e..8c0104a1b 100644 --- a/crates/anvil/src/eth/fees.rs +++ b/crates/anvil/src/eth/fees.rs @@ -55,8 +55,6 @@ pub struct FeeManager { elasticity: Arc>, } -// === impl FeeManager === - impl FeeManager { pub fn new( spec_id: SpecId, @@ -195,8 +193,6 @@ pub struct FeeHistoryService { storage_info: StorageInfo, } -// === impl FeeHistoryService === - impl FeeHistoryService { pub fn new( new_blocks: NewBlockNotifications, diff --git a/crates/anvil/src/eth/miner.rs b/crates/anvil/src/eth/miner.rs index 2199a5061..442c6fe7b 100644 --- a/crates/anvil/src/eth/miner.rs +++ b/crates/anvil/src/eth/miner.rs @@ -27,8 +27,6 @@ pub struct Miner { inner: Arc, } -// === impl Miner === - impl Miner { /// Returns a new miner with that operates in the given `mode` pub fn new(mode: MiningMode) -> Self { @@ -79,8 +77,6 @@ pub struct MinerInner { waker: AtomicWaker, } -// === impl MinerInner === - impl MinerInner { /// Call the waker again fn wake(&self) { @@ -112,8 +108,6 @@ pub enum MiningMode { FixedBlockTime(FixedBlockTimeMiner), } -// === impl MiningMode === - impl MiningMode { pub fn instant(max_transactions: usize, listener: Receiver) -> Self { MiningMode::Auto(ReadyTransactionMiner { @@ -151,8 +145,6 @@ pub struct FixedBlockTimeMiner { interval: Interval, } -// === impl FixedBlockTimeMiner === - impl FixedBlockTimeMiner { /// Creates a new instance with an interval of `duration` pub fn new(duration: Duration) -> Self { @@ -185,8 +177,6 @@ pub struct ReadyTransactionMiner { rx: Fuse>, } -// === impl ReadyTransactionMiner === - impl ReadyTransactionMiner { fn poll(&mut self, pool: &Arc, cx: &mut Context<'_>) -> Poll>> { // drain the notification stream diff --git a/crates/anvil/src/eth/pool/transactions.rs b/crates/anvil/src/eth/pool/transactions.rs index 9a229001d..23841b0d7 100644 --- a/crates/anvil/src/eth/pool/transactions.rs +++ b/crates/anvil/src/eth/pool/transactions.rs @@ -37,8 +37,6 @@ pub enum TransactionOrder { Fees, } -// === impl TransactionOrder === - impl TransactionOrder { /// Returns the priority of the transactions pub fn priority(&self, tx: &TypedTransaction) -> TransactionPriority { @@ -686,8 +684,6 @@ pub struct ReadyTransaction { pub requires_offset: usize, } -// === impl ReadyTransaction == - impl ReadyTransaction { pub fn provides(&self) -> &[TxMarker] { &self.transaction.transaction.provides diff --git a/crates/anvil/src/filter.rs b/crates/anvil/src/filter.rs index 268a8f46e..b1d2370f5 100644 --- a/crates/anvil/src/filter.rs +++ b/crates/anvil/src/filter.rs @@ -33,8 +33,6 @@ pub struct Filters { keepalive: Duration, } -// === impl Filters === - impl Filters { /// Adds a new `EthFilter` to the set pub async fn add_filter(&self, filter: EthFilter) -> String { @@ -123,8 +121,6 @@ pub enum EthFilter { PendingTransactions(Receiver), } -// === impl EthFilter === - impl Stream for EthFilter { type Item = ResponseResult; @@ -165,8 +161,6 @@ pub struct LogsFilter { pub historic: Option>, } -// === impl LogsFilter === - impl LogsFilter { /// Returns all the logs since the last time this filter was polled pub fn poll(&mut self, cx: &mut Context<'_>) -> Vec { diff --git a/crates/anvil/src/logging.rs b/crates/anvil/src/logging.rs index 5819aafc9..ba97ab7ef 100644 --- a/crates/anvil/src/logging.rs +++ b/crates/anvil/src/logging.rs @@ -17,8 +17,6 @@ pub struct NodeLogLayer { state: LoggingManager, } -// === impl NodeLogLayer === - impl NodeLogLayer { /// Returns a new instance of this layer pub fn new(state: LoggingManager) -> Self { @@ -51,8 +49,6 @@ pub struct LoggingManager { pub enabled: Arc>, } -// === impl LoggingManager === - impl LoggingManager { /// Returns true if logging is currently enabled pub fn is_enabled(&self) -> bool { diff --git a/crates/anvil/src/pubsub.rs b/crates/anvil/src/pubsub.rs index a063ee669..46caa9d7c 100644 --- a/crates/anvil/src/pubsub.rs +++ b/crates/anvil/src/pubsub.rs @@ -24,8 +24,6 @@ pub struct LogsSubscription { pub id: SubscriptionId, } -// === impl LogsSubscription === - impl LogsSubscription { fn poll(&mut self, cx: &mut Context<'_>) -> Poll> { loop { @@ -68,8 +66,6 @@ pub struct EthSubscriptionResponse { params: EthSubscriptionParams, } -// === impl EthSubscriptionResponse === - impl EthSubscriptionResponse { pub fn new(params: EthSubscriptionParams) -> Self { Self { jsonrpc: Version::V2, method: "eth_subscription", params } @@ -92,8 +88,6 @@ pub enum EthSubscription { PendingTransactions(Receiver, SubscriptionId), } -// === impl EthSubscription === - impl EthSubscription { fn poll_response(&mut self, cx: &mut Context<'_>) -> Poll> { match self { diff --git a/crates/anvil/src/server/handler.rs b/crates/anvil/src/server/handler.rs index 2cab0d5b7..79adb87df 100644 --- a/crates/anvil/src/server/handler.rs +++ b/crates/anvil/src/server/handler.rs @@ -19,8 +19,6 @@ pub struct HttpEthRpcHandler { api: EthApi, } -// === impl WsEthRpcHandler === - impl HttpEthRpcHandler { /// Creates a new instance of the handler using the given `EthApi` pub fn new(api: EthApi) -> Self { diff --git a/crates/anvil/src/service.rs b/crates/anvil/src/service.rs index 71cfdfecb..6759f6812 100644 --- a/crates/anvil/src/service.rs +++ b/crates/anvil/src/service.rs @@ -112,8 +112,6 @@ struct BlockProducer { queued: VecDeque>>, } -// === impl BlockProducer === - impl BlockProducer { fn new(backend: Arc) -> Self { Self { idle_backend: Some(backend), block_mining: None, queued: Default::default() } diff --git a/crates/anvil/src/tasks/mod.rs b/crates/anvil/src/tasks/mod.rs index e42b0437c..a63b19016 100644 --- a/crates/anvil/src/tasks/mod.rs +++ b/crates/anvil/src/tasks/mod.rs @@ -22,8 +22,6 @@ pub struct TaskManager { on_shutdown: Shutdown, } -// === impl TaskManager === - impl TaskManager { /// Creates a new instance of the task manager pub fn new(tokio_handle: Handle, on_shutdown: Shutdown) -> Self { diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index 87a1b6985..19377e845 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -34,7 +34,6 @@ pub struct LocalFork { fork_handle: NodeHandle, } -// === impl LocalFork === #[allow(dead_code)] impl LocalFork { /// Spawns two nodes with the test config diff --git a/crates/common/src/shell.rs b/crates/common/src/shell.rs index 9955269ee..89d990536 100644 --- a/crates/common/src/shell.rs +++ b/crates/common/src/shell.rs @@ -70,8 +70,6 @@ pub struct Shell { verbosity: Verbosity, } -// === impl Shell === - impl Shell { /// Creates a new shell instance pub fn new(output: ShellOut, verbosity: Verbosity) -> Self { @@ -210,8 +208,6 @@ pub enum ShellOut { Stream, } -// === impl ShellOut === - impl ShellOut { /// Creates a new shell that writes to memory pub fn memory() -> Self { @@ -294,8 +290,6 @@ pub enum Verbosity { Silent, } -// === impl Verbosity === - impl Verbosity { /// Returns true if json mode pub fn is_json(&self) -> bool { diff --git a/crates/config/src/endpoints.rs b/crates/config/src/endpoints.rs index 36cc8c7b6..f29e72545 100644 --- a/crates/config/src/endpoints.rs +++ b/crates/config/src/endpoints.rs @@ -15,8 +15,6 @@ pub struct RpcEndpoints { endpoints: BTreeMap, } -// === impl RpcEndpoints === - impl RpcEndpoints { /// Creates a new list of endpoints pub fn new( @@ -132,8 +130,6 @@ pub enum RpcEndpoint { Env(String), } -// === impl RpcEndpoint === - impl RpcEndpoint { /// Returns the url variant pub fn as_url(&self) -> Option<&str> { @@ -341,8 +337,6 @@ pub struct ResolvedRpcEndpoints { endpoints: BTreeMap>, } -// === impl ResolvedEndpoints === - impl ResolvedRpcEndpoints { /// Returns true if there's an endpoint that couldn't be resolved pub fn has_unresolved(&self) -> bool { diff --git a/crates/config/src/error.rs b/crates/config/src/error.rs index 448553cdc..fe193171a 100644 --- a/crates/config/src/error.rs +++ b/crates/config/src/error.rs @@ -140,8 +140,6 @@ pub enum SolidityErrorCode { Other(u64), } -// === impl SolidityErrorCode === - impl SolidityErrorCode { /// The textual identifier for this error /// diff --git a/crates/config/src/etherscan.rs b/crates/config/src/etherscan.rs index fb254a635..8863ad059 100644 --- a/crates/config/src/etherscan.rs +++ b/crates/config/src/etherscan.rs @@ -66,8 +66,6 @@ pub struct EtherscanConfigs { configs: BTreeMap, } -// === impl Endpoints === - impl EtherscanConfigs { /// Creates a new list of etherscan configs pub fn new(configs: impl IntoIterator, EtherscanConfig)>) -> Self { @@ -121,8 +119,6 @@ pub struct ResolvedEtherscanConfigs { configs: BTreeMap>, } -// === impl ResolvedEtherscanConfigs === - impl ResolvedEtherscanConfigs { /// Creates a new list of resolved etherscan configs pub fn new( @@ -181,8 +177,6 @@ pub struct EtherscanConfig { pub key: EtherscanApiKey, } -// === impl EtherscanConfig === - impl EtherscanConfig { /// Returns the etherscan config required to create a client. /// @@ -262,8 +256,6 @@ pub struct ResolvedEtherscanConfig { pub chain: Option, } -// === impl ResolvedEtherscanConfig === - impl ResolvedEtherscanConfig { /// Creates a new instance using the api key and chain pub fn create(api_key: impl Into, chain: impl Into) -> Option { @@ -350,8 +342,6 @@ pub enum EtherscanApiKey { Env(String), } -// === impl EtherscanApiKey === - impl EtherscanApiKey { /// Returns the key variant pub fn as_key(&self) -> Option<&str> { diff --git a/crates/config/src/fs_permissions.rs b/crates/config/src/fs_permissions.rs index fbbe67af9..6a75beb05 100644 --- a/crates/config/src/fs_permissions.rs +++ b/crates/config/src/fs_permissions.rs @@ -17,8 +17,6 @@ pub struct FsPermissions { pub permissions: Vec, } -// === impl FsPermissions === - impl FsPermissions { /// Creates anew instance with the given `permissions` pub fn new(permissions: impl IntoIterator) -> Self { @@ -108,8 +106,6 @@ pub struct PathPermission { pub path: PathBuf, } -// === impl PathPermission === - impl PathPermission { /// Returns a new permission for the path and the given access pub fn new(path: impl Into, access: FsAccessPermission) -> Self { @@ -174,8 +170,6 @@ pub enum FsAccessPermission { Write, } -// === impl FsAccessPermission === - impl FsAccessPermission { /// Returns true if the access is allowed pub fn is_granted(&self, kind: FsAccessKind) -> bool { diff --git a/crates/config/src/resolve.rs b/crates/config/src/resolve.rs index b817f4367..981ddc886 100644 --- a/crates/config/src/resolve.rs +++ b/crates/config/src/resolve.rs @@ -19,8 +19,6 @@ pub struct UnresolvedEnvVarError { pub source: VarError, } -// === impl UnresolvedEnvVarError === - impl UnresolvedEnvVarError { /// Tries to resolve a value pub fn try_resolve(&self) -> Result { diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index 1e648491d..94a1eeead 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -414,8 +414,6 @@ pub struct Backend { inner: BackendInner, } -// === impl Backend === - impl Backend { /// Creates a new Backend with a spawned multi fork thread. pub fn spawn(fork: Option) -> Self { @@ -934,8 +932,6 @@ impl Backend { } } -// === impl a bunch of `revm::Database` adjacent implementations === - impl DatabaseExt for Backend { fn snapshot(&mut self, journaled_state: &JournaledState, env: &Env) -> U256 { trace!("create snapshot"); @@ -1514,8 +1510,6 @@ pub struct Fork { journaled_state: JournaledState, } -// === impl Fork === - impl Fork { /// Returns true if the account is a contract pub fn is_contract(&self, acc: Address) -> bool { @@ -1587,8 +1581,6 @@ pub struct BackendInner { pub cheatcode_access_accounts: HashSet
, } -// === impl BackendInner === - impl BackendInner { pub fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId> { self.issued_local_fork_ids diff --git a/crates/evm/core/src/backend/snapshot.rs b/crates/evm/core/src/backend/snapshot.rs index 4c0c665f2..5fd0cfa77 100644 --- a/crates/evm/core/src/backend/snapshot.rs +++ b/crates/evm/core/src/backend/snapshot.rs @@ -23,8 +23,6 @@ pub struct BackendSnapshot { pub env: Env, } -// === impl BackendSnapshot === - impl BackendSnapshot { /// Takes a new snapshot pub fn new(db: T, journaled_state: JournaledState, env: Env) -> Self { diff --git a/crates/evm/core/src/fork/database.rs b/crates/evm/core/src/fork/database.rs index 4b1e3d6b0..2712b5779 100644 --- a/crates/evm/core/src/fork/database.rs +++ b/crates/evm/core/src/fork/database.rs @@ -207,8 +207,6 @@ pub struct ForkDbSnapshot { pub snapshot: StateSnapshot, } -// === impl DbSnapshot === - impl ForkDbSnapshot { fn get_storage(&self, address: Address, index: U256) -> Option { self.local.accounts.get(&address).and_then(|account| account.storage.get(&index)).copied() diff --git a/crates/evm/core/src/fork/multi.rs b/crates/evm/core/src/fork/multi.rs index da1f980ff..bece8c4f5 100644 --- a/crates/evm/core/src/fork/multi.rs +++ b/crates/evm/core/src/fork/multi.rs @@ -72,8 +72,6 @@ pub struct MultiFork { _shutdown: Arc, } -// === impl MultiForkBackend === - impl MultiFork { /// Creates a new pair multi fork pair pub fn new() -> (Self, MultiForkHandler) { @@ -222,8 +220,6 @@ pub struct MultiForkHandler { flush_cache_interval: Option, } -// === impl MultiForkHandler === - impl MultiForkHandler { fn new(incoming: Receiver) -> Self { Self { @@ -438,8 +434,6 @@ struct CreatedFork { num_senders: Arc, } -// === impl CreatedFork === - impl CreatedFork { pub fn new(opts: CreateFork, backend: SharedBackend) -> Self { Self { opts, backend, num_senders: Arc::new(AtomicUsize::new(1)) } diff --git a/crates/forge/bin/cmd/fmt.rs b/crates/forge/bin/cmd/fmt.rs index b3207bcb7..34df8dbcf 100644 --- a/crates/forge/bin/cmd/fmt.rs +++ b/crates/forge/bin/cmd/fmt.rs @@ -43,8 +43,6 @@ pub struct FmtArgs { impl_figment_convert_basic!(FmtArgs); -// === impl FmtArgs === - impl FmtArgs { pub fn run(self) -> Result<()> { let config = self.try_load_config_emit_warnings()?; diff --git a/crates/forge/bin/cmd/test/filter.rs b/crates/forge/bin/cmd/test/filter.rs index c30b3434a..1a7f355d7 100644 --- a/crates/forge/bin/cmd/test/filter.rs +++ b/crates/forge/bin/cmd/test/filter.rs @@ -164,8 +164,6 @@ pub struct ProjectPathsAwareFilter { paths: ProjectPathsConfig, } -// === impl ProjectPathsAwareFilter === - impl ProjectPathsAwareFilter { /// Returns true if the filter is empty. pub fn is_empty(&self) -> bool { diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 95f88dd8d..bd0e23736 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -197,8 +197,6 @@ pub struct ScriptArgs { pub retry: RetryArgs, } -// === impl ScriptArgs === - impl ScriptArgs { async fn preprocess(self) -> Result { let script_wallets = From d5fb75006c668935398f26516400b9f193a7caae Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 5 Jun 2024 16:17:11 +0200 Subject: [PATCH 355/430] chore: add and use workspace.lints (#8067) * chore: add and use workspace.lints * chore: clippy --fix * fixdoc --- Cargo.lock | 6 - Cargo.toml | 14 ++ crates/anvil/Cargo.toml | 3 + crates/anvil/core/Cargo.toml | 8 +- crates/anvil/core/src/eth/proof.rs | 2 +- crates/anvil/core/src/eth/subscription.rs | 10 +- crates/anvil/core/src/eth/transaction/mod.rs | 211 +++++++++--------- crates/anvil/core/src/lib.rs | 7 + crates/anvil/core/src/types.rs | 13 +- crates/anvil/rpc/Cargo.toml | 6 +- crates/anvil/rpc/src/error.rs | 62 +++-- crates/anvil/rpc/src/lib.rs | 7 + crates/anvil/rpc/src/request.rs | 8 +- crates/anvil/rpc/src/response.rs | 12 +- crates/anvil/server/Cargo.toml | 3 + crates/anvil/server/src/config.rs | 4 +- crates/anvil/server/src/ipc.rs | 2 +- crates/anvil/server/src/lib.rs | 3 +- crates/anvil/src/cmd.rs | 8 +- crates/anvil/src/config.rs | 2 +- crates/anvil/src/eth/backend/mem/cache.rs | 2 +- .../anvil/src/eth/backend/mem/in_memory_db.rs | 2 +- crates/anvil/src/eth/backend/mem/mod.rs | 4 +- crates/anvil/src/eth/backend/mem/storage.rs | 2 +- crates/anvil/src/eth/backend/time.rs | 4 +- crates/anvil/src/eth/error.rs | 76 +++---- crates/anvil/src/eth/fees.rs | 16 +- crates/anvil/src/eth/miner.rs | 10 +- crates/anvil/src/eth/otterscan/types.rs | 10 +- crates/anvil/src/eth/pool/mod.rs | 4 +- crates/anvil/src/eth/pool/transactions.rs | 8 +- crates/anvil/src/filter.rs | 6 +- crates/anvil/src/hardfork.rs | 132 +++++------ crates/anvil/src/lib.rs | 5 +- crates/anvil/src/pubsub.rs | 6 +- crates/anvil/src/service.rs | 2 +- crates/anvil/tests/it/transaction.rs | 3 +- crates/cast/Cargo.toml | 3 + crates/cast/bin/cmd/access_list.rs | 4 +- crates/cast/bin/cmd/call.rs | 2 +- crates/cast/bin/cmd/create2.rs | 2 +- crates/cast/bin/cmd/estimate.rs | 2 +- crates/cast/bin/cmd/find_block.rs | 2 +- crates/cast/bin/cmd/interface.rs | 9 +- crates/cast/bin/cmd/logs.rs | 4 +- crates/cast/bin/cmd/mktx.rs | 2 +- crates/cast/bin/cmd/rpc.rs | 2 +- crates/cast/bin/cmd/run.rs | 2 +- crates/cast/bin/cmd/send.rs | 2 +- crates/cast/bin/cmd/wallet/mod.rs | 25 +-- crates/cast/bin/cmd/wallet/vanity.rs | 2 +- crates/cast/bin/main.rs | 2 +- crates/cast/src/base.rs | 4 +- crates/cast/src/errors.rs | 8 +- crates/cast/src/lib.rs | 25 ++- crates/cast/src/rlp_converter.rs | 30 +-- crates/cheatcodes/Cargo.toml | 3 + crates/cheatcodes/spec/Cargo.toml | 3 + crates/cheatcodes/spec/src/lib.rs | 3 +- crates/cheatcodes/spec/src/vm.rs | 28 ++- crates/cheatcodes/src/evm/prank.rs | 14 +- crates/cheatcodes/src/json.rs | 4 +- crates/cheatcodes/src/lib.rs | 3 +- crates/cheatcodes/src/test/assert.rs | 8 +- crates/chisel/Cargo.toml | 3 + crates/chisel/src/cmd.rs | 44 ++-- crates/chisel/src/dispatcher.rs | 13 +- crates/chisel/src/executor.rs | 10 +- crates/chisel/src/lib.rs | 5 +- crates/chisel/src/session.rs | 6 +- crates/chisel/src/session_source.rs | 2 +- crates/chisel/src/solidity_helper.rs | 4 +- crates/chisel/tests/cache.rs | 14 +- crates/cli/Cargo.toml | 3 + crates/cli/src/lib.rs | 7 +- crates/cli/src/opts/build/core.rs | 2 +- crates/cli/src/opts/dependency.rs | 2 +- crates/cli/src/utils/cmd.rs | 4 +- crates/common/Cargo.toml | 3 + crates/common/src/compile.rs | 16 +- crates/common/src/contracts.rs | 11 +- crates/common/src/ens.rs | 14 +- crates/common/src/errors/fs.rs | 18 +- crates/common/src/fmt/console.rs | 6 +- crates/common/src/fmt/ui.rs | 18 +- crates/common/src/lib.rs | 6 +- crates/common/src/provider/mod.rs | 6 +- .../common/src/provider/runtime_transport.rs | 7 +- crates/common/src/selectors.rs | 4 +- crates/common/src/serde_helpers.rs | 12 +- crates/common/src/shell.rs | 24 +- crates/common/src/term.rs | 8 +- crates/config/src/lib.rs | 5 +- crates/debugger/Cargo.toml | 3 + crates/debugger/src/lib.rs | 3 +- crates/debugger/src/tui/context.rs | 12 +- crates/debugger/src/tui/draw.rs | 4 +- crates/doc/Cargo.toml | 3 + crates/doc/src/document.rs | 34 ++- crates/doc/src/lib.rs | 12 +- crates/doc/src/parser/comment.rs | 30 +-- crates/doc/src/parser/item.rs | 18 +- crates/doc/src/parser/mod.rs | 2 +- .../doc/src/preprocessor/infer_hyperlinks.rs | 2 +- crates/evm/core/Cargo.toml | 3 + crates/evm/core/src/backend/diagnostic.rs | 4 +- crates/evm/core/src/backend/error.rs | 14 +- crates/evm/core/src/backend/mod.rs | 2 +- crates/evm/core/src/backend/snapshot.rs | 2 +- crates/evm/core/src/fork/cache.rs | 8 +- crates/evm/core/src/fork/multi.rs | 2 +- crates/evm/core/src/lib.rs | 3 +- crates/evm/coverage/Cargo.toml | 3 + crates/evm/coverage/src/lib.rs | 11 +- crates/evm/evm/Cargo.toml | 3 + crates/evm/evm/src/executors/invariant/mod.rs | 2 +- crates/evm/evm/src/executors/mod.rs | 8 +- crates/evm/evm/src/lib.rs | 3 +- crates/evm/fuzz/Cargo.toml | 3 + .../evm/fuzz/src/invariant/call_override.rs | 2 +- crates/evm/fuzz/src/invariant/filters.rs | 2 +- crates/evm/fuzz/src/lib.rs | 13 +- crates/evm/fuzz/src/strategies/int.rs | 4 +- crates/evm/fuzz/src/strategies/state.rs | 2 +- crates/evm/traces/Cargo.toml | 3 + crates/evm/traces/src/decoder/mod.rs | 4 +- crates/evm/traces/src/lib.rs | 5 +- crates/fmt/Cargo.toml | 3 + crates/fmt/src/buffer.rs | 8 +- crates/fmt/src/chunk.rs | 6 +- crates/fmt/src/comments.rs | 14 +- crates/fmt/src/helpers.rs | 4 +- crates/fmt/src/inline_config.rs | 10 +- crates/fmt/src/lib.rs | 1 + crates/fmt/src/solang_ext/ast_eq.rs | 24 +- crates/fmt/src/string.rs | 14 +- crates/fmt/src/visit.rs | 120 +++++----- crates/fmt/tests/formatter.rs | 2 +- crates/forge/Cargo.toml | 3 + crates/forge/README.md | 4 +- crates/forge/bin/cmd/bind.rs | 2 +- crates/forge/bin/cmd/cache.rs | 10 +- crates/forge/bin/cmd/clone.rs | 14 +- crates/forge/bin/cmd/coverage.rs | 2 +- crates/forge/bin/cmd/create.rs | 6 +- crates/forge/bin/cmd/flatten.rs | 2 +- crates/forge/bin/cmd/generate/mod.rs | 2 +- crates/forge/bin/cmd/init.rs | 2 +- crates/forge/bin/cmd/inspect.rs | 2 +- crates/forge/bin/cmd/install.rs | 4 +- crates/forge/bin/cmd/selectors.rs | 8 +- crates/forge/bin/cmd/snapshot.rs | 8 +- crates/forge/bin/cmd/test/mod.rs | 2 +- crates/forge/bin/cmd/watch.rs | 2 +- crates/forge/src/coverage.rs | 16 +- crates/forge/src/lib.rs | 3 + crates/forge/src/result.rs | 18 +- crates/forge/tests/cli/config.rs | 1 - crates/forge/tests/cli/verify.rs | 2 +- crates/forge/tests/it/test_helpers.rs | 6 +- crates/linking/Cargo.toml | 3 + crates/linking/src/lib.rs | 9 +- crates/macros/Cargo.toml | 3 + crates/macros/src/lib.rs | 7 +- crates/script/Cargo.toml | 3 + crates/script/src/broadcast.rs | 8 +- crates/script/src/build.rs | 2 +- crates/script/src/lib.rs | 5 + crates/script/src/multi_sequence.rs | 12 +- crates/script/src/progress.rs | 8 +- crates/script/src/providers.rs | 4 +- crates/script/src/receipts.rs | 4 +- crates/script/src/sequence.rs | 21 +- crates/script/src/transaction.rs | 2 +- crates/script/src/verify.rs | 2 +- crates/test-utils/Cargo.toml | 3 + crates/test-utils/src/filter.rs | 4 +- crates/test-utils/src/lib.rs | 7 +- crates/test-utils/src/script.rs | 26 +-- crates/test-utils/src/util.rs | 16 +- crates/verify/Cargo.toml | 3 + crates/verify/src/bytecode.rs | 16 +- crates/verify/src/lib.rs | 7 +- crates/verify/src/provider.rs | 28 +-- crates/verify/src/retry.rs | 2 +- crates/wallets/Cargo.toml | 9 +- crates/wallets/src/error.rs | 2 +- crates/wallets/src/lib.rs | 7 + crates/wallets/src/wallet_signer.rs | 8 +- 189 files changed, 992 insertions(+), 951 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b802db9cd..920d46dd1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -780,16 +780,13 @@ dependencies = [ "alloy-consensus", "alloy-dyn-abi", "alloy-eips", - "alloy-network", "alloy-primitives", "alloy-rlp", "alloy-rpc-types", "alloy-rpc-types-trace", "alloy-serde", "alloy-trie", - "anvil-core", "bytes", - "c-kzg", "foundry-common", "foundry-evm", "rand", @@ -802,7 +799,6 @@ dependencies = [ name = "anvil-rpc" version = "0.2.0" dependencies = [ - "rand", "serde", "serde_json", ] @@ -3743,9 +3739,7 @@ dependencies = [ "const-hex", "derive_builder", "eyre", - "foundry-common", "foundry-config", - "itertools 0.13.0", "rpassword", "serde", "thiserror", diff --git a/Cargo.toml b/Cargo.toml index 3818778fa..7becafbc7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,20 @@ homepage = "https://github.com/foundry-rs/foundry" repository = "https://github.com/foundry-rs/foundry" exclude = ["benches/", "tests/", "test-data/", "testdata/"] +[workspace.lints.clippy] +dbg-macro = "warn" +manual-string-new = "warn" +uninlined-format-args = "warn" +use-self = "warn" + +[workspace.lints.rust] +rust-2018-idioms = "deny" +# unreachable-pub = "warn" +unused-must-use = "deny" + +[workspace.lints.rustdoc] +# all = "warn" + # Speed up compilation time for dev builds by reducing emitted debug info. # NOTE: Debuggers may provide less useful information with this setting. # Uncomment this section if you're using a debugger. diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index 96c0a2071..749db58db 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [[bin]] name = "anvil" path = "src/anvil.rs" diff --git a/crates/anvil/core/Cargo.toml b/crates/anvil/core/Cargo.toml index 92f907c4e..aa7a323ed 100644 --- a/crates/anvil/core/Cargo.toml +++ b/crates/anvil/core/Cargo.toml @@ -9,6 +9,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [dependencies] foundry-common.workspace = true foundry-evm.workspace = true @@ -25,7 +28,6 @@ alloy-rpc-types-trace.workspace = true alloy-serde.workspace = true alloy-rlp.workspace = true alloy-eips.workspace = true -alloy-network = { workspace = true, features = ["k256"] } alloy-consensus = { workspace = true, features = ["k256", "kzg"] } alloy-dyn-abi = { workspace = true, features = ["std", "eip712"] } alloy-trie.workspace = true @@ -33,14 +35,10 @@ alloy-trie.workspace = true serde = { workspace = true, optional = true } serde_json.workspace = true bytes = "1.4" -c-kzg = { version = "1", features = ["serde"] } # misc rand = "0.8" -[dev-dependencies] -anvil-core = { path = ".", features = ["serde"] } - [features] default = ["serde"] impersonated-tx = [] diff --git a/crates/anvil/core/src/eth/proof.rs b/crates/anvil/core/src/eth/proof.rs index ba2357bff..450e53e47 100644 --- a/crates/anvil/core/src/eth/proof.rs +++ b/crates/anvil/core/src/eth/proof.rs @@ -14,7 +14,7 @@ pub struct BasicAccount { impl Default for BasicAccount { fn default() -> Self { - BasicAccount { + Self { balance: U256::ZERO, nonce: U256::ZERO, code_hash: KECCAK_EMPTY, diff --git a/crates/anvil/core/src/eth/subscription.rs b/crates/anvil/core/src/eth/subscription.rs index cec95ba31..9d347e9a6 100644 --- a/crates/anvil/core/src/eth/subscription.rs +++ b/crates/anvil/core/src/eth/subscription.rs @@ -17,15 +17,15 @@ pub enum SubscriptionId { impl SubscriptionId { /// Generates a new random hex identifier pub fn random_hex() -> Self { - SubscriptionId::String(hex_id()) + Self::String(hex_id()) } } impl fmt::Display for SubscriptionId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - SubscriptionId::Number(num) => num.fmt(f), - SubscriptionId::String(s) => s.fmt(f), + Self::Number(num) => num.fmt(f), + Self::String(s) => s.fmt(f), } } } @@ -33,8 +33,8 @@ impl fmt::Display for SubscriptionId { impl fmt::Debug for SubscriptionId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - SubscriptionId::Number(num) => num.fmt(f), - SubscriptionId::String(s) => s.fmt(f), + Self::Number(num) => num.fmt(f), + Self::String(s) => s.fmt(f), } } } diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 6710ce1bc..0aa6b8330 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -231,13 +231,13 @@ impl From for TypedTransaction { impl From for MaybeImpersonatedTransaction { fn from(value: TypedTransaction) -> Self { - MaybeImpersonatedTransaction::new(value) + Self::new(value) } } impl Decodable for MaybeImpersonatedTransaction { fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { - TypedTransaction::decode(buf).map(MaybeImpersonatedTransaction::new) + TypedTransaction::decode(buf).map(Self::new) } } @@ -610,57 +610,57 @@ pub enum TypedTransaction { impl TypedTransaction { /// Returns true if the transaction uses dynamic fees: EIP1559 or EIP4844 pub fn is_dynamic_fee(&self) -> bool { - matches!(self, TypedTransaction::EIP1559(_)) || matches!(self, TypedTransaction::EIP4844(_)) + matches!(self, Self::EIP1559(_)) || matches!(self, Self::EIP4844(_)) } pub fn gas_price(&self) -> u128 { match self { - TypedTransaction::Legacy(tx) => tx.tx().gas_price, - TypedTransaction::EIP2930(tx) => tx.tx().gas_price, - TypedTransaction::EIP1559(tx) => tx.tx().max_fee_per_gas, - TypedTransaction::EIP4844(tx) => tx.tx().tx().max_fee_per_gas, - TypedTransaction::Deposit(_) => 0, + Self::Legacy(tx) => tx.tx().gas_price, + Self::EIP2930(tx) => tx.tx().gas_price, + Self::EIP1559(tx) => tx.tx().max_fee_per_gas, + Self::EIP4844(tx) => tx.tx().tx().max_fee_per_gas, + Self::Deposit(_) => 0, } } pub fn gas_limit(&self) -> u128 { match self { - TypedTransaction::Legacy(tx) => tx.tx().gas_limit, - TypedTransaction::EIP2930(tx) => tx.tx().gas_limit, - TypedTransaction::EIP1559(tx) => tx.tx().gas_limit, - TypedTransaction::EIP4844(tx) => tx.tx().tx().gas_limit, - TypedTransaction::Deposit(tx) => tx.gas_limit, + Self::Legacy(tx) => tx.tx().gas_limit, + Self::EIP2930(tx) => tx.tx().gas_limit, + Self::EIP1559(tx) => tx.tx().gas_limit, + Self::EIP4844(tx) => tx.tx().tx().gas_limit, + Self::Deposit(tx) => tx.gas_limit, } } pub fn value(&self) -> U256 { U256::from(match self { - TypedTransaction::Legacy(tx) => tx.tx().value, - TypedTransaction::EIP2930(tx) => tx.tx().value, - TypedTransaction::EIP1559(tx) => tx.tx().value, - TypedTransaction::EIP4844(tx) => tx.tx().tx().value, - TypedTransaction::Deposit(tx) => tx.value, + Self::Legacy(tx) => tx.tx().value, + Self::EIP2930(tx) => tx.tx().value, + Self::EIP1559(tx) => tx.tx().value, + Self::EIP4844(tx) => tx.tx().tx().value, + Self::Deposit(tx) => tx.value, }) } pub fn data(&self) -> &Bytes { match self { - TypedTransaction::Legacy(tx) => &tx.tx().input, - TypedTransaction::EIP2930(tx) => &tx.tx().input, - TypedTransaction::EIP1559(tx) => &tx.tx().input, - TypedTransaction::EIP4844(tx) => &tx.tx().tx().input, - TypedTransaction::Deposit(tx) => &tx.input, + Self::Legacy(tx) => &tx.tx().input, + Self::EIP2930(tx) => &tx.tx().input, + Self::EIP1559(tx) => &tx.tx().input, + Self::EIP4844(tx) => &tx.tx().tx().input, + Self::Deposit(tx) => &tx.input, } } /// Returns the transaction type pub fn r#type(&self) -> Option { match self { - TypedTransaction::Legacy(_) => None, - TypedTransaction::EIP2930(_) => Some(1), - TypedTransaction::EIP1559(_) => Some(2), - TypedTransaction::EIP4844(_) => Some(3), - TypedTransaction::Deposit(_) => Some(0x7E), + Self::Legacy(_) => None, + Self::EIP2930(_) => Some(1), + Self::EIP1559(_) => Some(2), + Self::EIP4844(_) => Some(3), + Self::Deposit(_) => Some(0x7E), } } @@ -682,14 +682,14 @@ impl TypedTransaction { pub fn blob_gas(&self) -> Option { match self { - TypedTransaction::EIP4844(tx) => Some(tx.tx().tx().blob_gas() as u128), + Self::EIP4844(tx) => Some(tx.tx().tx().blob_gas() as u128), _ => None, } } pub fn max_fee_per_blob_gas(&self) -> Option { match self { - TypedTransaction::EIP4844(tx) => Some(tx.tx().tx().max_fee_per_blob_gas), + Self::EIP4844(tx) => Some(tx.tx().tx().max_fee_per_blob_gas), _ => None, } } @@ -697,7 +697,7 @@ impl TypedTransaction { /// Returns a helper type that contains commonly used values as fields pub fn essentials(&self) -> TransactionEssentials { match self { - TypedTransaction::Legacy(t) => TransactionEssentials { + Self::Legacy(t) => TransactionEssentials { kind: t.tx().to, input: t.tx().input.clone(), nonce: t.tx().nonce, @@ -711,7 +711,7 @@ impl TypedTransaction { chain_id: t.tx().chain_id, access_list: Default::default(), }, - TypedTransaction::EIP2930(t) => TransactionEssentials { + Self::EIP2930(t) => TransactionEssentials { kind: t.tx().to, input: t.tx().input.clone(), nonce: t.tx().nonce, @@ -725,7 +725,7 @@ impl TypedTransaction { chain_id: Some(t.tx().chain_id), access_list: t.tx().access_list.clone(), }, - TypedTransaction::EIP1559(t) => TransactionEssentials { + Self::EIP1559(t) => TransactionEssentials { kind: t.tx().to, input: t.tx().input.clone(), nonce: t.tx().nonce, @@ -739,7 +739,7 @@ impl TypedTransaction { chain_id: Some(t.tx().chain_id), access_list: t.tx().access_list.clone(), }, - TypedTransaction::EIP4844(t) => TransactionEssentials { + Self::EIP4844(t) => TransactionEssentials { kind: TxKind::Call(t.tx().tx().to), input: t.tx().tx().input.clone(), nonce: t.tx().tx().nonce, @@ -753,7 +753,7 @@ impl TypedTransaction { chain_id: Some(t.tx().tx().chain_id), access_list: t.tx().tx().access_list.clone(), }, - TypedTransaction::Deposit(t) => TransactionEssentials { + Self::Deposit(t) => TransactionEssentials { kind: t.kind, input: t.input.clone(), nonce: t.nonce, @@ -772,49 +772,49 @@ impl TypedTransaction { pub fn nonce(&self) -> u64 { match self { - TypedTransaction::Legacy(t) => t.tx().nonce, - TypedTransaction::EIP2930(t) => t.tx().nonce, - TypedTransaction::EIP1559(t) => t.tx().nonce, - TypedTransaction::EIP4844(t) => t.tx().tx().nonce, - TypedTransaction::Deposit(t) => t.nonce, + Self::Legacy(t) => t.tx().nonce, + Self::EIP2930(t) => t.tx().nonce, + Self::EIP1559(t) => t.tx().nonce, + Self::EIP4844(t) => t.tx().tx().nonce, + Self::Deposit(t) => t.nonce, } } pub fn chain_id(&self) -> Option { match self { - TypedTransaction::Legacy(t) => t.tx().chain_id, - TypedTransaction::EIP2930(t) => Some(t.tx().chain_id), - TypedTransaction::EIP1559(t) => Some(t.tx().chain_id), - TypedTransaction::EIP4844(t) => Some(t.tx().tx().chain_id), - TypedTransaction::Deposit(t) => t.chain_id(), + Self::Legacy(t) => t.tx().chain_id, + Self::EIP2930(t) => Some(t.tx().chain_id), + Self::EIP1559(t) => Some(t.tx().chain_id), + Self::EIP4844(t) => Some(t.tx().tx().chain_id), + Self::Deposit(t) => t.chain_id(), } } pub fn as_legacy(&self) -> Option<&Signed> { match self { - TypedTransaction::Legacy(tx) => Some(tx), + Self::Legacy(tx) => Some(tx), _ => None, } } /// Returns true whether this tx is a legacy transaction pub fn is_legacy(&self) -> bool { - matches!(self, TypedTransaction::Legacy(_)) + matches!(self, Self::Legacy(_)) } /// Returns true whether this tx is a EIP1559 transaction pub fn is_eip1559(&self) -> bool { - matches!(self, TypedTransaction::EIP1559(_)) + matches!(self, Self::EIP1559(_)) } /// Returns true whether this tx is a EIP2930 transaction pub fn is_eip2930(&self) -> bool { - matches!(self, TypedTransaction::EIP2930(_)) + matches!(self, Self::EIP2930(_)) } /// Returns true whether this tx is a EIP4844 transaction pub fn is_eip4844(&self) -> bool { - matches!(self, TypedTransaction::EIP4844(_)) + matches!(self, Self::EIP4844(_)) } /// Returns the hash of the transaction. @@ -823,11 +823,11 @@ impl TypedTransaction { /// hash. This allows us to treat impersonated transactions as unique. pub fn hash(&self) -> B256 { match self { - TypedTransaction::Legacy(t) => *t.hash(), - TypedTransaction::EIP2930(t) => *t.hash(), - TypedTransaction::EIP1559(t) => *t.hash(), - TypedTransaction::EIP4844(t) => *t.hash(), - TypedTransaction::Deposit(t) => t.hash(), + Self::Legacy(t) => *t.hash(), + Self::EIP2930(t) => *t.hash(), + Self::EIP1559(t) => *t.hash(), + Self::EIP4844(t) => *t.hash(), + Self::Deposit(t) => t.hash(), } } @@ -851,22 +851,22 @@ impl TypedTransaction { /// Recovers the Ethereum address which was used to sign the transaction. pub fn recover(&self) -> Result { match self { - TypedTransaction::Legacy(tx) => tx.recover_signer(), - TypedTransaction::EIP2930(tx) => tx.recover_signer(), - TypedTransaction::EIP1559(tx) => tx.recover_signer(), - TypedTransaction::EIP4844(tx) => tx.recover_signer(), - TypedTransaction::Deposit(tx) => tx.recover(), + Self::Legacy(tx) => tx.recover_signer(), + Self::EIP2930(tx) => tx.recover_signer(), + Self::EIP1559(tx) => tx.recover_signer(), + Self::EIP4844(tx) => tx.recover_signer(), + Self::Deposit(tx) => tx.recover(), } } /// Returns what kind of transaction this is pub fn kind(&self) -> TxKind { match self { - TypedTransaction::Legacy(tx) => tx.tx().to, - TypedTransaction::EIP2930(tx) => tx.tx().to, - TypedTransaction::EIP1559(tx) => tx.tx().to, - TypedTransaction::EIP4844(tx) => TxKind::Call(tx.tx().tx().to), - TypedTransaction::Deposit(tx) => tx.kind, + Self::Legacy(tx) => tx.tx().to, + Self::EIP2930(tx) => tx.tx().to, + Self::EIP1559(tx) => tx.tx().to, + Self::EIP4844(tx) => TxKind::Call(tx.tx().tx().to), + Self::Deposit(tx) => tx.kind, } } @@ -878,11 +878,11 @@ impl TypedTransaction { /// Returns the Signature of the transaction pub fn signature(&self) -> Signature { match self { - TypedTransaction::Legacy(tx) => *tx.signature(), - TypedTransaction::EIP2930(tx) => *tx.signature(), - TypedTransaction::EIP1559(tx) => *tx.signature(), - TypedTransaction::EIP4844(tx) => *tx.signature(), - TypedTransaction::Deposit(_) => Signature::from_scalars_and_parity( + Self::Legacy(tx) => *tx.signature(), + Self::EIP2930(tx) => *tx.signature(), + Self::EIP1559(tx) => *tx.signature(), + Self::EIP4844(tx) => *tx.signature(), + Self::Deposit(_) => Signature::from_scalars_and_parity( B256::with_last_byte(1), B256::with_last_byte(1), false, @@ -895,11 +895,11 @@ impl TypedTransaction { impl Encodable for TypedTransaction { fn encode(&self, out: &mut dyn bytes::BufMut) { match self { - TypedTransaction::Legacy(tx) => TxEnvelope::from(tx.clone()).encode(out), - TypedTransaction::EIP2930(tx) => TxEnvelope::from(tx.clone()).encode(out), - TypedTransaction::EIP1559(tx) => TxEnvelope::from(tx.clone()).encode(out), - TypedTransaction::EIP4844(tx) => TxEnvelope::from(tx.clone()).encode(out), - TypedTransaction::Deposit(tx) => { + Self::Legacy(tx) => TxEnvelope::from(tx.clone()).encode(out), + Self::EIP2930(tx) => TxEnvelope::from(tx.clone()).encode(out), + Self::EIP1559(tx) => TxEnvelope::from(tx.clone()).encode(out), + Self::EIP4844(tx) => TxEnvelope::from(tx.clone()).encode(out), + Self::Deposit(tx) => { let tx_payload_len = tx.fields_len(); let tx_header_len = Header { list: false, payload_length: tx_payload_len }.length(); Header { list: false, payload_length: 1 + tx_payload_len + tx_header_len } @@ -939,21 +939,21 @@ impl Encodable2718 for TypedTransaction { fn encode_2718_len(&self) -> usize { match self { - TypedTransaction::Legacy(tx) => TxEnvelope::from(tx.clone()).encode_2718_len(), - TypedTransaction::EIP2930(tx) => TxEnvelope::from(tx.clone()).encode_2718_len(), - TypedTransaction::EIP1559(tx) => TxEnvelope::from(tx.clone()).encode_2718_len(), - TypedTransaction::EIP4844(tx) => TxEnvelope::from(tx.clone()).encode_2718_len(), - TypedTransaction::Deposit(tx) => 1 + tx.length(), + Self::Legacy(tx) => TxEnvelope::from(tx.clone()).encode_2718_len(), + 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::Deposit(tx) => 1 + tx.length(), } } fn encode_2718(&self, out: &mut dyn BufMut) { match self { - TypedTransaction::Legacy(tx) => TxEnvelope::from(tx.clone()).encode_2718(out), - TypedTransaction::EIP2930(tx) => TxEnvelope::from(tx.clone()).encode_2718(out), - TypedTransaction::EIP1559(tx) => TxEnvelope::from(tx.clone()).encode_2718(out), - TypedTransaction::EIP4844(tx) => TxEnvelope::from(tx.clone()).encode_2718(out), - TypedTransaction::Deposit(tx) => { + Self::Legacy(tx) => TxEnvelope::from(tx.clone()).encode_2718(out), + 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::Deposit(tx) => { out.put_u8(0x7E); tx.encode(out); } @@ -985,10 +985,10 @@ impl Decodable2718 for TypedTransaction { impl From for TypedTransaction { fn from(value: TxEnvelope) -> Self { match value { - TxEnvelope::Legacy(tx) => TypedTransaction::Legacy(tx), - TxEnvelope::Eip2930(tx) => TypedTransaction::EIP2930(tx), - TxEnvelope::Eip1559(tx) => TypedTransaction::EIP1559(tx), - TxEnvelope::Eip4844(tx) => TypedTransaction::EIP4844(tx), + TxEnvelope::Legacy(tx) => Self::Legacy(tx), + TxEnvelope::Eip2930(tx) => Self::EIP2930(tx), + TxEnvelope::Eip1559(tx) => Self::EIP1559(tx), + TxEnvelope::Eip4844(tx) => Self::EIP4844(tx), _ => unreachable!(), } } @@ -1139,11 +1139,8 @@ pub enum TypedReceipt { impl TypedReceipt { pub fn as_receipt_with_bloom(&self) -> &ReceiptWithBloom { match self { - TypedReceipt::Legacy(r) | - TypedReceipt::EIP1559(r) | - TypedReceipt::EIP2930(r) | - TypedReceipt::EIP4844(r) => r, - TypedReceipt::Deposit(r) => &r.inner, + Self::Legacy(r) | Self::EIP1559(r) | Self::EIP2930(r) | Self::EIP4844(r) => r, + Self::Deposit(r) => &r.inner, } } } @@ -1165,10 +1162,10 @@ impl TypedReceipt { impl From> for TypedReceipt { fn from(value: ReceiptEnvelope) -> Self { match value { - ReceiptEnvelope::Legacy(r) => TypedReceipt::Legacy(r), - ReceiptEnvelope::Eip2930(r) => TypedReceipt::EIP2930(r), - ReceiptEnvelope::Eip1559(r) => TypedReceipt::EIP1559(r), - ReceiptEnvelope::Eip4844(r) => TypedReceipt::EIP4844(r), + ReceiptEnvelope::Legacy(r) => Self::Legacy(r), + ReceiptEnvelope::Eip2930(r) => Self::EIP2930(r), + ReceiptEnvelope::Eip1559(r) => Self::EIP1559(r), + ReceiptEnvelope::Eip4844(r) => Self::EIP4844(r), _ => unreachable!(), } } @@ -1177,33 +1174,33 @@ impl From> for TypedReceipt r.encode(out), + Self::Legacy(r) => r.encode(out), receipt => { let payload_len = match receipt { - TypedReceipt::EIP2930(r) => r.length() + 1, - TypedReceipt::EIP1559(r) => r.length() + 1, - TypedReceipt::EIP4844(r) => r.length() + 1, - TypedReceipt::Deposit(r) => r.length() + 1, + Self::EIP2930(r) => r.length() + 1, + Self::EIP1559(r) => r.length() + 1, + Self::EIP4844(r) => r.length() + 1, + Self::Deposit(r) => r.length() + 1, _ => unreachable!("receipt already matched"), }; match receipt { - TypedReceipt::EIP2930(r) => { + Self::EIP2930(r) => { Header { list: true, payload_length: payload_len }.encode(out); 1u8.encode(out); r.encode(out); } - TypedReceipt::EIP1559(r) => { + Self::EIP1559(r) => { Header { list: true, payload_length: payload_len }.encode(out); 2u8.encode(out); r.encode(out); } - TypedReceipt::EIP4844(r) => { + Self::EIP4844(r) => { Header { list: true, payload_length: payload_len }.encode(out); 3u8.encode(out); r.encode(out); } - TypedReceipt::Deposit(r) => { + Self::Deposit(r) => { Header { list: true, payload_length: payload_len }.encode(out); 0x7Eu8.encode(out); r.encode(out); diff --git a/crates/anvil/core/src/lib.rs b/crates/anvil/core/src/lib.rs index a09e7cd68..5d51d4691 100644 --- a/crates/anvil/core/src/lib.rs +++ b/crates/anvil/core/src/lib.rs @@ -1,3 +1,10 @@ +//! # anvil-core +//! +//! Core Ethereum types for Anvil. + +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + /// Various Ethereum types pub mod eth; diff --git a/crates/anvil/core/src/types.rs b/crates/anvil/core/src/types.rs index 87209606f..79a6c7a2c 100644 --- a/crates/anvil/core/src/types.rs +++ b/crates/anvil/core/src/types.rs @@ -40,12 +40,11 @@ impl<'de> serde::Deserialize<'de> for Forking { } let f = match ForkingVariants::deserialize(deserializer)? { ForkingVariants::Fork(ForkOpts { json_rpc_url, block_number }) => { - Forking { json_rpc_url, block_number } + Self { json_rpc_url, block_number } + } + ForkingVariants::Tagged(f) => { + Self { json_rpc_url: f.forking.json_rpc_url, block_number: f.forking.block_number } } - ForkingVariants::Tagged(f) => Forking { - json_rpc_url: f.forking.json_rpc_url, - block_number: f.forking.block_number, - }, }; Ok(f) } @@ -70,7 +69,7 @@ pub enum EvmMineOptions { impl Default for EvmMineOptions { fn default() -> Self { - EvmMineOptions::Options { timestamp: None, blocks: None } + Self::Options { timestamp: None, blocks: None } } } @@ -110,7 +109,7 @@ impl From for usize { #[cfg(feature = "serde")] impl<'a> serde::Deserialize<'a> for Index { - fn deserialize(deserializer: D) -> Result + fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'a>, { diff --git a/crates/anvil/rpc/Cargo.toml b/crates/anvil/rpc/Cargo.toml index 6d4e76540..46a148ea4 100644 --- a/crates/anvil/rpc/Cargo.toml +++ b/crates/anvil/rpc/Cargo.toml @@ -9,9 +9,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [dependencies] serde.workspace = true serde_json.workspace = true - -[dev-dependencies] -rand = "0.8" diff --git a/crates/anvil/rpc/src/error.rs b/crates/anvil/rpc/src/error.rs index b1929818f..0836b8a27 100644 --- a/crates/anvil/rpc/src/error.rs +++ b/crates/anvil/rpc/src/error.rs @@ -16,7 +16,7 @@ pub struct RpcError { impl RpcError { /// New [Error] with the given [ErrorCode] pub const fn new(code: ErrorCode) -> Self { - RpcError { message: Cow::Borrowed(code.message()), code, data: None } + Self { message: Cow::Borrowed(code.message()), code, data: None } } /// Creates a new `ParseError` @@ -44,7 +44,7 @@ impl RpcError { where M: Into, { - RpcError { code: ErrorCode::InvalidParams, message: message.into().into(), data: None } + Self { code: ErrorCode::InvalidParams, message: message.into().into(), data: None } } /// Creates a new `InternalError` with a message @@ -52,7 +52,7 @@ impl RpcError { where M: Into, { - RpcError { code: ErrorCode::InternalError, message: message.into().into(), data: None } + Self { code: ErrorCode::InternalError, message: message.into().into(), data: None } } /// Creates a new rpc error for when a transaction was rejected @@ -60,11 +60,7 @@ impl RpcError { where M: Into, { - RpcError { - code: ErrorCode::TransactionRejected, - message: message.into().into(), - data: None, - } + Self { code: ErrorCode::TransactionRejected, message: message.into().into(), data: None } } } @@ -100,28 +96,28 @@ impl ErrorCode { /// Returns the error code as `i64` pub fn code(&self) -> i64 { match *self { - ErrorCode::ParseError => -32700, - ErrorCode::InvalidRequest => -32600, - ErrorCode::MethodNotFound => -32601, - ErrorCode::InvalidParams => -32602, - ErrorCode::InternalError => -32603, - ErrorCode::TransactionRejected => -32003, - ErrorCode::ExecutionError => 3, - ErrorCode::ServerError(c) => c, + Self::ParseError => -32700, + Self::InvalidRequest => -32600, + Self::MethodNotFound => -32601, + Self::InvalidParams => -32602, + Self::InternalError => -32603, + Self::TransactionRejected => -32003, + Self::ExecutionError => 3, + Self::ServerError(c) => c, } } /// Returns the message associated with the error pub const fn message(&self) -> &'static str { match *self { - ErrorCode::ParseError => "Parse error", - ErrorCode::InvalidRequest => "Invalid request", - ErrorCode::MethodNotFound => "Method not found", - ErrorCode::InvalidParams => "Invalid params", - ErrorCode::InternalError => "Internal error", - ErrorCode::TransactionRejected => "Transaction rejected", - ErrorCode::ServerError(_) => "Server error", - ErrorCode::ExecutionError => "Execution error", + Self::ParseError => "Parse error", + Self::InvalidRequest => "Invalid request", + Self::MethodNotFound => "Method not found", + Self::InvalidParams => "Invalid params", + Self::InternalError => "Internal error", + Self::TransactionRejected => "Transaction rejected", + Self::ServerError(_) => "Server error", + Self::ExecutionError => "Execution error", } } } @@ -136,7 +132,7 @@ impl Serialize for ErrorCode { } impl<'a> Deserialize<'a> for ErrorCode { - fn deserialize(deserializer: D) -> Result + fn deserialize(deserializer: D) -> Result where D: Deserializer<'a>, { @@ -147,14 +143,14 @@ impl<'a> Deserialize<'a> for ErrorCode { impl From for ErrorCode { fn from(code: i64) -> Self { match code { - -32700 => ErrorCode::ParseError, - -32600 => ErrorCode::InvalidRequest, - -32601 => ErrorCode::MethodNotFound, - -32602 => ErrorCode::InvalidParams, - -32603 => ErrorCode::InternalError, - -32003 => ErrorCode::TransactionRejected, - 3 => ErrorCode::ExecutionError, - _ => ErrorCode::ServerError(code), + -32700 => Self::ParseError, + -32600 => Self::InvalidRequest, + -32601 => Self::MethodNotFound, + -32602 => Self::InvalidParams, + -32603 => Self::InternalError, + -32003 => Self::TransactionRejected, + 3 => Self::ExecutionError, + _ => Self::ServerError(code), } } } diff --git a/crates/anvil/rpc/src/lib.rs b/crates/anvil/rpc/src/lib.rs index 939fbff16..bd5382cee 100644 --- a/crates/anvil/rpc/src/lib.rs +++ b/crates/anvil/rpc/src/lib.rs @@ -1,3 +1,10 @@ +//! # anvil-rpc +//! +//! JSON-RPC types. + +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + /// JSON-RPC request bindings pub mod request; diff --git a/crates/anvil/rpc/src/request.rs b/crates/anvil/rpc/src/request.rs index 8630f4a4c..5cb8510b8 100644 --- a/crates/anvil/rpc/src/request.rs +++ b/crates/anvil/rpc/src/request.rs @@ -77,7 +77,7 @@ pub enum RequestParams { impl From for serde_json::Value { fn from(params: RequestParams) -> Self { match params { - RequestParams::None => serde_json::Value::Null, + RequestParams::None => Self::Null, RequestParams::Array(arr) => arr.into(), RequestParams::Object(obj) => obj.into(), } @@ -106,9 +106,9 @@ pub enum Id { impl fmt::Display for Id { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Id::String(s) => s.fmt(f), - Id::Number(n) => n.fmt(f), - Id::Null => f.write_str("null"), + Self::String(s) => s.fmt(f), + Self::Number(n) => n.fmt(f), + Self::Null => f.write_str("null"), } } } diff --git a/crates/anvil/rpc/src/response.rs b/crates/anvil/rpc/src/response.rs index c7649dbe3..49ad70d0d 100644 --- a/crates/anvil/rpc/src/response.rs +++ b/crates/anvil/rpc/src/response.rs @@ -24,7 +24,7 @@ impl From for RpcResponse { impl RpcResponse { pub fn new(id: Id, content: impl Into) -> Self { - RpcResponse { jsonrpc: Version::V2, id: Some(id), result: content.into() } + Self { jsonrpc: Version::V2, id: Some(id), result: content.into() } } pub fn invalid_request(id: Id) -> Self { @@ -47,17 +47,17 @@ impl ResponseResult { where S: Serialize + 'static, { - ResponseResult::Success(serde_json::to_value(&content).unwrap()) + Self::Success(serde_json::to_value(&content).unwrap()) } pub fn error(error: RpcError) -> Self { - ResponseResult::Error(error) + Self::Error(error) } } impl From for ResponseResult { fn from(err: RpcError) -> Self { - ResponseResult::error(err) + Self::error(err) } } /// Synchronous response @@ -80,12 +80,12 @@ impl Response { impl From for Response { fn from(err: RpcError) -> Self { - Response::error(err) + Self::error(err) } } impl From for Response { fn from(resp: RpcResponse) -> Self { - Response::Single(resp) + Self::Single(resp) } } diff --git a/crates/anvil/server/Cargo.toml b/crates/anvil/server/Cargo.toml index 250bf2787..051da35c7 100644 --- a/crates/anvil/server/Cargo.toml +++ b/crates/anvil/server/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [dependencies] anvil-rpc = { path = "../rpc" } diff --git a/crates/anvil/server/src/config.rs b/crates/anvil/server/src/config.rs index 29b0e8812..ea97d24cd 100644 --- a/crates/anvil/server/src/config.rs +++ b/crates/anvil/server/src/config.rs @@ -52,7 +52,7 @@ impl FromStr for HeaderValueWrapper { type Err = ::Err; fn from_str(s: &str) -> Result { - Ok(HeaderValueWrapper(s.parse()?)) + Ok(Self(s.parse()?)) } } @@ -91,6 +91,6 @@ impl From for HeaderValue { impl From for HeaderValueWrapper { fn from(header: HeaderValue) -> Self { - HeaderValueWrapper(header) + Self(header) } } diff --git a/crates/anvil/server/src/ipc.rs b/crates/anvil/server/src/ipc.rs index a1ca67af8..ddcf5a21f 100644 --- a/crates/anvil/server/src/ipc.rs +++ b/crates/anvil/server/src/ipc.rs @@ -34,7 +34,7 @@ impl IpcEndpoint { /// connections, See [`PubSubConnection`] that should be spawned #[instrument(target = "ipc", skip_all)] pub fn incoming(self) -> io::Result>> { - let IpcEndpoint { handler, path } = self; + let Self { handler, path } = self; trace!(%path, "starting IPC server"); diff --git a/crates/anvil/server/src/lib.rs b/crates/anvil/server/src/lib.rs index 5bd81b043..b60182505 100644 --- a/crates/anvil/server/src/lib.rs +++ b/crates/anvil/server/src/lib.rs @@ -1,6 +1,7 @@ //! Bootstrap [axum] RPC servers. -#![warn(missing_docs, unused_crate_dependencies)] +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #[macro_use] extern crate tracing; diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index f36b20013..5febf2905 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -596,7 +596,7 @@ impl Future for PeriodicStateDumper { if this.interval.poll_tick(cx).is_ready() { let api = this.api.clone(); let path = this.dump_state.clone().expect("exists; see above"); - this.in_progress_dump = Some(Box::pin(PeriodicStateDumper::dump_state(api, path))); + this.in_progress_dump = Some(Box::pin(Self::dump_state(api, path))); } else { break } @@ -663,17 +663,17 @@ impl FromStr for ForkUrl { fn from_str(s: &str) -> Result { if let Some((url, block)) = s.rsplit_once('@') { if block == "latest" { - return Ok(ForkUrl { url: url.to_string(), block: None }) + return Ok(Self { url: url.to_string(), block: None }) } // this will prevent false positives for auths `user:password@example.com` if !block.is_empty() && !block.contains(':') && !block.contains('.') { let block: u64 = block .parse() .map_err(|_| format!("Failed to parse block number: `{block}`"))?; - return Ok(ForkUrl { url: url.to_string(), block: Some(block) }) + return Ok(Self { url: url.to_string(), block: Some(block) }) } } - Ok(ForkUrl { url: s.to_string(), block: None }) + Ok(Self { url: s.to_string(), block: None }) } } diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 7ee7d5e34..24a354267 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -1043,7 +1043,7 @@ latest block number: {latest_block}" // the block, and the block number is less than equal the latest block, then // the user is forking from a non-archive node with an older block number. if fork_block_number <= latest_block { - message.push_str(&format!("\n{}", NON_ARCHIVE_NODE_WARNING)); + message.push_str(&format!("\n{NON_ARCHIVE_NODE_WARNING}")); } panic!("{}", message); } diff --git a/crates/anvil/src/eth/backend/mem/cache.rs b/crates/anvil/src/eth/backend/mem/cache.rs index d4aff5948..e51aaae7e 100644 --- a/crates/anvil/src/eth/backend/mem/cache.rs +++ b/crates/anvil/src/eth/backend/mem/cache.rs @@ -102,7 +102,7 @@ impl DiskStateCache { impl Default for DiskStateCache { fn default() -> Self { - DiskStateCache { temp_path: anvil_tmp_dir(), temp_dir: None } + Self { temp_path: anvil_tmp_dir(), temp_dir: None } } } diff --git a/crates/anvil/src/eth/backend/mem/in_memory_db.rs b/crates/anvil/src/eth/backend/mem/in_memory_db.rs index 4a1a429f0..2049c9f90 100644 --- a/crates/anvil/src/eth/backend/mem/in_memory_db.rs +++ b/crates/anvil/src/eth/backend/mem/in_memory_db.rs @@ -94,7 +94,7 @@ impl Db for MemDb { } fn current_state(&self) -> StateDb { - StateDb::new(MemDb { inner: self.inner.clone(), ..Default::default() }) + StateDb::new(Self { inner: self.inner.clone(), ..Default::default() }) } } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index c705ec376..335d1620e 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -115,8 +115,8 @@ pub enum BlockRequest { impl BlockRequest { pub fn block_number(&self) -> BlockNumber { match *self { - BlockRequest::Pending(_) => BlockNumber::Pending, - BlockRequest::Number(n) => BlockNumber::Number(n), + Self::Pending(_) => BlockNumber::Pending, + Self::Number(n) => BlockNumber::Number(n), } } } diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index 1f97addb4..46365739e 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -250,7 +250,7 @@ impl BlockchainStorage { } pub fn forked(block_number: u64, block_hash: B256, total_difficulty: U256) -> Self { - BlockchainStorage { + Self { blocks: Default::default(), hashes: HashMap::from([(U64::from(block_number), block_hash)]), best_hash: block_hash, diff --git a/crates/anvil/src/eth/backend/time.rs b/crates/anvil/src/eth/backend/time.rs index 5cad24f2b..0822baa04 100644 --- a/crates/anvil/src/eth/backend/time.rs +++ b/crates/anvil/src/eth/backend/time.rs @@ -26,8 +26,8 @@ pub struct TimeManager { } impl TimeManager { - pub fn new(start_timestamp: u64) -> TimeManager { - let time_manager = TimeManager { + pub fn new(start_timestamp: u64) -> Self { + let time_manager = Self { last_timestamp: Default::default(), offset: Default::default(), next_exact_timestamp: Default::default(), diff --git a/crates/anvil/src/eth/error.rs b/crates/anvil/src/eth/error.rs index 417ec77a0..f7818585d 100644 --- a/crates/anvil/src/eth/error.rs +++ b/crates/anvil/src/eth/error.rs @@ -95,23 +95,23 @@ pub enum BlockchainError { impl From for BlockchainError { fn from(err: RpcError) -> Self { - BlockchainError::RpcError(err) + Self::RpcError(err) } } impl From> for BlockchainError where - T: Into, + T: Into, { fn from(err: EVMError) -> Self { match err { EVMError::Transaction(err) => InvalidTransactionError::from(err).into(), EVMError::Header(err) => match err { - InvalidHeader::ExcessBlobGasNotSet => BlockchainError::ExcessBlobGasNotSet, - InvalidHeader::PrevrandaoNotSet => BlockchainError::PrevrandaoNotSet, + InvalidHeader::ExcessBlobGasNotSet => Self::ExcessBlobGasNotSet, + InvalidHeader::PrevrandaoNotSet => Self::PrevrandaoNotSet, }, EVMError::Database(err) => err.into(), - EVMError::Custom(err) => BlockchainError::Message(err), + EVMError::Custom(err) => Self::Message(err), } } } @@ -234,58 +234,32 @@ impl From for InvalidTransactionError { fn from(err: revm::primitives::InvalidTransaction) -> Self { use revm::primitives::InvalidTransaction; match err { - InvalidTransaction::InvalidChainId => InvalidTransactionError::InvalidChainId, - InvalidTransaction::PriorityFeeGreaterThanMaxFee => { - InvalidTransactionError::TipAboveFeeCap - } - InvalidTransaction::GasPriceLessThanBasefee => InvalidTransactionError::FeeCapTooLow, + InvalidTransaction::InvalidChainId => Self::InvalidChainId, + InvalidTransaction::PriorityFeeGreaterThanMaxFee => Self::TipAboveFeeCap, + InvalidTransaction::GasPriceLessThanBasefee => Self::FeeCapTooLow, InvalidTransaction::CallerGasLimitMoreThanBlock => { - InvalidTransactionError::GasTooHigh(ErrDetail { - detail: String::from("CallerGasLimitMoreThanBlock"), - }) + Self::GasTooHigh(ErrDetail { detail: String::from("CallerGasLimitMoreThanBlock") }) } InvalidTransaction::CallGasCostMoreThanGasLimit => { - InvalidTransactionError::GasTooHigh(ErrDetail { - detail: String::from("CallGasCostMoreThanGasLimit"), - }) - } - InvalidTransaction::RejectCallerWithCode => InvalidTransactionError::SenderNoEOA, - InvalidTransaction::LackOfFundForMaxFee { .. } => { - InvalidTransactionError::InsufficientFunds - } - InvalidTransaction::OverflowPaymentInTransaction => { - InvalidTransactionError::GasUintOverflow - } - InvalidTransaction::NonceOverflowInTransaction => { - InvalidTransactionError::NonceMaxValue - } - InvalidTransaction::CreateInitCodeSizeLimit => { - InvalidTransactionError::MaxInitCodeSizeExceeded - } - InvalidTransaction::NonceTooHigh { .. } => InvalidTransactionError::NonceTooHigh, - InvalidTransaction::NonceTooLow { .. } => InvalidTransactionError::NonceTooLow, - InvalidTransaction::AccessListNotSupported => { - InvalidTransactionError::AccessListNotSupported - } - InvalidTransaction::BlobGasPriceGreaterThanMax => { - InvalidTransactionError::BlobFeeCapTooLow + Self::GasTooHigh(ErrDetail { detail: String::from("CallGasCostMoreThanGasLimit") }) } + InvalidTransaction::RejectCallerWithCode => Self::SenderNoEOA, + InvalidTransaction::LackOfFundForMaxFee { .. } => Self::InsufficientFunds, + InvalidTransaction::OverflowPaymentInTransaction => Self::GasUintOverflow, + InvalidTransaction::NonceOverflowInTransaction => Self::NonceMaxValue, + InvalidTransaction::CreateInitCodeSizeLimit => Self::MaxInitCodeSizeExceeded, + InvalidTransaction::NonceTooHigh { .. } => Self::NonceTooHigh, + InvalidTransaction::NonceTooLow { .. } => Self::NonceTooLow, + InvalidTransaction::AccessListNotSupported => Self::AccessListNotSupported, + InvalidTransaction::BlobGasPriceGreaterThanMax => Self::BlobFeeCapTooLow, InvalidTransaction::BlobVersionedHashesNotSupported => { - InvalidTransactionError::BlobVersionedHashesNotSupported - } - InvalidTransaction::MaxFeePerBlobGasNotSupported => { - InvalidTransactionError::MaxFeePerBlobGasNotSupported - } - InvalidTransaction::BlobCreateTransaction => { - InvalidTransactionError::BlobCreateTransaction - } - InvalidTransaction::BlobVersionNotSupported => { - InvalidTransactionError::BlobVersionNotSupported - } - InvalidTransaction::EmptyBlobs => InvalidTransactionError::EmptyBlobs, - InvalidTransaction::TooManyBlobs { max, have } => { - InvalidTransactionError::TooManyBlobs(max, have) + Self::BlobVersionedHashesNotSupported } + InvalidTransaction::MaxFeePerBlobGasNotSupported => Self::MaxFeePerBlobGasNotSupported, + InvalidTransaction::BlobCreateTransaction => Self::BlobCreateTransaction, + InvalidTransaction::BlobVersionNotSupported => Self::BlobVersionNotSupported, + InvalidTransaction::EmptyBlobs => Self::EmptyBlobs, + InvalidTransaction::TooManyBlobs { max, have } => Self::TooManyBlobs(max, have), _ => todo!(), } } diff --git a/crates/anvil/src/eth/fees.rs b/crates/anvil/src/eth/fees.rs index 8c0104a1b..922f149e3 100644 --- a/crates/anvil/src/eth/fees.rs +++ b/crates/anvil/src/eth/fees.rs @@ -387,12 +387,8 @@ impl FeeDetails { /// If neither `gas_price` nor `max_fee_per_gas` is `Some`, this will set both to `0` pub fn or_zero_fees(self) -> Self { - let FeeDetails { - gas_price, - max_fee_per_gas, - max_priority_fee_per_gas, - max_fee_per_blob_gas, - } = self; + let Self { gas_price, max_fee_per_gas, max_priority_fee_per_gas, max_fee_per_blob_gas } = + self; let no_fees = gas_price.is_none() && max_fee_per_gas.is_none(); let gas_price = if no_fees { Some(0) } else { gas_price }; @@ -415,11 +411,11 @@ impl FeeDetails { request_max_fee: Option, request_priority: Option, max_fee_per_blob_gas: Option, - ) -> Result { + ) -> Result { match (request_gas_price, request_max_fee, request_priority, max_fee_per_blob_gas) { (gas_price, None, None, None) => { // Legacy request, all default to gas price. - Ok(FeeDetails { + Ok(Self { gas_price, max_fee_per_gas: gas_price, max_priority_fee_per_gas: gas_price, @@ -435,7 +431,7 @@ impl FeeDetails { return Err(BlockchainError::InvalidFeeInput) } } - Ok(FeeDetails { + Ok(Self { gas_price: max_fee, max_fee_per_gas: max_fee, max_priority_fee_per_gas: max_priority, @@ -451,7 +447,7 @@ impl FeeDetails { return Err(BlockchainError::InvalidFeeInput) } } - Ok(FeeDetails { + Ok(Self { gas_price: max_fee, max_fee_per_gas: max_fee, max_priority_fee_per_gas: max_priority, diff --git a/crates/anvil/src/eth/miner.rs b/crates/anvil/src/eth/miner.rs index 442c6fe7b..b559351fe 100644 --- a/crates/anvil/src/eth/miner.rs +++ b/crates/anvil/src/eth/miner.rs @@ -110,7 +110,7 @@ pub enum MiningMode { impl MiningMode { pub fn instant(max_transactions: usize, listener: Receiver) -> Self { - MiningMode::Auto(ReadyTransactionMiner { + Self::Auto(ReadyTransactionMiner { max_transactions, has_pending_txs: None, rx: listener.fuse(), @@ -118,7 +118,7 @@ impl MiningMode { } pub fn interval(duration: Duration) -> Self { - MiningMode::FixedBlockTime(FixedBlockTimeMiner::new(duration)) + Self::FixedBlockTime(FixedBlockTimeMiner::new(duration)) } /// polls the [Pool] and returns those transactions that should be put in a block, if any. @@ -128,9 +128,9 @@ impl MiningMode { cx: &mut Context<'_>, ) -> Poll>> { match self { - MiningMode::None => Poll::Pending, - MiningMode::Auto(miner) => miner.poll(pool, cx), - MiningMode::FixedBlockTime(miner) => miner.poll(pool, cx), + Self::None => Poll::Pending, + Self::Auto(miner) => miner.poll(pool, cx), + Self::FixedBlockTime(miner) => miner.poll(pool, cx), } } } diff --git a/crates/anvil/src/eth/otterscan/types.rs b/crates/anvil/src/eth/otterscan/types.rs index fc0104066..7f8576257 100644 --- a/crates/anvil/src/eth/otterscan/types.rs +++ b/crates/anvil/src/eth/otterscan/types.rs @@ -286,7 +286,7 @@ impl OtsSearchTransactions { impl OtsInternalOperation { /// Converts a batch of traces into a batch of internal operations, to comply with the spec for /// [`ots_getInternalOperations`](https://github.com/otterscan/otterscan/blob/develop/docs/custom-jsonrpc.md#ots_getinternaloperations) - pub fn batch_build(traces: MinedTransaction) -> Vec { + pub fn batch_build(traces: MinedTransaction) -> Vec { traces .info .traces @@ -322,7 +322,7 @@ impl OtsTrace { .filter_map(|trace| match trace.trace.action { Action::Call(call) => { if let Ok(ots_type) = call.call_type.try_into() { - Some(OtsTrace { + Some(Self { r#type: ots_type, depth: trace.trace.trace_address.len(), from: call.from, @@ -346,9 +346,9 @@ impl TryFrom for OtsTraceType { fn try_from(value: CallType) -> std::result::Result { match value { - CallType::Call => Ok(OtsTraceType::Call), - CallType::StaticCall => Ok(OtsTraceType::StaticCall), - CallType::DelegateCall => Ok(OtsTraceType::DelegateCall), + CallType::Call => Ok(Self::Call), + CallType::StaticCall => Ok(Self::StaticCall), + CallType::DelegateCall => Ok(Self::DelegateCall), _ => Err(()), } } diff --git a/crates/anvil/src/eth/pool/mod.rs b/crates/anvil/src/eth/pool/mod.rs index c94fafaca..9ef45ace2 100644 --- a/crates/anvil/src/eth/pool/mod.rs +++ b/crates/anvil/src/eth/pool/mod.rs @@ -464,8 +464,8 @@ pub enum AddedTransaction { impl AddedTransaction { pub fn hash(&self) -> &TxHash { match self { - AddedTransaction::Ready(tx) => &tx.hash, - AddedTransaction::Pending { hash } => hash, + Self::Ready(tx) => &tx.hash, + Self::Pending { hash } => hash, } } } diff --git a/crates/anvil/src/eth/pool/transactions.rs b/crates/anvil/src/eth/pool/transactions.rs index 23841b0d7..7a1e8e8ab 100644 --- a/crates/anvil/src/eth/pool/transactions.rs +++ b/crates/anvil/src/eth/pool/transactions.rs @@ -41,8 +41,8 @@ impl TransactionOrder { /// Returns the priority of the transactions pub fn priority(&self, tx: &TypedTransaction) -> TransactionPriority { match self { - TransactionOrder::Fifo => TransactionPriority::default(), - TransactionOrder::Fees => TransactionPriority(tx.gas_price()), + Self::Fifo => TransactionPriority::default(), + Self::Fees => TransactionPriority(tx.gas_price()), } } } @@ -53,8 +53,8 @@ impl FromStr for TransactionOrder { fn from_str(s: &str) -> Result { let s = s.to_lowercase(); let order = match s.as_str() { - "fees" => TransactionOrder::Fees, - "fifo" => TransactionOrder::Fifo, + "fees" => Self::Fees, + "fifo" => Self::Fifo, _ => return Err(format!("Unknown TransactionOrder: `{s}`")), }; Ok(order) diff --git a/crates/anvil/src/filter.rs b/crates/anvil/src/filter.rs index b1d2370f5..2144ce27c 100644 --- a/crates/anvil/src/filter.rs +++ b/crates/anvil/src/filter.rs @@ -127,15 +127,15 @@ impl Stream for EthFilter { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let pin = self.get_mut(); match pin { - EthFilter::Logs(logs) => Poll::Ready(Some(Ok(logs.poll(cx)).to_rpc_result())), - EthFilter::Blocks(blocks) => { + Self::Logs(logs) => Poll::Ready(Some(Ok(logs.poll(cx)).to_rpc_result())), + Self::Blocks(blocks) => { let mut new_blocks = Vec::new(); while let Poll::Ready(Some(block)) = blocks.poll_next_unpin(cx) { new_blocks.push(block.hash); } Poll::Ready(Some(Ok(new_blocks).to_rpc_result())) } - EthFilter::PendingTransactions(tx) => { + Self::PendingTransactions(tx) => { let mut new_txs = Vec::new(); while let Poll::Ready(Some(tx_hash)) = tx.poll_next_unpin(cx) { new_txs.push(tx_hash); diff --git a/crates/anvil/src/hardfork.rs b/crates/anvil/src/hardfork.rs index 7d95f13e7..3c6eb0aa8 100644 --- a/crates/anvil/src/hardfork.rs +++ b/crates/anvil/src/hardfork.rs @@ -29,22 +29,22 @@ impl Hardfork { /// Get the first block number of the hardfork. pub fn fork_block(&self) -> u64 { match *self { - Hardfork::Frontier => 0, - Hardfork::Homestead => 1150000, - Hardfork::Dao => 1920000, - Hardfork::Tangerine => 2463000, - Hardfork::SpuriousDragon => 2675000, - Hardfork::Byzantium => 4370000, - Hardfork::Constantinople | Hardfork::Petersburg => 7280000, - Hardfork::Istanbul => 9069000, - Hardfork::Muirglacier => 9200000, - Hardfork::Berlin => 12244000, - Hardfork::London => 12965000, - Hardfork::ArrowGlacier => 13773000, - Hardfork::GrayGlacier => 15050000, - Hardfork::Paris => 15537394, - Hardfork::Shanghai => 17034870, - Hardfork::Cancun | Hardfork::Latest => 19426587, + Self::Frontier => 0, + Self::Homestead => 1150000, + Self::Dao => 1920000, + Self::Tangerine => 2463000, + Self::SpuriousDragon => 2675000, + Self::Byzantium => 4370000, + Self::Constantinople | Self::Petersburg => 7280000, + Self::Istanbul => 9069000, + Self::Muirglacier => 9200000, + Self::Berlin => 12244000, + Self::London => 12965000, + Self::ArrowGlacier => 13773000, + Self::GrayGlacier => 15050000, + Self::Paris => 15537394, + Self::Shanghai => 17034870, + Self::Cancun | Self::Latest => 19426587, } } } @@ -55,24 +55,24 @@ impl FromStr for Hardfork { fn from_str(s: &str) -> Result { let s = s.to_lowercase(); let hardfork = match s.as_str() { - "frontier" | "1" => Hardfork::Frontier, - "homestead" | "2" => Hardfork::Homestead, - "dao" | "3" => Hardfork::Dao, - "tangerine" | "4" => Hardfork::Tangerine, - "spuriousdragon" | "5" => Hardfork::SpuriousDragon, - "byzantium" | "6" => Hardfork::Byzantium, - "constantinople" | "7" => Hardfork::Constantinople, - "petersburg" | "8" => Hardfork::Petersburg, - "istanbul" | "9" => Hardfork::Istanbul, - "muirglacier" | "10" => Hardfork::Muirglacier, - "berlin" | "11" => Hardfork::Berlin, - "london" | "12" => Hardfork::London, - "arrowglacier" | "13" => Hardfork::ArrowGlacier, - "grayglacier" | "14" => Hardfork::GrayGlacier, - "paris" | "merge" | "15" => Hardfork::Paris, - "shanghai" | "16" => Hardfork::Shanghai, - "cancun" | "17" => Hardfork::Cancun, - "latest" => Hardfork::Latest, + "frontier" | "1" => Self::Frontier, + "homestead" | "2" => Self::Homestead, + "dao" | "3" => Self::Dao, + "tangerine" | "4" => Self::Tangerine, + "spuriousdragon" | "5" => Self::SpuriousDragon, + "byzantium" | "6" => Self::Byzantium, + "constantinople" | "7" => Self::Constantinople, + "petersburg" | "8" => Self::Petersburg, + "istanbul" | "9" => Self::Istanbul, + "muirglacier" | "10" => Self::Muirglacier, + "berlin" | "11" => Self::Berlin, + "london" | "12" => Self::London, + "arrowglacier" | "13" => Self::ArrowGlacier, + "grayglacier" | "14" => Self::GrayGlacier, + "paris" | "merge" | "15" => Self::Paris, + "shanghai" | "16" => Self::Shanghai, + "cancun" | "17" => Self::Cancun, + "latest" => Self::Latest, _ => return Err(format!("Unknown hardfork {s}")), }; Ok(hardfork) @@ -82,23 +82,23 @@ impl FromStr for Hardfork { impl From for SpecId { fn from(fork: Hardfork) -> Self { match fork { - Hardfork::Frontier => SpecId::FRONTIER, - Hardfork::Homestead => SpecId::HOMESTEAD, - Hardfork::Dao => SpecId::HOMESTEAD, - Hardfork::Tangerine => SpecId::TANGERINE, - Hardfork::SpuriousDragon => SpecId::SPURIOUS_DRAGON, - Hardfork::Byzantium => SpecId::BYZANTIUM, - Hardfork::Constantinople => SpecId::CONSTANTINOPLE, - Hardfork::Petersburg => SpecId::PETERSBURG, - Hardfork::Istanbul => SpecId::ISTANBUL, - Hardfork::Muirglacier => SpecId::MUIR_GLACIER, - Hardfork::Berlin => SpecId::BERLIN, - Hardfork::London => SpecId::LONDON, - Hardfork::ArrowGlacier => SpecId::LONDON, - Hardfork::GrayGlacier => SpecId::GRAY_GLACIER, - Hardfork::Paris => SpecId::MERGE, - Hardfork::Shanghai => SpecId::SHANGHAI, - Hardfork::Cancun | Hardfork::Latest => SpecId::CANCUN, + Hardfork::Frontier => Self::FRONTIER, + Hardfork::Homestead => Self::HOMESTEAD, + Hardfork::Dao => Self::HOMESTEAD, + Hardfork::Tangerine => Self::TANGERINE, + Hardfork::SpuriousDragon => Self::SPURIOUS_DRAGON, + Hardfork::Byzantium => Self::BYZANTIUM, + Hardfork::Constantinople => Self::CONSTANTINOPLE, + Hardfork::Petersburg => Self::PETERSBURG, + Hardfork::Istanbul => Self::ISTANBUL, + Hardfork::Muirglacier => Self::MUIR_GLACIER, + Hardfork::Berlin => Self::BERLIN, + Hardfork::London => Self::LONDON, + Hardfork::ArrowGlacier => Self::LONDON, + Hardfork::GrayGlacier => Self::GRAY_GLACIER, + Hardfork::Paris => Self::MERGE, + Hardfork::Shanghai => Self::SHANGHAI, + Hardfork::Cancun | Hardfork::Latest => Self::CANCUN, } } } @@ -112,21 +112,21 @@ impl> From for Hardfork { }; match num { - _i if num < 1_150_000 => Hardfork::Frontier, - _i if num < 1_920_000 => Hardfork::Dao, - _i if num < 2_463_000 => Hardfork::Homestead, - _i if num < 2_675_000 => Hardfork::Tangerine, - _i if num < 4_370_000 => Hardfork::SpuriousDragon, - _i if num < 7_280_000 => Hardfork::Byzantium, - _i if num < 9_069_000 => Hardfork::Constantinople, - _i if num < 9_200_000 => Hardfork::Istanbul, - _i if num < 12_244_000 => Hardfork::Muirglacier, - _i if num < 12_965_000 => Hardfork::Berlin, - _i if num < 13_773_000 => Hardfork::London, - _i if num < 15_050_000 => Hardfork::ArrowGlacier, - _i if num < 17_034_870 => Hardfork::Paris, - _i if num < 19_426_587 => Hardfork::Shanghai, - _ => Hardfork::Latest, + _i if num < 1_150_000 => Self::Frontier, + _i if num < 1_920_000 => Self::Dao, + _i if num < 2_463_000 => Self::Homestead, + _i if num < 2_675_000 => Self::Tangerine, + _i if num < 4_370_000 => Self::SpuriousDragon, + _i if num < 7_280_000 => Self::Byzantium, + _i if num < 9_069_000 => Self::Constantinople, + _i if num < 9_200_000 => Self::Istanbul, + _i if num < 12_244_000 => Self::Muirglacier, + _i if num < 12_965_000 => Self::Berlin, + _i if num < 13_773_000 => Self::London, + _i if num < 15_050_000 => Self::ArrowGlacier, + _i if num < 17_034_870 => Self::Paris, + _i if num < 19_426_587 => Self::Shanghai, + _ => Self::Latest, } } } diff --git a/crates/anvil/src/lib.rs b/crates/anvil/src/lib.rs index 8fbb130f2..da08b0a4a 100644 --- a/crates/anvil/src/lib.rs +++ b/crates/anvil/src/lib.rs @@ -1,3 +1,6 @@ +#![doc = include_str!("../README.md")] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + #[macro_use] extern crate tracing; @@ -272,7 +275,7 @@ impl NodeHandle { self.config.print(fork); if !self.config.silent { if let Some(ipc_path) = self.ipc_path() { - println!("IPC path: {}", ipc_path); + println!("IPC path: {ipc_path}"); } println!( "Listening on {}", diff --git a/crates/anvil/src/pubsub.rs b/crates/anvil/src/pubsub.rs index 46caa9d7c..21a5c631c 100644 --- a/crates/anvil/src/pubsub.rs +++ b/crates/anvil/src/pubsub.rs @@ -91,8 +91,8 @@ pub enum EthSubscription { impl EthSubscription { fn poll_response(&mut self, cx: &mut Context<'_>) -> Poll> { match self { - EthSubscription::Logs(listener) => listener.poll(cx), - EthSubscription::Header(blocks, storage, id) => { + Self::Logs(listener) => listener.poll(cx), + Self::Header(blocks, storage, id) => { // this loop ensures we poll the receiver until it is pending, in which case the // underlying `UnboundedReceiver` will register the new waker, see // [`futures::channel::mpsc::UnboundedReceiver::poll_next()`] @@ -110,7 +110,7 @@ impl EthSubscription { } } } - EthSubscription::PendingTransactions(tx, id) => { + Self::PendingTransactions(tx, id) => { let res = ready!(tx.poll_next_unpin(cx)) .map(SubscriptionResult::TransactionHash) .map(to_rpc_result) diff --git a/crates/anvil/src/service.rs b/crates/anvil/src/service.rs index 6759f6812..6f8624d94 100644 --- a/crates/anvil/src/service.rs +++ b/crates/anvil/src/service.rs @@ -151,7 +151,7 @@ impl Stream for BlockProducer { Poll::Ready(Some(outcome)) } Err(err) => { - panic!("miner task failed: {}", err); + panic!("miner task failed: {err}"); } } } else { diff --git a/crates/anvil/tests/it/transaction.rs b/crates/anvil/tests/it/transaction.rs index b8ef827a7..c693381c6 100644 --- a/crates/anvil/tests/it/transaction.rs +++ b/crates/anvil/tests/it/transaction.rs @@ -1076,8 +1076,7 @@ async fn test_estimate_gas() { let error_message = error_result.unwrap_err().to_string(); assert!( error_message.contains("Insufficient funds for gas * price + value"), - "Error message did not match expected: {}", - error_message + "Error message did not match expected: {error_message}" ); // Setup state override to simulate sufficient funds for the recipient. diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index 443e5992c..421bd6d4d 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [[bin]] name = "cast" path = "bin/main.rs" diff --git a/crates/cast/bin/cmd/access_list.rs b/crates/cast/bin/cmd/access_list.rs index 0ec110bae..2a672bffd 100644 --- a/crates/cast/bin/cmd/access_list.rs +++ b/crates/cast/bin/cmd/access_list.rs @@ -49,7 +49,7 @@ pub struct AccessListArgs { impl AccessListArgs { pub async fn run(self) -> Result<()> { - let AccessListArgs { to, sig, args, tx, eth, block, json: to_json } = self; + let Self { to, sig, args, tx, eth, block, json: to_json } = self; let config = Config::from(ð); let provider = utils::get_provider(&config)?; @@ -73,7 +73,7 @@ impl AccessListArgs { let access_list: String = cast.access_list(&tx, block, to_json).await?; - println!("{}", access_list); + println!("{access_list}"); Ok(()) } diff --git a/crates/cast/bin/cmd/call.rs b/crates/cast/bin/cmd/call.rs index 3b4e8da36..afd6108c6 100644 --- a/crates/cast/bin/cmd/call.rs +++ b/crates/cast/bin/cmd/call.rs @@ -95,7 +95,7 @@ pub enum CallSubcommands { impl CallArgs { pub async fn run(self) -> Result<()> { - let CallArgs { + let Self { to, mut sig, mut args, diff --git a/crates/cast/bin/cmd/create2.rs b/crates/cast/bin/cmd/create2.rs index dae91c642..8c3b12dad 100644 --- a/crates/cast/bin/cmd/create2.rs +++ b/crates/cast/bin/cmd/create2.rs @@ -81,7 +81,7 @@ pub struct Create2Output { impl Create2Args { pub fn run(self) -> Result { - let Create2Args { + let Self { starts_with, ends_with, matching, diff --git a/crates/cast/bin/cmd/estimate.rs b/crates/cast/bin/cmd/estimate.rs index 5e60c9796..c48ce2d43 100644 --- a/crates/cast/bin/cmd/estimate.rs +++ b/crates/cast/bin/cmd/estimate.rs @@ -67,7 +67,7 @@ pub enum EstimateSubcommands { impl EstimateArgs { pub async fn run(self) -> Result<()> { - let EstimateArgs { to, mut sig, mut args, mut tx, block, eth, command } = self; + let Self { to, mut sig, mut args, mut tx, block, eth, command } = self; let config = Config::from(ð); let provider = utils::get_provider(&config)?; diff --git a/crates/cast/bin/cmd/find_block.rs b/crates/cast/bin/cmd/find_block.rs index f75f2c82f..e598a2121 100644 --- a/crates/cast/bin/cmd/find_block.rs +++ b/crates/cast/bin/cmd/find_block.rs @@ -18,7 +18,7 @@ pub struct FindBlockArgs { impl FindBlockArgs { pub async fn run(self) -> Result<()> { - let FindBlockArgs { timestamp, rpc } = self; + let Self { timestamp, rpc } = self; let ts_target = timestamp; let config = Config::from(&rpc); diff --git a/crates/cast/bin/cmd/interface.rs b/crates/cast/bin/cmd/interface.rs index 14de351f2..59956b78e 100644 --- a/crates/cast/bin/cmd/interface.rs +++ b/crates/cast/bin/cmd/interface.rs @@ -44,14 +44,7 @@ pub struct InterfaceArgs { impl InterfaceArgs { pub async fn run(self) -> Result<()> { - let InterfaceArgs { - path_or_address, - name, - pragma, - output: output_location, - etherscan, - json, - } = self; + let Self { path_or_address, name, pragma, output: output_location, etherscan, json } = self; let source = if Path::new(&path_or_address).exists() { AbiPath::Local { path: path_or_address, name } } else { diff --git a/crates/cast/bin/cmd/logs.rs b/crates/cast/bin/cmd/logs.rs index 85f0c8414..3c538f210 100644 --- a/crates/cast/bin/cmd/logs.rs +++ b/crates/cast/bin/cmd/logs.rs @@ -60,7 +60,7 @@ pub struct LogsArgs { impl LogsArgs { pub async fn run(self) -> Result<()> { - let LogsArgs { + let Self { from_block, to_block, address, @@ -91,7 +91,7 @@ impl LogsArgs { if !subscribe { let logs = cast.filter_logs(filter, json).await?; - println!("{}", logs); + println!("{logs}"); return Ok(()) } diff --git a/crates/cast/bin/cmd/mktx.rs b/crates/cast/bin/cmd/mktx.rs index 999688b19..cd0fdfecd 100644 --- a/crates/cast/bin/cmd/mktx.rs +++ b/crates/cast/bin/cmd/mktx.rs @@ -54,7 +54,7 @@ pub enum MakeTxSubcommands { impl MakeTxArgs { pub async fn run(self) -> Result<()> { - let MakeTxArgs { to, mut sig, mut args, command, tx, eth } = self; + let Self { to, mut sig, mut args, command, tx, eth } = self; let code = if let Some(MakeTxSubcommands::Create { code, diff --git a/crates/cast/bin/cmd/rpc.rs b/crates/cast/bin/cmd/rpc.rs index 9dffcfd18..cae5d3386 100644 --- a/crates/cast/bin/cmd/rpc.rs +++ b/crates/cast/bin/cmd/rpc.rs @@ -35,7 +35,7 @@ pub struct RpcArgs { impl RpcArgs { pub async fn run(self) -> Result<()> { - let RpcArgs { raw, method, params, rpc } = self; + let Self { raw, method, params, rpc } = self; let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index 0f3ca609d..d9ba45726 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -98,7 +98,7 @@ impl RunArgs { let tx = provider .get_transaction_by_hash(tx_hash) .await - .wrap_err_with(|| format!("tx not found: {:?}", tx_hash))? + .wrap_err_with(|| format!("tx not found: {tx_hash:?}"))? .ok_or_else(|| eyre::eyre!("tx not found: {:?}", tx_hash))?; // check if the tx is a system transaction diff --git a/crates/cast/bin/cmd/send.rs b/crates/cast/bin/cmd/send.rs index ea9cfcdcd..d4aee145b 100644 --- a/crates/cast/bin/cmd/send.rs +++ b/crates/cast/bin/cmd/send.rs @@ -78,7 +78,7 @@ pub enum SendTxSubcommands { impl SendTxArgs { pub async fn run(self) -> Result<()> { - let SendTxArgs { + let Self { eth, to, mut sig, diff --git a/crates/cast/bin/cmd/wallet/mod.rs b/crates/cast/bin/cmd/wallet/mod.rs index 3068af083..b312e4a60 100644 --- a/crates/cast/bin/cmd/wallet/mod.rs +++ b/crates/cast/bin/cmd/wallet/mod.rs @@ -172,7 +172,7 @@ pub enum WalletSubcommands { impl WalletSubcommands { pub async fn run(self) -> Result<()> { match self { - WalletSubcommands::New { path, unsafe_password, number, json, .. } => { + Self::New { path, unsafe_password, number, json, .. } => { let mut rng = thread_rng(); let mut json_values = if json { Some(vec![]) } else { None }; @@ -240,7 +240,7 @@ impl WalletSubcommands { } } } - WalletSubcommands::NewMnemonic { words, accounts } => { + Self::NewMnemonic { words, accounts } => { let mut rng = thread_rng(); let phrase = Mnemonic::::new_with_count(&mut rng, words)?.to_phrase(); @@ -261,10 +261,10 @@ impl WalletSubcommands { println!("Private key: 0x{}\n", hex::encode(wallet.signer().to_bytes())); } } - WalletSubcommands::Vanity(cmd) => { + Self::Vanity(cmd) => { cmd.run()?; } - WalletSubcommands::Address { wallet, private_key_override } => { + Self::Address { wallet, private_key_override } => { let wallet = private_key_override .map(|pk| WalletOpts { raw: RawWalletOpts { private_key: Some(pk), ..Default::default() }, @@ -276,7 +276,7 @@ impl WalletSubcommands { let addr = wallet.address(); println!("{}", addr.to_checksum(None)); } - WalletSubcommands::Sign { message, data, from_file, no_hash, wallet } => { + Self::Sign { message, data, from_file, no_hash, wallet } => { let wallet = wallet.signer().await?; let sig = if data { let typed_data: TypedData = if from_file { @@ -294,7 +294,7 @@ impl WalletSubcommands { }; println!("0x{}", hex::encode(sig.as_bytes())); } - WalletSubcommands::Verify { message, signature, address } => { + Self::Verify { message, signature, address } => { let recovered_address = Self::recover_address_from_message(&message, &signature)?; if address == recovered_address { println!("Validation succeeded. Address {address} signed this message."); @@ -302,12 +302,7 @@ impl WalletSubcommands { println!("Validation failed. Address {address} did not sign this message."); } } - WalletSubcommands::Import { - account_name, - keystore_dir, - unsafe_password, - raw_wallet_options, - } => { + Self::Import { account_name, keystore_dir, unsafe_password, raw_wallet_options } => { // Set up keystore directory let dir = if let Some(path) = keystore_dir { Path::new(&path).to_path_buf() @@ -365,10 +360,10 @@ flag to set your key via: ); println!("{}", success_message.green()); } - WalletSubcommands::List(cmd) => { + Self::List(cmd) => { cmd.run().await?; } - WalletSubcommands::DerivePrivateKey { mnemonic, mnemonic_index } => { + Self::DerivePrivateKey { mnemonic, mnemonic_index } => { let phrase = Mnemonic::::new_from_phrase(mnemonic.as_str())?.to_phrase(); let builder = MnemonicBuilder::::default().phrase(phrase.as_str()); let derivation_path = "m/44'/60'/0'/0/"; @@ -381,7 +376,7 @@ flag to set your key via: println!("Address: {}", wallet.address()); println!("Private key: 0x{}\n", hex::encode(wallet.signer().to_bytes())); } - WalletSubcommands::DecryptKeystore { account_name, keystore_dir, unsafe_password } => { + Self::DecryptKeystore { account_name, keystore_dir, unsafe_password } => { // Set up keystore directory let dir = if let Some(path) = keystore_dir { Path::new(&path).to_path_buf() diff --git a/crates/cast/bin/cmd/wallet/vanity.rs b/crates/cast/bin/cmd/wallet/vanity.rs index 28ada95b1..5bab0e318 100644 --- a/crates/cast/bin/cmd/wallet/vanity.rs +++ b/crates/cast/bin/cmd/wallet/vanity.rs @@ -64,7 +64,7 @@ struct Wallets { impl WalletData { pub fn new(wallet: &LocalWallet) -> Self { - WalletData { + Self { address: wallet.address().to_checksum(None), private_key: format!("0x{}", hex::encode(wallet.signer().to_bytes())), } diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index 0af23e200..7baf54331 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -309,7 +309,7 @@ async fn main() -> Result<()> { { let resolved = match func_names { Some(v) => v.join("|"), - None => "".to_string(), + None => String::new(), }; println!("{selector}\t{arguments:max_args_len$}\t{resolved}"); } diff --git a/crates/cast/src/base.rs b/crates/cast/src/base.rs index 44f58091e..63ae59c1c 100644 --- a/crates/cast/src/base.rs +++ b/crates/cast/src/base.rs @@ -90,7 +90,7 @@ impl TryFrom for Base { impl From for u32 { fn from(b: Base) -> Self { - b as u32 + b as Self } } @@ -293,7 +293,7 @@ impl From for NumberWithBase { impl From for I256 { fn from(n: NumberWithBase) -> Self { - I256::from_raw(n.number) + Self::from_raw(n.number) } } diff --git a/crates/cast/src/errors.rs b/crates/cast/src/errors.rs index 40a8e9755..3e6c5977b 100644 --- a/crates/cast/src/errors.rs +++ b/crates/cast/src/errors.rs @@ -15,18 +15,18 @@ pub enum FunctionSignatureError { impl fmt::Display for FunctionSignatureError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - FunctionSignatureError::MissingSignature => { + Self::MissingSignature => { writeln!(f, "Function signature must be set") } - FunctionSignatureError::MissingEtherscan { sig } => { + Self::MissingEtherscan { sig } => { writeln!(f, "Failed to determine function signature for `{sig}`")?; writeln!(f, "To lookup a function signature of a deployed contract by name, a valid ETHERSCAN_API_KEY must be set.")?; write!(f, "\tOr did you mean:\t {sig}()") } - FunctionSignatureError::UnknownChain(chain) => { + Self::UnknownChain(chain) => { write!(f, "Resolving via etherscan requires a known chain. Unknown chain: {chain}") } - FunctionSignatureError::MissingToAddress => f.write_str("Target address must be set"), + Self::MissingToAddress => f.write_str("Target address must be set"), } } } diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index c83fe9aa1..70e8523ec 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -1,3 +1,6 @@ +#![doc = include_str!("../README.md")] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + use alloy_consensus::TxEnvelope; use alloy_dyn_abi::{DynSolType, DynSolValue, FunctionExt}; use alloy_json_abi::{ContractObject, Function}; @@ -288,7 +291,7 @@ where pub async fn publish( &self, mut raw_tx: String, - ) -> Result> { + ) -> Result> { raw_tx = match raw_tx.strip_prefix("0x") { Some(s) => s.to_string(), None => raw_tx, @@ -348,7 +351,7 @@ where async fn block_field_as_num>(&self, block: B, field: String) -> Result { let block = block.into(); - let block_field = Cast::block( + let block_field = Self::block( self, block, false, @@ -367,22 +370,22 @@ where } pub async fn base_fee>(&self, block: B) -> Result { - Cast::block_field_as_num(self, block, String::from("baseFeePerGas")).await + Self::block_field_as_num(self, block, String::from("baseFeePerGas")).await } pub async fn age>(&self, block: B) -> Result { let timestamp_str = - Cast::block_field_as_num(self, block, String::from("timestamp")).await?.to_string(); + Self::block_field_as_num(self, block, String::from("timestamp")).await?.to_string(); let datetime = DateTime::from_timestamp(timestamp_str.parse::().unwrap(), 0).unwrap(); Ok(datetime.format("%a %b %e %H:%M:%S %Y").to_string()) } pub async fn timestamp>(&self, block: B) -> Result { - Cast::block_field_as_num(self, block, "timestamp".to_string()).await + Self::block_field_as_num(self, block, "timestamp".to_string()).await } pub async fn chain(&self) -> Result<&str> { - let genesis_hash = Cast::block( + let genesis_hash = Self::block( self, 0, false, @@ -394,7 +397,7 @@ where Ok(match &genesis_hash[..] { "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" => { - match &(Cast::block(self, 1920000, false, Some("hash".to_string()), false).await?)[..] + match &(Self::block(self, 1920000, false, Some("hash".to_string()), false).await?)[..] { "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f" => { "etclive" @@ -433,7 +436,7 @@ where "0x6d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34" => "bsctest", "0x0d21840abff46b96c84b2ac9e10e4f5cdaeb5693cb665db62a2f3b02d2d57b5b" => "bsc", "0x31ced5b9beb7f8782b014660da0cb18cc409f121f408186886e1ca3e8eeca96b" => { - match &(Cast::block(self, 1, false, Some(String::from("hash")), false).await?)[..] { + match &(Self::block(self, 1, false, Some(String::from("hash")), false).await?)[..] { "0x738639479dc82d199365626f90caa82f7eafcfe9ed354b456fb3d294597ceb53" => { "avalanche-fuji" } @@ -931,12 +934,12 @@ where } first = false; let log_str = serde_json::to_string(&log).unwrap(); - write!(output, "{}", log_str)?; + write!(output, "{log_str}")?; } else { let log_str = log.pretty() .replacen('\n', "- ", 1) // Remove empty first line .replace('\n', "\n "); // Indent - writeln!(output, "{}", log_str)?; + writeln!(output, "{log_str}")?; } }, // Break on cancel signal, to allow for closing JSON bracket @@ -1959,7 +1962,7 @@ impl SimpleCast { let mut nonce = nonce_start; while nonce < u32::MAX && !found.load(Ordering::Relaxed) { - let input = format!("{}{}({}", name, nonce, params); + let input = format!("{name}{nonce}({params}"); let hash = keccak256(input.as_bytes()); let selector = &hash[..4]; diff --git a/crates/cast/src/rlp_converter.rs b/crates/cast/src/rlp_converter.rs index d0e94301e..d0d60c082 100644 --- a/crates/cast/src/rlp_converter.rs +++ b/crates/cast/src/rlp_converter.rs @@ -14,8 +14,8 @@ pub enum Item { impl Encodable for Item { fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { match self { - Item::Array(arr) => arr.encode(out), - Item::Data(data) => <[u8]>::encode(data, out), + Self::Array(arr) => arr.encode(out), + Self::Data(data) => <[u8]>::encode(data, out), } } } @@ -31,11 +31,11 @@ impl Decodable for Item { let view = &mut d; let mut v = Vec::new(); while !view.is_empty() { - v.push(Item::decode(view)?); + v.push(Self::decode(view)?); } - Ok(Item::Array(v)) + Ok(Self::Array(v)) } else { - Ok(Item::Data(d.to_vec())) + Ok(Self::Data(d.to_vec())) }; buf.advance(h.payload_length); r @@ -43,16 +43,16 @@ impl Decodable for Item { } impl Item { - pub(crate) fn value_to_item(value: &Value) -> eyre::Result { + pub(crate) fn value_to_item(value: &Value) -> eyre::Result { return match value { - Value::Null => Ok(Item::Data(vec![])), + Value::Null => Ok(Self::Data(vec![])), Value::Bool(_) => { eyre::bail!("RLP input should not contain booleans") } // If a value is passed without quotes we cast it to string - Value::Number(n) => Ok(Item::value_to_item(&Value::String(n.to_string()))?), - Value::String(s) => Ok(Item::Data(hex::decode(s).expect("Could not decode hex"))), - Value::Array(values) => values.iter().map(Item::value_to_item).collect(), + Value::Number(n) => Ok(Self::value_to_item(&Value::String(n.to_string()))?), + Value::String(s) => Ok(Self::Data(hex::decode(s).expect("Could not decode hex"))), + Value::Array(values) => values.iter().map(Self::value_to_item).collect(), Value::Object(_) => { eyre::bail!("RLP input can not contain objects") } @@ -60,9 +60,9 @@ impl Item { } } -impl FromIterator for Item { - fn from_iter>(iter: T) -> Self { - Item::Array(Vec::from_iter(iter)) +impl FromIterator for Item { + fn from_iter>(iter: T) -> Self { + Self::Array(Vec::from_iter(iter)) } } @@ -70,10 +70,10 @@ impl FromIterator for Item { impl fmt::Display for Item { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { match self { - Item::Data(dat) => { + Self::Data(dat) => { write!(f, "\"0x{}\"", hex::encode(dat))?; } - Item::Array(items) => { + Self::Array(items) => { f.write_str("[")?; for (i, item) in items.iter().enumerate() { if i > 0 { diff --git a/crates/cheatcodes/Cargo.toml b/crates/cheatcodes/Cargo.toml index f712b7766..074740765 100644 --- a/crates/cheatcodes/Cargo.toml +++ b/crates/cheatcodes/Cargo.toml @@ -11,6 +11,9 @@ homepage.workspace = true repository.workspace = true exclude.workspace = true +[lints] +workspace = true + [dependencies] foundry-cheatcodes-spec.workspace = true foundry-common.workspace = true diff --git a/crates/cheatcodes/spec/Cargo.toml b/crates/cheatcodes/spec/Cargo.toml index ae4ca21e0..738bb62b7 100644 --- a/crates/cheatcodes/spec/Cargo.toml +++ b/crates/cheatcodes/spec/Cargo.toml @@ -11,6 +11,9 @@ homepage.workspace = true repository.workspace = true exclude.workspace = true +[lints] +workspace = true + [dependencies] foundry-macros.workspace = true alloy-sol-types = { workspace = true, features = ["json"] } diff --git a/crates/cheatcodes/spec/src/lib.rs b/crates/cheatcodes/spec/src/lib.rs index b2a267f8d..37cf149cd 100644 --- a/crates/cheatcodes/spec/src/lib.rs +++ b/crates/cheatcodes/spec/src/lib.rs @@ -1,5 +1,6 @@ #![doc = include_str!("../README.md")] -#![warn(missing_docs, unreachable_pub, unused_crate_dependencies, rust_2018_idioms)] +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] use serde::{Deserialize, Serialize}; use std::{borrow::Cow, fmt}; diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index d1123b448..410039852 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -2159,23 +2159,21 @@ impl PartialEq for ForgeContext { // and script group case (any of dry run, broadcast or resume). fn eq(&self, other: &Self) -> bool { match (self, other) { - (_, &ForgeContext::TestGroup) => { - self == &ForgeContext::Test || - self == &ForgeContext::Snapshot || - self == &ForgeContext::Coverage + (_, &Self::TestGroup) => { + self == &Self::Test || self == &Self::Snapshot || self == &Self::Coverage } - (_, &ForgeContext::ScriptGroup) => { - self == &ForgeContext::ScriptDryRun || - self == &ForgeContext::ScriptBroadcast || - self == &ForgeContext::ScriptResume + (_, &Self::ScriptGroup) => { + self == &Self::ScriptDryRun || + self == &Self::ScriptBroadcast || + self == &Self::ScriptResume } - (&ForgeContext::Test, &ForgeContext::Test) | - (&ForgeContext::Snapshot, &ForgeContext::Snapshot) | - (&ForgeContext::Coverage, &ForgeContext::Coverage) | - (&ForgeContext::ScriptDryRun, &ForgeContext::ScriptDryRun) | - (&ForgeContext::ScriptBroadcast, &ForgeContext::ScriptBroadcast) | - (&ForgeContext::ScriptResume, &ForgeContext::ScriptResume) | - (&ForgeContext::Unknown, &ForgeContext::Unknown) => true, + (&Self::Test, &Self::Test) | + (&Self::Snapshot, &Self::Snapshot) | + (&Self::Coverage, &Self::Coverage) | + (&Self::ScriptDryRun, &Self::ScriptDryRun) | + (&Self::ScriptBroadcast, &Self::ScriptBroadcast) | + (&Self::ScriptResume, &Self::ScriptResume) | + (&Self::Unknown, &Self::Unknown) => true, _ => false, } } diff --git a/crates/cheatcodes/src/evm/prank.rs b/crates/cheatcodes/src/evm/prank.rs index 3e1452d42..4e4ef81f7 100644 --- a/crates/cheatcodes/src/evm/prank.rs +++ b/crates/cheatcodes/src/evm/prank.rs @@ -29,16 +29,8 @@ impl Prank { new_origin: Option
, depth: u64, single_call: bool, - ) -> Prank { - Prank { - prank_caller, - prank_origin, - new_caller, - new_origin, - depth, - single_call, - used: false, - } + ) -> Self { + Self { prank_caller, prank_origin, new_caller, new_origin, depth, single_call, used: false } } /// Apply the prank by setting `used` to true iff it is false @@ -47,7 +39,7 @@ impl Prank { if self.used { None } else { - Some(Prank { used: true, ..self.clone() }) + Some(Self { used: true, ..self.clone() }) } } } diff --git a/crates/cheatcodes/src/json.rs b/crates/cheatcodes/src/json.rs index e493078a3..6b7dd0e97 100644 --- a/crates/cheatcodes/src/json.rs +++ b/crates/cheatcodes/src/json.rs @@ -251,7 +251,7 @@ impl Cheatcode for serializeBytes_1Call { impl Cheatcode for serializeUintToHexCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { objectKey, valueKey, value } = self; - let hex = format!("0x{:x}", value); + let hex = format!("0x{value:x}"); serialize_json(state, objectKey, Some(valueKey), &hex) } } @@ -445,7 +445,7 @@ pub(super) fn json_value_to_token(value: &Value) -> Result { if let Some(mut val) = string.strip_prefix("0x") { let s; if val.len() % 2 != 0 { - s = format!("0{}", val); + s = format!("0{val}"); val = &s[..]; } let bytes = hex::decode(val)?; diff --git a/crates/cheatcodes/src/lib.rs b/crates/cheatcodes/src/lib.rs index 01695fa70..023ef877d 100644 --- a/crates/cheatcodes/src/lib.rs +++ b/crates/cheatcodes/src/lib.rs @@ -2,7 +2,8 @@ //! //! Foundry cheatcodes implementations. -#![warn(missing_docs, unreachable_pub, unused_crate_dependencies, rust_2018_idioms)] +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #![allow(elided_lifetimes_in_paths)] // Cheats context uses 3 lifetimes #[macro_use] diff --git a/crates/cheatcodes/src/test/assert.rs b/crates/cheatcodes/src/test/assert.rs index 3c1ab22f3..c7dce1473 100644 --- a/crates/cheatcodes/src/test/assert.rs +++ b/crates/cheatcodes/src/test/assert.rs @@ -925,7 +925,7 @@ impl Cheatcode for assertLeDecimal_3Call { impl Cheatcode for assertApproxEqAbs_0Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { Ok(uint_assert_approx_eq_abs(self.left, self.right, self.maxDelta) - .map_err(|e| format!("assertion failed: {}", e))?) + .map_err(|e| format!("assertion failed: {e}"))?) } } @@ -939,7 +939,7 @@ impl Cheatcode for assertApproxEqAbs_1Call { impl Cheatcode for assertApproxEqAbs_2Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { Ok(int_assert_approx_eq_abs(self.left, self.right, self.maxDelta) - .map_err(|e| format!("assertion failed: {}", e))?) + .map_err(|e| format!("assertion failed: {e}"))?) } } @@ -981,7 +981,7 @@ impl Cheatcode for assertApproxEqAbsDecimal_3Call { impl Cheatcode for assertApproxEqRel_0Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { Ok(uint_assert_approx_eq_rel(self.left, self.right, self.maxPercentDelta) - .map_err(|e| format!("assertion failed: {}", e))?) + .map_err(|e| format!("assertion failed: {e}"))?) } } @@ -995,7 +995,7 @@ impl Cheatcode for assertApproxEqRel_1Call { impl Cheatcode for assertApproxEqRel_2Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { Ok(int_assert_approx_eq_rel(self.left, self.right, self.maxPercentDelta) - .map_err(|e| format!("assertion failed: {}", e))?) + .map_err(|e| format!("assertion failed: {e}"))?) } } diff --git a/crates/chisel/Cargo.toml b/crates/chisel/Cargo.toml index f5e9b7b02..f527deb2e 100644 --- a/crates/chisel/Cargo.toml +++ b/crates/chisel/Cargo.toml @@ -13,6 +13,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [[bin]] name = "chisel" path = "bin/main.rs" diff --git a/crates/chisel/src/cmd.rs b/crates/chisel/src/cmd.rs index 99e4086b3..86925fc1b 100644 --- a/crates/chisel/src/cmd.rs +++ b/crates/chisel/src/cmd.rs @@ -61,24 +61,24 @@ impl FromStr for ChiselCommand { fn from_str(s: &str) -> Result { match s.to_lowercase().as_ref() { - "help" | "h" => Ok(ChiselCommand::Help), - "quit" | "q" => Ok(ChiselCommand::Quit), - "clear" | "c" => Ok(ChiselCommand::Clear), - "source" | "so" => Ok(ChiselCommand::Source), - "save" | "s" => Ok(ChiselCommand::Save), - "list" | "ls" => Ok(ChiselCommand::ListSessions), - "load" | "l" => Ok(ChiselCommand::Load), - "clearcache" | "cc" => Ok(ChiselCommand::ClearCache), - "fork" | "f" => Ok(ChiselCommand::Fork), - "traces" | "t" => Ok(ChiselCommand::Traces), - "calldata" | "cd" => Ok(ChiselCommand::Calldata), - "memdump" | "md" => Ok(ChiselCommand::MemDump), - "stackdump" | "sd" => Ok(ChiselCommand::StackDump), - "export" | "ex" => Ok(ChiselCommand::Export), - "fetch" | "fe" => Ok(ChiselCommand::Fetch), - "exec" | "e" => Ok(ChiselCommand::Exec), - "rawstack" | "rs" => Ok(ChiselCommand::RawStack), - "edit" => Ok(ChiselCommand::Edit), + "help" | "h" => Ok(Self::Help), + "quit" | "q" => Ok(Self::Quit), + "clear" | "c" => Ok(Self::Clear), + "source" | "so" => Ok(Self::Source), + "save" | "s" => Ok(Self::Save), + "list" | "ls" => Ok(Self::ListSessions), + "load" | "l" => Ok(Self::Load), + "clearcache" | "cc" => Ok(Self::ClearCache), + "fork" | "f" => Ok(Self::Fork), + "traces" | "t" => Ok(Self::Traces), + "calldata" | "cd" => Ok(Self::Calldata), + "memdump" | "md" => Ok(Self::MemDump), + "stackdump" | "sd" => Ok(Self::StackDump), + "export" | "ex" => Ok(Self::Export), + "fetch" | "fe" => Ok(Self::Fetch), + "exec" | "e" => Ok(Self::Exec), + "rawstack" | "rs" => Ok(Self::RawStack), + "edit" => Ok(Self::Edit), _ => Err(ChiselDispatcher::make_error(format!( "Unknown command \"{s}\"! See available commands with `!help`.", )) @@ -103,10 +103,10 @@ pub enum CmdCategory { impl core::fmt::Display for CmdCategory { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let string = match self { - CmdCategory::General => "General", - CmdCategory::Session => "Session", - CmdCategory::Env => "Environment", - CmdCategory::Debug => "Debug", + Self::General => "General", + Self::Session => "Session", + Self::Env => "Environment", + Self::Debug => "Debug", }; f.write_str(string) } diff --git a/crates/chisel/src/dispatcher.rs b/crates/chisel/src/dispatcher.rs index e32b1ac01..33520e308 100644 --- a/crates/chisel/src/dispatcher.rs +++ b/crates/chisel/src/dispatcher.rs @@ -86,11 +86,11 @@ impl DispatchResult { pub fn is_error(&self) -> bool { matches!( self, - DispatchResult::Failure(_) | - DispatchResult::CommandFailed(_) | - DispatchResult::UnrecognizedCommand(_) | - DispatchResult::SolangParserFailed(_) | - DispatchResult::FileIoError(_) + Self::Failure(_) | + Self::CommandFailed(_) | + Self::UnrecognizedCommand(_) | + Self::SolangParserFailed(_) | + Self::FileIoError(_) ) } } @@ -414,8 +414,7 @@ impl ChiselDispatcher { ))) } Err(e) => DispatchResult::CommandFailed(Self::make_error(format!( - "Invalid calldata: {}", - e + "Invalid calldata: {e}" ))), } } diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index eea595486..f9cce4994 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -349,7 +349,7 @@ fn format_token(token: DynSolValue) -> String { DynSolValue::Int(i, bit_len) => { format!( "Type: {}\n├ Hex: {}\n├ Hex (full word): {}\n└ Decimal: {}", - format!("int{}", bit_len).red(), + format!("int{bit_len}").red(), format!( "0x{}", format!("{i:x}") @@ -367,7 +367,7 @@ fn format_token(token: DynSolValue) -> String { DynSolValue::Uint(i, bit_len) => { format!( "Type: {}\n├ Hex: {}\n├ Hex (full word): {}\n└ Decimal: {}", - format!("uint{}", bit_len).red(), + format!("uint{bit_len}").red(), format!( "0x{}", format!("{i:x}") @@ -755,7 +755,7 @@ impl Type { .map(|(returns, _)| map_parameters(returns)) .unwrap_or_default(); Self::Function( - Box::new(Type::Custom(vec!["__fn_type__".to_string()])), + Box::new(Self::Custom(vec!["__fn_type__".to_string()])), params, returns, ) @@ -900,7 +900,7 @@ impl Type { /// Recurses over itself, appending all the idents and function arguments in the order that they /// are found - fn recurse(&self, types: &mut Vec, args: &mut Option>>) { + fn recurse(&self, types: &mut Vec, args: &mut Option>>) { match self { Self::Builtin(ty) => types.push(ty.to_string()), Self::Custom(tys) => types.extend(tys.clone()), @@ -1167,7 +1167,7 @@ impl Type { .function_definitions .get(&function_name.name)?; let return_parameter = contract.as_ref().returns.first()?.to_owned().1?; - Type::ethabi(&return_parameter.ty, Some(intermediate)).map(|p| (contract_expr.unwrap(), p)) + Self::ethabi(&return_parameter.ty, Some(intermediate)).map(|p| (contract_expr.unwrap(), p)) } /// Inverts Int to Uint and viceversa. diff --git a/crates/chisel/src/lib.rs b/crates/chisel/src/lib.rs index 2004ed0c4..d0085c327 100644 --- a/crates/chisel/src/lib.rs +++ b/crates/chisel/src/lib.rs @@ -1,8 +1,5 @@ #![doc = include_str!("../README.md")] -#![warn(missing_docs)] -#![warn(unused_extern_crates)] -#![forbid(unsafe_code)] -#![forbid(where_clauses_object_safety)] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] /// REPL input dispatcher module pub mod dispatcher; diff --git a/crates/chisel/src/session.rs b/crates/chisel/src/session.rs index 3be34a179..e0865e28a 100644 --- a/crates/chisel/src/session.rs +++ b/crates/chisel/src/session.rs @@ -205,9 +205,9 @@ impl ChiselSession { /// /// Optionally, an owned instance of the loaded chisel session. pub fn load(id: &str) -> Result { - let cache_dir = ChiselSession::cache_dir()?; + let cache_dir = Self::cache_dir()?; let contents = std::fs::read_to_string(Path::new(&format!("{cache_dir}chisel-{id}.json")))?; - let chisel_env: ChiselSession = serde_json::from_str(&contents)?; + let chisel_env: Self = serde_json::from_str(&contents)?; Ok(chisel_env) } @@ -241,7 +241,7 @@ impl ChiselSession { pub fn latest() -> Result { let last_session = Self::latest_cached_session()?; let last_session_contents = std::fs::read_to_string(Path::new(&last_session))?; - let chisel_env: ChiselSession = serde_json::from_str(&last_session_contents)?; + let chisel_env: Self = serde_json::from_str(&last_session_contents)?; Ok(chisel_env) } } diff --git a/crates/chisel/src/session_source.rs b/crates/chisel/src/session_source.rs index 6882a63aa..b8ba084cd 100644 --- a/crates/chisel/src/session_source.rs +++ b/crates/chisel/src/session_source.rs @@ -225,7 +225,7 @@ impl SessionSource { /// /// Optionally, a shallow-cloned [SessionSource] with the passed content appended to the /// source code. - pub fn clone_with_new_line(&self, mut content: String) -> Result<(SessionSource, bool)> { + pub fn clone_with_new_line(&self, mut content: String) -> Result<(Self, bool)> { let new_source = self.shallow_clone(); if let Some(parsed) = parse_fragment(new_source.solc, new_source.config, &content) .or_else(|| { diff --git a/crates/chisel/src/solidity_helper.rs b/crates/chisel/src/solidity_helper.rs index f09ad1683..84140c820 100644 --- a/crates/chisel/src/solidity_helper.rs +++ b/crates/chisel/src/solidity_helper.rs @@ -101,7 +101,7 @@ impl SolidityHelper { } /// Highlights a solidity source string - pub fn highlight(input: &str) -> Cow { + pub fn highlight(input: &str) -> Cow<'_, str> { if !yansi::is_enabled() { return Cow::Borrowed(input) } @@ -256,7 +256,7 @@ impl Highlighter for SolidityHelper { } impl Validator for SolidityHelper { - fn validate(&self, ctx: &mut ValidationContext) -> rustyline::Result { + fn validate(&self, ctx: &mut ValidationContext<'_>) -> rustyline::Result { Ok(Self::validate_closed(ctx.input())) } } diff --git a/crates/chisel/tests/cache.rs b/crates/chisel/tests/cache.rs index 5575a814a..7be980ec8 100644 --- a/crates/chisel/tests/cache.rs +++ b/crates/chisel/tests/cache.rs @@ -45,7 +45,7 @@ fn test_write_session() { foundry_config, ..Default::default() }) - .unwrap_or_else(|e| panic!("Failed to create ChiselSession!, {}", e)); + .unwrap_or_else(|e| panic!("Failed to create ChiselSession!, {e}")); // Write the session let cached_session_name = env.write().unwrap(); @@ -73,7 +73,7 @@ fn test_write_session_with_name() { foundry_config, ..Default::default() }) - .unwrap_or_else(|e| panic!("Failed to create ChiselSession! {}", e)); + .unwrap_or_else(|e| panic!("Failed to create ChiselSession! {e}")); env.id = Some(String::from("test")); // Write the session @@ -123,7 +123,7 @@ fn test_list_sessions() { foundry_config, ..Default::default() }) - .unwrap_or_else(|e| panic!("Failed to create ChiselSession! {}", e)); + .unwrap_or_else(|e| panic!("Failed to create ChiselSession! {e}")); env.write().unwrap(); @@ -150,7 +150,7 @@ fn test_load_cache() { foundry_config, ..Default::default() }) - .unwrap_or_else(|e| panic!("Failed to create ChiselSession! {}", e)); + .unwrap_or_else(|e| panic!("Failed to create ChiselSession! {e}")); env.write().unwrap(); // Load the session @@ -178,7 +178,7 @@ fn test_write_same_session_multiple_times() { foundry_config, ..Default::default() }) - .unwrap_or_else(|e| panic!("Failed to create ChiselSession! {}", e)); + .unwrap_or_else(|e| panic!("Failed to create ChiselSession! {e}")); env.write().unwrap(); env.write().unwrap(); env.write().unwrap(); @@ -201,7 +201,7 @@ fn test_load_latest_cache() { foundry_config: foundry_config.clone(), ..Default::default() }) - .unwrap_or_else(|e| panic!("Failed to create ChiselSession! {}", e)); + .unwrap_or_else(|e| panic!("Failed to create ChiselSession! {e}")); env.write().unwrap(); let wait_time = std::time::Duration::from_millis(100); @@ -211,7 +211,7 @@ fn test_load_latest_cache() { foundry_config, ..Default::default() }) - .unwrap_or_else(|e| panic!("Failed to create ChiselSession! {}", e)); + .unwrap_or_else(|e| panic!("Failed to create ChiselSession! {e}")); env2.write().unwrap(); // Load the latest session diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 8c4f353dd..7b66c7045 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -9,6 +9,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [dependencies] forge-fmt.workspace = true foundry-common.workspace = true diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index f0e90a96c..6f5e2f607 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -1,4 +1,9 @@ -#![warn(unused_crate_dependencies)] +//! # foundry-cli +//! +//! Common CLI utilities. + +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #[macro_use] extern crate tracing; diff --git a/crates/cli/src/opts/build/core.rs b/crates/cli/src/opts/build/core.rs index 85575bccc..a3143144d 100644 --- a/crates/cli/src/opts/build/core.rs +++ b/crates/cli/src/opts/build/core.rs @@ -172,7 +172,7 @@ impl<'a> From<&'a CoreBuildArgs> for Figment { impl<'a> From<&'a CoreBuildArgs> for Config { fn from(args: &'a CoreBuildArgs) -> Self { let figment: Figment = args.into(); - let mut config = Config::from_provider(figment).sanitized(); + let mut config = Self::from_provider(figment).sanitized(); // if `--config-path` is set we need to adjust the config's root path to the actual root // path for the project, otherwise it will the parent dir of the `--config-path` if args.project_paths.config_path.is_some() { diff --git a/crates/cli/src/opts/dependency.rs b/crates/cli/src/opts/dependency.rs index 5fa1d851f..945386d2b 100644 --- a/crates/cli/src/opts/dependency.rs +++ b/crates/cli/src/opts/dependency.rs @@ -120,7 +120,7 @@ impl FromStr for Dependency { (None, None, None) }; - Ok(Dependency { name: name.or_else(|| alias.clone()).unwrap(), url, tag, alias }) + Ok(Self { name: name.or_else(|| alias.clone()).unwrap(), url, tag, alias }) } } diff --git a/crates/cli/src/utils/cmd.rs b/crates/cli/src/utils/cmd.rs index b9020c045..6c010655b 100644 --- a/crates/cli/src/utils/cmd.rs +++ b/crates/cli/src/utils/cmd.rs @@ -37,7 +37,7 @@ pub fn remove_contract( let contract = if let Some(contract) = output.remove(path.to_string_lossy(), name) { contract } else { - let mut err = format!("could not find artifact: `{}`", name); + let mut err = format!("could not find artifact: `{name}`"); if let Some(suggestion) = super::did_you_mean(name, output.artifacts().map(|(name, _)| name)).pop() { @@ -146,7 +146,7 @@ pub fn init_progress(len: u64, label: &str) -> indicatif::ProgressBar { let mut template = "{prefix}{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {pos}/{len} " .to_string(); - write!(template, "{}", label).unwrap(); + write!(template, "{label}").unwrap(); template += " ({eta})"; pb.set_style( indicatif::ProgressStyle::with_template(&template) diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index bdf0e3457..5b11a1be9 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -9,6 +9,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [dependencies] foundry-block-explorers = { workspace = true, features = ["foundry-compilers"] } foundry-compilers.workspace = true diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index 4b1cd7874..13fb79be0 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -305,10 +305,10 @@ impl ContractSources { output: &ProjectCompileOutput, root: &Path, libraries: &Libraries, - ) -> Result { + ) -> Result { let linker = Linker::new(root, output.artifact_ids().collect()); - let mut sources = ContractSources::default(); + let mut sources = Self::default(); for (id, artifact) in output.artifact_ids() { if let Some(file_id) = artifact.id { let abs_path = root.join(&id.source); @@ -618,18 +618,18 @@ pub enum SkipBuildFilter { impl SkipBuildFilter { fn new(s: &str) -> Self { match s { - "test" | "tests" => SkipBuildFilter::Tests, - "script" | "scripts" => SkipBuildFilter::Scripts, - s => SkipBuildFilter::Custom(s.to_string()), + "test" | "tests" => Self::Tests, + "script" | "scripts" => Self::Scripts, + s => Self::Custom(s.to_string()), } } /// Returns the pattern to match against a file fn file_pattern(&self) -> &str { match self { - SkipBuildFilter::Tests => ".t.sol", - SkipBuildFilter::Scripts => ".s.sol", - SkipBuildFilter::Custom(s) => s.as_str(), + Self::Tests => ".t.sol", + Self::Scripts => ".s.sol", + Self::Custom(s) => s.as_str(), } } diff --git a/crates/common/src/contracts.rs b/crates/common/src/contracts.rs index 3e6b2ce10..046a07425 100644 --- a/crates/common/src/contracts.rs +++ b/crates/common/src/contracts.rs @@ -117,7 +117,7 @@ impl ContractsByArtifact { } /// Finds a contract which has a similar bytecode as `code`. - pub fn find_by_creation_code(&self, code: &[u8]) -> Option { + pub fn find_by_creation_code(&self, code: &[u8]) -> Option> { self.iter().find(|(_, contract)| { if let Some(bytecode) = contract.bytecode() { bytecode_diff_score(bytecode.as_ref(), code) <= 0.1 @@ -128,7 +128,7 @@ impl ContractsByArtifact { } /// Finds a contract which has a similar deployed bytecode as `code`. - pub fn find_by_deployed_code(&self, code: &[u8]) -> Option { + pub fn find_by_deployed_code(&self, code: &[u8]) -> Option> { self.iter().find(|(_, contract)| { if let Some(deployed_bytecode) = contract.deployed_bytecode() { bytecode_diff_score(deployed_bytecode.as_ref(), code) <= 0.1 @@ -140,7 +140,7 @@ impl ContractsByArtifact { /// Finds a contract which deployed bytecode exactly matches the given code. Accounts for link /// references and immutables. - pub fn find_by_deployed_code_exact(&self, code: &[u8]) -> Option { + pub fn find_by_deployed_code_exact(&self, code: &[u8]) -> Option> { self.iter().find(|(_, contract)| { let Some(deployed_bytecode) = &contract.deployed_bytecode else { return false; @@ -233,7 +233,10 @@ impl ContractsByArtifact { /// Finds a contract which has the same contract name or identifier as `id`. If more than one is /// found, return error. - pub fn find_by_name_or_identifier(&self, id: &str) -> Result> { + pub fn find_by_name_or_identifier( + &self, + id: &str, + ) -> Result>> { let contracts = self .iter() .filter(|(artifact, _)| artifact.name == id || artifact.identifier() == id) diff --git a/crates/common/src/ens.rs b/crates/common/src/ens.rs index b96ecba8d..f84ea84ce 100644 --- a/crates/common/src/ens.rs +++ b/crates/common/src/ens.rs @@ -68,27 +68,27 @@ impl NameOrAddress { provider: &P, ) -> Result { match self { - NameOrAddress::Name(name) => provider.resolve_name(name).await, - NameOrAddress::Address(addr) => Ok(*addr), + Self::Name(name) => provider.resolve_name(name).await, + Self::Address(addr) => Ok(*addr), } } } impl From for NameOrAddress { fn from(name: String) -> Self { - NameOrAddress::Name(name) + Self::Name(name) } } impl From<&String> for NameOrAddress { fn from(name: &String) -> Self { - NameOrAddress::Name(name.clone()) + Self::Name(name.clone()) } } impl From
for NameOrAddress { fn from(addr: Address) -> Self { - NameOrAddress::Address(addr) + Self::Address(addr) } } @@ -97,9 +97,9 @@ impl FromStr for NameOrAddress { fn from_str(s: &str) -> Result { if let Ok(addr) = Address::from_str(s) { - Ok(NameOrAddress::Address(addr)) + Ok(Self::Address(addr)) } else { - Ok(NameOrAddress::Name(s.to_string())) + Ok(Self::Name(s.to_string())) } } } diff --git a/crates/common/src/errors/fs.rs b/crates/common/src/errors/fs.rs index b929e7838..1e466171e 100644 --- a/crates/common/src/errors/fs.rs +++ b/crates/common/src/errors/fs.rs @@ -46,47 +46,47 @@ pub enum FsPathError { impl FsPathError { /// Returns the complementary error variant for [`std::fs::write`]. pub fn write(source: io::Error, path: impl Into) -> Self { - FsPathError::Write { source, path: path.into() } + Self::Write { source, path: path.into() } } /// Returns the complementary error variant for [`std::fs::read`]. pub fn read(source: io::Error, path: impl Into) -> Self { - FsPathError::Read { source, path: path.into() } + Self::Read { source, path: path.into() } } /// Returns the complementary error variant for [`std::fs::copy`]. pub fn copy(source: io::Error, from: impl Into, to: impl Into) -> Self { - FsPathError::Copy { source, from: from.into(), to: to.into() } + Self::Copy { source, from: from.into(), to: to.into() } } /// Returns the complementary error variant for [`std::fs::read_link`]. pub fn read_link(source: io::Error, path: impl Into) -> Self { - FsPathError::ReadLink { source, path: path.into() } + Self::ReadLink { source, path: path.into() } } /// Returns the complementary error variant for [`File::create`]. pub fn create_file(source: io::Error, path: impl Into) -> Self { - FsPathError::CreateFile { source, path: path.into() } + Self::CreateFile { source, path: path.into() } } /// Returns the complementary error variant for [`std::fs::remove_file`]. pub fn remove_file(source: io::Error, path: impl Into) -> Self { - FsPathError::RemoveFile { source, path: path.into() } + Self::RemoveFile { source, path: path.into() } } /// Returns the complementary error variant for [`std::fs::create_dir`]. pub fn create_dir(source: io::Error, path: impl Into) -> Self { - FsPathError::CreateDir { source, path: path.into() } + Self::CreateDir { source, path: path.into() } } /// Returns the complementary error variant for [`std::fs::remove_dir`]. pub fn remove_dir(source: io::Error, path: impl Into) -> Self { - FsPathError::RemoveDir { source, path: path.into() } + Self::RemoveDir { source, path: path.into() } } /// Returns the complementary error variant for [`File::open`]. pub fn open(source: io::Error, path: impl Into) -> Self { - FsPathError::Open { source, path: path.into() } + Self::Open { source, path: path.into() } } } diff --git a/crates/common/src/fmt/console.rs b/crates/common/src/fmt/console.rs index e4b33c5cd..efade2e43 100644 --- a/crates/common/src/fmt/console.rs +++ b/crates/common/src/fmt/console.rs @@ -47,7 +47,7 @@ impl ConsoleFmt for String { FormatSpec::Number | FormatSpec::Integer | FormatSpec::Exponential | - FormatSpec::Hexadecimal => String::from("NaN"), + FormatSpec::Hexadecimal => Self::from("NaN"), } } } @@ -77,7 +77,7 @@ impl ConsoleFmt for U256 { } FormatSpec::Exponential => { let log = self.pretty().len() - 1; - let exp10 = U256::from(10).pow(U256::from(log)); + let exp10 = Self::from(10).pow(Self::from(log)); let amount = *self; let integer = amount / exp10; let decimal = (amount % exp10).to_string(); @@ -110,7 +110,7 @@ impl ConsoleFmt for I256 { } else { self.pretty().len() - 1 }; - let exp10 = I256::exp10(log); + let exp10 = Self::exp10(log); let integer = (amount / exp10).twos_complement(); let decimal = (amount % exp10).twos_complement().to_string(); let decimal = format!("{decimal:0>log$}").trim_end_matches('0').to_string(); diff --git a/crates/common/src/fmt/ui.rs b/crates/common/src/fmt/ui.rs index 635d1434b..f2f647ae8 100644 --- a/crates/common/src/fmt/ui.rs +++ b/crates/common/src/fmt/ui.rs @@ -223,7 +223,7 @@ blobGasUsed {}", // additional captured fields for (key, val) in other.iter() { - pretty.push_str(&format!("\n{} {}", key, val)); + pretty.push_str(&format!("\n{key} {val}")); } pretty @@ -271,9 +271,9 @@ transactions: {}", impl UIfmt for BlockTransactions { fn pretty(&self) -> String { match self { - BlockTransactions::Hashes(hashes) => hashes.pretty(), - BlockTransactions::Full(transactions) => transactions.pretty(), - BlockTransactions::Uncle => String::new(), + Self::Hashes(hashes) => hashes.pretty(), + Self::Full(transactions) => transactions.pretty(), + Self::Uncle => String::new(), } } } @@ -369,11 +369,11 @@ impl From for EthValue { impl UIfmt for EthValue { fn pretty(&self) -> String { match self { - EthValue::U64(num) => num.pretty(), - EthValue::U256(num) => num.pretty(), - EthValue::U64Array(arr) => arr.pretty(), - EthValue::U256Array(arr) => arr.pretty(), - EthValue::Other(val) => val.to_string().trim_matches('"').to_string(), + Self::U64(num) => num.pretty(), + Self::U256(num) => num.pretty(), + Self::U64Array(arr) => arr.pretty(), + Self::U256Array(arr) => arr.pretty(), + Self::Other(val) => val.to_string().trim_matches('"').to_string(), } } } diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index 2ffc066d6..fa1cecbbe 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -1,7 +1,11 @@ +//! # foundry-common +//! //! Common utilities for building and using foundry's tools. -#![warn(missing_docs, unused_crate_dependencies)] +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#[allow(unused_extern_crates)] // Used by `ConsoleFmt`. extern crate self as foundry_common; #[macro_use] diff --git a/crates/common/src/provider/mod.rs b/crates/common/src/provider/mod.rs index 1083052b5..388a795dd 100644 --- a/crates/common/src/provider/mod.rs +++ b/crates/common/src/provider/mod.rs @@ -106,7 +106,7 @@ impl ProviderBuilder { .or_else(|err| match err { ParseError::RelativeUrlWithoutBase => { if SocketAddr::from_str(url_str).is_ok() { - Url::parse(&format!("http://{}", url_str)) + Url::parse(&format!("http://{url_str}")) } else { let path = Path::new(url_str); @@ -235,7 +235,7 @@ impl ProviderBuilder { /// Constructs the `RetryProvider` taking all configs into account. pub fn build(self) -> Result { - let ProviderBuilder { + let Self { url, chain: _, max_retry, @@ -270,7 +270,7 @@ impl ProviderBuilder { /// Constructs the `RetryProvider` with a signer pub fn build_with_signer(self, signer: EthereumSigner) -> Result { - let ProviderBuilder { + let Self { url, chain: _, max_retry, diff --git a/crates/common/src/provider/runtime_transport.rs b/crates/common/src/provider/runtime_transport.rs index 38a24e81b..72c4dadc9 100644 --- a/crates/common/src/provider/runtime_transport.rs +++ b/crates/common/src/provider/runtime_transport.rs @@ -1,5 +1,6 @@ //! 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 alloy_json_rpc::{RequestPacket, ResponsePacket}; use alloy_pubsub::{PubSubConnect, PubSubFrontend}; @@ -11,7 +12,7 @@ use alloy_transport_http::Http; use alloy_transport_ipc::IpcConnect; use alloy_transport_ws::WsConnect; use reqwest::header::{HeaderName, HeaderValue}; -use std::{path::PathBuf, str::FromStr, sync::Arc}; +use std::{fmt, path::PathBuf, str::FromStr, sync::Arc}; use thiserror::Error; use tokio::sync::RwLock; use tower::Service; @@ -125,8 +126,8 @@ impl RuntimeTransportBuilder { } } -impl ::core::fmt::Display for RuntimeTransport { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { +impl fmt::Display for RuntimeTransport { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "RuntimeTransport {}", self.url) } } diff --git a/crates/common/src/selectors.rs b/crates/common/src/selectors.rs index 2537b2c0c..bf820a412 100644 --- a/crates/common/src/selectors.rs +++ b/crates/common/src/selectors.rs @@ -369,7 +369,7 @@ pub struct PossibleSigs { impl PossibleSigs { fn new() -> Self { - PossibleSigs { method: SelectorOrSig::Selector("0x00000000".to_string()), data: vec![] } + Self { method: SelectorOrSig::Selector("0x00000000".to_string()), data: vec![] } } } @@ -634,7 +634,7 @@ mod tests { let abi: JsonAbi = serde_json::from_str(r#"[{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function", "methodIdentifiers": {"transfer(address,uint256)(uint256)": "0xa9059cbb"}}]"#).unwrap(); let result = import_selectors(SelectorImportData::Abi(vec![abi])).await; - println!("{:?}", result); + println!("{result:?}"); assert_eq!( result.unwrap().result.function.duplicated.get("transfer(address,uint256)").unwrap(), "0xa9059cbb" diff --git a/crates/common/src/serde_helpers.rs b/crates/common/src/serde_helpers.rs index c1b61b6e2..776d049c4 100644 --- a/crates/common/src/serde_helpers.rs +++ b/crates/common/src/serde_helpers.rs @@ -15,10 +15,10 @@ pub enum Numeric { } impl From for U256 { - fn from(n: Numeric) -> U256 { + fn from(n: Numeric) -> Self { match n { Numeric::U256(n) => n, - Numeric::Num(n) => U256::from(n), + Numeric::Num(n) => Self::from(n), } } } @@ -28,7 +28,7 @@ impl FromStr for Numeric { fn from_str(s: &str) -> Result { if let Ok(val) = s.parse::() { - Ok(Numeric::U256(U256::from(val))) + Ok(Self::U256(U256::from(val))) } else if s.starts_with("0x") { U256::from_str_radix(s, 16).map(Numeric::U256).map_err(|err| err.to_string()) } else { @@ -63,10 +63,8 @@ impl NumberOrHexU256 { /// Tries to convert this into a [U256]]. pub fn try_into_u256(self) -> Result { match self { - NumberOrHexU256::Int(num) => { - U256::from_str(num.to_string().as_str()).map_err(E::custom) - } - NumberOrHexU256::Hex(val) => Ok(val), + Self::Int(num) => U256::from_str(num.to_string().as_str()).map_err(E::custom), + Self::Hex(val) => Ok(val), } } } diff --git a/crates/common/src/shell.rs b/crates/common/src/shell.rs index 89d990536..ac5ff1b96 100644 --- a/crates/common/src/shell.rs +++ b/crates/common/src/shell.rs @@ -213,18 +213,18 @@ impl ShellOut { pub fn memory() -> Self { #[allow(clippy::box_default)] #[allow(clippy::arc_with_non_send_sync)] - ShellOut::Write(WriteShellOut(Arc::new(Mutex::new(Box::new(Vec::new()))))) + Self::Write(WriteShellOut(Arc::new(Mutex::new(Box::new(Vec::new()))))) } /// Write a fragment to stdout fn write_stdout(&self, fragment: impl fmt::Display) -> io::Result<()> { match *self { - ShellOut::Stream => { + Self::Stream => { let stdout = io::stdout(); let mut handle = stdout.lock(); writeln!(handle, "{fragment}")?; } - ShellOut::Write(ref w) => { + Self::Write(ref w) => { w.write(fragment)?; } } @@ -234,12 +234,12 @@ impl ShellOut { /// Write output to stderr fn write_stderr(&self, fragment: impl fmt::Display) -> io::Result<()> { match *self { - ShellOut::Stream => { + Self::Stream => { let stderr = io::stderr(); let mut handle = stderr.lock(); writeln!(handle, "{fragment}")?; } - ShellOut::Write(ref w) => { + Self::Write(ref w) => { w.write(fragment)?; } } @@ -252,12 +252,12 @@ impl ShellOut { for<'r> F: FnOnce(&'r mut (dyn Write + 'r)) -> R, { match *self { - ShellOut::Stream => { + Self::Stream => { let stdout = io::stdout(); let mut handler = stdout.lock(); f(&mut handler) } - ShellOut::Write(ref w) => w.with_stdout(f), + Self::Write(ref w) => w.with_stdout(f), } } @@ -268,12 +268,12 @@ impl ShellOut { for<'r> F: FnOnce(&'r mut (dyn Write + 'r)) -> R, { match *self { - ShellOut::Stream => { + Self::Stream => { let stderr = io::stderr(); let mut handler = stderr.lock(); f(&mut handler) } - ShellOut::Write(ref w) => w.with_err(f), + Self::Write(ref w) => w.with_err(f), } } } @@ -293,16 +293,16 @@ pub enum Verbosity { impl Verbosity { /// Returns true if json mode pub fn is_json(&self) -> bool { - matches!(self, Verbosity::Json) + matches!(self, Self::Json) } /// Returns true if silent pub fn is_silent(&self) -> bool { - matches!(self, Verbosity::Silent) + matches!(self, Self::Silent) } /// Returns true if normal verbosity pub fn is_normal(&self) -> bool { - matches!(self, Verbosity::Normal) + matches!(self, Self::Normal) } } diff --git a/crates/common/src/term.rs b/crates/common/src/term.rs index 81aff615c..4057a504f 100644 --- a/crates/common/src/term.rs +++ b/crates/common/src/term.rs @@ -34,8 +34,8 @@ pub struct TermSettings { impl TermSettings { /// Returns a new [`TermSettings`], configured from the current environment. - pub fn from_env() -> TermSettings { - TermSettings { indicate_progress: std::io::stdout().is_terminal() } + pub fn from_env() -> Self { + Self { indicate_progress: std::io::stdout().is_terminal() } } } @@ -55,7 +55,7 @@ impl Spinner { } pub fn with_indicator(indicator: &'static [&'static str], msg: impl Into) -> Self { - Spinner { + Self { indicator, no_progress: !TERM_SETTINGS.indicate_progress, message: msg.into(), @@ -125,7 +125,7 @@ impl SpinnerReporter { }) .expect("failed to spawn thread"); - SpinnerReporter { sender } + Self { sender } } fn send_msg(&self, msg: impl Into) { diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index cc2cfc1b6..6d64e3625 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -1,6 +1,9 @@ +//! # foundry-config +//! //! Foundry configuration. -#![warn(missing_docs, unused_crate_dependencies)] +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #[macro_use] extern crate tracing; diff --git a/crates/debugger/Cargo.toml b/crates/debugger/Cargo.toml index bc3c746e3..812c6fdad 100644 --- a/crates/debugger/Cargo.toml +++ b/crates/debugger/Cargo.toml @@ -9,6 +9,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [dependencies] foundry-common.workspace = true foundry-compilers.workspace = true diff --git a/crates/debugger/src/lib.rs b/crates/debugger/src/lib.rs index 5683cb8a5..ed5da9342 100644 --- a/crates/debugger/src/lib.rs +++ b/crates/debugger/src/lib.rs @@ -2,7 +2,8 @@ //! //! Interactive Solidity TUI debugger. -#![warn(unused_crate_dependencies, unreachable_pub)] +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #[macro_use] extern crate tracing; diff --git a/crates/debugger/src/tui/context.rs b/crates/debugger/src/tui/context.rs index 58a8e57f4..22ea7d5d3 100644 --- a/crates/debugger/src/tui/context.rs +++ b/crates/debugger/src/tui/context.rs @@ -27,18 +27,18 @@ impl BufferKind { /// Helper to cycle through the active buffers. pub(crate) fn next(&self) -> Self { match self { - BufferKind::Memory => BufferKind::Calldata, - BufferKind::Calldata => BufferKind::Returndata, - BufferKind::Returndata => BufferKind::Memory, + Self::Memory => Self::Calldata, + Self::Calldata => Self::Returndata, + Self::Returndata => Self::Memory, } } /// Helper to format the title of the active buffer pane pub(crate) fn title(&self, size: usize) -> String { match self { - BufferKind::Memory => format!("Memory (max expansion: {} bytes)", size), - BufferKind::Calldata => format!("Calldata (size: {} bytes)", size), - BufferKind::Returndata => format!("Returndata (size: {} bytes)", size), + Self::Memory => format!("Memory (max expansion: {size} bytes)"), + Self::Calldata => format!("Calldata (size: {size} bytes)"), + Self::Returndata => format!("Returndata (size: {size} bytes)"), } } } diff --git a/crates/debugger/src/tui/draw.rs b/crates/debugger/src/tui/draw.rs index 9a33e6a05..8e55687a0 100644 --- a/crates/debugger/src/tui/draw.rs +++ b/crates/debugger/src/tui/draw.rs @@ -420,7 +420,7 @@ impl DebuggerContext<'_> { let params = OpcodeParam::of(step.instruction); - let text: Vec = stack + let text: Vec> = stack .iter() .rev() .enumerate() @@ -515,7 +515,7 @@ impl DebuggerContext<'_> { let height = area.height as usize; let end_line = self.draw_memory.current_buf_startline + height; - let text: Vec = buf + let text: Vec> = buf .chunks(32) .enumerate() .skip(self.draw_memory.current_buf_startline) diff --git a/crates/doc/Cargo.toml b/crates/doc/Cargo.toml index 1965befb8..baf914347 100644 --- a/crates/doc/Cargo.toml +++ b/crates/doc/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [dependencies] forge-fmt.workspace = true foundry-common.workspace = true diff --git a/crates/doc/src/document.rs b/crates/doc/src/document.rs index 3f1f2935c..be4c1e647 100644 --- a/crates/doc/src/document.rs +++ b/crates/doc/src/document.rs @@ -90,36 +90,34 @@ pub enum DocumentContent { impl DocumentContent { pub(crate) fn len(&self) -> usize { match self { - DocumentContent::Empty => 0, - DocumentContent::Single(_) => 1, - DocumentContent::Constants(items) => items.len(), - DocumentContent::OverloadedFunctions(items) => items.len(), + Self::Empty => 0, + Self::Single(_) => 1, + Self::Constants(items) => items.len(), + Self::OverloadedFunctions(items) => items.len(), } } pub(crate) fn get_mut(&mut self, index: usize) -> Option<&mut ParseItem> { match self { - DocumentContent::Empty => None, - DocumentContent::Single(item) => { + Self::Empty => None, + Self::Single(item) => { if index == 0 { Some(item) } else { None } } - DocumentContent::Constants(items) => items.get_mut(index), - DocumentContent::OverloadedFunctions(items) => items.get_mut(index), + Self::Constants(items) => items.get_mut(index), + Self::OverloadedFunctions(items) => items.get_mut(index), } } pub fn iter_items(&self) -> ParseItemIter<'_> { match self { - DocumentContent::Empty => ParseItemIter { next: None, other: None }, - DocumentContent::Single(item) => ParseItemIter { next: Some(item), other: None }, - DocumentContent::Constants(items) => { - ParseItemIter { next: None, other: Some(items.iter()) } - } - DocumentContent::OverloadedFunctions(items) => { + Self::Empty => ParseItemIter { next: None, other: None }, + Self::Single(item) => ParseItemIter { next: Some(item), other: None }, + Self::Constants(items) => ParseItemIter { next: None, other: Some(items.iter()) }, + Self::OverloadedFunctions(items) => { ParseItemIter { next: None, other: Some(items.iter()) } } } @@ -127,12 +125,12 @@ impl DocumentContent { pub fn iter_items_mut(&mut self) -> ParseItemIterMut<'_> { match self { - DocumentContent::Empty => ParseItemIterMut { next: None, other: None }, - DocumentContent::Single(item) => ParseItemIterMut { next: Some(item), other: None }, - DocumentContent::Constants(items) => { + Self::Empty => ParseItemIterMut { next: None, other: None }, + Self::Single(item) => ParseItemIterMut { next: Some(item), other: None }, + Self::Constants(items) => { ParseItemIterMut { next: None, other: Some(items.iter_mut()) } } - DocumentContent::OverloadedFunctions(items) => { + Self::OverloadedFunctions(items) => { ParseItemIterMut { next: None, other: Some(items.iter_mut()) } } } diff --git a/crates/doc/src/lib.rs b/crates/doc/src/lib.rs index 3f92be47a..fd8e4d3ba 100644 --- a/crates/doc/src/lib.rs +++ b/crates/doc/src/lib.rs @@ -1,13 +1,9 @@ //! The module for generating Solidity documentation. //! -//! See [DocBuilder] - -#![warn(missing_debug_implementations, missing_docs, unreachable_pub, unused_crate_dependencies)] -#![deny(unused_must_use, rust_2018_idioms)] -#![doc(test( - no_crate_inject, - attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables)) -))] +//! See [`DocBuilder`]. + +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #[macro_use] extern crate tracing; diff --git a/crates/doc/src/parser/comment.rs b/crates/doc/src/parser/comment.rs index b6e6d08af..55e69249d 100644 --- a/crates/doc/src/parser/comment.rs +++ b/crates/doc/src/parser/comment.rs @@ -28,20 +28,20 @@ impl CommentTag { fn from_str(s: &str) -> Option { let trimmed = s.trim(); let tag = match trimmed { - "title" => CommentTag::Title, - "author" => CommentTag::Author, - "notice" => CommentTag::Notice, - "dev" => CommentTag::Dev, - "param" => CommentTag::Param, - "return" => CommentTag::Return, - "inheritdoc" => CommentTag::Inheritdoc, + "title" => Self::Title, + "author" => Self::Author, + "notice" => Self::Notice, + "dev" => Self::Dev, + "param" => Self::Param, + "return" => Self::Return, + "inheritdoc" => Self::Inheritdoc, _ if trimmed.starts_with("custom:") => { // `@custom:param` tag will be parsed as `CommentTag::Param` due to a limitation // on specifying parameter docs for unnamed function arguments. let custom_tag = trimmed.trim_start_matches("custom:").trim(); match custom_tag { - "param" => CommentTag::Param, - _ => CommentTag::Custom(custom_tag.to_owned()), + "param" => Self::Param, + _ => Self::Custom(custom_tag.to_owned()), } } _ => { @@ -126,9 +126,9 @@ impl Comments { pub fn merge_inheritdoc( &self, ident: &str, - inheritdocs: Option>, - ) -> Comments { - let mut result = Comments(Vec::from_iter(self.iter().cloned())); + inheritdocs: Option>, + ) -> Self { + let mut result = Self(Vec::from_iter(self.iter().cloned())); if let (Some(inheritdocs), Some(base)) = (inheritdocs, self.find_inheritdoc_base()) { let key = format!("{base}.{ident}"); @@ -157,18 +157,18 @@ pub struct CommentsRef<'a>(Vec<&'a Comment>); impl<'a> CommentsRef<'a> { /// Filter a collection of comments and return only those that match a provided tag - pub fn include_tag(&self, tag: CommentTag) -> CommentsRef<'a> { + pub fn include_tag(&self, tag: CommentTag) -> Self { self.include_tags(&[tag]) } /// Filter a collection of comments and return only those that match provided tags - pub fn include_tags(&self, tags: &[CommentTag]) -> CommentsRef<'a> { + pub fn include_tags(&self, tags: &[CommentTag]) -> Self { // Cloning only references here CommentsRef(self.iter().cloned().filter(|c| tags.contains(&c.tag)).collect()) } /// Filter a collection of comments and return only those that do not match provided tags - pub fn exclude_tags(&self, tags: &[CommentTag]) -> CommentsRef<'a> { + pub fn exclude_tags(&self, tags: &[CommentTag]) -> Self { // Cloning only references here CommentsRef(self.iter().cloned().filter(|c| !tags.contains(&c.tag)).collect()) } diff --git a/crates/doc/src/parser/item.rs b/crates/doc/src/parser/item.rs index 60faf78b4..b93f0d199 100644 --- a/crates/doc/src/parser/item.rs +++ b/crates/doc/src/parser/item.rs @@ -75,7 +75,7 @@ impl ParseItem { } /// Set children on the [ParseItem]. - pub fn with_children(mut self, children: Vec) -> Self { + pub fn with_children(mut self, children: Vec) -> Self { self.children = children; self } @@ -171,16 +171,16 @@ impl ParseSource { /// Get the identity of the source pub fn ident(&self) -> String { match self { - ParseSource::Contract(contract) => contract.name.safe_unwrap().name.to_owned(), - ParseSource::Variable(var) => var.name.safe_unwrap().name.to_owned(), - ParseSource::Event(event) => event.name.safe_unwrap().name.to_owned(), - ParseSource::Error(error) => error.name.safe_unwrap().name.to_owned(), - ParseSource::Struct(structure) => structure.name.safe_unwrap().name.to_owned(), - ParseSource::Enum(enumerable) => enumerable.name.safe_unwrap().name.to_owned(), - ParseSource::Function(func) => { + Self::Contract(contract) => contract.name.safe_unwrap().name.to_owned(), + Self::Variable(var) => var.name.safe_unwrap().name.to_owned(), + Self::Event(event) => event.name.safe_unwrap().name.to_owned(), + Self::Error(error) => error.name.safe_unwrap().name.to_owned(), + Self::Struct(structure) => structure.name.safe_unwrap().name.to_owned(), + Self::Enum(enumerable) => enumerable.name.safe_unwrap().name.to_owned(), + Self::Function(func) => { func.name.as_ref().map_or(func.ty.to_string(), |n| n.name.to_owned()) } - ParseSource::Type(ty) => ty.name.name.to_owned(), + Self::Type(ty) => ty.name.name.to_owned(), } } } diff --git a/crates/doc/src/parser/mod.rs b/crates/doc/src/parser/mod.rs index 6f03a5fb2..aa493c5c6 100644 --- a/crates/doc/src/parser/mod.rs +++ b/crates/doc/src/parser/mod.rs @@ -52,7 +52,7 @@ struct ParserContext { impl Parser { /// Create a new instance of [Parser]. pub fn new(comments: Vec, source: String) -> Self { - Parser { comments, source, ..Default::default() } + Self { comments, source, ..Default::default() } } /// Set formatter config on the [Parser] diff --git a/crates/doc/src/preprocessor/infer_hyperlinks.rs b/crates/doc/src/preprocessor/infer_hyperlinks.rs index 865c4302f..a7f7c19c4 100644 --- a/crates/doc/src/preprocessor/infer_hyperlinks.rs +++ b/crates/doc/src/preprocessor/infer_hyperlinks.rs @@ -179,7 +179,7 @@ impl InferInlineHyperlinks { }; if let Some(target) = target { let display_value = link.markdown_link_display_value(); - let markdown_link = format!("[{}]({})", display_value, target); + let markdown_link = format!("[{display_value}]({target})"); // replace the link with the markdown link comment.value = comment.value.as_str().replacen(link.as_str(), markdown_link.as_str(), 1); diff --git a/crates/evm/core/Cargo.toml b/crates/evm/core/Cargo.toml index 589e13b25..c0b1b3e3d 100644 --- a/crates/evm/core/Cargo.toml +++ b/crates/evm/core/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [dependencies] foundry-cheatcodes-spec.workspace = true foundry-common.workspace = true diff --git a/crates/evm/core/src/backend/diagnostic.rs b/crates/evm/core/src/backend/diagnostic.rs index f4de9260a..109190a8f 100644 --- a/crates/evm/core/src/backend/diagnostic.rs +++ b/crates/evm/core/src/backend/diagnostic.rs @@ -26,7 +26,7 @@ impl RevertDiagnostic { |addr: &Address| labels.get(addr).cloned().unwrap_or_else(|| addr.to_string()); match self { - RevertDiagnostic::ContractExistsOnOtherForks { contract, active, available_on } => { + Self::ContractExistsOnOtherForks { contract, active, available_on } => { let contract_label = get_label(contract); format!( @@ -37,7 +37,7 @@ impl RevertDiagnostic { available_on.iter().format(", ") ) } - RevertDiagnostic::ContractDoesNotExist { contract, persistent, .. } => { + Self::ContractDoesNotExist { contract, persistent, .. } => { let contract_label = get_label(contract); if *persistent { format!("Contract {contract_label} does not exist") diff --git a/crates/evm/core/src/backend/error.rs b/crates/evm/core/src/backend/error.rs index f1f9733d4..2f40bd456 100644 --- a/crates/evm/core/src/backend/error.rs +++ b/crates/evm/core/src/backend/error.rs @@ -54,12 +54,12 @@ pub enum DatabaseError { impl DatabaseError { /// Create a new error with a message pub fn msg(msg: impl Into) -> Self { - DatabaseError::Message(msg.into()) + Self::Message(msg.into()) } /// Create a new error with a message pub fn display(msg: impl std::fmt::Display) -> Self { - DatabaseError::Message(msg.to_string()) + Self::Message(msg.to_string()) } fn get_rpc_error(&self) -> Option<&eyre::Error> { @@ -79,7 +79,7 @@ impl DatabaseError { Self::BlockNotFound(_) | Self::TransactionNotFound(_) | Self::MissingCreate2Deployer => None, - DatabaseError::Other(_) => None, + Self::Other(_) => None, } } @@ -96,7 +96,7 @@ impl DatabaseError { impl From for DatabaseError { fn from(value: tokio::task::JoinError) -> Self { - DatabaseError::display(value) + Self::display(value) } } @@ -113,11 +113,11 @@ impl From for DatabaseError { } // Note: this is mostly necessary to use some revm internals that return an [EVMError] -impl From> for DatabaseError { - fn from(err: EVMError) -> Self { +impl From> for DatabaseError { + fn from(err: EVMError) -> Self { match err { EVMError::Database(err) => err, - err => DatabaseError::Other(err.to_string()), + err => Self::Other(err.to_string()), } } } diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index 94a1eeead..bb43c7243 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -1233,7 +1233,7 @@ impl DatabaseExt for Backend { Ok(()) } - fn transact>( + fn transact>( &mut self, maybe_id: Option, transaction: B256, diff --git a/crates/evm/core/src/backend/snapshot.rs b/crates/evm/core/src/backend/snapshot.rs index 5fd0cfa77..f8961c7a0 100644 --- a/crates/evm/core/src/backend/snapshot.rs +++ b/crates/evm/core/src/backend/snapshot.rs @@ -56,6 +56,6 @@ pub enum RevertSnapshotAction { impl RevertSnapshotAction { /// Returns `true` if the action is to keep the snapshot pub fn is_keep(&self) -> bool { - matches!(self, RevertSnapshotAction::RevertKeep) + matches!(self, Self::RevertKeep) } } diff --git a/crates/evm/core/src/fork/cache.rs b/crates/evm/core/src/fork/cache.rs index ce13f69c1..fd768e3f7 100644 --- a/crates/evm/core/src/fork/cache.rs +++ b/crates/evm/core/src/fork/cache.rs @@ -132,11 +132,7 @@ impl BlockchainDbMeta { .and_then(|url| url.host().map(|host| host.to_string())) .unwrap_or(url); - BlockchainDbMeta { - cfg_env: env.cfg.clone(), - block_env: env.block, - hosts: BTreeSet::from([host]), - } + Self { cfg_env: env.cfg.clone(), block_env: env.block, hosts: BTreeSet::from([host]) } } } @@ -451,7 +447,7 @@ impl<'de> Deserialize<'de> for JsonBlockCacheData { let Data { meta, data: StateSnapshot { accounts, storage, block_hashes } } = Data::deserialize(deserializer)?; - Ok(JsonBlockCacheData { + Ok(Self { meta: Arc::new(RwLock::new(meta)), data: Arc::new(MemDb { accounts: RwLock::new(accounts), diff --git a/crates/evm/core/src/fork/multi.rs b/crates/evm/core/src/fork/multi.rs index bece8c4f5..79b30c0ce 100644 --- a/crates/evm/core/src/fork/multi.rs +++ b/crates/evm/core/src/fork/multi.rs @@ -41,7 +41,7 @@ impl ForkId { Some(n) => write!(id, "{n:#x}").unwrap(), None => id.push_str("latest"), } - ForkId(id) + Self(id) } /// Returns the identifier of the fork. diff --git a/crates/evm/core/src/lib.rs b/crates/evm/core/src/lib.rs index ef28ad922..c2c2e2006 100644 --- a/crates/evm/core/src/lib.rs +++ b/crates/evm/core/src/lib.rs @@ -2,7 +2,8 @@ //! //! Core EVM abstractions. -#![warn(unused_crate_dependencies)] +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] use auto_impl::auto_impl; use revm::{inspectors::NoOpInspector, interpreter::CreateInputs, Database, EvmContext, Inspector}; diff --git a/crates/evm/coverage/Cargo.toml b/crates/evm/coverage/Cargo.toml index 57a7a9f2b..6858159de 100644 --- a/crates/evm/coverage/Cargo.toml +++ b/crates/evm/coverage/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [dependencies] foundry-common.workspace = true foundry-compilers.workspace = true diff --git a/crates/evm/coverage/src/lib.rs b/crates/evm/coverage/src/lib.rs index 9496c0aca..58bfb9cdb 100644 --- a/crates/evm/coverage/src/lib.rs +++ b/crates/evm/coverage/src/lib.rs @@ -2,7 +2,8 @@ //! //! EVM bytecode coverage analysis. -#![warn(unreachable_pub, unused_crate_dependencies, rust_2018_idioms)] +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #[macro_use] extern crate tracing; @@ -155,7 +156,7 @@ impl CoverageReport { pub struct HitMaps(pub HashMap); impl HitMaps { - pub fn merge(&mut self, other: HitMaps) { + 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) { for (pc, hits) in extra_hits { @@ -166,7 +167,7 @@ impl HitMaps { } } - pub fn merged(mut self, other: HitMaps) -> Self { + pub fn merged(mut self, other: Self) -> Self { self.merge(other); self } @@ -206,14 +207,14 @@ impl HitMap { } /// Merge another hitmap into this, assuming the bytecode is consistent - pub fn merge(&mut self, other: &HitMap) -> Result<(), eyre::Report> { + pub fn merge(&mut self, other: &Self) -> Result<(), eyre::Report> { for (pc, hits) in &other.hits { *self.hits.entry(*pc).or_default() += hits; } Ok(()) } - pub fn consistent_bytecode(&self, hm1: &HitMap, hm2: &HitMap) -> bool { + pub fn consistent_bytecode(&self, hm1: &Self, hm2: &Self) -> bool { // Consider the bytecodes consistent if they are the same out as far as the // recorded hits let len1 = hm1.hits.last_key_value(); diff --git a/crates/evm/evm/Cargo.toml b/crates/evm/evm/Cargo.toml index 5168c35e0..e3a1c9c6b 100644 --- a/crates/evm/evm/Cargo.toml +++ b/crates/evm/evm/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [dependencies] foundry-cheatcodes.workspace = true foundry-common.workspace = true diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 69ef106f6..2474ed5aa 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -219,7 +219,7 @@ impl<'a> InvariantExecutor<'a> { U256::ZERO, ) .map_err(|e| { - TestCaseError::fail(format!("Could not make raw evm call: {}", e)) + TestCaseError::fail(format!("Could not make raw evm call: {e}")) })?; if call_result.result.as_ref() == MAGIC_ASSUME { diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 7e770f54f..ba1e5277c 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -101,7 +101,7 @@ impl Executor { }, ); - Executor { backend, env, inspector, gas_limit } + Self { backend, env, inspector, gas_limit } } /// Returns the spec id of the executor @@ -513,7 +513,7 @@ impl Executor { // Check if a DSTest assertion failed let executor = - Executor::new(backend, self.env.clone(), self.inspector.clone(), self.gas_limit); + Self::new(backend, self.env.clone(), self.inspector.clone(), self.gas_limit); let call = executor.call_sol(CALLER, address, &ITest::failedCall {}, U256::ZERO, None); if let Ok(CallResult { raw: _, decoded_result: ITest::failedReturn { _0: failed } }) = call @@ -610,13 +610,13 @@ pub enum EvmError { impl From for EvmError { fn from(err: ExecutionErr) -> Self { - EvmError::Execution(Box::new(err)) + Self::Execution(Box::new(err)) } } impl From for EvmError { fn from(err: alloy_sol_types::Error) -> Self { - EvmError::AbiError(err.into()) + Self::AbiError(err.into()) } } diff --git a/crates/evm/evm/src/lib.rs b/crates/evm/evm/src/lib.rs index a699b6bf8..598012770 100644 --- a/crates/evm/evm/src/lib.rs +++ b/crates/evm/evm/src/lib.rs @@ -2,7 +2,8 @@ //! //! Main Foundry EVM backend abstractions. -#![warn(unreachable_pub, unused_crate_dependencies, rust_2018_idioms)] +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #[macro_use] extern crate tracing; diff --git a/crates/evm/fuzz/Cargo.toml b/crates/evm/fuzz/Cargo.toml index 5fd6db65d..87d5d9ab3 100644 --- a/crates/evm/fuzz/Cargo.toml +++ b/crates/evm/fuzz/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [dependencies] foundry-common.workspace = true foundry-compilers.workspace = true diff --git a/crates/evm/fuzz/src/invariant/call_override.rs b/crates/evm/fuzz/src/invariant/call_override.rs index a98c50002..a65b0abc9 100644 --- a/crates/evm/fuzz/src/invariant/call_override.rs +++ b/crates/evm/fuzz/src/invariant/call_override.rs @@ -38,7 +38,7 @@ impl RandomCallGenerator { ) -> Self { let strategy = weighted(0.9, strategy).sboxed(); - RandomCallGenerator { + Self { test_address, runner: Arc::new(Mutex::new(runner)), strategy, diff --git a/crates/evm/fuzz/src/invariant/filters.rs b/crates/evm/fuzz/src/invariant/filters.rs index 2925c6e45..d0ebb33e5 100644 --- a/crates/evm/fuzz/src/invariant/filters.rs +++ b/crates/evm/fuzz/src/invariant/filters.rs @@ -69,6 +69,6 @@ impl SenderFilters { excluded.push(addr_0); } targeted.retain(|addr| !excluded.contains(addr)); - SenderFilters { targeted, excluded } + Self { targeted, excluded } } } diff --git a/crates/evm/fuzz/src/lib.rs b/crates/evm/fuzz/src/lib.rs index c6b5e0049..7da74601f 100644 --- a/crates/evm/fuzz/src/lib.rs +++ b/crates/evm/fuzz/src/lib.rs @@ -2,7 +2,8 @@ //! //! EVM fuzzing implementation using [`proptest`]. -#![warn(unreachable_pub, unused_crate_dependencies, rust_2018_idioms)] +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #[macro_use] extern crate tracing; @@ -67,7 +68,7 @@ impl BaseCounterExample { if let Some(func) = abi.functions().find(|f| f.selector() == bytes[..4]) { // skip the function selector when decoding if let Ok(args) = func.abi_decode_input(&bytes[4..], false) { - return BaseCounterExample { + return Self { sender: Some(sender), addr: Some(addr), calldata: bytes.clone(), @@ -82,7 +83,7 @@ impl BaseCounterExample { } } - BaseCounterExample { + Self { sender: Some(sender), addr: Some(addr), calldata: bytes.clone(), @@ -99,7 +100,7 @@ impl BaseCounterExample { args: Vec, traces: Option, ) -> Self { - BaseCounterExample { + Self { sender: None, addr: None, calldata: bytes, @@ -132,7 +133,7 @@ impl fmt::Display for BaseCounterExample { } if let Some(args) = &self.args { - write!(f, " args=[{}]", args) + write!(f, " args=[{args}]") } else { write!(f, " args=[]") } @@ -308,7 +309,7 @@ pub struct FuzzFixtures { } impl FuzzFixtures { - pub fn new(fixtures: HashMap) -> FuzzFixtures { + pub fn new(fixtures: HashMap) -> Self { Self { inner: Arc::new(fixtures) } } diff --git a/crates/evm/fuzz/src/strategies/int.rs b/crates/evm/fuzz/src/strategies/int.rs index fc8f88ed6..3f8ff548b 100644 --- a/crates/evm/fuzz/src/strategies/int.rs +++ b/crates/evm/fuzz/src/strategies/int.rs @@ -56,7 +56,7 @@ impl ValueTree for IntValueTree { } fn simplify(&mut self) -> bool { - if self.fixed || !IntValueTree::magnitude_greater(self.hi, self.lo) { + if self.fixed || !Self::magnitude_greater(self.hi, self.lo) { return false } self.hi = self.curr; @@ -64,7 +64,7 @@ impl ValueTree for IntValueTree { } fn complicate(&mut self) -> bool { - if self.fixed || !IntValueTree::magnitude_greater(self.hi, self.lo) { + if self.fixed || !Self::magnitude_greater(self.hi, self.lo) { return false } diff --git a/crates/evm/fuzz/src/strategies/state.rs b/crates/evm/fuzz/src/strategies/state.rs index 6ff36f8e4..3e4628d9a 100644 --- a/crates/evm/fuzz/src/strategies/state.rs +++ b/crates/evm/fuzz/src/strategies/state.rs @@ -33,7 +33,7 @@ pub struct EvmFuzzState { } impl EvmFuzzState { - pub fn new(db: &CacheDB, config: FuzzDictionaryConfig) -> EvmFuzzState { + pub fn new(db: &CacheDB, config: FuzzDictionaryConfig) -> Self { // Sort accounts to ensure deterministic dictionary generation from the same setUp state. let mut accs = db.accounts.iter().collect::>(); accs.sort_by_key(|(address, _)| *address); diff --git a/crates/evm/traces/Cargo.toml b/crates/evm/traces/Cargo.toml index 0d6192aef..d6b13e3cc 100644 --- a/crates/evm/traces/Cargo.toml +++ b/crates/evm/traces/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [dependencies] foundry-block-explorers.workspace = true foundry-common.workspace = true diff --git a/crates/evm/traces/src/decoder/mod.rs b/crates/evm/traces/src/decoder/mod.rs index 73bf52bdd..75ea3d260 100644 --- a/crates/evm/traces/src/decoder/mod.rs +++ b/crates/evm/traces/src/decoder/mod.rs @@ -698,13 +698,13 @@ mod tests { for (function_signature, data, expected) in cheatcode_input_test_cases { let function = Function::parse(function_signature).unwrap(); let result = decoder.decode_cheatcode_inputs(&function, &data); - assert_eq!(result, expected, "Input case failed for: {}", function_signature); + assert_eq!(result, expected, "Input case failed for: {function_signature}"); } for (function_signature, expected) in cheatcode_output_test_cases { let function = Function::parse(function_signature).unwrap(); let result = Some(decoder.decode_cheatcode_outputs(&function).unwrap_or_default()); - assert_eq!(result, expected, "Output case failed for: {}", function_signature); + assert_eq!(result, expected, "Output case failed for: {function_signature}"); } } } diff --git a/crates/evm/traces/src/lib.rs b/crates/evm/traces/src/lib.rs index 46ba27891..052be9c05 100644 --- a/crates/evm/traces/src/lib.rs +++ b/crates/evm/traces/src/lib.rs @@ -2,7 +2,8 @@ //! //! EVM trace identifying and decoding. -#![warn(unreachable_pub, unused_crate_dependencies, rust_2018_idioms)] +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #[macro_use] extern crate tracing; @@ -86,7 +87,7 @@ pub async fn render_trace_arena( // Display trace header let (trace, return_data) = render_trace(&node.trace, decoder).await?; - writeln!(s, "{left}{}", trace)?; + writeln!(s, "{left}{trace}")?; // Display logs and subcalls let left_prefix = format!("{child}{BRANCH}"); diff --git a/crates/fmt/Cargo.toml b/crates/fmt/Cargo.toml index 8fbf962eb..835b2e8b7 100644 --- a/crates/fmt/Cargo.toml +++ b/crates/fmt/Cargo.toml @@ -9,6 +9,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [dependencies] foundry-config.workspace = true diff --git a/crates/fmt/src/buffer.rs b/crates/fmt/src/buffer.rs index 11c0838ec..8d62a70f9 100644 --- a/crates/fmt/src/buffer.rs +++ b/crates/fmt/src/buffer.rs @@ -22,16 +22,16 @@ enum WriteState { impl WriteState { fn comment_state(&self) -> CommentState { match self { - WriteState::LineStart(state) => *state, - WriteState::WriteTokens(state) => *state, - WriteState::WriteString(_) => CommentState::None, + Self::LineStart(state) => *state, + Self::WriteTokens(state) => *state, + Self::WriteString(_) => CommentState::None, } } } impl Default for WriteState { fn default() -> Self { - WriteState::LineStart(CommentState::default()) + Self::LineStart(CommentState::default()) } } diff --git a/crates/fmt/src/chunk.rs b/crates/fmt/src/chunk.rs index badce88db..7d9ce25c7 100644 --- a/crates/fmt/src/chunk.rs +++ b/crates/fmt/src/chunk.rs @@ -12,13 +12,13 @@ pub struct Chunk { impl From for Chunk { fn from(string: String) -> Self { - Chunk { content: string, ..Default::default() } + Self { content: string, ..Default::default() } } } impl From<&str> for Chunk { fn from(string: &str) -> Self { - Chunk { content: string.to_owned(), ..Default::default() } + Self { content: string.to_owned(), ..Default::default() } } } @@ -37,7 +37,7 @@ impl SurroundingChunk { before: Option, next: Option, ) -> Self { - SurroundingChunk { before, next, content: format!("{content}"), spaced: None } + Self { before, next, content: format!("{content}"), spaced: None } } pub fn spaced(mut self) -> Self { diff --git a/crates/fmt/src/comments.rs b/crates/fmt/src/comments.rs index 03f4e4181..5b9d7fb27 100644 --- a/crates/fmt/src/comments.rs +++ b/crates/fmt/src/comments.rs @@ -72,11 +72,7 @@ impl CommentWithMetadata { } /// Construct a comment with metadata by analyzing its surrounding source code - fn from_comment_and_src( - comment: Comment, - src: &str, - last_comment: Option<&CommentWithMetadata>, - ) -> Self { + fn from_comment_and_src(comment: Comment, src: &str, last_comment: Option<&Self>) -> Self { let src_before = &src[..comment.loc().start()]; if src_before.is_empty() { return Self::new(comment, CommentPosition::Prefix, false, 0) @@ -429,10 +425,10 @@ impl<'a> Iterator for NonCommentChars<'a> { /// Helpers for iterating over comment containing strings pub trait CommentStringExt { - fn comment_state_char_indices(&self) -> CommentStateCharIndices; + fn comment_state_char_indices(&self) -> CommentStateCharIndices<'_>; #[inline] - fn non_comment_chars(&self) -> NonCommentChars { + fn non_comment_chars(&self) -> NonCommentChars<'_> { NonCommentChars(self.comment_state_char_indices()) } @@ -447,14 +443,14 @@ where T: AsRef, { #[inline] - fn comment_state_char_indices(&self) -> CommentStateCharIndices { + fn comment_state_char_indices(&self) -> CommentStateCharIndices<'_> { CommentStateCharIndices::new(self.as_ref()) } } impl CommentStringExt for str { #[inline] - fn comment_state_char_indices(&self) -> CommentStateCharIndices { + fn comment_state_char_indices(&self) -> CommentStateCharIndices<'_> { CommentStateCharIndices::new(self) } } diff --git a/crates/fmt/src/helpers.rs b/crates/fmt/src/helpers.rs index 9a31edeb0..1bed8069a 100644 --- a/crates/fmt/src/helpers.rs +++ b/crates/fmt/src/helpers.rs @@ -23,7 +23,7 @@ pub struct Parsed<'a> { } /// Parse source code -pub fn parse(src: &str) -> Result> { +pub fn parse(src: &str) -> Result, Vec> { let (pt, comments) = solang_parser::parse(src, 0)?; let comments = Comments::new(comments, src); let (inline_config_items, invalid_inline_config_items): (Vec<_>, Vec<_>) = @@ -35,7 +35,7 @@ pub fn parse(src: &str) -> Result> { /// Format parsed code pub fn format_to( writer: W, - mut parsed: Parsed, + mut parsed: Parsed<'_>, config: FormatterConfig, ) -> Result<(), FormatterError> { trace!(?parsed, ?config, "Formatting"); diff --git a/crates/fmt/src/inline_config.rs b/crates/fmt/src/inline_config.rs index 702669fe5..7593bb92c 100644 --- a/crates/fmt/src/inline_config.rs +++ b/crates/fmt/src/inline_config.rs @@ -23,11 +23,11 @@ impl FromStr for InlineConfigItem { type Err = InvalidInlineConfigItem; fn from_str(s: &str) -> Result { Ok(match s { - "disable-next-item" => InlineConfigItem::DisableNextItem, - "disable-line" => InlineConfigItem::DisableLine, - "disable-next-line" => InlineConfigItem::DisableNextLine, - "disable-start" => InlineConfigItem::DisableStart, - "disable-end" => InlineConfigItem::DisableEnd, + "disable-next-item" => Self::DisableNextItem, + "disable-line" => Self::DisableLine, + "disable-next-line" => Self::DisableNextLine, + "disable-start" => Self::DisableStart, + "disable-end" => Self::DisableEnd, s => return Err(InvalidInlineConfigItem(s.into())), }) } diff --git a/crates/fmt/src/lib.rs b/crates/fmt/src/lib.rs index 958d74647..4d5fe17a7 100644 --- a/crates/fmt/src/lib.rs +++ b/crates/fmt/src/lib.rs @@ -1,5 +1,6 @@ #![doc = include_str!("../README.md")] #![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #[macro_use] extern crate tracing; diff --git a/crates/fmt/src/solang_ext/ast_eq.rs b/crates/fmt/src/solang_ext/ast_eq.rs index e31fc2b41..2640008e2 100644 --- a/crates/fmt/src/solang_ext/ast_eq.rs +++ b/crates/fmt/src/solang_ext/ast_eq.rs @@ -225,13 +225,13 @@ macro_rules! wrap_in_box { impl AstEq for Statement { fn ast_eq(&self, other: &Self) -> bool { match self { - Statement::If(loc, expr, stmt1, stmt2) => { + Self::If(loc, expr, stmt1, stmt2) => { #[allow(clippy::borrowed_box)] - let wrap_if = |stmt1: &Box, stmt2: &Option>| { + let wrap_if = |stmt1: &Box, stmt2: &Option>| { ( wrap_in_box!(stmt1, *loc), stmt2.as_ref().map(|stmt2| { - if matches!(**stmt2, Statement::If(..)) { + if matches!(**stmt2, Self::If(..)) { stmt2.clone() } else { wrap_in_box!(stmt2, *loc) @@ -241,7 +241,7 @@ impl AstEq for Statement { }; let (stmt1, stmt2) = wrap_if(stmt1, stmt2); let left = (loc, expr, &stmt1, &stmt2); - if let Statement::If(loc, expr, stmt1, stmt2) = other { + if let Self::If(loc, expr, stmt1, stmt2) = other { let (stmt1, stmt2) = wrap_if(stmt1, stmt2); let right = (loc, expr, &stmt1, &stmt2); left.ast_eq(&right) @@ -249,10 +249,10 @@ impl AstEq for Statement { false } } - Statement::While(loc, expr, stmt1) => { + Self::While(loc, expr, stmt1) => { let stmt1 = wrap_in_box!(stmt1, *loc); let left = (loc, expr, &stmt1); - if let Statement::While(loc, expr, stmt1) = other { + if let Self::While(loc, expr, stmt1) = other { let stmt1 = wrap_in_box!(stmt1, *loc); let right = (loc, expr, &stmt1); left.ast_eq(&right) @@ -260,10 +260,10 @@ impl AstEq for Statement { false } } - Statement::DoWhile(loc, stmt1, expr) => { + Self::DoWhile(loc, stmt1, expr) => { let stmt1 = wrap_in_box!(stmt1, *loc); let left = (loc, &stmt1, expr); - if let Statement::DoWhile(loc, stmt1, expr) = other { + if let Self::DoWhile(loc, stmt1, expr) = other { let stmt1 = wrap_in_box!(stmt1, *loc); let right = (loc, &stmt1, expr); left.ast_eq(&right) @@ -271,10 +271,10 @@ impl AstEq for Statement { false } } - Statement::For(loc, stmt1, expr, stmt2, stmt3) => { + Self::For(loc, stmt1, expr, stmt2, stmt3) => { let stmt3 = stmt3.as_ref().map(|stmt3| wrap_in_box!(stmt3, *loc)); let left = (loc, stmt1, expr, stmt2, &stmt3); - if let Statement::For(loc, stmt1, expr, stmt2, stmt3) = other { + if let Self::For(loc, stmt1, expr, stmt2, stmt3) = other { let stmt3 = stmt3.as_ref().map(|stmt3| wrap_in_box!(stmt3, *loc)); let right = (loc, stmt1, expr, stmt2, &stmt3); left.ast_eq(&right) @@ -282,11 +282,11 @@ impl AstEq for Statement { false } } - Statement::Try(loc, expr, returns, catch) => { + Self::Try(loc, expr, returns, catch) => { let left_returns = returns.as_ref().map(|(params, stmt)| (filter_params(params), stmt)); let left = (loc, expr, left_returns, catch); - if let Statement::Try(loc, expr, returns, catch) = other { + if let Self::Try(loc, expr, returns, catch) = other { let right_returns = returns.as_ref().map(|(params, stmt)| (filter_params(params), stmt)); let right = (loc, expr, right_returns, catch); diff --git a/crates/fmt/src/string.rs b/crates/fmt/src/string.rs index 082d25d52..1dbc2f2f6 100644 --- a/crates/fmt/src/string.rs +++ b/crates/fmt/src/string.rs @@ -100,12 +100,14 @@ impl<'a> Iterator for QuotedRanges<'a> { /// Helpers for iterating over quoted strings pub trait QuotedStringExt { - /// Get an iterator of characters, indices and their quoted string state - fn quote_state_char_indices(&self) -> QuoteStateCharIndices; - /// Get an iterator of quoted string ranges - fn quoted_ranges(&self) -> QuotedRanges { + /// Returns an iterator of characters, indices and their quoted string state. + fn quote_state_char_indices(&self) -> QuoteStateCharIndices<'_>; + + /// Returns an iterator of quoted string ranges. + fn quoted_ranges(&self) -> QuotedRanges<'_> { QuotedRanges(self.quote_state_char_indices()) } + /// Check to see if a string is quoted. This will return true if the first character /// is a quote and the last character is a quote with no non-quoted sections in between. fn is_quoted(&self) -> bool { @@ -126,13 +128,13 @@ impl QuotedStringExt for T where T: AsRef, { - fn quote_state_char_indices(&self) -> QuoteStateCharIndices { + fn quote_state_char_indices(&self) -> QuoteStateCharIndices<'_> { QuoteStateCharIndices::new(self.as_ref()) } } impl QuotedStringExt for str { - fn quote_state_char_indices(&self) -> QuoteStateCharIndices { + fn quote_state_char_indices(&self) -> QuoteStateCharIndices<'_> { QuoteStateCharIndices::new(self) } } diff --git a/crates/fmt/src/visit.rs b/crates/fmt/src/visit.rs index da7f3ca37..ef9273a30 100644 --- a/crates/fmt/src/visit.rs +++ b/crates/fmt/src/visit.rs @@ -456,19 +456,19 @@ impl Visitable for SourceUnitPart { V: Visitor, { match self { - SourceUnitPart::ContractDefinition(contract) => v.visit_contract(contract), - SourceUnitPart::PragmaDirective(loc, ident, str) => v.visit_pragma(*loc, ident, str), - SourceUnitPart::ImportDirective(import) => import.visit(v), - SourceUnitPart::EnumDefinition(enumeration) => v.visit_enum(enumeration), - SourceUnitPart::StructDefinition(structure) => v.visit_struct(structure), - SourceUnitPart::EventDefinition(event) => v.visit_event(event), - SourceUnitPart::ErrorDefinition(error) => v.visit_error(error), - SourceUnitPart::FunctionDefinition(function) => v.visit_function(function), - SourceUnitPart::VariableDefinition(variable) => v.visit_var_definition(variable), - SourceUnitPart::TypeDefinition(def) => v.visit_type_definition(def), - SourceUnitPart::StraySemicolon(_) => v.visit_stray_semicolon(), - SourceUnitPart::Using(using) => v.visit_using(using), - SourceUnitPart::Annotation(annotation) => v.visit_annotation(annotation), + Self::ContractDefinition(contract) => v.visit_contract(contract), + Self::PragmaDirective(loc, ident, str) => v.visit_pragma(*loc, ident, str), + Self::ImportDirective(import) => import.visit(v), + Self::EnumDefinition(enumeration) => v.visit_enum(enumeration), + Self::StructDefinition(structure) => v.visit_struct(structure), + Self::EventDefinition(event) => v.visit_event(event), + Self::ErrorDefinition(error) => v.visit_error(error), + Self::FunctionDefinition(function) => v.visit_function(function), + Self::VariableDefinition(variable) => v.visit_var_definition(variable), + Self::TypeDefinition(def) => v.visit_type_definition(def), + Self::StraySemicolon(_) => v.visit_stray_semicolon(), + Self::Using(using) => v.visit_using(using), + Self::Annotation(annotation) => v.visit_annotation(annotation), } } } @@ -479,11 +479,11 @@ impl Visitable for Import { V: Visitor, { match self { - Import::Plain(import, loc) => v.visit_import_plain(*loc, import), - Import::GlobalSymbol(global, import_as, loc) => { + Self::Plain(import, loc) => v.visit_import_plain(*loc, import), + Self::GlobalSymbol(global, import_as, loc) => { v.visit_import_global(*loc, global, import_as) } - Import::Rename(from, imports, loc) => v.visit_import_renames(*loc, imports, from), + Self::Rename(from, imports, loc) => v.visit_import_renames(*loc, imports, from), } } } @@ -494,16 +494,16 @@ impl Visitable for ContractPart { V: Visitor, { match self { - ContractPart::StructDefinition(structure) => v.visit_struct(structure), - ContractPart::EventDefinition(event) => v.visit_event(event), - ContractPart::ErrorDefinition(error) => v.visit_error(error), - ContractPart::EnumDefinition(enumeration) => v.visit_enum(enumeration), - ContractPart::VariableDefinition(variable) => v.visit_var_definition(variable), - ContractPart::FunctionDefinition(function) => v.visit_function(function), - ContractPart::TypeDefinition(def) => v.visit_type_definition(def), - ContractPart::StraySemicolon(_) => v.visit_stray_semicolon(), - ContractPart::Using(using) => v.visit_using(using), - ContractPart::Annotation(annotation) => v.visit_annotation(annotation), + Self::StructDefinition(structure) => v.visit_struct(structure), + Self::EventDefinition(event) => v.visit_event(event), + Self::ErrorDefinition(error) => v.visit_error(error), + Self::EnumDefinition(enumeration) => v.visit_enum(enumeration), + Self::VariableDefinition(variable) => v.visit_var_definition(variable), + Self::FunctionDefinition(function) => v.visit_function(function), + Self::TypeDefinition(def) => v.visit_type_definition(def), + Self::StraySemicolon(_) => v.visit_stray_semicolon(), + Self::Using(using) => v.visit_using(using), + Self::Annotation(annotation) => v.visit_annotation(annotation), } } } @@ -514,40 +514,34 @@ impl Visitable for Statement { V: Visitor, { match self { - Statement::Block { loc, unchecked, statements } => { + Self::Block { loc, unchecked, statements } => { v.visit_block(*loc, *unchecked, statements) } - Statement::Assembly { loc, dialect, block, flags } => { + Self::Assembly { loc, dialect, block, flags } => { v.visit_assembly(*loc, dialect, block, flags) } - Statement::Args(loc, args) => v.visit_args(*loc, args), - Statement::If(loc, cond, if_branch, else_branch) => { + Self::Args(loc, args) => v.visit_args(*loc, args), + Self::If(loc, cond, if_branch, else_branch) => { v.visit_if(*loc, cond, if_branch, else_branch, true) } - Statement::While(loc, cond, body) => v.visit_while(*loc, cond, body), - Statement::Expression(loc, expr) => { + Self::While(loc, cond, body) => v.visit_while(*loc, cond, body), + Self::Expression(loc, expr) => { v.visit_expr(*loc, expr)?; v.visit_stray_semicolon() } - Statement::VariableDefinition(loc, declaration, expr) => { + Self::VariableDefinition(loc, declaration, expr) => { v.visit_var_definition_stmt(*loc, declaration, expr) } - Statement::For(loc, init, cond, update, body) => { - v.visit_for(*loc, init, cond, update, body) - } - Statement::DoWhile(loc, body, cond) => v.visit_do_while(*loc, body, cond), - Statement::Continue(loc) => v.visit_continue(*loc, true), - Statement::Break(loc) => v.visit_break(*loc, true), - Statement::Return(loc, expr) => v.visit_return(*loc, expr), - Statement::Revert(loc, error, args) => v.visit_revert(*loc, error, args), - Statement::RevertNamedArgs(loc, error, args) => { - v.visit_revert_named_args(*loc, error, args) - } - Statement::Emit(loc, event) => v.visit_emit(*loc, event), - Statement::Try(loc, expr, returns, clauses) => { - v.visit_try(*loc, expr, returns, clauses) - } - Statement::Error(loc) => v.visit_parser_error(*loc), + Self::For(loc, init, cond, update, body) => v.visit_for(*loc, init, cond, update, body), + Self::DoWhile(loc, body, cond) => v.visit_do_while(*loc, body, cond), + Self::Continue(loc) => v.visit_continue(*loc, true), + Self::Break(loc) => v.visit_break(*loc, true), + Self::Return(loc, expr) => v.visit_return(*loc, expr), + Self::Revert(loc, error, args) => v.visit_revert(*loc, error, args), + Self::RevertNamedArgs(loc, error, args) => v.visit_revert_named_args(*loc, error, args), + Self::Emit(loc, event) => v.visit_emit(*loc, event), + Self::Try(loc, expr, returns, clauses) => v.visit_try(*loc, expr, returns, clauses), + Self::Error(loc) => v.visit_parser_error(*loc), } } } @@ -603,24 +597,20 @@ impl Visitable for YulStatement { V: Visitor, { match self { - YulStatement::Assign(loc, exprs, expr) => { - v.visit_yul_assignment(*loc, exprs, &mut Some(expr)) - } - YulStatement::Block(block) => { - v.visit_yul_block(block.loc, block.statements.as_mut(), false) - } - YulStatement::Break(loc) => v.visit_break(*loc, false), - YulStatement::Continue(loc) => v.visit_continue(*loc, false), - YulStatement::For(stmt) => v.visit_yul_for(stmt), - YulStatement::FunctionCall(stmt) => v.visit_yul_function_call(stmt), - YulStatement::FunctionDefinition(stmt) => v.visit_yul_fun_def(stmt), - YulStatement::If(loc, expr, block) => v.visit_yul_if(*loc, expr, block), - YulStatement::Leave(loc) => v.visit_yul_leave(*loc), - YulStatement::Switch(stmt) => v.visit_yul_switch(stmt), - YulStatement::VariableDeclaration(loc, idents, expr) => { + Self::Assign(loc, exprs, expr) => v.visit_yul_assignment(*loc, exprs, &mut Some(expr)), + Self::Block(block) => v.visit_yul_block(block.loc, block.statements.as_mut(), false), + Self::Break(loc) => v.visit_break(*loc, false), + Self::Continue(loc) => v.visit_continue(*loc, false), + Self::For(stmt) => v.visit_yul_for(stmt), + Self::FunctionCall(stmt) => v.visit_yul_function_call(stmt), + Self::FunctionDefinition(stmt) => v.visit_yul_fun_def(stmt), + Self::If(loc, expr, block) => v.visit_yul_if(*loc, expr, block), + Self::Leave(loc) => v.visit_yul_leave(*loc), + Self::Switch(stmt) => v.visit_yul_switch(stmt), + Self::VariableDeclaration(loc, idents, expr) => { v.visit_yul_var_declaration(*loc, idents, expr) } - YulStatement::Error(loc) => v.visit_parser_error(*loc), + Self::Error(loc) => v.visit_parser_error(*loc), } } } diff --git a/crates/fmt/tests/formatter.rs b/crates/fmt/tests/formatter.rs index efbd63ef5..ba1d9216d 100644 --- a/crates/fmt/tests/formatter.rs +++ b/crates/fmt/tests/formatter.rs @@ -94,7 +94,7 @@ fn test_formatter( struct PrettyString(String); impl PartialEq for PrettyString { - fn eq(&self, other: &PrettyString) -> bool { + fn eq(&self, other: &Self) -> bool { self.0.lines().eq(other.0.lines()) } } diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 940acb087..eec949610 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [[bin]] name = "forge" path = "bin/main.rs" diff --git a/crates/forge/README.md b/crates/forge/README.md index 5ac9d5747..d4cfeede2 100644 --- a/crates/forge/README.md +++ b/crates/forge/README.md @@ -386,7 +386,7 @@ Logs: If you are working in a repo with NPM-style imports, like -``` +```solidity import "@openzeppelin/contracts/access/Ownable.sol"; ``` @@ -398,7 +398,7 @@ For example, if you have `@openzeppelin` imports, you would 2. Create a remappings file: `touch remappings.txt` 3. Add this line to `remappings.txt` -``` +```text @openzeppelin/=lib/openzeppelin-contracts/ ``` diff --git a/crates/forge/bin/cmd/bind.rs b/crates/forge/bin/cmd/bind.rs index 3643642e7..b4cd5ede8 100644 --- a/crates/forge/bin/cmd/bind.rs +++ b/crates/forge/bin/cmd/bind.rs @@ -191,7 +191,7 @@ impl BindArgs { .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)); + .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 { diff --git a/crates/forge/bin/cmd/cache.rs b/crates/forge/bin/cmd/cache.rs index ff3117d34..1adaf4d26 100644 --- a/crates/forge/bin/cmd/cache.rs +++ b/crates/forge/bin/cmd/cache.rs @@ -55,7 +55,7 @@ pub struct CleanArgs { impl CleanArgs { pub fn run(self) -> Result<()> { - let CleanArgs { chains, blocks, etherscan } = self; + let Self { chains, blocks, etherscan } = self; for chain_or_all in chains { match chain_or_all { @@ -91,7 +91,7 @@ pub struct LsArgs { impl LsArgs { pub fn run(self) -> Result<()> { - let LsArgs { chains } = self; + let Self { chains } = self; let mut cache = Cache::default(); for chain_or_all in chains { match chain_or_all { @@ -117,9 +117,9 @@ impl FromStr for ChainOrAll { fn from_str(s: &str) -> Result { if let Ok(chain) = NamedChain::from_str(s) { - Ok(ChainOrAll::NamedChain(chain)) + Ok(Self::NamedChain(chain)) } else if s == "all" { - Ok(ChainOrAll::All) + Ok(Self::All) } else { Err(format!("Expected known chain or all, found: {s}")) } @@ -150,7 +150,7 @@ pub struct ChainOrAllValueParser { impl Default for ChainOrAllValueParser { fn default() -> Self { - ChainOrAllValueParser { inner: possible_chains() } + Self { inner: possible_chains() } } } diff --git a/crates/forge/bin/cmd/clone.rs b/crates/forge/bin/cmd/clone.rs index 61e4d637f..822f595fb 100644 --- a/crates/forge/bin/cmd/clone.rs +++ b/crates/forge/bin/cmd/clone.rs @@ -88,14 +88,8 @@ pub struct CloneArgs { impl CloneArgs { pub async fn run(self) -> Result<()> { - let CloneArgs { - address, - root, - opts, - etherscan, - no_remappings_txt, - keep_directory_structure, - } = self; + let Self { address, root, opts, etherscan, no_remappings_txt, keep_directory_structure } = + self; // step 0. get the chain and api key from the config let config = Config::from(ðerscan); @@ -131,7 +125,7 @@ impl CloneArgs { if !opts.no_commit { let git = Git::new(&root).quiet(opts.quiet); git.add(Some("--all"))?; - let msg = format!("chore: forge clone {}", address); + let msg = format!("chore: forge clone {address}"); git.commit(&msg)?; } @@ -619,7 +613,7 @@ mod tests { use std::collections::BTreeMap; fn assert_successful_compilation(root: &PathBuf) -> ProjectCompileOutput { - println!("project_root: {:#?}", root); + println!("project_root: {root:#?}"); compile_project(root, false).expect("compilation failure") } diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index 2fcfbb1dc..b6a549742 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -146,7 +146,7 @@ impl CoverageArgs { // Collect source files. let project_paths = &project.paths; - let mut versioned_sources = HashMap::::new(); + let mut versioned_sources = HashMap::>::new(); for (path, source_file, version) in output.output().sources.sources_with_version() { report.add_source(version.clone(), source_file.id as usize, path.clone()); diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index 6ef37cf10..3b0bc12bc 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -394,7 +394,7 @@ where B: Clone, { fn clone(&self) -> Self { - ContractDeploymentTx { deployer: self.deployer.clone(), _contract: self._contract } + Self { deployer: self.deployer.clone(), _contract: self._contract } } } @@ -422,7 +422,7 @@ where B: Clone, { fn clone(&self) -> Self { - Deployer { + Self { tx: self.tx.clone(), abi: self.abi.clone(), client: self.client.clone(), @@ -512,7 +512,7 @@ where B: Clone, { fn clone(&self) -> Self { - DeploymentTxFactory { + Self { client: self.client.clone(), abi: self.abi.clone(), bytecode: self.bytecode.clone(), diff --git a/crates/forge/bin/cmd/flatten.rs b/crates/forge/bin/cmd/flatten.rs index 0c9c6f23a..3cd5abb68 100644 --- a/crates/forge/bin/cmd/flatten.rs +++ b/crates/forge/bin/cmd/flatten.rs @@ -32,7 +32,7 @@ pub struct FlattenArgs { impl FlattenArgs { pub fn run(self) -> Result<()> { - let FlattenArgs { target_path, output, project_paths } = self; + let Self { target_path, output, project_paths } = self; // flatten is a subset of `BuildArgs` so we can reuse that to get the config let build_args = CoreBuildArgs { project_paths, ..Default::default() }; diff --git a/crates/forge/bin/cmd/generate/mod.rs b/crates/forge/bin/cmd/generate/mod.rs index fd40a78ae..2c3a51282 100644 --- a/crates/forge/bin/cmd/generate/mod.rs +++ b/crates/forge/bin/cmd/generate/mod.rs @@ -39,7 +39,7 @@ impl GenerateTestArgs { fs::create_dir_all("test")?; // Define the test file path - let test_file_path = Path::new("test").join(format!("{}.t.sol", contract_name)); + let test_file_path = Path::new("test").join(format!("{contract_name}.t.sol")); // Write the test content to the test file. fs::write(&test_file_path, test_content)?; diff --git a/crates/forge/bin/cmd/init.rs b/crates/forge/bin/cmd/init.rs index 87010244c..5c2b56ea8 100644 --- a/crates/forge/bin/cmd/init.rs +++ b/crates/forge/bin/cmd/init.rs @@ -43,7 +43,7 @@ pub struct InitArgs { impl InitArgs { pub fn run(self) -> Result<()> { - let InitArgs { root, template, branch, opts, offline, force, vscode } = self; + let Self { root, template, branch, opts, offline, force, vscode } = self; let DependencyInstallOpts { shallow, no_git, no_commit, quiet } = opts; // create the root dir if it does not exist diff --git a/crates/forge/bin/cmd/inspect.rs b/crates/forge/bin/cmd/inspect.rs index b76ca2878..1c782f8bd 100644 --- a/crates/forge/bin/cmd/inspect.rs +++ b/crates/forge/bin/cmd/inspect.rs @@ -37,7 +37,7 @@ pub struct InspectArgs { impl InspectArgs { pub fn run(self) -> Result<()> { - let InspectArgs { mut contract, field, build, pretty } = self; + let Self { mut contract, field, build, pretty } = self; trace!(target: "forge", ?field, ?contract, "running forge inspect"); diff --git a/crates/forge/bin/cmd/install.rs b/crates/forge/bin/cmd/install.rs index ccec7d5de..57096f859 100644 --- a/crates/forge/bin/cmd/install.rs +++ b/crates/forge/bin/cmd/install.rs @@ -94,7 +94,7 @@ impl DependencyInstallOpts { /// /// Returns true if any dependency was installed. pub fn install_missing_dependencies(mut self, config: &mut Config) -> bool { - let DependencyInstallOpts { quiet, .. } = self; + let Self { quiet, .. } = self; let lib = config.install_lib_dir(); if self.git(config).has_missing_dependencies(Some(lib)).unwrap_or(false) { // The extra newline is needed, otherwise the compiler output will overwrite the message @@ -114,7 +114,7 @@ impl DependencyInstallOpts { /// Installs all dependencies pub fn install(self, config: &mut Config, dependencies: Vec) -> Result<()> { - let DependencyInstallOpts { no_git, no_commit, quiet, .. } = self; + let Self { no_git, no_commit, quiet, .. } = self; let git = self.git(config); diff --git a/crates/forge/bin/cmd/selectors.rs b/crates/forge/bin/cmd/selectors.rs index d3cada4b5..81188f35b 100644 --- a/crates/forge/bin/cmd/selectors.rs +++ b/crates/forge/bin/cmd/selectors.rs @@ -60,7 +60,7 @@ pub enum SelectorsSubcommands { impl SelectorsSubcommands { pub async fn run(self) -> Result<()> { match self { - SelectorsSubcommands::Upload { contract, all, project_paths } => { + Self::Upload { contract, all, project_paths } => { let build_args = CoreBuildArgs { project_paths: project_paths.clone(), compiler: CompilerArgs { @@ -118,7 +118,7 @@ impl SelectorsSubcommands { } } } - SelectorsSubcommands::Collision { mut first_contract, mut second_contract, build } => { + Self::Collision { mut first_contract, mut second_contract, build } => { // Compile the project with the two contracts included let project = build.project()?; let mut compiler = ProjectCompiler::new().quiet(true); @@ -174,7 +174,7 @@ impl SelectorsSubcommands { println!("{table}"); } } - SelectorsSubcommands::List { contract, project_paths } => { + Self::List { contract, project_paths } => { println!("Listing selectors for contracts in the project..."); let build_args = CoreBuildArgs { project_paths: project_paths.clone(), @@ -199,7 +199,7 @@ impl SelectorsSubcommands { let suggestion = if let Some(suggestion) = foundry_cli::utils::did_you_mean(&contract, candidates).pop() { format!("\nDid you mean `{suggestion}`?") } else { - "".to_string() + String::new() }; eyre::eyre!( "Could not find artifact `{contract}` in the compiled artifacts{suggestion}", diff --git a/crates/forge/bin/cmd/snapshot.rs b/crates/forge/bin/cmd/snapshot.rs index 5f91951d8..8cb5fbb06 100644 --- a/crates/forge/bin/cmd/snapshot.rs +++ b/crates/forge/bin/cmd/snapshot.rs @@ -131,7 +131,7 @@ impl FromStr for Format { fn from_str(s: &str) -> Result { match s { - "t" | "table" => Ok(Format::Table), + "t" | "table" => Ok(Self::Table), _ => Err(format!("Unrecognized format `{s}`")), } } @@ -211,7 +211,7 @@ impl FromStr for SnapshotEntry { cap.name("file").and_then(|file| { cap.name("sig").and_then(|sig| { if let Some(gas) = cap.name("gas") { - Some(SnapshotEntry { + Some(Self { contract_name: file.as_str().to_string(), signature: sig.as_str().to_string(), gas_used: TestKindReport::Standard { @@ -221,7 +221,7 @@ impl FromStr for SnapshotEntry { } else if let Some(runs) = cap.name("runs") { cap.name("avg") .and_then(|avg| cap.name("med").map(|med| (runs, avg, med))) - .map(|(runs, avg, med)| SnapshotEntry { + .map(|(runs, avg, med)| Self { contract_name: file.as_str().to_string(), signature: sig.as_str().to_string(), gas_used: TestKindReport::Fuzz { @@ -237,7 +237,7 @@ impl FromStr for SnapshotEntry { cap.name("reverts").map(|med| (runs, avg, med)) }) }) - .map(|(runs, calls, reverts)| SnapshotEntry { + .map(|(runs, calls, reverts)| Self { contract_name: file.as_str().to_string(), signature: sig.as_str().to_string(), gas_used: TestKindReport::Invariant { diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index a441b8e66..165a07374 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -165,7 +165,7 @@ impl TestArgs { let output = project.compile_sparse(Box::new(filter.clone()))?; if output.has_compiler_errors() { - println!("{}", output); + println!("{output}"); eyre::bail!("Compilation failed"); } diff --git a/crates/forge/bin/cmd/watch.rs b/crates/forge/bin/cmd/watch.rs index 1412cb15e..e12b42f7b 100644 --- a/crates/forge/bin/cmd/watch.rs +++ b/crates/forge/bin/cmd/watch.rs @@ -163,7 +163,7 @@ struct WatchTestState { } /// The `on_action` hook for `forge test --watch` -fn on_test(action: OnActionState) { +fn on_test(action: OnActionState<'_, WatchTestState>) { let OnActionState { args, runtime, action, wx, cmd, other } = action; let WatchTestState { project_root, no_reconfigure, last_test_files } = other; diff --git a/crates/forge/src/coverage.rs b/crates/forge/src/coverage.rs index 29920d735..8b06f3074 100644 --- a/crates/forge/src/coverage.rs +++ b/crates/forge/src/coverage.rs @@ -81,7 +81,7 @@ pub struct LcovReporter<'a> { } impl<'a> LcovReporter<'a> { - pub fn new(destination: &'a mut (dyn Write + 'a)) -> LcovReporter<'a> { + pub fn new(destination: &'a mut (dyn Write + 'a)) -> Self { Self { destination } } } @@ -195,7 +195,7 @@ pub struct BytecodeReporter { } impl BytecodeReporter { - pub fn new(root: PathBuf, destdir: PathBuf) -> BytecodeReporter { + pub fn new(root: PathBuf, destdir: PathBuf) -> Self { Self { root, destdir } } } @@ -218,7 +218,7 @@ impl CoverageReporter for BytecodeReporter { let hits = hits .hits .get(&(code.offset as usize)) - .map(|h| format!("[{:03}]", h)) + .map(|h| format!("[{h:03}]")) .unwrap_or(" ".to_owned()); let source_id = source_element.index(); let source_path = source_id.and_then(|i| { @@ -246,13 +246,9 @@ impl CoverageReporter for BytecodeReporter { end )?; } else if let Some(source_id) = source_id { - writeln!( - formatted, - "{} {:40} // SRCID{}: ({}-{})", - hits, code, source_id, start, end - )?; + writeln!(formatted, "{hits} {code:40} // SRCID{source_id}: ({start}-{end})")?; } else { - writeln!(formatted, "{} {:40}", hits, code)?; + writeln!(formatted, "{hits} {code:40}")?; } } fs::write( @@ -273,7 +269,7 @@ struct LineNumberCache { impl LineNumberCache { pub fn new(root: PathBuf) -> Self { - LineNumberCache { root, line_offsets: HashMap::new() } + Self { root, line_offsets: HashMap::new() } } pub fn get_position(&mut self, path: &Path, offset: usize) -> eyre::Result<(usize, usize)> { diff --git a/crates/forge/src/lib.rs b/crates/forge/src/lib.rs index 6a9334b16..56ca43286 100644 --- a/crates/forge/src/lib.rs +++ b/crates/forge/src/lib.rs @@ -1,3 +1,6 @@ +#![doc = include_str!("../README.md")] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + #[macro_use] extern crate tracing; diff --git a/crates/forge/src/result.rs b/crates/forge/src/result.rs index cd168c1d7..9b4a926aa 100644 --- a/crates/forge/src/result.rs +++ b/crates/forge/src/result.rs @@ -442,13 +442,13 @@ pub enum TestKindReport { impl fmt::Display for TestKindReport { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - TestKindReport::Standard { gas } => { + Self::Standard { gas } => { write!(f, "(gas: {gas})") } - TestKindReport::Fuzz { runs, mean_gas, median_gas } => { + Self::Fuzz { runs, mean_gas, median_gas } => { write!(f, "(runs: {runs}, μ: {mean_gas}, ~: {median_gas})") } - TestKindReport::Invariant { runs, calls, reverts } => { + Self::Invariant { runs, calls, reverts } => { write!(f, "(runs: {runs}, calls: {calls}, reverts: {reverts})") } } @@ -459,11 +459,11 @@ impl TestKindReport { /// Returns the main gas value to compare against pub fn gas(&self) -> u64 { match self { - TestKindReport::Standard { gas } => *gas, + Self::Standard { gas } => *gas, // We use the median for comparisons - TestKindReport::Fuzz { median_gas, .. } => *median_gas, + Self::Fuzz { median_gas, .. } => *median_gas, // We return 0 since it's not applicable - TestKindReport::Invariant { .. } => 0, + Self::Invariant { .. } => 0, } } } @@ -497,11 +497,11 @@ impl TestKind { /// The gas consumed by this test pub fn report(&self) -> TestKindReport { match self { - TestKind::Standard(gas) => TestKindReport::Standard { gas: *gas }, - TestKind::Fuzz { runs, mean_gas, median_gas, .. } => { + Self::Standard(gas) => TestKindReport::Standard { gas: *gas }, + Self::Fuzz { runs, mean_gas, median_gas, .. } => { TestKindReport::Fuzz { runs: *runs, mean_gas: *mean_gas, median_gas: *median_gas } } - TestKind::Invariant { runs, calls, reverts } => { + Self::Invariant { runs, calls, reverts } => { TestKindReport::Invariant { runs: *runs, calls: *calls, reverts: *reverts } } } diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index e8812dce1..31a9ec1fa 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -446,7 +446,6 @@ forgetest!(can_set_gas_price, |prj, cmd| { forgetest_init!(can_detect_lib_foundry_toml, |prj, cmd| { let config = cmd.config(); let remappings = config.remappings.iter().cloned().map(Remapping::from).collect::>(); - dbg!(&remappings); similar_asserts::assert_eq!( remappings, vec![ diff --git a/crates/forge/tests/cli/verify.rs b/crates/forge/tests/cli/verify.rs index deedcc9ab..2f1f1368d 100644 --- a/crates/forge/tests/cli/verify.rs +++ b/crates/forge/tests/cli/verify.rs @@ -79,7 +79,7 @@ fn parse_verification_result(cmd: &mut TestCommand, retries: u32) -> eyre::Resul retry.run(|| -> eyre::Result<()> { let output = cmd.unchecked_output(); let out = String::from_utf8_lossy(&output.stdout); - println!("{}", out); + println!("{out}"); if out.contains("Contract successfully verified") { return Ok(()) } diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index cd54526d6..d9f5298b1 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -39,9 +39,9 @@ pub enum ForgeTestProfile { impl fmt::Display for ForgeTestProfile { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - ForgeTestProfile::Default => write!(f, "default"), - ForgeTestProfile::Cancun => write!(f, "cancun"), - ForgeTestProfile::MultiVersion => write!(f, "multi-version"), + Self::Default => write!(f, "default"), + Self::Cancun => write!(f, "cancun"), + Self::MultiVersion => write!(f, "multi-version"), } } } diff --git a/crates/linking/Cargo.toml b/crates/linking/Cargo.toml index 77c54ccf7..2587244a2 100644 --- a/crates/linking/Cargo.toml +++ b/crates/linking/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [dependencies] foundry-compilers = { workspace = true, features = ["full"] } semver = "1" diff --git a/crates/linking/src/lib.rs b/crates/linking/src/lib.rs index 980dfc8fd..25ef8fe19 100644 --- a/crates/linking/src/lib.rs +++ b/crates/linking/src/lib.rs @@ -1,4 +1,9 @@ +//! # foundry-linking +//! +//! EVM bytecode linker. + #![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] use alloy_primitives::{Address, Bytes, B256}; use foundry_compilers::{ @@ -47,7 +52,7 @@ impl<'a> Linker<'a> { pub fn new( root: impl Into, contracts: ArtifactContracts>, - ) -> Linker<'a> { + ) -> Self { Linker { root: root.into(), contracts } } @@ -397,7 +402,7 @@ mod tests { "incorrect library address for dependency {dep_identifier} of {identifier}" ); } else { - panic!("Library {} not found", dep_identifier); + panic!("Library {dep_identifier} not found"); } } } diff --git a/crates/macros/Cargo.toml b/crates/macros/Cargo.toml index a2c070c53..671d37be8 100644 --- a/crates/macros/Cargo.toml +++ b/crates/macros/Cargo.toml @@ -9,6 +9,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [lib] proc-macro = true # proc-macro tests aren't fully supported by cargo-nextest archives diff --git a/crates/macros/src/lib.rs b/crates/macros/src/lib.rs index 136b65f51..dbdaa208c 100644 --- a/crates/macros/src/lib.rs +++ b/crates/macros/src/lib.rs @@ -1,4 +1,9 @@ -#![warn(unused_crate_dependencies)] +//! # foundry-macros +//! +//! Internal Foundry proc-macros. + +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #[macro_use] extern crate proc_macro_error; diff --git a/crates/script/Cargo.toml b/crates/script/Cargo.toml index 3d41ac795..9c9879392 100644 --- a/crates/script/Cargo.toml +++ b/crates/script/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [dependencies] forge-verify.workspace = true foundry-cli.workspace = true diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index 63b2de818..d9fe5640d 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -119,13 +119,13 @@ impl SendTransactionsKind { /// Returns an error if no matching signer is found or the address is not unlocked pub fn for_sender(&self, addr: &Address) -> Result> { match self { - SendTransactionsKind::Unlocked(unlocked) => { + Self::Unlocked(unlocked) => { if !unlocked.contains(addr) { bail!("Sender address {:?} is not unlocked", addr) } Ok(SendTransactionKind::Unlocked(*addr)) } - SendTransactionsKind::Raw(wallets) => { + Self::Raw(wallets) => { if let Some(wallet) = wallets.get(addr) { Ok(SendTransactionKind::Raw(wallet)) } else { @@ -138,8 +138,8 @@ impl SendTransactionsKind { /// How many signers are set pub fn signers_count(&self) -> usize { match self { - SendTransactionsKind::Unlocked(addr) => addr.len(), - SendTransactionsKind::Raw(signers) => signers.len(), + Self::Unlocked(addr) => addr.len(), + Self::Raw(signers) => signers.len(), } } } diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index 90e72c66c..7a785b6e6 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -36,7 +36,7 @@ pub struct BuildData { } impl BuildData { - pub fn get_linker(&self) -> Linker { + pub fn get_linker(&self) -> Linker<'_> { Linker::new(self.project_root.clone(), self.output.artifact_ids().collect()) } diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index bd0e23736..3cea29f20 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -1,4 +1,9 @@ +//! # foundry-script +//! +//! Smart contract scripting. + #![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #[macro_use] extern crate tracing; diff --git a/crates/script/src/multi_sequence.rs b/crates/script/src/multi_sequence.rs index ea6dfd0d4..24490bf01 100644 --- a/crates/script/src/multi_sequence.rs +++ b/crates/script/src/multi_sequence.rs @@ -28,8 +28,8 @@ pub struct SensitiveMultiChainSequence { } impl SensitiveMultiChainSequence { - fn from_multi_sequence(sequence: MultiChainSequence) -> SensitiveMultiChainSequence { - SensitiveMultiChainSequence { + fn from_multi_sequence(sequence: MultiChainSequence) -> Self { + Self { deployments: sequence.deployments.into_iter().map(|sequence| sequence.into()).collect(), } } @@ -43,9 +43,9 @@ impl MultiChainSequence { config: &Config, dry_run: bool, ) -> Result { - let (path, sensitive_path) = MultiChainSequence::get_paths(config, sig, target, dry_run)?; + let (path, sensitive_path) = Self::get_paths(config, sig, target, dry_run)?; - Ok(MultiChainSequence { deployments, path, sensitive_path, timestamp: now().as_secs() }) + Ok(Self { deployments, path, sensitive_path, timestamp: now().as_secs() }) } /// Gets paths in the formats @@ -91,8 +91,8 @@ impl MultiChainSequence { /// Loads the sequences for the multi chain deployment. pub fn load(config: &Config, sig: &str, target: &ArtifactId, dry_run: bool) -> Result { - let (path, sensitive_path) = MultiChainSequence::get_paths(config, sig, target, dry_run)?; - let mut sequence: MultiChainSequence = foundry_compilers::utils::read_json_file(&path) + let (path, sensitive_path) = Self::get_paths(config, sig, target, dry_run)?; + let mut sequence: Self = foundry_compilers::utils::read_json_file(&path) .wrap_err("Multi-chain deployment not found.")?; let sensitive_sequence: SensitiveMultiChainSequence = foundry_compilers::utils::read_json_file(&sensitive_path) diff --git a/crates/script/src/progress.rs b/crates/script/src/progress.rs index c8fd065cb..6f028688b 100644 --- a/crates/script/src/progress.rs +++ b/crates/script/src/progress.rs @@ -56,13 +56,7 @@ impl SequenceProgressState { txs.set_position(sequence.receipts.len() as u64); receipts.set_position(sequence.receipts.len() as u64); - let mut state = SequenceProgressState { - top_spinner, - txs, - receipts, - tx_spinners: Default::default(), - multi, - }; + let mut state = Self { top_spinner, txs, receipts, tx_spinners: Default::default(), multi }; for tx_hash in sequence.pending.iter() { state.tx_sent(*tx_hash); diff --git a/crates/script/src/providers.rs b/crates/script/src/providers.rs index aeb94fddf..7f1aa0eb3 100644 --- a/crates/script/src/providers.rs +++ b/crates/script/src/providers.rs @@ -55,7 +55,7 @@ pub enum GasPrice { } impl ProviderInfo { - pub async fn new(rpc: &str, mut is_legacy: bool) -> Result { + pub async fn new(rpc: &str, mut is_legacy: bool) -> Result { let provider = Arc::new(get_http_provider(rpc)); let chain = provider.get_chain_id().await?; @@ -73,7 +73,7 @@ impl ProviderInfo { ) }; - Ok(ProviderInfo { provider, chain, gas_price }) + Ok(Self { provider, chain, gas_price }) } /// Returns the gas price to use diff --git a/crates/script/src/receipts.rs b/crates/script/src/receipts.rs index 16f97536b..7189f8da4 100644 --- a/crates/script/src/receipts.rs +++ b/crates/script/src/receipts.rs @@ -16,9 +16,9 @@ pub enum TxStatus { impl From for TxStatus { fn from(receipt: AnyTransactionReceipt) -> Self { if !receipt.inner.inner.inner.receipt.status { - TxStatus::Revert(receipt) + Self::Revert(receipt) } else { - TxStatus::Success(receipt) + Self::Success(receipt) } } } diff --git a/crates/script/src/sequence.rs b/crates/script/src/sequence.rs index f98326564..f65314335 100644 --- a/crates/script/src/sequence.rs +++ b/crates/script/src/sequence.rs @@ -32,22 +32,22 @@ pub enum ScriptSequenceKind { impl ScriptSequenceKind { pub fn save(&mut self, silent: bool, save_ts: bool) -> Result<()> { match self { - ScriptSequenceKind::Single(sequence) => sequence.save(silent, save_ts), - ScriptSequenceKind::Multi(sequence) => sequence.save(silent, save_ts), + Self::Single(sequence) => sequence.save(silent, save_ts), + Self::Multi(sequence) => sequence.save(silent, save_ts), } } pub fn sequences(&self) -> &[ScriptSequence] { match self { - ScriptSequenceKind::Single(sequence) => std::slice::from_ref(sequence), - ScriptSequenceKind::Multi(sequence) => &sequence.deployments, + Self::Single(sequence) => std::slice::from_ref(sequence), + Self::Multi(sequence) => &sequence.deployments, } } pub fn sequences_mut(&mut self) -> &mut [ScriptSequence] { match self { - ScriptSequenceKind::Single(sequence) => std::slice::from_mut(sequence), - ScriptSequenceKind::Multi(sequence) => &mut sequence.deployments, + Self::Single(sequence) => std::slice::from_mut(sequence), + Self::Multi(sequence) => &mut sequence.deployments, } } /// Updates underlying sequence paths to not be under /dry-run directory. @@ -58,11 +58,11 @@ impl ScriptSequenceKind { target: &ArtifactId, ) -> Result<()> { match self { - ScriptSequenceKind::Single(sequence) => { + Self::Single(sequence) => { sequence.paths = Some(ScriptSequence::get_paths(config, sig, target, sequence.chain, false)?); } - ScriptSequenceKind::Multi(sequence) => { + Self::Multi(sequence) => { (sequence.path, sequence.sensitive_path) = MultiChainSequence::get_paths(config, sig, target, false)?; } @@ -114,7 +114,7 @@ pub struct SensitiveScriptSequence { impl From for SensitiveScriptSequence { fn from(sequence: ScriptSequence) -> Self { - SensitiveScriptSequence { + Self { transactions: sequence .transactions .iter() @@ -133,8 +133,7 @@ impl ScriptSequence { chain_id: u64, dry_run: bool, ) -> Result { - let (path, sensitive_path) = - ScriptSequence::get_paths(config, sig, target, chain_id, dry_run)?; + let (path, sensitive_path) = Self::get_paths(config, sig, target, chain_id, dry_run)?; let mut script_sequence: Self = foundry_compilers::utils::read_json_file(&path) .wrap_err(format!("Deployment not found for chain `{chain_id}`."))?; diff --git a/crates/script/src/transaction.rs b/crates/script/src/transaction.rs index f5f9399d4..fd63734d1 100644 --- a/crates/script/src/transaction.rs +++ b/crates/script/src/transaction.rs @@ -41,7 +41,7 @@ pub struct TransactionWithMetadata { } fn default_string() -> Option { - Some("".to_string()) + Some(String::new()) } fn default_address() -> Option
{ diff --git a/crates/script/src/verify.rs b/crates/script/src/verify.rs index 11dab9575..1e719fecf 100644 --- a/crates/script/src/verify.rs +++ b/crates/script/src/verify.rs @@ -77,7 +77,7 @@ impl VerifyBundle { let via_ir = config.via_ir; - VerifyBundle { + Self { num_of_optimizations, known_contracts, etherscan: Default::default(), diff --git a/crates/test-utils/Cargo.toml b/crates/test-utils/Cargo.toml index c34faf250..710050dab 100644 --- a/crates/test-utils/Cargo.toml +++ b/crates/test-utils/Cargo.toml @@ -11,6 +11,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [dependencies] foundry-common.workspace = true foundry-compilers = { workspace = true, features = ["project-util"] } diff --git a/crates/test-utils/src/filter.rs b/crates/test-utils/src/filter.rs index e24f87c17..003b0170f 100644 --- a/crates/test-utils/src/filter.rs +++ b/crates/test-utils/src/filter.rs @@ -13,7 +13,7 @@ pub struct Filter { impl Filter { pub fn new(test_pattern: &str, contract_pattern: &str, path_pattern: &str) -> Self { - Filter { + Self { test_regex: Regex::new(test_pattern) .unwrap_or_else(|_| panic!("Failed to parse test pattern: `{test_pattern}`")), contract_regex: Regex::new(contract_pattern).unwrap_or_else(|_| { @@ -60,7 +60,7 @@ impl Filter { } pub fn matches_all() -> Self { - Filter { + Self { test_regex: Regex::new(".*").unwrap(), contract_regex: Regex::new(".*").unwrap(), path_regex: Regex::new(".*").unwrap(), diff --git a/crates/test-utils/src/lib.rs b/crates/test-utils/src/lib.rs index a4b70f493..28bb4def1 100644 --- a/crates/test-utils/src/lib.rs +++ b/crates/test-utils/src/lib.rs @@ -1,4 +1,9 @@ -#![warn(unused_crate_dependencies, unreachable_pub)] +//! # foundry-test-utils +//! +//! Internal Foundry testing utilities. + +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #[macro_use] extern crate tracing; diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index 232417f87..0bc4d399d 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -60,7 +60,7 @@ impl ScriptTester { target_contract: &str, ) -> Self { init_tracing(); - ScriptTester::copy_testdata(project_root).unwrap(); + Self::copy_testdata(project_root).unwrap(); init_script_cmd(&mut cmd, project_root, target_contract, endpoint); let mut provider = None; @@ -68,7 +68,7 @@ impl ScriptTester { provider = Some(get_http_provider(endpoint)) } - ScriptTester { + Self { accounts_pub: vec![ Address::from_str("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266").unwrap(), Address::from_str("0x70997970C51812dc3A010C7d01b50e0d17dc79C8").unwrap(), @@ -297,17 +297,17 @@ impl ScriptOutcome { pub fn is_err(&self) -> bool { match self { - ScriptOutcome::OkNoEndpoint | - ScriptOutcome::OkSimulation | - ScriptOutcome::OkBroadcast | - ScriptOutcome::WarnSpecifyDeployer | - ScriptOutcome::OkRun => false, - ScriptOutcome::MissingSender | - ScriptOutcome::MissingWallet | - ScriptOutcome::StaticCallNotAllowed | - ScriptOutcome::UnsupportedLibraries | - ScriptOutcome::ErrorSelectForkOnBroadcast | - ScriptOutcome::ScriptFailed => true, + Self::OkNoEndpoint | + Self::OkSimulation | + Self::OkBroadcast | + Self::WarnSpecifyDeployer | + Self::OkRun => false, + Self::MissingSender | + Self::MissingWallet | + Self::StaticCallNotAllowed | + Self::UnsupportedLibraries | + Self::ErrorSelectForkOnBroadcast | + Self::ScriptFailed => true, } } } diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index 0ada068fd..b45803e66 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -733,18 +733,18 @@ impl TestCommand { } /// Replaces the underlying command. - pub fn set_cmd(&mut self, cmd: Command) -> &mut TestCommand { + pub fn set_cmd(&mut self, cmd: Command) -> &mut Self { self.cmd = cmd; self } /// Resets the command to the default `forge` command. - pub fn forge_fuse(&mut self) -> &mut TestCommand { + pub fn forge_fuse(&mut self) -> &mut Self { self.set_cmd(self.project.forge_bin()) } /// Resets the command to the default `cast` command. - pub fn cast_fuse(&mut self) -> &mut TestCommand { + pub fn cast_fuse(&mut self) -> &mut Self { self.set_cmd(self.project.cast_bin()) } @@ -758,13 +758,13 @@ impl TestCommand { } /// Add an argument to pass to the command. - pub fn arg>(&mut self, arg: A) -> &mut TestCommand { + pub fn arg>(&mut self, arg: A) -> &mut Self { self.cmd.arg(arg); self } /// Add any number of arguments to the command. - pub fn args(&mut self, args: I) -> &mut TestCommand + pub fn args(&mut self, args: I) -> &mut Self where I: IntoIterator, A: AsRef, @@ -773,13 +773,13 @@ impl TestCommand { self } - pub fn stdin(&mut self, fun: impl FnOnce(ChildStdin) + 'static) -> &mut TestCommand { + pub fn stdin(&mut self, fun: impl FnOnce(ChildStdin) + 'static) -> &mut Self { self.stdin_fun = Some(Box::new(fun)); self } /// Convenience function to add `--root project.root()` argument - pub fn root_arg(&mut self) -> &mut TestCommand { + pub fn root_arg(&mut self) -> &mut Self { let root = self.project.root().to_path_buf(); self.arg("--root").arg(root) } @@ -809,7 +809,7 @@ impl TestCommand { /// Note that this does not need to be called normally, since the creation /// of this TestCommand causes its working directory to be set to the /// test's directory automatically. - pub fn current_dir>(&mut self, dir: P) -> &mut TestCommand { + pub fn current_dir>(&mut self, dir: P) -> &mut Self { self.cmd.current_dir(dir); self } diff --git a/crates/verify/Cargo.toml b/crates/verify/Cargo.toml index 54d886f7c..378a292b8 100644 --- a/crates/verify/Cargo.toml +++ b/crates/verify/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [dependencies] foundry-config.workspace = true foundry-cli.workspace = true diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index 8a2a49f9c..486367018 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -459,7 +459,7 @@ impl VerifyBytecodeArgs { if !self.json { println!( "{} with status {}", - format!("{:?} code matched", bytecode_type).green().bold(), + format!("{bytecode_type:?} code matched").green().bold(), res.1.unwrap().green().bold() ); } else { @@ -475,8 +475,7 @@ impl VerifyBytecodeArgs { println!( "{}", format!( - "{:?} code did not match - this may be due to varying compiler settings", - bytecode_type + "{bytecode_type:?} code did not match - this may be due to varying compiler settings" ) .red() .bold() @@ -491,8 +490,7 @@ impl VerifyBytecodeArgs { matched: false, verification_type: self.verification_type, message: Some(format!( - "{:?} code did not match - this may be due to varying compiler settings", - bytecode_type + "{bytecode_type:?} code did not match - this may be due to varying compiler settings" )), }; json_results.push(json_res); @@ -515,8 +513,8 @@ impl FromStr for VerificationType { fn from_str(s: &str) -> Result { match s { - "full" => Ok(VerificationType::Full), - "partial" => Ok(VerificationType::Partial), + "full" => Ok(Self::Full), + "partial" => Ok(Self::Partial), _ => eyre::bail!("Invalid verification type"), } } @@ -534,8 +532,8 @@ impl From for String { impl fmt::Display for VerificationType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - VerificationType::Full => write!(f, "full"), - VerificationType::Partial => write!(f, "partial"), + Self::Full => write!(f, "full"), + Self::Partial => write!(f, "partial"), } } } diff --git a/crates/verify/src/lib.rs b/crates/verify/src/lib.rs index 200abbf3d..d3edcf62f 100644 --- a/crates/verify/src/lib.rs +++ b/crates/verify/src/lib.rs @@ -1,4 +1,9 @@ +//! # foundry-verify +//! +//! Smart contract verification. + #![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #[macro_use] extern crate tracing; @@ -48,7 +53,7 @@ pub struct VerifierArgs { impl Default for VerifierArgs { fn default() -> Self { - VerifierArgs { verifier: VerificationProviderType::Etherscan, verifier_url: None } + Self { verifier: VerificationProviderType::Etherscan, verifier_url: None } } } diff --git a/crates/verify/src/provider.rs b/crates/verify/src/provider.rs index cc65078d3..7d29f6146 100644 --- a/crates/verify/src/provider.rs +++ b/crates/verify/src/provider.rs @@ -118,10 +118,10 @@ impl FromStr for VerificationProviderType { fn from_str(s: &str) -> Result { match s { - "e" | "etherscan" => Ok(VerificationProviderType::Etherscan), - "s" | "sourcify" => Ok(VerificationProviderType::Sourcify), - "b" | "blockscout" => Ok(VerificationProviderType::Blockscout), - "o" | "oklink" => Ok(VerificationProviderType::Oklink), + "e" | "etherscan" => Ok(Self::Etherscan), + "s" | "sourcify" => Ok(Self::Sourcify), + "b" | "blockscout" => Ok(Self::Blockscout), + "o" | "oklink" => Ok(Self::Oklink), _ => Err(format!("Unknown provider: {s}")), } } @@ -130,16 +130,16 @@ impl FromStr for VerificationProviderType { impl fmt::Display for VerificationProviderType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - VerificationProviderType::Etherscan => { + Self::Etherscan => { write!(f, "etherscan")?; } - VerificationProviderType::Sourcify => { + Self::Sourcify => { write!(f, "sourcify")?; } - VerificationProviderType::Blockscout => { + Self::Blockscout => { write!(f, "blockscout")?; } - VerificationProviderType::Oklink => { + Self::Oklink => { write!(f, "oklink")?; } }; @@ -160,19 +160,15 @@ impl VerificationProviderType { /// Returns the corresponding `VerificationProvider` for the key pub fn client(&self, key: &Option) -> Result> { match self { - VerificationProviderType::Etherscan => { + Self::Etherscan => { if key.as_ref().map_or(true, |key| key.is_empty()) { eyre::bail!("ETHERSCAN_API_KEY must be set") } Ok(Box::::default()) } - VerificationProviderType::Sourcify => { - Ok(Box::::default()) - } - VerificationProviderType::Blockscout => { - Ok(Box::::default()) - } - VerificationProviderType::Oklink => Ok(Box::::default()), + Self::Sourcify => Ok(Box::::default()), + Self::Blockscout => Ok(Box::::default()), + Self::Oklink => Ok(Box::::default()), } } } diff --git a/crates/verify/src/retry.rs b/crates/verify/src/retry.rs index 8ffc61b88..528fd7497 100644 --- a/crates/verify/src/retry.rs +++ b/crates/verify/src/retry.rs @@ -37,7 +37,7 @@ impl Default for RetryArgs { impl From for Retry { fn from(r: RetryArgs) -> Self { - Retry::new(r.retries, Some(Duration::from_secs(r.delay as u64))) + Self::new(r.retries, Some(Duration::from_secs(r.delay as u64))) } } diff --git a/crates/wallets/Cargo.toml b/crates/wallets/Cargo.toml index a8c7658de..5a0477253 100644 --- a/crates/wallets/Cargo.toml +++ b/crates/wallets/Cargo.toml @@ -9,7 +9,12 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lints] +workspace = true + [dependencies] +foundry-config.workspace = true + alloy-primitives.workspace = true alloy-signer = { workspace = true, features = ["eip712"] } alloy-signer-wallet = { workspace = true, features = ["mnemonic", "keystore"] } @@ -25,15 +30,11 @@ alloy-signer-aws = { workspace = true, features = ["eip712"], optional = true } aws-config = { version = "1", optional = true } # default-features are necessary aws-sdk-kms = { version = "1", default-features = false, optional = true } -foundry-config.workspace = true -foundry-common.workspace = true - async-trait = "0.1" clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } derive_builder = "0.20.0" eyre.workspace = true hex = { workspace = true, features = ["serde"] } -itertools.workspace = true rpassword = "7" serde.workspace = true thiserror = "1" diff --git a/crates/wallets/src/error.rs b/crates/wallets/src/error.rs index 8b341cc3d..e3cc67c61 100644 --- a/crates/wallets/src/error.rs +++ b/crates/wallets/src/error.rs @@ -38,6 +38,6 @@ pub enum WalletSignerError { impl WalletSignerError { pub fn aws_unsupported() -> Self { - WalletSignerError::UnsupportedSigner("AWS KMS") + Self::UnsupportedSigner("AWS KMS") } } diff --git a/crates/wallets/src/lib.rs b/crates/wallets/src/lib.rs index 38ff5e7fa..e3be0971e 100644 --- a/crates/wallets/src/lib.rs +++ b/crates/wallets/src/lib.rs @@ -1,3 +1,10 @@ +//! # foundry-wallets +//! +//! Utilities for working with multiple signers. + +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + #[macro_use] extern crate tracing; diff --git a/crates/wallets/src/wallet_signer.rs b/crates/wallets/src/wallet_signer.rs index 9ab30e1ad..0e0bbf709 100644 --- a/crates/wallets/src/wallet_signer.rs +++ b/crates/wallets/src/wallet_signer.rs @@ -70,10 +70,10 @@ impl WalletSigner { pub async fn available_senders(&self, max: usize) -> Result> { let mut senders = Vec::new(); match self { - WalletSigner::Local(local) => { + Self::Local(local) => { senders.push(local.address()); } - WalletSigner::Ledger(ledger) => { + Self::Ledger(ledger) => { for i in 0..max { if let Ok(address) = ledger.get_address_with_path(&LedgerHDPath::LedgerLive(i)).await @@ -89,7 +89,7 @@ impl WalletSigner { } } } - WalletSigner::Trezor(trezor) => { + Self::Trezor(trezor) => { for i in 0..max { if let Ok(address) = trezor.get_address_with_path(&TrezorHDPath::TrezorLive(i)).await @@ -99,7 +99,7 @@ impl WalletSigner { } } #[cfg(feature = "aws-kms")] - WalletSigner::Aws(aws) => { + Self::Aws(aws) => { senders.push(alloy_signer::Signer::address(aws)); } } From 11d9c1a06343d3e98dffb73c2a0618024f703b4e Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 5 Jun 2024 17:30:10 +0200 Subject: [PATCH 356/430] chore: fix rustdoc warnings (#8069) * ci * chore: fix rustdoc warnings --- .github/workflows/deny.yml | 38 +++---- .github/workflows/dependencies.yml | 103 +++++++++--------- .github/workflows/test.yml | 12 +- Cargo.toml | 2 +- crates/anvil/core/src/eth/block.rs | 6 +- crates/anvil/core/src/eth/mod.rs | 10 +- .../core/src/eth/transaction/optimism.rs | 2 +- crates/anvil/rpc/src/error.rs | 16 +-- crates/anvil/rpc/src/response.rs | 2 +- crates/anvil/server/src/ipc.rs | 6 +- crates/anvil/src/cmd.rs | 6 +- crates/anvil/src/config.rs | 11 +- crates/anvil/src/eth/api.rs | 6 +- crates/anvil/src/eth/backend/mem/mod.rs | 7 +- crates/anvil/src/eth/otterscan/api.rs | 10 +- crates/anvil/src/eth/pool/transactions.rs | 6 +- crates/anvil/src/service.rs | 14 +-- crates/anvil/src/tasks/mod.rs | 2 + crates/cast/src/rlp_converter.rs | 7 +- crates/cheatcodes/spec/src/cheatcode.rs | 2 +- crates/cheatcodes/src/env.rs | 2 +- crates/cheatcodes/src/evm.rs | 2 +- crates/cheatcodes/src/fs.rs | 2 +- crates/cheatcodes/src/json.rs | 2 +- crates/cheatcodes/src/lib.rs | 21 +++- crates/cheatcodes/src/script.rs | 4 +- crates/cheatcodes/src/string.rs | 2 +- crates/cheatcodes/src/test.rs | 2 +- crates/cheatcodes/src/toml.rs | 2 +- crates/cheatcodes/src/utils.rs | 2 +- crates/chisel/src/cmd.rs | 6 +- crates/chisel/src/dispatcher.rs | 2 +- crates/chisel/src/executor.rs | 15 +-- crates/chisel/src/lib.rs | 8 -- crates/chisel/src/session.rs | 2 +- crates/chisel/src/session_source.rs | 4 +- crates/cli/src/opts/build/core.rs | 4 +- crates/cli/src/opts/build/mod.rs | 2 +- crates/cli/src/opts/build/paths.rs | 2 +- crates/cli/src/opts/ethereum.rs | 6 +- crates/cli/src/utils/cmd.rs | 2 +- crates/cli/src/utils/mod.rs | 12 +- crates/common/src/errors/fs.rs | 35 +++--- crates/common/src/evm.rs | 4 +- crates/common/src/fmt/dynamic.rs | 4 +- crates/common/src/provider/tower.rs | 2 +- crates/common/src/selectors.rs | 61 ++++++----- crates/common/src/serde_helpers.rs | 6 +- crates/common/src/shell.rs | 4 +- crates/common/src/term.rs | 2 - crates/config/src/endpoints.rs | 4 +- crates/config/src/error.rs | 2 +- crates/config/src/etherscan.rs | 2 +- crates/config/src/fix.rs | 4 +- crates/config/src/inline/error.rs | 5 +- crates/config/src/inline/mod.rs | 8 +- crates/config/src/lib.rs | 96 ++++++++-------- crates/config/src/macros.rs | 10 +- crates/config/src/providers/mod.rs | 2 + crates/doc/src/parser/comment.rs | 5 +- crates/doc/src/preprocessor/inheritdoc.rs | 2 +- crates/doc/src/writer/as_doc.rs | 2 +- crates/evm/core/src/backend/in_memory_db.rs | 13 ++- crates/evm/core/src/backend/mod.rs | 10 +- crates/evm/core/src/decode.rs | 2 +- crates/evm/core/src/fork/cache.rs | 8 +- crates/evm/core/src/fork/mod.rs | 5 +- crates/evm/core/src/opts.rs | 6 +- crates/evm/coverage/src/lib.rs | 25 +++-- crates/evm/evm/src/executors/builder.rs | 10 +- crates/evm/evm/src/executors/mod.rs | 18 +-- crates/evm/evm/src/inspectors/stack.rs | 4 +- crates/evm/fuzz/src/strategies/int.rs | 1 - crates/evm/fuzz/src/strategies/uint.rs | 1 - .../evm/traces/src/identifier/signatures.rs | 6 +- crates/fmt/src/formatter.rs | 9 +- crates/fmt/src/helpers.rs | 10 +- crates/fmt/src/inline_config.rs | 6 +- crates/script/src/broadcast.rs | 5 +- crates/script/src/build.rs | 6 +- crates/script/src/sequence.rs | 4 +- crates/script/src/simulate.rs | 8 +- crates/verify/src/bytecode.rs | 6 +- crates/verify/src/etherscan/flatten.rs | 2 +- 84 files changed, 397 insertions(+), 402 deletions(-) diff --git a/.github/workflows/deny.yml b/.github/workflows/deny.yml index 277b1c943..e8e4d5b84 100644 --- a/.github/workflows/deny.yml +++ b/.github/workflows/deny.yml @@ -1,26 +1,26 @@ name: deny on: - push: - branches: [master] - paths: [Cargo.lock, deny.toml] - pull_request: - branches: [master] - paths: [Cargo.lock, deny.toml] + push: + branches: [master] + paths: [Cargo.lock, deny.toml] + pull_request: + branches: [master] + paths: [Cargo.lock, deny.toml] env: - CARGO_TERM_COLOR: always + CARGO_TERM_COLOR: always jobs: - cargo-deny: - name: cargo deny check - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - uses: actions/checkout@v4 - - uses: EmbarkStudios/cargo-deny-action@v1 - with: - command: check all - # Clear out arguments to not pass `--all-features` to `cargo deny`. - # many crates have an `openssl` feature which enables banned dependencies - arguments: "" + cargo-deny: + name: cargo deny check + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + - uses: EmbarkStudios/cargo-deny-action@v1 + with: + command: check all + # Clear out arguments to not pass `--all-features` to `cargo deny`. + # many crates have an `openssl` feature which enables banned dependencies + arguments: "" diff --git a/.github/workflows/dependencies.yml b/.github/workflows/dependencies.yml index fd3daf853..ad87a4858 100644 --- a/.github/workflows/dependencies.yml +++ b/.github/workflows/dependencies.yml @@ -1,64 +1,61 @@ -# Automatically run `cargo update` periodically +# Runs `cargo update` periodically. name: dependencies on: - schedule: - # Run weekly - - cron: "0 0 * * SUN" - workflow_dispatch: - # Needed so we can run it manually + schedule: + # Run weekly + - cron: "0 0 * * SUN" + workflow_dispatch: + # Needed so we can run it manually env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - BRANCH: cargo-update - TITLE: "chore(deps): weekly `cargo update`" - BODY: | - Automation to keep dependencies in `Cargo.lock` current. + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BRANCH: cargo-update + TITLE: "chore(deps): weekly `cargo update`" + BODY: | + Automation to keep dependencies in `Cargo.lock` current. -
cargo update log -

+

cargo update log +

- ```log - $cargo_update_log - ``` + ```log + $cargo_update_log + ``` -

-
+

+
jobs: - update: - name: Update - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@nightly - - - name: cargo update - # Remove first line that always just says "Updating crates.io index" - run: - cargo update --color never 2>&1 | sed '/crates.io index/d' | tee -a - cargo_update.log - - - name: craft commit message and PR body - id: msg - run: | - export cargo_update_log="$(cat cargo_update.log)" - - echo "commit_message<> $GITHUB_OUTPUT - printf "$TITLE\n\n$cargo_update_log\n" >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - - echo "body<> $GITHUB_OUTPUT - echo "$BODY" | envsubst >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - - - name: Create Pull Request - uses: peter-evans/create-pull-request@v5 - with: - add-paths: ./Cargo.lock - commit-message: ${{ steps.msg.outputs.commit_message }} - title: ${{ env.TITLE }} - body: ${{ steps.msg.outputs.body }} - branch: ${{ env.BRANCH }} + update: + name: Update + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + + - name: cargo update + # Remove first line that always just says "Updating crates.io index" + run: cargo update --color never 2>&1 | sed '/crates.io index/d' | tee -a cargo_update.log + + - name: craft commit message and PR body + id: msg + run: | + export cargo_update_log="$(cat cargo_update.log)" + + echo "commit_message<> $GITHUB_OUTPUT + printf "$TITLE\n\n$cargo_update_log\n" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + echo "body<> $GITHUB_OUTPUT + echo "$BODY" | envsubst >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v6 + with: + add-paths: ./Cargo.lock + commit-message: ${{ steps.msg.outputs.commit_message }} + title: ${{ env.TITLE }} + body: ${{ steps.msg.outputs.body }} + branch: ${{ env.BRANCH }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 291417506..01dbc675e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -94,20 +94,18 @@ jobs: docs: runs-on: ubuntu-latest timeout-minutes: 30 - permissions: - contents: write - pages: write steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly - uses: Swatinem/rust-cache@v2 with: cache-on-failure: true - - run: cargo doc --workspace --all-features --no-deps --document-private-items + - name: Build documentation + run: cargo doc --workspace --all-features --no-deps --document-private-items env: - RUSTDOCFLAGS: - --cfg docsrs --show-type-layout --generate-link-to-definition --enable-index-page - -Zunstable-options + RUSTDOCFLAGS: --cfg docsrs -D warnings --show-type-layout --generate-link-to-definition -Zunstable-options + - name: Doc index page redirection + run: echo '' > target/doc/index.html - name: Deploy documentation uses: peaceiris/actions-gh-pages@v3 if: github.event_name == 'push' && github.ref == 'refs/heads/master' diff --git a/Cargo.toml b/Cargo.toml index 7becafbc7..e3e35dd0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rust-2018-idioms = "deny" unused-must-use = "deny" [workspace.lints.rustdoc] -# all = "warn" +all = "warn" # Speed up compilation time for dev builds by reducing emitted debug info. # NOTE: Debuggers may provide less useful information with this setting. diff --git a/crates/anvil/core/src/eth/block.rs b/crates/anvil/core/src/eth/block.rs index bf96a0df5..1b0895dcd 100644 --- a/crates/anvil/core/src/eth/block.rs +++ b/crates/anvil/core/src/eth/block.rs @@ -30,10 +30,10 @@ pub struct Block { } impl Block { - /// Creates a new block + /// Creates a new block. /// - /// Note: if the `impersonate-tx` feature is enabled this will also accept - /// [MaybeImpersonatedTransaction] + /// Note: if the `impersonate-tx` feature is enabled this will also accept + /// `MaybeImpersonatedTransaction`. pub fn new( partial_header: PartialHeader, transactions: impl IntoIterator, diff --git a/crates/anvil/core/src/eth/mod.rs b/crates/anvil/core/src/eth/mod.rs index dd7d05c97..1d39a488f 100644 --- a/crates/anvil/core/src/eth/mod.rs +++ b/crates/anvil/core/src/eth/mod.rs @@ -620,26 +620,26 @@ pub enum EthRequest { /// Returns the number of transactions currently pending for inclusion in the next block(s), as /// well as the ones that are being scheduled for future execution only. - /// Ref: [Here](https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_status) + /// Ref: #[cfg_attr(feature = "serde", serde(rename = "txpool_status", with = "empty_params"))] TxPoolStatus(()), /// Returns a summary of all the transactions currently pending for inclusion in the next /// block(s), as well as the ones that are being scheduled for future execution only. - /// Ref: [Here](https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_inspect) + /// Ref: #[cfg_attr(feature = "serde", serde(rename = "txpool_inspect", with = "empty_params"))] TxPoolInspect(()), /// Returns the details of all transactions currently pending for inclusion in the next /// block(s), as well as the ones that are being scheduled for future execution only. - /// Ref: [Here](https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_content) + /// Ref: #[cfg_attr(feature = "serde", serde(rename = "txpool_content", with = "empty_params"))] TxPoolContent(()), /// Otterscan's `ots_getApiLevel` endpoint /// Otterscan currently requires this endpoint, even though it's not part of the ots_* - /// https://github.com/otterscan/otterscan/blob/071d8c55202badf01804f6f8d53ef9311d4a9e47/src/useProvider.ts#L71 - /// Related upstream issue: https://github.com/otterscan/otterscan/issues/1081 + /// + /// Related upstream issue: #[cfg_attr(feature = "serde", serde(rename = "erigon_getHeaderByNumber"))] ErigonGetHeaderByNumber( #[cfg_attr( diff --git a/crates/anvil/core/src/eth/transaction/optimism.rs b/crates/anvil/core/src/eth/transaction/optimism.rs index dedaffaf3..912bdf26c 100644 --- a/crates/anvil/core/src/eth/transaction/optimism.rs +++ b/crates/anvil/core/src/eth/transaction/optimism.rs @@ -286,7 +286,7 @@ impl DepositTransaction { len } - /// Decodes the inner [TxDeposit] fields from RLP bytes. + /// Decodes the inner fields from RLP bytes /// /// NOTE: This assumes a RLP header has already been decoded, and _just_ decodes the following /// RLP fields in the following order: diff --git a/crates/anvil/rpc/src/error.rs b/crates/anvil/rpc/src/error.rs index 0836b8a27..4eec040ac 100644 --- a/crates/anvil/rpc/src/error.rs +++ b/crates/anvil/rpc/src/error.rs @@ -14,32 +14,32 @@ pub struct RpcError { } impl RpcError { - /// New [Error] with the given [ErrorCode] + /// New [`RpcError`] with the given [`ErrorCode`]. pub const fn new(code: ErrorCode) -> Self { Self { message: Cow::Borrowed(code.message()), code, data: None } } - /// Creates a new `ParseError` + /// Creates a new `ParseError` error. pub const fn parse_error() -> Self { Self::new(ErrorCode::ParseError) } - /// Creates a new `MethodNotFound` + /// Creates a new `MethodNotFound` error. pub const fn method_not_found() -> Self { Self::new(ErrorCode::MethodNotFound) } - /// Creates a new `InvalidRequest` + /// Creates a new `InvalidRequest` error. pub const fn invalid_request() -> Self { Self::new(ErrorCode::InvalidRequest) } - /// Creates a new `InternalError` + /// Creates a new `InternalError` error. pub const fn internal_error() -> Self { Self::new(ErrorCode::InternalError) } - /// Creates a new `InvalidParams` + /// Creates a new `InvalidParams` error. pub fn invalid_params(message: M) -> Self where M: Into, @@ -47,7 +47,7 @@ impl RpcError { Self { code: ErrorCode::InvalidParams, message: message.into().into(), data: None } } - /// Creates a new `InternalError` with a message + /// Creates a new `InternalError` error with a message. pub fn internal_error_with(message: M) -> Self where M: Into, @@ -55,7 +55,7 @@ impl RpcError { Self { code: ErrorCode::InternalError, message: message.into().into(), data: None } } - /// Creates a new rpc error for when a transaction was rejected + /// Creates a new RPC error for when a transaction was rejected. pub fn transaction_rejected(message: M) -> Self where M: Into, diff --git a/crates/anvil/rpc/src/response.rs b/crates/anvil/rpc/src/response.rs index 49ad70d0d..2fd01327d 100644 --- a/crates/anvil/rpc/src/response.rs +++ b/crates/anvil/rpc/src/response.rs @@ -72,7 +72,7 @@ pub enum Response { } impl Response { - /// Creates new [Response] with the given [Error] + /// Creates new [`Response`] with the given [`RpcError`]. pub fn error(error: RpcError) -> Self { RpcResponse::new(Id::Null, ResponseResult::Error(error)).into() } diff --git a/crates/anvil/server/src/ipc.rs b/crates/anvil/server/src/ipc.rs index ddcf5a21f..8743f0642 100644 --- a/crates/anvil/server/src/ipc.rs +++ b/crates/anvil/server/src/ipc.rs @@ -28,10 +28,10 @@ impl IpcEndpoint { Self { handler, path } } - /// Returns a stream of incoming connection handlers + /// Returns a stream of incoming connection handlers. /// - /// This establishes the ipc endpoint, converts the incoming connections into handled eth - /// connections, See [`PubSubConnection`] that should be spawned + /// This establishes the IPC endpoint, converts the incoming connections into handled + /// connections. #[instrument(target = "ipc", skip_all)] pub fn incoming(self) -> io::Result>> { let Self { handler, path } = self; diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index 5febf2905..576c63a9f 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -417,8 +417,7 @@ pub struct AnvilEvmArgs { /// /// default value: 330 /// - /// See --fork-url. - /// See also, https://docs.alchemy.com/reference/compute-units#what-are-cups-compute-units-per-second + /// See also --fork-url and #[arg( long, requires = "fork_url", @@ -432,8 +431,7 @@ pub struct AnvilEvmArgs { /// /// default value: false /// - /// See --fork-url. - /// See also, https://docs.alchemy.com/reference/compute-units#what-are-cups-compute-units-per-second + /// See also --fork-url and #[arg( long, requires = "fork_url", diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 24a354267..1b332c3a3 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -951,9 +951,9 @@ impl NodeConfig { } /// Configures everything related to forking based on the passed `eth_rpc_url`: - /// - returning a tuple of a [ForkedDatabase](ForkedDatabase) wrapped in an [Arc](Arc) - /// [RwLock](tokio::sync::RwLock) and [ClientFork](ClientFork) wrapped in an [Option](Option) - /// which can be used in a [Backend](mem::Backend) to fork from. + /// - returning a tuple of a [ForkedDatabase] wrapped in an [Arc] [RwLock](tokio::sync::RwLock) + /// and [ClientFork] wrapped in an [Option] which can be used in a [Backend](mem::Backend) to + /// fork from. /// - modifying some parameters of the passed `env` /// - mutating some members of `self` pub async fn setup_fork_db( @@ -973,9 +973,8 @@ impl NodeConfig { } /// Configures everything related to forking based on the passed `eth_rpc_url`: - /// - returning a tuple of a [ForkedDatabase](ForkedDatabase) and - /// [ClientForkConfig](ClientForkConfig) which can be used to build a - /// [ClientFork](ClientFork) to fork from. + /// - returning a tuple of a [ForkedDatabase] and [ClientForkConfig] which can be used to build + /// a [ClientFork] to fork from. /// - modifying some parameters of the passed `env` /// - mutating some members of `self` pub async fn setup_fork_db_config( diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 6d4da2af4..1afb7cd33 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -146,7 +146,7 @@ impl EthApi { } } - /// Executes the [EthRequest] and returns an RPC [RpcResponse] + /// Executes the [EthRequest] and returns an RPC [ResponseResult]. pub async fn execute(&self, request: EthRequest) -> ResponseResult { trace!(target: "rpc::api", "executing eth request"); match request { @@ -2196,7 +2196,7 @@ impl EthApi { /// Estimates the gas usage of the `request` with the state. /// - /// This will execute the [CallRequest] and find the best gas limit via binary search + /// This will execute the transaction request and find the best gas limit via binary search. fn do_estimate_gas_with_state( &self, mut request: WithOtherFields, @@ -2643,7 +2643,7 @@ enum GasEstimationCallResult { EvmError(InstructionResult), } -/// Converts the result of a call to revm EVM into a [GasEstimationCallRes]. +/// Converts the result of a call to revm EVM into a [`GasEstimationCallResult`]. impl TryFrom, u128, State)>> for GasEstimationCallResult { type Error = BlockchainError; diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 335d1620e..81f4ea639 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -1,4 +1,5 @@ -//! In memory blockchain backend +//! In-memory blockchain backend. + use self::state::trie_storage; use crate::{ config::PruneStateHistoryConfig, @@ -130,7 +131,7 @@ pub struct Backend { /// the evm during its execution. /// /// At time of writing, there are two different types of `Db`: - /// - [`MemDb`](crate::mem::MemDb): everything is stored in memory + /// - [`MemDb`](crate::mem::in_memory_db::MemDb): everything is stored in memory /// - [`ForkDb`](crate::mem::fork_db::ForkedDatabase): forks off a remote client, missing /// data is retrieved via RPC-calls /// @@ -141,7 +142,7 @@ pub struct Backend { /// potentially blocks for some time, even taking into account the rate limits of RPC /// endpoints. Therefor the `Db` is guarded by a `tokio::sync::RwLock` here so calls that /// need to read from it, while it's currently written to, don't block. E.g. a new block is - /// currently mined and a new [`Self::set_storage()`] request is being executed. + /// currently mined and a new [`Self::set_storage_at()`] request is being executed. db: Arc>>, /// stores all block related data in memory blockchain: Blockchain, diff --git a/crates/anvil/src/eth/otterscan/api.rs b/crates/anvil/src/eth/otterscan/api.rs index d2a1c7f0c..dac698a5a 100644 --- a/crates/anvil/src/eth/otterscan/api.rs +++ b/crates/anvil/src/eth/otterscan/api.rs @@ -13,10 +13,10 @@ use alloy_rpc_types_trace::parity::{Action, CreateAction, CreateOutput, TraceOut use itertools::Itertools; impl EthApi { - /// Otterscan currently requires this endpoint, even though it's not part of the ots_* - /// https://github.com/otterscan/otterscan/blob/071d8c55202badf01804f6f8d53ef9311d4a9e47/src/useProvider.ts#L71 + /// Otterscan currently requires this endpoint, even though it's not part of the `ots_*`. + /// Ref: /// - /// As a faster alternative to eth_getBlockByNumber (by excluding uncle block + /// As a faster alternative to `eth_getBlockByNumber` (by excluding uncle block /// information), which is not relevant in the context of an anvil node pub async fn erigon_get_header_by_number(&self, number: BlockNumber) -> Result> { node_info!("ots_getApiLevel"); @@ -24,8 +24,8 @@ impl EthApi { self.backend.block_by_number(number).await } - /// As per the latest Otterscan source code, at least version 8 is needed - /// https://github.com/otterscan/otterscan/blob/071d8c55202badf01804f6f8d53ef9311d4a9e47/src/params.ts#L1C2-L1C2 + /// As per the latest Otterscan source code, at least version 8 is needed. + /// Ref: pub async fn ots_get_api_level(&self) -> Result { node_info!("ots_getApiLevel"); diff --git a/crates/anvil/src/eth/pool/transactions.rs b/crates/anvil/src/eth/pool/transactions.rs index 7a1e8e8ab..026268231 100644 --- a/crates/anvil/src/eth/pool/transactions.rs +++ b/crates/anvil/src/eth/pool/transactions.rs @@ -409,12 +409,12 @@ impl ReadyTransactions { id } - /// Adds a new transactions to the ready queue + /// Adds a new transactions to the ready queue. /// /// # Panics /// - /// if the pending transaction is not ready: [PendingTransaction::is_ready()] - /// or the transaction is already included + /// If the pending transaction is not ready ([`PendingPoolTransaction::is_ready`]) + /// or the transaction is already included. pub fn add_transaction( &mut self, tx: PendingPoolTransaction, diff --git a/crates/anvil/src/service.rs b/crates/anvil/src/service.rs index 6f8624d94..0f70ad3b0 100644 --- a/crates/anvil/src/service.rs +++ b/crates/anvil/src/service.rs @@ -23,17 +23,17 @@ use tokio::{task::JoinHandle, time::Interval}; /// The type that drives the blockchain's state /// /// This service is basically an endless future that continuously polls the miner which returns -/// transactions for the next block, then those transactions are handed off to the -/// [backend](backend::mem::Backend) to construct a new block, if all transactions were successfully -/// included in a new block they get purged from the `Pool`. +/// transactions for the next block, then those transactions are handed off to the backend to +/// construct a new block, if all transactions were successfully included in a new block they get +/// purged from the `Pool`. pub struct NodeService { - /// the pool that holds all transactions + /// The pool that holds all transactions. pool: Arc, - /// creates new blocks + /// Creates new blocks. block_producer: BlockProducer, - /// the miner responsible to select transactions from the `pool´ + /// The miner responsible to select transactions from the `pool`. miner: Miner, - /// maintenance task for fee history related tasks + /// Maintenance task for fee history related tasks. fee_history: FeeHistoryService, /// Tracks all active filters filters: Filters, diff --git a/crates/anvil/src/tasks/mod.rs b/crates/anvil/src/tasks/mod.rs index a63b19016..499cb3e67 100644 --- a/crates/anvil/src/tasks/mod.rs +++ b/crates/anvil/src/tasks/mod.rs @@ -1,5 +1,7 @@ //! Task management support +#![allow(rustdoc::private_doc_tests)] + use crate::{shutdown::Shutdown, tasks::block_listener::BlockListener, EthApi}; use alloy_network::AnyNetwork; use alloy_primitives::B256; diff --git a/crates/cast/src/rlp_converter.rs b/crates/cast/src/rlp_converter.rs index d0d60c082..cab852ab3 100644 --- a/crates/cast/src/rlp_converter.rs +++ b/crates/cast/src/rlp_converter.rs @@ -2,9 +2,10 @@ use alloy_rlp::{Buf, Decodable, Encodable, Header}; use serde_json::Value; use std::fmt; -/// Arbitrary nested data -/// Item::Array(vec![]); is equivalent to [] -/// Item::Array(vec![Item::Data(vec![])]); is equivalent to [""] or [null] +/// Arbitrary nested data. +/// +/// - `Item::Array(vec![])` is equivalent to `[]`. +/// - `Item::Array(vec![Item::Data(vec![])])` is equivalent to `[""]` or `[null]`. #[derive(Clone, Debug, PartialEq, Eq)] pub enum Item { Data(Vec), diff --git a/crates/cheatcodes/spec/src/cheatcode.rs b/crates/cheatcodes/spec/src/cheatcode.rs index 95aa9aa47..91573d89e 100644 --- a/crates/cheatcodes/spec/src/cheatcode.rs +++ b/crates/cheatcodes/spec/src/cheatcode.rs @@ -2,7 +2,7 @@ use super::Function; use alloy_sol_types::SolCall; use serde::{Deserialize, Serialize}; -/// Cheatcode definition trait. Implemented by all [`Vm`] functions. +/// Cheatcode definition trait. Implemented by all [`Vm`](crate::Vm) functions. pub trait CheatcodeDef: std::fmt::Debug + Clone + SolCall { /// The static cheatcode definition. const CHEATCODE: &'static Cheatcode<'static>; diff --git a/crates/cheatcodes/src/env.rs b/crates/cheatcodes/src/env.rs index b8245dda1..bba8069fb 100644 --- a/crates/cheatcodes/src/env.rs +++ b/crates/cheatcodes/src/env.rs @@ -1,4 +1,4 @@ -//! Implementations of [`Environment`](crate::Group::Environment) cheatcodes. +//! Implementations of [`Environment`](spec::Group::Environment) cheatcodes. use crate::{string, Cheatcode, Cheatcodes, Error, Result, Vm::*}; use alloy_dyn_abi::DynSolType; diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index 7eae7331b..e652bf284 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -1,4 +1,4 @@ -//! Implementations of [`Evm`](crate::Group::Evm) cheatcodes. +//! Implementations of [`Evm`](spec::Group::Evm) cheatcodes. use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Result, Vm::*}; use alloy_genesis::{Genesis, GenesisAccount}; diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index 75e0d2467..c50f2cf97 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -1,4 +1,4 @@ -//! Implementations of [`Filesystem`](crate::Group::Filesystem) cheatcodes. +//! Implementations of [`Filesystem`](spec::Group::Filesystem) cheatcodes. use super::string::parse; use crate::{Cheatcode, Cheatcodes, Result, Vm::*}; diff --git a/crates/cheatcodes/src/json.rs b/crates/cheatcodes/src/json.rs index 6b7dd0e97..d3c3e9596 100644 --- a/crates/cheatcodes/src/json.rs +++ b/crates/cheatcodes/src/json.rs @@ -1,4 +1,4 @@ -//! Implementations of [`Json`](crate::Group::Json) cheatcodes. +//! Implementations of [`Json`](spec::Group::Json) cheatcodes. use crate::{string, Cheatcode, Cheatcodes, Result, Vm::*}; use alloy_dyn_abi::{DynSolType, DynSolValue}; diff --git a/crates/cheatcodes/src/lib.rs b/crates/cheatcodes/src/lib.rs index 023ef877d..be13f0f8e 100644 --- a/crates/cheatcodes/src/lib.rs +++ b/crates/cheatcodes/src/lib.rs @@ -19,26 +19,37 @@ pub use config::CheatsConfig; pub use error::{Error, ErrorKind, Result}; pub use inspector::{BroadcastableTransaction, BroadcastableTransactions, Cheatcodes, Context}; pub use spec::{CheatcodeDef, Vm}; +pub use Vm::ForgeContext; #[macro_use] mod error; + mod base64; + mod config; + mod env; +pub use env::set_execution_context; + mod evm; + mod fs; + mod inspector; + mod json; + mod script; +pub use script::{ScriptWallets, ScriptWalletsInner}; + mod string; + mod test; +pub use test::expect::ExpectedCallTracker; + mod toml; -mod utils; -pub use env::set_execution_context; -pub use script::ScriptWallets; -pub use test::expect::ExpectedCallTracker; -pub use Vm::ForgeContext; +mod utils; /// Cheatcode implementation. pub(crate) trait Cheatcode: CheatcodeDef + DynCheatcode { diff --git a/crates/cheatcodes/src/script.rs b/crates/cheatcodes/src/script.rs index 8eacb52a8..c9195f449 100644 --- a/crates/cheatcodes/src/script.rs +++ b/crates/cheatcodes/src/script.rs @@ -1,4 +1,4 @@ -//! Implementations of [`Scripting`](crate::Group::Scripting) cheatcodes. +//! Implementations of [`Scripting`](spec::Group::Scripting) cheatcodes. use crate::{Cheatcode, CheatsCtxt, DatabaseExt, Result, Vm::*}; use alloy_primitives::{Address, B256, U256}; @@ -83,7 +83,7 @@ pub struct ScriptWalletsInner { pub provided_sender: Option
, } -/// Clonable wrapper around [ScriptWalletsInner]. +/// Clonable wrapper around [`ScriptWalletsInner`]. #[derive(Debug, Clone)] pub struct ScriptWallets { /// Inner data. diff --git a/crates/cheatcodes/src/string.rs b/crates/cheatcodes/src/string.rs index c98560a72..07e9e89f5 100644 --- a/crates/cheatcodes/src/string.rs +++ b/crates/cheatcodes/src/string.rs @@ -1,4 +1,4 @@ -//! Implementations of [`String`](crate::Group::String) cheatcodes. +//! Implementations of [`String`](spec::Group::String) cheatcodes. use crate::{Cheatcode, Cheatcodes, Result, Vm::*}; use alloy_dyn_abi::{DynSolType, DynSolValue}; diff --git a/crates/cheatcodes/src/test.rs b/crates/cheatcodes/src/test.rs index 18487c0aa..cab8b9f8b 100644 --- a/crates/cheatcodes/src/test.rs +++ b/crates/cheatcodes/src/test.rs @@ -1,4 +1,4 @@ -//! Implementations of [`Testing`](crate::Group::Testing) cheatcodes. +//! Implementations of [`Testing`](spec::Group::Testing) cheatcodes. use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Error, Result, Vm::*}; use alloy_primitives::Address; diff --git a/crates/cheatcodes/src/toml.rs b/crates/cheatcodes/src/toml.rs index 886c38a4a..e1827dbef 100644 --- a/crates/cheatcodes/src/toml.rs +++ b/crates/cheatcodes/src/toml.rs @@ -1,4 +1,4 @@ -//! Implementations of [`Toml`](crate::Group::Toml) cheatcodes. +//! Implementations of [`Toml`](spec::Group::Toml) cheatcodes. use crate::{ json::{ diff --git a/crates/cheatcodes/src/utils.rs b/crates/cheatcodes/src/utils.rs index 95a7c6d32..4785e0197 100644 --- a/crates/cheatcodes/src/utils.rs +++ b/crates/cheatcodes/src/utils.rs @@ -1,4 +1,4 @@ -//! Implementations of [`Utils`](crate::Group::Utils) cheatcodes. +//! Implementations of [`Utilities`](spec::Group::Utilities) cheatcodes. use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Result, Vm::*}; use alloy_primitives::{keccak256, Address, B256, U256}; diff --git a/crates/chisel/src/cmd.rs b/crates/chisel/src/cmd.rs index 86925fc1b..c13272bc9 100644 --- a/crates/chisel/src/cmd.rs +++ b/crates/chisel/src/cmd.rs @@ -19,10 +19,10 @@ pub enum ChiselCommand { /// Print the generated source contract Source, /// Save the current session to the cache - /// Takes: [session-id] + /// Takes: `` Save, /// Load a previous session from cache - /// Takes: + /// Takes: `` /// /// WARNING: This will overwrite the current session (though the current session will be /// optimistically cached) @@ -45,7 +45,7 @@ pub enum ChiselCommand { /// Export the current REPL session source to a Script file Export, /// Fetch an interface of a verified contract on Etherscan - /// Takes: + /// Takes: ` ` Fetch, /// Executes a shell command Exec, diff --git a/crates/chisel/src/dispatcher.rs b/crates/chisel/src/dispatcher.rs index 33520e308..5c13c8e01 100644 --- a/crates/chisel/src/dispatcher.rs +++ b/crates/chisel/src/dispatcher.rs @@ -942,7 +942,7 @@ impl ChiselDispatcher { Ok(()) } - /// Format a type that implements [fmt::Display] as a chisel error string. + /// Format a type that implements [std::fmt::Display] as a chisel error string. /// /// ### Takes /// diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index f9cce4994..a177aa211 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -279,7 +279,7 @@ impl SessionSource { /// /// ### Takes /// - /// The final statement's program counter for the [ChiselInspector] + /// The final statement's program counter for the ChiselInspector /// /// ### Returns /// @@ -323,17 +323,8 @@ impl SessionSource { } } -/// Formats a [Token] into an inspection message -/// -/// ### Takes -/// -/// An owned [Token] -/// -/// ### Returns -/// -/// A formatted [Token] for use in inspection output. -/// -/// TODO: Verbosity option +/// Formats a value into an inspection message +// TODO: Verbosity option fn format_token(token: DynSolValue) -> String { match token { DynSolValue::Address(a) => { diff --git a/crates/chisel/src/lib.rs b/crates/chisel/src/lib.rs index d0085c327..9e7dcc9fb 100644 --- a/crates/chisel/src/lib.rs +++ b/crates/chisel/src/lib.rs @@ -1,30 +1,22 @@ #![doc = include_str!("../README.md")] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -/// REPL input dispatcher module pub mod dispatcher; -/// Builtin Chisel commands pub mod cmd; pub mod history; -/// Chisel Environment Module pub mod session; -/// Chisel Session Source wrapper pub mod session_source; -/// REPL contract runner pub mod runner; -/// REPL contract executor pub mod executor; -/// A Solidity Helper module for rustyline pub mod solidity_helper; -/// Prelude of all chisel modules pub mod prelude { pub use crate::{ cmd::*, dispatcher::*, runner::*, session::*, session_source::*, solidity_helper::*, diff --git a/crates/chisel/src/session.rs b/crates/chisel/src/session.rs index e0865e28a..2f293c1cd 100644 --- a/crates/chisel/src/session.rs +++ b/crates/chisel/src/session.rs @@ -247,7 +247,7 @@ impl ChiselSession { } /// Generic helper function that attempts to convert a type that has -/// an [Into] implementation into a formatted date string. +/// an [`Into`] implementation into a formatted date string. fn systemtime_strftime(dt: T, format: &str) -> Result where T: Into, diff --git a/crates/chisel/src/session_source.rs b/crates/chisel/src/session_source.rs index b8ba084cd..500cd97ef 100644 --- a/crates/chisel/src/session_source.rs +++ b/crates/chisel/src/session_source.rs @@ -303,11 +303,11 @@ impl SessionSource { self } - /// Generates and foundry_compilers::CompilerInput from the source + /// Generates and [`SolcInput`] from the source. /// /// ### Returns /// - /// A [CompilerInput] object containing forge-std's `Vm` interface as well as the REPL contract + /// A [`SolcInput`] object containing forge-std's `Vm` interface as well as the REPL contract /// source. pub fn compiler_input(&self) -> SolcInput { let mut sources = Sources::new(); diff --git a/crates/cli/src/opts/build/core.rs b/crates/cli/src/opts/build/core.rs index a3143144d..3d45de225 100644 --- a/crates/cli/src/opts/build/core.rs +++ b/crates/cli/src/opts/build/core.rs @@ -131,8 +131,8 @@ impl CoreBuildArgs { /// Returns the `Project` for the current workspace /// /// This loads the `foundry_config::Config` for the current workspace (see - /// [`utils::find_project_root_path`] and merges the cli `BuildArgs` into it before returning - /// [`foundry_config::Config::project()`] + /// `find_project_root_path` and merges the cli `BuildArgs` into it before returning + /// [`foundry_config::Config::project()`]). pub fn project(&self) -> Result> { let config = self.try_load_config_emit_warnings()?; Ok(config.project()?) diff --git a/crates/cli/src/opts/build/mod.rs b/crates/cli/src/opts/build/mod.rs index e752ae53f..2ed2ea463 100644 --- a/crates/cli/src/opts/build/mod.rs +++ b/crates/cli/src/opts/build/mod.rs @@ -39,7 +39,7 @@ pub struct CompilerArgs { /// /// Example keys: evm.assembly, ewasm, ir, irOptimized, metadata /// - /// For a full description, see https://docs.soliditylang.org/en/v0.8.13/using-the-compiler.html#input-description + /// For a full description, see #[arg(long, num_args(1..), value_name = "SELECTOR")] #[serde(skip_serializing_if = "Vec::is_empty")] pub extra_output: Vec, diff --git a/crates/cli/src/opts/build/paths.rs b/crates/cli/src/opts/build/paths.rs index 949742751..9ec2dc001 100644 --- a/crates/cli/src/opts/build/paths.rs +++ b/crates/cli/src/opts/build/paths.rs @@ -64,7 +64,7 @@ pub struct ProjectPathsArgs { } impl ProjectPathsArgs { - /// Returns the root directory to use for configuring the [Project] + /// Returns the root directory to use for configuring the project. /// /// This will be the `--root` argument if provided, otherwise see [find_project_root_path()] /// diff --git a/crates/cli/src/opts/ethereum.rs b/crates/cli/src/opts/ethereum.rs index 272d3a5a2..c4e8f0887 100644 --- a/crates/cli/src/opts/ethereum.rs +++ b/crates/cli/src/opts/ethereum.rs @@ -21,9 +21,11 @@ pub struct RpcOpts { #[arg(short = 'r', long = "rpc-url", env = "ETH_RPC_URL")] pub url: Option, - /// Use the Flashbots RPC URL with fast mode (https://rpc.flashbots.net/fast). + /// Use the Flashbots RPC URL with fast mode (). + /// /// This shares the transaction privately with all registered builders. - /// https://docs.flashbots.net/flashbots-protect/quick-start#faster-transactions + /// + /// See: #[arg(long)] pub flashbots: bool, diff --git a/crates/cli/src/utils/cmd.rs b/crates/cli/src/utils/cmd.rs index 6c010655b..05e6ad0ae 100644 --- a/crates/cli/src/utils/cmd.rs +++ b/crates/cli/src/utils/cmd.rs @@ -192,7 +192,7 @@ pub fn has_batch_support(chain_id: u64) -> bool { /// Helpers for loading configuration. /// /// This is usually implicitly implemented on a "&CmdArgs" struct via impl macros defined in -/// `forge_config` (See [`forge_config::impl_figment_convert`] for more details) and the impl +/// `forge_config` (see [`foundry_config::impl_figment_convert`] for more details) and the impl /// definition on `T: Into + Into` below. /// /// Each function also has an `emit_warnings` form which does the same thing as its counterpart but diff --git a/crates/cli/src/utils/mod.rs b/crates/cli/src/utils/mod.rs index 34333e593..a840a10c2 100644 --- a/crates/cli/src/utils/mod.rs +++ b/crates/cli/src/utils/mod.rs @@ -3,6 +3,7 @@ use alloy_primitives::U256; use alloy_provider::{network::AnyNetwork, Provider}; use alloy_transport::Transport; use eyre::{ContextCompat, Result}; +use foundry_common::provider::{ProviderBuilder, RetryProvider}; use foundry_config::{Chain, Config}; use std::{ ffi::OsStr, @@ -81,19 +82,18 @@ pub fn abi_to_solidity(abi: &JsonAbi, name: &str) -> Result { Ok(s) } -/// Returns a [RetryProvider](foundry_common::RetryProvider) instantiated using [Config]'s +/// Returns a [RetryProvider] instantiated using [Config]'s /// RPC -pub fn get_provider(config: &Config) -> Result { +pub fn get_provider(config: &Config) -> Result { get_provider_builder(config)?.build() } -/// Returns a [ProviderBuilder](foundry_common::provider::ProviderBuilder) instantiated using -/// [Config] values. +/// Returns a [ProviderBuilder] instantiated using [Config] values. /// /// Defaults to `http://localhost:8545` and `Mainnet`. -pub fn get_provider_builder(config: &Config) -> Result { +pub fn get_provider_builder(config: &Config) -> Result { let url = config.get_rpc_url_or_localhost_http()?; - let mut builder = foundry_common::provider::ProviderBuilder::new(url.as_ref()); + let mut builder = ProviderBuilder::new(url.as_ref()); if let Ok(chain) = config.chain.unwrap_or_default().try_into() { builder = builder.chain(chain); diff --git a/crates/common/src/errors/fs.rs b/crates/common/src/errors/fs.rs index 1e466171e..9ea84b9ce 100644 --- a/crates/common/src/errors/fs.rs +++ b/crates/common/src/errors/fs.rs @@ -3,36 +3,39 @@ use std::{ path::{Path, PathBuf}, }; -/// Various error variants for `std::fs` operations that serve as an addition to the io::Error which +#[allow(unused_imports)] +use std::fs::{self, File}; + +/// Various error variants for `fs` operations that serve as an addition to the io::Error which /// does not provide any information about the path. #[derive(Debug, thiserror::Error)] #[allow(missing_docs)] pub enum FsPathError { - /// Provides additional path context for [`std::fs::write`]. + /// Provides additional path context for [`fs::write`]. #[error("failed to write to {path:?}: {source}")] Write { source: io::Error, path: PathBuf }, - /// Provides additional path context for [`std::fs::read`]. + /// Provides additional path context for [`fs::read`]. #[error("failed to read from {path:?}: {source}")] Read { source: io::Error, path: PathBuf }, - /// Provides additional path context for [`std::fs::copy`]. + /// Provides additional path context for [`fs::copy`]. #[error("failed to copy from {from:?} to {to:?}: {source}")] Copy { source: io::Error, from: PathBuf, to: PathBuf }, - /// Provides additional path context for [`std::fs::read_link`]. + /// Provides additional path context for [`fs::read_link`]. #[error("failed to read from {path:?}: {source}")] ReadLink { source: io::Error, path: PathBuf }, /// Provides additional path context for [`File::create`]. #[error("failed to create file {path:?}: {source}")] CreateFile { source: io::Error, path: PathBuf }, - /// Provides additional path context for [`std::fs::remove_file`]. + /// Provides additional path context for [`fs::remove_file`]. #[error("failed to remove file {path:?}: {source}")] RemoveFile { source: io::Error, path: PathBuf }, - /// Provides additional path context for [`std::fs::create_dir`]. + /// Provides additional path context for [`fs::create_dir`]. #[error("failed to create dir {path:?}: {source}")] CreateDir { source: io::Error, path: PathBuf }, - /// Provides additional path context for [`std::fs::remove_dir`]. + /// Provides additional path context for [`fs::remove_dir`]. #[error("failed to remove dir {path:?}: {source}")] RemoveDir { source: io::Error, path: PathBuf }, - /// Provides additional path context for [`std::fs::File::open`]. + /// Provides additional path context for [`File::open`]. #[error("failed to open file {path:?}: {source}")] Open { source: io::Error, path: PathBuf }, /// Provides additional path context for the file whose contents should be parsed as JSON. @@ -44,22 +47,22 @@ pub enum FsPathError { } impl FsPathError { - /// Returns the complementary error variant for [`std::fs::write`]. + /// Returns the complementary error variant for [`fs::write`]. pub fn write(source: io::Error, path: impl Into) -> Self { Self::Write { source, path: path.into() } } - /// Returns the complementary error variant for [`std::fs::read`]. + /// Returns the complementary error variant for [`fs::read`]. pub fn read(source: io::Error, path: impl Into) -> Self { Self::Read { source, path: path.into() } } - /// Returns the complementary error variant for [`std::fs::copy`]. + /// Returns the complementary error variant for [`fs::copy`]. pub fn copy(source: io::Error, from: impl Into, to: impl Into) -> Self { Self::Copy { source, from: from.into(), to: to.into() } } - /// Returns the complementary error variant for [`std::fs::read_link`]. + /// Returns the complementary error variant for [`fs::read_link`]. pub fn read_link(source: io::Error, path: impl Into) -> Self { Self::ReadLink { source, path: path.into() } } @@ -69,17 +72,17 @@ impl FsPathError { Self::CreateFile { source, path: path.into() } } - /// Returns the complementary error variant for [`std::fs::remove_file`]. + /// Returns the complementary error variant for [`fs::remove_file`]. pub fn remove_file(source: io::Error, path: impl Into) -> Self { Self::RemoveFile { source, path: path.into() } } - /// Returns the complementary error variant for [`std::fs::create_dir`]. + /// Returns the complementary error variant for [`fs::create_dir`]. pub fn create_dir(source: io::Error, path: impl Into) -> Self { Self::CreateDir { source, path: path.into() } } - /// Returns the complementary error variant for [`std::fs::remove_dir`]. + /// Returns the complementary error variant for [`fs::remove_dir`]. pub fn remove_dir(source: io::Error, path: impl Into) -> Self { Self::RemoveDir { source, path: path.into() } } diff --git a/crates/common/src/evm.rs b/crates/common/src/evm.rs index a0cc4d4b8..98355c890 100644 --- a/crates/common/src/evm.rs +++ b/crates/common/src/evm.rs @@ -117,13 +117,13 @@ pub struct EvmArgs { /// /// default value: 330 /// - /// See also --fork-url and https://docs.alchemy.com/reference/compute-units#what-are-cups-compute-units-per-second + /// See also --fork-url and #[arg(long, alias = "cups", value_name = "CUPS", help_heading = "Fork config")] pub compute_units_per_second: Option, /// Disables rate limiting for this node's provider. /// - /// See also --fork-url and https://docs.alchemy.com/reference/compute-units#what-are-cups-compute-units-per-second + /// See also --fork-url and #[arg( long, value_name = "NO_RATE_LIMITS", diff --git a/crates/common/src/fmt/dynamic.rs b/crates/common/src/fmt/dynamic.rs index 2e30f2f7e..19330d474 100644 --- a/crates/common/src/fmt/dynamic.rs +++ b/crates/common/src/fmt/dynamic.rs @@ -85,7 +85,7 @@ impl DynValueFormatter { } } -/// Wrapper that implements [`Display`] for a [`DynSolValue`]. +/// Wrapper that implements [`Display`](fmt::Display) for a [`DynSolValue`]. struct DynValueDisplay<'a> { /// The value to display. value: &'a DynSolValue, @@ -94,7 +94,7 @@ struct DynValueDisplay<'a> { } impl<'a> DynValueDisplay<'a> { - /// Creates a new [`Display`] wrapper for the given value. + /// Creates a new [`Display`](fmt::Display) wrapper for the given value. #[inline] fn new(value: &'a DynSolValue, raw: bool) -> Self { Self { value, formatter: DynValueFormatter { raw } } diff --git a/crates/common/src/provider/tower.rs b/crates/common/src/provider/tower.rs index 2f97d2f8f..bb64ee8ab 100644 --- a/crates/common/src/provider/tower.rs +++ b/crates/common/src/provider/tower.rs @@ -31,7 +31,7 @@ pub struct RetryBackoffLayer { } impl RetryBackoffLayer { - /// Creates a new [RetryWithPolicyLayer] with the given parameters + /// Creates a new retry layer with the given parameters. pub fn new( max_rate_limit_retries: u32, max_timeout_retries: u32, diff --git a/crates/common/src/selectors.rs b/crates/common/src/selectors.rs index bf820a412..a051db1f0 100644 --- a/crates/common/src/selectors.rs +++ b/crates/common/src/selectors.rs @@ -19,15 +19,15 @@ use std::{ const SELECTOR_LOOKUP_URL: &str = "https://api.openchain.xyz/signature-database/v1/lookup"; const SELECTOR_IMPORT_URL: &str = "https://api.openchain.xyz/signature-database/v1/import"; -/// The standard request timeout for API requests +/// The standard request timeout for API requests. const REQ_TIMEOUT: Duration = Duration::from_secs(15); -/// How many request can time out before we decide this is a spurious connection +/// How many request can time out before we decide this is a spurious connection. const MAX_TIMEDOUT_REQ: usize = 4usize; -/// A client that can request API data from `https://api.openchain.xyz` +/// A client that can request API data from OpenChain. #[derive(Clone, Debug)] -pub struct SignEthClient { +pub struct OpenChainClient { inner: reqwest::Client, /// Whether the connection is spurious, or API is down spurious_connection: Arc, @@ -37,7 +37,7 @@ pub struct SignEthClient { max_timedout_requests: usize, } -impl SignEthClient { +impl OpenChainClient { /// Creates a new client with default settings pub fn new() -> reqwest::Result { let inner = reqwest::Client::builder() @@ -113,7 +113,7 @@ impl SignEthClient { } if is_connectivity_err(err) { - warn!("spurious network detected for https://api.openchain.xyz"); + warn!("spurious network detected for OpenChain"); let previous = self.timedout_requests.fetch_add(1, Ordering::SeqCst); if previous >= self.max_timedout_requests { self.set_spurious(); @@ -138,7 +138,7 @@ impl SignEthClient { Ok(()) } - /// Decodes the given function or event selector using https://api.openchain.xyz + /// Decodes the given function or event selector using OpenChain pub async fn decode_selector( &self, selector: &str, @@ -151,7 +151,7 @@ impl SignEthClient { .ok_or_else(|| eyre::eyre!("No signature found")) } - /// Decodes the given function or event selectors using https://api.openchain.xyz + /// Decodes the given function or event selectors using OpenChain pub async fn decode_selectors( &self, selector_type: SelectorType, @@ -201,8 +201,6 @@ impl SignEthClient { result: ApiResult, } - // using openchain.xyz signature database over 4byte - // see https://github.com/foundry-rs/foundry/issues/1672 let url = format!( "{SELECTOR_LOOKUP_URL}?{ltype}={selectors_str}", ltype = match selector_type { @@ -238,7 +236,7 @@ impl SignEthClient { .collect()) } - /// Fetches a function signature given the selector using https://api.openchain.xyz + /// Fetches a function signature given the selector using OpenChain pub async fn decode_function_selector(&self, selector: &str) -> eyre::Result> { self.decode_selector(selector, SelectorType::Function).await } @@ -263,7 +261,7 @@ impl SignEthClient { .collect::>()) } - /// Fetches an event signature given the 32 byte topic using https://api.openchain.xyz + /// Fetches an event signature given the 32 byte topic using OpenChain pub async fn decode_event_topic(&self, topic: &str) -> eyre::Result> { self.decode_selector(topic, SelectorType::Event).await } @@ -271,10 +269,10 @@ impl SignEthClient { /// Pretty print calldata and if available, fetch possible function signatures /// /// ```no_run - /// use foundry_common::selectors::SignEthClient; + /// use foundry_common::selectors::OpenChainClient; /// /// # async fn foo() -> eyre::Result<()> { - /// let pretty_data = SignEthClient::new()? + /// let pretty_data = OpenChainClient::new()? /// .pretty_calldata( /// "0x70a08231000000000000000000000000d0074f4e6490ae3f888d1d4f7e3e43326bd3f0f5" /// .to_string(), @@ -320,7 +318,7 @@ impl SignEthClient { Ok(possible_info) } - /// uploads selectors to https://api.openchain.xyz using the given data + /// uploads selectors to OpenChain using the given data pub async fn import_selectors( &self, data: SelectorImportData, @@ -397,44 +395,47 @@ impl fmt::Display for PossibleSigs { } } +/// The type of selector fetched from OpenChain. #[derive(Clone, Copy)] pub enum SelectorType { + /// A function selector. Function, + /// An event selector. Event, } -/// Decodes the given function or event selector using https://api.openchain.xyz +/// Decodes the given function or event selector using OpenChain. pub async fn decode_selector( selector_type: SelectorType, selector: &str, ) -> eyre::Result> { - SignEthClient::new()?.decode_selector(selector, selector_type).await + OpenChainClient::new()?.decode_selector(selector, selector_type).await } -/// Decodes the given function or event selectors using https://api.openchain.xyz +/// Decodes the given function or event selectors using OpenChain. pub async fn decode_selectors( selector_type: SelectorType, selectors: impl IntoIterator>, ) -> eyre::Result>>> { - SignEthClient::new()?.decode_selectors(selector_type, selectors).await + OpenChainClient::new()?.decode_selectors(selector_type, selectors).await } -/// Fetches a function signature given the selector https://api.openchain.xyz +/// Fetches a function signature given the selector using OpenChain. pub async fn decode_function_selector(selector: &str) -> eyre::Result> { - SignEthClient::new()?.decode_function_selector(selector).await + OpenChainClient::new()?.decode_function_selector(selector).await } -/// Fetches all possible signatures and attempts to abi decode the calldata +/// Fetches all possible signatures and attempts to abi decode the calldata using OpenChain. pub async fn decode_calldata(calldata: &str) -> eyre::Result> { - SignEthClient::new()?.decode_calldata(calldata).await + OpenChainClient::new()?.decode_calldata(calldata).await } -/// Fetches an event signature given the 32 byte topic using https://api.openchain.xyz +/// Fetches an event signature given the 32 byte topic using OpenChain. pub async fn decode_event_topic(topic: &str) -> eyre::Result> { - SignEthClient::new()?.decode_event_topic(topic).await + OpenChainClient::new()?.decode_event_topic(topic).await } -/// Pretty print calldata and if available, fetch possible function signatures +/// Pretty print calldata and if available, fetch possible function signatures. /// /// ```no_run /// use foundry_common::selectors::pretty_calldata; @@ -453,7 +454,7 @@ pub async fn pretty_calldata( calldata: impl AsRef, offline: bool, ) -> eyre::Result { - SignEthClient::new()?.pretty_calldata(calldata, offline).await + OpenChainClient::new()?.pretty_calldata(calldata, offline).await } #[derive(Debug, Default, PartialEq, Eq, Serialize)] @@ -519,13 +520,13 @@ impl SelectorImportResponse { .iter() .for_each(|(k, v)| println!("Duplicated: Event {k}: {v}")); - println!("Selectors successfully uploaded to https://api.openchain.xyz"); + println!("Selectors successfully uploaded to OpenChain"); } } -/// uploads selectors to https://api.openchain.xyz using the given data +/// uploads selectors to OpenChain using the given data pub async fn import_selectors(data: SelectorImportData) -> eyre::Result { - SignEthClient::new()?.import_selectors(data).await + OpenChainClient::new()?.import_selectors(data).await } #[derive(Debug, Default, PartialEq)] diff --git a/crates/common/src/serde_helpers.rs b/crates/common/src/serde_helpers.rs index 776d049c4..a7cc8bee1 100644 --- a/crates/common/src/serde_helpers.rs +++ b/crates/common/src/serde_helpers.rs @@ -82,11 +82,11 @@ where #[derive(Deserialize)] #[serde(untagged)] pub enum NumericSeq { - /// Single parameter sequence (e.g [1]) + /// Single parameter sequence (e.g `[1]`). Seq([Numeric; 1]), - /// U256 + /// `U256`. U256(U256), - /// Native u64 + /// Native `u64`. Num(u64), } diff --git a/crates/common/src/shell.rs b/crates/common/src/shell.rs index ac5ff1b96..898b154fa 100644 --- a/crates/common/src/shell.rs +++ b/crates/common/src/shell.rs @@ -103,14 +103,14 @@ impl Shell { /// Write a fragment to stdout /// - /// Caller is responsible for deciding whether [`Shell::verbosity`] is affects output. + /// Caller is responsible for deciding whether [`Shell`] verbosity affects output. pub fn write_stdout(&self, fragment: impl fmt::Display) -> io::Result<()> { self.output.write_stdout(fragment) } /// Write a fragment to stderr /// - /// Caller is responsible for deciding whether [`Shell::verbosity`] is affects output. + /// Caller is responsible for deciding whether [`Shell`] verbosity affects output. pub fn write_stderr(&self, fragment: impl fmt::Display) -> io::Result<()> { self.output.write_stderr(fragment) } diff --git a/crates/common/src/term.rs b/crates/common/src/term.rs index 4057a504f..3fcc8f7fb 100644 --- a/crates/common/src/term.rs +++ b/crates/common/src/term.rs @@ -166,12 +166,10 @@ impl Reporter for SpinnerReporter { )); } - /// Invoked before a new [`Solc`] bin is installed fn on_solc_installation_start(&self, version: &Version) { self.send_msg(format!("Installing Solc version {version}")); } - /// Invoked before a new [`Solc`] bin was successfully installed fn on_solc_installation_success(&self, version: &Version) { self.send_msg(format!("Successfully installed Solc {version}")); } diff --git a/crates/config/src/endpoints.rs b/crates/config/src/endpoints.rs index f29e72545..74157b0e9 100644 --- a/crates/config/src/endpoints.rs +++ b/crates/config/src/endpoints.rs @@ -233,7 +233,7 @@ pub struct RpcEndpointConfig { } impl RpcEndpointConfig { - /// Returns the url this type holds, see [RpcEndpoints::resolve()] + /// Returns the url this type holds, see [`RpcEndpoint::resolve`] pub fn resolve(self) -> Result { self.endpoint.resolve() } @@ -329,7 +329,7 @@ impl Default for RpcEndpointConfig { } } -/// Container type for _resolved_ endpoints, see [RpcEndpoints::resolve_all()] +/// Container type for _resolved_ endpoints, see [`RpcEndpoint::resolve`]. #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct ResolvedRpcEndpoints { /// contains all named endpoints and their URL or an error if we failed to resolve the env var diff --git a/crates/config/src/error.rs b/crates/config/src/error.rs index fe193171a..0f6dae9bf 100644 --- a/crates/config/src/error.rs +++ b/crates/config/src/error.rs @@ -110,7 +110,7 @@ pub enum SolidityErrorCode { ContractExceeds24576Bytes, /// Warning after shanghai if init code size exceeds 49152 bytes ContractInitCodeSizeExceeds49152Bytes, - /// Warning that Function state mutability can be restricted to [view,pure] + /// Warning that Function state mutability can be restricted to view/pure. FunctionStateMutabilityCanBeRestricted, /// Warning: Unused local variable UnusedLocalVariable, diff --git a/crates/config/src/etherscan.rs b/crates/config/src/etherscan.rs index 8863ad059..c4f3fe700 100644 --- a/crates/config/src/etherscan.rs +++ b/crates/config/src/etherscan.rs @@ -111,7 +111,7 @@ impl DerefMut for EtherscanConfigs { } } -/// Container type for _resolved_ etherscan keys, see [EtherscanConfigs::resolve_all()] +/// Container type for _resolved_ etherscan keys, see [`EtherscanConfigs::resolved`]. #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct ResolvedEtherscanConfigs { /// contains all named `ResolvedEtherscanConfig` or an error if we failed to resolve the env diff --git a/crates/config/src/fix.rs b/crates/config/src/fix.rs index a49097c8f..73d52d979 100644 --- a/crates/config/src/fix.rs +++ b/crates/config/src/fix.rs @@ -1,4 +1,4 @@ -//! Helpers to automatically fix configuration warnings +//! Helpers to automatically fix configuration warnings. use crate::{Config, Warning}; use figment::providers::Env; @@ -51,7 +51,7 @@ impl DerefMut for TomlFile { } } -/// The error emitted when failing to insert a profile into [profile] +/// The error emitted when failing to insert into a profile. #[derive(Debug)] struct InsertProfileError { pub message: String, diff --git a/crates/config/src/inline/error.rs b/crates/config/src/inline/error.rs index e998a2156..ddcb6a61b 100644 --- a/crates/config/src/inline/error.rs +++ b/crates/config/src/inline/error.rs @@ -1,4 +1,4 @@ -/// Errors returned by the [`InlineConfigParser`] trait. +/// 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. @@ -16,8 +16,7 @@ pub enum InlineConfigParserError { ParseBool(String, String), } -/// Wrapper error struct that catches config parsing -/// errors [`InlineConfigParserError`], enriching them with context information +/// 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}")] diff --git a/crates/config/src/inline/mod.rs b/crates/config/src/inline/mod.rs index f2222901f..adc67424f 100644 --- a/crates/config/src/inline/mod.rs +++ b/crates/config/src/inline/mod.rs @@ -1,13 +1,15 @@ use crate::Config; -pub use conf_parser::{parse_config_bool, parse_config_u32, validate_profiles, InlineConfigParser}; -pub use error::{InlineConfigError, InlineConfigParserError}; -pub use natspec::NatSpec; use once_cell::sync::Lazy; use std::collections::HashMap; mod conf_parser; +pub use conf_parser::*; + mod error; +pub use error::*; + mod natspec; +pub use natspec::*; pub const INLINE_CONFIG_FUZZ_KEY: &str = "fuzz"; pub const INLINE_CONFIG_INVARIANT_KEY: &str = "invariant"; diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 6d64e3625..2d658894b 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -47,17 +47,19 @@ use std::{ str::FromStr, }; -// Macros useful for creating a figment. mod macros; -// Utilities for making it easier to handle tests. pub mod utils; -pub use crate::utils::*; +pub use utils::*; mod endpoints; pub use endpoints::{ResolvedRpcEndpoints, RpcEndpoint, RpcEndpoints}; mod etherscan; +use etherscan::{ + EtherscanConfigError, EtherscanConfigs, EtherscanEnvProvider, ResolvedEtherscanConfig, +}; + mod resolve; pub use resolve::UnresolvedEnvVarError; @@ -68,9 +70,11 @@ pub mod fmt; pub use fmt::FormatterConfig; pub mod fs_permissions; -pub use crate::fs_permissions::FsPermissions; +pub use fs_permissions::FsPermissions; +use fs_permissions::PathPermission; pub mod error; +use error::ExtractConfigError; pub use error::SolidityErrorCode; pub mod doc; @@ -79,36 +83,25 @@ pub use doc::DocConfig; mod warning; pub use warning::*; -// helpers for fixing configuration warnings pub mod fix; // reexport so cli types can implement `figment::Provider` to easily merge compiler arguments pub use alloy_chains::{Chain, NamedChain}; pub use figment; -/// config providers pub mod providers; +use providers::{remappings::RemappingsProvider, FallbackProfileProvider, WarningsProvider}; -/// compilers supported by foundry -pub mod language; +mod language; pub use language::Language; -use crate::{ - error::ExtractConfigError, - etherscan::{EtherscanConfigError, EtherscanConfigs, ResolvedEtherscanConfig}, -}; -use providers::*; - mod fuzz; pub use fuzz::{FuzzConfig, FuzzDictionaryConfig}; mod invariant; -use crate::fs_permissions::PathPermission; pub use invariant::InvariantConfig; -use providers::remappings::RemappingsProvider; mod inline; -use crate::etherscan::EtherscanEnvProvider; pub use inline::{validate_profiles, InlineConfig, InlineConfigError, InlineConfigParser, NatSpec}; /// Foundry configuration @@ -193,7 +186,7 @@ pub struct Config { /// auto-detection. /// /// **Note** for backwards compatibility reasons this also accepts solc_version from the toml - /// file, see [`BackwardsCompatProvider`] + /// file, see `BackwardsCompatTomlProvider`. pub solc: Option, /// The Vyper instance to use if any. pub vyper: Option, @@ -369,8 +362,7 @@ pub struct Config { /// Whether to compile in sparse mode /// /// If this option is enabled, only the required contracts/files will be selected to be - /// included in solc's output selection, see also - /// [OutputSelection](foundry_compilers::artifacts::output_selection::OutputSelection) + /// included in solc's output selection, see also [`OutputSelection`]. pub sparse_mode: bool, /// Generates additional build info json files for every new build, containing the /// `CompilerInput` and `CompilerOutput`. @@ -952,7 +944,7 @@ impl Config { Ok(MultiCompilerSettings { solc: self.solc_settings()?, vyper: self.vyper_settings()? }) } - /// Returns all configured [`Remappings`] + /// Returns all configured remappings. /// /// **Note:** this will add an additional `/=` remapping here, see /// [Self::get_source_dir_remapping()] @@ -1491,77 +1483,77 @@ impl Config { toml::to_string_pretty(&toml::Value::Table(wrapping_table)) } - /// Returns the path to the `foundry.toml` of this `Config` + /// Returns the path to the `foundry.toml` of this `Config`. pub fn get_config_path(&self) -> PathBuf { self.__root.0.join(Config::FILE_NAME) } - /// Returns the selected profile + /// Returns the selected profile. /// - /// If the `FOUNDRY_PROFILE` env variable is not set, this returns the `DEFAULT_PROFILE` + /// If the `FOUNDRY_PROFILE` env variable is not set, this returns the `DEFAULT_PROFILE`. pub fn selected_profile() -> Profile { Profile::from_env_or("FOUNDRY_PROFILE", Config::DEFAULT_PROFILE) } - /// Returns the path to foundry's global toml file that's stored at `~/.foundry/foundry.toml` + /// Returns the path to foundry's global TOML file: `~/.foundry/foundry.toml`. pub fn foundry_dir_toml() -> Option { Self::foundry_dir().map(|p| p.join(Config::FILE_NAME)) } - /// Returns the path to foundry's config dir `~/.foundry/` + /// Returns the path to foundry's config dir: `~/.foundry/`. pub fn foundry_dir() -> Option { dirs_next::home_dir().map(|p| p.join(Config::FOUNDRY_DIR_NAME)) } - /// Returns the path to foundry's cache dir `~/.foundry/cache` + /// Returns the path to foundry's cache dir: `~/.foundry/cache`. pub fn foundry_cache_dir() -> Option { Self::foundry_dir().map(|p| p.join("cache")) } - /// Returns the path to foundry rpc cache dir `~/.foundry/cache/rpc` + /// Returns the path to foundry rpc cache dir: `~/.foundry/cache/rpc`. pub fn foundry_rpc_cache_dir() -> Option { Some(Self::foundry_cache_dir()?.join("rpc")) } - /// Returns the path to foundry chain's cache dir `~/.foundry/cache/rpc/` + /// Returns the path to foundry chain's cache dir: `~/.foundry/cache/rpc/` pub fn foundry_chain_cache_dir(chain_id: impl Into) -> Option { Some(Self::foundry_rpc_cache_dir()?.join(chain_id.into().to_string())) } - /// Returns the path to foundry's etherscan cache dir `~/.foundry/cache/etherscan` + /// Returns the path to foundry's etherscan cache dir: `~/.foundry/cache/etherscan`. pub fn foundry_etherscan_cache_dir() -> Option { Some(Self::foundry_cache_dir()?.join("etherscan")) } - /// Returns the path to foundry's keystores dir `~/.foundry/keystores` + /// Returns the path to foundry's keystores dir: `~/.foundry/keystores`. pub fn foundry_keystores_dir() -> Option { Some(Self::foundry_dir()?.join("keystores")) } - /// Returns the path to foundry's etherscan cache dir for `chain_id` + /// Returns the path to foundry's etherscan cache dir for `chain_id`: /// `~/.foundry/cache/etherscan/` pub fn foundry_etherscan_chain_cache_dir(chain_id: impl Into) -> Option { Some(Self::foundry_etherscan_cache_dir()?.join(chain_id.into().to_string())) } - /// Returns the path to the cache dir of the `block` on the `chain` - /// `~/.foundry/cache/rpc// + /// Returns the path to the cache dir of the `block` on the `chain`: + /// `~/.foundry/cache/rpc//` pub fn foundry_block_cache_dir(chain_id: impl Into, block: u64) -> Option { Some(Self::foundry_chain_cache_dir(chain_id)?.join(format!("{block}"))) } - /// Returns the path to the cache file of the `block` on the `chain` + /// Returns the path to the cache file of the `block` on the `chain`: /// `~/.foundry/cache/rpc///storage.json` pub fn foundry_block_cache_file(chain_id: impl Into, block: u64) -> Option { Some(Self::foundry_block_cache_dir(chain_id, block)?.join("storage.json")) } - #[doc = r#"Returns the path to `foundry`'s data directory inside the user's data directory - |Platform | Value | Example | - | ------- | ------------------------------------- | -------------------------------- | - | Linux | `$XDG_CONFIG_HOME` or `$HOME`/.config/foundry | /home/alice/.config/foundry| - | macOS | `$HOME`/Library/Application Support/foundry | /Users/Alice/Library/Application Support/foundry | - | Windows | `{FOLDERID_RoamingAppData}/foundry` | C:\Users\Alice\AppData\Roaming/foundry | - "#] + /// Returns the path to `foundry`'s data directory inside the user's data directory. + /// + /// | Platform | Value | Example | + /// | ------- | --------------------------------------------- | ------------------------------------------------ | + /// | Linux | `$XDG_CONFIG_HOME` or `$HOME`/.config/foundry | /home/alice/.config/foundry | + /// | macOS | `$HOME`/Library/Application Support/foundry | /Users/Alice/Library/Application Support/foundry | + /// | Windows | `{FOLDERID_RoamingAppData}/foundry` | C:\Users\Alice\AppData\Roaming/foundry | pub fn data_dir() -> eyre::Result { let path = dirs_next::data_dir().wrap_err("Failed to find data directory")?.join("foundry"); std::fs::create_dir_all(&path).wrap_err("Failed to create module directory")?; @@ -1573,7 +1565,7 @@ impl Config { /// and the first hit is used. /// /// If this search comes up empty, then it checks if a global `foundry.toml` exists at - /// `~/.foundry/foundry.tol`, see [`Self::foundry_dir_toml()`] + /// `~/.foundry/foundry.toml`, see [`Self::foundry_dir_toml`]. pub fn find_config_file() -> Option { fn find(path: &Path) -> Option { if path.is_absolute() { @@ -1596,7 +1588,7 @@ impl Config { .or_else(|| Self::foundry_dir_toml().filter(|p| p.exists())) } - /// Clears the foundry cache + /// Clears the foundry cache. pub fn clean_foundry_cache() -> eyre::Result<()> { if let Some(cache_dir) = Config::foundry_cache_dir() { let path = cache_dir.as_path(); @@ -1608,7 +1600,7 @@ impl Config { Ok(()) } - /// Clears the foundry cache for `chain` + /// Clears the foundry cache for `chain`. pub fn clean_foundry_chain_cache(chain: Chain) -> eyre::Result<()> { if let Some(cache_dir) = Config::foundry_chain_cache_dir(chain) { let path = cache_dir.as_path(); @@ -1620,7 +1612,7 @@ impl Config { Ok(()) } - /// Clears the foundry cache for `chain` and `block` + /// Clears the foundry cache for `chain` and `block`. pub fn clean_foundry_block_cache(chain: Chain, block: u64) -> eyre::Result<()> { if let Some(cache_dir) = Config::foundry_block_cache_dir(chain, block) { let path = cache_dir.as_path(); @@ -1632,7 +1624,7 @@ impl Config { Ok(()) } - /// Clears the foundry etherscan cache + /// Clears the foundry etherscan cache. pub fn clean_foundry_etherscan_cache() -> eyre::Result<()> { if let Some(cache_dir) = Config::foundry_etherscan_cache_dir() { let path = cache_dir.as_path(); @@ -1644,7 +1636,7 @@ impl Config { Ok(()) } - /// Clears the foundry etherscan cache for `chain` + /// Clears the foundry etherscan cache for `chain`. pub fn clean_foundry_etherscan_chain_cache(chain: Chain) -> eyre::Result<()> { if let Some(cache_dir) = Config::foundry_etherscan_chain_cache_dir(chain) { let path = cache_dir.as_path(); @@ -1656,7 +1648,7 @@ impl Config { Ok(()) } - /// List the data in the foundry cache + /// List the data in the foundry cache. pub fn list_foundry_cache() -> eyre::Result { if let Some(cache_dir) = Config::foundry_rpc_cache_dir() { let mut cache = Cache { chains: vec![] }; @@ -1679,7 +1671,7 @@ impl Config { } } - /// List the cached data for `chain` + /// List the cached data for `chain`. pub fn list_foundry_chain_cache(chain: Chain) -> eyre::Result { let block_explorer_data_size = match Config::foundry_etherscan_chain_cache_dir(chain) { Some(cache_dir) => Self::get_cached_block_explorer_data(&cache_dir)?, @@ -1701,7 +1693,7 @@ impl Config { } } - //The path provided to this function should point to a cached chain folder + /// The path provided to this function should point to a cached chain folder. fn get_cached_blocks(chain_path: &Path) -> eyre::Result> { let mut blocks = vec![]; if !chain_path.exists() { @@ -1724,7 +1716,7 @@ impl Config { Ok(blocks) } - //The path provided to this function should point to the etherscan cache for a chain + /// The path provided to this function should point to the etherscan cache for a chain. fn get_cached_block_explorer_data(chain_path: &Path) -> eyre::Result { if !chain_path.exists() { return Ok(0) diff --git a/crates/config/src/macros.rs b/crates/config/src/macros.rs index d7e206a97..d4f3a59ba 100644 --- a/crates/config/src/macros.rs +++ b/crates/config/src/macros.rs @@ -1,7 +1,8 @@ -/// A macro to implement converters from a type to [`Config`] and [`figment::Figment`] +/// A macro to implement converters from a type to [`Config`](crate::Config) and +/// [`figment::Figment`]. /// /// This can be used to remove some boilerplate code that's necessary to add additional layer(s) to -/// the [`Config`]'s default `Figment`. +/// the `Config`'s default `Figment`. /// /// `impl_figment` takes the default `Config` and merges additional `Provider`, therefore the /// targeted type, requires an implementation of `figment::Profile`. @@ -9,7 +10,7 @@ /// # Example /// /// Use `impl_figment` on a type with a `root: Option` field, which will be used for -/// [`Config::figment_with_root()`] +/// [`Config::figment_with_root()`](crate::Config::figment_with_root). /// /// ```rust /// use std::path::PathBuf; @@ -183,7 +184,8 @@ macro_rules! merge_impl_figment_convert { }; } -/// A macro to implement converters from a type to [`Config`] and [`figment::Figment`] +/// A macro to implement converters from a type to [`Config`](crate::Config) and +/// [`figment::Figment`]. /// /// Via [Config::to_figment](crate::Config::to_figment) and the /// [Cast](crate::FigmentProviders::Cast) profile. diff --git a/crates/config/src/providers/mod.rs b/crates/config/src/providers/mod.rs index 9dbbf38ea..d8fdbf438 100644 --- a/crates/config/src/providers/mod.rs +++ b/crates/config/src/providers/mod.rs @@ -1,3 +1,5 @@ +//! Config providers. + use crate::{Config, Warning, DEPRECATIONS}; use figment::{ value::{Dict, Map, Value}, diff --git a/crates/doc/src/parser/comment.rs b/crates/doc/src/parser/comment.rs index 55e69249d..0f8e91c79 100644 --- a/crates/doc/src/parser/comment.rs +++ b/crates/doc/src/parser/comment.rs @@ -3,7 +3,7 @@ use solang_parser::doccomment::DocCommentTag; use std::collections::HashMap; /// The natspec comment tag explaining the purpose of the comment. -/// See: https://docs.soliditylang.org/en/v0.8.17/natspec-format.html#tags. +/// See: . #[derive(Clone, Debug, PartialEq)] pub enum CommentTag { /// A title that should describe the contract/interface @@ -54,7 +54,8 @@ impl CommentTag { } /// The natspec documentation comment. -/// https://docs.soliditylang.org/en/v0.8.17/natspec-format.html +/// +/// Ref: #[derive(Clone, Debug, PartialEq)] pub struct Comment { /// The doc comment tag. diff --git a/crates/doc/src/preprocessor/inheritdoc.rs b/crates/doc/src/preprocessor/inheritdoc.rs index 583df72ba..8d29a64fc 100644 --- a/crates/doc/src/preprocessor/inheritdoc.rs +++ b/crates/doc/src/preprocessor/inheritdoc.rs @@ -5,7 +5,7 @@ use crate::{ use forge_fmt::solang_ext::SafeUnwrap; use std::collections::HashMap; -/// [ContractInheritance] preprocessor id. +/// [`Inheritdoc`] preprocessor ID. pub const INHERITDOC_ID: PreprocessorId = PreprocessorId("inheritdoc"); /// The inheritdoc preprocessor. diff --git a/crates/doc/src/writer/as_doc.rs b/crates/doc/src/writer/as_doc.rs index 68e4451fa..a21a59c11 100644 --- a/crates/doc/src/writer/as_doc.rs +++ b/crates/doc/src/writer/as_doc.rs @@ -10,7 +10,7 @@ use itertools::Itertools; use solang_parser::pt::{Base, FunctionDefinition}; use std::path::{Path, PathBuf}; -/// The result of [Asdoc::as_doc] method. +/// The result of [`AsDoc::as_doc`]. pub type AsDocResult = Result; /// A trait for formatting a parse unit as documentation. diff --git a/crates/evm/core/src/backend/in_memory_db.rs b/crates/evm/core/src/backend/in_memory_db.rs index a3cb3aab4..e8f371f61 100644 --- a/crates/evm/core/src/backend/in_memory_db.rs +++ b/crates/evm/core/src/backend/in_memory_db.rs @@ -1,4 +1,5 @@ -//! The in memory DB +//! In-memory database. + use crate::{backend::error::DatabaseError, snapshot::Snapshots}; use alloy_primitives::{Address, B256, U256}; use revm::{ @@ -7,14 +8,14 @@ use revm::{ Database, DatabaseCommit, }; -/// Type alias for an in memory database +/// Type alias for an in-memory database. /// -/// See `EmptyDBWrapper` +/// See [`EmptyDBWrapper`]. pub type FoundryEvmInMemoryDB = CacheDB; -/// In memory Database for anvil +/// In-memory [`Database`] for Anvil. /// -/// This acts like a wrapper type for [InMemoryDB] but is capable of applying snapshots +/// This acts like a wrapper type for [`FoundryEvmInMemoryDB`] but is capable of applying snapshots. #[derive(Debug)] pub struct MemDb { pub inner: FoundryEvmInMemoryDB, @@ -81,7 +82,7 @@ impl DatabaseCommit for MemDb { /// /// This will also _always_ return `Some(AccountInfo)`: /// -/// The [`Database`](revm::Database) implementation for `CacheDB` manages an `AccountState` for the +/// The [`Database`] implementation for `CacheDB` manages an `AccountState` for the /// `DbAccount`, this will be set to `AccountState::NotExisting` if the account does not exist yet. /// This is because there's a distinction between "non-existing" and "empty", /// see . diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index bb43c7243..f9c1becc9 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -54,7 +54,7 @@ pub type LocalForkId = U256; /// This is used for fast lookup type ForkLookupIndex = usize; -/// All accounts that will have persistent storage across fork swaps. See also [`clone_data()`] +/// All accounts that will have persistent storage across fork swaps. const DEFAULT_PERSISTENT_ACCOUNTS: [Address; 3] = [CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, CALLER]; @@ -392,8 +392,8 @@ pub struct Backend { /// The journaled_state to use to initialize new forks with /// /// The way [`revm::JournaledState`] works is, that it holds the "hot" accounts loaded from the - /// underlying `Database` that feeds the Account and State data ([`revm::AccountInfo`])to the - /// journaled_state so it can apply changes to the state while the evm executes. + /// underlying `Database` that feeds the Account and State data to the journaled_state so it + /// can apply changes to the state while the EVM executes. /// /// In a way the `JournaledState` is something like a cache that /// 1. check if account is already loaded (hot) @@ -404,7 +404,7 @@ pub struct Backend { /// ([`DatabaseExt::select_fork`]). /// /// This will be an empty `JournaledState`, which will be populated with persistent accounts, - /// See [`Self::update_fork_db()`] and [`clone_data()`]. + /// See [`Self::update_fork_db()`]. fork_init_journaled_state: JournaledState, /// The currently active fork database /// @@ -1572,8 +1572,6 @@ pub struct BackendInner { /// All accounts that should be kept persistent when switching forks. /// This means all accounts stored here _don't_ use a separate storage section on each fork /// instead the use only one that's persistent across fork swaps. - /// - /// See also [`clone_data()`] pub persistent_accounts: HashSet
, /// The configured spec id pub spec_id: SpecId, diff --git a/crates/evm/core/src/decode.rs b/crates/evm/core/src/decode.rs index 8b09316e3..fa3d099ab 100644 --- a/crates/evm/core/src/decode.rs +++ b/crates/evm/core/src/decode.rs @@ -105,7 +105,7 @@ impl RevertDecoder { /// Tries to decode an error message from the given revert bytes. /// - /// See [`decode_revert`] for more information. + /// See [`decode`](Self::decode) for more information. pub fn maybe_decode(&self, err: &[u8], status: Option) -> Option { if err.len() < SELECTOR_LEN { if let Some(status) = status { diff --git a/crates/evm/core/src/fork/cache.rs b/crates/evm/core/src/fork/cache.rs index fd768e3f7..9aea93585 100644 --- a/crates/evm/core/src/fork/cache.rs +++ b/crates/evm/core/src/fork/cache.rs @@ -30,9 +30,9 @@ pub struct BlockchainDb { } impl BlockchainDb { - /// Creates a new instance of the [BlockchainDb] + /// Creates a new instance of the [BlockchainDb]. /// - /// if a `cache_path` is provided it attempts to load a previously stored [JsonBlockCacheData] + /// If a `cache_path` is provided it attempts to load a previously stored [JsonBlockCacheData] /// and will try to use the cached entries it holds. /// /// This will return a new and empty [MemDb] if @@ -99,7 +99,7 @@ impl BlockchainDb { &self.db.block_hashes } - /// Returns the [revm::Env] related metadata + /// Returns the Env related metadata pub fn meta(&self) -> &Arc> { &self.meta } @@ -327,7 +327,7 @@ impl DatabaseCommit for MemDb { } } -/// A [BlockCacheDB] that stores the cached content in a json file +/// A DB that stores the cached content in a json file #[derive(Debug)] pub struct JsonBlockCacheDB { /// Where this cache file is stored. diff --git a/crates/evm/core/src/fork/mod.rs b/crates/evm/core/src/fork/mod.rs index 61dd7bf47..a6387b0bb 100644 --- a/crates/evm/core/src/fork/mod.rs +++ b/crates/evm/core/src/fork/mod.rs @@ -8,7 +8,10 @@ mod init; pub use init::environment; mod cache; -pub use cache::{BlockchainDb, BlockchainDbMeta, JsonBlockCacheDB, MemDb}; +pub use cache::{ + BlockchainDb, BlockchainDbMeta, FlushJsonBlockCacheDB, JsonBlockCacheDB, JsonBlockCacheData, + MemDb, StorageInfo, +}; pub mod database; diff --git a/crates/evm/core/src/opts.rs b/crates/evm/core/src/opts.rs index 2fe3d0390..4ff429902 100644 --- a/crates/evm/core/src/opts.rs +++ b/crates/evm/core/src/opts.rs @@ -141,14 +141,14 @@ impl EvmOpts { /// storage caching for the [CreateFork] will be enabled if /// - `fork_url` is present /// - `fork_block_number` is present - /// - [StorageCachingConfig] allows the `fork_url` + chain id pair + /// - `StorageCachingConfig` allows the `fork_url` + chain ID pair /// - storage is allowed (`no_storage_caching = false`) /// /// If all these criteria are met, then storage caching is enabled and storage info will be - /// written to [Config::foundry_cache_dir()]///storage.json + /// written to `///storage.json`. /// /// for `mainnet` and `--fork-block-number 14435000` on mac the corresponding storage cache will - /// be at `~/.foundry/cache/mainnet/14435000/storage.json` + /// be at `~/.foundry/cache/mainnet/14435000/storage.json`. pub fn get_fork(&self, config: &Config, env: revm::primitives::Env) -> Option { let url = self.fork_url.clone()?; let enable_caching = config.enable_caching(&url, env.cfg.chain_id); diff --git a/crates/evm/coverage/src/lib.rs b/crates/evm/coverage/src/lib.rs index 58bfb9cdb..15588296f 100644 --- a/crates/evm/coverage/src/lib.rs +++ b/crates/evm/coverage/src/lib.rs @@ -33,17 +33,17 @@ pub use inspector::CoverageCollector; /// "anchors"). A single coverage item may be referred to by multiple anchors. #[derive(Clone, Debug, Default)] pub struct CoverageReport { - /// A map of source IDs to the source path + /// A map of source IDs to the source path. pub source_paths: HashMap<(Version, usize), PathBuf>, - /// A map of source paths to source IDs + /// A map of source paths to source IDs. pub source_paths_to_ids: HashMap<(Version, PathBuf), usize>, /// All coverage items for the codebase, keyed by the compiler version. pub items: HashMap>, /// All item anchors for the codebase, keyed by their contract ID. pub anchors: HashMap, Vec)>, - /// All the bytecode hits for the codebase + /// All the bytecode hits for the codebase. pub bytecode_hits: HashMap, - /// The bytecode -> source mappings + /// The bytecode -> source mappings. pub source_maps: HashMap, } @@ -59,7 +59,7 @@ impl CoverageReport { self.source_paths_to_ids.get(&(version, path)).copied() } - /// Add the source maps + /// Add the source maps. pub fn add_source_maps( &mut self, source_maps: impl IntoIterator, @@ -67,12 +67,12 @@ impl CoverageReport { self.source_maps.extend(source_maps); } - /// Add coverage items to this report + /// Add coverage items to this report. pub fn add_items(&mut self, version: Version, items: impl IntoIterator) { self.items.entry(version).or_default().extend(items); } - /// Add anchors to this report + /// Add anchors to this report. pub fn add_anchors( &mut self, anchors: impl IntoIterator, Vec))>, @@ -80,7 +80,7 @@ impl CoverageReport { self.anchors.extend(anchors); } - /// Get coverage summaries by source file path + /// Get coverage summaries by source file path. pub fn summary_by_file(&self) -> impl Iterator { let mut summaries = BTreeMap::new(); @@ -99,7 +99,7 @@ impl CoverageReport { summaries.into_iter() } - /// Get coverage items by source file path + /// Get coverage items by source file path. pub fn items_by_source(&self) -> impl Iterator)> { let mut items_by_source: BTreeMap<_, Vec<_>> = BTreeMap::new(); @@ -117,10 +117,11 @@ impl CoverageReport { items_by_source.into_iter() } - /// Processes data from a [HitMap] and sets hit counts for coverage items in this coverage map. + /// Processes data from a [`HitMap`] and sets hit counts for coverage items in this coverage + /// map. /// /// This function should only be called *after* all the relevant sources have been processed and - /// added to the map (see [add_source]). + /// added to the map (see [`add_source`](Self::add_source)). pub fn add_hit_map( &mut self, contract_id: &ContractId, @@ -151,7 +152,7 @@ impl CoverageReport { } } -/// A collection of [HitMap]s +/// A collection of [`HitMap`]s. #[derive(Clone, Debug, Default)] pub struct HitMaps(pub HashMap); diff --git a/crates/evm/evm/src/executors/builder.rs b/crates/evm/evm/src/executors/builder.rs index 370c3bcf3..97cdc9218 100644 --- a/crates/evm/evm/src/executors/builder.rs +++ b/crates/evm/evm/src/executors/builder.rs @@ -8,12 +8,12 @@ use revm::primitives::{Env, EnvWithHandlerCfg, SpecId}; /// /// By default, the [`Executor`] will be configured with an empty [`InspectorStack`]. /// -/// [`Cheatcodes`]: super::inspector::Cheatcodes -/// [`InspectorStack`]: super::inspector::InspectorStack +/// [`Cheatcodes`]: super::Cheatcodes +/// [`InspectorStack`]: super::InspectorStack #[derive(Clone, Debug)] #[must_use = "builders do nothing unless you call `build` on them"] pub struct ExecutorBuilder { - /// The configuration used to build an [InspectorStack]. + /// The configuration used to build an `InspectorStack`. stack: InspectorStackBuilder, /// The gas limit. gas_limit: Option, @@ -45,7 +45,7 @@ impl ExecutorBuilder { self } - /// Sets the EVM spec to use + /// Sets the EVM spec to use. #[inline] pub fn spec(mut self, spec: SpecId) -> Self { self.spec_id = spec; @@ -53,8 +53,6 @@ impl ExecutorBuilder { } /// Sets the executor gas limit. - /// - /// See [Executor::gas_limit] for more info on why you might want to set this. #[inline] pub fn gas_limit(mut self, gas_limit: U256) -> Self { self.gas_limit = Some(gas_limit); diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index ba1e5277c..f8a1bd495 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -452,21 +452,21 @@ impl Executor { self.ensure_success(address, reverted, state_changeset, should_fail).unwrap_or_default() } - /// This is the same as [Self::is_success] but intended for outcomes of [Self::call_raw] used in - /// fuzzing and invariant testing. + /// This is the same as [`Self::is_success`] but intended for outcomes of [`Self::call_raw`] + /// used in fuzzing and invariant testing. /// /// ## Background /// - /// Executing and failure checking [`Executor::ensure_success`] are two steps, for ds-test + /// Executing and failure checking `Executor::ensure_success` are two steps, for ds-test /// legacy reasons failures can be stored in a global variables and needs to be called via a /// solidity call `failed()(bool)`. /// - /// For fuzz tests we’re using the `CowBackend` which is a Cow of the executor’s backend which - /// lazily clones the backend when it’s mutated via cheatcodes like `snapshot`. Snapshots - /// make it even more complicated because now we also need to keep track of that global - /// variable when we revert to a snapshot (because it is stored in state). Now, the problem - /// is that the `CowBackend` is dropped after every call, so we need to keep track of the - /// snapshot failure in the [`RawCallResult`] instead. + /// For fuzz tests we’re using the `CowBackend` which lazily clones the backend when it’s + /// mutated via cheatcodes like `snapshot`. Snapshots make it even more complicated because + /// now we also need to keep track of that global variable when we revert to a snapshot + /// (because it is stored in state). Now, the problem is that the `CowBackend` is dropped + /// after every call, so we need to keep track of the snapshot failure in the + /// [`RawCallResult`] instead. pub fn is_raw_call_success( &self, address: Address, diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 022586ae2..1eb21d6de 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -142,8 +142,6 @@ impl InspectorStackBuilder { } /// Builds the stack of inspectors to use when transacting/committing on the EVM. - /// - /// See also [`revm::Evm::inspect_ref`] and [`revm::Evm::commit_ref`]. pub fn build(self) -> InspectorStack { let Self { block, @@ -259,7 +257,7 @@ pub struct InspectorData { /// Used to adjust EVM state while in inner context. /// /// We need this to avoid breaking changes due to EVM behavior differences in isolated vs -/// non-isolated mode. For descriptions and workarounds for those changes see: https://github.com/foundry-rs/foundry/pull/7186#issuecomment-1959102195 +/// non-isolated mode. For descriptions and workarounds for those changes see: #[derive(Debug, Clone)] pub struct InnerContextData { /// The sender of the inner EVM context. diff --git a/crates/evm/fuzz/src/strategies/int.rs b/crates/evm/fuzz/src/strategies/int.rs index 3f8ff548b..3732de061 100644 --- a/crates/evm/fuzz/src/strategies/int.rs +++ b/crates/evm/fuzz/src/strategies/int.rs @@ -7,7 +7,6 @@ use proptest::{ use rand::Rng; /// Value tree for signed ints (up to int256). -/// This is very similar to [proptest::BinarySearch] pub struct IntValueTree { /// Lower base (by absolute value) lo: I256, diff --git a/crates/evm/fuzz/src/strategies/uint.rs b/crates/evm/fuzz/src/strategies/uint.rs index 77a080f8c..af133efa0 100644 --- a/crates/evm/fuzz/src/strategies/uint.rs +++ b/crates/evm/fuzz/src/strategies/uint.rs @@ -7,7 +7,6 @@ use proptest::{ use rand::Rng; /// Value tree for unsigned ints (up to uint256). -/// This is very similar to [proptest::BinarySearch] pub struct UintValueTree { /// Lower base lo: U256, diff --git a/crates/evm/traces/src/identifier/signatures.rs b/crates/evm/traces/src/identifier/signatures.rs index 976e9ea69..cd69cf947 100644 --- a/crates/evm/traces/src/identifier/signatures.rs +++ b/crates/evm/traces/src/identifier/signatures.rs @@ -2,7 +2,7 @@ use alloy_json_abi::{Event, Function}; use foundry_common::{ abi::{get_event, get_func}, fs, - selectors::{SelectorType, SignEthClient}, + selectors::{OpenChainClient, SelectorType}, }; use serde::{Deserialize, Serialize}; use std::{ @@ -31,7 +31,7 @@ pub struct SignaturesIdentifier { /// Selectors that were unavailable during the session. unavailable: HashSet, /// The API client to fetch signatures from - sign_eth_api: SignEthClient, + sign_eth_api: OpenChainClient, /// whether traces should be decoded via `sign_eth_api` offline: bool, } @@ -42,7 +42,7 @@ impl SignaturesIdentifier { cache_path: Option, offline: bool, ) -> eyre::Result { - let sign_eth_api = SignEthClient::new()?; + let sign_eth_api = OpenChainClient::new()?; let identifier = if let Some(cache_path) = cache_path { let path = cache_path.join("signatures"); diff --git a/crates/fmt/src/formatter.rs b/crates/fmt/src/formatter.rs index d10cf4b38..52a789034 100644 --- a/crates/fmt/src/formatter.rs +++ b/crates/fmt/src/formatter.rs @@ -393,7 +393,7 @@ impl<'a, W: Write> Formatter<'a, W> { Ok(out) } - /// Transform [Visitable] items to a list of chunks and then sort those chunks by [AttrSortKey] + /// Transform [Visitable] items to a list of chunks and then sort those chunks. fn items_to_chunks_sorted<'b>( &mut self, next_byte_offset: Option, @@ -585,8 +585,8 @@ impl<'a, W: Write> Formatter<'a, W> { Ok(false) } - /// Write a raw comment. This is like [`write_comment`] but won't do any formatting or worry - /// about whitespace behind the comment + /// Write a raw comment. This is like [`write_comment`](Self::write_comment) but won't do any + /// formatting or worry about whitespace behind the comment. fn write_raw_comment(&mut self, comment: &CommentWithMetadata) -> Result<()> { self.write_raw(&comment.comment)?; if comment.is_line() { @@ -1255,7 +1255,8 @@ impl<'a, W: Write> Formatter<'a, W> { /// Visit the yul string with an optional identifier. /// If the identifier is present, write the value in the format `:`. - /// Ref: https://docs.soliditylang.org/en/v0.8.15/yul.html#variable-declarations + /// + /// Ref: fn visit_yul_string_with_ident( &mut self, loc: Loc, diff --git a/crates/fmt/src/helpers.rs b/crates/fmt/src/helpers.rs index 1bed8069a..26fb48c40 100644 --- a/crates/fmt/src/helpers.rs +++ b/crates/fmt/src/helpers.rs @@ -10,15 +10,15 @@ use std::{fmt::Write, path::Path}; /// Result of parsing the source code #[derive(Debug)] pub struct Parsed<'a> { - /// The original source code + /// The original source code. pub src: &'a str, - /// The Parse Tree via [`solang`] + /// The parse tree. pub pt: SourceUnit, - /// Parsed comments + /// Parsed comments. pub comments: Comments, - /// Parsed inline config + /// Parsed inline config. pub inline_config: InlineConfig, - /// Invalid inline config items parsed + /// Invalid inline config items parsed. pub invalid_inline_config_items: Vec<(Loc, InvalidInlineConfigItem)>, } diff --git a/crates/fmt/src/inline_config.rs b/crates/fmt/src/inline_config.rs index 7593bb92c..9f0e54806 100644 --- a/crates/fmt/src/inline_config.rs +++ b/crates/fmt/src/inline_config.rs @@ -61,8 +61,10 @@ impl DisabledRange { /// An inline config. Keeps track of disabled ranges. /// /// This is a list of Inline Config items for locations in a source file. This is -/// usually acquired by parsing the comments for an `forgefmt:` items. See -/// [`Comments::parse_inline_config_items`] for details. +/// usually acquired by parsing the comments for an `forgefmt:` items. +/// +/// See [`Comments::parse_inline_config_items`](crate::Comments::parse_inline_config_items) for +/// details. #[derive(Debug, Default)] pub struct InlineConfig { disabled_ranges: Vec, diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index d9fe5640d..aaf866a11 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -144,8 +144,9 @@ impl SendTransactionsKind { } } -/// State after we have bundled all [TransactionWithMetadata] objects into a single -/// [ScriptSequenceKind] object containing one or more script sequences. +/// State after we have bundled all +/// [`TransactionWithMetadata`](crate::transaction::TransactionWithMetadata) objects into a single +/// [`ScriptSequenceKind`] object containing one or more script sequences. pub struct BundledState { pub args: ScriptArgs, pub script_config: ScriptConfig, diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index 7a785b6e6..3f9a49960 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -27,11 +27,11 @@ use std::{path::PathBuf, str::FromStr, sync::Arc}; /// Container for the compiled contracts. #[derive(Debug)] pub struct BuildData { - /// Root of the project + /// Root of the project. pub project_root: PathBuf, - /// Linker which can be used to link contracts, owns [ArtifactContracts] map. + /// The compiler output. pub output: ProjectCompileOutput, - /// Id of target contract artifact. + /// ID of target contract artifact. pub target: ArtifactId, } diff --git a/crates/script/src/sequence.rs b/crates/script/src/sequence.rs index f65314335..f95262aae 100644 --- a/crates/script/src/sequence.rs +++ b/crates/script/src/sequence.rs @@ -216,8 +216,8 @@ impl ScriptSequence { } /// Gets paths in the formats - /// ./broadcast/[contract_filename]/[chain_id]/[sig]-[timestamp].json and - /// ./cache/[contract_filename]/[chain_id]/[sig]-[timestamp].json + /// `./broadcast/[contract_filename]/[chain_id]/[sig]-[timestamp].json` and + /// `./cache/[contract_filename]/[chain_id]/[sig]-[timestamp].json`. pub fn get_paths( config: &Config, sig: &str, diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index a27506810..418dd036a 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -26,11 +26,11 @@ use std::{ sync::Arc, }; -/// Same as [ExecutedState], but also contains [ExecutionArtifacts] which are obtained from -/// [ScriptResult]. +/// Same as [ExecutedState](crate::execute::ExecutedState), but also contains [ExecutionArtifacts] +/// which are obtained from [ScriptResult]. /// -/// Can be either converted directly to [BundledState] via [PreSimulationState::resume] or driven to -/// it through [FilledTransactionsState]. +/// Can be either converted directly to [BundledState] or driven to it through +/// [FilledTransactionsState]. pub struct PreSimulationState { pub args: ScriptArgs, pub script_config: ScriptConfig, diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index 486367018..b49af6006 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -59,7 +59,8 @@ pub struct VerifyBytecodeArgs { #[clap(short = 'r', long, value_name = "RPC_URL", env = "ETH_RPC_URL")] pub rpc_url: Option, - /// Verfication Type: `full` or `partial`. Ref: https://docs.sourcify.dev/docs/full-vs-partial-match/ + /// Verfication Type: `full` or `partial`. + /// Ref: #[clap(long, default_value = "full", value_name = "TYPE")] pub verification_type: VerificationType, @@ -498,7 +499,8 @@ impl VerifyBytecodeArgs { } } -/// Enum to represent the type of verification: `full` or `partial`. Ref: https://docs.sourcify.dev/docs/full-vs-partial-match/ +/// Enum to represent the type of verification: `full` or `partial`. +/// Ref: #[derive(Debug, Clone, clap::ValueEnum, Default, PartialEq, Eq, Serialize, Deserialize, Copy)] pub enum VerificationType { #[default] diff --git a/crates/verify/src/etherscan/flatten.rs b/crates/verify/src/etherscan/flatten.rs index 1542f3a0e..fa888f922 100644 --- a/crates/verify/src/etherscan/flatten.rs +++ b/crates/verify/src/etherscan/flatten.rs @@ -57,7 +57,7 @@ impl EtherscanSourceProvider for EtherscanFlattenedSource { impl EtherscanFlattenedSource { /// Attempts to compile the flattened content locally with the compiler version. /// - /// This expects the completely flattened `content´ and will try to compile it using the + /// This expects the completely flattened content and will try to compile it using the /// provided compiler. If the compiler is missing it will be installed. /// /// # Errors From 66bea839570abf47cbc38f8e74eb8f9ef0d1c0df Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 5 Jun 2024 18:55:30 +0200 Subject: [PATCH 357/430] ci: bump softprops/action-gh-release (#8070) --- .github/workflows/release.yml | 480 +++++++++++++++++----------------- 1 file changed, 239 insertions(+), 241 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3db72dfcf..6c78fed2a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,268 +1,266 @@ name: release on: - push: - tags: - - "v*.*.*" - schedule: - - cron: "0 0 * * *" - workflow_dispatch: + push: + tags: + - "v*.*.*" + schedule: + - cron: "0 0 * * *" + workflow_dispatch: env: - CARGO_TERM_COLOR: always - IS_NIGHTLY: ${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }} + CARGO_TERM_COLOR: always + IS_NIGHTLY: ${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }} jobs: - prepare: - name: Prepare release - runs-on: ubuntu-latest - timeout-minutes: 30 - outputs: - tag_name: ${{ steps.release_info.outputs.tag_name }} - release_name: ${{ steps.release_info.outputs.release_name }} - changelog: ${{ steps.build_changelog.outputs.changelog }} - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 + prepare: + name: Prepare release + runs-on: ubuntu-latest + timeout-minutes: 30 + outputs: + tag_name: ${{ steps.release_info.outputs.tag_name }} + release_name: ${{ steps.release_info.outputs.release_name }} + changelog: ${{ steps.build_changelog.outputs.changelog }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 - - name: Compute release name and tag - id: release_info - run: | - if [[ $IS_NIGHTLY ]]; then - echo "tag_name=nightly-${GITHUB_SHA}" >> $GITHUB_OUTPUT - echo "release_name=Nightly ($(date '+%Y-%m-%d'))" >> $GITHUB_OUTPUT - else - echo "tag_name=${GITHUB_REF_NAME}" >> $GITHUB_OUTPUT - echo "release_name=${GITHUB_REF_NAME}" >> $GITHUB_OUTPUT - fi + - name: Compute release name and tag + id: release_info + run: | + if [[ $IS_NIGHTLY ]]; then + echo "tag_name=nightly-${GITHUB_SHA}" >> $GITHUB_OUTPUT + echo "release_name=Nightly ($(date '+%Y-%m-%d'))" >> $GITHUB_OUTPUT + else + echo "tag_name=${GITHUB_REF_NAME}" >> $GITHUB_OUTPUT + echo "release_name=${GITHUB_REF_NAME}" >> $GITHUB_OUTPUT + fi - # Creates a `nightly-SHA` tag for this specific nightly - # This tag is used for this specific nightly version's release - # which allows users to roll back. It is also used to build - # the changelog. - - name: Create build-specific nightly tag - if: ${{ env.IS_NIGHTLY }} - uses: actions/github-script@v7 - env: - TAG_NAME: ${{ steps.release_info.outputs.tag_name }} - with: - script: | - const createTag = require('./.github/scripts/create-tag.js') - await createTag({ github, context }, process.env.TAG_NAME) + # Creates a `nightly-SHA` tag for this specific nightly + # This tag is used for this specific nightly version's release + # which allows users to roll back. It is also used to build + # the changelog. + - name: Create build-specific nightly tag + if: ${{ env.IS_NIGHTLY }} + uses: actions/github-script@v7 + env: + TAG_NAME: ${{ steps.release_info.outputs.tag_name }} + with: + script: | + const createTag = require('./.github/scripts/create-tag.js') + await createTag({ github, context }, process.env.TAG_NAME) - - name: Build changelog - id: build_changelog - uses: mikepenz/release-changelog-builder-action@v4 - with: - configuration: "./.github/changelog.json" - fromTag: ${{ env.IS_NIGHTLY && 'nightly' || '' }} - toTag: ${{ steps.release_info.outputs.tag_name }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Build changelog + id: build_changelog + uses: mikepenz/release-changelog-builder-action@v4 + with: + configuration: "./.github/changelog.json" + fromTag: ${{ env.IS_NIGHTLY && 'nightly' || '' }} + toTag: ${{ steps.release_info.outputs.tag_name }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - release-docker: - name: Release Docker - uses: ./.github/workflows/docker-publish.yml + release-docker: + name: Release Docker + uses: ./.github/workflows/docker-publish.yml - release: - name: ${{ matrix.target }} (${{ matrix.runner }}) - runs-on: ${{ matrix.runner }} - timeout-minutes: 240 - needs: prepare - strategy: - fail-fast: false - matrix: - include: - # `runner`: GHA runner label - # `target`: Rust build target triple - # `platform` and `arch`: Used in tarball names - # `svm`: target platform to use for the Solc binary: https://github.com/roynalnaruto/svm-rs/blob/84cbe0ac705becabdc13168bae28a45ad2299749/svm-builds/build.rs#L4-L24 - - runner: Linux-20.04 - target: x86_64-unknown-linux-gnu - svm_target_platform: linux-amd64 - platform: linux - arch: amd64 - - runner: Linux-20.04 - target: aarch64-unknown-linux-gnu - svm_target_platform: linux-aarch64 - platform: linux - arch: arm64 - - runner: macos-12-large - target: x86_64-apple-darwin - svm_target_platform: macosx-amd64 - platform: darwin - arch: amd64 - - runner: macos-latest-large - target: aarch64-apple-darwin - svm_target_platform: macosx-aarch64 - platform: darwin - arch: arm64 - - runner: Windows - target: x86_64-pc-windows-msvc - svm_target_platform: windows-amd64 - platform: win32 - arch: amd64 - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - with: - targets: ${{ matrix.target }} - - uses: Swatinem/rust-cache@v2 - with: - key: ${{ matrix.target }} - cache-on-failure: true + release: + name: ${{ matrix.target }} (${{ matrix.runner }}) + runs-on: ${{ matrix.runner }} + timeout-minutes: 240 + needs: prepare + strategy: + fail-fast: false + matrix: + include: + # `runner`: GHA runner label + # `target`: Rust build target triple + # `platform` and `arch`: Used in tarball names + # `svm`: target platform to use for the Solc binary: https://github.com/roynalnaruto/svm-rs/blob/84cbe0ac705becabdc13168bae28a45ad2299749/svm-builds/build.rs#L4-L24 + - runner: Linux-20.04 + target: x86_64-unknown-linux-gnu + svm_target_platform: linux-amd64 + platform: linux + arch: amd64 + - runner: Linux-20.04 + target: aarch64-unknown-linux-gnu + svm_target_platform: linux-aarch64 + platform: linux + arch: arm64 + - runner: macos-12-large + target: x86_64-apple-darwin + svm_target_platform: macosx-amd64 + platform: darwin + arch: amd64 + - runner: macos-latest-large + target: aarch64-apple-darwin + svm_target_platform: macosx-aarch64 + platform: darwin + arch: arm64 + - runner: Windows + target: x86_64-pc-windows-msvc + svm_target_platform: windows-amd64 + platform: win32 + arch: amd64 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + - uses: Swatinem/rust-cache@v2 + with: + key: ${{ matrix.target }} + cache-on-failure: true - - name: Apple M1 setup - if: matrix.target == 'aarch64-apple-darwin' - run: | - echo "SDKROOT=$(xcrun -sdk macosx --show-sdk-path)" >> $GITHUB_ENV - echo "MACOSX_DEPLOYMENT_TARGET=$(xcrun -sdk macosx --show-sdk-platform-version)" >> $GITHUB_ENV + - name: Apple M1 setup + if: matrix.target == 'aarch64-apple-darwin' + run: | + echo "SDKROOT=$(xcrun -sdk macosx --show-sdk-path)" >> $GITHUB_ENV + echo "MACOSX_DEPLOYMENT_TARGET=$(xcrun -sdk macosx --show-sdk-platform-version)" >> $GITHUB_ENV - - name: Linux ARM setup - if: matrix.target == 'aarch64-unknown-linux-gnu' - run: | - sudo apt-get update -y - sudo apt-get install -y gcc-aarch64-linux-gnu - echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> $GITHUB_ENV + - name: Linux ARM setup + if: matrix.target == 'aarch64-unknown-linux-gnu' + run: | + sudo apt-get update -y + sudo apt-get install -y gcc-aarch64-linux-gnu + echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> $GITHUB_ENV - - name: Build binaries - env: - SVM_TARGET_PLATFORM: ${{ matrix.svm_target_platform }} - shell: bash - run: | - set -eo pipefail - target="${{ matrix.target }}" - flags=() + - name: Build binaries + env: + SVM_TARGET_PLATFORM: ${{ matrix.svm_target_platform }} + shell: bash + run: | + set -eo pipefail + target="${{ matrix.target }}" + flags=() - # `jemalloc` and `keccak-asm` are not supported on MSVC or aarch64 Linux. - if [[ "$target" != *msvc* && "$target" != "aarch64-unknown-linux-gnu" ]]; then - flags+=(--features asm-keccak,jemalloc) - fi + # `jemalloc` and `keccak-asm` are not supported on MSVC or aarch64 Linux. + if [[ "$target" != *msvc* && "$target" != "aarch64-unknown-linux-gnu" ]]; then + flags+=(--features asm-keccak,jemalloc) + fi - [[ "$target" == *windows* ]] && exe=".exe" + [[ "$target" == *windows* ]] && exe=".exe" - cargo build --release --bins --target "$target" "${flags[@]}" + cargo build --release --bins --target "$target" "${flags[@]}" - bins=(anvil cast chisel forge) - for name in "${bins[@]}"; do - bin=./target/$target/release/$name$exe - file "$bin" || true - ldd "$bin" || true - $bin --version || true - done + bins=(anvil cast chisel forge) + for name in "${bins[@]}"; do + bin=./target/$target/release/$name$exe + file "$bin" || true + ldd "$bin" || true + $bin --version || true + done - - name: Archive binaries - id: artifacts - env: - PLATFORM_NAME: ${{ matrix.platform }} - TARGET: ${{ matrix.target }} - ARCH: ${{ matrix.arch }} - VERSION_NAME: - ${{ (env.IS_NIGHTLY && 'nightly') || needs.prepare.outputs.tag_name }} - shell: bash - run: | - if [ "$PLATFORM_NAME" == "linux" ]; then - tar -czvf "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.tar.gz" -C ./target/${TARGET}/release forge cast anvil chisel - echo "file_name=foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.tar.gz" >> $GITHUB_OUTPUT - elif [ "$PLATFORM_NAME" == "darwin" ]; then - # We need to use gtar here otherwise the archive is corrupt. - # See: https://github.com/actions/virtual-environments/issues/2619 - gtar -czvf "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.tar.gz" -C ./target/${TARGET}/release forge cast anvil chisel - echo "file_name=foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.tar.gz" >> $GITHUB_OUTPUT - else - cd ./target/${TARGET}/release - 7z a -tzip "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.zip" forge.exe cast.exe anvil.exe chisel.exe - mv "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.zip" ../../../ - echo "file_name=foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.zip" >> $GITHUB_OUTPUT - fi + - name: Archive binaries + id: artifacts + env: + PLATFORM_NAME: ${{ matrix.platform }} + TARGET: ${{ matrix.target }} + ARCH: ${{ matrix.arch }} + VERSION_NAME: ${{ (env.IS_NIGHTLY && 'nightly') || needs.prepare.outputs.tag_name }} + shell: bash + run: | + if [ "$PLATFORM_NAME" == "linux" ]; then + tar -czvf "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.tar.gz" -C ./target/${TARGET}/release forge cast anvil chisel + echo "file_name=foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.tar.gz" >> $GITHUB_OUTPUT + elif [ "$PLATFORM_NAME" == "darwin" ]; then + # We need to use gtar here otherwise the archive is corrupt. + # See: https://github.com/actions/virtual-environments/issues/2619 + gtar -czvf "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.tar.gz" -C ./target/${TARGET}/release forge cast anvil chisel + echo "file_name=foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.tar.gz" >> $GITHUB_OUTPUT + else + cd ./target/${TARGET}/release + 7z a -tzip "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.zip" forge.exe cast.exe anvil.exe chisel.exe + mv "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.zip" ../../../ + echo "file_name=foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.zip" >> $GITHUB_OUTPUT + fi - - name: Build man page - id: man - if: matrix.target == 'x86_64-unknown-linux-gnu' - env: - PLATFORM_NAME: ${{ matrix.platform }} - TARGET: ${{ matrix.target }} - VERSION_NAME: - ${{ (env.IS_NIGHTLY && 'nightly') || needs.prepare.outputs.tag_name }} - shell: bash - run: | - sudo apt-get -y install help2man - help2man -N ./target/${TARGET}/release/forge > forge.1 - help2man -N ./target/${TARGET}/release/cast > cast.1 - help2man -N ./target/${TARGET}/release/anvil > anvil.1 - help2man -N ./target/${TARGET}/release/chisel > chisel.1 - gzip forge.1 - gzip cast.1 - gzip anvil.1 - gzip chisel.1 - tar -czvf "foundry_man_${VERSION_NAME}.tar.gz" forge.1.gz cast.1.gz anvil.1.gz chisel.1.gz - echo "foundry_man=foundry_man_${VERSION_NAME}.tar.gz" >> $GITHUB_OUTPUT + - name: Build man page + id: man + if: matrix.target == 'x86_64-unknown-linux-gnu' + env: + PLATFORM_NAME: ${{ matrix.platform }} + TARGET: ${{ matrix.target }} + VERSION_NAME: ${{ (env.IS_NIGHTLY && 'nightly') || needs.prepare.outputs.tag_name }} + shell: bash + run: | + sudo apt-get -y install help2man + help2man -N ./target/${TARGET}/release/forge > forge.1 + help2man -N ./target/${TARGET}/release/cast > cast.1 + help2man -N ./target/${TARGET}/release/anvil > anvil.1 + help2man -N ./target/${TARGET}/release/chisel > chisel.1 + gzip forge.1 + gzip cast.1 + gzip anvil.1 + gzip chisel.1 + tar -czvf "foundry_man_${VERSION_NAME}.tar.gz" forge.1.gz cast.1.gz anvil.1.gz chisel.1.gz + echo "foundry_man=foundry_man_${VERSION_NAME}.tar.gz" >> $GITHUB_OUTPUT - # Creates the release for this specific version - - name: Create release - uses: softprops/action-gh-release@v1 - with: - name: ${{ needs.prepare.outputs.release_name }} - tag_name: ${{ needs.prepare.outputs.tag_name }} - prerelease: ${{ env.IS_NIGHTLY }} - body: ${{ needs.prepare.outputs.changelog }} - files: | - ${{ steps.artifacts.outputs.file_name }} - ${{ steps.man.outputs.foundry_man }} + # Creates the release for this specific version + - name: Create release + uses: softprops/action-gh-release@v2 + with: + name: ${{ needs.prepare.outputs.release_name }} + tag_name: ${{ needs.prepare.outputs.tag_name }} + prerelease: ${{ env.IS_NIGHTLY }} + body: ${{ needs.prepare.outputs.changelog }} + files: | + ${{ steps.artifacts.outputs.file_name }} + ${{ steps.man.outputs.foundry_man }} - # If this is a nightly release, it also updates the release - # tagged `nightly` for compatibility with `foundryup` - - name: Update nightly release - if: ${{ env.IS_NIGHTLY }} - uses: softprops/action-gh-release@v1 - with: - name: "Nightly" - tag_name: "nightly" - prerelease: true - body: ${{ needs.prepare.outputs.changelog }} - files: | - ${{ steps.artifacts.outputs.file_name }} - ${{ steps.man.outputs.foundry_man }} + # If this is a nightly release, it also updates the release + # tagged `nightly` for compatibility with `foundryup` + - name: Update nightly release + if: ${{ env.IS_NIGHTLY }} + uses: softprops/action-gh-release@v2 + with: + name: "Nightly" + tag_name: "nightly" + prerelease: true + body: ${{ needs.prepare.outputs.changelog }} + files: | + ${{ steps.artifacts.outputs.file_name }} + ${{ steps.man.outputs.foundry_man }} - cleanup: - name: Release cleanup - runs-on: ubuntu-latest - timeout-minutes: 30 - needs: release - if: always() - steps: - - uses: actions/checkout@v4 + cleanup: + name: Release cleanup + runs-on: ubuntu-latest + timeout-minutes: 30 + needs: release + if: always() + steps: + - uses: actions/checkout@v4 - # Moves the `nightly` tag to `HEAD` - - name: Move nightly tag - if: ${{ env.IS_NIGHTLY }} - uses: actions/github-script@v7 - with: - script: | - const moveTag = require('./.github/scripts/move-tag.js') - await moveTag({ github, context }, 'nightly') + # Moves the `nightly` tag to `HEAD` + - name: Move nightly tag + if: ${{ env.IS_NIGHTLY }} + uses: actions/github-script@v7 + with: + script: | + const moveTag = require('./.github/scripts/move-tag.js') + await moveTag({ github, context }, 'nightly') - - name: Delete old nightlies - uses: actions/github-script@v7 - with: - script: | - const prunePrereleases = require('./.github/scripts/prune-prereleases.js') - await prunePrereleases({github, context}) + - name: Delete old nightlies + uses: actions/github-script@v7 + with: + script: | + const prunePrereleases = require('./.github/scripts/prune-prereleases.js') + await prunePrereleases({github, context}) - # If any of the jobs fail, this will create a high-priority issue to signal so. - issue: - name: Open an issue - runs-on: ubuntu-latest - needs: [prepare, release-docker, release, cleanup] - if: failure() - steps: - - uses: actions/checkout@v4 - - uses: JasonEtco/create-an-issue@v2 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - WORKFLOW_URL: | - ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - with: - update_existing: true - filename: .github/RELEASE_FAILURE_ISSUE_TEMPLATE.md + # If any of the jobs fail, this will create a high-priority issue to signal so. + issue: + name: Open an issue + runs-on: ubuntu-latest + needs: [prepare, release-docker, release, cleanup] + if: failure() + steps: + - uses: actions/checkout@v4 + - uses: JasonEtco/create-an-issue@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + WORKFLOW_URL: | + ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + with: + update_existing: true + filename: .github/RELEASE_FAILURE_ISSUE_TEMPLATE.md From 00854b602ef0e67379a2027ccc5d0aad553e5333 Mon Sep 17 00:00:00 2001 From: teddav Date: Wed, 5 Jun 2024 18:55:40 +0200 Subject: [PATCH 358/430] chore: update alloy and revm (#8057) * chore: update alloy and revm * rm * deny * update * patch instead --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- Cargo.lock | 60 +++++++++++------------ Cargo.toml | 58 ++++++++++++---------- crates/anvil/src/eth/backend/fork.rs | 7 ++- crates/anvil/src/eth/backend/mem/mod.rs | 1 - crates/anvil/src/eth/backend/mem/state.rs | 4 +- crates/anvil/tests/it/api.rs | 10 +--- crates/anvil/tests/it/traces.rs | 5 +- crates/cast/bin/cmd/estimate.rs | 2 +- crates/evm/core/src/backend/mod.rs | 8 +-- crates/evm/core/src/utils.rs | 2 +- crates/evm/evm/src/executors/mod.rs | 1 - deny.toml | 1 + 12 files changed, 81 insertions(+), 78 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 920d46dd1..5c4b9c57f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,7 +79,7 @@ dependencies = [ [[package]] name = "alloy-consensus" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" +source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" dependencies = [ "alloy-eips", "alloy-primitives", @@ -92,7 +92,7 @@ dependencies = [ [[package]] name = "alloy-contract" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" +source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -132,7 +132,7 @@ dependencies = [ [[package]] name = "alloy-eips" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" +source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -147,7 +147,7 @@ dependencies = [ [[package]] name = "alloy-genesis" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" +source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" dependencies = [ "alloy-primitives", "alloy-serde", @@ -170,7 +170,7 @@ dependencies = [ [[package]] name = "alloy-json-rpc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" +source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" dependencies = [ "alloy-primitives", "serde", @@ -182,7 +182,7 @@ dependencies = [ [[package]] name = "alloy-network" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" +source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" dependencies = [ "alloy-consensus", "alloy-eips", @@ -227,9 +227,10 @@ dependencies = [ [[package]] name = "alloy-provider" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" +source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" dependencies = [ "alloy-chains", + "alloy-consensus", "alloy-eips", "alloy-json-rpc", "alloy-network", @@ -261,7 +262,7 @@ dependencies = [ [[package]] name = "alloy-pubsub" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" +source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -301,7 +302,7 @@ dependencies = [ [[package]] name = "alloy-rpc-client" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" +source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -325,7 +326,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" +source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" dependencies = [ "alloy-consensus", "alloy-eips", @@ -343,7 +344,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" +source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" dependencies = [ "alloy-consensus", "alloy-eips", @@ -360,7 +361,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" +source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -372,7 +373,7 @@ dependencies = [ [[package]] name = "alloy-serde" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" +source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" dependencies = [ "alloy-primitives", "serde", @@ -382,7 +383,7 @@ dependencies = [ [[package]] name = "alloy-signer" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" +source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -397,7 +398,7 @@ dependencies = [ [[package]] name = "alloy-signer-aws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" +source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" dependencies = [ "alloy-consensus", "alloy-network", @@ -414,7 +415,7 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" +source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -433,7 +434,7 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" +source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" dependencies = [ "alloy-consensus", "alloy-network", @@ -449,7 +450,7 @@ dependencies = [ [[package]] name = "alloy-signer-wallet" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" +source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" dependencies = [ "alloy-consensus", "alloy-network", @@ -540,7 +541,7 @@ dependencies = [ [[package]] name = "alloy-transport" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" +source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" dependencies = [ "alloy-json-rpc", "base64 0.22.1", @@ -558,7 +559,7 @@ dependencies = [ [[package]] name = "alloy-transport-http" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" +source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -572,7 +573,7 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" +source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -592,7 +593,7 @@ dependencies = [ [[package]] name = "alloy-transport-ws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=5940871#5940871a103ed9332956959a9f2efcc2caa73bf0" +source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -6388,8 +6389,7 @@ dependencies = [ [[package]] name = "revm" version = "9.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a2c336f9921588e50871c00024feb51a521eca50ce6d01494bb9c50f837c8ed" +source = "git+https://github.com/bluealloy/revm.git?rev=a28a543#a28a5439b9cfb7494cbd670da10cbedcfe6c5854" dependencies = [ "auto_impl", "cfg-if", @@ -6403,7 +6403,7 @@ dependencies = [ [[package]] name = "revm-inspectors" version = "0.1.0" -source = "git+https://github.com/paradigmxyz/revm-inspectors?rev=0d3f1f4#0d3f1f4e41bfa2e1e56d37994494934edf0a11ef" +source = "git+https://github.com/paradigmxyz/revm-inspectors?rev=a278649#a2786496b2edcb06abed5a33682c532d070bf47c" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -6420,8 +6420,7 @@ dependencies = [ [[package]] name = "revm-interpreter" version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a58182c7454179826f9dad2ca577661963092ce9d0fd0c9d682c1e9215a72e70" +source = "git+https://github.com/bluealloy/revm.git?rev=a28a543#a28a5439b9cfb7494cbd670da10cbedcfe6c5854" dependencies = [ "revm-primitives", "serde", @@ -6430,13 +6429,13 @@ dependencies = [ [[package]] name = "revm-precompile" version = "7.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc8af9aa737eef0509a50d9f3cc1a631557a00ef2e70a3aa8a75d9ee0ed275bb" +source = "git+https://github.com/bluealloy/revm.git?rev=a28a543#a28a5439b9cfb7494cbd670da10cbedcfe6c5854" dependencies = [ "aurora-engine-modexp", "c-kzg", "k256", "once_cell", + "p256", "revm-primitives", "ripemd", "secp256k1", @@ -6447,8 +6446,7 @@ dependencies = [ [[package]] name = "revm-primitives" version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9bf5d465e64b697da6a111cb19e798b5b2ebb18e5faf2ad48e9e8d47c64add2" +source = "git+https://github.com/bluealloy/revm.git?rev=a28a543#a28a5439b9cfb7494cbd670da10cbedcfe6c5854" dependencies = [ "alloy-primitives", "auto_impl", diff --git a/Cargo.toml b/Cargo.toml index e3e35dd0d..d3809d541 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -156,9 +156,9 @@ foundry-compilers = { version = "0.6.0", default-features = false } ## revm # no default features to avoid c-kzg -revm = { version = "9", default-features = false } -revm-primitives = { version = "4", default-features = false } -revm-inspectors = { git = "https://github.com/paradigmxyz/revm-inspectors", rev = "0d3f1f4", features = [ +revm = { version = "9.0.0", default-features = false } +revm-primitives = { version = "4.0.0", default-features = false } +revm-inspectors = { git = "https://github.com/paradigmxyz/revm-inspectors", rev = "a278649", features = [ "serde", ] } @@ -166,29 +166,29 @@ revm-inspectors = { git = "https://github.com/paradigmxyz/revm-inspectors", rev ethers-contract-abigen = { version = "2.0.14", default-features = false } ## alloy -alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } -alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } -alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } -alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } -alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } -alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } -alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } -alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } -alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } -alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } -alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } -alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } -alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } -alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } -alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } -alloy-signer-wallet = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } -alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } -alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } -alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } -alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } -alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } -alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } -alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "5940871", default-features = false } +alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } +alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } +alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } +alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } +alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } +alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } +alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } +alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } +alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } +alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } +alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } +alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } +alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } +alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } +alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } +alloy-signer-wallet = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } +alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } +alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } +alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } +alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } +alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } +alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } +alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } alloy-primitives = { version = "0.7.1", features = ["getrandom", "rand"] } alloy-dyn-abi = "0.7.1" alloy-json-abi = "0.7.1" @@ -237,3 +237,9 @@ hyper = "1.0" reqwest = { version = "0.12", default-features = false } tower = "0.4" tower-http = "0.5" + +[patch.crates-io] +revm = { git = "https://github.com/bluealloy/revm.git", rev = "a28a543" } +revm-interpreter = { git = "https://github.com/bluealloy/revm.git", rev = "a28a543" } +revm-precompile = { git = "https://github.com/bluealloy/revm.git", rev = "a28a543" } +revm-primitives = { git = "https://github.com/bluealloy/revm.git", rev = "a28a543" } diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index 04d62f48b..a04e13440 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -2,7 +2,10 @@ use crate::eth::{backend::db::Db, error::BlockchainError}; use alloy_primitives::{Address, Bytes, StorageValue, B256, U256}; -use alloy_provider::{ext::DebugApi, Provider}; +use alloy_provider::{ + ext::{DebugApi, TraceApi}, + Provider, +}; use alloy_rpc_types::{ request::TransactionRequest, AccessListWithGasUsed, Block, BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, EIP1186AccountProofResponse, FeeHistory, @@ -183,7 +186,7 @@ impl ClientFork { block: Option, ) -> Result { let block = block.unwrap_or_default(); - let res = self.provider().estimate_gas(request).block_id(block.into()).await?; + let res = self.provider().estimate_gas(request).block(block.into()).await?; Ok(res) } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 81f4ea639..895e7a3f9 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -1162,7 +1162,6 @@ impl Backend { access_list: access_list.unwrap_or_default().flattened(), blob_hashes: blob_versioned_hashes.unwrap_or_default(), optimism: OptimismFields { enveloped_tx: Some(Bytes::new()), ..Default::default() }, - ..Default::default() }; if env.block.basefee == revm::primitives::U256::ZERO { diff --git a/crates/anvil/src/eth/backend/mem/state.rs b/crates/anvil/src/eth/backend/mem/state.rs index fb8be9b99..dd52eedfa 100644 --- a/crates/anvil/src/eth/backend/mem/state.rs +++ b/crates/anvil/src/eth/backend/mem/state.rs @@ -109,13 +109,13 @@ where *account, new_account_state .iter() - .map(|(key, value)| ((*key).into(), (*value))) + .map(|(key, value)| ((*key).into(), (*value).into())) .collect(), )?; } (None, Some(account_state_diff)) => { for (key, value) in account_state_diff.iter() { - cache_db.insert_account_storage(*account, (*key).into(), *value)?; + cache_db.insert_account_storage(*account, (*key).into(), (*value).into())?; } } }; diff --git a/crates/anvil/tests/it/api.rs b/crates/anvil/tests/it/api.rs index 8dd9b570c..0f1d45d6f 100644 --- a/crates/anvil/tests/it/api.rs +++ b/crates/anvil/tests/it/api.rs @@ -268,10 +268,7 @@ async fn can_call_with_state_override() { *simple_storage_contract.address(), AccountOverride { // The `lastSender` is in the first storage slot - state_diff: Some(HashMap::from([( - B256::ZERO, - U256::from_be_slice(B256::from(account.into_word()).as_slice()), - )])), + state_diff: Some(HashMap::from([(B256::ZERO, account.into_word())])), ..Default::default() }, )]); @@ -295,10 +292,7 @@ async fn can_call_with_state_override() { *simple_storage_contract.address(), AccountOverride { // The `lastSender` is in the first storage slot - state: Some(HashMap::from([( - B256::ZERO, - U256::from_be_slice(B256::from(account.into_word()).as_slice()), - )])), + state: Some(HashMap::from([(B256::ZERO, account.into_word())])), ..Default::default() }, )]); diff --git a/crates/anvil/tests/it/traces.rs b/crates/anvil/tests/it/traces.rs index 011e489bb..b06f7ce05 100644 --- a/crates/anvil/tests/it/traces.rs +++ b/crates/anvil/tests/it/traces.rs @@ -1,7 +1,10 @@ use crate::{fork::fork_config, utils::http_provider_with_signer}; use alloy_network::{EthereumSigner, TransactionBuilder}; use alloy_primitives::{hex, Address, Bytes, U256}; -use alloy_provider::{ext::DebugApi, Provider}; +use alloy_provider::{ + ext::{DebugApi, TraceApi}, + Provider, +}; use alloy_rpc_types::{BlockNumberOrTag, TransactionRequest, WithOtherFields}; use alloy_rpc_types_trace::{ geth::{GethDebugTracingCallOptions, GethTrace}, diff --git a/crates/cast/bin/cmd/estimate.rs b/crates/cast/bin/cmd/estimate.rs index c48ce2d43..d16cfac0f 100644 --- a/crates/cast/bin/cmd/estimate.rs +++ b/crates/cast/bin/cmd/estimate.rs @@ -104,7 +104,7 @@ impl EstimateArgs { .build_raw(sender) .await?; - let gas = provider.estimate_gas(&tx).block_id(block.unwrap_or_default()).await?; + let gas = provider.estimate_gas(&tx).block(block.unwrap_or_default()).await?; println!("{gas}"); Ok(()) } diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index f9c1becc9..1f6a61517 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -17,8 +17,8 @@ use revm::{ inspectors::NoOpInspector, precompile::{PrecompileSpecId, Precompiles}, primitives::{ - Account, AccountInfo, Bytecode, Env, EnvWithHandlerCfg, HashMap as Map, Log, - ResultAndState, SpecId, State, StorageSlot, TransactTo, KECCAK_EMPTY, + Account, AccountInfo, Bytecode, Env, EnvWithHandlerCfg, EvmState, EvmStorageSlot, + HashMap as Map, Log, ResultAndState, SpecId, TransactTo, KECCAK_EMPTY, }, Database, DatabaseCommit, JournaledState, }; @@ -1361,7 +1361,7 @@ impl DatabaseExt for Backend { let slot = U256::from_be_bytes(slot.0); ( slot, - StorageSlot::new_changed( + EvmStorageSlot::new_changed( state_acc .storage .get(&slot) @@ -1893,7 +1893,7 @@ fn commit_transaction>( } /// Helper method which updates data in the state with the data from the database. -pub fn update_state(state: &mut State, db: &mut DB) -> Result<(), DB::Error> { +pub fn update_state(state: &mut EvmState, db: &mut DB) -> Result<(), DB::Error> { for (addr, acc) in state.iter_mut() { acc.info = db.basic(*addr)?.unwrap_or_default(); for (key, val) in acc.storage.iter_mut() { diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index 230c068e6..d8114f209 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -16,7 +16,7 @@ use revm::{ }; use std::{cell::RefCell, rc::Rc, sync::Arc}; -pub use revm::primitives::State as StateChangeset; +pub use revm::primitives::EvmState as StateChangeset; /// Depending on the configured chain id and block number this should apply any specific changes /// diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index f8a1bd495..acb7cadea 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -808,7 +808,6 @@ fn convert_executed_result( &env.tx.data, env.tx.transact_to.is_create(), &env.tx.access_list, - &env.tx.eof_initcodes, ); let result = match &out { diff --git a/deny.toml b/deny.toml index d69e73391..cdde3ec0c 100644 --- a/deny.toml +++ b/deny.toml @@ -88,4 +88,5 @@ unknown-git = "deny" allow-git = [ "https://github.com/alloy-rs/alloy", "https://github.com/paradigmxyz/revm-inspectors", + "https://github.com/bluealloy/revm", ] From e764c316c96c8062e9bc52f78f7ee45ea359bc60 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 6 Jun 2024 13:30:37 +0200 Subject: [PATCH 359/430] perf(fuzz): use ahash for state (#8053) * perf(fuzz): use ahash for state * chore: clippy * fmt * Fix test --------- Co-authored-by: grandizzy --- Cargo.lock | 2 + Cargo.toml | 2 + crates/evm/evm/src/executors/mod.rs | 5 +- crates/evm/fuzz/Cargo.toml | 8 +- crates/evm/fuzz/src/inspector.rs | 3 +- crates/evm/fuzz/src/strategies/param.rs | 45 ++++----- crates/evm/fuzz/src/strategies/state.rs | 124 +++++++++++++----------- crates/forge/tests/cli/test_cmd.rs | 4 +- 8 files changed, 101 insertions(+), 92 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5c4b9c57f..8d4cc5d11 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,6 +45,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", + "getrandom", "once_cell", "version_check", "zerocopy", @@ -3629,6 +3630,7 @@ dependencies = [ name = "foundry-evm-fuzz" version = "0.2.0" dependencies = [ + "ahash", "alloy-dyn-abi", "alloy-json-abi", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index d3809d541..aca385035 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -97,6 +97,7 @@ scrypt.opt-level = 3 inherits = "dev" opt-level = 1 debug-assertions = false +overflow-checks = false strip = "debuginfo" panic = "abort" codegen-units = 16 @@ -200,6 +201,7 @@ alloy-rlp = "0.3.3" solang-parser = "=0.3.3" ## misc +ahash = "0.8" arrayvec = "0.7" base64 = "0.22" chrono = { version = "0.4", default-features = false, features = [ diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index acb7cadea..ceb269a53 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -50,7 +50,7 @@ pub use tracing::TracingExecutor; sol! { interface ITest { function setUp() external; - function failed() external view returns (bool); + function failed() external view returns (bool failed); } } @@ -515,8 +515,7 @@ impl Executor { let executor = Self::new(backend, self.env.clone(), self.inspector.clone(), self.gas_limit); let call = executor.call_sol(CALLER, address, &ITest::failedCall {}, U256::ZERO, None); - if let Ok(CallResult { raw: _, decoded_result: ITest::failedReturn { _0: failed } }) = - call + if let Ok(CallResult { raw: _, decoded_result: ITest::failedReturn { failed } }) = call { debug!(failed, "DSTest::failed()"); success = !failed; diff --git a/crates/evm/fuzz/Cargo.toml b/crates/evm/fuzz/Cargo.toml index 87d5d9ab3..4101c080f 100644 --- a/crates/evm/fuzz/Cargo.toml +++ b/crates/evm/fuzz/Cargo.toml @@ -23,7 +23,12 @@ foundry-evm-traces.workspace = true alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } alloy-json-abi.workspace = true -alloy-primitives = { workspace = true, features = ["serde", "getrandom", "arbitrary", "rlp"] } +alloy-primitives = { workspace = true, features = [ + "serde", + "getrandom", + "arbitrary", + "rlp", +] } revm = { workspace = true, features = [ "std", "serde", @@ -43,3 +48,4 @@ serde = "1" thiserror = "1" tracing = "0.1" indexmap.workspace = true +ahash.workspace = true diff --git a/crates/evm/fuzz/src/inspector.rs b/crates/evm/fuzz/src/inspector.rs index fe5c13456..052d87dac 100644 --- a/crates/evm/fuzz/src/inspector.rs +++ b/crates/evm/fuzz/src/inspector.rs @@ -1,5 +1,4 @@ use crate::{invariant::RandomCallGenerator, strategies::EvmFuzzState}; -use alloy_primitives::U256; use revm::{ interpreter::{CallInputs, CallOutcome, CallScheme, Interpreter}, Database, EvmContext, Inspector, @@ -62,7 +61,7 @@ impl Inspector for Fuzzer { impl Fuzzer { /// Collects `stack` and `memory` values into the fuzz dictionary. fn collect_data(&mut self, interpreter: &Interpreter) { - self.fuzz_state.collect_values(interpreter.stack().data().iter().map(U256::to_be_bytes)); + self.fuzz_state.collect_values(interpreter.stack().data().iter().copied().map(Into::into)); // TODO: disabled for now since it's flooding the dictionary // for index in 0..interpreter.shared_memory.len() / 32 { diff --git a/crates/evm/fuzz/src/strategies/param.rs b/crates/evm/fuzz/src/strategies/param.rs index df7fb28c0..0b64418d6 100644 --- a/crates/evm/fuzz/src/strategies/param.rs +++ b/crates/evm/fuzz/src/strategies/param.rs @@ -128,33 +128,23 @@ pub fn fuzz_param_from_state( // Generate a bias and use it to pick samples or non-persistent values (50 / 50). // Use `Index` instead of `Selector` when selecting a value to avoid iterating over the // entire dictionary. - ((0..100).prop_flat_map(Just), any::()).prop_map( - move |(bias, index)| { - let state = state.dictionary_read(); - let values = match bias { - x if x < 50 => { - if let Some(sample_values) = state.samples(param.clone()) { - sample_values - } else { - state.values() - } - } - _ => state.values(), - }; - let index = index.index(values.len()); - *values.iter().nth(index).unwrap() - }, - ) + any::<(bool, prop::sample::Index)>().prop_map(move |(bias, index)| { + let state = state.dictionary_read(); + let values = if bias { state.samples(¶m) } else { None } + .unwrap_or_else(|| state.values()) + .as_slice(); + values[index.index(values.len())] + }) }; // Convert the value based on the parameter type match *param { - DynSolType::Address => value() - .prop_map(move |value| DynSolValue::Address(Address::from_word(value.into()))) - .boxed(), + DynSolType::Address => { + value().prop_map(move |value| DynSolValue::Address(Address::from_word(value))).boxed() + } DynSolType::Function => value() .prop_map(move |value| { - DynSolValue::Function(alloy_primitives::Function::from_word(value.into())) + DynSolValue::Function(alloy_primitives::Function::from_word(value)) }) .boxed(), DynSolType::FixedBytes(size @ 1..=32) => value() @@ -172,19 +162,17 @@ pub fn fuzz_param_from_state( }) .boxed(), DynSolType::Bytes => { - value().prop_map(move |value| DynSolValue::Bytes(value.into())).boxed() + value().prop_map(move |value| DynSolValue::Bytes(value.0.into())).boxed() } DynSolType::Int(n @ 8..=256) => match n / 8 { 32 => value() - .prop_map(move |value| { - DynSolValue::Int(I256::from_raw(U256::from_be_bytes(value)), 256) - }) + .prop_map(move |value| DynSolValue::Int(I256::from_raw(value.into()), 256)) .boxed(), 1..=31 => value() .prop_map(move |value| { // Generate a uintN in the correct range, then shift it to the range of intN // by subtracting 2^(N-1) - let uint = U256::from_be_bytes(value) % U256::from(1).wrapping_shl(n); + let uint = U256::from_be_bytes(value.0) % U256::from(1).wrapping_shl(n); let max_int_plus1 = U256::from(1).wrapping_shl(n - 1); let num = I256::from_raw(uint.wrapping_sub(max_int_plus1)); DynSolValue::Int(num, n) @@ -194,11 +182,12 @@ pub fn fuzz_param_from_state( }, DynSolType::Uint(n @ 8..=256) => match n / 8 { 32 => value() - .prop_map(move |value| DynSolValue::Uint(U256::from_be_bytes(value), 256)) + .prop_map(move |value| DynSolValue::Uint(U256::from_be_bytes(value.0), 256)) .boxed(), 1..=31 => value() .prop_map(move |value| { - DynSolValue::Uint(U256::from_be_bytes(value) % U256::from(1).wrapping_shl(n), n) + let uint = U256::from_be_bytes(value.0) % U256::from(1).wrapping_shl(n); + DynSolValue::Uint(uint, n) }) .boxed(), _ => unreachable!(), diff --git a/crates/evm/fuzz/src/strategies/state.rs b/crates/evm/fuzz/src/strategies/state.rs index 3e4628d9a..59a037156 100644 --- a/crates/evm/fuzz/src/strategies/state.rs +++ b/crates/evm/fuzz/src/strategies/state.rs @@ -18,6 +18,8 @@ use std::{ sync::Arc, }; +type AIndexSet = IndexSet>; + /// The maximum number of bytes we will look at in bytecodes to find push bytes (24 KiB). /// /// This is to limit the performance impact of fuzz tests that might deploy arbitrarily sized @@ -44,7 +46,7 @@ impl EvmFuzzState { Self { inner: Arc::new(RwLock::new(dictionary)) } } - pub fn collect_values(&self, values: impl IntoIterator) { + pub fn collect_values(&self, values: impl IntoIterator) { let mut dict = self.inner.write(); for value in values { dict.insert_value(value, true); @@ -93,9 +95,9 @@ impl EvmFuzzState { #[derive(Default)] pub struct FuzzDictionary { /// Collected state values. - state_values: IndexSet<[u8; 32]>, + state_values: AIndexSet, /// Addresses that already had their PUSH bytes collected. - addresses: IndexSet
, + addresses: AIndexSet
, /// Configuration for the dictionary. config: FuzzDictionaryConfig, /// New key indexes added to the dictionary since container initialization. @@ -103,7 +105,7 @@ pub struct FuzzDictionary { /// New address indexes added to the dictionary since container initialization. new_addreses: Vec, /// Sample typed values that are collected from call result and used across invariant runs. - sample_values: HashMap>, + sample_values: HashMap>, misses: usize, hits: usize, @@ -127,32 +129,33 @@ impl FuzzDictionary { /// Insert common values into the dictionary at initialization. fn prefill(&mut self) { - self.insert_value([0; 32], false); + self.insert_value(B256::ZERO, false); } /// Insert values from initial db state into fuzz dictionary. /// These values are persisted across invariant runs. fn insert_db_values(&mut self, db_state: Vec<(&Address, &DbAccount)>) { + let collected = false; for (address, account) in db_state { // Insert basic account information - self.insert_value(address.into_word().into(), false); + self.insert_value(address.into_word(), collected); // Insert push bytes - self.insert_push_bytes_values(address, &account.info, false); + self.insert_push_bytes_values(address, &account.info, collected); // Insert storage values. if self.config.include_storage { // Sort storage values before inserting to ensure deterministic dictionary. let values = account.storage.iter().collect::>(); for (slot, value) in values { - self.insert_storage_value(slot, value, false); + self.insert_storage_value(slot, value, collected); } } } - // need at least some state data if db is empty otherwise we can't select random data for - // state fuzzing + // We need at least some state data if DB is empty, + // otherwise we can't select random data for state fuzzing. if self.values().is_empty() { - // prefill with a random addresses - self.insert_value(Address::random().into_word().into(), false); + // Prefill with a random address. + self.insert_value(Address::random().into_word(), false); } } @@ -193,8 +196,8 @@ impl FuzzDictionary { // If we weren't able to decode event then we insert raw data in fuzz dictionary. if !log_decoded { - for topic in log.topics() { - self.insert_value(topic.0, true); + for &topic in log.topics() { + self.insert_value(topic, true); } let chunks = log.data.data.chunks_exact(32); let rem = chunks.remainder(); @@ -202,7 +205,7 @@ impl FuzzDictionary { self.insert_value(chunk.try_into().unwrap(), true); } if !rem.is_empty() { - self.insert_value(B256::right_padding_from(rem).0, true); + self.insert_value(B256::right_padding_from(rem), true); } } } @@ -214,15 +217,16 @@ impl FuzzDictionary { /// Insert values from call state changeset into fuzz dictionary. /// These values are removed at the end of current run. fn insert_state_values(&mut self, state_changeset: &StateChangeset) { + let collected = true; for (address, account) in state_changeset { // Insert basic account information. - self.insert_value(address.into_word().into(), true); + self.insert_value(address.into_word(), collected); // Insert push bytes. - self.insert_push_bytes_values(address, &account.info, true); + self.insert_push_bytes_values(address, &account.info, collected); // Insert storage values. if self.config.include_storage { for (slot, value) in &account.storage { - self.insert_storage_value(slot, &value.present_value, true); + self.insert_storage_value(slot, &value.present_value, collected); } } } @@ -239,7 +243,7 @@ impl FuzzDictionary { ) { if self.config.include_push_bytes && !self.addresses.contains(address) { // Insert push bytes - if let Some(code) = account_info.code.clone() { + if let Some(code) = &account_info.code { self.insert_address(*address, collected); self.collect_push_bytes(code.bytes_slice(), collected); } @@ -262,16 +266,16 @@ impl FuzzDictionary { } let push_value = U256::try_from_be_slice(&code[push_start..push_end]).unwrap(); - // Also add the value below and above the push value to the dictionary. if push_value != U256::ZERO { - // Never add 0 to the dictionary as it's always present, and it's a pretty - // common value that this is worth it. - self.insert_value(push_value.to_be_bytes(), collected); + // Never add 0 to the dictionary as it's always present. + self.insert_value(push_value.into(), collected); - self.insert_value((push_value - U256::from(1)).to_be_bytes(), collected); - } - if push_value != U256::MAX { - self.insert_value((push_value + U256::from(1)).to_be_bytes(), collected); + // Also add the value below and above the push value to the dictionary. + self.insert_value((push_value - U256::from(1)).into(), collected); + + if push_value != U256::MAX { + self.insert_value((push_value + U256::from(1)).into(), collected); + } } i += push_size; @@ -283,16 +287,16 @@ impl FuzzDictionary { /// Insert values from single storage slot and storage value into fuzz dictionary. /// If storage values are newly collected then they are removed at the end of current run. fn insert_storage_value(&mut self, storage_slot: &U256, storage_value: &U256, collected: bool) { - self.insert_value(B256::from(*storage_slot).0, collected); - self.insert_value(B256::from(*storage_value).0, collected); + self.insert_value(B256::from(*storage_slot), collected); + self.insert_value(B256::from(*storage_value), collected); // also add the value below and above the storage value to the dictionary. if *storage_value != U256::ZERO { let below_value = storage_value - U256::from(1); - self.insert_value(B256::from(below_value).0, collected); + self.insert_value(B256::from(below_value), collected); } if *storage_value != U256::MAX { let above_value = storage_value + U256::from(1); - self.insert_value(B256::from(above_value).0, collected); + self.insert_value(B256::from(above_value), collected); } } @@ -309,7 +313,7 @@ impl FuzzDictionary { /// Insert raw value into fuzz dictionary. /// If value is newly collected then it is removed by index at the end of current run. - fn insert_value(&mut self, value: [u8; 32], collected: bool) { + fn insert_value(&mut self, value: B256, collected: bool) { if self.state_values.len() < self.config.max_fuzz_dictionary_values { let (index, new_value) = self.state_values.insert_full(value); let counter = if new_value { &mut self.misses } else { &mut self.hits }; @@ -323,10 +327,13 @@ impl FuzzDictionary { /// Insert sample values that are reused across multiple runs. /// The number of samples is limited to invariant run depth. /// If collected samples limit is reached then values are inserted as regular values. - pub fn insert_sample_values(&mut self, sample_values: Vec, limit: u32) { + pub fn insert_sample_values( + &mut self, + sample_values: impl IntoIterator, + limit: u32, + ) { for sample in sample_values { if let (Some(sample_type), Some(sample_value)) = (sample.as_type(), sample.as_word()) { - let sample_value = sample_value.into(); if let Some(values) = self.sample_values.get_mut(&sample_type) { if values.len() < limit as usize { values.insert(sample_value); @@ -341,7 +348,7 @@ impl FuzzDictionary { } } - pub fn values(&self) -> &IndexSet<[u8; 32]> { + pub fn values(&self) -> &AIndexSet { &self.state_values } @@ -354,12 +361,12 @@ impl FuzzDictionary { } #[inline] - pub fn samples(&self, param_type: DynSolType) -> Option<&IndexSet<[u8; 32]>> { - self.sample_values.get(¶m_type) + pub fn samples(&self, param_type: &DynSolType) -> Option<&AIndexSet> { + self.sample_values.get(param_type) } #[inline] - pub fn addresses(&self) -> &IndexSet
{ + pub fn addresses(&self) -> &AIndexSet
{ &self.addresses } @@ -400,25 +407,30 @@ pub fn collect_created_contracts( ) -> eyre::Result<()> { let mut writable_targeted = targeted_contracts.targets.lock(); for (address, account) in state_changeset { - if !setup_contracts.contains_key(address) { - if let (true, Some(code)) = (&account.is_touched(), &account.info.code) { - if !code.is_empty() { - if let Some((artifact, contract)) = - project_contracts.find_by_deployed_code(&code.original_bytes()) - { - if let Some(functions) = - artifact_filters.get_targeted_functions(artifact, &contract.abi)? - { - created_contracts.push(*address); - writable_targeted.insert( - *address, - (artifact.name.clone(), contract.abi.clone(), functions), - ); - } - } - } - } + if setup_contracts.contains_key(address) { + continue; + } + if !account.is_touched() { + continue; + } + let Some(code) = &account.info.code else { + continue; + }; + if code.is_empty() { + continue; } + let Some((artifact, contract)) = + project_contracts.find_by_deployed_code(code.original_byte_slice()) + else { + continue; + }; + let Some(functions) = artifact_filters.get_targeted_functions(artifact, &contract.abi)? + else { + continue; + }; + created_contracts.push(*address); + writable_targeted + .insert(*address, (artifact.name.clone(), contract.abi.clone(), functions)); } Ok(()) } diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index c9f76a3aa..0e4f24138 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -591,6 +591,6 @@ contract CounterTest is Test { let runs_split = &stderr[start_runs + 6..]; runs_split.find(',').map(|end_runs| &runs_split[..end_runs]) }); - // make sure there are only 54 runs (with proptest shrinking same test results in 292 runs) - assert_eq!(runs.unwrap().parse::().unwrap(), 54); + // make sure there are only 61 runs (with proptest shrinking same test results in 298 runs) + assert_eq!(runs.unwrap().parse::().unwrap(), 61); }); From 7e6ebaf09dcb1ca6f7087d87d20d8ef9435a3ec6 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 6 Jun 2024 15:29:58 +0200 Subject: [PATCH 360/430] fix: ensure suggested prio fee is at least 1e9 (#8081) --- crates/anvil/src/eth/api.rs | 16 +++++++--------- crates/anvil/src/eth/backend/mem/mod.rs | 8 ++++---- crates/anvil/src/eth/fees.rs | 3 +++ 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 1afb7cd33..57acc197c 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -14,7 +14,7 @@ use crate::{ error::{ BlockchainError, FeeHistoryError, InvalidTransactionError, Result, ToRpcResponseResult, }, - fees::{FeeDetails, FeeHistoryCache}, + fees::{FeeDetails, FeeHistoryCache, MIN_SUGGESTED_PRIORITY_FEE}, macros::node_info, miner::FixedBlockTimeMiner, pool::{ @@ -1357,20 +1357,18 @@ impl EthApi { } /// Returns the suggested fee cap. + /// + /// Returns at least [MIN_SUGGESTED_PRIORITY_FEE] fn lowest_suggestion_tip(&self) -> u128 { let block_number = self.backend.best_number(); let latest_cached_block = self.fee_history_cache.lock().get(&block_number).cloned(); match latest_cached_block { - Some(block) => block.rewards.iter().copied().min().unwrap_or(1e9 as u128), - None => self - .fee_history_cache - .lock() - .values() - .flat_map(|b| b.rewards.clone()) - .min() - .unwrap_or(1e9 as u128), + Some(block) => block.rewards.iter().copied().min(), + None => self.fee_history_cache.lock().values().flat_map(|b| b.rewards.clone()).min(), } + .map(|fee| fee.max(MIN_SUGGESTED_PRIORITY_FEE)) + .unwrap_or(MIN_SUGGESTED_PRIORITY_FEE) } /// Creates a filter object, based on filter options, to notify when the state changes (logs). diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 895e7a3f9..2619c7556 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -19,7 +19,7 @@ use crate::{ validate::TransactionValidator, }, error::{BlockchainError, ErrDetail, InvalidTransactionError}, - fees::{FeeDetails, FeeManager}, + fees::{FeeDetails, FeeManager, MIN_SUGGESTED_PRIORITY_FEE}, macros::node_info, pool::transactions::PoolTransaction, util::get_precompiles_for, @@ -1140,9 +1140,9 @@ impl Backend { env.block.basefee = U256::from(base); } - let gas_price = gas_price - .or(max_fee_per_gas) - .unwrap_or_else(|| self.fees().raw_gas_price().saturating_add(1e9 as u128)); + let gas_price = gas_price.or(max_fee_per_gas).unwrap_or_else(|| { + self.fees().raw_gas_price().saturating_add(MIN_SUGGESTED_PRIORITY_FEE) + }); let caller = from.unwrap_or_default(); let to = to.as_ref().and_then(TxKind::to); env.tx = TxEnv { diff --git a/crates/anvil/src/eth/fees.rs b/crates/anvil/src/eth/fees.rs index 922f149e3..e0c8685fd 100644 --- a/crates/anvil/src/eth/fees.rs +++ b/crates/anvil/src/eth/fees.rs @@ -31,6 +31,9 @@ pub const INITIAL_GAS_PRICE: u128 = 1_875_000_000; /// Bounds the amount the base fee can change between blocks. pub const BASE_FEE_CHANGE_DENOMINATOR: u128 = 8; +/// Minimum suggested priority fee +pub const MIN_SUGGESTED_PRIORITY_FEE: u128 = 1e9 as u128; + pub fn default_elasticity() -> f64 { 1f64 / BaseFeeParams::ethereum().elasticity_multiplier as f64 } From 11388608b48ce7bd4b46161810da72b230df97b4 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Thu, 6 Jun 2024 19:01:55 +0530 Subject: [PATCH 361/430] fix(verify-bytecode): check contract name in cache (#8079) * fix(verify-bytecode): strongly check contract name when fetching from cache * nit * nits * nit * clippy --- crates/verify/src/bytecode.rs | 49 +++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index b49af6006..ee7c5ddde 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -225,11 +225,13 @@ impl VerifyBytecodeArgs { (VerificationType::Partial, _) => (VerificationType::Partial, true), }; + trace!(?verification_type, has_metadata); // Etherscan compilation metadata let etherscan_metadata = source_code.items.first().unwrap(); let local_bytecode = if let Some(local_bytecode) = self.build_using_cache(etherscan_metadata, &config) { + trace!("using cache"); local_bytecode } else { self.build_project(&config)? @@ -411,37 +413,44 @@ impl VerifyBytecodeArgs { for (key, value) in cached_artifacts { let name = self.contract.name.to_owned() + ".sol"; let version = etherscan_settings.compiler_version.to_owned(); + // Ignores vyper if version.starts_with("vyper:") { return None; } // Parse etherscan version string let version = version.split('+').next().unwrap_or("").trim_start_matches('v').to_string(); + + // Check if `out/directory` name matches the contract name if key.ends_with(name.as_str()) { - if let Some(artifact) = value.into_iter().next() { + let artifacts = + value.iter().flat_map(|(_, artifacts)| artifacts.iter()).collect::>(); + let name = name.replace(".sol", ".json"); + for artifact in artifacts { + // Check if ABI file matches the name + if !artifact.file.ends_with(&name) { + continue; + } + + // Check if Solidity version matches if let Ok(version) = Version::parse(&version) { - if let Some(artifact) = artifact.1.iter().find(|a| { - a.version.major == version.major && - a.version.minor == version.minor && - a.version.patch == version.patch - }) { - return artifact - .artifact - .bytecode - .as_ref() - .and_then(|bytes| bytes.bytes().to_owned()) - .cloned(); + if !(artifact.version.major == version.major && + artifact.version.minor == version.minor && + artifact.version.patch == version.patch) + { + continue; } } - let artifact = artifact.1.first().unwrap(); // Get the first artifact - let local_bytecode = if let Some(local_bytecode) = &artifact.artifact.bytecode { - local_bytecode.bytes() - } else { - None - }; - - return local_bytecode.map(|bytes| bytes.to_owned()); + + return artifact + .artifact + .bytecode + .as_ref() + .and_then(|bytes| bytes.bytes().to_owned()) + .cloned(); } + + return None } } From 0248a62892bb958c986b43d2444d318f960ad99b Mon Sep 17 00:00:00 2001 From: Dhairya Sethi <55102840+DhairyaSethi@users.noreply.github.com> Date: Thu, 6 Jun 2024 19:40:11 +0530 Subject: [PATCH 362/430] feat(cheatcode): promptSecretUint (#8082) --- crates/cheatcodes/assets/cheatcodes.json | 20 ++++++++++++++++++++ crates/cheatcodes/spec/src/vm.rs | 4 ++++ crates/cheatcodes/src/fs.rs | 7 +++++++ crates/evm/traces/src/decoder/mod.rs | 2 +- testdata/cheats/Vm.sol | 1 + testdata/default/cheats/Prompt.t.sol | 3 +++ 6 files changed, 36 insertions(+), 1 deletion(-) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 3f2f3cb33..786fc38b4 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -6371,6 +6371,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "promptSecretUint", + "description": "Prompts the user for hidden uint256 in the terminal (usually pk).", + "declaration": "function promptSecretUint(string calldata promptText) external returns (uint256);", + "visibility": "external", + "mutability": "", + "signature": "promptSecretUint(string)", + "selector": "0x69ca02b7", + "selectorBytes": [ + 105, + 202, + 2, + 183 + ] + }, + "group": "filesystem", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "promptUint", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 410039852..1538dc16e 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1499,6 +1499,10 @@ interface Vm { #[cheatcode(group = Filesystem)] function promptSecret(string calldata promptText) external returns (string memory input); + /// Prompts the user for hidden uint256 in the terminal (usually pk). + #[cheatcode(group = Filesystem)] + function promptSecretUint(string calldata promptText) external returns (uint256); + /// Prompts the user for an address in the terminal. #[cheatcode(group = Filesystem)] function promptAddress(string calldata promptText) external returns (address); diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index c50f2cf97..e4ee12513 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -426,6 +426,13 @@ impl Cheatcode for promptSecretCall { } } +impl Cheatcode for promptSecretUintCall { + fn apply(&self, state: &mut Cheatcodes) -> Result { + let Self { promptText: text } = self; + parse(&prompt(state, text, prompt_password)?, &DynSolType::Uint(256)) + } +} + impl Cheatcode for promptAddressCall { fn apply(&self, state: &mut Cheatcodes) -> Result { let Self { promptText: text } = self; diff --git a/crates/evm/traces/src/decoder/mod.rs b/crates/evm/traces/src/decoder/mod.rs index 75ea3d260..2dbdbd7bf 100644 --- a/crates/evm/traces/src/decoder/mod.rs +++ b/crates/evm/traces/src/decoder/mod.rs @@ -523,7 +523,7 @@ impl CallTraceDecoder { match func.name.as_str() { s if s.starts_with("env") => Some(""), "createWallet" | "deriveKey" => Some(""), - "promptSecret" => Some(""), + "promptSecret" | "promptSecretUint" => Some(""), "parseJson" if self.verbosity < 5 => Some(""), "readFile" if self.verbosity < 5 => Some(""), _ => None, diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index e1551362a..23e1f699c 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -314,6 +314,7 @@ interface Vm { function prompt(string calldata promptText) external returns (string memory input); function promptAddress(string calldata promptText) external returns (address); function promptSecret(string calldata promptText) external returns (string memory input); + function promptSecretUint(string calldata promptText) external returns (uint256); function promptUint(string calldata promptText) external returns (uint256); function randomAddress() external returns (address); function randomUint() external returns (uint256); diff --git a/testdata/default/cheats/Prompt.t.sol b/testdata/default/cheats/Prompt.t.sol index 9e461c2b5..33f83fea8 100644 --- a/testdata/default/cheats/Prompt.t.sol +++ b/testdata/default/cheats/Prompt.t.sol @@ -15,6 +15,9 @@ contract PromptTest is DSTest { vm._expectCheatcodeRevert(); vm.promptSecret("test"); + + vm._expectCheatcodeRevert(); + uint256 test = vm.promptSecretUint("test"); } function testPrompt_Address() public { From 729a76629eb787354d17dfa6a9350ce45382c8b5 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 6 Jun 2024 19:06:14 +0200 Subject: [PATCH 363/430] feat: add `skip` key to foundry.toml (#8061) * feat: introduce 'skip' config key * update patch * conflicts * fix test * fmt * update patch * fix doc * update patch * update patch * rm patch * bump compilers * review fixes * fix tests --- Cargo.lock | 10 +- Cargo.toml | 4 +- crates/cli/src/opts/build/core.rs | 20 ++- crates/common/Cargo.toml | 2 - crates/common/src/compile.rs | 178 +++--------------------- crates/common/src/glob.rs | 94 ------------- crates/common/src/lib.rs | 1 - crates/config/Cargo.toml | 1 + crates/config/src/filter.rs | 204 ++++++++++++++++++++++++++++ crates/config/src/lib.rs | 43 +++++- crates/doc/Cargo.toml | 1 - crates/doc/src/builder.rs | 3 +- crates/forge/bin/cmd/bind.rs | 36 ++--- crates/forge/bin/cmd/build.rs | 27 +--- crates/forge/bin/cmd/fmt.rs | 4 +- crates/forge/bin/cmd/test/filter.rs | 3 +- crates/forge/bin/cmd/test/mod.rs | 2 +- crates/forge/tests/cli/build.rs | 29 ++++ crates/forge/tests/cli/config.rs | 1 + crates/script/src/lib.rs | 7 - crates/verify/src/bytecode.rs | 15 +- 21 files changed, 356 insertions(+), 329 deletions(-) delete mode 100644 crates/common/src/glob.rs create mode 100644 crates/config/src/filter.rs diff --git a/Cargo.lock b/Cargo.lock index 8d4cc5d11..263fa9de3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3184,7 +3184,6 @@ dependencies = [ "derive_more", "eyre", "forge-fmt", - "foundry-common", "foundry-compilers", "foundry-config", "itertools 0.13.0", @@ -3437,8 +3436,6 @@ dependencies = [ "foundry-config", "foundry-linking", "foundry-macros", - "glob", - "globset", "num-format", "once_cell", "reqwest", @@ -3459,16 +3456,18 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe70a3860ec9f1861e5d82cbd4ffc55756975c0826dacf8ae4d6d696df8f7f53" +checksum = "f9a92aa3e4d0aa91fda44c1840c68d634fc126bdd06099516eb2b81035e5e6d0" dependencies = [ "alloy-json-abi", "alloy-primitives", "auto_impl", "cfg-if", + "derivative", "dirs 5.0.1", "dunce", + "dyn-clone", "fs_extra", "futures-util", "home", @@ -3509,6 +3508,7 @@ dependencies = [ "figment", "foundry-block-explorers", "foundry-compilers", + "glob", "globset", "number_prefix", "once_cell", diff --git a/Cargo.toml b/Cargo.toml index aca385035..eac8025a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -153,7 +153,7 @@ foundry-linking = { path = "crates/linking" } # solc & compilation utilities foundry-block-explorers = { version = "0.3.0", default-features = false } -foundry-compilers = { version = "0.6.0", default-features = false } +foundry-compilers = { version = "0.6.2", default-features = false } ## revm # no default features to avoid c-kzg @@ -244,4 +244,4 @@ tower-http = "0.5" revm = { git = "https://github.com/bluealloy/revm.git", rev = "a28a543" } revm-interpreter = { git = "https://github.com/bluealloy/revm.git", rev = "a28a543" } revm-precompile = { git = "https://github.com/bluealloy/revm.git", rev = "a28a543" } -revm-primitives = { git = "https://github.com/bluealloy/revm.git", rev = "a28a543" } +revm-primitives = { git = "https://github.com/bluealloy/revm.git", rev = "a28a543" } \ No newline at end of file diff --git a/crates/cli/src/opts/build/core.rs b/crates/cli/src/opts/build/core.rs index 3d45de225..bf0fd019f 100644 --- a/crates/cli/src/opts/build/core.rs +++ b/crates/cli/src/opts/build/core.rs @@ -13,6 +13,7 @@ use foundry_config::{ value::{Dict, Map, Value}, Figment, Metadata, Profile, Provider, }, + filter::SkipBuildFilter, providers::remappings::Remappings, Config, }; @@ -118,6 +119,13 @@ pub struct CoreBuildArgs { #[serde(skip_serializing_if = "Option::is_none")] pub build_info_path: Option, + /// Skip building files whose names contain the given filter. + /// + /// `test` and `script` are aliases for `.t.sol` and `.s.sol`. + #[arg(long, num_args(1..))] + #[serde(skip)] + pub skip: Option>, + #[command(flatten)] #[serde(flatten)] pub compiler: CompilerArgs, @@ -148,7 +156,7 @@ impl CoreBuildArgs { // Loads project's figment and merges the build cli arguments into it impl<'a> From<&'a CoreBuildArgs> for Figment { fn from(args: &'a CoreBuildArgs) -> Self { - let figment = if let Some(ref config_path) = args.project_paths.config_path { + let mut figment = if let Some(ref config_path) = args.project_paths.config_path { if !config_path.exists() { panic!("error: config-path `{}` does not exist", config_path.display()) } @@ -165,7 +173,15 @@ impl<'a> From<&'a CoreBuildArgs> for Figment { let mut remappings = Remappings::new_with_remappings(args.project_paths.get_remappings()); remappings .extend(figment.extract_inner::>("remappings").unwrap_or_default()); - figment.merge(("remappings", remappings.into_inner())).merge(args) + figment = figment.merge(("remappings", remappings.into_inner())).merge(args); + + if let Some(skip) = &args.skip { + let mut skip = skip.iter().map(|s| s.file_pattern().to_string()).collect::>(); + skip.extend(figment.extract_inner::>("skip").unwrap_or_default()); + figment = figment.merge(("skip", skip)); + }; + + figment } } diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 5b11a1be9..1a48e80ac 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -42,8 +42,6 @@ clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } comfy-table = "7" dunce = "1" eyre.workspace = true -glob = "0.3" -globset = "0.4" once_cell = "1" reqwest.workspace = true semver = "1" diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index 13fb79be0..4ab4b170d 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -1,28 +1,25 @@ //! Support for compiling [foundry_compilers::Project] -use crate::{compact_to_contract, glob::GlobMatcher, term::SpinnerReporter, TestFunctionExt}; +use crate::{compact_to_contract, term::SpinnerReporter, TestFunctionExt}; use comfy_table::{presets::ASCII_MARKDOWN, Attribute, Cell, CellAlignment, Color, Table}; use eyre::{Context, Result}; use foundry_block_explorers::contract::Metadata; use foundry_compilers::{ - artifacts::{BytecodeObject, ContractBytecodeSome, Libraries}, - compilers::{solc::SolcCompiler, Compiler}, + artifacts::{BytecodeObject, ContractBytecodeSome, Libraries, Source}, + compilers::{solc::SolcCompiler, CompilationError, Compiler}, remappings::Remapping, report::{BasicStdoutReporter, NoReporter, Report}, - Artifact, ArtifactId, FileFilter, Project, ProjectBuilder, ProjectCompileOutput, - ProjectPathsConfig, Solc, SolcConfig, SparseOutputFileFilter, + Artifact, ArtifactId, Project, ProjectBuilder, ProjectCompileOutput, ProjectPathsConfig, Solc, + SolcConfig, }; use foundry_linking::Linker; use num_format::{Locale, ToFormattedString}; use rustc_hash::FxHashMap; use std::{ collections::{BTreeMap, HashMap}, - convert::Infallible, fmt::Display, io::IsTerminal, path::{Path, PathBuf}, - result, - str::FromStr, time::Instant, }; @@ -31,7 +28,7 @@ use std::{ /// This is merely a wrapper for [`Project::compile()`] which also prints to stdout depending on its /// settings. #[must_use = "ProjectCompiler does nothing unless you call a `compile*` method"] -pub struct ProjectCompiler { +pub struct ProjectCompiler { /// Whether we are going to verify the contracts after compilation. verify: Option, @@ -47,21 +44,18 @@ pub struct ProjectCompiler { /// Whether to bail on compiler errors. bail: Option, - /// Files to exclude. - filter: Option>>, - /// Extra files to include, that are not necessarily in the project's source dir. files: Vec, } -impl Default for ProjectCompiler { +impl Default for ProjectCompiler { #[inline] fn default() -> Self { Self::new() } } -impl ProjectCompiler { +impl ProjectCompiler { /// Create a new builder with the default settings. #[inline] pub fn new() -> Self { @@ -71,7 +65,6 @@ impl ProjectCompiler { print_sizes: None, quiet: Some(crate::shell::verbosity().is_silent()), bail: None, - filter: None, files: Vec::new(), } } @@ -121,13 +114,6 @@ impl ProjectCompiler { self } - /// Sets the filter to use. - #[inline] - pub fn filter(mut self, filter: Box>) -> Self { - self.filter = Some(filter); - self - } - /// Sets extra files to include, that are not necessarily in the project's source dir. #[inline] pub fn files(mut self, files: impl IntoIterator) -> Self { @@ -136,7 +122,7 @@ impl ProjectCompiler { } /// Compiles the project. - pub fn compile( + pub fn compile( mut self, project: &Project, ) -> Result> { @@ -148,17 +134,17 @@ impl ProjectCompiler { } // Taking is fine since we don't need these in `compile_with`. - let filter = std::mem::take(&mut self.filter); let files = std::mem::take(&mut self.files); self.compile_with(|| { - if !files.is_empty() { - project.compile_files(files) - } else if let Some(filter) = filter { - project.compile_sparse(filter) + let sources = if !files.is_empty() { + Source::read_all(files)? } else { - project.compile() - } - .map_err(Into::into) + project.paths.read_input_files()? + }; + + foundry_compilers::project::ProjectCompiler::with_sources(project, sources)? + .compile() + .map_err(Into::into) }) } @@ -173,9 +159,9 @@ impl ProjectCompiler { /// ProjectCompiler::new().compile_with(|| Ok(prj.compile()?)).unwrap(); /// ``` #[instrument(target = "forge::compile", skip_all)] - fn compile_with(self, f: F) -> Result> + fn compile_with(self, f: F) -> Result> where - F: FnOnce() -> Result>, + F: FnOnce() -> Result>, { let quiet = self.quiet.unwrap_or(false); let bail = self.bail.unwrap_or(true); @@ -223,7 +209,7 @@ impl ProjectCompiler { } /// If configured, this will print sizes or names - fn handle_output(&self, output: &ProjectCompileOutput) { + fn handle_output(&self, output: &ProjectCompileOutput) { let print_names = self.print_names.unwrap_or(false); let print_sizes = self.print_sizes.unwrap_or(false); @@ -479,7 +465,7 @@ pub fn compile_target( project: &Project, quiet: bool, ) -> Result> { - ProjectCompiler::::new().quiet(quiet).files([target_path.into()]).compile(project) + ProjectCompiler::new().quiet(quiet).files([target_path.into()]).compile(project) } /// Compiles an Etherscan source from metadata by creating a project. @@ -563,125 +549,3 @@ pub fn etherscan_project( .no_artifacts() .build(compiler)?) } - -/// Bundles multiple `SkipBuildFilter` into a single `FileFilter` -#[derive(Clone, Debug)] -pub struct SkipBuildFilters { - /// All provided filters. - pub matchers: Vec, - /// Root of the project. - pub project_root: PathBuf, -} - -impl FileFilter for SkipBuildFilters { - /// Only returns a match if _no_ exclusion filter matches - fn is_match(&self, file: &Path) -> bool { - self.matchers.iter().all(|matcher| { - if !is_match_exclude(matcher, file) { - false - } else { - file.strip_prefix(&self.project_root) - .map_or(true, |stripped| is_match_exclude(matcher, stripped)) - } - }) - } -} - -impl FileFilter for &SkipBuildFilters { - fn is_match(&self, file: &Path) -> bool { - (*self).is_match(file) - } -} - -impl SkipBuildFilters { - /// Creates a new `SkipBuildFilters` from multiple `SkipBuildFilter`. - pub fn new( - filters: impl IntoIterator, - project_root: PathBuf, - ) -> Result { - let matchers = filters.into_iter().map(|m| m.compile()).collect::>(); - matchers.map(|filters| Self { matchers: filters, project_root }) - } -} - -/// A filter that excludes matching contracts from the build -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum SkipBuildFilter { - /// Exclude all `.t.sol` contracts - Tests, - /// Exclude all `.s.sol` contracts - Scripts, - /// Exclude if the file matches - Custom(String), -} - -impl SkipBuildFilter { - fn new(s: &str) -> Self { - match s { - "test" | "tests" => Self::Tests, - "script" | "scripts" => Self::Scripts, - s => Self::Custom(s.to_string()), - } - } - - /// Returns the pattern to match against a file - fn file_pattern(&self) -> &str { - match self { - Self::Tests => ".t.sol", - Self::Scripts => ".s.sol", - Self::Custom(s) => s.as_str(), - } - } - - fn compile(&self) -> Result { - self.file_pattern().parse().map_err(Into::into) - } -} - -impl FromStr for SkipBuildFilter { - type Err = Infallible; - - fn from_str(s: &str) -> result::Result { - Ok(Self::new(s)) - } -} - -/// Matches file only if the filter does not apply. -/// -/// This returns the inverse of `file.name.contains(pattern) || matcher.is_match(file)`. -fn is_match_exclude(matcher: &GlobMatcher, path: &Path) -> bool { - fn is_match(matcher: &GlobMatcher, path: &Path) -> Option { - let file_name = path.file_name()?.to_str()?; - Some(file_name.contains(matcher.as_str()) || matcher.is_match(path)) - } - - !is_match(matcher, path).unwrap_or_default() -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_build_filter() { - let tests = SkipBuildFilter::Tests.compile().unwrap(); - let scripts = SkipBuildFilter::Scripts.compile().unwrap(); - let custom = |s: &str| SkipBuildFilter::Custom(s.to_string()).compile().unwrap(); - - let file = Path::new("A.t.sol"); - assert!(!is_match_exclude(&tests, file)); - assert!(is_match_exclude(&scripts, file)); - assert!(!is_match_exclude(&custom("A.t"), file)); - - let file = Path::new("A.s.sol"); - assert!(is_match_exclude(&tests, file)); - assert!(!is_match_exclude(&scripts, file)); - assert!(!is_match_exclude(&custom("A.s"), file)); - - let file = Path::new("/home/test/Foo.sol"); - assert!(!is_match_exclude(&custom("*/test/**"), file)); - - let file = Path::new("/home/script/Contract.sol"); - assert!(!is_match_exclude(&custom("*/script/**"), file)); - } -} diff --git a/crates/common/src/glob.rs b/crates/common/src/glob.rs deleted file mode 100644 index 070f70367..000000000 --- a/crates/common/src/glob.rs +++ /dev/null @@ -1,94 +0,0 @@ -//! Contains `globset::Glob` wrapper functions used for filtering. - -use std::{ - fmt, - path::{Path, PathBuf}, - str::FromStr, -}; - -/// Expand globs with a root path. -pub fn expand_globs( - root: &Path, - patterns: impl IntoIterator>, -) -> eyre::Result> { - let mut expanded = Vec::new(); - for pattern in patterns { - for paths in glob::glob(&root.join(pattern.as_ref()).display().to_string())? { - expanded.push(paths?); - } - } - Ok(expanded) -} - -/// A `globset::Glob` that creates its `globset::GlobMatcher` when its created, so it doesn't need -/// to be compiled when the filter functions `TestFilter` functions are called. -#[derive(Clone, Debug)] -pub struct GlobMatcher { - /// The compiled glob - pub matcher: globset::GlobMatcher, -} - -impl GlobMatcher { - /// Creates a new `GlobMatcher` from a `globset::Glob`. - pub fn new(glob: globset::Glob) -> Self { - Self { matcher: glob.compile_matcher() } - } - - /// Tests whether the given path matches this pattern or not. - /// - /// The glob `./test/*` won't match absolute paths like `test/Contract.sol`, which is common - /// format here, so we also handle this case here - pub fn is_match(&self, path: &Path) -> bool { - if self.matcher.is_match(path) { - return true; - } - - if !path.starts_with("./") && self.as_str().starts_with("./") { - return self.matcher.is_match(format!("./{}", path.display())); - } - - false - } - - /// Returns the `globset::Glob`. - pub fn glob(&self) -> &globset::Glob { - self.matcher.glob() - } - - /// Returns the `Glob` string used to compile this matcher. - pub fn as_str(&self) -> &str { - self.glob().glob() - } -} - -impl fmt::Display for GlobMatcher { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.glob().fmt(f) - } -} - -impl FromStr for GlobMatcher { - type Err = globset::Error; - - fn from_str(s: &str) -> Result { - s.parse::().map(Self::new) - } -} - -impl From for GlobMatcher { - fn from(glob: globset::Glob) -> Self { - Self::new(glob) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn can_match_glob_paths() { - let matcher: GlobMatcher = "./test/*".parse().unwrap(); - assert!(matcher.is_match(Path::new("test/Contract.sol"))); - assert!(matcher.is_match(Path::new("./test/Contract.sol"))); - } -} diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index fa1cecbbe..7b1c0ff76 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -21,7 +21,6 @@ pub mod errors; pub mod evm; pub mod fmt; pub mod fs; -pub mod glob; pub mod provider; pub mod retry; pub mod selectors; diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml index d5a1ddda6..c487c78c6 100644 --- a/crates/config/Cargo.toml +++ b/crates/config/Cargo.toml @@ -25,6 +25,7 @@ dunce = "1" eyre.workspace = true figment = { version = "0.10", features = ["toml", "env"] } globset = "0.4" +glob = "0.3" Inflector = "0.11" number_prefix = "0.4" once_cell = "1" diff --git a/crates/config/src/filter.rs b/crates/config/src/filter.rs new file mode 100644 index 000000000..385b44225 --- /dev/null +++ b/crates/config/src/filter.rs @@ -0,0 +1,204 @@ +//! Helpers for constructing and using [FileFilter]s. + +use core::fmt; +use foundry_compilers::FileFilter; +use std::{ + convert::Infallible, + path::{Path, PathBuf}, + str::FromStr, +}; + +/// Expand globs with a root path. +pub fn expand_globs( + root: &Path, + patterns: impl IntoIterator>, +) -> eyre::Result> { + let mut expanded = Vec::new(); + for pattern in patterns { + for paths in glob::glob(&root.join(pattern.as_ref()).display().to_string())? { + expanded.push(paths?); + } + } + Ok(expanded) +} + +/// A `globset::Glob` that creates its `globset::GlobMatcher` when its created, so it doesn't need +/// to be compiled when the filter functions `TestFilter` functions are called. +#[derive(Clone, Debug)] +pub struct GlobMatcher { + /// The compiled glob + pub matcher: globset::GlobMatcher, +} + +impl GlobMatcher { + /// Creates a new `GlobMatcher` from a `globset::Glob`. + pub fn new(glob: globset::Glob) -> Self { + Self { matcher: glob.compile_matcher() } + } + + /// Tests whether the given path matches this pattern or not. + /// + /// The glob `./test/*` won't match absolute paths like `test/Contract.sol`, which is common + /// format here, so we also handle this case here + pub fn is_match(&self, path: &Path) -> bool { + if self.matcher.is_match(path) { + return true; + } + + if let Some(file_name) = path.file_name().and_then(|n| n.to_str()) { + if file_name.contains(self.as_str()) { + return true; + } + } + + if !path.starts_with("./") && self.as_str().starts_with("./") { + return self.matcher.is_match(format!("./{}", path.display())); + } + + false + } + + /// Matches file only if the filter does not apply. + /// + /// This returns the inverse of `self.is_match(file)`. + fn is_match_exclude(&self, path: &Path) -> bool { + !self.is_match(path) + } + + /// Returns the `globset::Glob`. + pub fn glob(&self) -> &globset::Glob { + self.matcher.glob() + } + + /// Returns the `Glob` string used to compile this matcher. + pub fn as_str(&self) -> &str { + self.glob().glob() + } +} + +impl fmt::Display for GlobMatcher { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.glob().fmt(f) + } +} + +impl FromStr for GlobMatcher { + type Err = globset::Error; + + fn from_str(s: &str) -> Result { + s.parse::().map(Self::new) + } +} + +impl From for GlobMatcher { + fn from(glob: globset::Glob) -> Self { + Self::new(glob) + } +} + +/// Bundles multiple `SkipBuildFilter` into a single `FileFilter` +#[derive(Clone, Debug)] +pub struct SkipBuildFilters { + /// All provided filters. + pub matchers: Vec, + /// Root of the project. + pub project_root: PathBuf, +} + +impl FileFilter for SkipBuildFilters { + /// Only returns a match if _no_ exclusion filter matches + fn is_match(&self, file: &Path) -> bool { + self.matchers.iter().all(|matcher| { + if !matcher.is_match_exclude(file) { + false + } else { + file.strip_prefix(&self.project_root) + .map_or(true, |stripped| matcher.is_match_exclude(stripped)) + } + }) + } +} + +impl SkipBuildFilters { + /// Creates a new `SkipBuildFilters` from multiple `SkipBuildFilter`. + pub fn new>( + filters: impl IntoIterator, + project_root: PathBuf, + ) -> Self { + let matchers = filters.into_iter().map(|m| m.into()).collect(); + Self { matchers, project_root } + } +} + +/// A filter that excludes matching contracts from the build +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum SkipBuildFilter { + /// Exclude all `.t.sol` contracts + Tests, + /// Exclude all `.s.sol` contracts + Scripts, + /// Exclude if the file matches + Custom(String), +} + +impl SkipBuildFilter { + fn new(s: &str) -> Self { + match s { + "test" | "tests" => SkipBuildFilter::Tests, + "script" | "scripts" => SkipBuildFilter::Scripts, + s => SkipBuildFilter::Custom(s.to_string()), + } + } + + /// Returns the pattern to match against a file + pub fn file_pattern(&self) -> &str { + match self { + SkipBuildFilter::Tests => ".t.sol", + SkipBuildFilter::Scripts => ".s.sol", + SkipBuildFilter::Custom(s) => s.as_str(), + } + } +} + +impl FromStr for SkipBuildFilter { + type Err = Infallible; + + fn from_str(s: &str) -> Result { + Ok(Self::new(s)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_build_filter() { + let tests = GlobMatcher::from_str(SkipBuildFilter::Tests.file_pattern()).unwrap(); + let scripts = GlobMatcher::from_str(SkipBuildFilter::Scripts.file_pattern()).unwrap(); + let custom = |s| GlobMatcher::from_str(s).unwrap(); + + let file = Path::new("A.t.sol"); + assert!(!tests.is_match_exclude(file)); + assert!(scripts.is_match_exclude(file)); + assert!(!custom("A.t").is_match_exclude(file)); + + let file = Path::new("A.s.sol"); + assert!(tests.is_match_exclude(file)); + assert!(!scripts.is_match_exclude(file)); + assert!(!custom("A.s").is_match_exclude(file)); + + let file = Path::new("/home/test/Foo.sol"); + assert!(!custom("*/test/**").is_match_exclude(file)); + + let file = Path::new("/home/script/Contract.sol"); + assert!(!custom("*/script/**").is_match_exclude(file)); + } + + #[test] + fn can_match_glob_paths() { + let matcher: GlobMatcher = "./test/*".parse().unwrap(); + assert!(matcher.is_match(Path::new("test/Contract.sol"))); + assert!(matcher.is_match(Path::new("./test/Contract.sol"))); + } +} diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 2d658894b..781e6b574 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -80,6 +80,9 @@ pub use error::SolidityErrorCode; pub mod doc; pub use doc::DocConfig; +pub mod filter; +pub use filter::SkipBuildFilters; + mod warning; pub use warning::*; @@ -171,6 +174,9 @@ pub struct Config { pub allow_paths: Vec, /// additional solc include paths for `--include-path` pub include_paths: Vec, + /// glob patterns to skip + #[serde(with = "from_vec_glob")] + pub skip: Vec, /// whether to force a `project.clean()` pub force: bool, /// evm version to use @@ -773,7 +779,7 @@ impl Config { /// Creates a [Project] with the given `cached` and `no_artifacts` flags pub fn create_project(&self, cached: bool, no_artifacts: bool) -> Result { - let project = Project::builder() + let mut builder = Project::builder() .artifacts(self.configured_artifacts_handler()) .paths(self.project_paths()) .settings(self.compiler_settings()?) @@ -787,8 +793,14 @@ impl Config { .set_offline(self.offline) .set_cached(cached && !self.build_info) .set_build_info(!no_artifacts && self.build_info) - .set_no_artifacts(no_artifacts) - .build(self.compiler()?)?; + .set_no_artifacts(no_artifacts); + + if !self.skip.is_empty() { + let filter = SkipBuildFilters::new(self.skip.clone(), self.__root.0.clone()); + builder = builder.sparse_output(filter); + } + + let project = builder.build(self.compiler()?)?; if self.force { self.cleanup(&project)?; @@ -1901,6 +1913,30 @@ pub(crate) mod from_opt_glob { } } +/// Ser/de `globset::Glob` explicitly to handle `Option` properly +pub(crate) mod from_vec_glob { + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + pub fn serialize(value: &[globset::Glob], serializer: S) -> Result + where + S: Serializer, + { + let value = value.iter().map(|g| g.glob()).collect::>(); + value.serialize(serializer) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + let s: Vec = Vec::deserialize(deserializer)?; + s.into_iter() + .map(|s| globset::Glob::new(&s)) + .collect::, _>>() + .map_err(serde::de::Error::custom) + } +} + /// A helper wrapper around the root path used during Config detection #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] #[serde(transparent)] @@ -2065,6 +2101,7 @@ impl Default for Config { unchecked_cheatcode_artifacts: false, create2_library_salt: Config::DEFAULT_CREATE2_LIBRARY_SALT, lang: Language::Solidity, + skip: vec![], __non_exhaustive: (), __warnings: vec![], } diff --git a/crates/doc/Cargo.toml b/crates/doc/Cargo.toml index baf914347..36b107a9e 100644 --- a/crates/doc/Cargo.toml +++ b/crates/doc/Cargo.toml @@ -15,7 +15,6 @@ workspace = true [dependencies] forge-fmt.workspace = true -foundry-common.workspace = true foundry-compilers.workspace = true foundry-config.workspace = true diff --git a/crates/doc/src/builder.rs b/crates/doc/src/builder.rs index ae51e982c..3c8270a3a 100644 --- a/crates/doc/src/builder.rs +++ b/crates/doc/src/builder.rs @@ -3,9 +3,8 @@ use crate::{ ParseSource, Parser, Preprocessor, }; use forge_fmt::{FormatterConfig, Visitable}; -use foundry_common::glob::expand_globs; use foundry_compilers::{utils::source_files_iter, SOLC_EXTENSIONS}; -use foundry_config::DocConfig; +use foundry_config::{filter::expand_globs, DocConfig}; use itertools::Itertools; use mdbook::MDBook; use rayon::prelude::*; diff --git a/crates/forge/bin/cmd/bind.rs b/crates/forge/bin/cmd/bind.rs index b4cd5ede8..dcf76fc2f 100644 --- a/crates/forge/bin/cmd/bind.rs +++ b/crates/forge/bin/cmd/bind.rs @@ -6,6 +6,7 @@ use eyre::{Result, WrapErr}; use foundry_cli::{opts::CoreBuildArgs, utils::LoadConfig}; use foundry_common::{compile::ProjectCompiler, fs::json_files}; use foundry_config::impl_figment_convert; +use regex::Regex; use std::{ fs, path::{Path, PathBuf}, @@ -32,10 +33,6 @@ pub struct BindArgs { #[arg(long)] pub select: Vec, - /// Create bindings only for contracts whose names do not match the specified filter(s) - #[arg(long, conflicts_with = "select")] - pub skip: Vec, - /// Explicitly generate bindings for all contracts /// /// By default all contracts ending with `Test` or `Script` are excluded. @@ -133,18 +130,25 @@ impl BindArgs { } /// Returns the filter to use for `MultiAbigen` - fn get_filter(&self) -> ContractFilter { + fn get_filter(&self) -> Result { if self.select_all { - return ContractFilter::All + return Ok(ContractFilter::All) } if !self.select.is_empty() { - return SelectContracts::default().extend_regex(self.select.clone()).into() + return Ok(SelectContracts::default().extend_regex(self.select.clone()).into()) } - if !self.skip.is_empty() { - return ExcludeContracts::default().extend_regex(self.skip.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 - ExcludeContracts::default() + Ok(ExcludeContracts::default() .extend_pattern([ ".*Test.*", ".*Script", @@ -155,13 +159,13 @@ impl BindArgs { "[Vv]m.*", ]) .extend_names(["IMulticall3"]) - .into() + .into()) } /// Returns an iterator over the JSON files and the contract name in the `artifacts` directory. - fn get_json_files(&self, artifacts: &Path) -> impl Iterator { - let filter = self.get_filter(); - json_files(artifacts) + fn get_json_files(&self, artifacts: &Path) -> Result> { + let filter = self.get_filter()?; + Ok(json_files(artifacts) .filter_map(|path| { // Ignore the build info JSON. if path.to_str()?.contains("/build-info/") { @@ -181,13 +185,13 @@ impl BindArgs { Some((name, path)) }) - .filter(move |(name, _path)| filter.is_match(name)) + .filter(move |(name, _path)| filter.is_match(name))) } /// Instantiate the multi-abigen fn get_multi(&self, artifacts: &Path) -> Result { let abigens = self - .get_json_files(artifacts) + .get_json_files(artifacts)? .map(|(name, path)| { trace!(?path, "parsing Abigen from file"); let abi = Abigen::new(name, path.to_str().unwrap()) diff --git a/crates/forge/bin/cmd/build.rs b/crates/forge/bin/cmd/build.rs index c2fc8088f..e78ac283a 100644 --- a/crates/forge/bin/cmd/build.rs +++ b/crates/forge/bin/cmd/build.rs @@ -2,7 +2,7 @@ use super::{install, watch::WatchArgs}; use clap::Parser; use eyre::Result; use foundry_cli::{opts::CoreBuildArgs, utils::LoadConfig}; -use foundry_common::compile::{ProjectCompiler, SkipBuildFilter, SkipBuildFilters}; +use foundry_common::compile::ProjectCompiler; use foundry_compilers::{Project, ProjectCompileOutput}; use foundry_config::{ figment::{ @@ -52,13 +52,6 @@ pub struct BuildArgs { #[serde(skip)] pub sizes: bool, - /// Skip building files whose names contain the given filter. - /// - /// `test` and `script` are aliases for `.t.sol` and `.s.sol`. - #[arg(long, num_args(1..))] - #[serde(skip)] - pub skip: Option>, - #[command(flatten)] #[serde(flatten)] pub args: CoreBuildArgs, @@ -87,19 +80,12 @@ impl BuildArgs { let project = config.project()?; - let mut compiler = ProjectCompiler::new() + let compiler = ProjectCompiler::new() .print_names(self.names) .print_sizes(self.sizes) .quiet(self.format_json) .bail(!self.format_json); - if let Some(ref skip) = self.skip { - if !skip.is_empty() { - let filter = SkipBuildFilters::new(skip.clone(), project.root().clone())?; - compiler = compiler.filter(Box::new(filter)); - } - }; - let output = compiler.compile(&project)?; if self.format_json { @@ -160,21 +146,22 @@ impl Provider for BuildArgs { #[cfg(test)] mod tests { use super::*; + use foundry_config::filter::SkipBuildFilter; #[test] fn can_parse_build_filters() { let args: BuildArgs = BuildArgs::parse_from(["foundry-cli", "--skip", "tests"]); - assert_eq!(args.skip, Some(vec![SkipBuildFilter::Tests])); + assert_eq!(args.args.skip, Some(vec![SkipBuildFilter::Tests])); let args: BuildArgs = BuildArgs::parse_from(["foundry-cli", "--skip", "scripts"]); - assert_eq!(args.skip, Some(vec![SkipBuildFilter::Scripts])); + assert_eq!(args.args.skip, Some(vec![SkipBuildFilter::Scripts])); let args: BuildArgs = BuildArgs::parse_from(["foundry-cli", "--skip", "tests", "--skip", "scripts"]); - assert_eq!(args.skip, Some(vec![SkipBuildFilter::Tests, SkipBuildFilter::Scripts])); + assert_eq!(args.args.skip, Some(vec![SkipBuildFilter::Tests, SkipBuildFilter::Scripts])); let args: BuildArgs = BuildArgs::parse_from(["foundry-cli", "--skip", "tests", "scripts"]); - assert_eq!(args.skip, Some(vec![SkipBuildFilter::Tests, SkipBuildFilter::Scripts])); + assert_eq!(args.args.skip, Some(vec![SkipBuildFilter::Tests, SkipBuildFilter::Scripts])); } #[test] diff --git a/crates/forge/bin/cmd/fmt.rs b/crates/forge/bin/cmd/fmt.rs index 34df8dbcf..b62f6a7eb 100644 --- a/crates/forge/bin/cmd/fmt.rs +++ b/crates/forge/bin/cmd/fmt.rs @@ -2,9 +2,9 @@ use clap::{Parser, ValueHint}; use eyre::Result; use forge_fmt::{format_to, parse, print_diagnostics_report}; use foundry_cli::utils::{FoundryPathExt, LoadConfig}; -use foundry_common::{fs, glob::expand_globs, term::cli_warn}; +use foundry_common::{fs, term::cli_warn}; use foundry_compilers::{compilers::solc::SolcLanguage, SOLC_EXTENSIONS}; -use foundry_config::impl_figment_convert_basic; +use foundry_config::{filter::expand_globs, impl_figment_convert_basic}; use rayon::prelude::*; use similar::{ChangeTag, TextDiff}; use std::{ diff --git a/crates/forge/bin/cmd/test/filter.rs b/crates/forge/bin/cmd/test/filter.rs index 1a7f355d7..7ececa7d4 100644 --- a/crates/forge/bin/cmd/test/filter.rs +++ b/crates/forge/bin/cmd/test/filter.rs @@ -1,8 +1,7 @@ use clap::Parser; use forge::TestFilter; -use foundry_common::glob::GlobMatcher; use foundry_compilers::{FileFilter, ProjectPathsConfig}; -use foundry_config::Config; +use foundry_config::{filter::GlobMatcher, Config}; use std::{fmt, path::Path}; /// The filter to use during testing. diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 165a07374..3f54e01e5 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -162,7 +162,7 @@ impl TestArgs { *selection = OutputSelection::common_output_selection(["abi".to_string()]); }); - let output = project.compile_sparse(Box::new(filter.clone()))?; + let output = project.compile()?; if output.has_compiler_errors() { println!("{output}"); diff --git a/crates/forge/tests/cli/build.rs b/crates/forge/tests/cli/build.rs index 668e4be5e..e2dca7e0a 100644 --- a/crates/forge/tests/cli/build.rs +++ b/crates/forge/tests/cli/build.rs @@ -1,4 +1,6 @@ +use foundry_config::Config; use foundry_test_utils::{forgetest, util::OutputExt}; +use globset::Glob; use std::path::PathBuf; // tests that json is printed when --json is passed @@ -40,3 +42,30 @@ forgetest_init!(build_sizes_no_forge_std, |prj, cmd| { assert!(!stdout.contains("std"), "\n{stdout}"); assert!(stdout.contains("Counter"), "\n{stdout}"); }); + +// tests that skip key in config can be used to skip non-compilable contract +forgetest_init!(test_can_skip_contract, |prj, cmd| { + prj.add_source( + "InvalidContract", + r" +contract InvalidContract { + some_invalid_syntax +} +", + ) + .unwrap(); + + prj.add_source( + "ValidContract", + r" +contract ValidContract {} +", + ) + .unwrap(); + + let config = + Config { skip: vec![Glob::new("src/InvalidContract.sol").unwrap()], ..Default::default() }; + prj.write_config(config); + + cmd.args(["build"]).assert_success(); +}); diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 31a9ec1fa..f26f8243e 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -137,6 +137,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { unchecked_cheatcode_artifacts: false, create2_library_salt: Config::DEFAULT_CREATE2_LIBRARY_SALT, lang: Language::Solidity, + skip: vec![], __non_exhaustive: (), __warnings: vec![], }; diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 3cea29f20..087834dce 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -22,7 +22,6 @@ use forge_verify::RetryArgs; use foundry_cli::{opts::CoreBuildArgs, utils::LoadConfig}; use foundry_common::{ abi::{encode_function_args, get_func}, - compile::SkipBuildFilter, evm::{Breakpoints, EvmArgs}, shell, ContractsByArtifact, CONTRACT_MAX_SIZE, SELECTOR_LEN, }; @@ -180,12 +179,6 @@ pub struct ScriptArgs { )] pub with_gas_price: Option, - /// Skip building files whose names contain the given filter. - /// - /// `test` and `script` are aliases for `.t.sol` and `.s.sol`. - #[arg(long, num_args(1..))] - pub skip: Option>, - #[command(flatten)] pub opts: CoreBuildArgs, diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index ee7c5ddde..ebe63adc5 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -8,16 +8,13 @@ use foundry_cli::{ opts::EtherscanOpts, utils::{self, read_constructor_args_file, LoadConfig}, }; -use foundry_common::{ - compile::{ProjectCompiler, SkipBuildFilter, SkipBuildFilters}, - provider::ProviderBuilder, -}; +use foundry_common::{compile::ProjectCompiler, provider::ProviderBuilder}; use foundry_compilers::{ artifacts::{BytecodeHash, BytecodeObject, CompactContractBytecode}, info::ContractInfo, Artifact, EvmVersion, }; -use foundry_config::{figment, impl_figment_convert, Chain, Config}; +use foundry_config::{figment, filter::SkipBuildFilter, impl_figment_convert, Chain, Config}; use foundry_evm::{ constants::DEFAULT_CREATE2_DEPLOYER, executors::TracingExecutor, utils::configure_tx_env, }; @@ -377,14 +374,8 @@ impl VerifyBytecodeArgs { fn build_project(&self, config: &Config) -> Result { let project = config.project()?; - let mut compiler = ProjectCompiler::new(); + let compiler = ProjectCompiler::new(); - if let Some(skip) = &self.skip { - if !skip.is_empty() { - let filter = SkipBuildFilters::new(skip.to_owned(), project.root().to_path_buf())?; - compiler = compiler.filter(Box::new(filter)); - } - } let output = compiler.compile(&project)?; let artifact = output From 3a024a19d03bddf12e921c94286b5af354e14e6a Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 6 Jun 2024 20:12:20 +0200 Subject: [PATCH 364/430] fix: update persistent accounts handling (#8083) --- crates/evm/core/src/backend/mod.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index 1f6a61517..addfccb85 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -767,7 +767,13 @@ impl Backend { let test_contract = match env.tx.transact_to { TransactTo::Call(to) => to, - TransactTo::Create => env.tx.caller.create(env.tx.nonce.unwrap_or_default()), + TransactTo::Create => { + let nonce = self + .basic_ref(env.tx.caller) + .map(|b| b.unwrap_or_default().nonce) + .unwrap_or_default(); + env.tx.caller.create(nonce) + } }; self.set_test_contract(test_contract); } From ca0f29b972786ffd1ddb136a8601b4d19bffe588 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 6 Jun 2024 20:12:32 +0200 Subject: [PATCH 365/430] fix(anvil): receipts root calculation (#8085) fix(anvil): update receipts root calculation --- crates/anvil/core/src/eth/transaction/mod.rs | 80 ++++++++++++++++++-- crates/anvil/src/eth/backend/executor.rs | 6 +- crates/anvil/src/eth/backend/mem/mod.rs | 2 +- 3 files changed, 78 insertions(+), 10 deletions(-) diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 0aa6b8330..712ac937d 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -7,7 +7,7 @@ use alloy_consensus::{ TxEnvelope, TxLegacy, TxReceipt, }; use alloy_eips::eip2718::{Decodable2718, Encodable2718}; -use alloy_primitives::{Address, Bloom, Bytes, Log, Signature, TxHash, TxKind, B256, U256}; +use alloy_primitives::{Address, Bloom, Bytes, Log, Signature, TxHash, TxKind, B256, U256, U64}; use alloy_rlp::{length_of_length, Decodable, Encodable, Header}; use alloy_rpc_types::{ other::OtherFields, request::TransactionRequest, AccessList, AnyTransactionReceipt, @@ -1030,8 +1030,10 @@ pub struct TransactionInfo { pub struct DepositReceipt { #[serde(flatten)] pub inner: ReceiptWithBloom, + #[serde(default, with = "alloy_serde::num::u64_opt_via_ruint")] pub deposit_nonce: Option, - pub deposit_nonce_version: Option, + #[serde(default, with = "alloy_serde::num::u64_opt_via_ruint")] + pub deposit_receipt_version: Option, } impl DepositReceipt { @@ -1041,7 +1043,7 @@ impl DepositReceipt { self.inner.logs_bloom.length() + self.inner.receipt.logs.length() + self.deposit_nonce.map_or(0, |n| n.length()) + - self.deposit_nonce_version.map_or(0, |n| n.length()) + self.deposit_receipt_version.map_or(0, |n| n.length()) } /// Returns the rlp header for the receipt payload. @@ -1059,7 +1061,7 @@ impl DepositReceipt { if let Some(n) = self.deposit_nonce { n.encode(out); } - if let Some(n) = self.deposit_nonce_version { + if let Some(n) = self.deposit_receipt_version { n.encode(out); } } @@ -1088,7 +1090,7 @@ impl DepositReceipt { logs_bloom, }, deposit_nonce, - deposit_nonce_version, + deposit_receipt_version: deposit_nonce_version, }; let consumed = started_len - b.len(); @@ -1257,6 +1259,62 @@ impl Decodable for TypedReceipt { } } +impl Encodable2718 for TypedReceipt { + fn type_flag(&self) -> Option { + match self { + Self::Legacy(_) => None, + Self::EIP2930(_) => Some(1), + Self::EIP1559(_) => Some(2), + Self::EIP4844(_) => Some(3), + Self::Deposit(_) => Some(0x7E), + } + } + + fn encode_2718_len(&self) -> usize { + match self { + Self::Legacy(r) => ReceiptEnvelope::Legacy(r.clone()).encode_2718_len(), + Self::EIP2930(r) => ReceiptEnvelope::Eip2930(r.clone()).encode_2718_len(), + Self::EIP1559(r) => ReceiptEnvelope::Eip1559(r.clone()).encode_2718_len(), + Self::EIP4844(r) => ReceiptEnvelope::Eip4844(r.clone()).encode_2718_len(), + Self::Deposit(r) => 1 + r.length(), + } + } + + fn encode_2718(&self, out: &mut dyn BufMut) { + match self { + Self::Legacy(r) => ReceiptEnvelope::Legacy(r.clone()).encode_2718(out), + Self::EIP2930(r) => ReceiptEnvelope::Eip2930(r.clone()).encode_2718(out), + Self::EIP1559(r) => ReceiptEnvelope::Eip1559(r.clone()).encode_2718(out), + Self::EIP4844(r) => ReceiptEnvelope::Eip4844(r.clone()).encode_2718(out), + Self::Deposit(r) => { + out.put_u8(0x7E); + r.encode(out); + } + } + } +} + +impl Decodable2718 for TypedReceipt { + fn typed_decode(ty: u8, buf: &mut &[u8]) -> alloy_rlp::Result { + if ty == 0x7E { + return Ok(Self::Deposit(DepositReceipt::decode(buf)?)) + } + match ReceiptEnvelope::typed_decode(ty, buf)? { + ReceiptEnvelope::Eip2930(tx) => Ok(Self::EIP2930(tx)), + ReceiptEnvelope::Eip1559(tx) => Ok(Self::EIP1559(tx)), + ReceiptEnvelope::Eip4844(tx) => Ok(Self::EIP4844(tx)), + _ => unreachable!(), + } + } + + fn fallback_decode(buf: &mut &[u8]) -> alloy_rlp::Result { + match ReceiptEnvelope::fallback_decode(buf)? { + ReceiptEnvelope::Legacy(tx) => Ok(Self::Legacy(tx)), + _ => unreachable!(), + } + } +} + pub type ReceiptResponse = TransactionReceipt>; pub fn convert_to_anvil_receipt(receipt: AnyTransactionReceipt) -> Option { @@ -1300,8 +1358,16 @@ pub fn convert_to_anvil_receipt(receipt: AnyTransactionReceipt) -> Option TypedReceipt::EIP4844(receipt_with_bloom), 0x7E => TypedReceipt::Deposit(DepositReceipt { inner: receipt_with_bloom, - deposit_nonce: other.get("depositNonce").and_then(|v| v.as_u64()), - deposit_nonce_version: other.get("depositNonceVersion").and_then(|v| v.as_u64()), + deposit_nonce: other + .get_deserialized::("depositNonce") + .transpose() + .ok()? + .map(|v| v.to()), + deposit_receipt_version: other + .get_deserialized::("depositReceiptVersion") + .transpose() + .ok()? + .map(|v| v.to()), }), _ => return None, }, diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index 4c7e01a47..52c379da6 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -9,6 +9,7 @@ use crate::{ PrecompileFactory, }; use alloy_consensus::{Header, Receipt, ReceiptWithBloom}; +use alloy_eips::eip2718::Encodable2718; use alloy_primitives::{Bloom, BloomInput, Log, B256}; use anvil_core::eth::{ block::{Block, BlockInfo, PartialHeader}, @@ -65,7 +66,7 @@ impl ExecutedTransaction { TypedTransaction::Deposit(tx) => TypedReceipt::Deposit(DepositReceipt { inner: receipt_with_bloom, deposit_nonce: Some(tx.nonce), - deposit_nonce_version: Some(1), + deposit_receipt_version: Some(1), }), } } @@ -201,7 +202,8 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' } let ommers: Vec
= Vec::new(); - let receipts_root = trie::ordered_trie_root(receipts.iter().map(alloy_rlp::encode)); + let receipts_root = + trie::ordered_trie_root(receipts.iter().map(Encodable2718::encoded_2718)); let partial_header = PartialHeader { parent_hash, diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 2619c7556..d2a62ebc1 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -2063,7 +2063,7 @@ impl Backend { TypedReceipt::Deposit(r) => TypedReceipt::Deposit(DepositReceipt { inner: receipt_with_bloom, deposit_nonce: r.deposit_nonce, - deposit_nonce_version: r.deposit_nonce_version, + deposit_receipt_version: r.deposit_receipt_version, }), }; From 9c343de6037550e831de7aaad5589aadb400062c Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 6 Jun 2024 20:26:08 +0200 Subject: [PATCH 366/430] feat: add `[vyper]` config section (#8086) * feat: add 'vyper_optimize' config setting * fix doc * [vyper] --- crates/config/src/language.rs | 39 ------------------------ crates/config/src/lib.rs | 52 +++++++++++++++++++++++--------- crates/config/src/vyper.rs | 15 +++++++++ crates/forge/tests/cli/config.rs | 5 ++- 4 files changed, 55 insertions(+), 56 deletions(-) delete mode 100644 crates/config/src/language.rs create mode 100644 crates/config/src/vyper.rs diff --git a/crates/config/src/language.rs b/crates/config/src/language.rs deleted file mode 100644 index dd1105e2d..000000000 --- a/crates/config/src/language.rs +++ /dev/null @@ -1,39 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::str::FromStr; - -/// Compilers supported by foundry. -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub enum Language { - /// Solidity - Solidity, - /// Vyper - Vyper, -} - -impl Language { - /// Returns the language name as a string. - pub const fn as_str(&self) -> &'static str { - match self { - Self::Solidity => "solidity", - Self::Vyper => "vyper", - } - } -} - -impl std::fmt::Display for Language { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(self.as_str()) - } -} - -impl FromStr for Language { - type Err = String; - - fn from_str(s: &str) -> Result { - match s { - "solidity" => Ok(Self::Solidity), - "vyper" => Ok(Self::Vyper), - s => Err(format!("Unknown language: {s}")), - } - } -} diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 781e6b574..9a3885fbb 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -95,9 +95,6 @@ pub use figment; pub mod providers; use providers::{remappings::RemappingsProvider, FallbackProfileProvider, WarningsProvider}; -mod language; -pub use language::Language; - mod fuzz; pub use fuzz::{FuzzConfig, FuzzDictionaryConfig}; @@ -107,6 +104,9 @@ pub use invariant::InvariantConfig; mod inline; pub use inline::{validate_profiles, InlineConfig, InlineConfigError, InlineConfigParser, NatSpec}; +mod vyper; +use vyper::VyperConfig; + /// Foundry configuration /// /// # Defaults @@ -194,8 +194,6 @@ pub struct Config { /// **Note** for backwards compatibility reasons this also accepts solc_version from the toml /// file, see `BackwardsCompatTomlProvider`. pub solc: Option, - /// The Vyper instance to use if any. - pub vyper: Option, /// whether to autodetect the solc compiler version to use pub auto_detect_solc: bool, /// Offline mode, if set, network access (downloading solc) is disallowed. @@ -407,9 +405,8 @@ pub struct Config { /// CREATE2 salt to use for the library deployment in scripts. pub create2_library_salt: B256, - /// Compiler to use - #[serde(with = "from_str_lowercase")] - pub lang: Language, + /// Configuration for Vyper compiler + pub vyper: VyperConfig, /// The root path where the config detection started from, `Config::with_root` #[doc(hidden)] @@ -454,7 +451,7 @@ impl Config { /// Standalone sections in the config which get integrated into the selected profile pub const STANDALONE_SECTIONS: &'static [&'static str] = - &["rpc_endpoints", "etherscan", "fmt", "doc", "fuzz", "invariant", "labels"]; + &["rpc_endpoints", "etherscan", "fmt", "doc", "fuzz", "invariant", "labels", "vyper"]; /// File name of config toml file pub const FILE_NAME: &'static str = "foundry.toml"; @@ -937,7 +934,7 @@ impl Config { /// Returns configured [Vyper] compiler. pub fn vyper_compiler(&self) -> Result, SolcError> { - let vyper = if let Some(path) = &self.vyper { + let vyper = if let Some(path) = &self.vyper.path { Some(Vyper::new(path)?) } else { Vyper::new("vyper").ok() @@ -1283,7 +1280,7 @@ impl Config { pub fn vyper_settings(&self) -> Result { Ok(VyperSettings { evm_version: Some(self.evm_version), - optimize: None, + optimize: self.vyper.optimize, bytecode_metadata: None, // TODO: We don't yet have a way to deserialize other outputs correctly, so request only // those for now. It should be enough to run tests and deploy contracts. @@ -2027,7 +2024,7 @@ impl Default for Config { gas_reports: vec!["*".to_string()], gas_reports_ignore: vec![], solc: None, - vyper: None, + vyper: Default::default(), auto_detect_solc: true, offline: false, optimizer: true, @@ -2100,7 +2097,6 @@ impl Default for Config { labels: Default::default(), unchecked_cheatcode_artifacts: false, create2_library_salt: Config::DEFAULT_CREATE2_LIBRARY_SALT, - lang: Language::Solidity, skip: vec![], __non_exhaustive: (), __warnings: vec![], @@ -2803,7 +2799,10 @@ mod tests { etherscan::ResolvedEtherscanConfigs, }; use figment::error::Kind::InvalidType; - use foundry_compilers::artifacts::{ModelCheckerEngine, YulDetails}; + use foundry_compilers::{ + artifacts::{ModelCheckerEngine, YulDetails}, + compilers::vyper::settings::VyperOptimizationMode, + }; use similar_asserts::assert_eq; use std::{collections::BTreeMap, fs::File, io::Write}; use tempfile::tempdir; @@ -4934,4 +4933,29 @@ mod tests { Ok(()) }); } + + #[test] + fn test_parse_vyper() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "foundry.toml", + r#" + [vyper] + optimize = "codesize" + path = "/path/to/vyper" + "#, + )?; + + let config = Config::load(); + assert_eq!( + config.vyper, + VyperConfig { + optimize: Some(VyperOptimizationMode::Codesize), + path: Some("/path/to/vyper".into()) + } + ); + + Ok(()) + }); + } } diff --git a/crates/config/src/vyper.rs b/crates/config/src/vyper.rs new file mode 100644 index 000000000..79d3fc01d --- /dev/null +++ b/crates/config/src/vyper.rs @@ -0,0 +1,15 @@ +//! Vyper specific configuration types. + +use foundry_compilers::compilers::vyper::settings::VyperOptimizationMode; +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct VyperConfig { + /// Vyper optimization mode. "gas", "none" or "codesize" + #[serde(default, skip_serializing_if = "Option::is_none")] + pub optimize: Option, + /// The Vyper instance to use if any. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub path: Option, +} diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index f26f8243e..12b2a2b84 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -9,7 +9,7 @@ use foundry_compilers::{ use foundry_config::{ cache::{CachedChains, CachedEndpoints, StorageCachingConfig}, fs_permissions::{FsAccessPermission, PathPermission}, - Config, FsPermissions, FuzzConfig, InvariantConfig, Language, SolcReq, + Config, FsPermissions, FuzzConfig, InvariantConfig, SolcReq, }; use foundry_evm::opts::EvmOpts; use foundry_test_utils::{ @@ -43,7 +43,6 @@ forgetest!(can_extract_config_values, |prj, cmd| { gas_reports: vec!["Contract".to_string()], gas_reports_ignore: vec![], solc: Some(SolcReq::Local(PathBuf::from("custom-solc"))), - vyper: Some(PathBuf::from("custom-vyper")), auto_detect_solc: false, auto_detect_remappings: true, offline: true, @@ -136,7 +135,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { isolate: true, unchecked_cheatcode_artifacts: false, create2_library_salt: Config::DEFAULT_CREATE2_LIBRARY_SALT, - lang: Language::Solidity, + vyper: Default::default(), skip: vec![], __non_exhaustive: (), __warnings: vec![], From ebfdefb7dca2515ab15c5035aa4b31bd8e0d6081 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Thu, 6 Jun 2024 22:49:34 +0300 Subject: [PATCH 367/430] fix(invariant): prevent new values leaking between runs (#8080) * fix(invariant): prevent new values leaking between runs * Changes after review: remove collected flag keep len of values and addresses from db and use them to revert new values * Fix typo * SImplify revert: use truncate --- crates/evm/fuzz/src/strategies/state.rs | 105 +++++++++++------------- 1 file changed, 46 insertions(+), 59 deletions(-) diff --git a/crates/evm/fuzz/src/strategies/state.rs b/crates/evm/fuzz/src/strategies/state.rs index 59a037156..39dd3a467 100644 --- a/crates/evm/fuzz/src/strategies/state.rs +++ b/crates/evm/fuzz/src/strategies/state.rs @@ -49,7 +49,7 @@ impl EvmFuzzState { pub fn collect_values(&self, values: impl IntoIterator) { let mut dict = self.inner.write(); for value in values { - dict.insert_value(value, true); + dict.insert_value(value); } } @@ -69,12 +69,12 @@ impl EvmFuzzState { dict.insert_logs_values(target_abi, logs, run_depth); dict.insert_result_values(target_function, result, run_depth); }); - dict.insert_state_values(state_changeset); + dict.insert_new_state_values(state_changeset); } /// Removes all newly added entries from the dictionary. /// - /// Should be called between fuzz/invariant runs to avoid accumumlating data derived from fuzz + /// Should be called between fuzz/invariant runs to avoid accumulating data derived from fuzz /// inputs. pub fn revert(&self) { self.inner.write().revert(); @@ -100,10 +100,12 @@ pub struct FuzzDictionary { addresses: AIndexSet
, /// Configuration for the dictionary. config: FuzzDictionaryConfig, - /// New key indexes added to the dictionary since container initialization. - new_values: Vec, - /// New address indexes added to the dictionary since container initialization. - new_addreses: Vec, + /// Number of state values initially collected from db. + /// Used to revert new collected values at the end of each run. + db_state_values: usize, + /// Number of address values initially collected from db. + /// Used to revert new collected addresses at the end of each run. + db_addresses: usize, /// Sample typed values that are collected from call result and used across invariant runs. sample_values: HashMap>, @@ -129,24 +131,23 @@ impl FuzzDictionary { /// Insert common values into the dictionary at initialization. fn prefill(&mut self) { - self.insert_value(B256::ZERO, false); + self.insert_value(B256::ZERO); } /// Insert values from initial db state into fuzz dictionary. /// These values are persisted across invariant runs. fn insert_db_values(&mut self, db_state: Vec<(&Address, &DbAccount)>) { - let collected = false; for (address, account) in db_state { // Insert basic account information - self.insert_value(address.into_word(), collected); + self.insert_value(address.into_word()); // Insert push bytes - self.insert_push_bytes_values(address, &account.info, collected); + self.insert_push_bytes_values(address, &account.info); // Insert storage values. if self.config.include_storage { // Sort storage values before inserting to ensure deterministic dictionary. let values = account.storage.iter().collect::>(); for (slot, value) in values { - self.insert_storage_value(slot, value, collected); + self.insert_storage_value(slot, value); } } } @@ -155,8 +156,13 @@ impl FuzzDictionary { // otherwise we can't select random data for state fuzzing. if self.values().is_empty() { // Prefill with a random address. - self.insert_value(Address::random().into_word(), false); + self.insert_value(Address::random().into_word()); } + + // Record number of values and addresses inserted from db to be used for reverting at the + // end of each run. + self.db_state_values = self.state_values.len(); + self.db_addresses = self.addresses.len(); } /// Insert values collected from call result into fuzz dictionary. @@ -197,15 +203,15 @@ impl FuzzDictionary { // If we weren't able to decode event then we insert raw data in fuzz dictionary. if !log_decoded { for &topic in log.topics() { - self.insert_value(topic, true); + self.insert_value(topic); } let chunks = log.data.data.chunks_exact(32); let rem = chunks.remainder(); for chunk in chunks { - self.insert_value(chunk.try_into().unwrap(), true); + self.insert_value(chunk.try_into().unwrap()); } if !rem.is_empty() { - self.insert_value(B256::right_padding_from(rem), true); + self.insert_value(B256::right_padding_from(rem)); } } } @@ -216,17 +222,16 @@ impl FuzzDictionary { /// Insert values from call state changeset into fuzz dictionary. /// These values are removed at the end of current run. - fn insert_state_values(&mut self, state_changeset: &StateChangeset) { - let collected = true; + fn insert_new_state_values(&mut self, state_changeset: &StateChangeset) { for (address, account) in state_changeset { // Insert basic account information. - self.insert_value(address.into_word(), collected); + self.insert_value(address.into_word()); // Insert push bytes. - self.insert_push_bytes_values(address, &account.info, collected); + self.insert_push_bytes_values(address, &account.info); // Insert storage values. if self.config.include_storage { for (slot, value) in &account.storage { - self.insert_storage_value(slot, &value.present_value, collected); + self.insert_storage_value(slot, &value.present_value); } } } @@ -235,22 +240,17 @@ impl FuzzDictionary { /// Insert values from push bytes into fuzz dictionary. /// Values are collected only once for a given address. /// If values are newly collected then they are removed at the end of current run. - fn insert_push_bytes_values( - &mut self, - address: &Address, - account_info: &AccountInfo, - collected: bool, - ) { + fn insert_push_bytes_values(&mut self, address: &Address, account_info: &AccountInfo) { if self.config.include_push_bytes && !self.addresses.contains(address) { // Insert push bytes if let Some(code) = &account_info.code { - self.insert_address(*address, collected); - self.collect_push_bytes(code.bytes_slice(), collected); + self.insert_address(*address); + self.collect_push_bytes(code.bytes_slice()); } } } - fn collect_push_bytes(&mut self, code: &[u8], collected: bool) { + fn collect_push_bytes(&mut self, code: &[u8]) { let mut i = 0; let len = code.len().min(PUSH_BYTE_ANALYSIS_LIMIT); while i < len { @@ -268,13 +268,13 @@ impl FuzzDictionary { let push_value = U256::try_from_be_slice(&code[push_start..push_end]).unwrap(); if push_value != U256::ZERO { // Never add 0 to the dictionary as it's always present. - self.insert_value(push_value.into(), collected); + self.insert_value(push_value.into()); // Also add the value below and above the push value to the dictionary. - self.insert_value((push_value - U256::from(1)).into(), collected); + self.insert_value((push_value - U256::from(1)).into()); if push_value != U256::MAX { - self.insert_value((push_value + U256::from(1)).into(), collected); + self.insert_value((push_value + U256::from(1)).into()); } } @@ -286,41 +286,35 @@ impl FuzzDictionary { /// Insert values from single storage slot and storage value into fuzz dictionary. /// If storage values are newly collected then they are removed at the end of current run. - fn insert_storage_value(&mut self, storage_slot: &U256, storage_value: &U256, collected: bool) { - self.insert_value(B256::from(*storage_slot), collected); - self.insert_value(B256::from(*storage_value), collected); + fn insert_storage_value(&mut self, storage_slot: &U256, storage_value: &U256) { + self.insert_value(B256::from(*storage_slot)); + self.insert_value(B256::from(*storage_value)); // also add the value below and above the storage value to the dictionary. if *storage_value != U256::ZERO { let below_value = storage_value - U256::from(1); - self.insert_value(B256::from(below_value), collected); + self.insert_value(B256::from(below_value)); } if *storage_value != U256::MAX { let above_value = storage_value + U256::from(1); - self.insert_value(B256::from(above_value), collected); + self.insert_value(B256::from(above_value)); } } /// Insert address into fuzz dictionary. /// If address is newly collected then it is removed by index at the end of current run. - fn insert_address(&mut self, address: Address, collected: bool) { + fn insert_address(&mut self, address: Address) { if self.addresses.len() < self.config.max_fuzz_dictionary_addresses { - let (index, new_address) = self.addresses.insert_full(address); - if new_address && collected { - self.new_addreses.push(index); - } + self.addresses.insert(address); } } /// Insert raw value into fuzz dictionary. /// If value is newly collected then it is removed by index at the end of current run. - fn insert_value(&mut self, value: B256, collected: bool) { + fn insert_value(&mut self, value: B256) { if self.state_values.len() < self.config.max_fuzz_dictionary_values { - let (index, new_value) = self.state_values.insert_full(value); + let new_value = self.state_values.insert(value); let counter = if new_value { &mut self.misses } else { &mut self.hits }; *counter += 1; - if new_value && collected { - self.new_values.push(index); - } } } @@ -339,7 +333,7 @@ impl FuzzDictionary { values.insert(sample_value); } else { // Insert as state value (will be removed at the end of the run). - self.insert_value(sample_value, true); + self.insert_value(sample_value); } } else { self.sample_values.entry(sample_type).or_default().insert(sample_value); @@ -370,17 +364,10 @@ impl FuzzDictionary { &self.addresses } + /// Revert values and addresses collected during the run by truncating to initial db len. pub fn revert(&mut self) { - // Revert new values collected during the run. - for &value_index in &self.new_values { - self.state_values.swap_remove_index(value_index); - } - for &address_index in &self.new_addreses { - self.addresses.swap_remove_index(address_index); - } - - self.new_values.clear(); - self.new_addreses.clear(); + self.state_values.truncate(self.db_state_values); + self.addresses.truncate(self.db_addresses); } pub fn log_stats(&self) { From 169f83fcb8252fa37f642c1e77a22640e95f0864 Mon Sep 17 00:00:00 2001 From: m4rio <92288535+mario-eth@users.noreply.github.com> Date: Thu, 6 Jun 2024 14:30:44 -0700 Subject: [PATCH 368/430] feat(forge): [ALPHA] add soldeer as an optional package manager. (#7161) * Soldeer integration * Making the sdependencies to be a recognized config * Updated soldeer version to 0.2.6 * updated soldeer version * Updated to soldeer 0.2.8 and simplified the command parsing on foundry * Update crates/forge/bin/cmd/soldeer.rs Co-authored-by: Oliver Nordbjerg * made the config more typed * added cli tests * chore: fmt * updated soldeer * solved the url dependency * updated soldeer version and added a test to confirm that it works with simplified version * removed the v from the forge dependency * Added custom type for soldeer config * moved the SoldeerConfig to the soldeer.rs and transformed it into AsRef * added constant_time_eq in deny.toml * Updated soldeer to v0.2.12 to disable TLS * clippy fixes * fmt * updated latest soldeer version * bugfix install dependency * bumped soldeer to v0.2.15 * clippy fixes --------- Co-authored-by: Oliver Nordbjerg Co-authored-by: Oliver Nordbjerg Co-authored-by: Matthias Seitz --- Cargo.lock | 225 +++++++++++++++++++++++++++++- Cargo.toml | 2 + crates/config/src/lib.rs | 20 ++- crates/config/src/soldeer.rs | 24 ++++ crates/forge/Cargo.toml | 4 + crates/forge/bin/cmd/mod.rs | 1 + crates/forge/bin/cmd/soldeer.rs | 37 +++++ crates/forge/bin/main.rs | 1 + crates/forge/bin/opts.rs | 5 +- crates/forge/tests/cli/config.rs | 1 + crates/forge/tests/cli/main.rs | 1 + crates/forge/tests/cli/soldeer.rs | 182 ++++++++++++++++++++++++ crates/forge/tests/it/fork.rs | 2 +- crates/test-utils/src/macros.rs | 17 +++ deny.toml | 1 + 15 files changed, 516 insertions(+), 7 deletions(-) create mode 100644 crates/config/src/soldeer.rs create mode 100644 crates/forge/bin/cmd/soldeer.rs create mode 100644 crates/forge/tests/cli/soldeer.rs diff --git a/Cargo.lock b/Cargo.lock index 263fa9de3..6f3f8da53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1688,6 +1688,27 @@ dependencies = [ "either", ] +[[package]] +name = "bzip2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "c-kzg" version = "1.0.2" @@ -1818,6 +1839,11 @@ name = "cc" version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +dependencies = [ + "jobserver", + "libc", + "once_cell", +] [[package]] name = "cfg-if" @@ -1879,6 +1905,7 @@ dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", + "serde", "windows-targets 0.52.5", ] @@ -2163,6 +2190,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + [[package]] name = "const-hex" version = "1.12.0" @@ -2182,6 +2219,12 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + [[package]] name = "convert_case" version = "0.4.0" @@ -2706,6 +2749,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "email-address-parser" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe19a4967eca30062be4abaf813d929ba48b3bfb21830367f7e1baae37f213a" +dependencies = [ + "console_error_panic_hook", + "pest", + "pest_derive", + "quick-xml", + "wasm-bindgen", +] + [[package]] name = "ena" version = "0.14.3" @@ -3159,6 +3215,7 @@ dependencies = [ "similar", "similar-asserts", "solang-parser", + "soldeer", "strum", "svm-rs", "tempfile", @@ -4768,6 +4825,15 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "jobserver" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" version = "0.3.69" @@ -5638,6 +5704,17 @@ dependencies = [ "windows-targets 0.52.5", ] +[[package]] +name = "password-hash" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + [[package]] name = "paste" version = "1.0.15" @@ -5657,6 +5734,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ "digest 0.10.7", + "hmac", + "password-hash", + "sha2", ] [[package]] @@ -6153,6 +6233,15 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +[[package]] +name = "quick-xml" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cc440ee4802a86e357165021e3e255a9143724da31db1e2ea540214c96a0f82" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.36" @@ -6364,6 +6453,7 @@ dependencies = [ "js-sys", "log", "mime", + "mime_guess", "native-tls", "once_cell", "percent-encoding", @@ -6379,10 +6469,12 @@ dependencies = [ "tokio", "tokio-native-tls", "tokio-rustls 0.25.0", + "tokio-util", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", "webpki-roots 0.26.1", "winreg", @@ -7131,6 +7223,19 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha256" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18278f6a914fa3070aa316493f7d2ddfb9ac86ebc06fa3b83bffda487e9065b0" +dependencies = [ + "async-trait", + "bytes", + "hex", + "sha2", + "tokio", +] + [[package]] name = "sha3" version = "0.10.8" @@ -7238,6 +7343,15 @@ dependencies = [ "similar", ] +[[package]] +name = "simple-home-dir" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c433538e900807402974e89beb88a98bda36e6f70f09a7225cdf11d013b8efe8" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "simple_asn1" version = "0.6.2" @@ -7295,6 +7409,35 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "soldeer" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfdc15c518ac6bcdc09565cfcda5d0926c65dd2153fea83fcaf1cd536b0d26f7" +dependencies = [ + "chrono", + "clap", + "email-address-parser", + "futures", + "once_cell", + "regex", + "reqwest", + "rpassword", + "serde", + "serde_derive", + "serde_json", + "sha256", + "simple-home-dir", + "tokio", + "toml 0.8.13", + "toml_edit 0.22.13", + "uuid 1.8.0", + "walkdir", + "yansi", + "zip 2.1.3", + "zip-extract", +] + [[package]] name = "spin" version = "0.5.2" @@ -7435,7 +7578,7 @@ dependencies = [ "sha2", "thiserror", "url", - "zip", + "zip 2.1.3", ] [[package]] @@ -8204,6 +8347,10 @@ name = "uuid" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +dependencies = [ + "getrandom", + "serde", +] [[package]] name = "valuable" @@ -8341,6 +8488,19 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "wasm-streams" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "watchexec" version = "2.3.2" @@ -8799,9 +8959,28 @@ dependencies = [ [[package]] name = "zip" -version = "2.1.1" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd56a4d5921bc2f99947ac5b3abe5f510b1be7376fdc5e9fce4a23c6a93e87c" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +dependencies = [ + "aes", + "byteorder", + "bzip2", + "constant_time_eq", + "crc32fast", + "crossbeam-utils", + "flate2", + "hmac", + "pbkdf2 0.11.0", + "sha1", + "zstd", +] + +[[package]] +name = "zip" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775a2b471036342aa69bc5a602bc889cb0a06cda00477d0c69566757d5553d39" dependencies = [ "arbitrary", "crc32fast", @@ -8814,6 +8993,17 @@ dependencies = [ "zopfli", ] +[[package]] +name = "zip-extract" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e109e5a291403b4c1e514d39f8a22d3f98d257e691a52bb1f16051bb1ffed63e" +dependencies = [ + "log", + "thiserror", + "zip 0.6.6", +] + [[package]] name = "zopfli" version = "0.8.1" @@ -8827,3 +9017,32 @@ dependencies = [ "once_cell", "simd-adler32", ] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.10+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml index eac8025a4..1751f6fa2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -239,6 +239,8 @@ hyper = "1.0" reqwest = { version = "0.12", default-features = false } tower = "0.4" tower-http = "0.5" +# soldeer +soldeer = "0.2.15" [patch.crates-io] revm = { git = "https://github.com/bluealloy/revm.git", rev = "a28a543" } diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 9a3885fbb..eaaee87c2 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -104,6 +104,9 @@ pub use invariant::InvariantConfig; mod inline; pub use inline::{validate_profiles, InlineConfig, InlineConfigError, InlineConfigParser, NatSpec}; +pub mod soldeer; +use soldeer::SoldeerConfig; + mod vyper; use vyper::VyperConfig; @@ -408,6 +411,9 @@ pub struct Config { /// Configuration for Vyper compiler pub vyper: VyperConfig, + /// Soldeer dependencies + pub dependencies: Option, + /// The root path where the config detection started from, `Config::with_root` #[doc(hidden)] // We're skipping serialization here, so it won't be included in the [`Config::to_string()`] @@ -450,8 +456,17 @@ impl Config { pub const PROFILE_SECTION: &'static str = "profile"; /// Standalone sections in the config which get integrated into the selected profile - pub const STANDALONE_SECTIONS: &'static [&'static str] = - &["rpc_endpoints", "etherscan", "fmt", "doc", "fuzz", "invariant", "labels", "vyper"]; + pub const STANDALONE_SECTIONS: &'static [&'static str] = &[ + "rpc_endpoints", + "etherscan", + "fmt", + "doc", + "fuzz", + "invariant", + "labels", + "dependencies", + "vyper", + ]; /// File name of config toml file pub const FILE_NAME: &'static str = "foundry.toml"; @@ -2098,6 +2113,7 @@ impl Default for Config { unchecked_cheatcode_artifacts: false, create2_library_salt: Config::DEFAULT_CREATE2_LIBRARY_SALT, skip: vec![], + dependencies: Default::default(), __non_exhaustive: (), __warnings: vec![], } diff --git a/crates/config/src/soldeer.rs b/crates/config/src/soldeer.rs new file mode 100644 index 000000000..5c1d01aa6 --- /dev/null +++ b/crates/config/src/soldeer.rs @@ -0,0 +1,24 @@ +//! Configuration specific to the `forge soldeer` command and the `forge_soldeer` package + +use std::collections::BTreeMap; + +use serde::{Deserialize, Serialize}; + +/// Soldeer dependencies config structure +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct SoldeerDependency { + /// The version of the dependency + pub version: String, + + /// The url from where the dependency was retrieved + pub url: String, +} + +/// Type for Soldeer configs, under dependencies tag in the foundry.toml +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct SoldeerConfig(BTreeMap); +impl AsRef for SoldeerConfig { + fn as_ref(&self) -> &SoldeerConfig { + self + } +} diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index eec949610..dcd40a4aa 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -99,9 +99,13 @@ hyper.workspace = true tower-http = { workspace = true, features = ["fs"] } opener = "0.6" +# soldeer +soldeer.workspace = true + [target.'cfg(unix)'.dependencies] tikv-jemallocator = { workspace = true, optional = true } + [dev-dependencies] anvil.workspace = true foundry-test-utils.workspace = true diff --git a/crates/forge/bin/cmd/mod.rs b/crates/forge/bin/cmd/mod.rs index c8d1dbb0e..d3e2f8b6d 100644 --- a/crates/forge/bin/cmd/mod.rs +++ b/crates/forge/bin/cmd/mod.rs @@ -59,6 +59,7 @@ pub mod remappings; pub mod remove; pub mod selectors; pub mod snapshot; +pub mod soldeer; pub mod test; pub mod tree; pub mod update; diff --git a/crates/forge/bin/cmd/soldeer.rs b/crates/forge/bin/cmd/soldeer.rs new file mode 100644 index 000000000..52e1240a0 --- /dev/null +++ b/crates/forge/bin/cmd/soldeer.rs @@ -0,0 +1,37 @@ +use clap::Parser; +use eyre::Result; + +use soldeer::commands::Subcommands; + +// CLI arguments for `forge soldeer`. +#[derive(Clone, Debug, Parser)] +#[clap(override_usage = "forge soldeer install [DEPENDENCY]~[VERSION] + forge soldeer push [DEPENDENCY]~[VERSION] + forge soldeer login + forge soldeer update + forge soldeer version")] +pub struct SoldeerArgs { + /// Command must be one of the following install/push/login/update/version. + #[command(subcommand)] + command: Subcommands, +} + +impl SoldeerArgs { + pub fn run(self) -> Result<()> { + match soldeer::run(self.command) { + Ok(_) => Ok(()), + Err(err) => Err(eyre::eyre!("Failed to run soldeer {}", err.message)), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use soldeer::commands::VersionDryRun; + + #[test] + fn test_soldeer_version() { + assert!(soldeer::run(Subcommands::VersionDryRun(VersionDryRun {})).is_ok()); + } +} diff --git a/crates/forge/bin/main.rs b/crates/forge/bin/main.rs index 8db9036e1..aff2ad530 100644 --- a/crates/forge/bin/main.rs +++ b/crates/forge/bin/main.rs @@ -111,6 +111,7 @@ fn main() -> Result<()> { GenerateSubcommands::Test(cmd) => cmd.run(), }, ForgeSubcommand::VerifyBytecode(cmd) => utils::block_on(cmd.run()), + ForgeSubcommand::Soldeer(cmd) => cmd.run(), } } diff --git a/crates/forge/bin/opts.rs b/crates/forge/bin/opts.rs index 6ca78da0f..a449bd75f 100644 --- a/crates/forge/bin/opts.rs +++ b/crates/forge/bin/opts.rs @@ -2,7 +2,7 @@ use crate::cmd::{ bind::BindArgs, build::BuildArgs, cache::CacheArgs, clone::CloneArgs, config, coverage, create::CreateArgs, debug::DebugArgs, doc::DocArgs, flatten, fmt::FmtArgs, geiger, generate, init::InitArgs, inspect, install::InstallArgs, remappings::RemappingArgs, remove::RemoveArgs, - selectors::SelectorsSubcommands, snapshot, test, tree, update, + selectors::SelectorsSubcommands, snapshot, soldeer, test, tree, update, }; use clap::{Parser, Subcommand, ValueHint}; use forge_script::ScriptArgs; @@ -161,6 +161,9 @@ pub enum ForgeSubcommand { /// Verify the deployed bytecode against its source. #[clap(visible_alias = "vb")] VerifyBytecode(VerifyBytecodeArgs), + + /// Soldeer dependency manager. + Soldeer(soldeer::SoldeerArgs), } #[cfg(test)] diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 12b2a2b84..073a6e451 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -137,6 +137,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { create2_library_salt: Config::DEFAULT_CREATE2_LIBRARY_SALT, vyper: Default::default(), skip: vec![], + dependencies: Default::default(), __non_exhaustive: (), __warnings: vec![], }; diff --git a/crates/forge/tests/cli/main.rs b/crates/forge/tests/cli/main.rs index 543b84dc7..0ac67d81b 100644 --- a/crates/forge/tests/cli/main.rs +++ b/crates/forge/tests/cli/main.rs @@ -15,6 +15,7 @@ mod debug; mod doc; mod multi_script; mod script; +mod soldeer; mod svm; mod test_cmd; mod verify; diff --git a/crates/forge/tests/cli/soldeer.rs b/crates/forge/tests/cli/soldeer.rs new file mode 100644 index 000000000..7f762e8dd --- /dev/null +++ b/crates/forge/tests/cli/soldeer.rs @@ -0,0 +1,182 @@ +//! Contains various tests related to `forge soldeer`. + +use std::{ + fs::{self, OpenOptions}, + path::Path, +}; + +use foundry_test_utils::forgesoldeer; +use std::io::Write; +forgesoldeer!(install_dependency, |prj, cmd| { + let command = "install"; + let dependency = "forge-std~1.8.1"; + + cmd.arg("soldeer").args([command, dependency]); + cmd.execute(); + + // Making sure the path was created to the dependency and that foundry.toml exists + // meaning that the dependencies were installed correctly + let path_dep_forge = + prj.root().join("dependencies").join("forge-std-1.8.1").join("foundry.toml"); + assert!(path_dep_forge.exists()); + + // Making sure the lock contents are the right ones + let path_lock_file = prj.root().join("soldeer.lock"); + let lock_contents = r#" +[[dependencies]] +name = "forge-std" +version = "1.8.1" +source = "https://soldeer-revisions.s3.amazonaws.com/forge-std/v1_8_1_23-03-2024_00:05:44_forge-std-v1.8.1.zip" +checksum = "0f7cd44f5670c31a9646d4031e70c66321cd3ed6ebac3c7278e4e57e4e5c5bd0" +"#; + + let actual_lock_contents = read_file_to_string(&path_lock_file); + assert_eq!(lock_contents, actual_lock_contents); + + // Making sure the foundry contents are the right ones + let foundry_file = prj.root().join("foundry.toml"); + let foundry_contents = r#"[profile.default] +src = "src" +out = "out" +libs = ["lib"] + +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options + +[dependencies] +forge-std = { version = "1.8.1" } +"#; + + let actual_foundry_contents = read_file_to_string(&foundry_file); + assert_eq!(foundry_contents, actual_foundry_contents); +}); + +forgesoldeer!(update_dependencies, |prj, cmd| { + let command = "update"; + + // We need to write this into the foundry.toml to make the update install the dependency + let foundry_updates = r#" +[dependencies] +forge-std = { version = "1.8.1" } +"#; + let foundry_file = prj.root().join("foundry.toml"); + + let mut file = OpenOptions::new().append(true).open(&foundry_file).unwrap(); + + if let Err(e) = write!(file, "{foundry_updates}") { + eprintln!("Couldn't write to file: {e}"); + } + + cmd.arg("soldeer").arg(command); + cmd.execute(); + + // Making sure the path was created to the dependency and that foundry.toml exists + // meaning that the dependencies were installed correctly + let path_dep_forge = + prj.root().join("dependencies").join("forge-std-1.8.1").join("foundry.toml"); + assert!(path_dep_forge.exists()); + + // Making sure the lock contents are the right ones + let path_lock_file = prj.root().join("soldeer.lock"); + let lock_contents = r#" +[[dependencies]] +name = "forge-std" +version = "1.8.1" +source = "https://soldeer-revisions.s3.amazonaws.com/forge-std/v1_8_1_23-03-2024_00:05:44_forge-std-v1.8.1.zip" +checksum = "0f7cd44f5670c31a9646d4031e70c66321cd3ed6ebac3c7278e4e57e4e5c5bd0" +"#; + + let actual_lock_contents = read_file_to_string(&path_lock_file); + assert_eq!(lock_contents, actual_lock_contents); + + // Making sure the foundry contents are the right ones + let foundry_contents = r#"[profile.default] +src = "src" +out = "out" +libs = ["lib"] + +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options + +[dependencies] +forge-std = { version = "1.8.1" } +"#; + + let actual_foundry_contents = read_file_to_string(&foundry_file); + assert_eq!(foundry_contents, actual_foundry_contents); +}); + +forgesoldeer!(update_dependencies_simple_version, |prj, cmd| { + let command = "update"; + + // We need to write this into the foundry.toml to make the update install the dependency, this + // is he simplified version of version specification + let foundry_updates = r#" +[dependencies] +forge-std = "1.8.1" +"#; + let foundry_file = prj.root().join("foundry.toml"); + + let mut file = OpenOptions::new().append(true).open(&foundry_file).unwrap(); + + if let Err(e) = write!(file, "{foundry_updates}") { + eprintln!("Couldn't write to file: {e}"); + } + + cmd.arg("soldeer").arg(command); + cmd.execute(); + + // Making sure the path was created to the dependency and that foundry.toml exists + // meaning that the dependencies were installed correctly + let path_dep_forge = + prj.root().join("dependencies").join("forge-std-1.8.1").join("foundry.toml"); + assert!(path_dep_forge.exists()); + + // Making sure the lock contents are the right ones + let path_lock_file = prj.root().join("soldeer.lock"); + let lock_contents = r#" +[[dependencies]] +name = "forge-std" +version = "1.8.1" +source = "https://soldeer-revisions.s3.amazonaws.com/forge-std/v1_8_1_23-03-2024_00:05:44_forge-std-v1.8.1.zip" +checksum = "0f7cd44f5670c31a9646d4031e70c66321cd3ed6ebac3c7278e4e57e4e5c5bd0" +"#; + + let actual_lock_contents = read_file_to_string(&path_lock_file); + assert_eq!(lock_contents, actual_lock_contents); + + // Making sure the foundry contents are the right ones + let foundry_contents = r#"[profile.default] +src = "src" +out = "out" +libs = ["lib"] + +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options + +[dependencies] +forge-std = "1.8.1" +"#; + + let actual_foundry_contents = read_file_to_string(&foundry_file); + assert_eq!(foundry_contents, actual_foundry_contents); +}); + +forgesoldeer!(login, |prj, cmd| { + let command = "login"; + + cmd.arg("soldeer").arg(command); + let output = cmd.unchecked_output(); + + // On login, we can only check if the prompt is displayed in the stdout + let stdout = String::from_utf8(output.stdout).expect("Could not parse the output"); + assert!(stdout.contains("Please enter your email")); +}); + +fn read_file_to_string(path: &Path) -> String { + let contents: String = match fs::read_to_string(path) { + Ok(c) => c, + Err(_) => { + // Return empty string + String::new() + } + }; + contents +} diff --git a/crates/forge/tests/it/fork.rs b/crates/forge/tests/it/fork.rs index 3f3576ac4..a6b215624 100644 --- a/crates/forge/tests/it/fork.rs +++ b/crates/forge/tests/it/fork.rs @@ -91,7 +91,7 @@ async fn test_transact_fork() { TestConfig::with_filter(runner, filter).run().await; } -/// Tests that we can create the same fork (provider,block) concurretnly in different tests +/// Tests that we can create the same fork (provider,block) concurrently in different tests #[tokio::test(flavor = "multi_thread")] async fn test_create_same_fork() { let runner = TEST_DATA_DEFAULT.runner(); diff --git a/crates/test-utils/src/macros.rs b/crates/test-utils/src/macros.rs index f05c8e36b..1f19adffe 100644 --- a/crates/test-utils/src/macros.rs +++ b/crates/test-utils/src/macros.rs @@ -92,3 +92,20 @@ macro_rules! forgetest_init { } }; } + +/// Setup forge soldeer +#[macro_export] +macro_rules! forgesoldeer { + ($(#[$attr:meta])* $test:ident, |$prj:ident, $cmd:ident| $e:expr) => { + $crate::forgesoldeer!($(#[$attr])* $test, $crate::foundry_compilers::PathStyle::Dapptools, |$prj, $cmd| $e); + }; + ($(#[$attr:meta])* $test:ident, $style:expr, |$prj:ident, $cmd:ident| $e:expr) => { + #[test] + $(#[$attr])* + fn $test() { + let (mut $prj, mut $cmd) = $crate::util::setup_forge(stringify!($test), $style); + $crate::util::initialize($prj.root()); + $e + } + }; +} diff --git a/deny.toml b/deny.toml index cdde3ec0c..d585fd650 100644 --- a/deny.toml +++ b/deny.toml @@ -59,6 +59,7 @@ exceptions = [ { allow = ["CC0-1.0"], name = "notify" }, { allow = ["CC0-1.0"], name = "dunce" }, { allow = ["CC0-1.0"], name = "aurora-engine-modexp" }, + { allow = ["CC0-1.0"], name = "constant_time_eq" }, ] #copyleft = "deny" From 31b70cc241881c5bfdcd8d4dae236b173f6b2376 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 6 Jun 2024 23:46:12 +0200 Subject: [PATCH 369/430] feat(cast): alias keccak256 to keccak (#8090) --- crates/cast/bin/opts.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cast/bin/opts.rs b/crates/cast/bin/opts.rs index 4a9a00992..1a36d2893 100644 --- a/crates/cast/bin/opts.rs +++ b/crates/cast/bin/opts.rs @@ -714,7 +714,7 @@ pub enum CastSubcommand { }, /// Hash arbitrary data using Keccak-256. - #[command(visible_alias = "k")] + #[command(visible_aliases = &["k", "keccak256"])] Keccak { /// The data to hash. data: Option, From a9a3c02bb241bf1968816edc6061aabeaf71e65f Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 6 Jun 2024 23:46:22 +0200 Subject: [PATCH 370/430] fix(cast): return an error if wallet validation failed (#8089) --- crates/cast/bin/cmd/wallet/mod.rs | 8 +++----- crates/cast/tests/cli/main.rs | 23 +++++++++++++++-------- crates/test-utils/src/util.rs | 8 ++++---- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/crates/cast/bin/cmd/wallet/mod.rs b/crates/cast/bin/cmd/wallet/mod.rs index b312e4a60..def14c17e 100644 --- a/crates/cast/bin/cmd/wallet/mod.rs +++ b/crates/cast/bin/cmd/wallet/mod.rs @@ -299,7 +299,7 @@ impl WalletSubcommands { if address == recovered_address { println!("Validation succeeded. Address {address} signed this message."); } else { - println!("Validation failed. Address {address} did not sign this message."); + eyre::bail!("Validation failed. Address {address} did not sign this message."); } } Self::Import { account_name, keystore_dir, unsafe_password, raw_wallet_options } => { @@ -428,11 +428,9 @@ flag to set your key via: #[cfg(test)] mod tests { - use std::str::FromStr; - - use alloy_primitives::address; - use super::*; + use alloy_primitives::address; + use std::str::FromStr; #[test] fn can_parse_wallet_sign_message() { diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 61594460f..30922cfb4 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -77,15 +77,22 @@ casttest!(wallet_address_keystore_with_password_file, |_prj, cmd| { // tests that `cast wallet sign message` outputs the expected signature casttest!(wallet_sign_message_utf8_data, |_prj, cmd| { - cmd.args([ - "wallet", - "sign", - "--private-key", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "test", - ]); + let pk = "0x0000000000000000000000000000000000000000000000000000000000000001"; + let address = "0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf"; + let msg = "test"; + let expected = "0xfe28833983d6faa0715c7e8c3873c725ddab6fa5bf84d40e780676e463e6bea20fc6aea97dc273a98eb26b0914e224c8dd5c615ceaab69ddddcf9b0ae3de0e371c"; + + cmd.args(["wallet", "sign", "--private-key", pk, msg]); let output = cmd.stdout_lossy(); - assert_eq!(output.trim(), "0xfe28833983d6faa0715c7e8c3873c725ddab6fa5bf84d40e780676e463e6bea20fc6aea97dc273a98eb26b0914e224c8dd5c615ceaab69ddddcf9b0ae3de0e371c"); + assert_eq!(output.trim(), expected); + + // Success. + cmd.cast_fuse() + .args(["wallet", "verify", "-a", address, msg, expected]) + .assert_non_empty_stdout(); + + // Fail. + cmd.cast_fuse().args(["wallet", "verify", "-a", address, "other msg", expected]).assert_err(); }); // tests that `cast wallet sign message` outputs the expected signature, given a 0x-prefixed data diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index b45803e66..e1a681db6 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -961,7 +961,7 @@ impl TestCommand { fs::write(format!("{}.stderr", name.display()), &output.stderr).unwrap(); } - /// Runs the command and asserts that it resulted in an error exit code. + /// Runs the command and asserts that it **failed** (resulted in an error exit code). #[track_caller] pub fn assert_err(&mut self) { let out = self.execute(); @@ -970,7 +970,7 @@ impl TestCommand { } } - /// Runs the command and asserts that something was printed to stderr. + /// Runs the command and asserts that it **failed** and something was printed to stderr. #[track_caller] pub fn assert_non_empty_stderr(&mut self) { let out = self.execute(); @@ -979,7 +979,7 @@ impl TestCommand { } } - /// Runs the command and asserts that something was printed to stdout. + /// Runs the command and asserts that it **succeeded** and something was printed to stdout. #[track_caller] pub fn assert_non_empty_stdout(&mut self) { let out = self.execute(); @@ -988,7 +988,7 @@ impl TestCommand { } } - /// Runs the command and asserts that nothing was printed to stdout. + /// Runs the command and asserts that it **failed** nothing was printed to stdout. #[track_caller] pub fn assert_empty_stdout(&mut self) { let out = self.execute(); From ad2ada4533a6443c0b5ef5795aa100e85c3f2faa Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 7 Jun 2024 00:35:02 +0200 Subject: [PATCH 371/430] chore: remove excess underscores from config fields (#8088) --- crates/cheatcodes/src/config.rs | 8 ++--- crates/cli/src/opts/build/core.rs | 2 +- crates/cli/src/utils/cmd.rs | 10 +++--- crates/cli/src/utils/mod.rs | 2 +- crates/config/src/fs_permissions.rs | 8 ++--- crates/config/src/lib.rs | 55 +++++++++++++++-------------- crates/forge/bin/cmd/fmt.rs | 4 +-- crates/forge/bin/cmd/geiger/mod.rs | 2 +- crates/forge/bin/cmd/update.rs | 2 +- crates/forge/bin/cmd/watch.rs | 2 +- crates/forge/tests/cli/config.rs | 6 ++-- crates/forge/tests/it/repros.rs | 2 +- crates/script/src/simulate.rs | 2 +- 13 files changed, 52 insertions(+), 53 deletions(-) diff --git a/crates/cheatcodes/src/config.rs b/crates/cheatcodes/src/config.rs index 54ba1dad1..5be433688 100644 --- a/crates/cheatcodes/src/config.rs +++ b/crates/cheatcodes/src/config.rs @@ -63,7 +63,7 @@ impl CheatsConfig { script_wallets: Option, running_version: Option, ) -> Self { - let mut allowed_paths = vec![config.__root.0.clone()]; + let mut allowed_paths = vec![config.root.0.clone()]; allowed_paths.extend(config.libs.clone()); allowed_paths.extend(config.allow_paths.clone()); @@ -82,8 +82,8 @@ impl CheatsConfig { no_storage_caching: config.no_storage_caching, rpc_endpoints, paths: config.project_paths(), - fs_permissions: config.fs_permissions.clone().joined(&config.__root), - root: config.__root.0.clone(), + fs_permissions: config.fs_permissions.clone().joined(config.root.as_ref()), + root: config.root.0.clone(), allowed_paths, evm_opts, labels: config.labels.clone(), @@ -228,7 +228,7 @@ mod tests { fn config(root: &str, fs_permissions: FsPermissions) -> CheatsConfig { CheatsConfig::new( - &Config { __root: PathBuf::from(root).into(), fs_permissions, ..Default::default() }, + &Config { root: PathBuf::from(root).into(), fs_permissions, ..Default::default() }, Default::default(), None, None, diff --git a/crates/cli/src/opts/build/core.rs b/crates/cli/src/opts/build/core.rs index bf0fd019f..61986b8fa 100644 --- a/crates/cli/src/opts/build/core.rs +++ b/crates/cli/src/opts/build/core.rs @@ -192,7 +192,7 @@ impl<'a> From<&'a CoreBuildArgs> for Config { // if `--config-path` is set we need to adjust the config's root path to the actual root // path for the project, otherwise it will the parent dir of the `--config-path` if args.project_paths.config_path.is_some() { - config.__root = args.project_paths.project_root().into(); + config.root = args.project_paths.project_root().into(); } config } diff --git a/crates/cli/src/utils/cmd.rs b/crates/cli/src/utils/cmd.rs index 05e6ad0ae..13da8d01d 100644 --- a/crates/cli/src/utils/cmd.rs +++ b/crates/cli/src/utils/cmd.rs @@ -265,31 +265,31 @@ where fn load_config_emit_warnings(self) -> Config { let config = self.load_config(); - config.__warnings.iter().for_each(|w| cli_warn!("{w}")); + config.warnings.iter().for_each(|w| cli_warn!("{w}")); config } fn try_load_config_emit_warnings(self) -> Result { let config = self.try_load_config()?; - config.__warnings.iter().for_each(|w| cli_warn!("{w}")); + config.warnings.iter().for_each(|w| cli_warn!("{w}")); Ok(config) } fn load_config_and_evm_opts_emit_warnings(self) -> Result<(Config, EvmOpts)> { let (config, evm_opts) = self.load_config_and_evm_opts()?; - config.__warnings.iter().for_each(|w| cli_warn!("{w}")); + config.warnings.iter().for_each(|w| cli_warn!("{w}")); Ok((config, evm_opts)) } fn load_config_unsanitized_emit_warnings(self) -> Config { let config = self.load_config_unsanitized(); - config.__warnings.iter().for_each(|w| cli_warn!("{w}")); + config.warnings.iter().for_each(|w| cli_warn!("{w}")); config } fn try_load_config_unsanitized_emit_warnings(self) -> Result { let config = self.try_load_config_unsanitized()?; - config.__warnings.iter().for_each(|w| cli_warn!("{w}")); + config.warnings.iter().for_each(|w| cli_warn!("{w}")); Ok(config) } } diff --git a/crates/cli/src/utils/mod.rs b/crates/cli/src/utils/mod.rs index a840a10c2..3a3e65c5e 100644 --- a/crates/cli/src/utils/mod.rs +++ b/crates/cli/src/utils/mod.rs @@ -289,7 +289,7 @@ impl<'a> Git<'a> { #[inline] pub fn from_config(config: &'a Config) -> Self { - Self::new(config.__root.0.as_path()) + Self::new(config.root.0.as_path()) } pub fn root_of(relative_to: &Path) -> Result { diff --git a/crates/config/src/fs_permissions.rs b/crates/config/src/fs_permissions.rs index 6a75beb05..5260b7488 100644 --- a/crates/config/src/fs_permissions.rs +++ b/crates/config/src/fs_permissions.rs @@ -67,22 +67,20 @@ impl FsPermissions { } /// Updates all `allowed_paths` and joins ([`Path::join`]) the `root` with all entries - pub fn join_all(&mut self, root: impl AsRef) { - let root = root.as_ref(); + pub fn join_all(&mut self, root: &Path) { self.permissions.iter_mut().for_each(|perm| { perm.path = root.join(&perm.path); }) } /// Same as [`Self::join_all`] but consumes the type - pub fn joined(mut self, root: impl AsRef) -> Self { + pub fn joined(mut self, root: &Path) -> Self { self.join_all(root); self } /// Removes all existing permissions for the given path - pub fn remove(&mut self, path: impl AsRef) { - let path = path.as_ref(); + pub fn remove(&mut self, path: &Path) { self.permissions.retain(|permission| permission.path != path) } diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index eaaee87c2..624edbebc 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -414,13 +414,17 @@ pub struct Config { /// Soldeer dependencies pub dependencies: Option, - /// The root path where the config detection started from, `Config::with_root` - #[doc(hidden)] - // We're skipping serialization here, so it won't be included in the [`Config::to_string()`] + /// The root path where the config detection started from, [`Config::with_root`]. + // We're skipping serialization here, so it won't be included in the [`Config::to_string()`] // representation, but will be deserialized from the `Figment` so that forge commands can // override it. - #[serde(rename = "root", default, skip_serializing)] - pub __root: RootPath, + #[serde(default, skip_serializing)] + pub root: RootPath, + + /// Warnings gathered when loading the Config. See [`WarningsProvider`] for more information + #[serde(rename = "__warnings", default, skip_serializing)] + pub warnings: Vec, + /// PRIVATE: This structure may grow, As such, constructing this structure should /// _always_ be done using a public constructor or update syntax: /// @@ -431,10 +435,7 @@ pub struct Config { /// ``` #[doc(hidden)] #[serde(skip)] - pub __non_exhaustive: (), - /// Warnings gathered when loading the Config. See [`WarningsProvider`] for more information - #[serde(default, skip_serializing)] - pub __warnings: Vec, + pub _non_exhaustive: (), } /// Mapping of fallback standalone sections. See [`FallbackProfileProvider`] @@ -558,7 +559,7 @@ impl Config { pub fn to_figment(self, providers: FigmentProviders) -> Figment { let mut c = self; let profile = Config::selected_profile(); - let mut figment = Figment::default().merge(DappHardhatDirProvider(&c.__root.0)); + let mut figment = Figment::default().merge(DappHardhatDirProvider(&c.root.0)); // merge global foundry.toml file if let Some(global_toml) = Config::foundry_dir_toml().filter(|p| p.exists()) { @@ -571,7 +572,7 @@ impl Config { // merge local foundry.toml file figment = Config::merge_toml_provider( figment, - TomlFileProvider::new(Some("FOUNDRY_CONFIG"), c.__root.0.join(Config::FILE_NAME)) + TomlFileProvider::new(Some("FOUNDRY_CONFIG"), c.root.0.join(Config::FILE_NAME)) .cached(), profile.clone(), ); @@ -620,7 +621,7 @@ impl Config { .extract_inner::>("libs") .map(Cow::Owned) .unwrap_or_else(|_| Cow::Borrowed(&c.libs)), - root: &c.__root.0, + root: &c.root.0, remappings: figment.extract_inner::>("remappings"), }; figment = figment.merge(remappings); @@ -638,7 +639,7 @@ impl Config { /// This joins all relative paths with the current root and attempts to make them canonic #[must_use] pub fn canonic(self) -> Self { - let root = self.__root.0.clone(); + let root = self.root.0.clone(); self.canonic_at(root) } @@ -808,7 +809,7 @@ impl Config { .set_no_artifacts(no_artifacts); if !self.skip.is_empty() { - let filter = SkipBuildFilters::new(self.skip.clone(), self.__root.0.clone()); + let filter = SkipBuildFilters::new(self.skip.clone(), self.root.0.clone()); builder = builder.sparse_output(filter); } @@ -926,7 +927,7 @@ impl Config { .artifacts(&self.out) .libs(self.libs.iter()) .remappings(self.get_all_remappings()) - .allowed_path(&self.__root.0) + .allowed_path(&self.root.0) .allowed_paths(&self.libs) .allowed_paths(&self.allow_paths) .include_paths(&self.include_paths); @@ -935,7 +936,7 @@ impl Config { builder = builder.build_infos(build_info_path); } - builder.build_with_root(&self.__root.0) + builder.build_with_root(&self.root.0) } /// Returns configuration for a compiler to use when setting up a [Project]. @@ -1183,7 +1184,7 @@ impl Config { /// Returns the remapping for the project's _test_ directory, but only if it exists pub fn get_test_dir_remapping(&self) -> Option { - if self.__root.0.join(&self.test).exists() { + if self.root.0.join(&self.test).exists() { get_dir_remapping(&self.test) } else { None @@ -1192,7 +1193,7 @@ impl Config { /// Returns the remapping for the project's _script_ directory, but only if it exists pub fn get_script_dir_remapping(&self) -> Option { - if self.__root.0.join(&self.script).exists() { + if self.root.0.join(&self.script).exists() { get_dir_remapping(&self.script) } else { None @@ -1360,7 +1361,7 @@ impl Config { let paths = ProjectPathsConfig::builder().build_with_root::<()>(&root); let artifacts: PathBuf = paths.artifacts.file_name().unwrap().into(); Config { - __root: paths.root.into(), + root: paths.root.into(), src: paths.sources.file_name().unwrap().into(), out: artifacts.clone(), libs: paths.libraries.into_iter().map(|lib| lib.file_name().unwrap().into()).collect(), @@ -1452,7 +1453,7 @@ impl Config { pub fn update_libs(&self) -> eyre::Result<()> { self.update(|doc| { let profile = self.profile.as_str().as_str(); - let root = &self.__root.0; + let root = &self.root.0; let libs: toml_edit::Value = self .libs .iter() @@ -1509,7 +1510,7 @@ impl Config { /// Returns the path to the `foundry.toml` of this `Config`. pub fn get_config_path(&self) -> PathBuf { - self.__root.0.join(Config::FILE_NAME) + self.root.0.join(Config::FILE_NAME) } /// Returns the selected profile. @@ -2006,7 +2007,7 @@ impl Provider for Config { fn data(&self) -> Result, figment::Error> { let mut data = Serialized::defaults(self).data()?; if let Some(entry) = data.get_mut(&self.profile) { - entry.insert("root".to_string(), Value::serialize(self.__root.clone())?); + entry.insert("root".to_string(), Value::serialize(self.root.clone())?); } Ok(data) } @@ -2023,7 +2024,7 @@ impl Default for Config { fs_permissions: FsPermissions::new([PathPermission::read("out")]), prague: false, isolate: false, - __root: Default::default(), + root: Default::default(), src: "src".into(), test: "test".into(), script: "script".into(), @@ -2114,8 +2115,8 @@ impl Default for Config { create2_library_salt: Config::DEFAULT_CREATE2_LIBRARY_SALT, skip: vec![], dependencies: Default::default(), - __non_exhaustive: (), - __warnings: vec![], + warnings: vec![], + _non_exhaustive: (), } } } @@ -2827,7 +2828,7 @@ mod tests { // Helper function to clear `__warnings` in config, since it will be populated during loading // from file, causing testing problem when comparing to those created from `default()`, etc. fn clear_warning(config: &mut Config) { - config.__warnings = vec![]; + config.warnings = vec![]; } #[test] @@ -4645,7 +4646,7 @@ mod tests { assert_eq!(loaded.src.file_name().unwrap(), "my-src"); assert_eq!(loaded.out.file_name().unwrap(), "my-out"); assert_eq!( - loaded.__warnings, + loaded.warnings, vec![Warning::UnknownSection { unknown_section: Profile::new("default"), source: Some("foundry.toml".into()) diff --git a/crates/forge/bin/cmd/fmt.rs b/crates/forge/bin/cmd/fmt.rs index b62f6a7eb..c7d84eddb 100644 --- a/crates/forge/bin/cmd/fmt.rs +++ b/crates/forge/bin/cmd/fmt.rs @@ -48,7 +48,7 @@ impl FmtArgs { let config = self.try_load_config_emit_warnings()?; // Expand ignore globs and canonicalize from the get go - let ignored = expand_globs(&config.__root.0, config.fmt.ignore.iter())? + let ignored = expand_globs(&config.root.0, config.fmt.ignore.iter())? .iter() .flat_map(foundry_common::fs::canonicalize_path) .collect::>(); @@ -97,7 +97,7 @@ impl FmtArgs { let format = |source: String, path: Option<&Path>| -> Result<_> { let name = match path { Some(path) => { - path.strip_prefix(&config.__root.0).unwrap_or(path).display().to_string() + path.strip_prefix(&config.root.0).unwrap_or(path).display().to_string() } None => "stdin".to_string(), }; diff --git a/crates/forge/bin/cmd/geiger/mod.rs b/crates/forge/bin/cmd/geiger/mod.rs index 451c29dbd..da0397cc5 100644 --- a/crates/forge/bin/cmd/geiger/mod.rs +++ b/crates/forge/bin/cmd/geiger/mod.rs @@ -98,7 +98,7 @@ impl GeigerArgs { eprintln!("{}\n", "ffi enabled".red()); } - let root = config.__root.0; + let root = config.root.0; let sum = sources .par_iter() diff --git a/crates/forge/bin/cmd/update.rs b/crates/forge/bin/cmd/update.rs index 05f8e0eb2..5ddc5460a 100644 --- a/crates/forge/bin/cmd/update.rs +++ b/crates/forge/bin/cmd/update.rs @@ -52,7 +52,7 @@ impl UpdateArgs { /// Returns `(root, paths)` where `root` is the root of the Git repository and `paths` are the /// relative paths of the dependencies. pub fn dependencies_paths(deps: &[Dependency], config: &Config) -> Result<(PathBuf, Vec)> { - let git_root = Git::root_of(&config.__root.0)?; + let git_root = Git::root_of(&config.root.0)?; let libs = config.install_lib_dir(); let mut paths = Vec::with_capacity(deps.len()); diff --git a/crates/forge/bin/cmd/watch.rs b/crates/forge/bin/cmd/watch.rs index e12b42f7b..327804f2b 100644 --- a/crates/forge/bin/cmd/watch.rs +++ b/crates/forge/bin/cmd/watch.rs @@ -137,7 +137,7 @@ pub async fn watch_test(args: TestArgs) -> Result<()> { args.watch.run_all; let state = WatchTestState { - project_root: config.__root.0, + project_root: config.root.0, no_reconfigure, last_test_files: Default::default(), }; diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 073a6e451..bd5598470 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -29,7 +29,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { // explicitly set all values let input = Config { profile: Config::DEFAULT_PROFILE, - __root: Default::default(), + root: Default::default(), src: "test-src".into(), test: "test-test".into(), script: "test-script".into(), @@ -138,8 +138,8 @@ forgetest!(can_extract_config_values, |prj, cmd| { vyper: Default::default(), skip: vec![], dependencies: Default::default(), - __non_exhaustive: (), - __warnings: vec![], + warnings: vec![], + _non_exhaustive: (), }; prj.write_config(input.clone()); let config = cmd.config(); diff --git a/crates/forge/tests/it/repros.rs b/crates/forge/tests/it/repros.rs index 0c0f1bc50..924a65b72 100644 --- a/crates/forge/tests/it/repros.rs +++ b/crates/forge/tests/it/repros.rs @@ -292,7 +292,7 @@ test_repro!(6538); // https://github.com/foundry-rs/foundry/issues/6554 test_repro!(6554; |config| { - let path = config.runner.config.__root.0.join("out/default/Issue6554.t.sol"); + let path = config.runner.config.root.0.join("out/default/Issue6554.t.sol"); let mut prj_config = Config::clone(&config.runner.config); prj_config.fs_permissions.add(PathPermission::read_write(path)); diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index 418dd036a..2b1fb14cf 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -402,7 +402,7 @@ impl FilledTransactionsState { )?) }; - let commit = get_commit_hash(&self.script_config.config.__root.0); + let commit = get_commit_hash(&self.script_config.config.root.0); let libraries = self .build_data From c5f0ea71cbd2fee21743560ea5f6e3da7b551bd2 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 7 Jun 2024 00:35:40 +0200 Subject: [PATCH 372/430] chore: simplify anvil precompile handler (#8087) --- crates/anvil/src/eth/backend/mem/mod.rs | 2 +- crates/anvil/src/evm.rs | 23 +++++++---------------- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index d2a62ebc1..238fe2ef9 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -816,7 +816,7 @@ impl Backend { I: InspectorExt>, { let mut evm = new_evm_with_inspector_ref(db, env, inspector); - if let Some(ref factory) = self.precompile_factory { + if let Some(factory) = &self.precompile_factory { inject_precompiles(&mut evm, factory.precompiles()); } evm diff --git a/crates/anvil/src/evm.rs b/crates/anvil/src/evm.rs index e6c76dd60..0e0d30f77 100644 --- a/crates/anvil/src/evm.rs +++ b/crates/anvil/src/evm.rs @@ -1,5 +1,5 @@ use alloy_primitives::Address; -use foundry_evm::revm::{precompile::Precompile, ContextPrecompile, ContextPrecompiles}; +use foundry_evm::revm::precompile::Precompile; use std::{fmt::Debug, sync::Arc}; /// Object-safe trait that enables injecting extra precompiles when using @@ -13,26 +13,17 @@ pub trait PrecompileFactory: Send + Sync + Unpin + Debug { /// /// This will add an additional handler that extends the default precompiles with the given set of /// precompiles. -pub fn inject_precompiles( +pub fn inject_precompiles( evm: &mut revm::Evm<'_, I, DB>, precompiles: Vec<(Address, Precompile)>, -) where - DB: revm::Database, -{ +) { evm.handler.append_handler_register_box(Box::new(move |handler| { let precompiles = precompiles.clone(); - let loaded_precompiles = handler.pre_execution().load_precompiles(); + let prev = handler.pre_execution.load_precompiles.clone(); handler.pre_execution.load_precompiles = Arc::new(move || { - let mut loaded_precompiles = loaded_precompiles.clone(); - loaded_precompiles.extend( - precompiles - .clone() - .into_iter() - .map(|(addr, p)| (addr, ContextPrecompile::Ordinary(p))), - ); - let mut default_precompiles = ContextPrecompiles::default(); - default_precompiles.extend(loaded_precompiles); - default_precompiles + let mut cx = prev(); + cx.extend(precompiles.iter().cloned().map(|(a, b)| (a, b.into()))); + cx }); })); } From 993951adeab9806b1eb12d189ac0f5aef9e87632 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Fri, 7 Jun 2024 00:49:05 +0200 Subject: [PATCH 373/430] fix: include Vyper sources when compiling scripts (#8091) --- crates/script/src/build.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index 3f9a49960..342096fda 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -16,9 +16,10 @@ use foundry_common::{ }; use foundry_compilers::{ artifacts::{BytecodeObject, Libraries}, + compilers::{multi::MultiCompilerLanguage, Language}, info::ContractInfo, utils::source_files_iter, - ArtifactId, ProjectCompileOutput, SOLC_EXTENSIONS, + ArtifactId, ProjectCompileOutput, }; use foundry_evm::constants::DEFAULT_CREATE2_DEPLOYER; use foundry_linking::Linker; @@ -184,9 +185,11 @@ impl PreprocessedState { } }; - let sources_to_compile = - source_files_iter(project.paths.sources.as_path(), SOLC_EXTENSIONS) - .chain([target_path.to_path_buf()]); + let sources_to_compile = source_files_iter( + project.paths.sources.as_path(), + MultiCompilerLanguage::FILE_EXTENSIONS, + ) + .chain([target_path.to_path_buf()]); let output = ProjectCompiler::new() .quiet_if(args.opts.silent) From 08b2d3efe3ce1ebb1482e5b554f0335fe6cf89a5 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 7 Jun 2024 01:14:54 +0200 Subject: [PATCH 374/430] feat(cast): implement ERC-7201 as `cast index-erc7201` (#8092) --- crates/cast/bin/main.rs | 5 +++++ crates/cast/bin/opts.rs | 10 +++++++++ crates/cast/tests/cli/main.rs | 13 ++++++++++++ crates/common/src/lib.rs | 15 ++----------- crates/common/src/utils.rs | 40 +++++++++++++++++++++++++++++++++++ 5 files changed, 70 insertions(+), 13 deletions(-) create mode 100644 crates/common/src/utils.rs diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index 7baf54331..4e85ef561 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -328,6 +328,11 @@ async fn main() -> Result<()> { CastSubcommand::Index { key_type, key, slot_number } => { println!("{}", SimpleCast::index(&key_type, &key, &slot_number)?); } + CastSubcommand::IndexErc7201 { id, formula_id } => { + eyre::ensure!(formula_id == "erc7201", "unsupported formula ID: {formula_id}"); + let id = stdin::unwrap_line(id)?; + println!("{}", foundry_common::erc7201(&id)); + } CastSubcommand::Implementation { block, who, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; diff --git a/crates/cast/bin/opts.rs b/crates/cast/bin/opts.rs index 1a36d2893..5a15e1df1 100644 --- a/crates/cast/bin/opts.rs +++ b/crates/cast/bin/opts.rs @@ -526,6 +526,16 @@ pub enum CastSubcommand { slot_number: String, }, + /// Compute storage slots as specified by `ERC-7201: Namespaced Storage Layout`. + #[command(name = "index-erc7201", alias = "index-erc-7201", visible_aliases = &["index7201", "in7201"])] + IndexErc7201 { + /// The arbitrary identifier. + id: Option, + /// The formula ID. Currently the only supported formula is `erc7201`. + #[arg(long, default_value = "erc7201")] + formula_id: String, + }, + /// Fetch the EIP-1967 implementation account #[command(visible_alias = "impl")] Implementation { diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 30922cfb4..016a15a81 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -888,3 +888,16 @@ casttest!(ens_resolve_no_dot_eth, |_prj, cmd| { let (_out, err) = cmd.unchecked_output_lossy(); assert!(err.contains("not found"), "{err:?}"); }); + +casttest!(index7201, |_prj, cmd| { + let tests = + [("example.main", "0x183a6125c38840424c4a85fa12bab2ab606c4b6d0e7cc73c0c06ba5300eab500")]; + for (id, expected) in tests { + cmd.cast_fuse(); + assert_eq!(cmd.args(["index-erc7201", id]).stdout_lossy().trim(), expected); + } +}); + +casttest!(index_unknown_formula_id, |_prj, cmd| { + cmd.args(["index-7201", "test", "--formula-id", "unknown"]).assert_err(); +}); diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index 7b1c0ff76..16071f221 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -29,21 +29,10 @@ pub mod shell; pub mod term; pub mod traits; pub mod transactions; +mod utils; pub use constants::*; pub use contracts::*; pub use traits::*; pub use transactions::*; - -/// Block on a future using the current tokio runtime on the current thread. -pub fn block_on(future: F) -> F::Output { - block_on_handle(&tokio::runtime::Handle::current(), future) -} - -/// Block on a future using the current tokio runtime on the current thread with the given handle. -pub fn block_on_handle( - handle: &tokio::runtime::Handle, - future: F, -) -> F::Output { - tokio::task::block_in_place(|| handle.block_on(future)) -} +pub use utils::*; diff --git a/crates/common/src/utils.rs b/crates/common/src/utils.rs new file mode 100644 index 000000000..267680747 --- /dev/null +++ b/crates/common/src/utils.rs @@ -0,0 +1,40 @@ +//! Uncategorised utilities. + +use alloy_primitives::{keccak256, B256, U256}; + +/// Block on a future using the current tokio runtime on the current thread. +pub fn block_on(future: F) -> F::Output { + block_on_handle(&tokio::runtime::Handle::current(), future) +} + +/// Block on a future using the current tokio runtime on the current thread with the given handle. +pub fn block_on_handle( + handle: &tokio::runtime::Handle, + future: F, +) -> F::Output { + tokio::task::block_in_place(|| handle.block_on(future)) +} + +/// Computes the storage slot as specified by `ERC-7201`, using the `erc7201` formula ID. +/// +/// This is defined as: +/// +/// ```text +/// erc7201(id: string) = keccak256(keccak256(id) - 1) & ~0xff +/// ``` +/// +/// # Examples +/// +/// ``` +/// use alloy_primitives::b256; +/// use foundry_common::erc7201; +/// +/// assert_eq!( +/// erc7201("example.main"), +/// b256!("183a6125c38840424c4a85fa12bab2ab606c4b6d0e7cc73c0c06ba5300eab500"), +/// ); +/// ``` +pub fn erc7201(id: &str) -> B256 { + let x = U256::from_be_bytes(keccak256(id).0) - U256::from(1); + keccak256(x.to_be_bytes::<32>()) & B256::from(!U256::from(0xff)) +} From fb4b6f87903ecb7a6d9f94e3cdfb8b39a7294f03 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 7 Jun 2024 13:37:16 +0200 Subject: [PATCH 375/430] chore: simplify PartialEq for ForgeContext (#8101) --- crates/cast/tests/cli/main.rs | 2 +- crates/cheatcodes/spec/src/lib.rs | 6 +++--- crates/cheatcodes/spec/src/vm.rs | 24 +++++++++++------------- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 016a15a81..3c86ec4bb 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -898,6 +898,6 @@ casttest!(index7201, |_prj, cmd| { } }); -casttest!(index_unknown_formula_id, |_prj, cmd| { +casttest!(index7201_unknown_formula_id, |_prj, cmd| { cmd.args(["index-7201", "test", "--formula-id", "unknown"]).assert_err(); }); diff --git a/crates/cheatcodes/spec/src/lib.rs b/crates/cheatcodes/spec/src/lib.rs index 37cf149cd..fffc146a9 100644 --- a/crates/cheatcodes/spec/src/lib.rs +++ b/crates/cheatcodes/spec/src/lib.rs @@ -91,10 +91,10 @@ impl Cheatcodes<'static> { Vm::AccountAccessKind::ENUM.clone(), Vm::ForgeContext::ENUM.clone(), ]), - errors: Vm::VM_ERRORS.iter().map(|&x| x.clone()).collect(), + errors: Vm::VM_ERRORS.iter().copied().cloned().collect(), events: Cow::Borrowed(&[]), - // events: Vm::VM_EVENTS.iter().map(|&x| x.clone()).collect(), - cheatcodes: Vm::CHEATCODES.iter().map(|&x| x.clone()).collect(), + // events: Vm::VM_EVENTS.iter().copied().cloned().collect(), + cheatcodes: Vm::CHEATCODES.iter().copied().cloned().collect(), } } } diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 1538dc16e..201bc90d7 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -2163,21 +2163,19 @@ impl PartialEq for ForgeContext { // and script group case (any of dry run, broadcast or resume). fn eq(&self, other: &Self) -> bool { match (self, other) { - (_, &Self::TestGroup) => { - self == &Self::Test || self == &Self::Snapshot || self == &Self::Coverage + (_, Self::TestGroup) => { + matches!(self, Self::Test | Self::Snapshot | Self::Coverage) } - (_, &Self::ScriptGroup) => { - self == &Self::ScriptDryRun || - self == &Self::ScriptBroadcast || - self == &Self::ScriptResume + (_, Self::ScriptGroup) => { + matches!(self, Self::ScriptDryRun | Self::ScriptBroadcast | Self::ScriptResume) } - (&Self::Test, &Self::Test) | - (&Self::Snapshot, &Self::Snapshot) | - (&Self::Coverage, &Self::Coverage) | - (&Self::ScriptDryRun, &Self::ScriptDryRun) | - (&Self::ScriptBroadcast, &Self::ScriptBroadcast) | - (&Self::ScriptResume, &Self::ScriptResume) | - (&Self::Unknown, &Self::Unknown) => true, + (Self::Test, Self::Test) | + (Self::Snapshot, Self::Snapshot) | + (Self::Coverage, Self::Coverage) | + (Self::ScriptDryRun, Self::ScriptDryRun) | + (Self::ScriptBroadcast, Self::ScriptBroadcast) | + (Self::ScriptResume, Self::ScriptResume) | + (Self::Unknown, Self::Unknown) => true, _ => false, } } From 741873cd63b41105c38b5fc24e8b2f6131ae7e9c Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 7 Jun 2024 15:42:28 +0200 Subject: [PATCH 376/430] chore: avoid cloning test results channel (#8103) --- crates/forge/src/multi_runner.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 3c3eb2ad4..de37469d8 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -167,7 +167,7 @@ impl MultiContractRunner { find_time, ); - contracts.par_iter().for_each_with(tx, |tx, &(id, contract)| { + contracts.par_iter().for_each(|&(id, contract)| { let _guard = handle.enter(); let result = self.run_tests(id, contract, db.clone(), filter, &handle); let _ = tx.send((id.identifier(), result)); From a9c0755b76c643fe2b4af3f04e3da17a621cb25a Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 7 Jun 2024 15:55:13 +0200 Subject: [PATCH 377/430] chore: simplify proptest strategies (#8105) --- crates/evm/evm/src/executors/invariant/mod.rs | 12 +- .../evm/fuzz/src/invariant/call_override.rs | 8 +- crates/evm/fuzz/src/lib.rs | 7 +- crates/evm/fuzz/src/strategies/calldata.rs | 13 +- crates/evm/fuzz/src/strategies/invariants.rs | 54 +++------ crates/evm/fuzz/src/strategies/mod.rs | 31 +---- crates/evm/fuzz/src/strategies/param.rs | 113 ++++++++---------- 7 files changed, 83 insertions(+), 155 deletions(-) diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 2474ed5aa..2c67c792d 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -22,7 +22,7 @@ use foundry_evm_fuzz::{ use foundry_evm_traces::CallTraceArena; use parking_lot::RwLock; use proptest::{ - strategy::{BoxedStrategy, Strategy}, + strategy::{Strategy, ValueTree}, test_runner::{TestCaseError, TestRunner}, }; use result::{assert_invariants, can_continue}; @@ -91,10 +91,6 @@ sol! { } } -/// Alias for (Dictionary for fuzzing, initial contracts to fuzz and an InvariantStrategy). -type InvariantPreparation = - (EvmFuzzState, FuzzRunIdentifiedContracts, BoxedStrategy); - /// Wrapper around any [`Executor`] implementor which provides fuzzing support using [`proptest`]. /// /// After instantiation, calling `fuzz` will proceed to hammer the deployed smart contracts with @@ -343,7 +339,8 @@ impl<'a> InvariantExecutor<'a> { &mut self, invariant_contract: &InvariantContract<'_>, fuzz_fixtures: &FuzzFixtures, - ) -> Result { + ) -> Result<(EvmFuzzState, FuzzRunIdentifiedContracts, impl Strategy)> + { // Finds out the chosen deployed contracts and/or senders. self.select_contract_artifacts(invariant_contract.address)?; let (targeted_senders, targeted_contracts) = @@ -360,8 +357,7 @@ impl<'a> InvariantExecutor<'a> { self.config.dictionary.dictionary_weight, fuzz_fixtures.clone(), ) - .no_shrink() - .boxed(); + .no_shrink(); // Allows `override_call_strat` to use the address given by the Fuzzer inspector during // EVM execution. diff --git a/crates/evm/fuzz/src/invariant/call_override.rs b/crates/evm/fuzz/src/invariant/call_override.rs index a65b0abc9..dddf59152 100644 --- a/crates/evm/fuzz/src/invariant/call_override.rs +++ b/crates/evm/fuzz/src/invariant/call_override.rs @@ -33,17 +33,15 @@ impl RandomCallGenerator { pub fn new( test_address: Address, runner: TestRunner, - strategy: SBoxedStrategy, + strategy: impl Strategy + Send + Sync + 'static, target_reference: Arc>, ) -> Self { - let strategy = weighted(0.9, strategy).sboxed(); - Self { test_address, runner: Arc::new(Mutex::new(runner)), - strategy, + strategy: weighted(0.9, strategy).sboxed(), target_reference, - last_sequence: Arc::new(RwLock::new(vec![])), + last_sequence: Arc::default(), replay: false, used: false, } diff --git a/crates/evm/fuzz/src/lib.rs b/crates/evm/fuzz/src/lib.rs index 7da74601f..f675e7755 100644 --- a/crates/evm/fuzz/src/lib.rs +++ b/crates/evm/fuzz/src/lib.rs @@ -316,10 +316,7 @@ impl FuzzFixtures { /// Returns configured fixtures for `param_name` fuzzed parameter. pub fn param_fixtures(&self, param_name: &str) -> Option<&[DynSolValue]> { if let Some(param_fixtures) = self.inner.get(&normalize_fixture(param_name)) { - match param_fixtures { - DynSolValue::FixedArray(_) => param_fixtures.as_fixed_array(), - _ => param_fixtures.as_array(), - } + param_fixtures.as_fixed_array().or_else(|| param_fixtures.as_array()) } else { None } @@ -334,5 +331,5 @@ pub fn fixture_name(function_name: String) -> String { /// Normalize fixture parameter name, for example `_Owner` to `owner`. fn normalize_fixture(param_name: &str) -> String { - param_name.trim_matches(&['_']).to_ascii_lowercase() + param_name.trim_matches('_').to_ascii_lowercase() } diff --git a/crates/evm/fuzz/src/strategies/calldata.rs b/crates/evm/fuzz/src/strategies/calldata.rs index 760d66175..c341cce9d 100644 --- a/crates/evm/fuzz/src/strategies/calldata.rs +++ b/crates/evm/fuzz/src/strategies/calldata.rs @@ -1,11 +1,11 @@ use crate::{ - strategies::{fuzz_param, fuzz_param_from_state, EvmFuzzState}, + strategies::{fuzz_param_from_state, fuzz_param_with_fixtures, EvmFuzzState}, FuzzFixtures, }; use alloy_dyn_abi::JsonAbiExt; use alloy_json_abi::Function; use alloy_primitives::Bytes; -use proptest::prelude::{BoxedStrategy, Strategy}; +use proptest::prelude::Strategy; /// Given a function, it returns a strategy which generates valid calldata /// for that function's input types, following declared test fixtures. @@ -16,9 +16,10 @@ pub fn fuzz_calldata(func: Function, fuzz_fixtures: &FuzzFixtures) -> impl Strat .inputs .iter() .map(|input| { - fuzz_param( + fuzz_param_with_fixtures( &input.selector_type().parse().unwrap(), fuzz_fixtures.param_fixtures(&input.name), + &input.name, ) }) .collect::>(); @@ -36,7 +37,10 @@ pub fn fuzz_calldata(func: Function, fuzz_fixtures: &FuzzFixtures) -> impl Strat /// Given a function and some state, it returns a strategy which generated valid calldata for the /// given function's input types, based on state taken from the EVM. -pub fn fuzz_calldata_from_state(func: Function, state: &EvmFuzzState) -> BoxedStrategy { +pub fn fuzz_calldata_from_state( + func: Function, + state: &EvmFuzzState, +) -> impl Strategy { let strats = func .inputs .iter() @@ -54,7 +58,6 @@ pub fn fuzz_calldata_from_state(func: Function, state: &EvmFuzzState) -> BoxedSt .into() }) .no_shrink() - .boxed() } #[cfg(test)] diff --git a/crates/evm/fuzz/src/strategies/invariants.rs b/crates/evm/fuzz/src/strategies/invariants.rs index 49971613e..1c143f922 100644 --- a/crates/evm/fuzz/src/strategies/invariants.rs +++ b/crates/evm/fuzz/src/strategies/invariants.rs @@ -16,7 +16,7 @@ pub fn override_call_strat( contracts: FuzzRunIdentifiedContracts, target: Arc>, fuzz_fixtures: FuzzFixtures, -) -> SBoxedStrategy { +) -> impl Strategy + Send + Sync + 'static { let contracts_ref = contracts.targets.clone(); proptest::prop_oneof![ 80 => proptest::strategy::LazyJust::new(move || *target.read()), @@ -44,7 +44,6 @@ pub fn override_call_strat( fuzz_contract_with_calldata(&fuzz_state, &fuzz_fixtures, target_address, func) }) }) - .sboxed() } /// Creates the invariant strategy. @@ -64,20 +63,6 @@ pub fn invariant_strat( dictionary_weight: u32, fuzz_fixtures: FuzzFixtures, ) -> impl Strategy { - // We only want to seed the first value, since we want to generate the rest as we mutate the - // state - generate_call(fuzz_state, senders, contracts, dictionary_weight, fuzz_fixtures) -} - -/// Strategy to generate a transaction where the `sender`, `target` and `calldata` are all generated -/// through specific strategies. -fn generate_call( - fuzz_state: EvmFuzzState, - senders: SenderFilters, - contracts: FuzzRunIdentifiedContracts, - dictionary_weight: u32, - fuzz_fixtures: FuzzFixtures, -) -> BoxedStrategy { let senders = Rc::new(senders); any::() .prop_flat_map(move |selector| { @@ -102,7 +87,6 @@ fn generate_call( }) }) .prop_map(|(sender, call_details)| BasicTxDetails { sender, call_details }) - .boxed() } /// Strategy to select a sender address: @@ -112,20 +96,16 @@ fn select_random_sender( fuzz_state: &EvmFuzzState, senders: Rc, dictionary_weight: u32, -) -> BoxedStrategy
{ +) -> impl Strategy { if !senders.targeted.is_empty() { - any::() - .prop_map(move |selector| *selector.select(&senders.targeted)) - .boxed() + any::().prop_map(move |index| *index.get(&senders.targeted)).boxed() } else { + assert!(dictionary_weight <= 100, "dictionary_weight must be <= 100"); proptest::prop_oneof![ - 100 - dictionary_weight => fuzz_param(&alloy_dyn_abi::DynSolType::Address, None) - .prop_map(move |addr| addr.as_address().unwrap()) - .boxed(), - dictionary_weight => fuzz_param_from_state(&alloy_dyn_abi::DynSolType::Address, fuzz_state) - .prop_map(move |addr| addr.as_address().unwrap()) - .boxed(), + 100 - dictionary_weight => fuzz_param(&alloy_dyn_abi::DynSolType::Address), + dictionary_weight => fuzz_param_from_state(&alloy_dyn_abi::DynSolType::Address, fuzz_state), ] + .prop_map(move |addr| addr.as_address().unwrap()) // Too many exclusions can slow down testing. .prop_filter("excluded sender", move |addr| !senders.excluded.contains(addr)) .boxed() @@ -139,15 +119,11 @@ fn select_random_sender( fn select_random_function( abi: &JsonAbi, targeted_functions: &[Function], -) -> BoxedStrategy { - if !targeted_functions.is_empty() { - let targeted_functions = targeted_functions.to_vec(); - let selector = any::() - .prop_map(move |selector| selector.select(&targeted_functions).clone()); - selector.boxed() +) -> impl Strategy { + let functions = if !targeted_functions.is_empty() { + targeted_functions.to_vec() } else { - let possible_funcs: Vec = abi - .functions() + abi.functions() .filter(|&func| { !matches!( func.state_mutability, @@ -155,11 +131,9 @@ fn select_random_function( ) }) .cloned() - .collect(); - let total_random = any::() - .prop_map(move |selector| selector.select(&possible_funcs).clone()); - total_random.boxed() - } + .collect() + }; + any::().prop_map(move |index| index.get(&functions).clone()) } /// Given a function, it returns a proptest strategy which generates valid abi-encoded calldata diff --git a/crates/evm/fuzz/src/strategies/mod.rs b/crates/evm/fuzz/src/strategies/mod.rs index b49adf16c..bf284591b 100644 --- a/crates/evm/fuzz/src/strategies/mod.rs +++ b/crates/evm/fuzz/src/strategies/mod.rs @@ -5,7 +5,7 @@ mod uint; pub use uint::UintStrategy; mod param; -pub use param::{fuzz_param, fuzz_param_from_state}; +pub use param::{fuzz_param, fuzz_param_from_state, fuzz_param_with_fixtures}; mod calldata; pub use calldata::{fuzz_calldata, fuzz_calldata_from_state}; @@ -15,32 +15,3 @@ pub use state::{collect_created_contracts, EvmFuzzState}; mod invariants; pub use invariants::{fuzz_contract_with_calldata, invariant_strat, override_call_strat}; - -/// Macro to create strategy with fixtures. -/// 1. A default strategy if no fixture defined for current parameter. -/// 2. A weighted strategy that use fixtures and default strategy values for current parameter. -/// -/// If fixture is not of the same type as fuzzed parameter then value is rejected and error raised. -macro_rules! fixture_strategy { - ($fixtures:ident, $strategy_value:expr, $default_strategy:expr) => { - if let Some(fixtures) = $fixtures { - proptest::prop_oneof![ - 50 => { - let custom_fixtures: Vec = - fixtures.iter().enumerate().map(|(_, value)| value.to_owned()).collect(); - let custom_fixtures_len = custom_fixtures.len(); - any::() - .prop_filter_map("invalid fixture", move |index| { - let index = index.index(custom_fixtures_len); - $strategy_value(custom_fixtures.get(index)) - }) - }, - 50 => $default_strategy - ].boxed() - } else { - $default_strategy.boxed() - } - }; -} - -pub(crate) use fixture_strategy; diff --git a/crates/evm/fuzz/src/strategies/param.rs b/crates/evm/fuzz/src/strategies/param.rs index 0b64418d6..65229d686 100644 --- a/crates/evm/fuzz/src/strategies/param.rs +++ b/crates/evm/fuzz/src/strategies/param.rs @@ -1,5 +1,4 @@ use super::state::EvmFuzzState; -use crate::strategies::fixture_strategy; use alloy_dyn_abi::{DynSolType, DynSolValue}; use alloy_primitives::{Address, B256, I256, U256}; use proptest::prelude::*; @@ -7,6 +6,13 @@ use proptest::prelude::*; /// The max length of arrays we fuzz for is 256. const MAX_ARRAY_LEN: usize = 256; +/// Given a parameter type, returns a strategy for generating values for that type. +/// +/// See [`fuzz_param_with_fixtures`] for more information. +pub fn fuzz_param(param: &DynSolType) -> BoxedStrategy { + fuzz_param_inner(param, None) +} + /// Given a parameter type and configured fixtures for param name, returns a strategy for generating /// values for that type. Fixtures can be currently generated for uint, int, address, bytes and /// string types and are defined for parameter name. @@ -18,29 +24,48 @@ const MAX_ARRAY_LEN: usize = 256; /// `fixture_owner` function can be used in a fuzzed test function with a signature like /// `function testFuzz_ownerAddress(address owner, uint amount)`. /// -/// Fuzzer will reject value and raise error if the fixture type is not of the same type as -/// parameter to fuzz. +/// Raises an error if all the fixture types are not of the same type as the input parameter. /// /// Works with ABI Encoder v2 tuples. -pub fn fuzz_param( +pub fn fuzz_param_with_fixtures( param: &DynSolType, - fuzz_fixtures: Option<&[DynSolValue]>, + fixtures: Option<&[DynSolValue]>, + name: &str, ) -> BoxedStrategy { - match *param { - DynSolType::Address => { - fixture_strategy!( - fuzz_fixtures, - |fixture: Option<&DynSolValue>| { - if let Some(val @ DynSolValue::Address(_)) = fixture { - Some(val.clone()) - } else { - error!("{:?} is not a valid address fixture", fixture.unwrap()); - None - } + fuzz_param_inner(param, fixtures.map(|f| (f, name))) +} + +fn fuzz_param_inner( + param: &DynSolType, + mut fuzz_fixtures: Option<(&[DynSolValue], &str)>, +) -> BoxedStrategy { + if let Some((fixtures, name)) = fuzz_fixtures { + if !fixtures.iter().all(|f| f.matches(param)) { + error!("fixtures for {name:?} do not match type {param}"); + fuzz_fixtures = None; + } + } + let fuzz_fixtures = fuzz_fixtures.map(|(f, _)| f); + + let value = || { + let default_strategy = DynSolValue::type_strategy(param); + if let Some(fixtures) = fuzz_fixtures { + proptest::prop_oneof![ + 50 => { + let fixtures = fixtures.to_vec(); + any::() + .prop_map(move |index| index.get(&fixtures).clone()) }, - DynSolValue::type_strategy(&DynSolType::Address) - ) + 50 => default_strategy, + ] + .boxed() + } else { + default_strategy.boxed() } + }; + + match *param { + DynSolType::Address => value(), DynSolType::Int(n @ 8..=256) => super::IntStrategy::new(n, fuzz_fixtures) .prop_map(move |x| DynSolValue::Int(x, n)) .boxed(), @@ -48,64 +73,28 @@ pub fn fuzz_param( .prop_map(move |x| DynSolValue::Uint(x, n)) .boxed(), DynSolType::Function | DynSolType::Bool => DynSolValue::type_strategy(param).boxed(), - DynSolType::Bytes => { - fixture_strategy!( - fuzz_fixtures, - |fixture: Option<&DynSolValue>| { - if let Some(val @ DynSolValue::Bytes(_)) = fixture { - Some(val.clone()) - } else { - error!("{:?} is not a valid bytes fixture", fixture.unwrap()); - None - } - }, - DynSolValue::type_strategy(&DynSolType::Bytes) - ) - } - DynSolType::FixedBytes(size @ 1..=32) => fixture_strategy!( - fuzz_fixtures, - |fixture: Option<&DynSolValue>| { - if let Some(val @ DynSolValue::FixedBytes(_, _)) = fixture { - if let Some(val) = val.as_fixed_bytes() { - if val.1 == size { - return Some(DynSolValue::FixedBytes(B256::from_slice(val.0), val.1)) - } - } - } - error!("{:?} is not a valid fixed bytes fixture", fixture.unwrap()); - None - }, - DynSolValue::type_strategy(&DynSolType::FixedBytes(size)) - ), - DynSolType::String => fixture_strategy!( - fuzz_fixtures, - |fixture: Option<&DynSolValue>| { - if let Some(val @ DynSolValue::String(_)) = fixture { - Some(val.clone()) - } else { - error!("{:?} is not a valid string fixture", fixture.unwrap()); - None - } - }, - DynSolValue::type_strategy(&DynSolType::String).prop_map(move |value| { + DynSolType::Bytes => value(), + DynSolType::FixedBytes(_size @ 1..=32) => value(), + DynSolType::String => value() + .prop_map(move |value| { DynSolValue::String( value.as_str().unwrap().trim().trim_end_matches('\0').to_string(), ) }) - ), + .boxed(), DynSolType::Tuple(ref params) => params .iter() - .map(|p| fuzz_param(p, None)) + .map(|param| fuzz_param_inner(param, None)) .collect::>() .prop_map(DynSolValue::Tuple) .boxed(), DynSolType::FixedArray(ref param, size) => { - proptest::collection::vec(fuzz_param(param, None), size) + proptest::collection::vec(fuzz_param_inner(param, None), size) .prop_map(DynSolValue::FixedArray) .boxed() } DynSolType::Array(ref param) => { - proptest::collection::vec(fuzz_param(param, None), 0..MAX_ARRAY_LEN) + proptest::collection::vec(fuzz_param_inner(param, None), 0..MAX_ARRAY_LEN) .prop_map(DynSolValue::Array) .boxed() } From a09713bac7cbb3ce1a7ddbf66ef20f3baf4a403d Mon Sep 17 00:00:00 2001 From: morito Date: Fri, 7 Jun 2024 22:59:40 +0900 Subject: [PATCH 378/430] Support GCP KMS Signer (#8096) * Add Google Cloud KMS Signer * Add GCP Signer option and error * fix format * Fix error handling * deps: add a google-longrunning feature to gcloud-sdk * Fix format * Fix format --- Cargo.lock | 425 +++++++++++++++++++++++++--- Cargo.toml | 1 + crates/wallets/Cargo.toml | 8 + crates/wallets/src/error.rs | 10 + crates/wallets/src/raw_wallet.rs | 4 +- crates/wallets/src/wallet.rs | 15 +- crates/wallets/src/wallet_signer.rs | 55 ++++ 7 files changed, 480 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6f3f8da53..5bd074b35 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -251,8 +251,8 @@ dependencies = [ "futures", "futures-utils-wasm", "lru", - "pin-project", - "reqwest", + "pin-project 1.1.5", + "reqwest 0.12.4", "serde", "serde_json", "tokio", @@ -313,8 +313,8 @@ dependencies = [ "alloy-transport-ipc", "alloy-transport-ws", "futures", - "pin-project", - "reqwest", + "pin-project 1.1.5", + "reqwest 0.12.4", "serde", "serde_json", "tokio", @@ -413,6 +413,23 @@ dependencies = [ "tracing", ] +[[package]] +name = "alloy-signer-gcp" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" +dependencies = [ + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "async-trait", + "gcloud-sdk", + "k256", + "spki", + "thiserror", + "tracing", +] + [[package]] name = "alloy-signer-ledger" version = "0.1.0" @@ -491,7 +508,7 @@ dependencies = [ "alloy-sol-macro-input", "const-hex", "heck 0.4.1", - "indexmap", + "indexmap 2.2.6", "proc-macro-error", "proc-macro2", "quote", @@ -564,7 +581,7 @@ source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0b dependencies = [ "alloy-json-rpc", "alloy-transport", - "reqwest", + "reqwest 0.12.4", "serde_json", "tower", "tracing", @@ -582,7 +599,7 @@ dependencies = [ "bytes", "futures", "interprocess", - "pin-project", + "pin-project 1.1.5", "serde", "serde_json", "tempfile", @@ -616,7 +633,7 @@ dependencies = [ "alloy-primitives", "alloy-rlp", "derive_more", - "hashbrown", + "hashbrown 0.14.5", "nybbles", "serde", "smallvec", @@ -738,7 +755,7 @@ dependencies = [ "anvil-server", "async-trait", "auto_impl", - "axum", + "axum 0.7.5", "bytes", "chrono", "clap", @@ -811,13 +828,13 @@ version = "0.2.0" dependencies = [ "anvil-rpc", "async-trait", - "axum", + "axum 0.7.5", "bytes", "clap", "futures", "interprocess", "parking_lot", - "pin-project", + "pin-project 1.1.5", "serde", "serde_json", "thiserror", @@ -990,6 +1007,19 @@ dependencies = [ "term", ] +[[package]] +name = "async-compression" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd066d0b4ef8ecb03a55319dc13aa6910616d0f44008a045bb1835af830abff5" +dependencies = [ + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", +] + [[package]] name = "async-priority-channel" version = "0.1.0" @@ -1414,6 +1444,34 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core 0.3.4", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.28", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper 0.1.2", + "tower", + "tower-layer", + "tower-service", +] + [[package]] name = "axum" version = "0.7.5" @@ -1421,7 +1479,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" dependencies = [ "async-trait", - "axum-core", + "axum-core 0.4.3", "base64 0.21.7", "bytes", "futures-util", @@ -1451,6 +1509,23 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + [[package]] name = "axum-core" version = "0.4.3" @@ -1878,7 +1953,7 @@ dependencies = [ "foundry-evm", "once_cell", "regex", - "reqwest", + "reqwest 0.12.4", "revm", "rustyline", "semver 1.0.23", @@ -2451,7 +2526,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown", + "hashbrown 0.14.5", "lock_api", "once_cell", "parking_lot_core", @@ -2777,6 +2852,15 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + [[package]] name = "endian-type" version = "0.1.2" @@ -3165,7 +3249,7 @@ dependencies = [ "alloy-transport", "anvil", "async-trait", - "axum", + "axum 0.7.5", "clap", "clap_complete", "clap_complete_fig", @@ -3206,7 +3290,7 @@ dependencies = [ "proptest", "rayon", "regex", - "reqwest", + "reqwest 0.12.4", "revm-inspectors", "rustc-hash", "semver 1.0.23", @@ -3338,7 +3422,7 @@ dependencies = [ "itertools 0.13.0", "once_cell", "regex", - "reqwest", + "reqwest 0.12.4", "revm-primitives", "semver 1.0.23", "serde", @@ -3368,7 +3452,7 @@ dependencies = [ "alloy-json-abi", "alloy-primitives", "foundry-compilers", - "reqwest", + "reqwest 0.12.4", "semver 1.0.23", "serde", "serde_json", @@ -3495,7 +3579,7 @@ dependencies = [ "foundry-macros", "num-format", "once_cell", - "reqwest", + "reqwest 0.12.4", "rustc-hash", "semver 1.0.23", "serde", @@ -3571,7 +3655,7 @@ dependencies = [ "once_cell", "path-slash", "regex", - "reqwest", + "reqwest 0.12.4", "revm-primitives", "semver 1.0.23", "serde", @@ -3698,7 +3782,7 @@ dependencies = [ "foundry-evm-core", "foundry-evm-coverage", "foundry-evm-traces", - "indexmap", + "indexmap 2.2.6", "itertools 0.13.0", "parking_lot", "proptest", @@ -3788,6 +3872,7 @@ dependencies = [ "alloy-primitives", "alloy-signer", "alloy-signer-aws", + "alloy-signer-gcp", "alloy-signer-ledger", "alloy-signer-trezor", "alloy-signer-wallet", @@ -3800,6 +3885,7 @@ dependencies = [ "derive_builder", "eyre", "foundry-config", + "gcloud-sdk", "rpassword", "serde", "thiserror", @@ -3949,6 +4035,49 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" +[[package]] +name = "gcemeta" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d460327b24cc34c86d53d60a90e9e6044817f7906ebd9baa5c3d0ee13e1ecf" +dependencies = [ + "bytes", + "hyper 0.14.28", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "gcloud-sdk" +version = "0.24.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa57d45d9a9778e0bf38deb6a4c9dbe293fa021d0b70b126b5dc593979b08569" +dependencies = [ + "async-trait", + "chrono", + "futures", + "gcemeta", + "hyper 0.14.28", + "jsonwebtoken", + "once_cell", + "prost", + "prost-types", + "reqwest 0.11.27", + "secret-vault-value", + "serde", + "serde_json", + "tokio", + "tonic", + "tower", + "tower-layer", + "tower-util", + "tracing", + "url", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -4239,7 +4368,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap", + "indexmap 2.2.6", "slab", "tokio", "tokio-util", @@ -4270,6 +4399,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hashbrown" version = "0.14.5" @@ -4525,6 +4660,18 @@ dependencies = [ "tower-service", ] +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper 0.14.28", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + [[package]] name = "hyper-tls" version = "0.6.0" @@ -4678,6 +4825,16 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + [[package]] name = "indexmap" version = "2.2.6" @@ -4685,7 +4842,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.14.5", ] [[package]] @@ -5039,7 +5196,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" dependencies = [ - "hashbrown", + "hashbrown 0.14.5", ] [[package]] @@ -5849,7 +6006,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap", + "indexmap 2.2.6", ] [[package]] @@ -5933,13 +6090,33 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ef0f924a5ee7ea9cbcea77529dba45f8a9ba9f622419fe3386ca581a3ae9d5a" +dependencies = [ + "pin-project-internal 0.4.30", +] + [[package]] name = "pin-project" version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ - "pin-project-internal", + "pin-project-internal 1.1.5", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "851c8d0ce9bebe43790dedfc86614c23494ac9f423dd618d3a61fc693eafe61e" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -6189,6 +6366,38 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "prost" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" +dependencies = [ + "anyhow", + "itertools 0.12.1", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "prost-types" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" +dependencies = [ + "prost", +] + [[package]] name = "protobuf" version = "3.3.0" @@ -6431,6 +6640,51 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "async-compression", + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.28", + "hyper-rustls 0.24.2", + "ipnet", + "js-sys", + "log", + "mime", + "mime_guess", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls 0.21.12", + "rustls-native-certs 0.6.3", + "rustls-pemfile 1.0.4", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 0.1.2", + "system-configuration", + "tokio", + "tokio-rustls 0.24.1", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "winreg 0.50.0", +] + [[package]] name = "reqwest" version = "0.12.4" @@ -6477,7 +6731,7 @@ dependencies = [ "wasm-streams", "web-sys", "webpki-roots 0.26.1", - "winreg", + "winreg 0.52.0", ] [[package]] @@ -6551,7 +6805,7 @@ dependencies = [ "derive_more", "dyn-clone", "enumn", - "hashbrown", + "hashbrown 0.14.5", "hex", "once_cell", "serde", @@ -7019,6 +7273,19 @@ dependencies = [ "cc", ] +[[package]] +name = "secret-vault-value" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5f8cfb86d2019f64a4cfb49e499f401f406fbec946c1ffeea9d0504284347de" +dependencies = [ + "prost", + "prost-types", + "serde", + "serde_json", + "zeroize", +] + [[package]] name = "security-framework" version = "2.11.0" @@ -7112,7 +7379,7 @@ version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ - "indexmap", + "indexmap 2.2.6", "itoa", "ryu", "serde", @@ -7571,7 +7838,7 @@ dependencies = [ "dirs 5.0.1", "fs4", "once_cell", - "reqwest", + "reqwest 0.12.4", "semver 1.0.23", "serde", "serde_json", @@ -7640,6 +7907,27 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tap" version = "1.0.1" @@ -7854,6 +8142,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "tokio-io-timeout" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-macros" version = "2.3.0" @@ -7963,7 +8261,7 @@ version = "0.8.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba" dependencies = [ - "indexmap", + "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", @@ -7985,7 +8283,7 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap", + "indexmap 2.2.6", "toml_datetime", "winnow 0.5.40", ] @@ -7996,13 +8294,44 @@ version = "0.22.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" dependencies = [ - "indexmap", + "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", "winnow 0.6.9", ] +[[package]] +name = "tonic" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76c4eb7a4e9ef9d4763600161f12f5070b92a578e1b634db88a6887844c91a13" +dependencies = [ + "async-stream", + "async-trait", + "axum 0.6.20", + "base64 0.21.7", + "bytes", + "h2", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.28", + "hyper-timeout", + "percent-encoding", + "pin-project 1.1.5", + "prost", + "rustls-native-certs 0.7.0", + "rustls-pemfile 2.1.2", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.25.0", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "topological-sort" version = "0.2.2" @@ -8017,9 +8346,13 @@ checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", - "pin-project", + "indexmap 1.9.3", + "pin-project 1.1.5", "pin-project-lite", + "rand", + "slab", "tokio", + "tokio-util", "tower-layer", "tower-service", "tracing", @@ -8062,6 +8395,18 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +[[package]] +name = "tower-util" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1093c19826d33807c72511e68f73b4a0469a3f22c2bd5f7d5212178b4b89674" +dependencies = [ + "futures-core", + "futures-util", + "pin-project 0.4.30", + "tower-service", +] + [[package]] name = "tracing" version = "0.1.40" @@ -8864,6 +9209,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "winreg" version = "0.52.0" @@ -8987,7 +9342,7 @@ dependencies = [ "crossbeam-utils", "displaydoc", "flate2", - "indexmap", + "indexmap 2.2.6", "memchr", "thiserror", "zopfli", diff --git a/Cargo.toml b/Cargo.toml index 1751f6fa2..2418bdf08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -184,6 +184,7 @@ alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", defa alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } alloy-signer-wallet = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } +alloy-signer-gcp = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } diff --git a/crates/wallets/Cargo.toml b/crates/wallets/Cargo.toml index 5a0477253..34dfda6d4 100644 --- a/crates/wallets/Cargo.toml +++ b/crates/wallets/Cargo.toml @@ -30,6 +30,13 @@ alloy-signer-aws = { workspace = true, features = ["eip712"], optional = true } aws-config = { version = "1", optional = true } # default-features are necessary aws-sdk-kms = { version = "1", default-features = false, optional = true } +# gcp-kms +alloy-signer-gcp = { workspace = true, features = ["eip712"], optional = true } +gcloud-sdk = { version = "0.24", features = [ + "google-cloud-kms-v1", + "google-longrunning", +], optional = true } + async-trait = "0.1" clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } derive_builder = "0.20.0" @@ -47,3 +54,4 @@ tokio = { workspace = true, features = ["macros"] } default = ["rustls"] rustls = ["aws-sdk-kms?/rustls"] aws-kms = ["dep:alloy-signer-aws", "dep:aws-config", "dep:aws-sdk-kms"] +gcp-kms = ["dep:alloy-signer-gcp", "dep:gcloud-sdk"] diff --git a/crates/wallets/src/error.rs b/crates/wallets/src/error.rs index e3cc67c61..c7ac55b82 100644 --- a/crates/wallets/src/error.rs +++ b/crates/wallets/src/error.rs @@ -7,6 +7,9 @@ use hex::FromHexError; #[cfg(feature = "aws-kms")] use alloy_signer_aws::AwsSignerError; +#[cfg(feature = "gcp-kms")] +use alloy_signer_gcp::GcpSignerError; + #[derive(Debug, thiserror::Error)] pub enum PrivateKeyError { #[error("Failed to create wallet from private key. Private key is invalid hex: {0}")] @@ -27,6 +30,9 @@ pub enum WalletSignerError { #[cfg(feature = "aws-kms")] Aws(#[from] AwsSignerError), #[error(transparent)] + #[cfg(feature = "gcp-kms")] + Gcp(#[from] GcpSignerError), + #[error(transparent)] Io(#[from] std::io::Error), #[error(transparent)] InvalidHex(#[from] FromHexError), @@ -40,4 +46,8 @@ impl WalletSignerError { pub fn aws_unsupported() -> Self { Self::UnsupportedSigner("AWS KMS") } + + pub fn gcp_unsupported() -> Self { + Self::UnsupportedSigner("Google Cloud KMS") + } } diff --git a/crates/wallets/src/raw_wallet.rs b/crates/wallets/src/raw_wallet.rs index f8a9d447c..3a5169cad 100644 --- a/crates/wallets/src/raw_wallet.rs +++ b/crates/wallets/src/raw_wallet.rs @@ -47,7 +47,7 @@ impl RawWalletOpts { return Ok(Some(PendingSigner::Interactive.unlock()?)); } if let Some(private_key) = &self.private_key { - return Ok(Some(utils::create_private_key_signer(private_key)?)) + return Ok(Some(utils::create_private_key_signer(private_key)?)); } if let Some(mnemonic) = &self.mnemonic { return Ok(Some(utils::create_mnemonic_signer( @@ -55,7 +55,7 @@ impl RawWalletOpts { self.mnemonic_passphrase.as_deref(), self.hd_path.as_deref(), self.mnemonic_index, - )?)) + )?)); } Ok(None) } diff --git a/crates/wallets/src/wallet.rs b/crates/wallets/src/wallet.rs index 5b984d701..ce049fe40 100644 --- a/crates/wallets/src/wallet.rs +++ b/crates/wallets/src/wallet.rs @@ -11,6 +11,7 @@ use serde::Serialize; /// 3. Trezor /// 4. Keystore (via file path) /// 5. AWS KMS +/// 6. Google Cloud KMS #[derive(Clone, Debug, Default, Serialize, Parser)] #[command(next_help_heading = "Wallet options", about = None, long_about = None)] pub struct WalletOpts { @@ -80,6 +81,10 @@ pub struct WalletOpts { /// Use AWS Key Management Service. #[arg(long, help_heading = "Wallet options - remote", hide = !cfg!(feature = "aws-kms"))] pub aws: bool, + + /// Use Google Cloud Key Management Service. + #[arg(long, help_heading = "Wallet options - remote", hide = !cfg!(feature = "gcp-kms"))] + pub gcp: bool, } impl WalletOpts { @@ -95,6 +100,13 @@ impl WalletOpts { } else if self.aws { let key_id = std::env::var("AWS_KMS_KEY_ID")?; WalletSigner::from_aws(key_id).await? + } else if self.gcp { + let project_id = std::env::var("GCP_PROJECT_ID")?; + let location = std::env::var("GCP_LOCATION")?; + let keyring = std::env::var("GCP_KEYRING")?; + let key_name = std::env::var("GCP_KEY_NAME")?; + let key_version = std::env::var("GCP_KEY_VERSION")?.parse()?; + WalletSigner::from_gcp(project_id, location, keyring, key_name, key_version).await? } else if let Some(raw_wallet) = self.raw.signer()? { raw_wallet } else if let Some(path) = utils::maybe_get_keystore_path( @@ -119,7 +131,7 @@ impl WalletOpts { Error accessing local wallet. Did you set a private key, mnemonic or keystore? Run `cast send --help` or `forge create --help` and use the corresponding CLI flag to set your key via: ---private-key, --mnemonic-path, --aws, --interactive, --trezor or --ledger. +--private-key, --mnemonic-path, --aws, --gcp, --interactive, --trezor or --ledger. Alternatively, if you're using a local node with unlocked accounts, use the --unlocked flag and either set the `ETH_FROM` environment variable to the address of the unlocked account you want to use, or provide the --from flag with the address directly." @@ -199,6 +211,7 @@ mod tests { ledger: false, trezor: false, aws: false, + gcp: false, }; match wallet.signer().await { Ok(_) => { diff --git a/crates/wallets/src/wallet_signer.rs b/crates/wallets/src/wallet_signer.rs index 0e0bbf709..d0f1de4a2 100644 --- a/crates/wallets/src/wallet_signer.rs +++ b/crates/wallets/src/wallet_signer.rs @@ -14,6 +14,15 @@ use std::path::PathBuf; #[cfg(feature = "aws-kms")] use {alloy_signer_aws::AwsSigner, aws_config::BehaviorVersion, aws_sdk_kms::Client as AwsClient}; +#[cfg(feature = "gcp-kms")] +use { + alloy_signer_gcp::{GcpKeyRingRef, GcpSigner, GcpSignerError, KeySpecifier}, + gcloud_sdk::{ + google::cloud::kms::v1::key_management_service_client::KeyManagementServiceClient, + GoogleApi, + }, +}; + pub type Result = std::result::Result; /// Wrapper enum around different signers. @@ -28,6 +37,9 @@ pub enum WalletSigner { /// Wrapper around AWS KMS signer. #[cfg(feature = "aws-kms")] Aws(AwsSigner), + /// Wrapper around Google Cloud KMS signer. + #[cfg(feature = "gcp-kms")] + Gcp(GcpSigner), } impl WalletSigner { @@ -57,6 +69,43 @@ impl WalletSigner { } } + pub async fn from_gcp( + project_id: String, + location: String, + keyring: String, + key_name: String, + key_version: u64, + ) -> Result { + #[cfg(feature = "gcp-kms")] + { + let keyring = GcpKeyRingRef::new(&project_id, &location, &keyring); + let client = match GoogleApi::from_function( + KeyManagementServiceClient::new, + "https://cloudkms.googleapis.com", + None, + ) + .await + { + Ok(c) => c, + Err(e) => return Err(WalletSignerError::from(GcpSignerError::GoogleKmsError(e))), + }; + + let specifier = KeySpecifier::new(keyring, &key_name, key_version); + + Ok(Self::Gcp(GcpSigner::new(client, specifier, None).await?)) + } + + #[cfg(not(feature = "gcp-kms"))] + { + let _ = project_id; + let _ = location; + let _ = keyring; + let _ = key_name; + let _ = key_version; + Err(WalletSignerError::gcp_unsupported()) + } + } + pub fn from_private_key(private_key: &B256) -> Result { Ok(Self::Local(LocalWallet::from_bytes(private_key)?)) } @@ -102,6 +151,10 @@ impl WalletSigner { Self::Aws(aws) => { senders.push(alloy_signer::Signer::address(aws)); } + #[cfg(feature = "gcp-kms")] + Self::Gcp(gcp) => { + senders.push(alloy_signer::Signer::address(gcp)); + } } Ok(senders) } @@ -136,6 +189,8 @@ macro_rules! delegate { Self::Trezor($inner) => $e, #[cfg(feature = "aws-kms")] Self::Aws($inner) => $e, + #[cfg(feature = "gcp-kms")] + Self::Gcp($inner) => $e, } }; } From f43d3ce8515af83cb75152dd84d2af220e0b894b Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Fri, 7 Jun 2024 17:07:49 +0300 Subject: [PATCH 379/430] feat(fuzz) - add test progress (#7914) * feat(forge) - add test progress * Code cleanup * Invariant progress bar cleanup * Display number of threads and shrink run counter * Add progress for regular fuzz tests too * Cleanup code, use rayon collect * Changes after review. Cleanup * Fix clippy --- Cargo.lock | 1 + crates/evm/evm/Cargo.toml | 1 + crates/evm/evm/src/executors/fuzz/mod.rs | 7 ++ crates/evm/evm/src/executors/invariant/mod.rs | 7 ++ .../evm/evm/src/executors/invariant/replay.rs | 4 +- .../evm/evm/src/executors/invariant/shrink.rs | 15 ++- crates/forge/bin/cmd/test/mod.rs | 7 +- crates/forge/src/lib.rs | 1 + crates/forge/src/multi_runner.rs | 59 +++++++-- crates/forge/src/progress.rs | 116 ++++++++++++++++++ crates/forge/src/runner.rs | 24 +++- 11 files changed, 228 insertions(+), 14 deletions(-) create mode 100644 crates/forge/src/progress.rs diff --git a/Cargo.lock b/Cargo.lock index 5bd074b35..a86b6e18a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3706,6 +3706,7 @@ dependencies = [ "foundry-evm-coverage", "foundry-evm-fuzz", "foundry-evm-traces", + "indicatif", "parking_lot", "proptest", "revm", diff --git a/crates/evm/evm/Cargo.toml b/crates/evm/evm/Cargo.toml index e3a1c9c6b..3b00c9d66 100644 --- a/crates/evm/evm/Cargo.toml +++ b/crates/evm/evm/Cargo.toml @@ -50,3 +50,4 @@ parking_lot = "0.12" proptest = "1" thiserror = "1" tracing = "0.1" +indicatif = "0.17" diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index 163d5444d..6cd411f19 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -14,6 +14,7 @@ use foundry_evm_fuzz::{ BaseCounterExample, CounterExample, FuzzCase, FuzzError, FuzzFixtures, FuzzTestResult, }; use foundry_evm_traces::CallTraceArena; +use indicatif::ProgressBar; use proptest::test_runner::{TestCaseError, TestError, TestRunner}; use std::{borrow::Cow, cell::RefCell}; @@ -59,6 +60,7 @@ impl FuzzedExecutor { address: Address, should_fail: bool, rd: &RevertDecoder, + progress: Option<&ProgressBar>, ) -> FuzzTestResult { // Stores the first Fuzzcase let first_case: RefCell> = RefCell::default(); @@ -91,6 +93,11 @@ impl FuzzedExecutor { let run_result = self.runner.clone().run(&strat, |calldata| { let fuzz_res = self.single_fuzz(address, should_fail, calldata)?; + // If running with progress then increment current run. + if let Some(progress) = progress { + progress.inc(1); + }; + match fuzz_res { FuzzOutcome::Case(case) => { let mut first_case = first_case.borrow_mut(); diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 2c67c792d..c32293493 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -20,6 +20,7 @@ use foundry_evm_fuzz::{ FuzzCase, FuzzFixtures, FuzzedCases, }; use foundry_evm_traces::CallTraceArena; +use indicatif::ProgressBar; use parking_lot::RwLock; use proptest::{ strategy::{Strategy, ValueTree}, @@ -135,6 +136,7 @@ impl<'a> InvariantExecutor<'a> { &mut self, invariant_contract: InvariantContract<'_>, fuzz_fixtures: &FuzzFixtures, + progress: Option<&ProgressBar>, ) -> Result { // Throw an error to abort test run if the invariant function accepts input params if !invariant_contract.invariant_function.inputs.is_empty() { @@ -314,6 +316,11 @@ impl<'a> InvariantExecutor<'a> { // Revert state to not persist values between runs. fuzz_state.revert(); + // If running with progress then increment completed runs. + if let Some(progress) = progress { + progress.inc(1); + } + Ok(()) }); diff --git a/crates/evm/evm/src/executors/invariant/replay.rs b/crates/evm/evm/src/executors/invariant/replay.rs index b6c2c6d9e..466ae0cf1 100644 --- a/crates/evm/evm/src/executors/invariant/replay.rs +++ b/crates/evm/evm/src/executors/invariant/replay.rs @@ -11,6 +11,7 @@ use foundry_evm_fuzz::{ BaseCounterExample, }; use foundry_evm_traces::{load_contracts, TraceKind, Traces}; +use indicatif::ProgressBar; use parking_lot::RwLock; use proptest::test_runner::TestError; use revm::primitives::U256; @@ -97,13 +98,14 @@ pub fn replay_error( logs: &mut Vec, traces: &mut Traces, coverage: &mut Option, + progress: Option<&ProgressBar>, ) -> Result> { match failed_case.test_error { // Don't use at the moment. TestError::Abort(_) => Ok(vec![]), TestError::Fail(_, ref calls) => { // Shrink sequence of failed calls. - let calls = shrink_sequence(failed_case, calls, &executor)?; + let calls = shrink_sequence(failed_case, calls, &executor, progress)?; set_up_inner_replay(&mut executor, &failed_case.inner_sequence); diff --git a/crates/evm/evm/src/executors/invariant/shrink.rs b/crates/evm/evm/src/executors/invariant/shrink.rs index 9d397bfe1..d61a84592 100644 --- a/crates/evm/evm/src/executors/invariant/shrink.rs +++ b/crates/evm/evm/src/executors/invariant/shrink.rs @@ -2,8 +2,9 @@ use crate::executors::{invariant::error::FailedInvariantCaseData, Executor}; use alloy_primitives::{Address, Bytes, U256}; use foundry_evm_core::constants::CALLER; use foundry_evm_fuzz::invariant::BasicTxDetails; +use indicatif::ProgressBar; use proptest::bits::{BitSetLike, VarBitSet}; -use std::borrow::Cow; +use std::{borrow::Cow, cmp::min}; #[derive(Clone, Copy, Debug)] struct Shrink { @@ -83,9 +84,17 @@ pub(crate) fn shrink_sequence( failed_case: &FailedInvariantCaseData, calls: &[BasicTxDetails], executor: &Executor, + progress: Option<&ProgressBar>, ) -> eyre::Result> { trace!(target: "forge::test", "Shrinking sequence of {} calls.", calls.len()); + // Reset run count and display shrinking message. + if let Some(progress) = progress { + progress.set_length(min(calls.len(), failed_case.shrink_run_limit as usize) as u64); + progress.reset(); + progress.set_message(" Shrink"); + } + // Special case test: the invariant is *unsatisfiable* - it took 0 calls to // break the invariant -- consider emitting a warning. let error_call_result = @@ -112,6 +121,10 @@ pub(crate) fn shrink_sequence( Ok((true, _)) if !shrinker.complicate() => break, _ => {} } + + if let Some(progress) = progress { + progress.inc(1); + } } Ok(shrinker.current().map(|idx| &calls[idx]).cloned().collect()) diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 3f54e01e5..d2de7ef23 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -135,6 +135,10 @@ pub struct TestArgs { /// Print detailed test summary table. #[arg(long, help_heading = "Display options", requires = "summary")] pub detailed: bool, + + /// Show test execution progress. + #[arg(long)] + pub show_progress: bool, } impl TestArgs { @@ -387,9 +391,10 @@ impl TestArgs { // Run tests. let (tx, rx) = channel::<(String, SuiteResult)>(); let timer = Instant::now(); + let show_progress = self.show_progress; let handle = tokio::task::spawn_blocking({ let filter = filter.clone(); - move || runner.test(&filter, tx) + move || runner.test(&filter, tx, show_progress) }); // Set up trace identifiers. diff --git a/crates/forge/src/lib.rs b/crates/forge/src/lib.rs index 56ca43286..c46800067 100644 --- a/crates/forge/src/lib.rs +++ b/crates/forge/src/lib.rs @@ -24,6 +24,7 @@ pub use multi_runner::{MultiContractRunner, MultiContractRunnerBuilder}; mod runner; pub use runner::ContractRunner; +mod progress; pub mod result; // TODO: remove diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index de37469d8..1e2be6c7c 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -1,7 +1,8 @@ //! Forge test runner for multiple contracts. use crate::{ - result::SuiteResult, runner::LIBRARY_DEPLOYER, ContractRunner, TestFilter, TestOptions, + progress::TestsProgress, result::SuiteResult, runner::LIBRARY_DEPLOYER, ContractRunner, + TestFilter, TestOptions, }; use alloy_json_abi::{Function, JsonAbi}; use alloy_primitives::{Address, Bytes, U256}; @@ -140,7 +141,7 @@ impl MultiContractRunner { filter: &dyn TestFilter, ) -> impl Iterator { let (tx, rx) = mpsc::channel(); - self.test(filter, tx); + self.test(filter, tx, false); rx.into_iter() } @@ -150,7 +151,12 @@ impl MultiContractRunner { /// before executing all contracts and their tests in _parallel_. /// /// Each Executor gets its own instance of the `Backend`. - pub fn test(&mut self, filter: &dyn TestFilter, tx: mpsc::Sender<(String, SuiteResult)>) { + pub fn test( + &mut self, + filter: &dyn TestFilter, + tx: mpsc::Sender<(String, SuiteResult)>, + show_progress: bool, + ) { let handle = tokio::runtime::Handle::current(); trace!("running all tests"); @@ -167,11 +173,45 @@ impl MultiContractRunner { find_time, ); - contracts.par_iter().for_each(|&(id, contract)| { - let _guard = handle.enter(); - let result = self.run_tests(id, contract, db.clone(), filter, &handle); - let _ = tx.send((id.identifier(), result)); - }) + if show_progress { + let tests_progress = TestsProgress::new(contracts.len(), rayon::current_num_threads()); + // Collect test suite results to stream at the end of test run. + let results: Vec<(String, SuiteResult)> = contracts + .par_iter() + .map(|&(id, contract)| { + let _guard = handle.enter(); + tests_progress.inner.lock().start_suite_progress(&id.identifier()); + + let result = self.run_tests( + id, + contract, + db.clone(), + filter, + &handle, + Some(&tests_progress), + ); + + tests_progress + .inner + .lock() + .end_suite_progress(&id.identifier(), result.summary()); + + (id.identifier(), result) + }) + .collect(); + + tests_progress.inner.lock().clear(); + + results.iter().for_each(|result| { + let _ = tx.send(result.to_owned()); + }); + } else { + contracts.par_iter().for_each(|&(id, contract)| { + let _guard = handle.enter(); + let result = self.run_tests(id, contract, db.clone(), filter, &handle, None); + let _ = tx.send((id.identifier(), result)); + }) + } } fn run_tests( @@ -181,6 +221,7 @@ impl MultiContractRunner { db: Backend, filter: &dyn TestFilter, handle: &tokio::runtime::Handle, + progress: Option<&TestsProgress>, ) -> SuiteResult { let identifier = artifact_id.identifier(); let mut span_name = identifier.as_str(); @@ -222,7 +263,9 @@ impl MultiContractRunner { self.sender, &self.revert_decoder, self.debug, + progress, ); + let r = runner.run_tests(filter, &self.test_options, self.known_contracts.clone(), handle); debug!(duration=?r.duration, "executed all tests in contract"); diff --git a/crates/forge/src/progress.rs b/crates/forge/src/progress.rs new file mode 100644 index 000000000..8d047c413 --- /dev/null +++ b/crates/forge/src/progress.rs @@ -0,0 +1,116 @@ +use indicatif::{MultiProgress, ProgressBar}; +use parking_lot::Mutex; +use std::{collections::HashMap, sync::Arc, time::Duration}; + +/// State of [ProgressBar]s displayed for the given test run. +/// Shows progress of all test suites matching filter. +/// For each test within the test suite an individual progress bar is displayed. +/// When a test suite completes, their progress is removed from overall progress and result summary +/// is displayed. +#[derive(Debug)] +pub struct TestsProgressState { + /// Main [MultiProgress] instance showing progress for all test suites. + multi: MultiProgress, + /// Progress bar counting completed / remaining test suites. + overall_progress: ProgressBar, + /// Individual test suites progress. + suites_progress: HashMap, +} + +impl TestsProgressState { + // Creates overall tests progress state. + pub fn new(suites_len: usize, threads_no: usize) -> Self { + let multi = MultiProgress::new(); + let overall_progress = multi.add(ProgressBar::new(suites_len as u64)); + overall_progress.set_style( + indicatif::ProgressStyle::with_template("{bar:40.cyan/blue} {pos:>7}/{len:7} {msg}") + .unwrap() + .progress_chars("##-"), + ); + overall_progress.set_message(format!("completed (with {} threads)", threads_no as u64)); + Self { multi, overall_progress, suites_progress: HashMap::default() } + } + + /// Creates new test suite progress and add it to overall progress. + pub fn start_suite_progress(&mut self, suite_name: &String) { + let suite_progress = self.multi.add(ProgressBar::new_spinner()); + suite_progress.set_style( + indicatif::ProgressStyle::with_template("{spinner} {wide_msg:.bold.dim}") + .unwrap() + .tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈ "), + ); + suite_progress.set_message(format!("{suite_name} ")); + suite_progress.enable_steady_tick(Duration::from_millis(100)); + self.suites_progress.insert(suite_name.to_owned(), suite_progress); + } + + /// Prints suite result summary and removes it from overall progress. + pub fn end_suite_progress(&mut self, suite_name: &String, result_summary: String) { + if let Some(suite_progress) = self.suites_progress.remove(suite_name) { + self.multi.suspend(|| { + println!("{suite_name}\n ↪ {result_summary}"); + }); + suite_progress.finish_and_clear(); + // Increment test progress bar to reflect completed test suite. + self.overall_progress.inc(1); + } + } + + /// Creates progress entry for fuzz tests. + /// Set the prefix and total number of runs. Message is updated during execution with current + /// phase. Test progress is placed under test suite progress entry so all tests within suite + /// are grouped. + pub fn start_fuzz_progress( + &mut self, + suite_name: &str, + test_name: &String, + runs: u32, + ) -> Option { + if let Some(suite_progress) = self.suites_progress.get(suite_name) { + let fuzz_progress = + self.multi.insert_after(suite_progress, ProgressBar::new(runs as u64)); + fuzz_progress.set_style( + indicatif::ProgressStyle::with_template( + " ↪ {prefix:.bold.dim}: [{pos}/{len}]{msg} Runs", + ) + .unwrap() + .tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈ "), + ); + fuzz_progress.set_prefix(test_name.to_string()); + Some(fuzz_progress) + } else { + None + } + } + + /// Removes overall test progress. + pub fn clear(&mut self) { + self.multi.clear().unwrap(); + } +} + +/// Clonable wrapper around [TestsProgressState]. +#[derive(Debug, Clone)] +pub struct TestsProgress { + pub inner: Arc>, +} + +impl TestsProgress { + pub fn new(suites_len: usize, threads_no: usize) -> Self { + Self { inner: Arc::new(Mutex::new(TestsProgressState::new(suites_len, threads_no))) } + } +} + +/// Helper function for creating fuzz test progress bar. +pub fn start_fuzz_progress( + tests_progress: Option<&TestsProgress>, + suite_name: &str, + test_name: &String, + runs: u32, +) -> Option { + if let Some(progress) = tests_progress { + progress.inner.lock().start_fuzz_progress(suite_name, test_name, runs) + } else { + None + } +} diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index dadd51963..46a6cad68 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -3,6 +3,7 @@ use crate::{ fuzz::{invariant::BasicTxDetails, BaseCounterExample}, multi_runner::{is_matching_test, TestContract}, + progress::{start_fuzz_progress, TestsProgress}, result::{SuiteResult, TestKind, TestResult, TestSetup, TestStatus}, TestFilter, TestOptions, }; @@ -68,6 +69,8 @@ pub struct ContractRunner<'a> { pub sender: Address, /// Should generate debug traces pub debug: bool, + /// Overall test run progress. + progress: Option<&'a TestsProgress>, } impl<'a> ContractRunner<'a> { @@ -81,6 +84,7 @@ impl<'a> ContractRunner<'a> { sender: Option
, revert_decoder: &'a RevertDecoder, debug: bool, + progress: Option<&'a TestsProgress>, ) -> Self { Self { name, @@ -91,6 +95,7 @@ impl<'a> ContractRunner<'a> { sender: sender.unwrap_or_default(), revert_decoder, debug, + progress, } } } @@ -379,6 +384,7 @@ impl<'a> ContractRunner<'a> { let res = if func.is_invariant_test() { 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, @@ -391,6 +397,7 @@ impl<'a> ContractRunner<'a> { debug_assert!(func.is_test()); 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()) } else { debug_assert!(func.is_test()); @@ -631,8 +638,11 @@ impl<'a> ContractRunner<'a> { } } + let progress = + start_fuzz_progress(self.progress, self.name, &func.name, invariant_config.runs); let InvariantFuzzTestResult { error, cases, reverts, last_run_inputs, gas_report_traces } = - match evm.invariant_fuzz(invariant_contract.clone(), &fuzz_fixtures) { + match evm.invariant_fuzz(invariant_contract.clone(), &fuzz_fixtures, progress.as_ref()) + { Ok(x) => x, Err(e) => { return TestResult { @@ -670,6 +680,7 @@ impl<'a> ContractRunner<'a> { &mut logs, &mut traces, &mut coverage, + progress.as_ref(), ) { Ok(call_sequence) => { if !call_sequence.is_empty() { @@ -754,6 +765,7 @@ impl<'a> ContractRunner<'a> { } = setup; // Run fuzz test + let progress = start_fuzz_progress(self.progress, self.name, &func.name, fuzz_config.runs); let start = Instant::now(); let fuzzed_executor = FuzzedExecutor::new( self.executor.clone(), @@ -761,8 +773,14 @@ impl<'a> ContractRunner<'a> { self.sender, fuzz_config.clone(), ); - let result = - fuzzed_executor.fuzz(func, &fuzz_fixtures, address, should_fail, self.revert_decoder); + let result = fuzzed_executor.fuzz( + func, + &fuzz_fixtures, + address, + should_fail, + self.revert_decoder, + progress.as_ref(), + ); let mut debug = Default::default(); let mut breakpoints = Default::default(); From c4d18c5790d89011f186c9f8827a921fcd983589 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 7 Jun 2024 16:54:26 +0200 Subject: [PATCH 380/430] chore: update lockfile --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index a86b6e18a..05cf20bb5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7689,7 +7689,7 @@ dependencies = [ "futures", "once_cell", "regex", - "reqwest", + "reqwest 0.12.4", "rpassword", "serde", "serde_derive", From a169ef75cc730f58bb1e2c4d9bc3b534951ad59c Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 7 Jun 2024 17:14:26 +0200 Subject: [PATCH 381/430] chore: propagate fmt parser errors (#8109) --- crates/cast/bin/cmd/interface.rs | 54 ++++++++++++++++++++- crates/cast/src/lib.rs | 71 +--------------------------- crates/fmt/src/formatter.rs | 10 +++- crates/fmt/src/helpers.rs | 41 +++++++++------- crates/fmt/src/lib.rs | 2 +- crates/forge/bin/cmd/fmt.rs | 9 ++-- crates/forge/bin/cmd/geiger/error.rs | 7 ++- crates/forge/bin/cmd/geiger/find.rs | 18 +++---- 8 files changed, 104 insertions(+), 108 deletions(-) diff --git a/crates/cast/bin/cmd/interface.rs b/crates/cast/bin/cmd/interface.rs index 59956b78e..a402ea8af 100644 --- a/crates/cast/bin/cmd/interface.rs +++ b/crates/cast/bin/cmd/interface.rs @@ -1,6 +1,9 @@ -use cast::{AbiPath, SimpleCast}; +use alloy_chains::Chain; +use alloy_json_abi::ContractObject; +use alloy_primitives::Address; use clap::Parser; use eyre::{Context, Result}; +use foundry_block_explorers::Client; use foundry_cli::opts::EtherscanOpts; use foundry_common::fs; use foundry_config::Config; @@ -58,7 +61,42 @@ impl InterfaceArgs { } }; - let interfaces = SimpleCast::generate_interface(source).await?; + let items = match source { + AbiPath::Local { path, name } => { + let file = std::fs::read_to_string(&path).wrap_err("unable to read abi file")?; + let obj: ContractObject = serde_json::from_str(&file)?; + let abi = + obj.abi.ok_or_else(|| eyre::eyre!("could not find ABI in file {path}"))?; + let name = name.unwrap_or_else(|| "Interface".to_owned()); + vec![(abi, name)] + } + AbiPath::Etherscan { address, chain, api_key } => { + let client = Client::new(chain, api_key)?; + let source = client.contract_source_code(address).await?; + source + .items + .into_iter() + .map(|item| Ok((item.abi()?, item.contract_name))) + .collect::>>()? + } + }; + + let interfaces = items + .into_iter() + .map(|(contract_abi, name)| { + let source = match foundry_cli::utils::abi_to_solidity(&contract_abi, &name) { + Ok(generated_source) => generated_source, + Err(e) => { + warn!("Failed to format interface for {name}: {e}"); + contract_abi.to_sol(&name, None) + } + }; + Ok(InterfaceSource { + json_abi: serde_json::to_string_pretty(&contract_abi)?, + source, + }) + }) + .collect::>>()?; // put it all together let res = if json { @@ -85,3 +123,15 @@ impl InterfaceArgs { Ok(()) } } + +struct InterfaceSource { + json_abi: String, + source: String, +} + +// Local is a path to the directory containing the ABI files +// In case of etherscan, ABI is fetched from the address on the chain +enum AbiPath { + Local { path: String, name: Option }, + Etherscan { address: Address, chain: Chain, api_key: String }, +} diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index 70e8523ec..2c48aa783 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -3,7 +3,7 @@ use alloy_consensus::TxEnvelope; use alloy_dyn_abi::{DynSolType, DynSolValue, FunctionExt}; -use alloy_json_abi::{ContractObject, Function}; +use alloy_json_abi::Function; use alloy_network::AnyNetwork; use alloy_primitives::{ utils::{keccak256, ParseUnits, Unit}, @@ -973,19 +973,6 @@ where } } -pub struct InterfaceSource { - pub name: String, - pub json_abi: String, - pub source: String, -} - -// Local is a path to the directory containing the ABI files -// In case of etherscan, ABI is fetched from the address on the chain -pub enum AbiPath { - Local { path: String, name: Option }, - Etherscan { address: Address, chain: Chain, api_key: String }, -} - pub struct SimpleCast; impl SimpleCast { @@ -1636,62 +1623,6 @@ impl SimpleCast { Ok(hex::encode_prefixed(calldata)) } - /// Generates an interface in solidity from either a local file ABI or a verified contract on - /// Etherscan. It returns a vector of [`InterfaceSource`] structs that contain the source of the - /// interface and their name. - /// - /// Note: This removes the constructor from the ABI before generating the interface. - /// - /// ```no_run - /// use cast::{AbiPath, SimpleCast as Cast}; - /// # async fn foo() -> eyre::Result<()> { - /// let path = - /// AbiPath::Local { path: "utils/testdata/interfaceTestABI.json".to_owned(), name: None }; - /// let interfaces = Cast::generate_interface(path).await?; - /// println!("interface {} {{\n {}\n}}", interfaces[0].name, interfaces[0].source); - /// # Ok(()) - /// # } - /// ``` - pub async fn generate_interface(address_or_path: AbiPath) -> Result> { - let (mut contract_abis, contract_names) = match address_or_path { - AbiPath::Local { path, name } => { - let file = std::fs::read_to_string(&path).wrap_err("unable to read abi file")?; - let obj: ContractObject = serde_json::from_str(&file)?; - let abi = - obj.abi.ok_or_else(|| eyre::eyre!("could not find ABI in file {path}"))?; - (vec![abi], vec![name.unwrap_or_else(|| "Interface".to_owned())]) - } - AbiPath::Etherscan { address, chain, api_key } => { - let client = Client::new(chain, api_key)?; - let source = client.contract_source_code(address).await?; - let names = source - .items - .iter() - .map(|item| item.contract_name.clone()) - .collect::>(); - - let abis = source.abis()?; - - (abis, names) - } - }; - contract_abis - .iter_mut() - .zip(contract_names) - .map(|(contract_abi, name)| { - // need to filter out the constructor - contract_abi.constructor.take(); - - let source = foundry_cli::utils::abi_to_solidity(contract_abi, &name)?; - Ok(InterfaceSource { - name, - json_abi: serde_json::to_string_pretty(contract_abi)?, - source, - }) - }) - .collect::>>() - } - /// Prints the slot number for the specified mapping type and input data. /// /// For value types `v`, slot number of `v` is `keccak256(concat(h(v), p))` where `h` is the diff --git a/crates/fmt/src/formatter.rs b/crates/fmt/src/formatter.rs index 52a789034..94052d9a9 100644 --- a/crates/fmt/src/formatter.rs +++ b/crates/fmt/src/formatter.rs @@ -6,6 +6,7 @@ use crate::{ comments::{ CommentPosition, CommentState, CommentStringExt, CommentType, CommentWithMetadata, Comments, }, + format_diagnostics_report, helpers::import_path_string, macros::*, solang_ext::{pt::*, *}, @@ -16,7 +17,8 @@ use crate::{ use alloy_primitives::Address; use foundry_config::fmt::{HexUnderscore, MultilineFuncHeaderStyle, SingleLineBlockStyle}; use itertools::{Either, Itertools}; -use std::{fmt::Write, str::FromStr}; +use solang_parser::diagnostics::Diagnostic; +use std::{fmt::Write, path::PathBuf, str::FromStr}; use thiserror::Error; type Result = std::result::Result; @@ -28,8 +30,11 @@ pub enum FormatterError { #[error(transparent)] Fmt(#[from] std::fmt::Error), /// Encountered invalid parse tree item. - #[error("Encountered invalid parse tree item at {0:?}")] + #[error("encountered invalid parse tree item at {0:?}")] InvalidParsedItem(Loc), + /// Failed to parse the source code + #[error("failed to parse file:\n{}", format_diagnostics_report(_0, _1.as_deref(), _2))] + Parse(String, Option, Vec), /// All other errors #[error(transparent)] Custom(Box), @@ -39,6 +44,7 @@ impl FormatterError { fn fmt() -> Self { Self::Fmt(std::fmt::Error) } + fn custom(err: impl std::error::Error + Send + Sync + 'static) -> Self { Self::Custom(Box::new(err)) } diff --git a/crates/fmt/src/helpers.rs b/crates/fmt/src/helpers.rs index 26fb48c40..7f05a9c09 100644 --- a/crates/fmt/src/helpers.rs +++ b/crates/fmt/src/helpers.rs @@ -22,8 +22,19 @@ pub struct Parsed<'a> { pub invalid_inline_config_items: Vec<(Loc, InvalidInlineConfigItem)>, } -/// Parse source code -pub fn parse(src: &str) -> Result, Vec> { +/// Parse source code. +pub fn parse(src: &str) -> Result, FormatterError> { + parse_raw(src).map_err(|diag| FormatterError::Parse(src.to_string(), None, diag)) +} + +/// Parse source code with a path for diagnostics. +pub fn parse2<'s>(src: &'s str, path: Option<&Path>) -> Result, FormatterError> { + parse_raw(src) + .map_err(|diag| FormatterError::Parse(src.to_string(), path.map(ToOwned::to_owned), diag)) +} + +/// Parse source code, returning a list of diagnostics on failure. +pub fn parse_raw(src: &str) -> Result, Vec> { let (pt, comments) = solang_parser::parse(src, 0)?; let comments = Comments::new(comments, src); let (inline_config_items, invalid_inline_config_items): (Vec<_>, Vec<_>) = @@ -46,10 +57,7 @@ pub fn format_to( /// Parse and format a string with default settings pub fn format(src: &str) -> Result { - let parsed = parse(src).map_err(|err| { - debug!(?err, "Parse error"); - FormatterError::Fmt(std::fmt::Error) - })?; + let parsed = parse(src)?; let mut output = String::new(); format_to(&mut output, parsed, FormatterConfig::default())?; @@ -75,18 +83,19 @@ pub fn offset_to_line_column(content: &str, start: usize) -> (usize, usize) { unreachable!("content.len() > start") } -/// Print the report of parser's diagnostics -pub fn print_diagnostics_report( +/// Formats parser diagnostics +pub fn format_diagnostics_report( content: &str, path: Option<&Path>, - diagnostics: Vec, -) -> std::io::Result<()> { + diagnostics: &[Diagnostic], +) -> String { if diagnostics.is_empty() { - return Ok(()); + return String::new(); } let filename = path.map(|p| p.file_name().unwrap().to_string_lossy().to_string()).unwrap_or_default(); + let mut s = Vec::new(); for diag in diagnostics { let (start, end) = (diag.loc.start(), diag.loc.end()); let mut report = Report::build(ReportKind::Error, &filename, start) @@ -94,16 +103,16 @@ pub fn print_diagnostics_report( .with_label( Label::new((&filename, start..end)) .with_color(Color::Red) - .with_message(format!("{}", diag.message.fg(Color::Red))), + .with_message(format!("{}", diag.message.as_str().fg(Color::Red))), ); - for note in diag.notes { - report = report.with_note(note.message); + for note in &diag.notes { + report = report.with_note(¬e.message); } - report.finish().print((&filename, Source::from(content)))?; + report.finish().write((&filename, Source::from(content)), &mut s).unwrap(); } - Ok(()) + String::from_utf8(s).unwrap() } pub fn import_path_string(path: &ImportPath) -> String { diff --git a/crates/fmt/src/lib.rs b/crates/fmt/src/lib.rs index 4d5fe17a7..006b4db02 100644 --- a/crates/fmt/src/lib.rs +++ b/crates/fmt/src/lib.rs @@ -21,7 +21,7 @@ pub use foundry_config::fmt::*; pub use comments::Comments; pub use formatter::{Formatter, FormatterError}; pub use helpers::{ - format, format_to, offset_to_line_column, parse, print_diagnostics_report, Parsed, + format, format_diagnostics_report, format_to, offset_to_line_column, parse, parse2, Parsed, }; pub use inline_config::InlineConfig; pub use visit::{Visitable, Visitor}; diff --git a/crates/forge/bin/cmd/fmt.rs b/crates/forge/bin/cmd/fmt.rs index c7d84eddb..55ba71780 100644 --- a/crates/forge/bin/cmd/fmt.rs +++ b/crates/forge/bin/cmd/fmt.rs @@ -1,6 +1,6 @@ use clap::{Parser, ValueHint}; -use eyre::Result; -use forge_fmt::{format_to, parse, print_diagnostics_report}; +use eyre::{Context, Result}; +use forge_fmt::{format_to, parse}; use foundry_cli::utils::{FoundryPathExt, LoadConfig}; use foundry_common::{fs, term::cli_warn}; use foundry_compilers::{compilers::solc::SolcLanguage, SOLC_EXTENSIONS}; @@ -102,9 +102,8 @@ impl FmtArgs { None => "stdin".to_string(), }; - let parsed = parse(&source).map_err(|diagnostics| { - let _ = print_diagnostics_report(&source, path, diagnostics); - eyre::eyre!("Failed to parse Solidity code for {name}. Leaving source unchanged.") + let parsed = parse(&source).wrap_err_with(|| { + format!("Failed to parse Solidity code for {name}. Leaving source unchanged.") })?; if !parsed.invalid_inline_config_items.is_empty() { diff --git a/crates/forge/bin/cmd/geiger/error.rs b/crates/forge/bin/cmd/geiger/error.rs index 77c6374ea..010fb237c 100644 --- a/crates/forge/bin/cmd/geiger/error.rs +++ b/crates/forge/bin/cmd/geiger/error.rs @@ -1,12 +1,11 @@ +use forge_fmt::FormatterError; use foundry_common::errors::FsPathError; -use solang_parser::diagnostics::Diagnostic; -use std::path::PathBuf; /// Possible errors when scanning a solidity file #[derive(Debug, thiserror::Error)] pub enum ScanFileError { #[error(transparent)] Io(#[from] FsPathError), - #[error("Failed to parse {1:?}: {0:?}")] - ParseSol(Vec, PathBuf), + #[error(transparent)] + ParseSol(#[from] FormatterError), } diff --git a/crates/forge/bin/cmd/geiger/find.rs b/crates/forge/bin/cmd/geiger/find.rs index 3ea9c0234..6629390ca 100644 --- a/crates/forge/bin/cmd/geiger/find.rs +++ b/crates/forge/bin/cmd/geiger/find.rs @@ -1,8 +1,8 @@ use super::{error::ScanFileError, visitor::CheatcodeVisitor}; use eyre::Result; -use forge_fmt::{offset_to_line_column, parse, Visitable}; +use forge_fmt::{offset_to_line_column, parse2, FormatterError, Visitable}; use foundry_common::fs; -use solang_parser::{diagnostics::Diagnostic, pt::Loc}; +use solang_parser::pt::Loc; use std::{ fmt, path::{Path, PathBuf}, @@ -12,14 +12,16 @@ 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) - .map_err(|diagnostic| ScanFileError::ParseSol(diagnostic, path.to_path_buf()))?; + 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) -> Result> { - let mut parsed = parse(src)?; +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) @@ -140,7 +142,7 @@ mod tests { } "; - let count = find_cheatcodes_in_string(s).unwrap(); + let count = find_cheatcodes_in_string(s, None).unwrap(); assert_eq!(count.ffi.len(), 1); assert!(!count.is_empty()); } @@ -156,7 +158,7 @@ mod tests { } "; - let count = find_cheatcodes_in_string(s).unwrap(); + let count = find_cheatcodes_in_string(s, None).unwrap(); assert_eq!(count.ffi.len(), 1); assert!(!count.is_empty()); } From e0785cf00fb5ce2006745a93cd9f121335be3f1d Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Fri, 7 Jun 2024 21:05:48 +0300 Subject: [PATCH 382/430] chore: add test for #2851 (#8112) chore: add test for 2851 --- crates/forge/tests/it/repros.rs | 7 +++++++ testdata/default/repros/Issue2851.t.sol | 28 +++++++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 testdata/default/repros/Issue2851.t.sol diff --git a/crates/forge/tests/it/repros.rs b/crates/forge/tests/it/repros.rs index 924a65b72..4f70a1179 100644 --- a/crates/forge/tests/it/repros.rs +++ b/crates/forge/tests/it/repros.rs @@ -332,3 +332,10 @@ test_repro!(5739); // https://github.com/foundry-rs/foundry/issues/8004 test_repro!(8004); + +// https://github.com/foundry-rs/foundry/issues/2851 +test_repro!(2851, false, None, |res| { + let mut res = res.remove("default/repros/Issue2851.t.sol:Issue2851Test").unwrap(); + let test = res.test_results.remove("invariantNotZero()").unwrap(); + assert_eq!(test.status, TestStatus::Failure); +}); diff --git a/testdata/default/repros/Issue2851.t.sol b/testdata/default/repros/Issue2851.t.sol new file mode 100644 index 000000000..f90a5f7c5 --- /dev/null +++ b/testdata/default/repros/Issue2851.t.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.1; + +import "ds-test/test.sol"; + +contract Backdoor { + uint256 public number = 1; + + function backdoor(uint256 newNumber) public payable { + uint256 x = newNumber - 1; + if (x == 6912213124124531) { + number = 0; + } + } +} + +// https://github.com/foundry-rs/foundry/issues/2851 +contract Issue2851Test is DSTest { + Backdoor back; + + function setUp() public { + back = new Backdoor(); + } + + function invariantNotZero() public { + assertEq(back.number(), 1); + } +} From 0b03a58b4a42b149a27e0b6cc1ff9559306f3603 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 7 Jun 2024 20:12:51 +0200 Subject: [PATCH 383/430] fix: bypass block gas limit if disabled (#8111) --- crates/anvil/src/eth/backend/executor.rs | 4 ++-- crates/anvil/tests/it/transaction.rs | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index 52c379da6..cccd26085 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -270,9 +270,9 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator }; let env = self.env_for(&transaction.pending_transaction); - // check that we comply with the block's gas limit + // check that we comply with the block's gas limit, if not disabled let max_gas = self.gas_used.saturating_add(env.tx.gas_limit as u128); - if max_gas > env.block.gas_limit.to::() { + if !env.cfg.disable_block_gas_limit && max_gas > env.block.gas_limit.to::() { return Some(TransactionExecutionOutcome::Exhausted(transaction)) } diff --git a/crates/anvil/tests/it/transaction.rs b/crates/anvil/tests/it/transaction.rs index c693381c6..c80f7b2b3 100644 --- a/crates/anvil/tests/it/transaction.rs +++ b/crates/anvil/tests/it/transaction.rs @@ -227,6 +227,28 @@ async fn can_reject_too_high_gas_limits() { let _ = pending.unwrap(); } +// +#[tokio::test(flavor = "multi_thread")] +async fn can_mine_large_gas_limit() { + let (api, handle) = spawn(NodeConfig::test().disable_block_gas_limit(true)).await; + let provider = handle.http_provider(); + + let accounts = handle.dev_wallets().collect::>(); + let from = accounts[0].address(); + let to = accounts[1].address(); + + let gas_limit = api.gas_limit().to::(); + let amount = handle.genesis_balance().checked_div(U256::from(3u64)).unwrap(); + + let tx = + TransactionRequest::default().to(to).value(amount).from(from).with_gas_limit(gas_limit * 3); + + // send transaction with higher gas limit + let pending = provider.send_transaction(WithOtherFields::new(tx)).await.unwrap(); + + let _resp = pending.get_receipt().await.unwrap(); +} + #[tokio::test(flavor = "multi_thread")] async fn can_reject_underpriced_replacement() { let (api, handle) = spawn(NodeConfig::test()).await; From 91d68ac38f3c9ff71daaa43ea01ee6f00887639d Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Sat, 8 Jun 2024 01:23:07 -0700 Subject: [PATCH 384/430] feat: Add block id to cast (#8074) * add blockId to cast * Update opts.rs * Update main.rs * Update main.rs * update * tests --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- crates/cast/bin/main.rs | 14 ++++++++++++-- crates/cast/bin/opts.rs | 2 ++ crates/cast/tests/cli/main.rs | 25 +++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index 4e85ef561..51c435273 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -253,10 +253,20 @@ async fn main() -> Result<()> { .await? ); } - CastSubcommand::BlockNumber { rpc } => { + CastSubcommand::BlockNumber { rpc, block } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; - println!("{}", Cast::new(provider).block_number().await?); + let number = match block { + Some(id) => provider + .get_block(id, false) + .await? + .ok_or_else(|| eyre::eyre!("block {id:?} not found"))? + .header + .number + .ok_or_else(|| eyre::eyre!("block {id:?} has no block number"))?, + None => Cast::new(provider).block_number().await?, + }; + println!("{number}"); } CastSubcommand::Chain { rpc } => { let config = Config::from(&rpc); diff --git a/crates/cast/bin/opts.rs b/crates/cast/bin/opts.rs index 5a15e1df1..4efe4245a 100644 --- a/crates/cast/bin/opts.rs +++ b/crates/cast/bin/opts.rs @@ -330,6 +330,8 @@ pub enum CastSubcommand { /// Get the latest block number. #[command(visible_alias = "bn")] BlockNumber { + /// The hash or tag to query. If not specified, the latest number is returned. + block: Option, #[command(flatten)] rpc: RpcOpts, }, diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 3c86ec4bb..01677464e 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -901,3 +901,28 @@ casttest!(index7201, |_prj, cmd| { casttest!(index7201_unknown_formula_id, |_prj, cmd| { cmd.args(["index-7201", "test", "--formula-id", "unknown"]).assert_err(); }); + +casttest!(block_number, |_prj, cmd| { + let eth_rpc_url = next_http_rpc_endpoint(); + let s = cmd.args(["block-number", "--rpc-url", eth_rpc_url.as_str()]).stdout_lossy(); + assert!(s.trim().parse::().unwrap() > 0, "{s}") +}); + +casttest!(block_number_latest, |_prj, cmd| { + let eth_rpc_url = next_http_rpc_endpoint(); + let s = cmd.args(["block-number", "--rpc-url", eth_rpc_url.as_str(), "latest"]).stdout_lossy(); + assert!(s.trim().parse::().unwrap() > 0, "{s}") +}); + +casttest!(block_number_hash, |_prj, cmd| { + let eth_rpc_url = next_http_rpc_endpoint(); + let s = cmd + .args([ + "block-number", + "--rpc-url", + eth_rpc_url.as_str(), + "0x88e96d4537bea4d9c05d12549907b32561d3bf31f45aae734cdc119f13406cb6", + ]) + .stdout_lossy(); + assert_eq!(s.trim().parse::().unwrap(), 1, "{s}") +}); From 91b12927d139bc736f65739543bd890696cbbb96 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Sat, 8 Jun 2024 18:03:05 +0300 Subject: [PATCH 385/430] fix(invariant): exclude default addresses from senders (#8118) * fix(invariant): exclude default addresses from senders * Changes after review: use array instead vec --- crates/evm/evm/src/executors/invariant/mod.rs | 12 ++++++++-- crates/forge/tests/it/invariant.rs | 19 ++++++++++++++++ .../common/InvariantExcludedSenders.t.sol | 22 +++++++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 testdata/default/fuzz/invariant/common/InvariantExcludedSenders.t.sol diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index c32293493..6ca49545c 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -8,7 +8,9 @@ use eyre::{eyre, ContextCompat, Result}; use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; use foundry_config::InvariantConfig; use foundry_evm_core::{ - constants::{CALLER, CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME}, + constants::{ + CALLER, CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME, + }, utils::get_function, }; use foundry_evm_fuzz::{ @@ -492,8 +494,14 @@ impl<'a> InvariantExecutor<'a> { ) -> Result<(SenderFilters, FuzzRunIdentifiedContracts)> { let targeted_senders = self.call_sol_default(to, &IInvariantTest::targetSendersCall {}).targetedSenders; - let excluded_senders = + let mut excluded_senders = self.call_sol_default(to, &IInvariantTest::excludeSendersCall {}).excludedSenders; + // Extend with default excluded addresses - https://github.com/foundry-rs/foundry/issues/4163 + excluded_senders.extend([ + CHEATCODE_ADDRESS, + HARDHAT_CONSOLE_ADDRESS, + DEFAULT_CREATE2_DEPLOYER, + ]); let sender_filters = SenderFilters::new(targeted_senders, excluded_senders); let selected = diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index 62e76c274..2f6fde05b 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -229,6 +229,10 @@ async fn test_invariant() { None, None, )], + ), + ( + "default/fuzz/invariant/common/InvariantExcludedSenders.t.sol:InvariantExcludedSendersTest", + vec![("invariant_check_sender()", true, None, None, None)], ) ]), ); @@ -668,3 +672,18 @@ 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 results = runner.test_collect(&filter); + assert_multiple( + &results, + BTreeMap::from([( + "default/fuzz/invariant/common/InvariantExcludedSenders.t.sol:InvariantExcludedSendersTest", + vec![("invariant_check_sender()", true, None, None, None)], + )]), + ); +} diff --git a/testdata/default/fuzz/invariant/common/InvariantExcludedSenders.t.sol b/testdata/default/fuzz/invariant/common/InvariantExcludedSenders.t.sol new file mode 100644 index 000000000..8fe0bed2c --- /dev/null +++ b/testdata/default/fuzz/invariant/common/InvariantExcludedSenders.t.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "ds-test/test.sol"; + +contract InvariantSenders { + function checkSender() external { + require(msg.sender != 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D, "sender cannot be cheatcode address"); + require(msg.sender != 0x000000000000000000636F6e736F6c652e6c6f67, "sender cannot be console address"); + require(msg.sender != 0x4e59b44847b379578588920cA78FbF26c0B4956C, "sender cannot be CREATE2 deployer"); + } +} + +contract InvariantExcludedSendersTest is DSTest { + InvariantSenders target; + + function setUp() public { + target = new InvariantSenders(); + } + + function invariant_check_sender() public view {} +} From 617931240f77e4751f04ae53e61ca855c78a5bbf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 9 Jun 2024 13:20:43 +0200 Subject: [PATCH 386/430] chore(deps): weekly `cargo update` (#8120) Updating git repository `https://github.com/bluealloy/revm.git` Updating git repository `https://github.com/alloy-rs/alloy` Updating git repository `https://github.com/paradigmxyz/revm-inspectors` Locking 37 packages to latest compatible versions Updating alloy-dyn-abi v0.7.4 -> v0.7.5 Updating alloy-json-abi v0.7.4 -> v0.7.5 Updating alloy-primitives v0.7.4 -> v0.7.5 Updating alloy-sol-macro v0.7.4 -> v0.7.5 Updating alloy-sol-macro-expander v0.7.4 -> v0.7.5 Updating alloy-sol-macro-input v0.7.4 -> v0.7.5 Updating alloy-sol-type-parser v0.7.4 -> v0.7.5 Updating alloy-sol-types v0.7.4 -> v0.7.5 Updating anstyle-query v1.0.3 -> v1.1.0 Updating aws-config v1.5.0 -> v1.5.1 Updating aws-sdk-kms v1.29.0 -> v1.30.0 Updating aws-sdk-sso v1.27.0 -> v1.29.0 Updating aws-sdk-ssooidc v1.28.0 -> v1.30.0 Updating aws-sdk-sts v1.28.0 -> v1.29.0 Updating aws-smithy-runtime v1.5.4 -> v1.5.5 Updating aws-smithy-runtime-api v1.6.1 -> v1.6.2 Updating aws-types v1.3.0 -> v1.3.1 Updating cc v1.0.98 -> v1.0.99 Updating clap v4.5.4 -> v4.5.6 Updating clap_builder v4.5.2 -> v4.5.6 Updating clap_complete v4.5.2 -> v4.5.5 Updating clap_complete_fig v4.5.0 -> v4.5.1 Updating clap_derive v4.5.4 -> v4.5.5 Updating clap_lex v0.7.0 -> v0.7.1 Updating evmole v0.3.3 -> v0.3.4 Removing heck v0.4.1 Updating hyper v0.14.28 -> v0.14.29 (latest: v1.3.1) Updating proc-macro2 v1.0.84 -> v1.0.85 Updating ruint v1.12.1 -> v1.12.3 Updating ruint-macro v1.2.0 -> v1.2.1 Updating strum_macros v0.26.3 -> v0.26.4 Updating syn-solidity v0.7.4 -> v0.7.5 Updating toml v0.8.13 -> v0.8.14 Updating toml_edit v0.22.13 -> v0.22.14 Updating unicode-width v0.1.12 -> v0.1.13 Updating utf8parse v0.2.1 -> v0.2.2 Updating webpki-roots v0.26.1 -> v0.26.2 Updating winnow v0.6.9 -> v0.6.13 note: pass `--verbose` to see 154 unchanged dependencies behind latest Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> --- Cargo.lock | 214 ++++++++++++++++++++++++++--------------------------- 1 file changed, 104 insertions(+), 110 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 05cf20bb5..5b5d98cab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -111,9 +111,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8425a283510106b1a6ad25dd4bb648ecde7da3fd2baeb9400a85ad62f51ec90b" +checksum = "efd2404399cb1b50572758e66e9b4bf088e5a3df9007be7126456c7e50af935f" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -127,7 +127,7 @@ dependencies = [ "proptest", "serde", "serde_json", - "winnow 0.6.9", + "winnow 0.6.13", ] [[package]] @@ -158,9 +158,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e30946aa6173020259055a44971f5cf40a7d76c931d209caeb51b333263df4f" +checksum = "7c3abf6446a292e19853aaca43590eeb48bf435dfd2c74200259e8f4872f6ce3" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -200,9 +200,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db8aa973e647ec336810a9356af8aea787249c9d00b1525359f3db29a68d231b" +checksum = "5277af0cbcc483ee6ad2c1e818090b5928d27f04fd6580680f31c1cf8068bcc2" dependencies = [ "alloy-rlp", "arbitrary", @@ -486,9 +486,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dbd17d67f3e89478c8a634416358e539e577899666c927bc3d2b1328ee9b6ca" +checksum = "30708a79919b082f2692423c8cc72fc250477e4a2ecb0d4a7244cd3cdb299965" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -500,14 +500,14 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6da95adcf4760bb4b108fefa51d50096c5e5fdd29ee72fed3e86ee414f2e34" +checksum = "1c7a679ac01774ab7e00a567a918d4231ae692c5c8cedaf4e16956c3116d7896" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", "const-hex", - "heck 0.4.1", + "heck", "indexmap 2.2.6", "proc-macro-error", "proc-macro2", @@ -519,14 +519,14 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32c8da04c1343871fb6ce5a489218f9c85323c8340a36e9106b5fc98d4dd59d5" +checksum = "356da0c2228aa6675a5faaa08a3e4061b967f924753983d72b9a18d9a3fad44e" dependencies = [ "alloy-json-abi", "const-hex", "dunce", - "heck 0.5.0", + "heck", "proc-macro2", "quote", "serde_json", @@ -536,18 +536,18 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368cae4dc052cad1d8f72eb2ae0c38027116933eeb49213c200a9e9875f208d7" +checksum = "81fd4783b0a5840479013e9ce960d2eb7b3be381f722e0fe3d1f7c3bb6bd4ebd" dependencies = [ - "winnow 0.6.9", + "winnow 0.6.13", ] [[package]] name = "alloy-sol-types" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a64d2d2395c1ac636b62419a7b17ec39031d6b2367e66e9acbf566e6055e9c" +checksum = "6eb5e6234c0b62514992589fe1578f64d418dbc8ef5cd1ab2d7f2f568f599698" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -706,9 +706,9 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ "windows-sys 0.52.0", ] @@ -1128,9 +1128,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "aws-config" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1234b742ac4a40a7d3459c6e3c99818271976a5a6ae3732cb415f4a9a94da7b6" +checksum = "2ac9889352d632214df943e26740c46a0f3da6e329fbd28164fe7ae1b061da7b" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1148,7 +1148,7 @@ dependencies = [ "fastrand", "hex", "http 0.2.12", - "hyper 0.14.28", + "hyper 0.14.29", "ring", "time", "tokio", @@ -1194,9 +1194,9 @@ dependencies = [ [[package]] name = "aws-sdk-kms" -version = "1.29.0" +version = "1.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d5f3e0faac32aa4edbcdcc10330e7b6b1fdbb2e83b44439f5696281e18d278" +checksum = "da951fb0dd1a02728a91c58af8464098766f1a0600af932dd3f8361b23e1bfc9" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1216,9 +1216,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.27.0" +version = "1.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aef53f254e16c00cfbfd69fa6eca4d858b7c161878db2cd248410af402d1951e" +checksum = "da75cf91cbb46686a27436d639a720a3a198b148efa76dc2467b7e5374a67fc0" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1238,9 +1238,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.28.0" +version = "1.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1b673b2972c38463955e27d76c9d2ebb0452a9ce8059a0e2c9ed67efe69ef35" +checksum = "cf2ec8a6687299685ed0a4a3137c129cdb132b5235bc3aa3443f6cffe468b9ff" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1260,9 +1260,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.28.0" +version = "1.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a422d2f3080421ed23630ada0e474c76e4279c18b4a379bff2f1062e05cef466" +checksum = "458f1031e094b1411b59b49b19e4118f069e1fe13a9c5b8888e933daaf7ffdd6" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1356,9 +1356,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.5.4" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "607e8b53aeb2bc23fb332159d72a69650cd9643c161d76cd3b7f88ac00b5a1bb" +checksum = "d0d3965f6417a92a6d1009c5958a67042f57e46342afb37ca58f9ad26744ec73" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -1370,7 +1370,7 @@ dependencies = [ "http 0.2.12", "http-body 0.4.6", "http-body 1.0.0", - "hyper 0.14.28", + "hyper 0.14.29", "hyper-rustls 0.24.2", "once_cell", "pin-project-lite", @@ -1382,9 +1382,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime-api" -version = "1.6.1" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b7d790d553d163c7d80a4e06e2906bf24b9172c9ebe045fc3a274e9358ab7bb" +checksum = "4179bd8a1c943e1aceb46c5b9fc014a561bd6c35a2153e816ba29076ee49d245" dependencies = [ "aws-smithy-async", "aws-smithy-types", @@ -1431,9 +1431,9 @@ dependencies = [ [[package]] name = "aws-types" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fa328e19c849b20ef7ada4c9b581dd12351ff35ecc7642d06e69de4f98407c" +checksum = "6f734808d43702a67e57d478a12e227d4d038d0b90c9005a78c87890d3805922" dependencies = [ "aws-credential-types", "aws-smithy-async", @@ -1457,7 +1457,7 @@ dependencies = [ "futures-util", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.28", + "hyper 0.14.29", "itoa", "matchit", "memchr", @@ -1911,9 +1911,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" dependencies = [ "jobserver", "libc", @@ -2023,9 +2023,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.4" +version = "4.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "a9689a29b593160de5bc4aacab7b5d54fb52231de70122626c178e6a368994c7" dependencies = [ "clap_builder", "clap_derive", @@ -2033,9 +2033,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "2e5387378c84f6faa26890ebf9f0a92989f8873d4d380467bcd0d8d8620424df" dependencies = [ "anstream", "anstyle", @@ -2048,18 +2048,18 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.2" +version = "4.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd79504325bf38b10165b02e89b4347300f855f273c4cb30c4a3209e6583275e" +checksum = "d2020fa13af48afc65a9a87335bda648309ab3d154cd03c7ff95b378c7ed39c4" dependencies = [ "clap", ] [[package]] name = "clap_complete_fig" -version = "4.5.0" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b3e65f91fabdd23cac3d57d39d5d938b4daabd070c335c006dccb866a61110" +checksum = "fb4bc503cddc1cd320736fb555d6598309ad07c2ddeaa23891a10ffb759ee612" dependencies = [ "clap", "clap_complete", @@ -2067,11 +2067,11 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", "syn 2.0.66", @@ -2079,9 +2079,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "clearscreen" @@ -3026,7 +3026,7 @@ dependencies = [ "serde", "serde_json", "syn 2.0.66", - "toml 0.8.13", + "toml 0.8.14", "walkdir", ] @@ -3078,9 +3078,9 @@ dependencies = [ [[package]] name = "evmole" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd4e05af4c306bcba507bd358feac33ec73f4314a89bd93758d035c629f2f5fe" +checksum = "5bdec28a767d874dd74270c882fb0aa31730af9138d41810fc63b8e7e49f6ddd" dependencies = [ "ruint", ] @@ -3165,7 +3165,7 @@ dependencies = [ "pear", "serde", "tempfile", - "toml 0.8.13", + "toml 0.8.14", "uncased", "version_check", ] @@ -3306,8 +3306,8 @@ dependencies = [ "thiserror", "tikv-jemallocator", "tokio", - "toml 0.8.13", - "toml_edit 0.22.13", + "toml 0.8.14", + "toml_edit 0.22.14", "tower-http", "tracing", "tracing-subscriber", @@ -3336,7 +3336,7 @@ dependencies = [ "serde_json", "solang-parser", "thiserror", - "toml 0.8.13", + "toml 0.8.14", "tracing", ] @@ -3351,7 +3351,7 @@ dependencies = [ "similar-asserts", "solang-parser", "thiserror", - "toml 0.8.13", + "toml 0.8.14", "tracing", "tracing-subscriber", ] @@ -3494,7 +3494,7 @@ dependencies = [ "semver 1.0.23", "serde_json", "thiserror", - "toml 0.8.13", + "toml 0.8.14", "tracing", "walkdir", ] @@ -3632,7 +3632,7 @@ dependencies = [ "tokio", "tracing", "walkdir", - "winnow 0.6.9", + "winnow 0.6.13", "yansi", ] @@ -3665,8 +3665,8 @@ dependencies = [ "solang-parser", "tempfile", "thiserror", - "toml 0.8.13", - "toml_edit 0.22.13", + "toml 0.8.14", + "toml_edit 0.22.14", "tracing", "walkdir", ] @@ -4043,7 +4043,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47d460327b24cc34c86d53d60a90e9e6044817f7906ebd9baa5c3d0ee13e1ecf" dependencies = [ "bytes", - "hyper 0.14.28", + "hyper 0.14.29", "serde", "serde_json", "thiserror", @@ -4061,7 +4061,7 @@ dependencies = [ "chrono", "futures", "gcemeta", - "hyper 0.14.28", + "hyper 0.14.29", "jsonwebtoken", "once_cell", "prost", @@ -4417,12 +4417,6 @@ dependencies = [ "serde", ] -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "heck" version = "0.5.0" @@ -4586,9 +4580,9 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.28" +version = "0.14.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" dependencies = [ "bytes", "futures-channel", @@ -4636,7 +4630,7 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http 0.2.12", - "hyper 0.14.28", + "hyper 0.14.29", "log", "rustls 0.21.12", "rustls-native-certs 0.6.3", @@ -4667,7 +4661,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ - "hyper 0.14.28", + "hyper 0.14.29", "pin-project-lite", "tokio", "tokio-io-timeout", @@ -6305,9 +6299,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.84" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ "unicode-ident", ] @@ -6656,7 +6650,7 @@ dependencies = [ "h2", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.28", + "hyper 0.14.29", "hyper-rustls 0.24.2", "ipnet", "js-sys", @@ -6731,7 +6725,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 0.26.1", + "webpki-roots 0.26.2", "winreg 0.52.0", ] @@ -6891,9 +6885,9 @@ dependencies = [ [[package]] name = "ruint" -version = "1.12.1" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f308135fef9fc398342da5472ce7c484529df23743fb7c734e0f3d472971e62" +checksum = "2c3cc4c2511671f327125da14133d0c5c5d137f006a1017a16f557bc85b16286" dependencies = [ "alloy-rlp", "arbitrary", @@ -6916,9 +6910,9 @@ dependencies = [ [[package]] name = "ruint-macro" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f86854cf50259291520509879a5c294c3c9a4c334e9ff65071c51e42ef1e2343" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" [[package]] name = "rusb" @@ -7697,8 +7691,8 @@ dependencies = [ "sha256", "simple-home-dir", "tokio", - "toml 0.8.13", - "toml_edit 0.22.13", + "toml 0.8.14", + "toml_edit 0.22.14", "uuid 1.8.0", "walkdir", "yansi", @@ -7799,11 +7793,11 @@ dependencies = [ [[package]] name = "strum_macros" -version = "0.26.3" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7993a8e3a9e88a00351486baae9522c91b123a088f76469e5bd5cc17198ea87" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", "rustversion", @@ -7886,9 +7880,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8db114c44cf843a8bacd37a146e37987a0b823a0e8bc4fdc610c9c72ab397a5" +checksum = "e6fe08d08d84f2c0a77f1e7c46518789d745c2e87a2721791ed7c3c9bc78df28" dependencies = [ "paste", "proc-macro2", @@ -8258,15 +8252,15 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.13" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" dependencies = [ "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.13", + "toml_edit 0.22.14", ] [[package]] @@ -8291,15 +8285,15 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.13" +version = "0.22.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" +checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" dependencies = [ "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.9", + "winnow 0.6.13", ] [[package]] @@ -8316,7 +8310,7 @@ dependencies = [ "h2", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.28", + "hyper 0.14.29", "hyper-timeout", "percent-encoding", "pin-project 1.1.5", @@ -8633,9 +8627,9 @@ dependencies = [ [[package]] name = "unicode-width" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "unicode-xid" @@ -8674,9 +8668,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" @@ -8913,9 +8907,9 @@ checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "webpki-roots" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" +checksum = "3c452ad30530b54a4d8e71952716a212b08efd0f3562baa66c29a618b07da7c3" dependencies = [ "rustls-pki-types", ] @@ -9203,9 +9197,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.9" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86c949fede1d13936a99f14fafd3e76fd642b556dd2ce96287fbe2e0151bfac6" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" dependencies = [ "memchr", ] From db60cb31925dea531e82516d9a4a3d8ccb42f8be Mon Sep 17 00:00:00 2001 From: poma Date: Mon, 10 Jun 2024 20:01:16 +0300 Subject: [PATCH 387/430] Support WalletOpts in `cast wallet derive-private-key` command (#8119) * Support WalletOpts in `cast wallet derive-private-key` command * rename cast wallet `derive-private-key` to `private-key` * fix formatting * Add aliases * verbose flag * tests * Make output format more consistent with other subcommands * hide legacy aliases * derivation path --------- Co-authored-by: poma --- crates/cast/bin/cmd/wallet/mod.rs | 70 ++++++++++++++++++++++++------- crates/cast/tests/cli/main.rs | 40 ++++++++++++++++++ 2 files changed, 96 insertions(+), 14 deletions(-) diff --git a/crates/cast/bin/cmd/wallet/mod.rs b/crates/cast/bin/cmd/wallet/mod.rs index def14c17e..a22d6a6ec 100644 --- a/crates/cast/bin/cmd/wallet/mod.rs +++ b/crates/cast/bin/cmd/wallet/mod.rs @@ -149,8 +149,24 @@ pub enum WalletSubcommands { List(ListArgs), /// Derives private key from mnemonic - #[command(name = "derive-private-key", visible_aliases = &["--derive-private-key"])] - DerivePrivateKey { mnemonic: String, mnemonic_index: Option }, + #[command(name = "private-key", visible_alias = "pk", aliases = &["derive-private-key", "--derive-private-key"])] + PrivateKey { + /// If provided, the private key will be derived from the specified menomonic phrase. + #[arg(value_name = "MNEMONIC")] + mnemonic_override: Option, + + /// If provided, the private key will be derived using the + /// specified mnemonic index (if integer) or derivation path. + #[arg(value_name = "MNEMONIC_INDEX_OR_DERIVATION_PATH")] + mnemonic_index_or_derivation_path_override: Option, + + /// Verbose mode, print the address and private key. + #[arg(short = 'v', long)] + verbose: bool, + + #[command(flatten)] + wallet: WalletOpts, + }, /// Decrypt a keystore file to get the private key #[command(name = "decrypt-keystore", visible_alias = "dk")] @@ -363,18 +379,44 @@ flag to set your key via: Self::List(cmd) => { cmd.run().await?; } - Self::DerivePrivateKey { mnemonic, mnemonic_index } => { - let phrase = Mnemonic::::new_from_phrase(mnemonic.as_str())?.to_phrase(); - let builder = MnemonicBuilder::::default().phrase(phrase.as_str()); - let derivation_path = "m/44'/60'/0'/0/"; - let index = mnemonic_index.unwrap_or_default(); - let wallet = builder - .clone() - .derivation_path(format!("{derivation_path}{index}"))? - .build()?; - println!("- Account:"); - println!("Address: {}", wallet.address()); - println!("Private key: 0x{}\n", hex::encode(wallet.signer().to_bytes())); + Self::PrivateKey { + wallet, + mnemonic_override, + mnemonic_index_or_derivation_path_override, + verbose, + } => { + let (index_override, derivation_path_override) = + match mnemonic_index_or_derivation_path_override { + Some(value) => match value.parse::() { + Ok(index) => (Some(index), None), + Err(_) => (None, Some(value)), + }, + None => (None, None), + }; + let wallet = WalletOpts { + raw: RawWalletOpts { + mnemonic: mnemonic_override.or(wallet.raw.mnemonic), + mnemonic_index: index_override.unwrap_or(wallet.raw.mnemonic_index), + hd_path: derivation_path_override.or(wallet.raw.hd_path), + ..wallet.raw + }, + ..wallet + } + .signer() + .await?; + match wallet { + WalletSigner::Local(wallet) => { + if verbose { + println!("Address: {}", wallet.address()); + println!("Private key: 0x{}", hex::encode(wallet.signer().to_bytes())); + } else { + println!("0x{}", hex::encode(wallet.signer().to_bytes())); + } + } + _ => { + eyre::bail!("Only local wallets are supported by this command."); + } + } } Self::DecryptKeystore { account_name, keystore_dir, unsafe_password } => { // Set up keystore directory diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 01677464e..5a6be401b 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -163,6 +163,46 @@ casttest!(wallet_list_local_accounts, |prj, cmd| { assert_eq!(list_output.matches('\n').count(), 10); }); +// tests that `cast wallet private-key` with arguments outputs the private key +casttest!(wallet_private_key_from_mnemonic_arg, |_prj, cmd| { + cmd.args([ + "wallet", + "private-key", + "test test test test test test test test test test test junk", + "1", + ]); + let output = cmd.stdout_lossy(); + assert_eq!(output.trim(), "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"); +}); + +// tests that `cast wallet private-key` with options outputs the private key +casttest!(wallet_private_key_from_mnemonic_option, |_prj, cmd| { + cmd.args([ + "wallet", + "private-key", + "--mnemonic", + "test test test test test test test test test test test junk", + "--mnemonic-index", + "1", + ]); + let output = cmd.stdout_lossy(); + assert_eq!(output.trim(), "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"); +}); + +// tests that `cast wallet private-key` with derivation path outputs the private key +casttest!(wallet_private_key_with_derivation_path, |_prj, cmd| { + cmd.args([ + "wallet", + "private-key", + "--mnemonic", + "test test test test test test test test test test test junk", + "--mnemonic-derivation-path", + "m/44'/60'/0'/0/1", + ]); + let output = cmd.stdout_lossy(); + assert_eq!(output.trim(), "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"); +}); + // tests that `cast wallet import` creates a keystore for a private key and that `cast wallet // decrypt-keystore` can access it casttest!(wallet_import_and_decrypt, |prj, cmd| { From 81896c5ca7075124b083ce97699cb1687910a757 Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Mon, 10 Jun 2024 11:04:59 -0700 Subject: [PATCH 388/430] Feat: add solc & evm version to anvil readme (#7945) * Update README.md * add forge --- crates/anvil/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/crates/anvil/README.md b/crates/anvil/README.md index cb62354fb..0e775441d 100644 --- a/crates/anvil/README.md +++ b/crates/anvil/README.md @@ -11,6 +11,15 @@ A local Ethereum node, akin to Ganache, designed for development with [**Forge** - mining modes: auto, interval, manual, none - ... +## Supported Versions + +- **anvil**: + - **evm**: Cancun +- **forge**: + - **solc**: Latest + - **evm**: Cancun + + ## Installation `anvil` binary is available via [`foundryup`](../../README.md#installation). From edcb8ad30f5cedd19533326edbb8579013860bc4 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 11 Jun 2024 12:23:11 +0200 Subject: [PATCH 389/430] feat: add too many warnings error variant (#8125) * feat: add too many warnings error variant * docs: add to readme --- crates/config/README.md | 2 +- crates/config/src/error.rs | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/config/README.md b/crates/config/README.md index 25100a5df..482ec29ac 100644 --- a/crates/config/README.md +++ b/crates/config/README.md @@ -103,7 +103,7 @@ eth_rpc_url = "https://example.com/" # Setting this option enables decoding of error traces from mainnet deployed / verfied contracts via etherscan etherscan_api_key = "YOURETHERSCANAPIKEY" # ignore solc warnings for missing license and exceeded contract size -# known error codes are: ["unreachable", "unused-return", "unused-param", "unused-var", "code-size", "shadowing", "func-mutability", "license", "pragma-solidity", "virtual-interfaces", "same-varname"] +# known error codes are: ["unreachable", "unused-return", "unused-param", "unused-var", "code-size", "shadowing", "func-mutability", "license", "pragma-solidity", "virtual-interfaces", "same-varname", "too-many-warnings"] # additional warnings can be added using their numeric error code: ["license", 1337] ignored_error_codes = ["license", "code-size"] ignored_warnings_from = ["path_to_ignore"] diff --git a/crates/config/src/error.rs b/crates/config/src/error.rs index 0f6dae9bf..016e32c47 100644 --- a/crates/config/src/error.rs +++ b/crates/config/src/error.rs @@ -136,6 +136,8 @@ pub enum SolidityErrorCode { PragmaSolidity, /// Uses transient opcodes TransientStorageUsed, + /// There are more than 256 warnings. Ignoring the rest. + TooManyWarnings, /// All other error codes Other(u64), } @@ -162,6 +164,7 @@ impl SolidityErrorCode { SolidityErrorCode::Unreachable => "unreachable", SolidityErrorCode::PragmaSolidity => "pragma-solidity", SolidityErrorCode::TransientStorageUsed => "transient-storage", + SolidityErrorCode::TooManyWarnings => "too-many-warnings", SolidityErrorCode::Other(code) => return Err(*code), }; Ok(s) @@ -187,6 +190,7 @@ impl From for u64 { SolidityErrorCode::Unreachable => 5740, SolidityErrorCode::PragmaSolidity => 3420, SolidityErrorCode::TransientStorageUsed => 2394, + SolidityErrorCode::TooManyWarnings => 4591, SolidityErrorCode::Other(code) => code, } } @@ -222,6 +226,7 @@ impl FromStr for SolidityErrorCode { "unreachable" => SolidityErrorCode::Unreachable, "pragma-solidity" => SolidityErrorCode::PragmaSolidity, "transient-storage" => SolidityErrorCode::TransientStorageUsed, + "too-many-warnings" => SolidityErrorCode::TooManyWarnings, _ => return Err(format!("Unknown variant {s}")), }; From 29e51120ac6e77a62242364a22108edea7274a2a Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 11 Jun 2024 14:25:15 +0300 Subject: [PATCH 390/430] fix(forge): preserve state of persisted accounts on rollFork(tx) / transact (#8129) * fix(forge): preserve state of persisted accounts on rollFork to tx / transact * Changes after review: cleaner way to check if account persistent --- crates/evm/core/src/backend/mod.rs | 36 +++++++++++++++----- crates/evm/evm/src/inspectors/stack.rs | 4 +-- crates/forge/tests/it/repros.rs | 3 ++ testdata/default/repros/Issue8006.t.sol | 44 +++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 10 deletions(-) create mode 100644 testdata/default/repros/Issue8006.t.sol diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index addfccb85..b96829196 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -900,6 +900,7 @@ impl Backend { ) -> eyre::Result> { trace!(?id, ?tx_hash, "replay until transaction"); + let persistent_accounts = self.inner.persistent_accounts.clone(); let fork_id = self.ensure_fork_id(id)?.clone(); let env = self.env_with_handler_cfg(env); @@ -929,6 +930,7 @@ impl Backend { journaled_state, fork, &fork_id, + &persistent_accounts, &mut NoOpInspector, )?; } @@ -1248,6 +1250,7 @@ impl DatabaseExt for Backend { inspector: &mut I, ) -> eyre::Result<()> { trace!(?maybe_id, ?transaction, "execute transaction"); + let persistent_accounts = self.inner.persistent_accounts.clone(); let id = self.ensure_fork(maybe_id)?; let fork_id = self.ensure_fork_id(id).cloned()?; @@ -1265,7 +1268,15 @@ impl DatabaseExt for Backend { let env = self.env_with_handler_cfg(env); let fork = self.inner.get_fork_by_id_mut(id)?; - commit_transaction(tx, env, journaled_state, fork, &fork_id, inspector) + commit_transaction( + tx, + env, + journaled_state, + fork, + &fork_id, + &persistent_accounts, + inspector, + ) } fn active_fork_id(&self) -> Option { @@ -1879,6 +1890,7 @@ fn commit_transaction>( journaled_state: &mut JournaledState, fork: &mut Fork, fork_id: &ForkId, + persistent_accounts: &HashSet
, inspector: I, ) -> eyre::Result<()> { configure_tx_env(&mut env.env, &tx); @@ -1894,16 +1906,23 @@ fn commit_transaction>( }; trace!(elapsed = ?now.elapsed(), "transacted transaction"); - apply_state_changeset(res.state, journaled_state, fork)?; + apply_state_changeset(res.state, journaled_state, fork, persistent_accounts)?; Ok(()) } /// Helper method which updates data in the state with the data from the database. -pub fn update_state(state: &mut EvmState, db: &mut DB) -> Result<(), DB::Error> { +/// Does not change state for persistent accounts (for roll fork to transaction and transact). +pub fn update_state( + state: &mut EvmState, + db: &mut DB, + persistent_accounts: Option<&HashSet
>, +) -> Result<(), DB::Error> { for (addr, acc) in state.iter_mut() { - acc.info = db.basic(*addr)?.unwrap_or_default(); - for (key, val) in acc.storage.iter_mut() { - val.present_value = db.storage(*addr, *key)?; + if !persistent_accounts.map_or(false, |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)?; + } } } @@ -1916,12 +1935,13 @@ fn apply_state_changeset( state: Map, journaled_state: &mut JournaledState, fork: &mut Fork, + persistent_accounts: &HashSet
, ) -> Result<(), DatabaseError> { // commit the state and update the loaded accounts fork.db.commit(state); - update_state(&mut journaled_state.state, &mut fork.db)?; - update_state(&mut fork.journaled_state.state, &mut fork.db)?; + update_state(&mut journaled_state.state, &mut fork.db, Some(persistent_accounts))?; + update_state(&mut fork.journaled_state.state, &mut fork.db, Some(persistent_accounts))?; Ok(()) } diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 1eb21d6de..52949e8e1 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -515,7 +515,7 @@ impl InspectorStack { ecx.db.commit(res.state.clone()); // Update both states with new DB data after commit. - if let Err(e) = update_state(&mut ecx.journaled_state.state, &mut ecx.db) { + if let Err(e) = update_state(&mut ecx.journaled_state.state, &mut ecx.db, None) { let res = InterpreterResult { result: InstructionResult::Revert, output: Bytes::from(e.to_string()), @@ -523,7 +523,7 @@ impl InspectorStack { }; return (res, None) } - if let Err(e) = update_state(&mut res.state, &mut ecx.db) { + if let Err(e) = update_state(&mut res.state, &mut ecx.db, None) { let res = InterpreterResult { result: InstructionResult::Revert, output: Bytes::from(e.to_string()), diff --git a/crates/forge/tests/it/repros.rs b/crates/forge/tests/it/repros.rs index 4f70a1179..9150da5f0 100644 --- a/crates/forge/tests/it/repros.rs +++ b/crates/forge/tests/it/repros.rs @@ -339,3 +339,6 @@ test_repro!(2851, false, None, |res| { let test = res.test_results.remove("invariantNotZero()").unwrap(); assert_eq!(test.status, TestStatus::Failure); }); + +// https://github.com/foundry-rs/foundry/issues/8006 +test_repro!(8006); diff --git a/testdata/default/repros/Issue8006.t.sol b/testdata/default/repros/Issue8006.t.sol new file mode 100644 index 000000000..1e2023bf1 --- /dev/null +++ b/testdata/default/repros/Issue8006.t.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "cheats/Vm.sol"; + +interface IERC20 { + function totalSupply() external view returns (uint256 supply); +} + +contract Mock { + function totalSupply() external view returns (uint256 supply) { + return 1; + } +} + +// https://github.com/foundry-rs/foundry/issues/8006 +contract Issue5739Test is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + IERC20 dai; + bytes32 transaction = 0x67cbad73764049e228495a3f90144aab4a37cb4b5fd697dffc234aa5ed811ace; + + function setUp() public { + vm.createSelectFork("rpcAlias", 16261704); + dai = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); + } + + function testRollForkEtchNotCalled() public { + // dai not persistent so should not call mock code + vm.etch(address(dai), address(new Mock()).code); + assertEq(dai.totalSupply(), 1); + vm.rollFork(transaction); + assertEq(dai.totalSupply(), 5155217627191887307044676292); + } + + function testRollForkEtchCalled() public { + // make dai persistent so mock code is preserved + vm.etch(address(dai), address(new Mock()).code); + vm.makePersistent(address(dai)); + assertEq(dai.totalSupply(), 1); + vm.rollFork(transaction); + assertEq(dai.totalSupply(), 1); + } +} From 0636c5dc935ba69557261ed5eec936c9d2d856dc Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 11 Jun 2024 21:28:00 +0300 Subject: [PATCH 391/430] chore: add roll fork invariant test with handler state (#8130) --- crates/forge/tests/it/invariant.rs | 45 ++++++++++++++----- .../invariant/common/InvariantRollFork.t.sol | 23 +++++++++- testdata/default/repros/Issue8006.t.sol | 2 +- 3 files changed, 57 insertions(+), 13 deletions(-) diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index 2f6fde05b..fbd906d0c 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -221,7 +221,7 @@ async fn test_invariant() { )], ), ( - "default/fuzz/invariant/common/InvariantRollFork.t.sol:InvariantRollForkTest", + "default/fuzz/invariant/common/InvariantRollFork.t.sol:InvariantRollForkBlockTest", vec![( "invariant_fork_handler_block()", false, @@ -230,6 +230,16 @@ async fn test_invariant() { None, )], ), + ( + "default/fuzz/invariant/common/InvariantRollFork.t.sol:InvariantRollForkStateTest", + vec![( + "invariant_fork_handler_state()", + false, + Some("revert: wrong supply".into()), + None, + None, + )], + ), ( "default/fuzz/invariant/common/InvariantExcludedSenders.t.sol:InvariantExcludedSendersTest", vec![("invariant_check_sender()", true, None, None, None)], @@ -658,18 +668,31 @@ async fn test_invariant_roll_fork_handler() { Some(tempfile::tempdir().unwrap().into_path()); let results = runner.test_collect(&filter); + assert_multiple( &results, - BTreeMap::from([( - "default/fuzz/invariant/common/InvariantRollFork.t.sol:InvariantRollForkTest", - vec![( - "invariant_fork_handler_block()", - false, - Some("revert: too many blocks mined".into()), - None, - None, - )], - )]), + BTreeMap::from([ + ( + "default/fuzz/invariant/common/InvariantRollFork.t.sol:InvariantRollForkBlockTest", + vec![( + "invariant_fork_handler_block()", + false, + Some("revert: too many blocks mined".into()), + None, + None, + )], + ), + ( + "default/fuzz/invariant/common/InvariantRollFork.t.sol:InvariantRollForkStateTest", + vec![( + "invariant_fork_handler_state()", + false, + Some("revert: wrong supply".into()), + None, + None, + )], + ), + ]), ); } diff --git a/testdata/default/fuzz/invariant/common/InvariantRollFork.t.sol b/testdata/default/fuzz/invariant/common/InvariantRollFork.t.sol index 7934a2916..d74509cd4 100644 --- a/testdata/default/fuzz/invariant/common/InvariantRollFork.t.sol +++ b/testdata/default/fuzz/invariant/common/InvariantRollFork.t.sol @@ -4,15 +4,21 @@ pragma solidity ^0.8.13; import "ds-test/test.sol"; import "cheats/Vm.sol"; +interface IERC20 { + function totalSupply() external view returns (uint256 supply); +} + contract RollForkHandler is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); + uint256 public totalSupply; function work() external { vm.rollFork(block.number + 1); + totalSupply = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F).totalSupply(); } } -contract InvariantRollForkTest is DSTest { +contract InvariantRollForkBlockTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); RollForkHandler forkHandler; @@ -27,3 +33,18 @@ contract InvariantRollForkTest is DSTest { require(block.number < 19812634, "too many blocks mined"); } } + +contract InvariantRollForkStateTest is DSTest { + Vm constant vm = Vm(HEVM_ADDRESS); + RollForkHandler forkHandler; + + function setUp() public { + vm.createSelectFork("rpcAlias", 19812632); + forkHandler = new RollForkHandler(); + } + + /// forge-config: default.invariant.runs = 1 + function invariant_fork_handler_state() public { + require(forkHandler.totalSupply() < 3254378807384273078310283461, "wrong supply"); + } +} diff --git a/testdata/default/repros/Issue8006.t.sol b/testdata/default/repros/Issue8006.t.sol index 1e2023bf1..1a45ddf97 100644 --- a/testdata/default/repros/Issue8006.t.sol +++ b/testdata/default/repros/Issue8006.t.sol @@ -15,7 +15,7 @@ contract Mock { } // https://github.com/foundry-rs/foundry/issues/8006 -contract Issue5739Test is DSTest { +contract Issue8006Test is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); IERC20 dai; bytes32 transaction = 0x67cbad73764049e228495a3f90144aab4a37cb4b5fd697dffc234aa5ed811ace; From 1ac4de029853655ec4a8e27fa0ca73132b273f73 Mon Sep 17 00:00:00 2001 From: Swanny Date: Tue, 11 Jun 2024 14:33:18 -0400 Subject: [PATCH 392/430] fix(forge): needs a aws-kms feature to allow for continued support of the --aws flag (#8131) --- crates/forge/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index dcd40a4aa..28d679262 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -135,6 +135,7 @@ rustls = [ openssl = ["foundry-cli/openssl", "reqwest/default-tls"] asm-keccak = ["alloy-primitives/asm-keccak"] jemalloc = ["dep:tikv-jemallocator"] +aws-kms = ["foundry-wallets/aws-kms"] [[bench]] name = "test" From 462b2ac6c038dc24b8f38b0c59b664d0740604c2 Mon Sep 17 00:00:00 2001 From: Swanny Date: Tue, 11 Jun 2024 14:33:36 -0400 Subject: [PATCH 393/430] fix(release): build forge and cast with aws support on release (#8132) * fix(release): build forge and cast with aws support on release * fix(release): add support for aws-kms features for docker and releases --- .github/workflows/release.yml | 4 +++- Dockerfile | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6c78fed2a..c993c0d8d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -137,7 +137,9 @@ jobs: # `jemalloc` and `keccak-asm` are not supported on MSVC or aarch64 Linux. if [[ "$target" != *msvc* && "$target" != "aarch64-unknown-linux-gnu" ]]; then - flags+=(--features asm-keccak,jemalloc) + flags+=(--features asm-keccak,jemalloc,cast/aws-kms,forge/aws-kms) + else + flags+=(--features cast/aws-kms,forge/aws-kms) fi [[ "$target" == *windows* ]] && exe=".exe" diff --git a/Dockerfile b/Dockerfile index e900ee1e8..aba0b3986 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,7 +19,7 @@ COPY . . RUN git update-index --force-write-index RUN --mount=type=cache,target=/root/.cargo/registry --mount=type=cache,target=/root/.cargo/git --mount=type=cache,target=/opt/foundry/target \ - source $HOME/.profile && cargo build --release \ + source $HOME/.profile && cargo build --release --features cast/aws-kms,forge/aws-kms \ && mkdir out \ && mv target/release/forge out/forge \ && mv target/release/cast out/cast \ From bc545937f543d153920bbe87d999c54f623d2f8e Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 12 Jun 2024 08:23:25 +0300 Subject: [PATCH 394/430] bump compilers (#8126) --- Cargo.lock | 8 +- Cargo.toml | 4 +- crates/common/src/compile.rs | 184 ++++++++++-------- crates/debugger/src/tui/draw.rs | 31 ++- crates/debugger/src/tui/mod.rs | 8 +- crates/evm/traces/src/identifier/etherscan.rs | 15 +- crates/forge/bin/cmd/bind.rs | 2 +- crates/forge/bin/cmd/test/mod.rs | 3 +- crates/forge/src/multi_runner.rs | 8 +- crates/forge/tests/cli/build.rs | 20 +- .../forge/tests/fixtures/compile_json.stdout | 9 +- crates/script/src/build.rs | 3 +- crates/test-utils/src/util.rs | 2 +- crates/verify/src/etherscan/flatten.rs | 5 +- 14 files changed, 165 insertions(+), 137 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5b5d98cab..91b6f3c50 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3444,9 +3444,9 @@ dependencies = [ [[package]] name = "foundry-block-explorers" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673c42208fee48238ef6833cf55295c9e9b5546383caf426da72d849fb43dc24" +checksum = "35344cf275788b0450c4b36d452b812d1122d622bafb29887ce244b8099a86ad" dependencies = [ "alloy-chains", "alloy-json-abi", @@ -3597,9 +3597,9 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a92aa3e4d0aa91fda44c1840c68d634fc126bdd06099516eb2b81035e5e6d0" +checksum = "81f9b10619d07d765a0336b1990ffcb1bb7b806a59b4d2e65cb78f5d77f373c5" dependencies = [ "alloy-json-abi", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index 2418bdf08..65532c074 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -152,8 +152,8 @@ foundry-wallets = { path = "crates/wallets" } foundry-linking = { path = "crates/linking" } # solc & compilation utilities -foundry-block-explorers = { version = "0.3.0", default-features = false } -foundry-compilers = { version = "0.6.2", default-features = false } +foundry-block-explorers = { version = "0.4.0", default-features = false } +foundry-compilers = { version = "0.7.0", default-features = false } ## revm # no default features to avoid c-kzg diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index 4ab4b170d..d26b362a9 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -6,11 +6,10 @@ use eyre::{Context, Result}; use foundry_block_explorers::contract::Metadata; use foundry_compilers::{ artifacts::{BytecodeObject, ContractBytecodeSome, Libraries, Source}, - compilers::{solc::SolcCompiler, CompilationError, Compiler}, + compilers::{multi::MultiCompilerLanguage, solc::SolcCompiler, Compiler}, remappings::Remapping, report::{BasicStdoutReporter, NoReporter, Report}, - Artifact, ArtifactId, Project, ProjectBuilder, ProjectCompileOutput, ProjectPathsConfig, Solc, - SolcConfig, + Artifact, Project, ProjectBuilder, ProjectCompileOutput, ProjectPathsConfig, Solc, SolcConfig, }; use foundry_linking::Linker; use num_format::{Locale, ToFormattedString}; @@ -20,6 +19,7 @@ use std::{ fmt::Display, io::IsTerminal, path::{Path, PathBuf}, + sync::Arc, time::Instant, }; @@ -122,10 +122,7 @@ impl ProjectCompiler { } /// Compiles the project. - pub fn compile( - mut self, - project: &Project, - ) -> Result> { + pub fn compile(mut self, project: &Project) -> Result> { // TODO: Avoid process::exit if !project.paths.has_input_files() && self.files.is_empty() { println!("Nothing to compile"); @@ -159,9 +156,9 @@ impl ProjectCompiler { /// ProjectCompiler::new().compile_with(|| Ok(prj.compile()?)).unwrap(); /// ``` #[instrument(target = "forge::compile", skip_all)] - fn compile_with(self, f: F) -> Result> + fn compile_with(self, f: F) -> Result> where - F: FnOnce() -> Result>, + F: FnOnce() -> Result>, { let quiet = self.quiet.unwrap_or(false); let bail = self.bail.unwrap_or(true); @@ -209,7 +206,7 @@ impl ProjectCompiler { } /// If configured, this will print sizes or names - fn handle_output(&self, output: &ProjectCompileOutput) { + fn handle_output(&self, output: &ProjectCompileOutput) { let print_names = self.print_names.unwrap_or(false); let print_sizes = self.print_sizes.unwrap_or(false); @@ -274,90 +271,121 @@ impl ProjectCompiler { } } -/// Contract source code and bytecode. +#[derive(Clone, Debug)] +pub struct SourceData { + pub source: Arc, + pub language: MultiCompilerLanguage, +} + +#[derive(Clone, Debug)] +pub struct ArtifactData { + pub bytecode: ContractBytecodeSome, + pub build_id: String, + pub file_id: u32, +} + +/// Contract source code and bytecode data used for debugger. #[derive(Clone, Debug, Default)] pub struct ContractSources { - /// Map over artifacts' contract names -> vector of file IDs - pub ids_by_name: HashMap>, - /// Map over file_id -> source code - pub sources_by_id: FxHashMap, - /// Map over file_id -> contract name -> bytecode - pub artifacts_by_id: FxHashMap>, + /// Map over build_id -> file_id -> (source code, language) + pub sources_by_id: HashMap>, + /// Map over contract name -> Vec<(bytecode, build_id, file_id)> + pub artifacts_by_name: HashMap>, } impl ContractSources { /// Collects the contract sources and artifacts from the project compile output. pub fn from_project_output( output: &ProjectCompileOutput, - root: &Path, - libraries: &Libraries, + link_data: Option<(&Path, &Libraries)>, ) -> Result { - let linker = Linker::new(root, output.artifact_ids().collect()); - let mut sources = Self::default(); + + sources.insert(output, link_data)?; + + Ok(sources) + } + + pub fn insert( + &mut self, + output: &ProjectCompileOutput, + link_data: Option<(&Path, &Libraries)>, + ) -> Result<()> + where + C::Language: Into, + { + let link_data = link_data.map(|(root, libraries)| { + let linker = Linker::new(root, output.artifact_ids().collect()); + (linker, libraries) + }); + for (id, artifact) in output.artifact_ids() { if let Some(file_id) = artifact.id { - let abs_path = root.join(&id.source); - let source_code = std::fs::read_to_string(abs_path).wrap_err_with(|| { - format!("failed to read artifact source file for `{}`", id.identifier()) - })?; - let linked = linker.link(&id, libraries)?; - let contract = compact_to_contract(linked.into_contract_bytecode())?; - sources.insert(&id, file_id, source_code, contract); + let artifact = if let Some((linker, libraries)) = link_data.as_ref() { + linker.link(&id, libraries)?.into_contract_bytecode() + } else { + artifact.clone().into_contract_bytecode() + }; + let bytecode = compact_to_contract(artifact.clone().into_contract_bytecode())?; + + self.artifacts_by_name.entry(id.name.clone()).or_default().push(ArtifactData { + bytecode, + build_id: id.build_id.clone(), + file_id, + }); } else { warn!(id = id.identifier(), "source not found"); } } - Ok(sources) - } - /// Inserts a contract into the sources. - pub fn insert( - &mut self, - artifact_id: &ArtifactId, - file_id: u32, - source: String, - bytecode: ContractBytecodeSome, - ) { - self.ids_by_name.entry(artifact_id.name.clone()).or_default().push(file_id); - self.sources_by_id.insert(file_id, source); - self.artifacts_by_id.entry(file_id).or_default().insert(artifact_id.name.clone(), bytecode); - } + // Not all source files produce artifacts, so we are populating sources by using build + // infos. + 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_code = if let Some(source) = files.get(path) { + source.clone() + } else { + let source = Source::read(path).wrap_err_with(|| { + format!("failed to read artifact source file for `{}`", path.display()) + })?; + files.insert(path.clone(), source.content.clone()); + source.content + }; + + self.sources_by_id.entry(build_id.clone()).or_default().insert( + *source_id, + SourceData { source: source_code, language: build.language.into() }, + ); + } + } - /// Returns the source for a contract by file ID. - pub fn get(&self, id: u32) -> Option<&String> { - self.sources_by_id.get(&id) + Ok(()) } /// Returns all sources for a contract by name. - pub fn get_sources<'a>( - &'a self, - name: &'a str, - ) -> Option> { - self.ids_by_name.get(name).map(|ids| { - ids.iter().filter_map(|id| { - Some(( - *id, - self.sources_by_id.get(id)?.as_ref(), - self.artifacts_by_id.get(id)?.get(name)?, - )) + pub fn get_sources( + &self, + name: &str, + ) -> Option> { + self.artifacts_by_name.get(name).map(|artifacts| { + artifacts.iter().filter_map(|artifact| { + let source = + self.sources_by_id.get(artifact.build_id.as_str())?.get(&artifact.file_id)?; + Some((artifact, source)) }) }) } - /// Returns all (name, source, bytecode) sets. - pub fn entries(&self) -> impl Iterator { - self.artifacts_by_id - .iter() - .filter_map(|(id, artifacts)| { - let source = self.sources_by_id.get(id)?; - Some( - artifacts - .iter() - .map(move |(name, bytecode)| (name.as_ref(), source.as_ref(), bytecode)), - ) + /// Returns all (name, bytecode, source) sets. + pub fn entries(&self) -> impl Iterator { + self.artifacts_by_name.iter().flat_map(|(name, artifacts)| { + artifacts.iter().filter_map(|artifact| { + let source = + self.sources_by_id.get(artifact.build_id.as_str())?.get(&artifact.file_id)?; + Some((name.as_str(), artifact, source)) }) - .flatten() + }) } } @@ -464,7 +492,7 @@ pub fn compile_target( target_path: &Path, project: &Project, quiet: bool, -) -> Result> { +) -> Result> { ProjectCompiler::new().quiet(quiet).files([target_path.into()]).compile(project) } @@ -472,7 +500,7 @@ pub fn compile_target( /// Returns the artifact_id, the file_id, and the bytecode pub async fn compile_from_source( metadata: &Metadata, -) -> Result<(ArtifactId, u32, ContractBytecodeSome)> { +) -> Result> { let root = tempfile::tempdir()?; let root_path = root.path(); let project = etherscan_project(metadata, root_path)?; @@ -483,23 +511,9 @@ pub async fn compile_from_source( eyre::bail!("{project_output}") } - let (artifact_id, file_id, contract) = project_output - .into_artifacts() - .find(|(artifact_id, _)| artifact_id.name == metadata.contract_name) - .map(|(aid, art)| { - (aid, art.source_file().expect("no source file").id, art.into_contract_bytecode()) - }) - .ok_or_else(|| { - eyre::eyre!( - "Unable to find bytecode in compiled output for contract: {}", - metadata.contract_name - ) - })?; - let bytecode = compact_to_contract(contract)?; - root.close()?; - Ok((artifact_id, file_id, bytecode)) + Ok(project_output) } /// Creates a [Project] from an Etherscan source. diff --git a/crates/debugger/src/tui/draw.rs b/crates/debugger/src/tui/draw.rs index 8e55687a0..2909ef803 100644 --- a/crates/debugger/src/tui/draw.rs +++ b/crates/debugger/src/tui/draw.rs @@ -3,7 +3,7 @@ use super::context::{BufferKind, DebuggerContext}; use crate::op::OpcodeParam; use alloy_primitives::U256; -use foundry_compilers::sourcemap::SourceElement; +use foundry_compilers::{compilers::multi::MultiCompilerLanguage, sourcemap::SourceElement}; use ratatui::{ layout::{Alignment, Constraint, Direction, Layout, Rect}, style::{Color, Modifier, Style}, @@ -344,32 +344,43 @@ impl DebuggerContext<'_> { let is_create = matches!(self.call_kind(), CallKind::Create | CallKind::Create2); let pc = self.current_step().pc; let Some((source_element, source_code)) = - files_source_code.find_map(|(file_id, source_code, contract_source)| { + files_source_code.find_map(|(artifact, source)| { let bytecode = if is_create { - &contract_source.bytecode + &artifact.bytecode.bytecode } else { - contract_source.deployed_bytecode.bytecode.as_ref()? + artifact.bytecode.deployed_bytecode.bytecode.as_ref()? }; - let source_map = bytecode.source_map()?.ok()?; + let source_map = bytecode.source_map()?.expect("failed to parse"); let pc_ic_map = if is_create { create_map } else { rt_map }; let ic = pc_ic_map.get(pc)?; - let source_element = source_map.get(ic)?; + + // Solc indexes source maps by instruction counter, but Vyper indexes by program + // counter. + let source_element = if matches!(source.language, MultiCompilerLanguage::Solc(_)) { + source_map.get(ic)? + } else { + source_map.get(pc)? + }; // if the source element has an index, find the sourcemap for that index - source_element + let res = source_element .index() // if index matches current file_id, return current source code .and_then(|index| { - (index == file_id).then(|| (source_element.clone(), source_code)) + (index == artifact.file_id) + .then(|| (source_element.clone(), source.source.as_str())) }) .or_else(|| { // otherwise find the source code for the element's index self.debugger .contracts_sources .sources_by_id + .get(&artifact.build_id)? .get(&source_element.index()?) - .map(|source_code| (source_element.clone(), source_code.as_ref())) - }) + .map(|source| (source_element.clone(), source.source.as_str())) + }); + + res }) else { return Err(format!("No source map for contract {contract_name}")); diff --git a/crates/debugger/src/tui/mod.rs b/crates/debugger/src/tui/mod.rs index d57c6e7a2..c810440e5 100644 --- a/crates/debugger/src/tui/mod.rs +++ b/crates/debugger/src/tui/mod.rs @@ -66,12 +66,12 @@ impl Debugger { ) -> Self { let pc_ic_maps = contracts_sources .entries() - .filter_map(|(contract_name, _, contract)| { + .filter_map(|(name, artifact, _)| { Some(( - contract_name.to_owned(), + name.to_owned(), ( - PcIcMap::new(contract.bytecode.bytes()?), - PcIcMap::new(contract.deployed_bytecode.bytes()?), + PcIcMap::new(artifact.bytecode.bytecode.bytes()?), + PcIcMap::new(artifact.bytecode.deployed_bytecode.bytes()?), ), )) }) diff --git a/crates/evm/traces/src/identifier/etherscan.rs b/crates/evm/traces/src/identifier/etherscan.rs index 977b69a42..794b3a9ed 100644 --- a/crates/evm/traces/src/identifier/etherscan.rs +++ b/crates/evm/traces/src/identifier/etherscan.rs @@ -59,14 +59,11 @@ impl EtherscanIdentifier { /// Etherscan and compiles them locally, for usage in the debugger. pub async fn get_compiled_contracts(&self) -> eyre::Result { // TODO: Add caching so we dont double-fetch contracts. - let contracts_iter = self + let outputs_fut = self .contracts .iter() // filter out vyper files - .filter(|(_, metadata)| !metadata.is_vyper()); - - let outputs_fut = contracts_iter - .clone() + .filter(|(_, metadata)| !metadata.is_vyper()) .map(|(address, metadata)| { println!("Compiling: {} {address}", metadata.contract_name); let err_msg = @@ -76,15 +73,13 @@ impl EtherscanIdentifier { .collect::>(); // poll all the futures concurrently - let artifacts = join_all(outputs_fut).await; + let outputs = join_all(outputs_fut).await; let mut sources: ContractSources = Default::default(); // construct the map - for (results, (_, metadata)) in artifacts.into_iter().zip(contracts_iter) { - // get the inner type - let (artifact_id, file_id, bytecode) = results?; - sources.insert(&artifact_id, file_id, metadata.source_code(), bytecode); + for output in outputs { + sources.insert(&output?, None)?; } Ok(sources) diff --git a/crates/forge/bin/cmd/bind.rs b/crates/forge/bin/cmd/bind.rs index dcf76fc2f..8283ca7d8 100644 --- a/crates/forge/bin/cmd/bind.rs +++ b/crates/forge/bin/cmd/bind.rs @@ -168,7 +168,7 @@ impl BindArgs { Ok(json_files(artifacts) .filter_map(|path| { // Ignore the build info JSON. - if path.to_str()?.contains("/build-info/") { + if path.to_str()?.contains("build-info") { return None; } diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index d2de7ef23..c27e2053d 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -337,8 +337,7 @@ impl TestArgs { let sources = ContractSources::from_project_output( output_clone.as_ref().unwrap(), - project.root(), - &libraries, + Some((project.root(), &libraries)), )?; // Run the debugger. diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 1e2be6c7c..1c1206fa0 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -8,7 +8,9 @@ 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_compilers::{artifacts::Libraries, Artifact, ArtifactId, ProjectCompileOutput}; +use foundry_compilers::{ + artifacts::Libraries, compilers::Compiler, Artifact, ArtifactId, ProjectCompileOutput, +}; use foundry_config::Config; use foundry_evm::{ backend::Backend, decode::RevertDecoder, executors::ExecutorBuilder, fork::CreateFork, @@ -356,10 +358,10 @@ impl MultiContractRunnerBuilder { /// Given an EVM, proceeds to return a runner which is able to execute all tests /// against that evm - pub fn build( + pub fn build( self, root: &Path, - output: ProjectCompileOutput, + output: ProjectCompileOutput, env: revm::primitives::Env, evm_opts: EvmOpts, ) -> Result { diff --git a/crates/forge/tests/cli/build.rs b/crates/forge/tests/cli/build.rs index e2dca7e0a..93b313742 100644 --- a/crates/forge/tests/cli/build.rs +++ b/crates/forge/tests/cli/build.rs @@ -1,7 +1,8 @@ +use foundry_common::fs::read_json_file; use foundry_config::Config; -use foundry_test_utils::{forgetest, util::OutputExt}; +use foundry_test_utils::forgetest; use globset::Glob; -use std::path::PathBuf; +use std::{collections::BTreeMap, path::PathBuf}; // tests that json is printed when --json is passed forgetest!(compile_json, |prj, cmd| { @@ -21,10 +22,17 @@ contract Dummy { // set up command cmd.args(["compile", "--format-json"]); - // run command and assert - cmd.unchecked_output().stdout_matches_path( - PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/compile_json.stdout"), - ); + // Exclude build_infos from output as IDs depend on root dir and are not deterministic. + let mut output: BTreeMap = + serde_json::from_str(&cmd.stdout_lossy()).unwrap(); + output.remove("build_infos"); + + let expected: BTreeMap = read_json_file( + &PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/compile_json.stdout"), + ) + .unwrap(); + + similar_asserts::assert_eq!(output, expected); }); // tests build output is as expected diff --git a/crates/forge/tests/fixtures/compile_json.stdout b/crates/forge/tests/fixtures/compile_json.stdout index f3eb33ea0..eff78e60c 100644 --- a/crates/forge/tests/fixtures/compile_json.stdout +++ b/crates/forge/tests/fixtures/compile_json.stdout @@ -1,4 +1,5 @@ { + "contracts": {}, "errors": [ { "sourceLocation": { @@ -11,10 +12,8 @@ "severity": "error", "errorCode": "7576", "message": "Undeclared identifier. Did you mean \"newNumber\"?", - "formattedMessage": "DeclarationError: Undeclared identifier. Did you mean \"newNumber\"?\n --> src/dummy.sol:7:18:\n |\n7 | number = newnumber; // error here\n | ^^^^^^^^^\n\n" + "formattedMessage": "DeclarationError: Undeclared identifier. Did you mean \"newNumber\"?\n --> src/jsonError.sol:7:18:\n |\n7 | number = newnumber; // error here\n | ^^^^^^^^^\n\n" } ], - "sources": {}, - "contracts": {}, - "build_infos": {} -} + "sources": {} +} \ No newline at end of file diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index 342096fda..f1a18d45f 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -136,8 +136,7 @@ impl LinkedBuildData { ) -> Result { let sources = ContractSources::from_project_output( &build_data.output, - &build_data.project_root, - &libraries, + Some((&build_data.project_root, &libraries)), )?; let known_contracts = diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index e1a681db6..6d2d5233a 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -1083,7 +1083,7 @@ static IGNORE_IN_FIXTURES: Lazy = Lazy::new(|| { Regex::new(&format!("({})", re.join("|"))).unwrap() }); -fn normalize_output(s: &str) -> String { +pub fn normalize_output(s: &str) -> String { let s = s.replace("\r\n", "\n").replace('\\', "/"); IGNORE_IN_FIXTURES.replace_all(&s, "").into_owned() } diff --git a/crates/verify/src/etherscan/flatten.rs b/crates/verify/src/etherscan/flatten.rs index fa888f922..ac586b48c 100644 --- a/crates/verify/src/etherscan/flatten.rs +++ b/crates/verify/src/etherscan/flatten.rs @@ -4,6 +4,7 @@ use eyre::{Context, Result}; use foundry_block_explorers::verify::CodeFormat; use foundry_compilers::{ artifacts::{BytecodeHash, Source}, + buildinfo::RawBuildInfo, compilers::{ solc::{SolcCompiler, SolcLanguage, SolcVersionedInput}, Compiler, CompilerInput, @@ -87,8 +88,8 @@ 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, out); + let mut o = AggregatedCompilerOutput::::default(); + o.extend(version.clone(), RawBuildInfo::new(&input, &out, false)?, out); let diags = o.diagnostics(&[], &[], Default::default()); eyre::bail!( From 481c74b140f8612997386f80bc33dc6f63a18f8d Mon Sep 17 00:00:00 2001 From: Zhuo Zhang <14835483+ZhangZhuoSJTU@users.noreply.github.com> Date: Wed, 12 Jun 2024 14:53:56 -0400 Subject: [PATCH 395/430] fix(forge): fix the bug where source code incorrectly overlaps during debugging (#8134) --- crates/debugger/src/tui/draw.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/debugger/src/tui/draw.rs b/crates/debugger/src/tui/draw.rs index 2909ef803..aa6ac13cf 100644 --- a/crates/debugger/src/tui/draw.rs +++ b/crates/debugger/src/tui/draw.rs @@ -322,6 +322,14 @@ impl DebuggerContext<'_> { lines.push(u_num, line, u_text); } + // pad with empty to each line to ensure the previous text is cleared + for line in &mut lines.lines { + // note that the \n is not included in the line length + if area.width as usize > line.width() + 1 { + line.push_span(Span::raw(" ".repeat(area.width as usize - line.width() - 1))); + } + } + Text::from(lines.lines) } From f8160598b76ac6fabb0133fe62fab4c432ccb5c9 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 12 Jun 2024 20:56:42 +0200 Subject: [PATCH 396/430] fix: remove hardcoded retries (#8141) --- crates/common/src/provider/tower.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/crates/common/src/provider/tower.rs b/crates/common/src/provider/tower.rs index bb64ee8ab..73088021d 100644 --- a/crates/common/src/provider/tower.rs +++ b/crates/common/src/provider/tower.rs @@ -55,7 +55,7 @@ impl tower::layer::Layer for RetryBackoffLayer { inner, policy: RateLimitRetryPolicy, max_rate_limit_retries: self.max_rate_limit_retries, - max_timeout_retries: self.max_timeout_retries, + _max_timeout_retries: self.max_timeout_retries, initial_backoff: self.initial_backoff, compute_units_per_second: self.compute_units_per_second, requests_enqueued: Arc::new(AtomicU32::new(0)), @@ -74,7 +74,7 @@ pub struct RetryBackoffService { /// The maximum number of retries for rate limit errors max_rate_limit_retries: u32, /// The maximum number of retries for timeout errors - max_timeout_retries: u32, + _max_timeout_retries: u32, /// The initial backoff in milliseconds initial_backoff: u64, /// The number of compute units per second for this service @@ -100,7 +100,6 @@ impl tower::Service for RetryBackoffService { Box::pin(async move { let ahead_in_queue = this.requests_enqueued.fetch_add(1, Ordering::SeqCst) as u64; let mut rate_limit_retry_number: u32 = 0; - let mut timeout_retries: u32 = 0; loop { let err; let fut = this.inner.call(request.clone()).await; @@ -157,11 +156,7 @@ impl tower::Service for RetryBackoffService { tokio::time::sleep(total_backoff).await; } else { - if timeout_retries < this.max_timeout_retries { - timeout_retries += 1; - continue; - } - + trace!("encountered non retryable error {err:?}"); this.requests_enqueued.fetch_sub(1, Ordering::SeqCst); return Err(err) } From 82f0aaa34c7f3abc542abc34a02ca941f73bc517 Mon Sep 17 00:00:00 2001 From: morito Date: Thu, 13 Jun 2024 04:21:30 +0900 Subject: [PATCH 397/430] chore(deps): Bump alloy 14ed25d (#8128) * chore: bump alloy 14ed25d * Format files * format: Remove unnecessary trailing comma updates * Fix to update only alloy in Cargo.lock * common: Enable eth feature for allot-rpc-types * Enable eth feature for alloy-rpc-types * bump alloy * new retry changes --------- Co-authored-by: Matthias Seitz --- Cargo.lock | 214 +++++++++++-------- Cargo.toml | 52 ++--- crates/anvil/core/src/eth/transaction/mod.rs | 14 +- crates/anvil/src/config.rs | 4 +- crates/anvil/src/eth/api.rs | 13 +- crates/anvil/src/eth/backend/executor.rs | 9 +- crates/anvil/src/eth/backend/fork.rs | 8 +- crates/anvil/src/eth/otterscan/api.rs | 2 +- crates/anvil/src/tasks/mod.rs | 2 +- crates/anvil/tests/it/anvil.rs | 7 +- crates/anvil/tests/it/anvil_api.rs | 37 ++-- crates/anvil/tests/it/api.rs | 15 +- crates/anvil/tests/it/eip4844.rs | 2 +- crates/anvil/tests/it/fork.rs | 44 ++-- crates/anvil/tests/it/gas.rs | 10 +- crates/anvil/tests/it/traces.rs | 2 +- crates/anvil/tests/it/transaction.rs | 10 +- crates/cast/Cargo.toml | 2 +- crates/cast/bin/cmd/run.rs | 2 +- crates/cast/bin/main.rs | 2 +- crates/cast/src/lib.rs | 5 +- crates/common/Cargo.toml | 2 +- crates/common/src/fmt/ui.rs | 4 +- crates/common/src/provider/retry.rs | 47 ++-- crates/common/src/transactions.rs | 2 +- crates/evm/core/src/fork/backend.rs | 6 +- crates/script/src/receipts.rs | 4 +- crates/verify/src/bytecode.rs | 2 +- 28 files changed, 297 insertions(+), 226 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 91b6f3c50..dc6ee9638 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,7 +80,7 @@ dependencies = [ [[package]] name = "alloy-consensus" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" +source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" dependencies = [ "alloy-eips", "alloy-primitives", @@ -93,7 +93,7 @@ dependencies = [ [[package]] name = "alloy-contract" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" +source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -101,7 +101,7 @@ dependencies = [ "alloy-primitives", "alloy-provider", "alloy-pubsub", - "alloy-rpc-types", + "alloy-rpc-types-eth", "alloy-sol-types", "alloy-transport", "futures", @@ -111,9 +111,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd2404399cb1b50572758e66e9b4bf088e5a3df9007be7126456c7e50af935f" +checksum = "cb6e6436a9530f25010d13653e206fab4c9feddacf21a54de8d7311b275bc56b" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -133,7 +133,7 @@ dependencies = [ [[package]] name = "alloy-eips" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" +source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -148,7 +148,7 @@ dependencies = [ [[package]] name = "alloy-genesis" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" +source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" dependencies = [ "alloy-primitives", "alloy-serde", @@ -158,9 +158,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3abf6446a292e19853aaca43590eeb48bf435dfd2c74200259e8f4872f6ce3" +checksum = "aaeaccd50238126e3a0ff9387c7c568837726ad4f4e399b528ca88104d6c25ef" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -171,7 +171,7 @@ dependencies = [ [[package]] name = "alloy-json-rpc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" +source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" dependencies = [ "alloy-primitives", "serde", @@ -183,13 +183,13 @@ dependencies = [ [[package]] name = "alloy-network" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" +source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" dependencies = [ "alloy-consensus", "alloy-eips", "alloy-json-rpc", "alloy-primitives", - "alloy-rpc-types", + "alloy-rpc-types-eth", "alloy-signer", "alloy-sol-types", "async-trait", @@ -200,9 +200,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5277af0cbcc483ee6ad2c1e818090b5928d27f04fd6580680f31c1cf8068bcc2" +checksum = "f783611babedbbe90db3478c120fb5f5daacceffc210b39adc0af4fe0da70bad" dependencies = [ "alloy-rlp", "arbitrary", @@ -228,7 +228,7 @@ dependencies = [ [[package]] name = "alloy-provider" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" +source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" dependencies = [ "alloy-chains", "alloy-consensus", @@ -238,7 +238,7 @@ dependencies = [ "alloy-primitives", "alloy-pubsub", "alloy-rpc-client", - "alloy-rpc-types", + "alloy-rpc-types-eth", "alloy-rpc-types-trace", "alloy-transport", "alloy-transport-http", @@ -263,7 +263,7 @@ dependencies = [ [[package]] name = "alloy-pubsub" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" +source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -303,7 +303,7 @@ dependencies = [ [[package]] name = "alloy-rpc-client" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" +source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -327,45 +327,55 @@ dependencies = [ [[package]] name = "alloy-rpc-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" +source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" +dependencies = [ + "alloy-rpc-types-eth", + "alloy-rpc-types-trace", + "alloy-serde", +] + +[[package]] +name = "alloy-rpc-types-engine" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" dependencies = [ "alloy-consensus", "alloy-eips", - "alloy-genesis", "alloy-primitives", "alloy-rlp", + "alloy-rpc-types-eth", "alloy-serde", - "alloy-sol-types", - "itertools 0.12.1", + "jsonwebtoken", + "rand", "serde", - "serde_json", "thiserror", ] [[package]] -name = "alloy-rpc-types-engine" +name = "alloy-rpc-types-eth" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" +source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" dependencies = [ "alloy-consensus", "alloy-eips", + "alloy-genesis", "alloy-primitives", "alloy-rlp", - "alloy-rpc-types", "alloy-serde", - "jsonwebtoken", - "rand", + "alloy-sol-types", + "itertools 0.13.0", "serde", + "serde_json", "thiserror", ] [[package]] name = "alloy-rpc-types-trace" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" +source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" dependencies = [ "alloy-primitives", - "alloy-rpc-types", + "alloy-rpc-types-eth", "alloy-serde", "serde", "serde_json", @@ -374,7 +384,7 @@ dependencies = [ [[package]] name = "alloy-serde" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" +source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" dependencies = [ "alloy-primitives", "serde", @@ -384,7 +394,7 @@ dependencies = [ [[package]] name = "alloy-signer" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" +source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -399,7 +409,7 @@ dependencies = [ [[package]] name = "alloy-signer-aws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" +source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" dependencies = [ "alloy-consensus", "alloy-network", @@ -416,7 +426,7 @@ dependencies = [ [[package]] name = "alloy-signer-gcp" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" +source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" dependencies = [ "alloy-consensus", "alloy-network", @@ -433,7 +443,7 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" +source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -452,7 +462,7 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" +source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" dependencies = [ "alloy-consensus", "alloy-network", @@ -468,7 +478,7 @@ dependencies = [ [[package]] name = "alloy-signer-wallet" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" +source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" dependencies = [ "alloy-consensus", "alloy-network", @@ -486,9 +496,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30708a79919b082f2692423c8cc72fc250477e4a2ecb0d4a7244cd3cdb299965" +checksum = "4bad41a7c19498e3f6079f7744656328699f8ea3e783bdd10d85788cd439f572" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -500,9 +510,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c7a679ac01774ab7e00a567a918d4231ae692c5c8cedaf4e16956c3116d7896" +checksum = "fd9899da7d011b4fe4c406a524ed3e3f963797dbc93b45479d60341d3a27b252" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", @@ -519,9 +529,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "356da0c2228aa6675a5faaa08a3e4061b967f924753983d72b9a18d9a3fad44e" +checksum = "d32d595768fdc61331a132b6f65db41afae41b9b97d36c21eb1b955c422a7e60" dependencies = [ "alloy-json-abi", "const-hex", @@ -536,18 +546,18 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fd4783b0a5840479013e9ce960d2eb7b3be381f722e0fe3d1f7c3bb6bd4ebd" +checksum = "baa2fbd22d353d8685bd9fee11ba2d8b5c3b1d11e56adb3265fcf1f32bfdf404" dependencies = [ "winnow 0.6.13", ] [[package]] name = "alloy-sol-types" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eb5e6234c0b62514992589fe1578f64d418dbc8ef5cd1ab2d7f2f568f599698" +checksum = "a49042c6d3b66a9fe6b2b5a8bf0d39fc2ae1ee0310a2a26ffedd79fb097878dd" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -559,7 +569,7 @@ dependencies = [ [[package]] name = "alloy-transport" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" +source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" dependencies = [ "alloy-json-rpc", "base64 0.22.1", @@ -571,13 +581,12 @@ dependencies = [ "tokio", "tower", "url", - "wasm-bindgen-futures", ] [[package]] name = "alloy-transport-http" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" +source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -591,7 +600,7 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" +source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -611,15 +620,16 @@ dependencies = [ [[package]] name = "alloy-transport-ws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=cc68b93#cc68b93605f4521c2b0bce1a7efaeff2046cf07c" +source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" dependencies = [ "alloy-pubsub", "alloy-transport", "futures", - "http 0.2.12", + "http 1.1.0", + "rustls 0.23.9", "serde_json", "tokio", - "tokio-tungstenite 0.20.1", + "tokio-tungstenite 0.23.0", "tracing", "ws_stream_wasm", ] @@ -2109,9 +2119,9 @@ dependencies = [ [[package]] name = "coins-bip32" -version = "0.8.7" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b6be4a5df2098cd811f3194f64ddb96c267606bffd9689ac7b0160097b01ad3" +checksum = "66c43ff7fd9ff522219058808a259e61423335767b1071d5b346de60d9219657" dependencies = [ "bs58", "coins-core", @@ -2125,9 +2135,9 @@ dependencies = [ [[package]] name = "coins-bip39" -version = "0.8.7" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db8fba409ce3dc04f7d804074039eb68b960b0829161f8e06c95fea3f122528" +checksum = "4c4587c0b4064da887ed39a6522f577267d57e58bdd583178cd877d721b56a2e" dependencies = [ "bitvec", "coins-bip32", @@ -2141,19 +2151,18 @@ dependencies = [ [[package]] name = "coins-core" -version = "0.8.7" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5286a0843c21f8367f7be734f89df9b822e0321d8bcce8d6e735aadff7d74979" +checksum = "6b3aeeec621f4daec552e9d28befd58020a78cfc364827d06a753e8bc13c6c4b" dependencies = [ "base64 0.21.7", "bech32", "bs58", + "const-hex", "digest 0.10.7", "generic-array", - "hex", "ripemd", "serde", - "serde_derive", "sha2", "sha3", "thiserror", @@ -2161,15 +2170,15 @@ dependencies = [ [[package]] name = "coins-ledger" -version = "0.10.1" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e076e6e5d9708f0b90afe2dbe5a8ba406b5c794347661e6e44618388c7e3a31" +checksum = "166ef757aa936b45f3e5d39c344047f65ef7d25a50067246a498021a816d074b" dependencies = [ "async-trait", "byteorder", "cfg-if", + "const-hex", "getrandom", - "hex", "hidapi-rusb", "js-sys", "log", @@ -6378,7 +6387,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.11.0", "proc-macro2", "quote", "syn 2.0.66", @@ -6725,7 +6734,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 0.26.2", + "webpki-roots", "winreg 0.52.0", ] @@ -6746,11 +6755,10 @@ dependencies = [ [[package]] name = "revm-inspectors" version = "0.1.0" -source = "git+https://github.com/paradigmxyz/revm-inspectors?rev=a278649#a2786496b2edcb06abed5a33682c532d070bf47c" +source = "git+https://github.com/paradigmxyz/revm-inspectors?rev=5cf339c#5cf339cada76e3dc38c864aab870d3cdb7b6860d" dependencies = [ "alloy-primitives", "alloy-rpc-types", - "alloy-rpc-types-trace", "alloy-sol-types", "anstyle", "colorchoice", @@ -6999,6 +7007,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls" +version = "0.23.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a218f0f6d05669de4eabfb24f31ce802035c952429d037507b4a4a39f0e60c5b" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki 0.102.4", + "subtle", + "zeroize", +] + [[package]] name = "rustls-native-certs" version = "0.6.3" @@ -7880,9 +7902,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6fe08d08d84f2c0a77f1e7c46518789d745c2e87a2721791ed7c3c9bc78df28" +checksum = "8d71e19bca02c807c9faa67b5a47673ff231b6e7449b251695188522f1dc44b2" dependencies = [ "paste", "proc-macro2", @@ -8189,6 +8211,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls 0.23.9", + "rustls-pki-types", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.15" @@ -8203,29 +8236,30 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.20.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" +checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" dependencies = [ "futures-util", "log", - "rustls 0.21.12", "tokio", - "tokio-rustls 0.24.1", - "tungstenite 0.20.1", - "webpki-roots 0.25.4", + "tungstenite 0.21.0", ] [[package]] name = "tokio-tungstenite" -version = "0.21.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" +checksum = "becd34a233e7e31a3dbf7c7241b38320f57393dcae8e7324b0167d21b8e320b0" dependencies = [ "futures-util", "log", + "rustls 0.23.9", + "rustls-pki-types", "tokio", - "tungstenite 0.21.0", + "tokio-rustls 0.26.0", + "tungstenite 0.23.0", + "webpki-roots", ] [[package]] @@ -8496,18 +8530,17 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" -version = "0.20.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" +checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" dependencies = [ "byteorder", "bytes", "data-encoding", - "http 0.2.12", + "http 1.1.0", "httparse", "log", "rand", - "rustls 0.21.12", "sha1", "thiserror", "url", @@ -8516,9 +8549,9 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.21.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" +checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8" dependencies = [ "byteorder", "bytes", @@ -8527,9 +8560,10 @@ dependencies = [ "httparse", "log", "rand", + "rustls 0.23.9", + "rustls-pki-types", "sha1", "thiserror", - "url", "utf-8", ] @@ -8899,12 +8933,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki-roots" -version = "0.25.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" - [[package]] name = "webpki-roots" version = "0.26.2" diff --git a/Cargo.toml b/Cargo.toml index 65532c074..58b2a8972 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -159,7 +159,7 @@ foundry-compilers = { version = "0.7.0", default-features = false } # no default features to avoid c-kzg revm = { version = "9.0.0", default-features = false } revm-primitives = { version = "4.0.0", default-features = false } -revm-inspectors = { git = "https://github.com/paradigmxyz/revm-inspectors", rev = "a278649", features = [ +revm-inspectors = { git = "https://github.com/paradigmxyz/revm-inspectors", rev = "5cf339c", features = [ "serde", ] } @@ -167,30 +167,30 @@ revm-inspectors = { git = "https://github.com/paradigmxyz/revm-inspectors", rev ethers-contract-abigen = { version = "2.0.14", default-features = false } ## alloy -alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } -alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } -alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } -alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } -alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } -alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } -alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } -alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } -alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } -alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } -alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } -alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } -alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } -alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } -alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } -alloy-signer-wallet = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } -alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } -alloy-signer-gcp = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } -alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } -alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } -alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } -alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } -alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } -alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "cc68b93", default-features = false } +alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } +alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } +alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } +alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } +alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } +alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } +alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } +alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } +alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } +alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } +alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } +alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } +alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } +alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } +alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } +alloy-signer-wallet = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } +alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } +alloy-signer-gcp = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } +alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } +alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } +alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } +alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } +alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } +alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } alloy-primitives = { version = "0.7.1", features = ["getrandom", "rand"] } alloy-dyn-abi = "0.7.1" alloy-json-abi = "0.7.1" @@ -247,4 +247,4 @@ soldeer = "0.2.15" revm = { git = "https://github.com/bluealloy/revm.git", rev = "a28a543" } revm-interpreter = { git = "https://github.com/bluealloy/revm.git", rev = "a28a543" } revm-precompile = { git = "https://github.com/bluealloy/revm.git", rev = "a28a543" } -revm-primitives = { git = "https://github.com/bluealloy/revm.git", rev = "a28a543" } \ No newline at end of file +revm-primitives = { git = "https://github.com/bluealloy/revm.git", rev = "a28a543" } diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 712ac937d..013759d3b 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -6,7 +6,7 @@ use alloy_consensus::{ AnyReceiptEnvelope, Receipt, ReceiptEnvelope, ReceiptWithBloom, Signed, TxEip1559, TxEip2930, TxEnvelope, TxLegacy, TxReceipt, }; -use alloy_eips::eip2718::{Decodable2718, Encodable2718}; +use alloy_eips::eip2718::{Decodable2718, Eip2718Error, Encodable2718}; use alloy_primitives::{Address, Bloom, Bytes, Log, Signature, TxHash, TxKind, B256, U256, U64}; use alloy_rlp::{length_of_length, Decodable, Encodable, Header}; use alloy_rpc_types::{ @@ -962,7 +962,7 @@ impl Encodable2718 for TypedTransaction { } impl Decodable2718 for TypedTransaction { - fn typed_decode(ty: u8, buf: &mut &[u8]) -> alloy_rlp::Result { + fn typed_decode(ty: u8, buf: &mut &[u8]) -> Result { if ty == 0x7E { return Ok(Self::Deposit(DepositTransaction::decode(buf)?)) } @@ -974,7 +974,7 @@ impl Decodable2718 for TypedTransaction { } } - fn fallback_decode(buf: &mut &[u8]) -> alloy_rlp::Result { + fn fallback_decode(buf: &mut &[u8]) -> Result { match TxEnvelope::fallback_decode(buf)? { TxEnvelope::Legacy(tx) => Ok(Self::Legacy(tx)), _ => unreachable!(), @@ -1295,7 +1295,7 @@ impl Encodable2718 for TypedReceipt { } impl Decodable2718 for TypedReceipt { - fn typed_decode(ty: u8, buf: &mut &[u8]) -> alloy_rlp::Result { + fn typed_decode(ty: u8, buf: &mut &[u8]) -> Result { if ty == 0x7E { return Ok(Self::Deposit(DepositReceipt::decode(buf)?)) } @@ -1307,7 +1307,7 @@ impl Decodable2718 for TypedReceipt { } } - fn fallback_decode(buf: &mut &[u8]) -> alloy_rlp::Result { + fn fallback_decode(buf: &mut &[u8]) -> Result { match ReceiptEnvelope::fallback_decode(buf)? { ReceiptEnvelope::Legacy(tx) => Ok(Self::Legacy(tx)), _ => unreachable!(), @@ -1506,7 +1506,7 @@ mod tests { let mut data = vec![]; let receipt = TypedReceipt::Legacy(ReceiptWithBloom { receipt: Receipt { - status: false, + status: false.into(), cumulative_gas_used: 0x1u128, logs: vec![Log { address: Address::from_str("0000000000000000000000000000000000000011").unwrap(), @@ -1541,7 +1541,7 @@ mod tests { let expected = TypedReceipt::Legacy(ReceiptWithBloom { receipt: Receipt { - status: false, + status: false.into(), cumulative_gas_used: 0x1u128, logs: vec![Log { address: Address::from_str("0000000000000000000000000000000000000011").unwrap(), diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 1b332c3a3..f9fe56c2c 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -1026,7 +1026,7 @@ impl NodeConfig { }; let block = provider - .get_block(BlockNumberOrTag::Number(fork_block_number).into(), false) + .get_block(BlockNumberOrTag::Number(fork_block_number).into(), false.into()) .await .expect("Failed to get fork block"); @@ -1282,7 +1282,7 @@ async fn find_latest_fork_block, T: Transport + Clone // walk back from the head of the chain, but at most 2 blocks, which should be more than enough // leeway for _ in 0..2 { - if let Some(block) = provider.get_block(num.into(), false).await? { + if let Some(block) = provider.get_block(num.into(), false.into()).await? { if block.header.hash.is_some() { break; } diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 57acc197c..49e0db680 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -1197,7 +1197,7 @@ impl EthApi { let number = self.backend.ensure_block_number(Some(BlockId::Number(block_number))).await?; if let Some(fork) = self.get_fork() { if fork.predates_fork_inclusive(number) { - return Ok(fork.uncle_by_block_number_and_index(number, idx.into()).await?); + return Ok(fork.uncle_by_block_number_and_index(number, idx.into()).await?) } } // It's impossible to have uncles outside of fork mode @@ -1923,7 +1923,14 @@ impl EthApi { if let Some(receipt) = self.backend.mined_transaction_receipt(tx.hash) { if let Some(output) = receipt.out { // insert revert reason if failure - if !receipt.inner.inner.as_receipt_with_bloom().receipt.status { + if !receipt + .inner + .inner + .as_receipt_with_bloom() + .receipt + .status + .coerce_status() + { if let Some(reason) = RevertDecoder::new().maybe_decode(&output, None) { @@ -2514,7 +2521,7 @@ impl EthApi { if let BlockRequest::Number(number) = block_request { if let Some(fork) = self.get_fork() { if fork.predates_fork_inclusive(number) { - return Ok(fork.get_nonce(address, number).await?); + return Ok(fork.get_nonce(address, number).await?) } } } diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index cccd26085..163428eeb 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -54,9 +54,12 @@ impl ExecutedTransaction { // successful return see [Return] let status_code = u8::from(self.exit_reason as u8 <= InstructionResult::SelfDestruct as u8); - let receipt_with_bloom: ReceiptWithBloom = - Receipt { status: status_code == 1, cumulative_gas_used: *cumulative_gas_used, logs } - .into(); + let receipt_with_bloom: ReceiptWithBloom = Receipt { + status: (status_code == 1).into(), + cumulative_gas_used: *cumulative_gas_used, + logs, + } + .into(); match &self.transaction.pending_transaction.transaction.transaction { TypedTransaction::Legacy(_) => TypedReceipt::Legacy(receipt_with_bloom), diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index a04e13440..feecc9487 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -75,8 +75,10 @@ impl ClientFork { } let provider = self.provider(); - let block = - provider.get_block(block_number, false).await?.ok_or(BlockchainError::BlockNotFound)?; + let block = provider + .get_block(block_number, false.into()) + .await? + .ok_or(BlockchainError::BlockNotFound)?; let block_hash = block.header.hash.ok_or(BlockchainError::BlockNotFound)?; let timestamp = block.header.timestamp; let base_fee = block.header.base_fee_per_gas; @@ -482,7 +484,7 @@ impl ClientFork { &self, block_id: impl Into, ) -> Result, TransportError> { - if let Some(block) = self.provider().get_block(block_id.into(), true).await? { + if let Some(block) = self.provider().get_block(block_id.into(), true.into()).await? { let hash = block.header.hash.unwrap(); let block_number = block.header.number.unwrap(); let mut storage = self.storage_write(); diff --git a/crates/anvil/src/eth/otterscan/api.rs b/crates/anvil/src/eth/otterscan/api.rs index dac698a5a..a830855d4 100644 --- a/crates/anvil/src/eth/otterscan/api.rs +++ b/crates/anvil/src/eth/otterscan/api.rs @@ -66,7 +66,7 @@ impl EthApi { node_info!("ots_getTransactionError"); if let Some(receipt) = self.backend.mined_transaction_receipt(hash) { - if !receipt.inner.inner.as_receipt_with_bloom().receipt.status { + if !receipt.inner.inner.as_receipt_with_bloom().receipt.status.coerce_status() { return Ok(receipt.out.map(|b| b.0.into()).unwrap_or(Bytes::default())) } } diff --git a/crates/anvil/src/tasks/mod.rs b/crates/anvil/src/tasks/mod.rs index 499cb3e67..d2ceb0dca 100644 --- a/crates/anvil/src/tasks/mod.rs +++ b/crates/anvil/src/tasks/mod.rs @@ -74,7 +74,7 @@ impl TaskManager { let provider = provider.clone(); let api = api.clone(); async move { - if let Ok(Some(block)) = provider.get_block(hash.into(), false).await { + if let Ok(Some(block)) = provider.get_block(hash.into(), false.into()).await { let _ = api .anvil_reset(Some(Forking { json_rpc_url: None, diff --git a/crates/anvil/tests/it/anvil.rs b/crates/anvil/tests/it/anvil.rs index 7aff5660e..b8aed751d 100644 --- a/crates/anvil/tests/it/anvil.rs +++ b/crates/anvil/tests/it/anvil.rs @@ -62,7 +62,7 @@ async fn test_can_set_genesis_timestamp() { assert_eq!( genesis_timestamp, - provider.get_block(0.into(), false).await.unwrap().unwrap().header.timestamp + provider.get_block(0.into(), false.into()).await.unwrap().unwrap().header.timestamp ); } @@ -71,5 +71,8 @@ async fn test_can_use_default_genesis_timestamp() { let (_api, handle) = spawn(NodeConfig::test()).await; let provider = handle.http_provider(); - assert_ne!(0u64, provider.get_block(0.into(), false).await.unwrap().unwrap().header.timestamp); + assert_ne!( + 0u64, + provider.get_block(0.into(), false.into()).await.unwrap().unwrap().header.timestamp + ); } diff --git a/crates/anvil/tests/it/anvil_api.rs b/crates/anvil/tests/it/anvil_api.rs index 0ebffe94a..11aa40bdc 100644 --- a/crates/anvil/tests/it/anvil_api.rs +++ b/crates/anvil/tests/it/anvil_api.rs @@ -287,14 +287,14 @@ async fn test_set_next_timestamp() { api.evm_mine(None).await.unwrap(); - let block = provider.get_block(BlockId::default(), false).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::default(), false.into()).await.unwrap().unwrap(); assert_eq!(block.header.number.unwrap(), 1); assert_eq!(block.header.timestamp, next_timestamp.as_secs()); api.evm_mine(None).await.unwrap(); - let next = provider.get_block(BlockId::default(), false).await.unwrap().unwrap(); + let next = provider.get_block(BlockId::default(), false.into()).await.unwrap().unwrap(); assert_eq!(next.header.number.unwrap(), 2); assert!(next.header.timestamp > block.header.timestamp); @@ -314,12 +314,12 @@ async fn test_evm_set_time() { // mine a block api.evm_mine(None).await.unwrap(); - let block = provider.get_block(BlockId::default(), false).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::default(), false.into()).await.unwrap().unwrap(); assert!(block.header.timestamp >= timestamp.as_secs()); api.evm_mine(None).await.unwrap(); - let next = provider.get_block(BlockId::default(), false).await.unwrap().unwrap(); + let next = provider.get_block(BlockId::default(), false.into()).await.unwrap().unwrap(); assert!(next.header.timestamp > block.header.timestamp); } @@ -338,7 +338,7 @@ async fn test_evm_set_time_in_past() { // mine a block api.evm_mine(None).await.unwrap(); - let block = provider.get_block(BlockId::default(), false).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::default(), false.into()).await.unwrap().unwrap(); assert!(block.header.timestamp >= timestamp.as_secs()); assert!(block.header.timestamp < now.as_secs()); @@ -353,42 +353,44 @@ async fn test_timestamp_interval() { let interval = 10; for _ in 0..5 { - let block = provider.get_block(BlockId::default(), false).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::default(), false.into()).await.unwrap().unwrap(); // mock timestamp api.evm_set_block_timestamp_interval(interval).unwrap(); api.evm_mine(None).await.unwrap(); - let new_block = provider.get_block(BlockId::default(), false).await.unwrap().unwrap(); + let new_block = + provider.get_block(BlockId::default(), false.into()).await.unwrap().unwrap(); assert_eq!(new_block.header.timestamp, block.header.timestamp + interval); } - let block = provider.get_block(BlockId::default(), false).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::default(), false.into()).await.unwrap().unwrap(); let next_timestamp = block.header.timestamp + 50; api.evm_set_next_block_timestamp(next_timestamp).unwrap(); api.evm_mine(None).await.unwrap(); - let block = provider.get_block(BlockId::default(), false).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::default(), false.into()).await.unwrap().unwrap(); assert_eq!(block.header.timestamp, next_timestamp); api.evm_mine(None).await.unwrap(); - let block = provider.get_block(BlockId::default(), false).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::default(), false.into()).await.unwrap().unwrap(); // interval also works after setting the next timestamp manually assert_eq!(block.header.timestamp, next_timestamp + interval); assert!(api.evm_remove_block_timestamp_interval().unwrap()); api.evm_mine(None).await.unwrap(); - let new_block = provider.get_block(BlockId::default(), false).await.unwrap().unwrap(); + let new_block = provider.get_block(BlockId::default(), false.into()).await.unwrap().unwrap(); // offset is applied correctly after resetting the interval assert!(new_block.header.timestamp > block.header.timestamp); api.evm_mine(None).await.unwrap(); - let another_block = provider.get_block(BlockId::default(), false).await.unwrap().unwrap(); + let another_block = + provider.get_block(BlockId::default(), false.into()).await.unwrap().unwrap(); // check interval is disabled assert!(another_block.header.timestamp - new_block.header.timestamp < interval); } @@ -429,7 +431,8 @@ async fn can_get_node_info() { let provider = handle.http_provider(); let block_number = provider.get_block_number().await.unwrap(); - let block = provider.get_block(BlockId::from(block_number), false).await.unwrap().unwrap(); + let block = + provider.get_block(BlockId::from(block_number), false.into()).await.unwrap().unwrap(); let expected_node_info = NodeInfo { current_block_number: U64::from(0), @@ -463,7 +466,8 @@ async fn can_get_metadata() { let block_number = provider.get_block_number().await.unwrap(); let chain_id = provider.get_chain_id().await.unwrap(); - let block = provider.get_block(BlockId::from(block_number), false).await.unwrap().unwrap(); + let block = + provider.get_block(BlockId::from(block_number), false.into()).await.unwrap().unwrap(); let expected_metadata = AnvilMetadata { latest_block_hash: block.header.hash.unwrap(), @@ -488,7 +492,8 @@ async fn can_get_metadata_on_fork() { let block_number = provider.get_block_number().await.unwrap(); let chain_id = provider.get_chain_id().await.unwrap(); - let block = provider.get_block(BlockId::from(block_number), false).await.unwrap().unwrap(); + let block = + provider.get_block(BlockId::from(block_number), false.into()).await.unwrap().unwrap(); let expected_metadata = AnvilMetadata { latest_block_hash: block.header.hash.unwrap(), @@ -541,7 +546,7 @@ async fn test_get_transaction_receipt() { let receipt = provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); // the block should have the new base fee - let block = provider.get_block(BlockId::default(), false).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::default(), false.into()).await.unwrap().unwrap(); assert_eq!(block.header.base_fee_per_gas.unwrap(), new_base_fee.to::()); // mine blocks diff --git a/crates/anvil/tests/it/api.rs b/crates/anvil/tests/it/api.rs index 0f1d45d6f..b7d6f51c5 100644 --- a/crates/anvil/tests/it/api.rs +++ b/crates/anvil/tests/it/api.rs @@ -113,11 +113,14 @@ async fn can_get_block_by_number() { provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); - let block = provider.get_block(BlockId::number(1), true).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::number(1), true.into()).await.unwrap().unwrap(); assert_eq!(block.transactions.len(), 1); - let block = - provider.get_block(BlockId::hash(block.header.hash.unwrap()), true).await.unwrap().unwrap(); + let block = provider + .get_block(BlockId::hash(block.header.hash.unwrap()), true.into()) + .await + .unwrap() + .unwrap(); assert_eq!(block.transactions.len(), 1); } @@ -132,7 +135,7 @@ async fn can_get_pending_block() { let provider = connect_pubsub_with_signer(&handle.http_endpoint(), signer).await; - let block = provider.get_block(BlockId::pending(), false).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::pending(), false.into()).await.unwrap().unwrap(); assert_eq!(block.header.number.unwrap(), 1); let num = provider.get_block_number().await.unwrap(); @@ -147,12 +150,12 @@ async fn can_get_pending_block() { let num = provider.get_block_number().await.unwrap(); assert_eq!(num, 0); - let block = provider.get_block(BlockId::pending(), false).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::pending(), false.into()).await.unwrap().unwrap(); assert_eq!(block.header.number.unwrap(), 1); assert_eq!(block.transactions.len(), 1); assert_eq!(block.transactions, BlockTransactions::Hashes(vec![*pending.tx_hash()])); - let block = provider.get_block(BlockId::pending(), true).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::pending(), true.into()).await.unwrap().unwrap(); assert_eq!(block.header.number.unwrap(), 1); assert_eq!(block.transactions.len(), 1); } diff --git a/crates/anvil/tests/it/eip4844.rs b/crates/anvil/tests/it/eip4844.rs index c9ab2943c..1d0df312b 100644 --- a/crates/anvil/tests/it/eip4844.rs +++ b/crates/anvil/tests/it/eip4844.rs @@ -198,7 +198,7 @@ async fn can_check_blob_fields_on_genesis() { let provider = http_provider(&handle.http_endpoint()); - let block = provider.get_block(BlockId::latest(), false).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::latest(), false.into()).await.unwrap().unwrap(); assert_eq!(block.header.blob_gas_used, Some(0)); assert_eq!(block.header.excess_blob_gas, Some(0)); diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index 19377e845..3ef8665ea 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -482,8 +482,11 @@ async fn test_fork_timestamp() { let (api, handle) = spawn(fork_config()).await; let provider = handle.http_provider(); - let block = - provider.get_block(BlockId::Number(BLOCK_NUMBER.into()), false).await.unwrap().unwrap(); + let block = provider + .get_block(BlockId::Number(BLOCK_NUMBER.into()), false.into()) + .await + .unwrap() + .unwrap(); assert_eq!(block.header.timestamp, BLOCK_TIMESTAMP); let accounts: Vec<_> = handle.dev_wallets().collect(); @@ -493,10 +496,10 @@ async fn test_fork_timestamp() { TransactionRequest::default().to(Address::random()).value(U256::from(1337u64)).from(from); let tx = WithOtherFields::new(tx); let tx = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); - let status = tx.inner.inner.inner.receipt.status; + let status = tx.inner.inner.inner.receipt.status.coerce_status(); assert!(status); - let block = provider.get_block(BlockId::latest(), false).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::latest(), false.into()).await.unwrap().unwrap(); let elapsed = start.elapsed().as_secs() + 1; @@ -509,8 +512,11 @@ async fn test_fork_timestamp() { api.anvil_reset(Some(Forking { json_rpc_url: None, block_number: Some(BLOCK_NUMBER) })) .await .unwrap(); - let block = - provider.get_block(BlockId::Number(BLOCK_NUMBER.into()), false).await.unwrap().unwrap(); + let block = provider + .get_block(BlockId::Number(BLOCK_NUMBER.into()), false.into()) + .await + .unwrap() + .unwrap(); assert_eq!(block.header.timestamp, BLOCK_TIMESTAMP); let tx = @@ -518,7 +524,7 @@ async fn test_fork_timestamp() { let tx = WithOtherFields::new(tx); let _ = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); // FIXME: Awaits endlessly here. - let block = provider.get_block(BlockId::latest(), false).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::latest(), false.into()).await.unwrap().unwrap(); let elapsed = start.elapsed().as_secs() + 1; let diff = block.header.timestamp - BLOCK_TIMESTAMP; assert!(diff <= elapsed); @@ -534,7 +540,7 @@ async fn test_fork_timestamp() { let tx = WithOtherFields::new(tx); let _tx = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); - let block = provider.get_block(BlockId::latest(), false).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::latest(), false.into()).await.unwrap().unwrap(); assert_eq!(block.header.timestamp, BLOCK_TIMESTAMP + 1); let tx = @@ -542,7 +548,7 @@ async fn test_fork_timestamp() { let tx = WithOtherFields::new(tx); let _ = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); - let block = provider.get_block(BlockId::latest(), false).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::latest(), false.into()).await.unwrap().unwrap(); let elapsed = start.elapsed().as_secs() + 1; let diff = block.header.timestamp - (BLOCK_TIMESTAMP + 1); assert!(diff <= elapsed); @@ -621,7 +627,7 @@ async fn test_fork_nft_set_approve_all() { let tx = WithOtherFields::new(tx); api.anvil_impersonate_account(owner).await.unwrap(); let tx = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); - let status = tx.inner.inner.inner.receipt.status; + let status = tx.inner.inner.inner.receipt.status.coerce_status(); assert!(status); // transfer: impersonate real owner and transfer nft @@ -636,7 +642,7 @@ async fn test_fork_nft_set_approve_all() { .with_input(call.calldata().to_owned()); let tx = WithOtherFields::new(tx); let tx = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); - let status = tx.inner.inner.inner.receipt.status; + let status = tx.inner.inner.inner.receipt.status.coerce_status(); assert!(status); let real_owner = nouns.ownerOf(token_id).call().await.unwrap(); @@ -697,7 +703,7 @@ async fn test_fork_can_send_opensea_tx() { let tx = WithOtherFields::new(tx); let tx = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); - let status = tx.inner.inner.inner.receipt.status; + let status = tx.inner.inner.inner.receipt.status.coerce_status(); assert!(status); } @@ -725,7 +731,7 @@ async fn test_fork_init_base_fee() { let provider = handle.http_provider(); - let block = provider.get_block(BlockId::latest(), false).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::latest(), false.into()).await.unwrap().unwrap(); // assert_eq!(block.header.number.unwrap(), 13184859u64); let init_base_fee = block.header.base_fee_per_gas.unwrap(); @@ -733,7 +739,7 @@ async fn test_fork_init_base_fee() { api.mine_one().await; - let block = provider.get_block(BlockId::latest(), false).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::latest(), false.into()).await.unwrap().unwrap(); let next_base_fee = block.header.base_fee_per_gas.unwrap(); assert!(next_base_fee < init_base_fee); @@ -944,7 +950,7 @@ async fn can_impersonate_in_fork() { let res = provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); assert_eq!(res.from, token_holder); - let status = res.inner.inner.inner.receipt.status; + let status = res.inner.inner.inner.receipt.status.coerce_status(); assert!(status); let balance = provider.get_balance(to).await.unwrap(); @@ -964,7 +970,7 @@ async fn test_total_difficulty_fork() { let difficulty = U256::from(13_680_435_288_526_144u128); let provider = handle.http_provider(); - let block = provider.get_block(BlockId::latest(), false).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::latest(), false.into()).await.unwrap().unwrap(); assert_eq!(block.header.total_difficulty, Some(total_difficulty)); assert_eq!(block.header.difficulty, difficulty); @@ -973,7 +979,7 @@ async fn test_total_difficulty_fork() { let next_total_difficulty = total_difficulty + difficulty; - let block = provider.get_block(BlockId::latest(), false).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::latest(), false.into()).await.unwrap().unwrap(); assert_eq!(block.header.total_difficulty, Some(next_total_difficulty)); assert_eq!(block.header.difficulty, U256::ZERO); } @@ -1065,7 +1071,7 @@ async fn test_fork_reset_moonbeam() { let tx = WithOtherFields::new(tx); api.anvil_impersonate_account(from).await.unwrap(); let tx = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); - let status = tx.inner.inner.inner.receipt.status; + let status = tx.inner.inner.inner.receipt.status.coerce_status(); assert!(status); // reset to check timestamp works after resetting @@ -1080,7 +1086,7 @@ async fn test_fork_reset_moonbeam() { TransactionRequest::default().to(Address::random()).value(U256::from(1337u64)).from(from); let tx = WithOtherFields::new(tx); let tx = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); - let status = tx.inner.inner.inner.receipt.status; + let status = tx.inner.inner.inner.receipt.status.coerce_status(); assert!(status); } diff --git a/crates/anvil/tests/it/gas.rs b/crates/anvil/tests/it/gas.rs index bca7102cb..f7ab59004 100644 --- a/crates/anvil/tests/it/gas.rs +++ b/crates/anvil/tests/it/gas.rs @@ -27,7 +27,7 @@ async fn test_basefee_full_block() { provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); let base_fee = provider - .get_block(BlockId::latest(), false) + .get_block(BlockId::latest(), false.into()) .await .unwrap() .unwrap() @@ -38,7 +38,7 @@ async fn test_basefee_full_block() { provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); let next_base_fee = provider - .get_block(BlockId::latest(), false) + .get_block(BlockId::latest(), false.into()) .await .unwrap() .unwrap() @@ -77,7 +77,7 @@ async fn test_basefee_half_block() { provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); let next_base_fee = provider - .get_block(BlockId::latest(), false) + .get_block(BlockId::latest(), false.into()) .await .unwrap() .unwrap() @@ -104,7 +104,7 @@ async fn test_basefee_empty_block() { provider.send_transaction(tx.clone()).await.unwrap().get_receipt().await.unwrap(); let base_fee = provider - .get_block(BlockId::latest(), false) + .get_block(BlockId::latest(), false.into()) .await .unwrap() .unwrap() @@ -116,7 +116,7 @@ async fn test_basefee_empty_block() { api.mine_one().await; let next_base_fee = provider - .get_block(BlockId::latest(), false) + .get_block(BlockId::latest(), false.into()) .await .unwrap() .unwrap() diff --git a/crates/anvil/tests/it/traces.rs b/crates/anvil/tests/it/traces.rs index b06f7ce05..38f7a61a1 100644 --- a/crates/anvil/tests/it/traces.rs +++ b/crates/anvil/tests/it/traces.rs @@ -353,7 +353,7 @@ async fn test_trace_address_fork2() { api.anvil_impersonate_account(from).await.unwrap(); let tx = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); - let status = tx.inner.inner.inner.receipt.status; + let status = tx.inner.inner.inner.receipt.status.coerce_status(); assert!(status); let traces = provider.trace_transaction(tx.transaction_hash).await.unwrap(); diff --git a/crates/anvil/tests/it/transaction.rs b/crates/anvil/tests/it/transaction.rs index c80f7b2b3..74c82133a 100644 --- a/crates/anvil/tests/it/transaction.rs +++ b/crates/anvil/tests/it/transaction.rs @@ -90,7 +90,7 @@ async fn can_order_transactions() { let lower_price = tx_lower.get_receipt().await.unwrap().transaction_hash; // get the block, await receipts - let block = provider.get_block(BlockId::latest(), false).await.unwrap().unwrap(); + let block = provider.get_block(BlockId::latest(), false.into()).await.unwrap().unwrap(); assert_eq!(block.transactions, BlockTransactions::Hashes(vec![higher_price, lower_price])) } @@ -129,7 +129,7 @@ async fn can_respect_nonces() { // this will unblock the currently pending tx let higher_tx = higher_pending_tx.get_receipt().await.unwrap(); // Awaits endlessly here due to alloy/#389 - let block = provider.get_block(1.into(), false).await.unwrap().unwrap(); + let block = provider.get_block(1.into(), false.into()).await.unwrap().unwrap(); assert_eq!(2, block.transactions.len()); assert_eq!( BlockTransactions::Hashes(vec![tx.transaction_hash, higher_tx.transaction_hash]), @@ -170,7 +170,7 @@ async fn can_replace_transaction() { // mine exactly one block api.mine_one().await; - let block = provider.get_block(1.into(), false).await.unwrap().unwrap(); + let block = provider.get_block(1.into(), false.into()).await.unwrap().unwrap(); assert_eq!(block.transactions.len(), 1); assert_eq!(BlockTransactions::Hashes(vec![higher_tx_hash]), block.transactions); @@ -286,7 +286,7 @@ async fn can_reject_underpriced_replacement() { let higher_priced_receipt = higher_priced_pending_tx.get_receipt().await.unwrap(); // ensure that only the higher priced tx was mined - let block = provider.get_block(1.into(), false).await.unwrap().unwrap(); + let block = provider.get_block(1.into(), false.into()).await.unwrap().unwrap(); assert_eq!(1, block.transactions.len()); assert_eq!( BlockTransactions::Hashes(vec![higher_priced_receipt.transaction_hash]), @@ -552,7 +552,7 @@ async fn call_past_state() { assert_eq!(value._0, "initial value"); let hash = provider - .get_block(BlockId::Number(1.into()), false) + .get_block(BlockId::Number(1.into()), false.into()) .await .unwrap() .unwrap() diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index 421bd6d4d..ddcee1ba2 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -39,7 +39,7 @@ alloy-primitives.workspace = true alloy-rlp.workspace = true alloy-provider = { workspace = true, features = ["reqwest", "ws", "ipc"] } alloy-transport.workspace = true -alloy-rpc-types.workspace = true +alloy-rpc-types = { workspace = true, features = ["eth"] } alloy-json-rpc.workspace = true alloy-signer.workspace = true alloy-signer-wallet = { workspace = true, features = ["mnemonic", "keystore"] } diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index d9ba45726..ccc70fd63 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -113,7 +113,7 @@ impl RunArgs { tx.block_number.ok_or_else(|| eyre::eyre!("tx may still be pending: {:?}", tx_hash))?; // fetch the block the transaction was mined in - let block = provider.get_block(tx_block_number.into(), true).await?; + let block = provider.get_block(tx_block_number.into(), true.into()).await?; // we need to fork off the parent block config.fork_block_number = Some(tx_block_number - 1); diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index 51c435273..e17fd9d86 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -258,7 +258,7 @@ async fn main() -> Result<()> { let provider = utils::get_provider(&config)?; let number = match block { Some(id) => provider - .get_block(id, false) + .get_block(id, false.into()) .await? .ok_or_else(|| eyre::eyre!("block {id:?} not found"))? .header diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index 2c48aa783..002212fcd 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -333,7 +333,7 @@ where let block = self .provider - .get_block(block, full) + .get_block(block, full.into()) .await? .ok_or_else(|| eyre::eyre!("block {:?} not found", block))?; @@ -855,7 +855,8 @@ where Some(block) => match block { BlockId::Number(block_number) => Ok(Some(block_number)), BlockId::Hash(hash) => { - let block = self.provider.get_block_by_hash(hash.block_hash, false).await?; + let block = + self.provider.get_block_by_hash(hash.block_hash, false.into()).await?; Ok(block.map(|block| block.header.number.unwrap()).map(BlockNumberOrTag::from)) } }, diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 1a48e80ac..56f430c4a 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -22,7 +22,7 @@ alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } alloy-json-abi.workspace = true alloy-primitives = { workspace = true, features = ["serde", "getrandom", "arbitrary", "rlp"] } alloy-rpc-types-engine.workspace = true -alloy-rpc-types.workspace = true +alloy-rpc-types = { workspace = true, features = ["eth"] } alloy-rpc-client.workspace = true alloy-provider.workspace = true alloy-transport.workspace = true diff --git a/crates/common/src/fmt/ui.rs b/crates/common/src/fmt/ui.rs index f2f647ae8..63b2905a8 100644 --- a/crates/common/src/fmt/ui.rs +++ b/crates/common/src/fmt/ui.rs @@ -209,7 +209,7 @@ blobGasUsed {}", serde_json::to_string(&logs).unwrap(), logs_bloom.pretty(), state_root.pretty(), - pretty_status(*status), + pretty_status(status.coerce_status()), transaction_hash.pretty(), transaction_index.pretty(), transaction_type, @@ -424,7 +424,7 @@ pub fn get_pretty_tx_receipt_attr( "logsBloom" | "logs_bloom" => Some(receipt.receipt.inner.inner.inner.logs_bloom.pretty()), "root" | "stateRoot" | "state_root " => Some(receipt.receipt.state_root.pretty()), "status" | "statusCode" | "status_code" => { - Some(pretty_status(receipt.receipt.inner.inner.inner.receipt.status)) + Some(pretty_status(receipt.receipt.inner.inner.inner.receipt.status.coerce_status())) } "transactionHash" | "transaction_hash" => Some(receipt.receipt.transaction_hash.pretty()), "transactionIndex" | "transaction_index" => { diff --git a/crates/common/src/provider/retry.rs b/crates/common/src/provider/retry.rs index b6adfb646..b7f3079bb 100644 --- a/crates/common/src/provider/retry.rs +++ b/crates/common/src/provider/retry.rs @@ -30,24 +30,7 @@ impl RetryPolicy for RateLimitRetryPolicy { // The transport could not serialize the error itself. The request was malformed from // the start. TransportError::SerError(_) => false, - TransportError::DeserError { text, .. } => { - if let Ok(resp) = serde_json::from_str::(text) { - return should_retry_json_rpc_error(&resp) - } - - // some providers send invalid JSON RPC in the error case (no `id:u64`), but the - // text should be a `JsonRpcError` - #[derive(Deserialize)] - struct Resp { - error: ErrorPayload, - } - - if let Ok(resp) = serde_json::from_str::(text) { - return should_retry_json_rpc_error(&resp.error) - } - - false - } + TransportError::DeserError { text, .. } => should_retry_body(text), TransportError::ErrorResp(err) => should_retry_json_rpc_error(err), TransportError::NullResp => true, TransportError::UnsupportedFeature(_) => false, @@ -76,6 +59,26 @@ impl RetryPolicy for RateLimitRetryPolicy { } } +/// Tries to decode the error body as payload and check if it should be retried +fn should_retry_body(body: &str) -> bool { + if let Ok(resp) = serde_json::from_str::(body) { + return should_retry_json_rpc_error(&resp) + } + + // some providers send invalid JSON RPC in the error case (no `id:u64`), but the + // text should be a `JsonRpcError` + #[derive(Deserialize)] + struct Resp { + error: ErrorPayload, + } + + if let Ok(resp) = serde_json::from_str::(body) { + return should_retry_json_rpc_error(&resp.error) + } + + false +} + /// Analyzes the [TransportErrorKind] and decides if the request should be retried based on the /// variant. fn should_retry_transport_level_error(error: &TransportErrorKind) -> bool { @@ -88,8 +91,16 @@ fn should_retry_transport_level_error(error: &TransportErrorKind) -> bool { msg.contains("429 Too Many Requests") } + TransportErrorKind::HttpError(err) => { + if err.status == 429 { + return true + } + should_retry_body(&err.body) + } // If the backend is gone, or there's a completely custom error, we should assume it's not // retryable. + TransportErrorKind::PubsubUnavailable => false, + TransportErrorKind::BackendGone => false, _ => false, } } diff --git a/crates/common/src/transactions.rs b/crates/common/src/transactions.rs index 98244ddb4..50fcf7d99 100644 --- a/crates/common/src/transactions.rs +++ b/crates/common/src/transactions.rs @@ -20,7 +20,7 @@ pub struct TransactionReceiptWithRevertReason { impl TransactionReceiptWithRevertReason { /// Returns if the status of the transaction is 0 (failure) pub fn is_failure(&self) -> bool { - !self.receipt.inner.inner.inner.receipt.status + !self.receipt.inner.inner.inner.receipt.status.coerce_status() } /// Updates the revert reason field using `eth_call` and returns an Err variant if the revert diff --git a/crates/evm/core/src/fork/backend.rs b/crates/evm/core/src/fork/backend.rs index 4dda2a994..468be738d 100644 --- a/crates/evm/core/src/fork/backend.rs +++ b/crates/evm/core/src/fork/backend.rs @@ -234,8 +234,10 @@ where fn request_full_block(&mut self, number: BlockId, sender: FullBlockSender) { let provider = self.provider.clone(); let fut = Box::pin(async move { - let block = - provider.get_block(number, true).await.wrap_err("could not fetch block {number:?}"); + let block = provider + .get_block(number, true.into()) + .await + .wrap_err("could not fetch block {number:?}"); (sender, block, number) }); diff --git a/crates/script/src/receipts.rs b/crates/script/src/receipts.rs index 7189f8da4..a080e2e0d 100644 --- a/crates/script/src/receipts.rs +++ b/crates/script/src/receipts.rs @@ -15,7 +15,7 @@ pub enum TxStatus { impl From for TxStatus { fn from(receipt: AnyTransactionReceipt) -> Self { - if !receipt.inner.inner.inner.receipt.status { + if !receipt.inner.inner.inner.receipt.status.coerce_status() { Self::Revert(receipt) } else { Self::Success(receipt) @@ -57,7 +57,7 @@ pub fn format_receipt(chain: Chain, receipt: &AnyTransactionReceipt) -> String { 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 { + status = if !receipt.inner.inner.inner.receipt.status.coerce_status() { "❌ [Failed]" } else { "✅ [Success]" diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index ebe63adc5..f6d2ea419 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -286,7 +286,7 @@ impl VerifyBytecodeArgs { let mut executor = TracingExecutor::new(env.clone(), fork, Some(fork_config.evm_version), false); env.block.number = U256::from(simulation_block); - let block = provider.get_block(simulation_block.into(), true).await?; + let block = provider.get_block(simulation_block.into(), true.into()).await?; // Workaround for the NonceTooHigh issue as we're not simulating prior txs of the same // block. From c2b5b500b83c35b2384b098842b50e364319b4b8 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 13 Jun 2024 01:28:14 +0200 Subject: [PATCH 398/430] perf: don't unnecessarily create CheatsConfig (#8143) --- crates/cheatcodes/src/inspector.rs | 51 +++++++++++++++++++++++++----- crates/cheatcodes/src/script.rs | 4 +-- crates/cheatcodes/src/utils.rs | 4 +-- 3 files changed, 47 insertions(+), 12 deletions(-) diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 968f2b4d7..449ebdaae 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -106,7 +106,7 @@ pub type BroadcastableTransactions = VecDeque; /// contract deployed on the live network is able to execute cheatcodes by simply calling the /// cheatcode address: by default, the caller, test contract and newly deployed contracts are /// allowed to execute cheatcodes -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug)] pub struct Cheatcodes { /// The block environment /// @@ -123,9 +123,6 @@ pub struct Cheatcodes { /// Address labels pub labels: HashMap, - /// Remembered private keys - pub script_wallets: Option, - /// Prank information pub prank: Option, @@ -210,13 +207,51 @@ pub struct Cheatcodes { pub breakpoints: Breakpoints, } +// This is not derived because calling this in `fn new` with `..Default::default()` creates a second +// `CheatsConfig` which is unused, and inside it `ProjectPathsConfig` is relatively expensive to +// create. +impl Default for Cheatcodes { + fn default() -> Self { + Self::new(Arc::default()) + } +} + impl Cheatcodes { /// Creates a new `Cheatcodes` with the given settings. - #[inline] pub fn new(config: Arc) -> Self { - let labels = config.labels.clone(); - let script_wallets = config.script_wallets.clone(); - Self { config, fs_commit: true, labels, script_wallets, ..Default::default() } + Self { + fs_commit: true, + labels: config.labels.clone(), + config, + block: Default::default(), + gas_price: Default::default(), + prank: Default::default(), + expected_revert: Default::default(), + fork_revert_diagnostic: Default::default(), + accesses: Default::default(), + recorded_account_diffs_stack: Default::default(), + recorded_logs: Default::default(), + last_call_gas: Default::default(), + mocked_calls: Default::default(), + expected_calls: Default::default(), + expected_emits: Default::default(), + allowed_mem_writes: Default::default(), + broadcast: Default::default(), + broadcastable_transactions: Default::default(), + context: Default::default(), + serialized_jsons: Default::default(), + eth_deals: Default::default(), + gas_metering: Default::default(), + gas_metering_create: Default::default(), + mapping_slots: Default::default(), + pc: Default::default(), + breakpoints: Default::default(), + } + } + + /// Returns the configured script wallets. + pub fn script_wallets(&self) -> Option<&ScriptWallets> { + self.config.script_wallets.as_ref() } fn apply_cheatcode( diff --git a/crates/cheatcodes/src/script.rs b/crates/cheatcodes/src/script.rs index c9195f449..97c65106a 100644 --- a/crates/cheatcodes/src/script.rs +++ b/crates/cheatcodes/src/script.rs @@ -137,7 +137,7 @@ fn broadcast( let mut new_origin = new_origin.cloned(); if new_origin.is_none() { - if let Some(script_wallets) = &ccx.state.script_wallets { + if let Some(script_wallets) = ccx.state.script_wallets() { let mut script_wallets = script_wallets.inner.lock(); if let Some(provided_sender) = script_wallets.provided_sender { new_origin = Some(provided_sender); @@ -176,7 +176,7 @@ fn broadcast_key( let result = broadcast(ccx, Some(&new_origin), single_call); if result.is_ok() { - if let Some(script_wallets) = &ccx.state.script_wallets { + if let Some(script_wallets) = ccx.state.script_wallets() { script_wallets.add_local_signer(wallet); } } diff --git a/crates/cheatcodes/src/utils.rs b/crates/cheatcodes/src/utils.rs index 4785e0197..f4f3ab485 100644 --- a/crates/cheatcodes/src/utils.rs +++ b/crates/cheatcodes/src/utils.rs @@ -92,7 +92,7 @@ impl Cheatcode for rememberKeyCall { let Self { privateKey } = self; let wallet = parse_wallet(privateKey)?; let address = wallet.address(); - if let Some(script_wallets) = &ccx.state.script_wallets { + if let Some(script_wallets) = ccx.state.script_wallets() { script_wallets.add_local_signer(wallet); } Ok(address.abi_encode()) @@ -214,7 +214,7 @@ pub(super) fn sign_with_wallet( signer: Option
, digest: &B256, ) -> Result { - let Some(script_wallets) = &ccx.state.script_wallets else { + let Some(script_wallets) = ccx.state.script_wallets() else { bail!("no wallets are available"); }; From 88011569efcebc9152267be217c8cfbedc32c07c Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 13 Jun 2024 01:28:42 +0200 Subject: [PATCH 399/430] chore: improve test timings and cleanup (#8144) --- crates/forge/bin/cmd/snapshot.rs | 4 +- crates/forge/src/multi_runner.rs | 8 ++-- crates/forge/src/result.rs | 34 ++++++++-------- crates/forge/src/runner.rs | 68 ++++++++++++-------------------- 4 files changed, 47 insertions(+), 67 deletions(-) diff --git a/crates/forge/bin/cmd/snapshot.rs b/crates/forge/bin/cmd/snapshot.rs index 8cb5fbb06..d35c1cdcb 100644 --- a/crates/forge/bin/cmd/snapshot.rs +++ b/crates/forge/bin/cmd/snapshot.rs @@ -214,7 +214,7 @@ impl FromStr for SnapshotEntry { Some(Self { contract_name: file.as_str().to_string(), signature: sig.as_str().to_string(), - gas_used: TestKindReport::Standard { + gas_used: TestKindReport::Unit { gas: gas.as_str().parse().unwrap(), }, }) @@ -455,7 +455,7 @@ mod tests { SnapshotEntry { contract_name: "Test".to_string(), signature: "deposit()".to_string(), - gas_used: TestKindReport::Standard { gas: 7222 } + gas_used: TestKindReport::Unit { gas: 7222 } } ); } diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 1c1206fa0..270a25202 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -184,7 +184,7 @@ impl MultiContractRunner { let _guard = handle.enter(); tests_progress.inner.lock().start_suite_progress(&id.identifier()); - let result = self.run_tests( + let result = self.run_test_suite( id, contract, db.clone(), @@ -210,13 +210,13 @@ impl MultiContractRunner { } else { contracts.par_iter().for_each(|&(id, contract)| { let _guard = handle.enter(); - let result = self.run_tests(id, contract, db.clone(), filter, &handle, None); + let result = self.run_test_suite(id, contract, db.clone(), filter, &handle, None); let _ = tx.send((id.identifier(), result)); }) } } - fn run_tests( + fn run_test_suite( &self, artifact_id: &ArtifactId, contract: &TestContract, @@ -252,7 +252,7 @@ impl MultiContractRunner { if !enabled!(tracing::Level::TRACE) { span_name = get_contract_name(&identifier); } - let _guard = debug_span!("run_suite", name = span_name).entered(); + let _guard = debug_span!("suite", name = span_name).entered(); debug!("start executing all tests in contract"); diff --git a/crates/forge/src/result.rs b/crates/forge/src/result.rs index 9b4a926aa..84c5cdea8 100644 --- a/crates/forge/src/result.rs +++ b/crates/forge/src/result.rs @@ -434,15 +434,15 @@ impl TestResult { /// Data report by a test. #[derive(Clone, Debug, PartialEq, Eq)] pub enum TestKindReport { - Standard { gas: u64 }, + Unit { gas: u64 }, Fuzz { runs: usize, mean_gas: u64, median_gas: u64 }, Invariant { runs: usize, calls: usize, reverts: usize }, } impl fmt::Display for TestKindReport { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Standard { gas } => { + match *self { + Self::Unit { gas } => { write!(f, "(gas: {gas})") } Self::Fuzz { runs, mean_gas, median_gas } => { @@ -458,10 +458,10 @@ impl fmt::Display for TestKindReport { impl TestKindReport { /// Returns the main gas value to compare against pub fn gas(&self) -> u64 { - match self { - Self::Standard { gas } => *gas, + match *self { + Self::Unit { gas } => gas, // We use the median for comparisons - Self::Fuzz { median_gas, .. } => *median_gas, + Self::Fuzz { median_gas, .. } => median_gas, // We return 0 since it's not applicable Self::Invariant { .. } => 0, } @@ -471,11 +471,9 @@ impl TestKindReport { /// Various types of tests #[derive(Clone, Debug, Serialize, Deserialize)] pub enum TestKind { - /// A standard test that consists of calling the defined solidity function - /// - /// Holds the consumed gas - Standard(u64), - /// A solidity fuzz test, that stores all test cases + /// A unit test. + Unit { gas: u64 }, + /// A fuzz test. Fuzz { /// we keep this for the debugger first_case: FuzzCase, @@ -483,26 +481,26 @@ pub enum TestKind { mean_gas: u64, median_gas: u64, }, - /// A solidity invariant test, that stores all test cases + /// An invariant test. Invariant { runs: usize, calls: usize, reverts: usize }, } impl Default for TestKind { fn default() -> Self { - Self::Standard(0) + Self::Unit { gas: 0 } } } impl TestKind { /// The gas consumed by this test pub fn report(&self) -> TestKindReport { - match self { - Self::Standard(gas) => TestKindReport::Standard { gas: *gas }, - Self::Fuzz { runs, mean_gas, median_gas, .. } => { - TestKindReport::Fuzz { runs: *runs, mean_gas: *mean_gas, median_gas: *median_gas } + match *self { + Self::Unit { gas } => TestKindReport::Unit { gas }, + Self::Fuzz { first_case: _, runs, mean_gas, median_gas } => { + TestKindReport::Fuzz { runs, mean_gas, median_gas } } Self::Invariant { runs, calls, reverts } => { - TestKindReport::Invariant { runs: *runs, calls: *calls, reverts: *reverts } + TestKindReport::Invariant { runs, calls, reverts } } } } diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 46a6cad68..51d904376 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -103,15 +103,15 @@ impl<'a> ContractRunner<'a> { impl<'a> ContractRunner<'a> { /// Deploys the test contract inside the runner from the sending account, and optionally runs /// the `setUp` function on the test contract. - pub fn setup(&mut self, setup: bool) -> TestSetup { - match self._setup(setup) { + pub fn setup(&mut self, call_setup: bool) -> TestSetup { + match self._setup(call_setup) { Ok(setup) => setup, Err(err) => TestSetup::failed(err.to_string()), } } - fn _setup(&mut self, setup: bool) -> Result { - trace!(?setup, "Setting test contract"); + fn _setup(&mut self, call_setup: bool) -> Result { + trace!(call_setup, "setting up"); // We max out their balance so that they can deploy and make calls. self.executor.set_balance(self.sender, U256::MAX)?; @@ -173,12 +173,12 @@ impl<'a> ContractRunner<'a> { self.executor.deploy_create2_deployer()?; // Optionally call the `setUp` function - let setup = if setup { - trace!("setting up"); + let result = 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!(contract=%address, "successfully setUp test"); + trace!(%address, "successfully called setUp"); (logs, traces, labels, None, coverage) } Err(EvmError::Execution(err)) => { @@ -215,7 +215,7 @@ impl<'a> ContractRunner<'a> { ) }; - Ok(setup) + Ok(result) } /// Collect fixtures from test contract. @@ -235,7 +235,8 @@ impl<'a> ContractRunner<'a> { /// current test. fn fuzz_fixtures(&mut self, address: Address) -> FuzzFixtures { let mut fixtures = HashMap::new(); - self.contract.abi.functions().filter(|func| func.is_fixture()).for_each(|func| { + let fixture_functions = self.contract.abi.functions().filter(|func| func.is_fixture()); + for func in fixture_functions { if func.inputs.is_empty() { // Read fixtures declared as functions. if let Ok(CallResult { raw: _, decoded_result }) = @@ -267,8 +268,7 @@ impl<'a> ContractRunner<'a> { } fixtures.insert(fixture_name(func.name.clone()), DynSolValue::Array(vals)); }; - }); - + } FuzzFixtures::new(fixtures) } @@ -287,7 +287,8 @@ impl<'a> ContractRunner<'a> { let setup_fns: Vec<_> = self.contract.abi.functions().filter(|func| func.name.is_setup()).collect(); - let needs_setup = setup_fns.len() == 1 && setup_fns[0].name == "setUp"; + // Whether to call the `setUp` function. + let call_setup = setup_fns.len() == 1 && setup_fns[0].name == "setUp"; // There is a single miss-cased `setUp` function, so we add a warning for &setup_fn in setup_fns.iter() { @@ -312,11 +313,13 @@ impl<'a> ContractRunner<'a> { let has_invariants = self.contract.abi.functions().any(|func| func.is_invariant_test()); // Invariant testing requires tracing to figure out what contracts were created. - let tmp_tracing = self.executor.inspector.tracer.is_none() && has_invariants && needs_setup; + let tmp_tracing = self.executor.inspector.tracer.is_none() && has_invariants && call_setup; if tmp_tracing { self.executor.set_tracing(true); } - let setup = self.setup(needs_setup); + let setup_time = Instant::now(); + let setup = self.setup(call_setup); + debug!("finished setting up in {:?}", setup_time.elapsed()); if tmp_tracing { self.executor.set_tracing(false); } @@ -330,10 +333,8 @@ impl<'a> ContractRunner<'a> { TestResult { status: TestStatus::Failure, reason: setup.reason, - counterexample: None, decoded_logs: decode_console_logs(&setup.logs), logs: setup.logs, - kind: TestKind::Standard(0), traces: setup.traces, coverage: setup.coverage, labeled_addresses: setup.labeled_addresses, @@ -367,6 +368,8 @@ impl<'a> ContractRunner<'a> { let test_results = functions .par_iter() .map(|&func| { + let start = Instant::now(); + let _guard = handle.enter(); let sig = func.signature(); @@ -381,7 +384,7 @@ impl<'a> ContractRunner<'a> { let setup = setup.clone(); let should_fail = func.is_test_fail(); - let res = if func.is_invariant_test() { + let mut res = if func.is_invariant_test() { let runner = test_options.invariant_runner(self.name, &func.name); let invariant_config = test_options.invariant_config(self.name, &func.name); @@ -404,6 +407,8 @@ impl<'a> ContractRunner<'a> { self.run_test(func, should_fail, setup) }; + res.duration = start.elapsed(); + (sig, res) }) .collect::>(); @@ -433,7 +438,6 @@ impl<'a> ContractRunner<'a> { // Run unit test let mut executor = self.executor.clone(); - let start: Instant = Instant::now(); let (raw_call_result, reason) = match executor.execute_test( self.sender, address, @@ -451,8 +455,6 @@ impl<'a> ContractRunner<'a> { decoded_logs: decode_console_logs(&logs), traces, labeled_addresses, - kind: TestKind::Standard(0), - duration: start.elapsed(), ..Default::default() } } @@ -463,8 +465,6 @@ impl<'a> ContractRunner<'a> { decoded_logs: decode_console_logs(&logs), traces, labeled_addresses, - kind: TestKind::Standard(0), - duration: start.elapsed(), ..Default::default() } } @@ -498,10 +498,6 @@ impl<'a> ContractRunner<'a> { should_fail, ); - // Record test execution time - let duration = start.elapsed(); - trace!(?duration, gas, reverted, should_fail, success); - TestResult { status: match success { true => TestStatus::Success, @@ -511,13 +507,13 @@ impl<'a> ContractRunner<'a> { counterexample: None, decoded_logs: decode_console_logs(&logs), logs, - kind: TestKind::Standard(gas.overflowing_sub(stipend).0), + kind: TestKind::Unit { gas: gas.wrapping_sub(stipend) }, traces, coverage, labeled_addresses, debug: debug_arena, breakpoints, - duration, + duration: std::time::Duration::default(), gas_report_traces: Vec::new(), } } @@ -532,12 +528,10 @@ impl<'a> ContractRunner<'a> { known_contracts: &ContractsByArtifact, identified_contracts: &ContractsByAddress, ) -> TestResult { - trace!(target: "forge::test::fuzz", "executing invariant test for {:?}", func.name); let TestSetup { address, logs, traces, labeled_addresses, coverage, fuzz_fixtures, .. } = setup; // First, run the test normally to see if it needs to be skipped. - let start = Instant::now(); if let Err(EvmError::SkipError) = self.executor.clone().execute_test( self.sender, address, @@ -554,7 +548,6 @@ impl<'a> ContractRunner<'a> { labeled_addresses, kind: TestKind::Invariant { runs: 1, calls: 1, reverts: 1 }, coverage, - duration: start.elapsed(), ..Default::default() } }; @@ -631,7 +624,6 @@ impl<'a> ContractRunner<'a> { coverage, counterexample: Some(CounterExample::Sequence(call_sequence)), kind: TestKind::Invariant { runs: 1, calls: 1, reverts: 1 }, - duration: start.elapsed(), ..Default::default() } } @@ -654,7 +646,6 @@ impl<'a> ContractRunner<'a> { traces, labeled_addresses, kind: TestKind::Invariant { runs: 0, calls: 0, reverts: 0 }, - duration: start.elapsed(), ..Default::default() } } @@ -739,7 +730,6 @@ impl<'a> ContractRunner<'a> { coverage, traces, labeled_addresses: labeled_addresses.clone(), - duration: start.elapsed(), gas_report_traces, ..Default::default() // TODO collect debug traces on the last run or error } @@ -766,7 +756,6 @@ impl<'a> ContractRunner<'a> { // Run fuzz test let progress = start_fuzz_progress(self.progress, self.name, &func.name, fuzz_config.runs); - let start = Instant::now(); let fuzzed_executor = FuzzedExecutor::new( self.executor.clone(), runner.clone(), @@ -790,15 +779,12 @@ impl<'a> ContractRunner<'a> { if let Some("SKIPPED") = result.reason.as_deref() { return TestResult { status: TestStatus::Skipped, - reason: None, decoded_logs: decode_console_logs(&logs), traces, labeled_addresses, - kind: TestKind::Standard(0), debug, breakpoints, coverage, - duration: start.elapsed(), ..Default::default() } } @@ -853,10 +839,6 @@ impl<'a> ContractRunner<'a> { traces.extend(result.traces.map(|traces| (TraceKind::Execution, traces))); coverage = merge_coverages(coverage, result.coverage); - // Record test execution time - let duration = start.elapsed(); - trace!(?duration, success = %result.success); - TestResult { status: match result.success { true => TestStatus::Success, @@ -872,7 +854,7 @@ impl<'a> ContractRunner<'a> { labeled_addresses, debug, breakpoints, - duration, + duration: std::time::Duration::default(), gas_report_traces: result.gas_report_traces.into_iter().map(|t| vec![t]).collect(), } } From 8e9cb1daf164a526c33d4c72de7a6325fc40bbf7 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 13 Jun 2024 03:46:58 +0200 Subject: [PATCH 400/430] chore(evm): clean up executor methods some more (#8104) * chore(evm): clean up executor methods some more * fix: rename _committing to transact_, further clarify the separation * chore: simplify is_success further * chore: don't clone executor in unit tests --- crates/cast/bin/cmd/call.rs | 2 +- crates/cast/bin/cmd/run.rs | 4 +- crates/chisel/src/runner.rs | 2 +- crates/evm/evm/src/executors/fuzz/mod.rs | 11 +- crates/evm/evm/src/executors/invariant/mod.rs | 10 +- .../evm/evm/src/executors/invariant/replay.rs | 2 +- .../evm/evm/src/executors/invariant/result.rs | 10 +- .../evm/evm/src/executors/invariant/shrink.rs | 15 +- crates/evm/evm/src/executors/mod.rs | 350 +++++++++--------- crates/forge/src/runner.rs | 18 +- crates/script/src/runner.rs | 4 +- crates/verify/src/bytecode.rs | 6 +- 12 files changed, 196 insertions(+), 238 deletions(-) diff --git a/crates/cast/bin/cmd/call.rs b/crates/cast/bin/cmd/call.rs index afd6108c6..5f4975fcc 100644 --- a/crates/cast/bin/cmd/call.rs +++ b/crates/cast/bin/cmd/call.rs @@ -170,7 +170,7 @@ impl CallArgs { TraceResult::try_from(deploy_result)? } TxKind::Call(to) => TraceResult::from_raw( - executor.call_raw_committing(sender, to, input, value)?, + executor.transact_raw(sender, to, input, value)?, TraceKind::Execution, ), }; diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index ccc70fd63..451964045 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -176,7 +176,7 @@ impl RunArgs { if let Some(to) = tx.to { trace!(tx=?tx.hash,?to, "executing previous call transaction"); - executor.commit_tx_with_env(env.clone()).wrap_err_with(|| { + executor.transact_with_env(env.clone()).wrap_err_with(|| { format!( "Failed to execute transaction: {:?} in block {}", tx.hash, env.block.number @@ -213,7 +213,7 @@ impl RunArgs { if let Some(to) = tx.to { trace!(tx=?tx.hash, to=?to, "executing call transaction"); - TraceResult::from_raw(executor.commit_tx_with_env(env)?, TraceKind::Execution) + TraceResult::from_raw(executor.transact_with_env(env)?, TraceKind::Execution) } else { trace!(tx=?tx.hash, "executing create transaction"); TraceResult::try_from(executor.deploy_with_env(env, None))? diff --git a/crates/chisel/src/runner.rs b/crates/chisel/src/runner.rs index 6ffcb6770..ff4c9695e 100644 --- a/crates/chisel/src/runner.rs +++ b/crates/chisel/src/runner.rs @@ -189,7 +189,7 @@ impl ChiselRunner { if commit { // if explicitly requested we can now commit the call - res = self.executor.call_raw_committing(from, to, calldata, value)?; + res = self.executor.transact_raw(from, to, calldata, value)?; } let RawCallResult { result, reverted, logs, traces, labels, chisel_state, .. } = res; diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index 6cd411f19..b578a65f3 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -16,7 +16,7 @@ use foundry_evm_fuzz::{ use foundry_evm_traces::CallTraceArena; use indicatif::ProgressBar; use proptest::test_runner::{TestCaseError, TestError, TestRunner}; -use std::{borrow::Cow, cell::RefCell}; +use std::cell::RefCell; mod types; pub use types::{CaseOutcome, CounterExampleOutcome, FuzzOutcome}; @@ -202,7 +202,6 @@ impl FuzzedExecutor { .executor .call_raw(self.sender, address, calldata.clone(), U256::ZERO) .map_err(|_| TestCaseError::fail(FuzzError::FailedContractCall))?; - let state_changeset = call.state_changeset.take().unwrap(); // When the `assume` cheatcode is called it returns a special string if call.result.as_ref() == MAGIC_ASSUME { @@ -214,13 +213,7 @@ impl FuzzedExecutor { .as_ref() .map_or_else(Default::default, |cheats| cheats.breakpoints.clone()); - let success = self.executor.is_raw_call_success( - address, - Cow::Owned(state_changeset), - &call, - should_fail, - ); - + let success = self.executor.is_raw_call_mut_success(address, &mut call, should_fail); if success { Ok(FuzzOutcome::Case(CaseOutcome { case: FuzzCase { calldata, gas: call.gas_used, stipend: call.stipend }, diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 6ca49545c..11291f173 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -155,7 +155,7 @@ impl<'a> InvariantExecutor<'a> { let failures = RefCell::new(InvariantFailures::new()); // Stores the calldata in the last run. - let last_run_calldata: RefCell> = RefCell::new(vec![]); + let last_run_inputs: RefCell> = RefCell::new(vec![]); // Stores additional traces for gas report. let gas_report_traces: RefCell>> = RefCell::default(); @@ -212,7 +212,7 @@ impl<'a> InvariantExecutor<'a> { // Execute call from the randomly generated sequence and commit state changes. let call_result = executor - .call_raw_committing( + .transact_raw( tx.sender, tx.call_details.target, tx.call_details.calldata.clone(), @@ -233,7 +233,7 @@ impl<'a> InvariantExecutor<'a> { } } else { // Collect data for fuzzing from the state changeset. - let mut state_changeset = call_result.state_changeset.to_owned().unwrap(); + let mut state_changeset = call_result.state_changeset.clone().unwrap(); if !call_result.reverted { collect_data( @@ -281,7 +281,7 @@ impl<'a> InvariantExecutor<'a> { .map_err(|e| TestCaseError::fail(e.to_string()))?; if !result.can_continue || current_run == self.config.depth - 1 { - last_run_calldata.borrow_mut().clone_from(&inputs); + last_run_inputs.borrow_mut().clone_from(&inputs); } if !result.can_continue { @@ -335,7 +335,7 @@ impl<'a> InvariantExecutor<'a> { error, cases: fuzz_cases.into_inner(), reverts, - last_run_inputs: last_run_calldata.take(), + last_run_inputs: last_run_inputs.into_inner(), gas_report_traces: gas_report_traces.into_inner(), }) } diff --git a/crates/evm/evm/src/executors/invariant/replay.rs b/crates/evm/evm/src/executors/invariant/replay.rs index 466ae0cf1..c21c26030 100644 --- a/crates/evm/evm/src/executors/invariant/replay.rs +++ b/crates/evm/evm/src/executors/invariant/replay.rs @@ -37,7 +37,7 @@ pub fn replay_run( // Replay each call from the sequence, collect logs, traces and coverage. for tx in inputs.iter() { - let call_result = executor.call_raw_committing( + let call_result = executor.transact_raw( tx.sender, tx.call_details.target, tx.call_details.calldata.clone(), diff --git a/crates/evm/evm/src/executors/invariant/result.rs b/crates/evm/evm/src/executors/invariant/result.rs index 2d08efb12..9b3103b3d 100644 --- a/crates/evm/evm/src/executors/invariant/result.rs +++ b/crates/evm/evm/src/executors/invariant/result.rs @@ -68,12 +68,8 @@ pub(crate) fn assert_invariants( U256::ZERO, )?; - let success = executor.is_raw_call_success( - invariant_contract.address, - Cow::Owned(call_result.state_changeset.take().unwrap()), - &call_result, - false, - ); + let success = + executor.is_raw_call_mut_success(invariant_contract.address, &mut call_result, false); if !success { // We only care about invariants which we haven't broken yet. if invariant_failures.error.is_none() { @@ -111,7 +107,7 @@ pub(crate) fn can_continue( let mut call_results = None; let handlers_succeeded = || { - targeted_contracts.targets.lock().iter().all(|(address, ..)| { + targeted_contracts.targets.lock().keys().all(|address| { executor.is_success(*address, false, Cow::Borrowed(state_changeset), false) }) }; diff --git a/crates/evm/evm/src/executors/invariant/shrink.rs b/crates/evm/evm/src/executors/invariant/shrink.rs index d61a84592..169955551 100644 --- a/crates/evm/evm/src/executors/invariant/shrink.rs +++ b/crates/evm/evm/src/executors/invariant/shrink.rs @@ -4,7 +4,7 @@ use foundry_evm_core::constants::CALLER; use foundry_evm_fuzz::invariant::BasicTxDetails; use indicatif::ProgressBar; use proptest::bits::{BitSetLike, VarBitSet}; -use std::{borrow::Cow, cmp::min}; +use std::cmp::min; #[derive(Clone, Copy, Debug)] struct Shrink { @@ -145,7 +145,7 @@ pub fn check_sequence( // Apply the call sequence. for call_index in sequence { let tx = &calls[call_index]; - let call_result = executor.call_raw_committing( + let call_result = executor.transact_raw( tx.sender, tx.call_details.target, tx.call_details.calldata.clone(), @@ -160,13 +160,6 @@ pub fn check_sequence( // Check the invariant for call sequence. let mut call_result = executor.call_raw(CALLER, test_address, calldata, U256::ZERO)?; - Ok(( - executor.is_raw_call_success( - test_address, - Cow::Owned(call_result.state_changeset.take().unwrap()), - &call_result, - false, - ), - true, - )) + let success = executor.is_raw_call_mut_success(test_address, &mut call_result, false); + Ok((success, true)) } diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index ceb269a53..58057c93f 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -54,14 +54,17 @@ sol! { } } -/// A type that can execute calls +/// EVM executor. /// /// The executor can be configured with various `revm::Inspector`s, like `Cheatcodes`. /// -/// There are two ways of executing calls: -/// - `committing`: any state changes made during the call are recorded and are persisting -/// - `raw`: state changes only exist for the duration of the call and are discarded afterwards, in -/// other words: the state of the underlying database remains unchanged. +/// There are multiple ways of interacting the EVM: +/// - `call`: executes a transaction, but does not persist any state changes; similar to `eth_call`, +/// where the EVM state is unchanged after the call. +/// - `transact`: executes a transaction and persists the state changes +/// - `deploy`: a special case of `transact`, specialized for persisting the state of a contract +/// deployment +/// - `setup`: a special case of `transact`, used to set up the environment for a test #[derive(Clone, Debug)] pub struct Executor { /// The underlying `revm::Database` that contains the EVM storage. @@ -81,6 +84,7 @@ pub struct Executor { } impl Executor { + /// Creates a new `Executor` with the given arguments. #[inline] pub fn new( mut backend: Backend, @@ -104,7 +108,7 @@ impl Executor { Self { backend, env, inspector, gas_limit } } - /// Returns the spec id of the executor + /// Returns the spec ID of the executor. pub fn spec_id(&self) -> SpecId { self.env.handler_cfg.spec_id } @@ -153,17 +157,16 @@ impl Executor { pub fn set_nonce(&mut self, address: Address, nonce: u64) -> DatabaseResult<&mut Self> { let mut account = self.backend.basic_ref(address)?.unwrap_or_default(); account.nonce = nonce; - self.backend.insert_account_info(address, account); Ok(self) } - /// Gets the nonce of an account + /// Returns the nonce of an account. pub fn get_nonce(&self, address: Address) -> DatabaseResult { Ok(self.backend.basic_ref(address)?.map(|acc| acc.nonce).unwrap_or_default()) } - /// Returns true if account has no code. + /// Returns `true` if the account has no code. pub fn is_empty_code(&self, address: Address) -> DatabaseResult { Ok(self.backend.basic_ref(address)?.map(|acc| acc.is_empty_code_hash()).unwrap_or(true)) } @@ -192,6 +195,54 @@ impl Executor { self } + /// Deploys a contract and commits the new state to the underlying database. + /// + /// Executes a CREATE transaction with the contract `code` and persistent database state + /// modifications. + pub fn deploy( + &mut self, + from: Address, + code: Bytes, + value: U256, + rd: Option<&RevertDecoder>, + ) -> Result { + let env = self.build_test_env(from, TransactTo::Create, code, value); + self.deploy_with_env(env, rd) + } + + /// Deploys a contract using the given `env` and commits the new state to the underlying + /// database. + /// + /// # Panics + /// + /// Panics if `env.tx.transact_to` is not `TransactTo::Create(_)`. + pub fn deploy_with_env( + &mut self, + env: EnvWithHandlerCfg, + rd: Option<&RevertDecoder>, + ) -> Result { + assert!( + matches!(env.tx.transact_to, TransactTo::Create), + "Expected create transaction, got {:?}", + env.tx.transact_to + ); + trace!(sender=%env.tx.caller, "deploying contract"); + + let mut result = self.transact_with_env(env)?; + result = result.into_result(rd)?; + let Some(Output::Create(_, Some(address))) = result.out else { + panic!("Deployment succeeded, but no address was returned: {result:#?}"); + }; + + // also mark this library as persistent, this will ensure that the state of the library is + // persistent across fork swaps in forking mode + self.backend.add_persistent_account(address); + + debug!(%address, "deployed contract"); + + Ok(DeployResult { raw: result, address }) + } + /// Calls the `setUp()` function on a contract. /// /// This will commit any state changes to the underlying database. @@ -209,7 +260,7 @@ impl Executor { let from = from.unwrap_or(CALLER); self.backend.set_test_contract(to).set_caller(from); let calldata = Bytes::from_static(&ITest::setUpCall::SELECTOR); - let mut res = self.call_raw_committing(from, to, calldata, U256::ZERO)?; + let mut res = self.transact_raw(from, to, calldata, U256::ZERO)?; res = res.into_result(rd)?; // record any changes made to the block's environment during setup @@ -217,10 +268,8 @@ impl Executor { // and also the chainid, which can be set manually self.env.cfg.chain_id = res.env.cfg.chain_id; - if let Some(changeset) = res.state_changeset.as_ref() { - let success = self - .ensure_success(to, res.reverted, Cow::Borrowed(changeset), false) - .map_err(|err| EvmError::Eyre(eyre::eyre!(err)))?; + if let Some(changeset) = &res.state_changeset { + let success = self.is_raw_call_success(to, Cow::Borrowed(changeset), &res, false); if !success { return Err(res.into_execution_error("execution error".to_string()).into()); } @@ -230,10 +279,8 @@ impl Executor { } /// Performs a call to an account on the current state of the VM. - /// - /// The state after the call is persisted. - pub fn call_committing( - &mut self, + pub fn call( + &self, from: Address, to: Address, func: &Function, @@ -242,49 +289,28 @@ impl Executor { rd: Option<&RevertDecoder>, ) -> Result { let calldata = Bytes::from(func.abi_encode_input(args)?); - let result = self.call_raw_committing(from, to, calldata, value)?; + let result = self.call_raw(from, to, calldata, value)?; result.into_decoded_result(func, rd) } - /// Performs a raw call to an account on the current state of the VM. - /// - /// The state after the call is persisted. - pub fn call_raw_committing( - &mut self, + /// Performs a call to an account on the current state of the VM. + pub fn call_sol( + &self, from: Address, to: Address, - calldata: Bytes, - value: U256, - ) -> eyre::Result { - let env = self.build_test_env(from, TransactTo::Call(to), calldata, value); - let mut result = self.call_raw_with_env(env)?; - self.commit(&mut result); - Ok(result) - } - - /// Executes the test function call - pub fn execute_test( - &mut self, - from: Address, - test_contract: Address, - func: &Function, - args: &[DynSolValue], + args: &C, value: U256, rd: Option<&RevertDecoder>, - ) -> Result { - let calldata = Bytes::from(func.abi_encode_input(args)?); - - // execute the call - let env = self.build_test_env(from, TransactTo::Call(test_contract), calldata, value); - let result = self.call_raw_with_env(env)?; - result.into_decoded_result(func, rd) + ) -> Result, EvmError> { + let calldata = Bytes::from(args.abi_encode()); + let mut raw = self.call_raw(from, to, calldata, value)?; + raw = raw.into_result(rd)?; + Ok(CallResult { decoded_result: C::abi_decode_returns(&raw.result, false)?, raw }) } /// Performs a call to an account on the current state of the VM. - /// - /// The state after the call is not persisted. - pub fn call( - &self, + pub fn transact( + &mut self, from: Address, to: Address, func: &Function, @@ -293,69 +319,59 @@ impl Executor { rd: Option<&RevertDecoder>, ) -> Result { let calldata = Bytes::from(func.abi_encode_input(args)?); - let result = self.call_raw(from, to, calldata, value)?; + let result = self.transact_raw(from, to, calldata, value)?; result.into_decoded_result(func, rd) } - /// Performs a call to an account on the current state of the VM. - /// - /// The state after the call is not persisted. - pub fn call_sol( + /// Performs a raw call to an account on the current state of the VM. + pub fn call_raw( &self, from: Address, to: Address, - args: &C, + calldata: Bytes, value: U256, - rd: Option<&RevertDecoder>, - ) -> Result, EvmError> { - let calldata = Bytes::from(args.abi_encode()); - let mut raw = self.call_raw(from, to, calldata, value)?; - raw = raw.into_result(rd)?; - Ok(CallResult { decoded_result: C::abi_decode_returns(&raw.result, false)?, raw }) + ) -> eyre::Result { + let env = self.build_test_env(from, TransactTo::Call(to), calldata, value); + self.call_with_env(env) } /// Performs a raw call to an account on the current state of the VM. - /// - /// Any state modifications made by the call are not committed. - /// - /// This intended for fuzz calls, which try to minimize [Backend] clones by using a Cow of the - /// underlying [Backend] so it only gets cloned when cheatcodes that require mutable access are - /// used. - pub fn call_raw( - &self, + pub fn transact_raw( + &mut self, from: Address, to: Address, calldata: Bytes, value: U256, ) -> eyre::Result { - let mut inspector = self.inspector.clone(); - // Build VM - let mut env = self.build_test_env(from, TransactTo::Call(to), calldata, value); - let mut db = CowBackend::new(&self.backend); - let result = db.inspect(&mut env, &mut inspector)?; - - // Persist the snapshot failure recorded on the fuzz backend wrapper. - let has_snapshot_failure = db.has_snapshot_failure(); - convert_executed_result(env, inspector, result, has_snapshot_failure) + let env = self.build_test_env(from, TransactTo::Call(to), calldata, value); + self.transact_with_env(env) } - /// Execute the transaction configured in `env.tx` and commit the changes - pub fn commit_tx_with_env(&mut self, env: EnvWithHandlerCfg) -> eyre::Result { - let mut result = self.call_raw_with_env(env)?; - self.commit(&mut result); - Ok(result) + /// Execute the transaction configured in `env.tx`. + /// + /// The state after the call is **not** persisted. + pub fn call_with_env(&self, mut env: EnvWithHandlerCfg) -> eyre::Result { + let mut inspector = self.inspector.clone(); + let mut backend = CowBackend::new(&self.backend); + let result = backend.inspect(&mut env, &mut inspector)?; + convert_executed_result(env, inspector, result, backend.has_snapshot_failure()) } - /// Execute the transaction configured in `env.tx` - pub fn call_raw_with_env(&mut self, mut env: EnvWithHandlerCfg) -> eyre::Result { - // execute the call + /// Execute the transaction configured in `env.tx`. + pub fn transact_with_env(&mut self, mut env: EnvWithHandlerCfg) -> eyre::Result { let mut inspector = self.inspector.clone(); - let result = self.backend.inspect(&mut env, &mut inspector)?; - convert_executed_result(env, inspector, result, self.backend.has_snapshot_failure()) + let backend = &mut self.backend; + let result = backend.inspect(&mut env, &mut inspector)?; + let mut result = + convert_executed_result(env, inspector, result, backend.has_snapshot_failure())?; + self.commit(&mut result); + Ok(result) } - /// Commit the changeset to the database and adjust `self.inspector_config` - /// values according to the executed call result + /// Commit the changeset to the database and adjust `self.inspector_config` values according to + /// the executed call result. + /// + /// This should not be exposed to the user, as it should be called only by `transact*`. fn commit(&mut self, result: &mut RawCallResult) { // Persist changes to db. if let Some(changes) = &result.state_changeset { @@ -363,8 +379,8 @@ impl Executor { } // Persist cheatcode state. - let mut cheatcodes = result.cheatcodes.take(); - if let Some(cheats) = cheatcodes.as_mut() { + self.inspector.cheatcodes = result.cheatcodes.take(); + if let Some(cheats) = self.inspector.cheatcodes.as_mut() { // Clear broadcastable transactions cheats.broadcastable_transactions.clear(); debug!(target: "evm::executors", "cleared broadcastable transactions"); @@ -372,59 +388,55 @@ impl Executor { // corrected_nonce value is needed outside of this context (setUp), so we don't // reset it. } - self.inspector.cheatcodes = cheatcodes; // Persist the changed environment. self.inspector.set_env(&result.env); } - /// Deploys a contract using the given `env` and commits the new state to the underlying - /// database. + /// Checks if a call to a test contract was successful. /// - /// # Panics - /// - /// Panics if `env.tx.transact_to` is not `TransactTo::Create(_)`. - pub fn deploy_with_env( - &mut self, - env: EnvWithHandlerCfg, - rd: Option<&RevertDecoder>, - ) -> Result { - assert!( - matches!(env.tx.transact_to, TransactTo::Create), - "Expected create transaction, got {:?}", - env.tx.transact_to - ); - trace!(sender=%env.tx.caller, "deploying contract"); - - let mut result = self.call_raw_with_env(env)?; - self.commit(&mut result); - result = result.into_result(rd)?; - let Some(Output::Create(_, Some(address))) = result.out else { - panic!("Deployment succeeded, but no address was returned: {result:#?}"); - }; - - // also mark this library as persistent, this will ensure that the state of the library is - // persistent across fork swaps in forking mode - self.backend.add_persistent_account(address); - - debug!(%address, "deployed contract"); - - Ok(DeployResult { raw: result, address }) + /// This is the same as [`Self::is_success`], but will consume the `state_changeset` map to use + /// internally when calling `failed()`. + pub fn is_raw_call_mut_success( + &self, + address: Address, + call_result: &mut RawCallResult, + should_fail: bool, + ) -> bool { + self.is_raw_call_success( + address, + Cow::Owned(call_result.state_changeset.take().unwrap_or_default()), + call_result, + should_fail, + ) } - /// Deploys a contract and commits the new state to the underlying database. + /// Checks if a call to a test contract was successful. /// - /// Executes a CREATE transaction with the contract `code` and persistent database state - /// modifications. - pub fn deploy( - &mut self, - from: Address, - code: Bytes, - value: U256, - rd: Option<&RevertDecoder>, - ) -> Result { - let env = self.build_test_env(from, TransactTo::Create, code, value); - self.deploy_with_env(env, rd) + /// This is the same as [`Self::is_success`] but intended for outcomes of [`Self::call_raw`]. + /// + /// ## Background + /// + /// Executing and failure checking `Executor::is_success` are two steps, for ds-test + /// legacy reasons failures can be stored in a global variables and needs to be called via a + /// solidity call `failed()(bool)`. + /// + /// Snapshots make this task more complicated because now we also need to keep track of that + /// global variable when we revert to a snapshot (because it is stored in state). Now, the + /// problem is that the `CowBackend` is dropped after every call, so we need to keep track + /// of the snapshot failure in the [`RawCallResult`] instead. + pub fn is_raw_call_success( + &self, + address: Address, + state_changeset: Cow<'_, StateChangeset>, + call_result: &RawCallResult, + should_fail: bool, + ) -> bool { + if call_result.has_snapshot_failure { + // a failure occurred in a reverted snapshot, which is considered a failed test + return should_fail; + } + self.is_success(address, call_result.reverted, state_changeset, should_fail) } /// Check if a call to a test contract was successful. @@ -449,48 +461,19 @@ impl Executor { state_changeset: Cow<'_, StateChangeset>, should_fail: bool, ) -> bool { - self.ensure_success(address, reverted, state_changeset, should_fail).unwrap_or_default() + let success = self.is_success_raw(address, reverted, state_changeset); + should_fail ^ success } - /// This is the same as [`Self::is_success`] but intended for outcomes of [`Self::call_raw`] - /// used in fuzzing and invariant testing. - /// - /// ## Background - /// - /// Executing and failure checking `Executor::ensure_success` are two steps, for ds-test - /// legacy reasons failures can be stored in a global variables and needs to be called via a - /// solidity call `failed()(bool)`. - /// - /// For fuzz tests we’re using the `CowBackend` which lazily clones the backend when it’s - /// mutated via cheatcodes like `snapshot`. Snapshots make it even more complicated because - /// now we also need to keep track of that global variable when we revert to a snapshot - /// (because it is stored in state). Now, the problem is that the `CowBackend` is dropped - /// after every call, so we need to keep track of the snapshot failure in the - /// [`RawCallResult`] instead. - pub fn is_raw_call_success( - &self, - address: Address, - state_changeset: Cow<'_, StateChangeset>, - call_result: &RawCallResult, - should_fail: bool, - ) -> bool { - if call_result.has_snapshot_failure { - // a failure occurred in a reverted snapshot, which is considered a failed test - return should_fail - } - self.is_success(address, call_result.reverted, state_changeset, should_fail) - } - - fn ensure_success( + fn is_success_raw( &self, address: Address, reverted: bool, state_changeset: Cow<'_, StateChangeset>, - should_fail: bool, - ) -> Result { + ) -> bool { if self.backend.has_snapshot_failure() { // a failure occurred in a reverted snapshot, which is considered a failed test - return Ok(should_fail) + return false; } let mut success = !reverted; @@ -500,9 +483,9 @@ impl Executor { // We only clone the test contract and cheatcode accounts, // that's all we need to evaluate success. - for addr in [address, CHEATCODE_ADDRESS] { - let acc = self.backend.basic_ref(addr)?.unwrap_or_default(); - backend.insert_account_info(addr, acc); + for address in [address, CHEATCODE_ADDRESS] { + let Ok(acc) = self.backend.basic_ref(address) else { return false }; + backend.insert_account_info(address, acc.unwrap_or_default()); } // If this test failed any asserts, then this changeset will contain changes @@ -515,16 +498,17 @@ impl Executor { let executor = Self::new(backend, self.env.clone(), self.inspector.clone(), self.gas_limit); let call = executor.call_sol(CALLER, address, &ITest::failedCall {}, U256::ZERO, None); - if let Ok(CallResult { raw: _, decoded_result: ITest::failedReturn { failed } }) = call - { - debug!(failed, "DSTest::failed()"); - success = !failed; + match call { + Ok(CallResult { raw: _, decoded_result: ITest::failedReturn { failed } }) => { + debug!(failed, "DSTest::failed()"); + success = !failed; + } + Err(err) => { + debug!(%err, "failed to call DSTest::failed()"); + } } } - - let result = should_fail ^ success; - debug!(should_fail, success, result); - Ok(result) + success } /// Creates the environment to use when executing a transaction in a test context diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 51d904376..fe4a675e8 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -38,7 +38,6 @@ use foundry_evm::{ use proptest::test_runner::TestRunner; use rayon::prelude::*; use std::{ - borrow::Cow, cmp::min, collections::{BTreeMap, HashMap}, time::Instant, @@ -437,8 +436,7 @@ impl<'a> ContractRunner<'a> { } = setup; // Run unit test - let mut executor = self.executor.clone(); - let (raw_call_result, reason) = match executor.execute_test( + let (mut raw_call_result, reason) = match self.executor.call( self.sender, address, func, @@ -470,15 +468,16 @@ impl<'a> ContractRunner<'a> { } }; + let success = + self.executor.is_raw_call_mut_success(setup.address, &mut raw_call_result, should_fail); + let RawCallResult { - reverted, gas_used: gas, stipend, logs: execution_logs, traces: execution_trace, coverage: execution_coverage, labels: new_labels, - state_changeset, debug, cheatcodes, .. @@ -491,13 +490,6 @@ impl<'a> ContractRunner<'a> { logs.extend(execution_logs); coverage = merge_coverages(coverage, execution_coverage); - let success = executor.is_success( - setup.address, - reverted, - Cow::Owned(state_changeset.unwrap()), - should_fail, - ); - TestResult { status: match success { true => TestStatus::Success, @@ -532,7 +524,7 @@ impl<'a> ContractRunner<'a> { setup; // First, run the test normally to see if it needs to be skipped. - if let Err(EvmError::SkipError) = self.executor.clone().execute_test( + if let Err(EvmError::SkipError) = self.executor.call( self.sender, address, func, diff --git a/crates/script/src/runner.rs b/crates/script/src/runner.rs index 0321d0c5e..e107180a8 100644 --- a/crates/script/src/runner.rs +++ b/crates/script/src/runner.rs @@ -92,7 +92,7 @@ impl ScriptRunner { let calldata = [salt.as_ref(), library.as_ref()].concat(); let result = self .executor - .call_raw_committing( + .transact_raw( self.evm_opts.sender, DEFAULT_CREATE2_DEPLOYER, calldata.clone().into(), @@ -301,7 +301,7 @@ impl ScriptRunner { // Otherwise don't re-execute, or some usecases might be broken: https://github.com/foundry-rs/foundry/issues/3921 if commit { gas_used = self.search_optimal_gas_usage(&res, from, to, &calldata, value)?; - res = self.executor.call_raw_committing(from, to, calldata, value)?; + res = self.executor.transact_raw(from, to, calldata, value)?; } let RawCallResult { result, reverted, logs, traces, labels, debug, transactions, .. } = res; diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index f6d2ea419..159233348 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -315,10 +315,10 @@ impl VerifyBytecodeArgs { 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."); } - let result = executor.commit_tx_with_env(env_with_handler.to_owned())?; + let result = executor.transact_with_env(env_with_handler.clone())?; - if result.result.len() > 20 { - eyre::bail!("Failed to deploy contract using commit_tx_with_env on fork at block {} | Err: Call result is greater than 20 bytes, cannot be converted to Address", simulation_block); + if result.result.len() != 20 { + eyre::bail!("Failed to deploy contract on fork at block {simulation_block}: call result is not exactly 20 bytes"); } Address::from_slice(&result.result) From b7dcf4661ddb4fa0cf3519c800df2d5c96500a7e Mon Sep 17 00:00:00 2001 From: poma Date: Thu, 13 Jun 2024 04:50:31 +0300 Subject: [PATCH 401/430] Allow to specify entropy when generating new mnemonic with cast (#8145) * Allow to specify entropy when generating new mnemonic with cast * change print color to yellow, a standard warning color --------- Co-authored-by: poma --- crates/cast/bin/cmd/wallet/mod.rs | 18 ++++++++++++++---- crates/cast/tests/cli/main.rs | 7 +++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/crates/cast/bin/cmd/wallet/mod.rs b/crates/cast/bin/cmd/wallet/mod.rs index a22d6a6ec..9daca4759 100644 --- a/crates/cast/bin/cmd/wallet/mod.rs +++ b/crates/cast/bin/cmd/wallet/mod.rs @@ -2,7 +2,7 @@ use alloy_dyn_abi::TypedData; use alloy_primitives::{Address, Signature, B256}; use alloy_signer::Signer; use alloy_signer_wallet::{ - coins_bip39::{English, Mnemonic}, + coins_bip39::{English, Entropy, Mnemonic}, LocalWallet, MnemonicBuilder, }; use clap::Parser; @@ -61,6 +61,10 @@ pub enum WalletSubcommands { /// Number of accounts to display #[arg(long, short, default_value = "1")] accounts: u8, + + /// Entropy to use for the mnemonic + #[arg(long, short, conflicts_with = "words")] + entropy: Option, }, /// Generate a vanity address. @@ -256,9 +260,15 @@ impl WalletSubcommands { } } } - Self::NewMnemonic { words, accounts } => { - let mut rng = thread_rng(); - let phrase = Mnemonic::::new_with_count(&mut rng, words)?.to_phrase(); + Self::NewMnemonic { words, accounts, entropy } => { + let phrase = if let Some(entropy) = entropy { + let entropy = Entropy::from_slice(&hex::decode(entropy)?)?; + println!("{}", "Generating mnemonic from provided entropy...".yellow()); + Mnemonic::::new_from_entropy(entropy).to_phrase() + } else { + let mut rng = thread_rng(); + Mnemonic::::new_with_count(&mut rng, words)?.to_phrase() + }; let builder = MnemonicBuilder::::default().phrase(phrase.as_str()); let derivation_path = "m/44'/60'/0'/0/"; diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 5a6be401b..7d7e33881 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -163,6 +163,13 @@ casttest!(wallet_list_local_accounts, |prj, cmd| { assert_eq!(list_output.matches('\n').count(), 10); }); +// tests that `cast wallet new-mnemonic --entropy` outputs the expected mnemonic +casttest!(wallet_mnemonic_from_entropy, |_prj, cmd| { + cmd.args(["wallet", "new-mnemonic", "--entropy", "0xdf9bf37e6fcdf9bf37e6fcdf9bf37e3c"]); + let output = cmd.stdout_lossy(); + assert!(output.contains("test test test test test test test test test test test junk")); +}); + // tests that `cast wallet private-key` with arguments outputs the private key casttest!(wallet_private_key_from_mnemonic_arg, |_prj, cmd| { cmd.args([ From 65bdd3159b7898887541c56fed2dd32af3944fbe Mon Sep 17 00:00:00 2001 From: m4rio <92288535+mario-eth@users.noreply.github.com> Date: Thu, 13 Jun 2024 10:44:50 -0700 Subject: [PATCH 402/430] fix: Soldeer: Fixed the url bug, it should be optional (#8155) * Fixed the url bug, it should be optional * added serde skip serializing --- crates/config/src/soldeer.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/config/src/soldeer.rs b/crates/config/src/soldeer.rs index 5c1d01aa6..511559bb9 100644 --- a/crates/config/src/soldeer.rs +++ b/crates/config/src/soldeer.rs @@ -11,7 +11,8 @@ pub struct SoldeerDependency { pub version: String, /// The url from where the dependency was retrieved - pub url: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub url: Option, } /// Type for Soldeer configs, under dependencies tag in the foundry.toml From 6a85dbaa62f1c305f31cab37781232913055ae28 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 13 Jun 2024 22:24:54 +0300 Subject: [PATCH 403/430] feat: display source name in debugger (#8154) * feat: display source name in debugger * fmt * clippy * refactor --- Cargo.lock | 1 - crates/common/Cargo.toml | 1 - crates/common/src/compile.rs | 38 +++++++------------ crates/debugger/src/tui/draw.rs | 30 +++++++++------ crates/evm/traces/Cargo.toml | 1 + crates/evm/traces/src/identifier/etherscan.rs | 23 +++++++---- crates/forge/bin/cmd/test/mod.rs | 3 +- crates/script/src/build.rs | 3 +- 8 files changed, 52 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dc6ee9638..206103b78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3594,7 +3594,6 @@ dependencies = [ "serde", "serde_json", "similar-asserts", - "tempfile", "thiserror", "tokio", "tower", diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 56f430c4a..907f3b625 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -47,7 +47,6 @@ reqwest.workspace = true semver = "1" serde_json.workspace = true serde.workspace = true -tempfile.workspace = true thiserror = "1" tokio.workspace = true tracing.workspace = true diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index d26b362a9..3488e6482 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -275,6 +275,7 @@ impl ProjectCompiler { pub struct SourceData { pub source: Arc, pub language: MultiCompilerLanguage, + pub name: String, } #[derive(Clone, Debug)] @@ -297,11 +298,12 @@ impl ContractSources { /// Collects the contract sources and artifacts from the project compile output. pub fn from_project_output( output: &ProjectCompileOutput, - link_data: Option<(&Path, &Libraries)>, + root: impl AsRef, + libraries: Option<&Libraries>, ) -> Result { let mut sources = Self::default(); - sources.insert(output, link_data)?; + sources.insert(output, root, libraries)?; Ok(sources) } @@ -309,12 +311,14 @@ impl ContractSources { pub fn insert( &mut self, output: &ProjectCompileOutput, - link_data: Option<(&Path, &Libraries)>, + root: impl AsRef, + libraries: Option<&Libraries>, ) -> Result<()> where C::Language: Into, { - let link_data = link_data.map(|(root, libraries)| { + let root = root.as_ref(); + let link_data = libraries.map(|libraries| { let linker = Linker::new(root, output.artifact_ids().collect()); (linker, libraries) }); @@ -355,7 +359,11 @@ impl ContractSources { self.sources_by_id.entry(build_id.clone()).or_default().insert( *source_id, - SourceData { source: source_code, language: build.language.into() }, + SourceData { + source: source_code, + language: build.language.into(), + name: path.strip_prefix(root).unwrap_or(path).to_string_lossy().to_string(), + }, ); } } @@ -496,26 +504,6 @@ pub fn compile_target( ProjectCompiler::new().quiet(quiet).files([target_path.into()]).compile(project) } -/// Compiles an Etherscan source from metadata by creating a project. -/// Returns the artifact_id, the file_id, and the bytecode -pub async fn compile_from_source( - metadata: &Metadata, -) -> Result> { - let root = tempfile::tempdir()?; - let root_path = root.path(); - let project = etherscan_project(metadata, root_path)?; - - let project_output = project.compile()?; - - if project_output.has_compiler_errors() { - eyre::bail!("{project_output}") - } - - root.close()?; - - Ok(project_output) -} - /// Creates a [Project] from an Etherscan source. pub fn etherscan_project( metadata: &Metadata, diff --git a/crates/debugger/src/tui/draw.rs b/crates/debugger/src/tui/draw.rs index aa6ac13cf..06056034c 100644 --- a/crates/debugger/src/tui/draw.rs +++ b/crates/debugger/src/tui/draw.rs @@ -191,8 +191,8 @@ impl DebuggerContext<'_> { } fn draw_src(&self, f: &mut Frame<'_>, area: Rect) { - let text_output = self.src_text(area); - let title = match self.call_kind() { + let (text_output, source_name) = self.src_text(area); + let call_kind_text = match self.call_kind() { CallKind::Create | CallKind::Create2 => "Contract creation", CallKind::Call => "Contract call", CallKind::StaticCall => "Contract staticcall", @@ -200,15 +200,20 @@ impl DebuggerContext<'_> { CallKind::DelegateCall => "Contract delegatecall", CallKind::AuthCall => "Contract authcall", }; + let title = format!( + "{} {} ", + call_kind_text, + source_name.map(|s| format!("| {s}")).unwrap_or_default() + ); let block = Block::default().title(title).borders(Borders::ALL); let paragraph = Paragraph::new(text_output).block(block).wrap(Wrap { trim: false }); f.render_widget(paragraph, area); } - fn src_text(&self, area: Rect) -> Text<'_> { - let (source_element, source_code) = match self.src_map() { + fn src_text(&self, area: Rect) -> (Text<'_>, Option<&str>) { + let (source_element, source_code, source_file) = match self.src_map() { Ok(r) => r, - Err(e) => return Text::from(e), + Err(e) => return (Text::from(e), None), }; // We are handed a vector of SourceElements that give us a span of sourcecode that is @@ -330,10 +335,11 @@ impl DebuggerContext<'_> { } } - Text::from(lines.lines) + (Text::from(lines.lines), Some(source_file)) } - fn src_map(&self) -> Result<(SourceElement, &str), String> { + /// Returns source map, source code and source name of the current line. + fn src_map(&self) -> Result<(SourceElement, &str, &str), String> { let address = self.address(); let Some(contract_name) = self.debugger.identified_contracts.get(address) else { return Err(format!("Unknown contract at address {address}")); @@ -351,7 +357,7 @@ impl DebuggerContext<'_> { let is_create = matches!(self.call_kind(), CallKind::Create | CallKind::Create2); let pc = self.current_step().pc; - let Some((source_element, source_code)) = + let Some((source_element, source_code, source_file)) = files_source_code.find_map(|(artifact, source)| { let bytecode = if is_create { &artifact.bytecode.bytecode @@ -376,7 +382,7 @@ impl DebuggerContext<'_> { // if index matches current file_id, return current source code .and_then(|index| { (index == artifact.file_id) - .then(|| (source_element.clone(), source.source.as_str())) + .then(|| (source_element.clone(), source.source.as_str(), &source.name)) }) .or_else(|| { // otherwise find the source code for the element's index @@ -385,7 +391,9 @@ impl DebuggerContext<'_> { .sources_by_id .get(&artifact.build_id)? .get(&source_element.index()?) - .map(|source| (source_element.clone(), source.source.as_str())) + .map(|source| { + (source_element.clone(), source.source.as_str(), &source.name) + }) }); res @@ -394,7 +402,7 @@ impl DebuggerContext<'_> { return Err(format!("No source map for contract {contract_name}")); }; - Ok((source_element, source_code)) + Ok((source_element, source_code, source_file)) } fn draw_op_list(&self, f: &mut Frame<'_>, area: Rect) { diff --git a/crates/evm/traces/Cargo.toml b/crates/evm/traces/Cargo.toml index d6b13e3cc..8c2ccb3ae 100644 --- a/crates/evm/traces/Cargo.toml +++ b/crates/evm/traces/Cargo.toml @@ -41,6 +41,7 @@ tokio = { workspace = true, features = ["time", "macros"] } tracing = "0.1" yansi.workspace = true rustc-hash.workspace = true +tempfile.workspace = true [dev-dependencies] tempfile.workspace = true diff --git a/crates/evm/traces/src/identifier/etherscan.rs b/crates/evm/traces/src/identifier/etherscan.rs index 794b3a9ed..87d7e9c92 100644 --- a/crates/evm/traces/src/identifier/etherscan.rs +++ b/crates/evm/traces/src/identifier/etherscan.rs @@ -4,13 +4,12 @@ use foundry_block_explorers::{ contract::{ContractMetadata, Metadata}, errors::EtherscanError, }; -use foundry_common::compile::{self, ContractSources}; +use foundry_common::compile::{etherscan_project, ContractSources}; use foundry_config::{Chain, Config}; use futures::{ future::{join_all, Future}, stream::{FuturesUnordered, Stream, StreamExt}, task::{Context, Poll}, - TryFutureExt, }; use std::{ borrow::Cow, @@ -64,11 +63,18 @@ impl EtherscanIdentifier { .iter() // filter out vyper files .filter(|(_, metadata)| !metadata.is_vyper()) - .map(|(address, metadata)| { + .map(|(address, metadata)| async move { println!("Compiling: {} {address}", metadata.contract_name); - let err_msg = - format!("Failed to compile contract {} from {address}", metadata.contract_name); - compile::compile_from_source(metadata).map_err(move |err| err.wrap_err(err_msg)) + let root = tempfile::tempdir()?; + let root_path = root.path(); + let project = etherscan_project(metadata, root_path)?; + let output = project.compile()?; + + if output.has_compiler_errors() { + eyre::bail!("{output}") + } + + Ok((project, output, root)) }) .collect::>(); @@ -78,8 +84,9 @@ impl EtherscanIdentifier { let mut sources: ContractSources = Default::default(); // construct the map - for output in outputs { - sources.insert(&output?, None)?; + for res in outputs { + let (project, output, _) = res?; + sources.insert(&output, project.root(), None)?; } Ok(sources) diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index c27e2053d..6f65dcfec 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -337,7 +337,8 @@ impl TestArgs { let sources = ContractSources::from_project_output( output_clone.as_ref().unwrap(), - Some((project.root(), &libraries)), + project.root(), + Some(&libraries), )?; // Run the debugger. diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index f1a18d45f..2fc0691d7 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -136,7 +136,8 @@ impl LinkedBuildData { ) -> Result { let sources = ContractSources::from_project_output( &build_data.output, - Some((&build_data.project_root, &libraries)), + &build_data.project_root, + Some(&libraries), )?; let known_contracts = From c2e529786c07ee7069cefcd4fe2db41f0e46cef6 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Fri, 14 Jun 2024 03:09:21 +0300 Subject: [PATCH 404/430] feat(build): add option to build specific dir (#8149) * feat(build): add option to build specific dir * Changes after review: - use source_files_iter helper, build single files and child dirs as well - rename arg to paths, use 0.. pos arg * Changes after review: reuse MultiCompilerLanguage::FILE_EXTENSIONS --- crates/cli/src/opts/build/core.rs | 5 ++ crates/forge/bin/cmd/build.rs | 15 ++++- crates/forge/tests/cli/cmd.rs | 63 +++++++++++++++++++ .../can_build_path_with_one_file.stdout | 3 + .../can_build_path_with_three_files.stdout | 3 + .../can_build_path_with_two_files.stdout | 3 + 6 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 crates/forge/tests/fixtures/can_build_path_with_one_file.stdout create mode 100644 crates/forge/tests/fixtures/can_build_path_with_three_files.stdout create mode 100644 crates/forge/tests/fixtures/can_build_path_with_two_files.stdout diff --git a/crates/cli/src/opts/build/core.rs b/crates/cli/src/opts/build/core.rs index 61986b8fa..5f27afaa6 100644 --- a/crates/cli/src/opts/build/core.rs +++ b/crates/cli/src/opts/build/core.rs @@ -126,6 +126,11 @@ pub struct CoreBuildArgs { #[serde(skip)] pub skip: Option>, + /// Build source files from specified paths. + #[arg(long, short, num_args(0..))] + #[serde(skip)] + pub paths: Option>, + #[command(flatten)] #[serde(flatten)] pub compiler: CompilerArgs, diff --git a/crates/forge/bin/cmd/build.rs b/crates/forge/bin/cmd/build.rs index e78ac283a..82d5c834e 100644 --- a/crates/forge/bin/cmd/build.rs +++ b/crates/forge/bin/cmd/build.rs @@ -3,7 +3,11 @@ use clap::Parser; use eyre::Result; use foundry_cli::{opts::CoreBuildArgs, utils::LoadConfig}; use foundry_common::compile::ProjectCompiler; -use foundry_compilers::{Project, ProjectCompileOutput}; +use foundry_compilers::{ + compilers::{multi::MultiCompilerLanguage, Language}, + utils::source_files_iter, + Project, ProjectCompileOutput, +}; use foundry_config::{ figment::{ self, @@ -80,7 +84,16 @@ impl BuildArgs { let project = config.project()?; + // Collect sources to compile if build subdirectories specified. + let mut files = vec![]; + if let Some(dirs) = self.args.paths { + for dir in dirs { + files.extend(source_files_iter(dir, MultiCompilerLanguage::FILE_EXTENSIONS)); + } + } + let compiler = ProjectCompiler::new() + .files(files) .print_names(self.names) .print_sizes(self.sizes) .quiet(self.format_json) diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index de2c24241..1c851dd09 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -1667,6 +1667,69 @@ function test_run() external {} ); }); +forgetest_init!(can_build_specific_paths, |prj, cmd| { + prj.wipe(); + prj.add_source( + "Counter.sol", + r" +contract Counter { +function count() external {} +}", + ) + .unwrap(); + prj.add_test( + "Foo.sol", + r" +contract Foo { +function test_foo() external {} +}", + ) + .unwrap(); + prj.add_test( + "Bar.sol", + r" +contract Bar { +function test_bar() external {} +}", + ) + .unwrap(); + + // Build 2 files within test dir + prj.clear(); + cmd.args(["build", "--paths", "test", "--force"]); + cmd.unchecked_output().stdout_matches_path( + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/can_build_path_with_two_files.stdout"), + ); + + // Build one file within src dir + prj.clear(); + cmd.forge_fuse(); + cmd.args(["build", "--paths", "src", "--force"]); + cmd.unchecked_output().stdout_matches_path( + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/can_build_path_with_one_file.stdout"), + ); + + // Build 3 files from test and src dirs + prj.clear(); + cmd.forge_fuse(); + cmd.args(["build", "--paths", "src", "test", "--force"]); + cmd.unchecked_output().stdout_matches_path( + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/can_build_path_with_three_files.stdout"), + ); + + // Build single test file + prj.clear(); + cmd.forge_fuse(); + cmd.args(["build", "--paths", "test/Bar.sol", "--force"]); + cmd.unchecked_output().stdout_matches_path( + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/can_build_path_with_one_file.stdout"), + ); +}); + // checks that build --sizes includes all contracts even if unchanged forgetest_init!(can_build_sizes_repeatedly, |prj, cmd| { prj.clear_cache(); diff --git a/crates/forge/tests/fixtures/can_build_path_with_one_file.stdout b/crates/forge/tests/fixtures/can_build_path_with_one_file.stdout new file mode 100644 index 000000000..3213db81a --- /dev/null +++ b/crates/forge/tests/fixtures/can_build_path_with_one_file.stdout @@ -0,0 +1,3 @@ +Compiling 1 files with 0.8.23 +Solc 0.8.23 finished in 33.25ms +Compiler run successful! diff --git a/crates/forge/tests/fixtures/can_build_path_with_three_files.stdout b/crates/forge/tests/fixtures/can_build_path_with_three_files.stdout new file mode 100644 index 000000000..4235acf29 --- /dev/null +++ b/crates/forge/tests/fixtures/can_build_path_with_three_files.stdout @@ -0,0 +1,3 @@ +Compiling 3 files with 0.8.23 +Solc 0.8.23 finished in 33.25ms +Compiler run successful! diff --git a/crates/forge/tests/fixtures/can_build_path_with_two_files.stdout b/crates/forge/tests/fixtures/can_build_path_with_two_files.stdout new file mode 100644 index 000000000..cf5900340 --- /dev/null +++ b/crates/forge/tests/fixtures/can_build_path_with_two_files.stdout @@ -0,0 +1,3 @@ +Compiling 2 files with 0.8.23 +Solc 0.8.23 finished in 33.25ms +Compiler run successful! From 4240376a8c3e0457c340e57c044e4819ae4f849e Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Fri, 14 Jun 2024 22:25:06 +0530 Subject: [PATCH 405/430] migrate(`forge-bind`): to alloy (#7919) * migrate(forge-bind): to alloy - boilerplate and `SolMacroGen` type * tokens to `SolInput` * use SolInputKind * update alloy-core deps version and use expand * write cargo.toml * alloy: write_to_module * use `tokens_for_sol` from lib * write to single_file * nit * nit * add sol attr * nits * add alloy `Filter` and reuse get_json_files * fix: throw err instead of panic! * nits * check cargo toml * check existing alloy bindings * clippy nits * fmt * doc nits * clean up and nits * extract `sol_macro_gen` to separate crate * can specify alloy version * nit * warning nit * clippy nit * nit Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * pretty fmt before writing * nit: format! * populate bytecode attr * clippy * nit Co-authored-by: Matthias Seitz * fmt nits * clippy * parse path to `SolInput` directly * fix: artifact duplication * dedup faster * add sol attributes * fix: alloy dep * clippy * clippy nits --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: Matthias Seitz --- Cargo.lock | 19 ++ Cargo.toml | 7 + crates/cast/bin/cmd/send.rs | 3 +- crates/cast/bin/cmd/wallet/mod.rs | 2 +- crates/forge/Cargo.toml | 3 + crates/forge/bin/cmd/bind.rs | 140 ++++++++- crates/forge/tests/cli/soldeer.rs | 8 +- crates/sol-macro-gen/Cargo.toml | 26 ++ crates/sol-macro-gen/src/lib.rs | 5 + crates/sol-macro-gen/src/sol_macro_gen.rs | 352 ++++++++++++++++++++++ 10 files changed, 549 insertions(+), 16 deletions(-) create mode 100644 crates/sol-macro-gen/Cargo.toml create mode 100644 crates/sol-macro-gen/src/lib.rs create mode 100644 crates/sol-macro-gen/src/sol_macro_gen.rs diff --git a/Cargo.lock b/Cargo.lock index 206103b78..596b73f95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3255,6 +3255,8 @@ dependencies = [ "alloy-rpc-types", "alloy-signer", "alloy-signer-wallet", + "alloy-sol-macro-expander", + "alloy-sol-macro-input", "alloy-transport", "anvil", "async-trait", @@ -3273,6 +3275,7 @@ dependencies = [ "forge-doc", "forge-fmt", "forge-script", + "forge-sol-macro-gen", "forge-verify", "foundry-block-explorers", "foundry-cli", @@ -3408,6 +3411,22 @@ dependencies = [ "yansi", ] +[[package]] +name = "forge-sol-macro-gen" +version = "0.2.0" +dependencies = [ + "alloy-json-abi", + "alloy-sol-macro-expander", + "alloy-sol-macro-input", + "eyre", + "foundry-common", + "prettyplease", + "proc-macro2", + "quote", + "serde_json", + "syn 2.0.66", +] + [[package]] name = "forge-verify" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 58b2a8972..663b727cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -135,6 +135,7 @@ forge-doc = { path = "crates/doc" } forge-fmt = { path = "crates/fmt" } forge-verify = { path = "crates/verify" } forge-script = { path = "crates/script" } +forge-sol-macro-gen = { path = "crates/sol-macro-gen" } foundry-cheatcodes = { path = "crates/cheatcodes" } foundry-cheatcodes-spec = { path = "crates/cheatcodes/spec" } foundry-cli = { path = "crates/cli" } @@ -195,6 +196,8 @@ alloy-primitives = { version = "0.7.1", features = ["getrandom", "rand"] } alloy-dyn-abi = "0.7.1" alloy-json-abi = "0.7.1" alloy-sol-types = "0.7.1" +alloy-sol-macro-input = "0.7.3" +alloy-sol-macro-expander = "0.7.3" syn-solidity = "0.7.1" alloy-chains = "0.1" alloy-trie = "0.4.1" @@ -202,6 +205,10 @@ alloy-rlp = "0.3.3" solang-parser = "=0.3.3" ## misc +proc-macro2 = "1.0.82" +quote = "1.0" +syn = "2.0" +prettyplease = "0.2.20" ahash = "0.8" arrayvec = "0.7" base64 = "0.22" diff --git a/crates/cast/bin/cmd/send.rs b/crates/cast/bin/cmd/send.rs index d4aee145b..b7d94402b 100644 --- a/crates/cast/bin/cmd/send.rs +++ b/crates/cast/bin/cmd/send.rs @@ -77,7 +77,8 @@ pub enum SendTxSubcommands { } impl SendTxArgs { - pub async fn run(self) -> Result<()> { + #[allow(dependency_on_unit_never_type_fallback)] + pub async fn run(self) -> Result<(), eyre::Report> { let Self { eth, to, diff --git a/crates/cast/bin/cmd/wallet/mod.rs b/crates/cast/bin/cmd/wallet/mod.rs index 9daca4759..c468803ce 100644 --- a/crates/cast/bin/cmd/wallet/mod.rs +++ b/crates/cast/bin/cmd/wallet/mod.rs @@ -262,7 +262,7 @@ impl WalletSubcommands { } Self::NewMnemonic { words, accounts, entropy } => { let phrase = if let Some(entropy) = entropy { - let entropy = Entropy::from_slice(&hex::decode(entropy)?)?; + let entropy = Entropy::from_slice(hex::decode(entropy)?)?; println!("{}", "Generating mnemonic from provided entropy...".yellow()); Mnemonic::::new_from_entropy(entropy).to_phrase() } else { diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 28d679262..995053fee 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -52,6 +52,7 @@ forge-doc.workspace = true forge-fmt.workspace = true forge-verify.workspace = true forge-script.workspace = true +forge-sol-macro-gen.workspace = true foundry-cli.workspace = true foundry-debugger.workspace = true @@ -63,6 +64,8 @@ alloy-provider = { workspace = true, features = ["reqwest", "ws", "ipc"] } alloy-network.workspace = true alloy-transport.workspace = true alloy-signer.workspace = true +alloy-sol-macro-input.workspace = true +alloy-sol-macro-expander = { workspace = true, features = ["json"] } alloy-consensus.workspace = true alloy-chains.workspace = true diff --git a/crates/forge/bin/cmd/bind.rs b/crates/forge/bin/cmd/bind.rs index 8283ca7d8..f9e9a22a4 100644 --- a/crates/forge/bin/cmd/bind.rs +++ b/crates/forge/bin/cmd/bind.rs @@ -3,6 +3,7 @@ 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}; use foundry_config::impl_figment_convert; @@ -81,9 +82,13 @@ pub struct BindArgs { skip_extra_derives: bool, /// Generate bindings for the `alloy` library, instead of `ethers`. - #[arg(skip)] + #[arg(long, conflicts_with = "ethers")] alloy: bool, + /// Specify the alloy version. + #[arg(long, value_name = "ALLOY_VERSION")] + alloy_version: Option, + /// Generate bindings for the `ethers` library, instead of `alloy` (default, deprecated). #[arg(long)] ethers: bool, @@ -100,13 +105,10 @@ impl BindArgs { } if !self.alloy { - eprintln!("Warning: ethers bindings are deprecated and will be removed in the future"); - /* eprintln!( "Warning: `--ethers` (default) bindings are deprecated and will be removed in the future. \ Consider using `--alloy` instead." ); - */ } let config = self.try_load_config_emit_warnings()?; @@ -162,9 +164,34 @@ impl BindArgs { .into()) } + fn get_alloy_filter(&self) -> Result { + if self.select_all { + // Select all json files + return Ok(Filter::All); + } + if !self.select.is_empty() { + // Return json files that match the select regex + return Ok(Filter::Select(self.select.clone())); + } + + if let Some(skip) = self.build_args.skip.as_ref().filter(|s| !s.is_empty()) { + return Ok(Filter::Skip( + skip.clone() + .into_iter() + .map(|s| Regex::new(s.file_pattern())) + .collect::, _>>()?, + )); + } + + // Exclude defaults + Ok(Filter::skip_default()) + } + /// 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.alloy; Ok(json_files(artifacts) .filter_map(|path| { // Ignore the build info JSON. @@ -185,7 +212,15 @@ 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 @@ -208,13 +243,32 @@ impl BindArgs { Ok(multi) } + fn get_solmacrogen(&self, artifacts: &Path) -> Result { + let mut dup = std::collections::HashSet::::new(); + let instances = self + .get_json_files(artifacts)? + .filter_map(|(name, path)| { + trace!(?path, "parsing SolMacroGen from file"); + if dup.insert(name.clone()) { + Some(SolMacroGen::new(path, name)) + } else { + None + } + }) + .collect::>(); + + let multi = MultiSolMacroGen::new(artifacts, instances); + eyre::ensure!(!multi.instances.is_empty(), "No contract artifacts found"); + Ok(multi) + } + /// Check that the existing bindings match the expected abigen output fn check_existing_bindings(&self, artifacts: &Path, bindings_root: &Path) -> Result<()> { if !self.alloy { return self.check_ethers(artifacts, bindings_root); } - todo!("alloy") + self.check_alloy(artifacts, bindings_root) } fn check_ethers(&self, artifacts: &Path, bindings_root: &Path) -> Result<()> { @@ -243,13 +297,30 @@ impl BindArgs { Ok(()) } + fn check_alloy(&self, artifacts: &Path, bindings_root: &Path) -> Result<()> { + let mut bindings = self.get_solmacrogen(artifacts)?; + bindings.generate_bindings()?; + println!("Checking bindings for {} contracts", bindings.instances.len()); + bindings.check_consistency( + &self.crate_name, + &self.crate_version, + bindings_root, + self.single_file, + !self.skip_cargo_toml, + self.module, + self.alloy_version.clone(), + )?; + println!("OK."); + Ok(()) + } + /// Generate the bindings fn generate_bindings(&self, artifacts: &Path, bindings_root: &Path) -> Result<()> { if !self.alloy { return self.generate_ethers(artifacts, bindings_root); } - todo!("alloy") + self.generate_alloy(artifacts, bindings_root) } fn generate_ethers(&self, artifacts: &Path, bindings_root: &Path) -> Result<()> { @@ -271,4 +342,59 @@ impl BindArgs { 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)?; + println!("Generating bindings for {} contracts", solmacrogen.instances.len()); + + if !self.module { + trace!(single_file = self.single_file, "generating crate"); + solmacrogen.write_to_crate( + &self.crate_name, + &self.crate_version, + bindings_root, + self.single_file, + self.alloy_version.clone(), + )?; + } else { + trace!(single_file = self.single_file, "generating module"); + solmacrogen.write_to_module(bindings_root, self.single_file)?; + } + + Ok(()) + } +} + +pub enum Filter { + All, + Select(Vec), + Skip(Vec), +} + +impl Filter { + pub fn is_match(&self, name: &str) -> bool { + match self { + Self::All => true, + Self::Select(regexes) => regexes.iter().any(|regex| regex.is_match(name)), + Self::Skip(regexes) => !regexes.iter().any(|regex| regex.is_match(name)), + } + } + + pub fn skip_default() -> Self { + let skip = [ + ".*Test.*", + ".*Script", + "console[2]?", + "CommonBase", + "Components", + "[Ss]td(Chains|Math|Error|Json|Utils|Cheats|Style|Invariant|Assertions|Toml|Storage(Safe)?)", + "[Vv]m.*", + "IMulticall3", + ] + .iter() + .map(|pattern| regex::Regex::new(pattern).unwrap()) + .collect::>(); + + Self::Skip(skip) + } } diff --git a/crates/forge/tests/cli/soldeer.rs b/crates/forge/tests/cli/soldeer.rs index 7f762e8dd..6bbba534d 100644 --- a/crates/forge/tests/cli/soldeer.rs +++ b/crates/forge/tests/cli/soldeer.rs @@ -171,12 +171,6 @@ forgesoldeer!(login, |prj, cmd| { }); fn read_file_to_string(path: &Path) -> String { - let contents: String = match fs::read_to_string(path) { - Ok(c) => c, - Err(_) => { - // Return empty string - String::new() - } - }; + let contents: String = fs::read_to_string(path).unwrap_or_default(); contents } diff --git a/crates/sol-macro-gen/Cargo.toml b/crates/sol-macro-gen/Cargo.toml new file mode 100644 index 000000000..c4da94041 --- /dev/null +++ b/crates/sol-macro-gen/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "forge-sol-macro-gen" +description = "Contains types and methods for generating rust bindings using sol!" +publish = false + +version.workspace = true +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +alloy-json-abi.workspace = true +alloy-sol-macro-input.workspace = true +alloy-sol-macro-expander = { workspace = true, features = ["json"] } +foundry-common.workspace = true + +proc-macro2.workspace = true +quote.workspace = true +syn.workspace = true +prettyplease.workspace = true + +serde_json.workspace = true +eyre.workspace = true diff --git a/crates/sol-macro-gen/src/lib.rs b/crates/sol-macro-gen/src/lib.rs new file mode 100644 index 000000000..0202827f2 --- /dev/null +++ b/crates/sol-macro-gen/src/lib.rs @@ -0,0 +1,5 @@ +//! This crate constains the logic for Rust bindings generating from Solidity contracts + +pub mod sol_macro_gen; + +pub use sol_macro_gen::*; diff --git a/crates/sol-macro-gen/src/sol_macro_gen.rs b/crates/sol-macro-gen/src/sol_macro_gen.rs new file mode 100644 index 000000000..1cc8cc910 --- /dev/null +++ b/crates/sol-macro-gen/src/sol_macro_gen.rs @@ -0,0 +1,352 @@ +//! SolMacroGen and MultiSolMacroGen +//! +//! This type encapsulates the logic for expansion of a Rust TokenStream from Solidity tokens. It +//! uses the `expand` method from `alloy_sol_macro_expander` underneath. +//! +//! It holds info such as `path` to the ABI file, `name` of the file and the rust binding being +//! generated, and lastly the `expansion` itself, i.e the Rust binding for the provided ABI. +//! +//! It contains methods to read the json abi, generate rust bindings from the abi and ultimately +//! write the bindings to a crate or modules. + +use alloy_sol_macro_expander::expand::expand; +use alloy_sol_macro_input::{SolInput, SolInputKind}; +use eyre::{Context, Ok, OptionExt, Result}; +use foundry_common::fs; +use proc_macro2::{Span, TokenStream}; +use std::{ + fmt::Write, + path::{Path, PathBuf}, + str::FromStr, +}; + +pub struct SolMacroGen { + pub path: PathBuf, + pub name: String, + pub expansion: Option, +} + +impl SolMacroGen { + pub fn new(path: PathBuf, name: String) -> Self { + Self { path, name, expansion: None } + } + + pub fn get_sol_input(&self) -> Result { + let path = self.path.to_string_lossy().into_owned(); + let name = proc_macro2::Ident::new(&self.name, Span::call_site()); + let tokens = quote::quote! { + #name, + #path + }; + + let sol_input: SolInput = syn::parse2(tokens).wrap_err("Failed to parse SolInput {e}")?; + + Ok(sol_input) + } +} + +pub struct MultiSolMacroGen { + pub artifacts_path: PathBuf, + pub instances: Vec, +} + +impl MultiSolMacroGen { + pub fn new(artifacts_path: &Path, instances: Vec) -> Self { + Self { artifacts_path: artifacts_path.to_path_buf(), instances } + } + + pub fn populate_expansion(&mut self, bindings_path: &Path) -> Result<()> { + for instance in &mut self.instances { + let path = bindings_path.join(format!("{}.rs", instance.name.to_lowercase())); + let expansion = fs::read_to_string(path).wrap_err("Failed to read file")?; + + let tokens = TokenStream::from_str(&expansion) + .map_err(|e| eyre::eyre!("Failed to parse TokenStream: {e}"))?; + instance.expansion = Some(tokens); + } + Ok(()) + } + + pub fn generate_bindings(&mut self) -> Result<()> { + for instance in &mut self.instances { + let input = instance.get_sol_input()?.normalize_json()?; + + let SolInput { attrs: _attrs, path: _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 SolInput")? + } + _ => unreachable!(), + }; + + instance.expansion = Some(tokens); + } + + Ok(()) + } + + pub fn write_to_crate( + &mut self, + name: &str, + version: &str, + bindings_path: &Path, + single_file: bool, + alloy_version: Option, + ) -> Result<()> { + self.generate_bindings()?; + + let src = bindings_path.join("src"); + + let _ = fs::create_dir_all(&src); + + // Write Cargo.toml + let cargo_toml_path = bindings_path.join("Cargo.toml"); + let mut toml_contents = format!( + r#"[package] +name = "{}" +version = "{}" +edition = "2021" + +[dependencies] +"#, + name, version + ); + + let alloy_dep = if let Some(alloy_version) = alloy_version { + format!( + r#"alloy = {{ git = "https://github.com/alloy-rs/alloy", rev = "{}", features = ["sol-types", "contract"] }}"#, + alloy_version + ) + } else { + r#"alloy = {{ git = "https://github.com/alloy-rs/alloy", features = ["sol-types", "contract"] }}"#.to_string() + }; + write!(toml_contents, "{}", alloy_dep)?; + + fs::write(cargo_toml_path, toml_contents).wrap_err("Failed to write Cargo.toml")?; + + let mut lib_contents = String::new(); + if single_file { + write!( + &mut lib_contents, + r#"#![allow(unused_imports, clippy::all)] + //! This module contains the sol! generated bindings for solidity contracts. + //! This is autogenerated code. + //! Do not manually edit these files. + //! These files may be overwritten by the codegen system at any time."# + )?; + } else { + write!( + &mut lib_contents, + r#"#![allow(unused_imports)] + "# + )?; + }; + + // Write src + for instance in &self.instances { + let name = instance.name.to_lowercase(); + let contents = instance.expansion.as_ref().unwrap().to_string(); + + if !single_file { + let path = src.join(format!("{}.rs", name)); + 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 { + write!(&mut lib_contents, "{}", contents)?; + } + } + + let lib_path = src.join("lib.rs"); + let lib_file = syn::parse_file(&lib_contents)?; + + let lib_contents = prettyplease::unparse(&lib_file); + + fs::write(lib_path, lib_contents).wrap_err("Failed to write lib.rs")?; + + Ok(()) + } + + pub fn write_to_module(&mut self, bindings_path: &Path, single_file: bool) -> Result<()> { + self.generate_bindings()?; + + let _ = fs::create_dir_all(bindings_path); + + let mut mod_contents = r#"#![allow(clippy::all)] + //! This module contains the sol! generated bindings for solidity contracts. + //! This is autogenerated code. + //! Do not manually edit these files. + //! These files may be overwritten by the codegen system at any time. + "# + .to_string(); + + for instance in &self.instances { + let name = instance.name.to_lowercase(); + if !single_file { + write!( + mod_contents, + r#"pub mod {}; + "#, + instance.name.to_lowercase() + )?; + let mut contents = + r#"//! This module was autogenerated by the alloy sol!. + //! More information can be found here . + "#.to_string(); + + write!(contents, "{}", instance.expansion.as_ref().unwrap())?; + let file = syn::parse_file(&contents)?; + + let contents = prettyplease::unparse(&file); + fs::write(bindings_path.join(format!("{}.rs", name)), contents) + .wrap_err("Failed to write file")?; + } else { + let mut contents = format!( + r#"//! This module was autogenerated by the alloy sol!. + //! More information can be found here . + pub use {}::*; + "#, + name + ); + write!(contents, "{}\n\n", instance.expansion.as_ref().unwrap())?; + write!(mod_contents, "{}", contents)?; + } + } + + let mod_path = bindings_path.join("mod.rs"); + let mod_file = syn::parse_file(&mod_contents)?; + let mod_contents = prettyplease::unparse(&mod_file); + + fs::write(mod_path, mod_contents).wrap_err("Failed to write mod.rs")?; + + Ok(()) + } + + /// Checks that the generated bindings are up to date with the latest version of + /// `sol!`. + /// + /// Returns `Ok(())` if the generated bindings are up to date, otherwise it returns + /// `Err(_)`. + #[allow(clippy::too_many_arguments)] + pub fn check_consistency( + &self, + name: &str, + version: &str, + crate_path: &Path, + single_file: bool, + check_cargo_toml: bool, + is_mod: bool, + alloy_version: Option, + ) -> Result<()> { + if check_cargo_toml { + self.check_cargo_toml(name, version, crate_path, alloy_version)?; + } + + let mut super_contents = String::new(); + if is_mod { + // mod.rs + write!( + &mut super_contents, + r#"#![allow(clippy::all)] + //! This module contains the sol! generated bindings for solidity contracts. + //! This is autogenerated code. + //! Do not manually edit these files. + //! These files may be overwritten by the codegen system at any time. + "# + )?; + } else { + // lib.rs + write!( + &mut super_contents, + r#"#![allow(unused_imports)] + "# + )?; + }; + if !single_file { + for instance in &self.instances { + let name = instance.name.to_lowercase(); + let path = crate_path.join(format!("src/{}.rs", name)); + let tokens = instance + .expansion + .as_ref() + .ok_or_eyre(format!("TokenStream for {path:?} does not exist"))? + .to_string(); + + self.check_file_contents(&path, &tokens)?; + + if !is_mod { + write!( + &mut super_contents, + r#"pub mod {}; + "#, + name + )?; + } + } + + let super_path = + if is_mod { crate_path.join("src/mod.rs") } else { crate_path.join("src/lib.rs") }; + self.check_file_contents(&super_path, &super_contents)?; + } + + Ok(()) + } + + fn check_file_contents(&self, file_path: &Path, expected_contents: &str) -> Result<()> { + eyre::ensure!( + file_path.is_file() && file_path.exists(), + "{} is not a file", + file_path.display() + ); + let file_contents = &fs::read_to_string(file_path).wrap_err("Failed to read file")?; + eyre::ensure!( + file_contents == expected_contents, + "File contents do not match expected contents for {file_path:?}" + ); + Ok(()) + } + + fn check_cargo_toml( + &self, + name: &str, + version: &str, + crate_path: &Path, + alloy_version: Option, + ) -> Result<()> { + eyre::ensure!(crate_path.is_dir(), "Crate path must be a directory"); + + let cargo_toml_path = crate_path.join("Cargo.toml"); + + eyre::ensure!(cargo_toml_path.is_file(), "Cargo.toml must exist"); + let cargo_toml_contents = + fs::read_to_string(cargo_toml_path).wrap_err("Failed to read Cargo.toml")?; + + let name_check = &format!("name = \"{}\"", name); + let version_check = &format!("version = \"{}\"", version); + let alloy_dep_check = if let Some(version) = alloy_version { + &format!( + r#"alloy = {{ git = "https://github.com/alloy-rs/alloy", rev = "{}", features = ["sol-types", "contract"] }}"#, + version + ) + } else { + &r#"alloy = {{ git = "https://github.com/alloy-rs/alloy", features = ["sol-types", "contract"] }}"#.to_string() + }; + let toml_consistent = cargo_toml_contents.contains(name_check) && + cargo_toml_contents.contains(version_check) && + cargo_toml_contents.contains(alloy_dep_check); + eyre::ensure!( + toml_consistent, + r#"The contents of Cargo.toml do not match the expected output of the latest `sol!` version. + This indicates that the existing bindings are outdated and need to be generated again."# + ); + + Ok(()) + } +} From b002fe8af68bdc3fb909a81c288bb93254b7b61e Mon Sep 17 00:00:00 2001 From: Ayene <2958807+ayenesimo1i@users.noreply.github.com> Date: Fri, 14 Jun 2024 20:32:14 +0300 Subject: [PATCH 406/430] chore: add known error codes (#8166) add known error codes --- crates/config/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/config/README.md b/crates/config/README.md index 482ec29ac..0ef55336a 100644 --- a/crates/config/README.md +++ b/crates/config/README.md @@ -103,7 +103,7 @@ eth_rpc_url = "https://example.com/" # Setting this option enables decoding of error traces from mainnet deployed / verfied contracts via etherscan etherscan_api_key = "YOURETHERSCANAPIKEY" # ignore solc warnings for missing license and exceeded contract size -# known error codes are: ["unreachable", "unused-return", "unused-param", "unused-var", "code-size", "shadowing", "func-mutability", "license", "pragma-solidity", "virtual-interfaces", "same-varname", "too-many-warnings"] +# known error codes are: ["unreachable", "unused-return", "unused-param", "unused-var", "code-size", "shadowing", "func-mutability", "license", "pragma-solidity", "virtual-interfaces", "same-varname", "too-many-warnings", "constructor-visibility", "init-code-size", "missing-receive-ether", "unnamed-return", "transient-storage"] # additional warnings can be added using their numeric error code: ["license", 1337] ignored_error_codes = ["license", "code-size"] ignored_warnings_from = ["path_to_ignore"] From dbfa025dfbb4ffdae71797fc41f8f46db4018f13 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Fri, 14 Jun 2024 20:32:35 +0300 Subject: [PATCH 407/430] fix: enable cache when `--build-info` is enabled (#8164) fix: enable cache when --build-info is enabled --- crates/config/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 624edbebc..463ea82aa 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -804,7 +804,7 @@ impl Config { Severity::Error }) .set_offline(self.offline) - .set_cached(cached && !self.build_info) + .set_cached(cached) .set_build_info(!no_artifacts && self.build_info) .set_no_artifacts(no_artifacts); From a20cef190b21fd57c4610e98a1c5032fac568fbb Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Sat, 15 Jun 2024 00:26:22 +0530 Subject: [PATCH 408/430] fix(forge-bind): alloy deps and file consistency check (#8167) * fix(forge): alloy deps in bind * nit: braces Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * fix(bind): file consistency check * chore: unknown lints --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- crates/cast/bin/cmd/send.rs | 2 +- crates/sol-macro-gen/src/sol_macro_gen.rs | 29 ++++++++++++++--------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/crates/cast/bin/cmd/send.rs b/crates/cast/bin/cmd/send.rs index b7d94402b..d684edcd4 100644 --- a/crates/cast/bin/cmd/send.rs +++ b/crates/cast/bin/cmd/send.rs @@ -77,7 +77,7 @@ pub enum SendTxSubcommands { } impl SendTxArgs { - #[allow(dependency_on_unit_never_type_fallback)] + #[allow(unknown_lints, dependency_on_unit_never_type_fallback)] pub async fn run(self) -> Result<(), eyre::Report> { let Self { eth, diff --git a/crates/sol-macro-gen/src/sol_macro_gen.rs b/crates/sol-macro-gen/src/sol_macro_gen.rs index 1cc8cc910..6ef48f8cb 100644 --- a/crates/sol-macro-gen/src/sol_macro_gen.rs +++ b/crates/sol-macro-gen/src/sol_macro_gen.rs @@ -123,7 +123,7 @@ edition = "2021" alloy_version ) } else { - r#"alloy = {{ git = "https://github.com/alloy-rs/alloy", features = ["sol-types", "contract"] }}"#.to_string() + r#"alloy = { git = "https://github.com/alloy-rs/alloy", features = ["sol-types", "contract"] }"#.to_string() }; write!(toml_contents, "{}", alloy_dep)?; @@ -306,8 +306,16 @@ edition = "2021" file_path.display() ); let file_contents = &fs::read_to_string(file_path).wrap_err("Failed to read file")?; + + // Format both + let file_contents = syn::parse_file(file_contents)?; + let formatted_file = prettyplease::unparse(&file_contents); + + let expected_contents = syn::parse_file(expected_contents)?; + let formatted_exp = prettyplease::unparse(&expected_contents); + eyre::ensure!( - file_contents == expected_contents, + formatted_file == formatted_exp, "File contents do not match expected contents for {file_path:?}" ); Ok(()) @@ -328,19 +336,18 @@ edition = "2021" let cargo_toml_contents = fs::read_to_string(cargo_toml_path).wrap_err("Failed to read Cargo.toml")?; - let name_check = &format!("name = \"{}\"", name); - let version_check = &format!("version = \"{}\"", version); + let name_check = format!("name = \"{name}\""); + let version_check = format!("version = \"{version}\""); let alloy_dep_check = if let Some(version) = alloy_version { - &format!( - r#"alloy = {{ git = "https://github.com/alloy-rs/alloy", rev = "{}", features = ["sol-types", "contract"] }}"#, - version + format!( + r#"alloy = {{ git = "https://github.com/alloy-rs/alloy", rev = "{version}", features = ["sol-types", "contract"] }}"#, ) } else { - &r#"alloy = {{ git = "https://github.com/alloy-rs/alloy", features = ["sol-types", "contract"] }}"#.to_string() + r#"alloy = { git = "https://github.com/alloy-rs/alloy", features = ["sol-types", "contract"] }"#.to_string() }; - let toml_consistent = cargo_toml_contents.contains(name_check) && - cargo_toml_contents.contains(version_check) && - cargo_toml_contents.contains(alloy_dep_check); + let toml_consistent = cargo_toml_contents.contains(&name_check) && + cargo_toml_contents.contains(&version_check) && + cargo_toml_contents.contains(&alloy_dep_check); eyre::ensure!( toml_consistent, r#"The contents of Cargo.toml do not match the expected output of the latest `sol!` version. From 46cde380e9b7b2ceb012f7d6a03ef2607f1193e8 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 14 Jun 2024 21:22:51 +0200 Subject: [PATCH 409/430] perf: slightly improve inspector stack (#8169) --- crates/evm/evm/src/inspectors/chisel_state.rs | 2 +- crates/evm/evm/src/inspectors/logs.rs | 9 ++- crates/evm/evm/src/inspectors/stack.rs | 57 ++++++++++++------- 3 files changed, 43 insertions(+), 25 deletions(-) diff --git a/crates/evm/evm/src/inspectors/chisel_state.rs b/crates/evm/evm/src/inspectors/chisel_state.rs index 9aa933d38..023389ed4 100644 --- a/crates/evm/evm/src/inspectors/chisel_state.rs +++ b/crates/evm/evm/src/inspectors/chisel_state.rs @@ -22,7 +22,7 @@ impl ChiselState { } impl Inspector for ChiselState { - #[inline] + #[cold] fn step_end(&mut self, interp: &mut Interpreter, _context: &mut EvmContext) { // If we are at the final pc of the REPL contract execution, set the state. // Subtraction can't overflow because `pc` is always at least 1 in `step_end`. diff --git a/crates/evm/evm/src/inspectors/logs.rs b/crates/evm/evm/src/inspectors/logs.rs index f2bf3a114..bde558dbf 100644 --- a/crates/evm/evm/src/inspectors/logs.rs +++ b/crates/evm/evm/src/inspectors/logs.rs @@ -1,4 +1,4 @@ -use alloy_primitives::{Address, Bytes, Log}; +use alloy_primitives::{Bytes, Log}; use alloy_sol_types::{SolEvent, SolInterface, SolValue}; use foundry_common::{fmt::ConsoleFmt, ErrorExt}; use foundry_evm_core::{ @@ -70,6 +70,9 @@ impl Inspector for LogCollector { fn convert_hh_log_to_event(call: HardhatConsole::HardhatConsoleCalls) -> Log { // Convert the parameters of the call to their string representation using `ConsoleFmt`. let fmt = call.fmt(Default::default()); - Log::new(Address::default(), vec![Console::log::SIGNATURE_HASH], fmt.abi_encode().into()) - .unwrap_or_else(|| Log { ..Default::default() }) + Log::new_unchecked( + HARDHAT_CONSOLE_ADDRESS, + vec![Console::log::SIGNATURE_HASH], + fmt.abi_encode().into(), + ) } diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 52949e8e1..c8497d93c 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -192,31 +192,25 @@ impl InspectorStackBuilder { /// dispatch. #[macro_export] macro_rules! call_inspectors { - ([$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $call:expr $(,)?) => {{$( - if let Some($id) = $inspector { - $call - } - )+}} + ([$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $call:expr $(,)?) => { + $( + if let Some($id) = $inspector { + $call + } + )+ + } } /// Same as [call_inspectors] macro, but with depth adjustment for isolated execution. macro_rules! call_inspectors_adjust_depth { (#[no_ret] [$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $call:expr, $self:ident, $data:ident $(,)?) => { - if $self.in_inner_context { - $data.journaled_state.depth += 1; - $( - if let Some($id) = $inspector { - $call - } - )+ - $data.journaled_state.depth -= 1; - } else { - $( - if let Some($id) = $inspector { - $call - } - )+ - } + $data.journaled_state.depth += $self.in_inner_context as usize; + $( + if let Some($id) = $inspector { + $call + } + )+ + $data.journaled_state.depth -= $self.in_inner_context as usize; }; ([$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $call:expr, $self:ident, $data:ident $(,)?) => { if $self.in_inner_context { @@ -303,6 +297,27 @@ impl InspectorStack { Self::default() } + /// Logs the status of the inspectors. + pub fn log_status(&self) { + trace!(enabled=%{ + let mut enabled = Vec::with_capacity(16); + macro_rules! push { + ($($id:ident),* $(,)?) => { + $( + if self.$id.is_some() { + enabled.push(stringify!($id)); + } + )* + }; + } + push!(cheatcodes, chisel_state, coverage, debugger, fuzzer, log_collector, printer, tracer); + if self.enable_isolation { + enabled.push("isolation"); + } + format!("[{}]", enabled.join(", ")) + }); + } + /// Set variables from an environment for the relevant inspectors. #[inline] pub fn set_env(&mut self, env: &Env) { @@ -624,7 +639,7 @@ impl Inspector<&mut DB> for InspectorStack { fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut EvmContext<&mut DB>) { call_inspectors_adjust_depth!( #[no_ret] - [&mut self.tracer, &mut self.cheatcodes, &mut self.chisel_state, &mut self.printer], + [&mut self.tracer, &mut self.chisel_state, &mut self.printer], |inspector| inspector.step_end(interpreter, ecx), self, ecx From 41a6945ca3ef8139ee15c2c52986b085792be7fa Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Fri, 14 Jun 2024 23:06:06 +0300 Subject: [PATCH 410/430] feat(invariant): introduce `afterInvariant` function (#8106) * feat(invariant): introduce tearDown function * Add Tests * Fix tests * tearDown -> afterInvariant refactor * Group has_invariants with tmp_tracing --- crates/common/src/traits.rs | 15 +++++ .../evm/evm/src/executors/invariant/error.rs | 2 +- crates/evm/evm/src/executors/invariant/mod.rs | 44 ++++++++++++++- .../evm/evm/src/executors/invariant/replay.rs | 27 +++++++-- .../evm/evm/src/executors/invariant/result.rs | 46 ++++++++++++---- .../evm/evm/src/executors/invariant/shrink.rs | 29 +++++++--- crates/evm/fuzz/src/invariant/mod.rs | 2 + crates/forge/src/runner.rs | 44 ++++++++++++--- crates/forge/tests/it/core.rs | 14 +++++ crates/forge/tests/it/invariant.rs | 55 +++++++++++++++++++ .../default/core/BadSigAfterInvariant.t.sol | 12 ++++ .../default/core/MultipleAfterInvariant.t.sol | 14 +++++ .../common/InvariantAfterInvariant.t.sol | 55 +++++++++++++++++++ 13 files changed, 324 insertions(+), 35 deletions(-) create mode 100644 testdata/default/core/BadSigAfterInvariant.t.sol create mode 100644 testdata/default/core/MultipleAfterInvariant.t.sol create mode 100644 testdata/default/fuzz/invariant/common/InvariantAfterInvariant.t.sol diff --git a/crates/common/src/traits.rs b/crates/common/src/traits.rs index 8ed1edbec..64d27563e 100644 --- a/crates/common/src/traits.rs +++ b/crates/common/src/traits.rs @@ -34,6 +34,9 @@ pub trait TestFunctionExt { /// Returns whether this function is a `setUp` function. fn is_setup(&self) -> bool; + /// Returns whether this function is `afterInvariant` function. + fn is_after_invariant(&self) -> bool; + /// Returns whether this function is a fixture function. fn is_fixture(&self) -> bool; } @@ -60,6 +63,10 @@ impl TestFunctionExt for Function { self.name.is_setup() } + fn is_after_invariant(&self) -> bool { + self.name.is_after_invariant() + } + fn is_fixture(&self) -> bool { self.name.is_fixture() } @@ -86,6 +93,10 @@ impl TestFunctionExt for String { self.as_str().is_setup() } + fn is_after_invariant(&self) -> bool { + self.as_str().is_after_invariant() + } + fn is_fixture(&self) -> bool { self.as_str().is_fixture() } @@ -112,6 +123,10 @@ impl TestFunctionExt for str { self.eq_ignore_ascii_case("setup") } + fn is_after_invariant(&self) -> bool { + self.eq_ignore_ascii_case("afterinvariant") + } + fn is_fixture(&self) -> bool { self.starts_with("fixture") } diff --git a/crates/evm/evm/src/executors/invariant/error.rs b/crates/evm/evm/src/executors/invariant/error.rs index 74f8d92cc..22d751ed5 100644 --- a/crates/evm/evm/src/executors/invariant/error.rs +++ b/crates/evm/evm/src/executors/invariant/error.rs @@ -88,7 +88,7 @@ impl FailedInvariantCaseData { debug_assert!(func.inputs.is_empty()); let origin = func.name.as_str(); Self { - test_error: proptest::test_runner::TestError::Fail( + test_error: TestError::Fail( format!("{origin}, reason: {revert_reason}").into(), calldata.to_vec(), ), diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 11291f173..4cbd8505d 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -2,7 +2,7 @@ use crate::{ executors::{Executor, RawCallResult}, inspectors::Fuzzer, }; -use alloy_primitives::{Address, FixedBytes, Selector, U256}; +use alloy_primitives::{Address, Bytes, FixedBytes, Selector, U256}; use alloy_sol_types::{sol, SolCall}; use eyre::{eyre, ContextCompat, Result}; use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; @@ -28,7 +28,7 @@ use proptest::{ strategy::{Strategy, ValueTree}, test_runner::{TestCaseError, TestRunner}, }; -use result::{assert_invariants, can_continue}; +use result::{assert_after_invariant, assert_invariants, can_continue}; use revm::primitives::HashMap; use shrink::shrink_sequence; use std::{cell::RefCell, collections::BTreeMap, sync::Arc}; @@ -43,6 +43,7 @@ mod result; pub use result::InvariantFuzzTestResult; mod shrink; +use crate::executors::EvmError; pub use shrink::check_sequence; sol! { @@ -65,6 +66,8 @@ sol! { string[] artifacts; } + function afterInvariant() external; + #[derive(Default)] function excludeArtifacts() public view returns (string[] memory excludedArtifacts); @@ -302,6 +305,19 @@ impl<'a> InvariantExecutor<'a> { ); } + // Call `afterInvariant` only if it is declared and test didn't fail already. + if invariant_contract.call_after_invariant && failures.borrow().error.is_none() { + assert_after_invariant( + &invariant_contract, + &self.config, + &targeted_contracts, + &mut executor, + &mut failures.borrow_mut(), + &inputs, + ) + .map_err(|_| TestCaseError::Fail("Failed to call afterInvariant".into()))?; + } + // We clear all the targeted contracts created during this run. if !created_contracts.is_empty() { let mut writable_targeted = targeted_contracts.targets.lock(); @@ -690,3 +706,27 @@ fn collect_data( state_changeset.insert(tx.sender, changed); } } + +/// Calls the `afterInvariant()` function on a contract. +/// Returns call result and if call succeeded. +/// The state after the call is not persisted. +pub(crate) fn call_after_invariant_function( + executor: &Executor, + to: Address, +) -> std::result::Result<(RawCallResult, bool), EvmError> { + let calldata = Bytes::from_static(&IInvariantTest::afterInvariantCall::SELECTOR); + let mut call_result = executor.call_raw(CALLER, to, calldata, U256::ZERO)?; + let success = executor.is_raw_call_mut_success(to, &mut call_result, false); + Ok((call_result, success)) +} + +/// Calls the invariant function and returns call result and if succeeded. +pub(crate) fn call_invariant_function( + executor: &Executor, + address: Address, + calldata: Bytes, +) -> Result<(RawCallResult, bool)> { + let mut call_result = executor.call_raw(CALLER, address, calldata, U256::ZERO)?; + let success = executor.is_raw_call_mut_success(address, &mut call_result, false); + Ok((call_result, success)) +} diff --git a/crates/evm/evm/src/executors/invariant/replay.rs b/crates/evm/evm/src/executors/invariant/replay.rs index c21c26030..d3d974d29 100644 --- a/crates/evm/evm/src/executors/invariant/replay.rs +++ b/crates/evm/evm/src/executors/invariant/replay.rs @@ -1,10 +1,12 @@ -use super::{error::FailedInvariantCaseData, shrink_sequence}; +use super::{ + call_after_invariant_function, call_invariant_function, error::FailedInvariantCaseData, + shrink_sequence, +}; use crate::executors::Executor; use alloy_dyn_abi::JsonAbiExt; use alloy_primitives::Log; use eyre::Result; use foundry_common::{ContractsByAddress, ContractsByArtifact}; -use foundry_evm_core::constants::CALLER; use foundry_evm_coverage::HitMaps; use foundry_evm_fuzz::{ invariant::{BasicTxDetails, InvariantContract}, @@ -75,15 +77,22 @@ pub fn replay_run( // Checking after each call doesn't add valuable info for passing scenario // (invariant call result is always success) nor for failed scenarios // (invariant call result is always success until the last call that breaks it). - let invariant_result = executor.call_raw( - CALLER, + let (invariant_result, invariant_success) = call_invariant_function( + &executor, invariant_contract.address, invariant_contract.invariant_function.abi_encode_input(&[])?.into(), - U256::ZERO, )?; traces.push((TraceKind::Execution, invariant_result.traces.clone().unwrap())); logs.extend(invariant_result.logs); + // Collect after invariant logs and traces. + if invariant_contract.call_after_invariant && invariant_success { + let (after_invariant_result, _) = + call_after_invariant_function(&executor, invariant_contract.address)?; + traces.push((TraceKind::Execution, after_invariant_result.traces.clone().unwrap())); + logs.extend(after_invariant_result.logs); + } + Ok(counterexample_sequence) } @@ -105,7 +114,13 @@ pub fn replay_error( TestError::Abort(_) => Ok(vec![]), TestError::Fail(_, ref calls) => { // Shrink sequence of failed calls. - let calls = shrink_sequence(failed_case, calls, &executor, progress)?; + let calls = shrink_sequence( + failed_case, + calls, + &executor, + invariant_contract.call_after_invariant, + progress, + )?; set_up_inner_replay(&mut executor, &failed_case.inner_sequence); diff --git a/crates/evm/evm/src/executors/invariant/result.rs b/crates/evm/evm/src/executors/invariant/result.rs index 9b3103b3d..5ddcccaf0 100644 --- a/crates/evm/evm/src/executors/invariant/result.rs +++ b/crates/evm/evm/src/executors/invariant/result.rs @@ -1,14 +1,16 @@ -use super::{error::FailedInvariantCaseData, InvariantFailures, InvariantFuzzError}; +use super::{ + call_after_invariant_function, call_invariant_function, error::FailedInvariantCaseData, + InvariantFailures, InvariantFuzzError, +}; use crate::executors::{Executor, RawCallResult}; use alloy_dyn_abi::JsonAbiExt; use eyre::Result; use foundry_config::InvariantConfig; -use foundry_evm_core::{constants::CALLER, utils::StateChangeset}; +use foundry_evm_core::utils::StateChangeset; use foundry_evm_fuzz::{ invariant::{BasicTxDetails, FuzzRunIdentifiedContracts, InvariantContract}, FuzzedCases, }; -use revm::primitives::U256; use revm_inspectors::tracing::CallTraceArena; use std::borrow::Cow; @@ -60,16 +62,11 @@ pub(crate) fn assert_invariants( } } - let func = invariant_contract.invariant_function; - let mut call_result = executor.call_raw( - CALLER, + let (call_result, success) = call_invariant_function( + executor, invariant_contract.address, - func.abi_encode_input(&[])?.into(), - U256::ZERO, + invariant_contract.invariant_function.abi_encode_input(&[])?.into(), )?; - - let success = - executor.is_raw_call_mut_success(invariant_contract.address, &mut call_result, false); if !success { // We only care about invariants which we haven't broken yet. if invariant_failures.error.is_none() { @@ -151,3 +148,30 @@ pub(crate) fn can_continue( } Ok(RichInvariantResults::new(true, call_results)) } + +/// Given the executor state, asserts conditions within `afterInvariant` function. +/// If call fails then the invariant test is considered failed. +pub(crate) fn assert_after_invariant( + invariant_contract: &InvariantContract<'_>, + invariant_config: &InvariantConfig, + targeted_contracts: &FuzzRunIdentifiedContracts, + executor: &mut Executor, + invariant_failures: &mut InvariantFailures, + inputs: &[BasicTxDetails], +) -> Result { + let (call_result, success) = + call_after_invariant_function(executor, invariant_contract.address)?; + // Fail the test case if `afterInvariant` doesn't succeed. + if !success { + let case_data = FailedInvariantCaseData::new( + invariant_contract, + invariant_config, + targeted_contracts, + inputs, + call_result, + &[], + ); + invariant_failures.error = Some(InvariantFuzzError::BrokenInvariant(case_data)); + } + Ok(success) +} diff --git a/crates/evm/evm/src/executors/invariant/shrink.rs b/crates/evm/evm/src/executors/invariant/shrink.rs index 169955551..5559ec821 100644 --- a/crates/evm/evm/src/executors/invariant/shrink.rs +++ b/crates/evm/evm/src/executors/invariant/shrink.rs @@ -1,6 +1,10 @@ -use crate::executors::{invariant::error::FailedInvariantCaseData, Executor}; +use crate::executors::{ + invariant::{ + call_after_invariant_function, call_invariant_function, error::FailedInvariantCaseData, + }, + Executor, +}; use alloy_primitives::{Address, Bytes, U256}; -use foundry_evm_core::constants::CALLER; use foundry_evm_fuzz::invariant::BasicTxDetails; use indicatif::ProgressBar; use proptest::bits::{BitSetLike, VarBitSet}; @@ -84,6 +88,7 @@ pub(crate) fn shrink_sequence( failed_case: &FailedInvariantCaseData, calls: &[BasicTxDetails], executor: &Executor, + call_after_invariant: bool, progress: Option<&ProgressBar>, ) -> eyre::Result> { trace!(target: "forge::test", "Shrinking sequence of {} calls.", calls.len()); @@ -97,9 +102,9 @@ pub(crate) fn shrink_sequence( // Special case test: the invariant is *unsatisfiable* - it took 0 calls to // break the invariant -- consider emitting a warning. - let error_call_result = - executor.call_raw(CALLER, failed_case.addr, failed_case.calldata.clone(), U256::ZERO)?; - if error_call_result.reverted { + let (_, success) = + call_invariant_function(executor, failed_case.addr, failed_case.calldata.clone())?; + if !success { return Ok(vec![]); } @@ -113,6 +118,7 @@ pub(crate) fn shrink_sequence( failed_case.addr, failed_case.calldata.clone(), failed_case.fail_on_revert, + call_after_invariant, ) { // If candidate sequence still fails then shrink more if possible. Ok((false, _)) if !shrinker.simplify() => break, @@ -133,7 +139,8 @@ pub(crate) fn shrink_sequence( /// Checks if the given call sequence breaks the invariant. /// Used in shrinking phase for checking candidate sequences and in replay failures phase to test /// persisted failures. -/// Returns the result of invariant check and if sequence was entirely applied. +/// Returns the result of invariant check (and afterInvariant call if needed) and if sequence was +/// entirely applied. pub fn check_sequence( mut executor: Executor, calls: &[BasicTxDetails], @@ -141,6 +148,7 @@ pub fn check_sequence( test_address: Address, calldata: Bytes, fail_on_revert: bool, + call_after_invariant: bool, ) -> eyre::Result<(bool, bool)> { // Apply the call sequence. for call_index in sequence { @@ -159,7 +167,12 @@ pub fn check_sequence( } // Check the invariant for call sequence. - let mut call_result = executor.call_raw(CALLER, test_address, calldata, U256::ZERO)?; - let success = executor.is_raw_call_mut_success(test_address, &mut call_result, false); + let (_, mut success) = call_invariant_function(&executor, test_address, calldata)?; + // Check after invariant result if invariant is success and `afterInvariant` function is + // declared. + if success && call_after_invariant { + (_, success) = call_after_invariant_function(&executor, test_address)?; + } + Ok((success, true)) } diff --git a/crates/evm/fuzz/src/invariant/mod.rs b/crates/evm/fuzz/src/invariant/mod.rs index e83c59549..26c94351e 100644 --- a/crates/evm/fuzz/src/invariant/mod.rs +++ b/crates/evm/fuzz/src/invariant/mod.rs @@ -71,6 +71,8 @@ pub struct InvariantContract<'a> { pub address: Address, /// Invariant function present in the test contract. pub invariant_function: &'a Function, + /// If true, `afterInvariant` function is called after each invariant run. + pub call_after_invariant: bool, /// ABI of the test contract. pub abi: &'a JsonAbi, } diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index fe4a675e8..ffd22bf10 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -283,12 +283,10 @@ impl<'a> ContractRunner<'a> { let start = Instant::now(); let mut warnings = Vec::new(); + // Check if `setUp` function with valid signature declared. let setup_fns: Vec<_> = self.contract.abi.functions().filter(|func| func.name.is_setup()).collect(); - - // Whether to call the `setUp` function. let call_setup = setup_fns.len() == 1 && setup_fns[0].name == "setUp"; - // There is a single miss-cased `setUp` function, so we add a warning for &setup_fn in setup_fns.iter() { if setup_fn.name != "setUp" { @@ -298,7 +296,6 @@ impl<'a> ContractRunner<'a> { )); } } - // There are multiple setUp function, so we return a single test result for `setUp` if setup_fns.len() > 1 { return SuiteResult::new( @@ -309,9 +306,34 @@ impl<'a> ContractRunner<'a> { ) } - let has_invariants = self.contract.abi.functions().any(|func| func.is_invariant_test()); + // Check if `afterInvariant` function with valid signature declared. + let after_invariant_fns: Vec<_> = + self.contract.abi.functions().filter(|func| func.name.is_after_invariant()).collect(); + if after_invariant_fns.len() > 1 { + // Return a single test result failure if multiple functions declared. + return SuiteResult::new( + start.elapsed(), + [( + "afterInvariant()".to_string(), + TestResult::fail("multiple afterInvariant functions".to_string()), + )] + .into(), + warnings, + ) + } + let call_after_invariant = after_invariant_fns.first().map_or(false, |after_invariant_fn| { + let match_sig = after_invariant_fn.name == "afterInvariant"; + if !match_sig { + warnings.push(format!( + "Found invalid afterInvariant function \"{}\" did you mean \"afterInvariant()\"?", + after_invariant_fn.signature() + )); + } + match_sig + }); // Invariant testing requires tracing to figure out what contracts were created. + let has_invariants = self.contract.abi.functions().any(|func| func.is_invariant_test()); let tmp_tracing = self.executor.inspector.tracer.is_none() && has_invariants && call_setup; if tmp_tracing { self.executor.set_tracing(true); @@ -392,6 +414,7 @@ impl<'a> ContractRunner<'a> { setup, invariant_config.clone(), func, + call_after_invariant, &known_contracts, identified_contracts.as_ref().unwrap(), ) @@ -511,12 +534,14 @@ impl<'a> ContractRunner<'a> { } #[instrument(level = "debug", name = "invariant", skip_all)] + #[allow(clippy::too_many_arguments)] pub fn run_invariant_test( &self, runner: TestRunner, setup: TestSetup, invariant_config: InvariantConfig, func: &Function, + call_after_invariant: bool, known_contracts: &ContractsByArtifact, identified_contracts: &ContractsByAddress, ) -> TestResult { @@ -551,8 +576,12 @@ impl<'a> ContractRunner<'a> { identified_contracts, known_contracts, ); - let invariant_contract = - InvariantContract { address, invariant_function: func, abi: &self.contract.abi }; + let invariant_contract = InvariantContract { + address, + invariant_function: func, + call_after_invariant, + abi: &self.contract.abi, + }; let mut logs = logs.clone(); let mut traces = traces.clone(); @@ -584,6 +613,7 @@ impl<'a> ContractRunner<'a> { invariant_contract.address, invariant_contract.invariant_function.selector().to_vec().into(), invariant_config.fail_on_revert, + invariant_contract.call_after_invariant, ) { if !success { // If sequence still fails then replay error to collect traces and diff --git a/crates/forge/tests/it/core.rs b/crates/forge/tests/it/core.rs index b229f151f..4c6fae77c 100644 --- a/crates/forge/tests/it/core.rs +++ b/crates/forge/tests/it/core.rs @@ -75,6 +75,20 @@ async fn test_core() { None, )], ), + ( + "default/core/MultipleAfterInvariant.t.sol:MultipleAfterInvariant", + vec![( + "afterInvariant()", + false, + Some("multiple afterInvariant functions".to_string()), + None, + None, + )], + ), + ( + "default/core/BadSigAfterInvariant.t.sol:BadSigAfterInvariant", + vec![("testShouldPassWithWarning()", true, None, None, None)], + ), ]), ); } diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index fbd906d0c..f2a8d69f9 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -27,6 +27,7 @@ macro_rules! get_counterexample { async fn test_invariant() { let filter = Filter::new(".*", ".*", ".*fuzz/invariant/(target|targetAbi|common)"); let mut runner = TEST_DATA_DEFAULT.runner(); + runner.test_options = TEST_DATA_DEFAULT.test_opts.clone(); runner.test_options.invariant.failure_persist_dir = Some(tempfile::tempdir().unwrap().into_path()); let results = runner.test_collect(&filter); @@ -243,6 +244,26 @@ async fn test_invariant() { ( "default/fuzz/invariant/common/InvariantExcludedSenders.t.sol:InvariantExcludedSendersTest", vec![("invariant_check_sender()", true, None, None, None)], + ), + ( + "default/fuzz/invariant/common/InvariantAfterInvariant.t.sol:InvariantAfterInvariantTest", + vec![ + ( + "invariant_after_invariant_failure()", + false, + Some("revert: afterInvariant failure".into()), + None, + None, + ), + ( + "invariant_failure()", + false, + Some("revert: invariant failure".into()), + None, + None, + ), + ("invariant_success()", true, None, None, None), + ], ) ]), ); @@ -710,3 +731,37 @@ async fn test_invariant_excluded_senders() { )]), ); } + +#[tokio::test(flavor = "multi_thread")] +async fn test_invariant_after_invariant() { + // Check failure on passing invariant and failed `afterInvariant` condition + let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantAfterInvariant.t.sol"); + let mut runner = TEST_DATA_DEFAULT.runner(); + runner.test_options.invariant.failure_persist_dir = + Some(tempfile::tempdir().unwrap().into_path()); + + let results = runner.test_collect(&filter); + assert_multiple( + &results, + BTreeMap::from([( + "default/fuzz/invariant/common/InvariantAfterInvariant.t.sol:InvariantAfterInvariantTest", + vec![ + ( + "invariant_after_invariant_failure()", + false, + Some("revert: afterInvariant failure".into()), + None, + None, + ), + ( + "invariant_failure()", + false, + Some("revert: invariant failure".into()), + None, + None, + ), + ("invariant_success()", true, None, None, None), + ], + )]), + ); +} diff --git a/testdata/default/core/BadSigAfterInvariant.t.sol b/testdata/default/core/BadSigAfterInvariant.t.sol new file mode 100644 index 000000000..6d303b04b --- /dev/null +++ b/testdata/default/core/BadSigAfterInvariant.t.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; + +contract BadSigAfterInvariant is DSTest { + function afterinvariant() public {} + + function testShouldPassWithWarning() public { + assert(true); + } +} diff --git a/testdata/default/core/MultipleAfterInvariant.t.sol b/testdata/default/core/MultipleAfterInvariant.t.sol new file mode 100644 index 000000000..446e76cbb --- /dev/null +++ b/testdata/default/core/MultipleAfterInvariant.t.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; + +contract MultipleAfterInvariant is DSTest { + function afterInvariant() public {} + + function afterinvariant() public {} + + function testFailShouldBeMarkedAsFailedBecauseOfAfterInvariant() public { + assert(true); + } +} diff --git a/testdata/default/fuzz/invariant/common/InvariantAfterInvariant.t.sol b/testdata/default/fuzz/invariant/common/InvariantAfterInvariant.t.sol new file mode 100644 index 000000000..3030b43e0 --- /dev/null +++ b/testdata/default/fuzz/invariant/common/InvariantAfterInvariant.t.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "ds-test/test.sol"; + +struct FuzzSelector { + address addr; + bytes4[] selectors; +} + +contract AfterInvariantHandler { + uint256 public count; + + function inc() external { + count += 1; + } +} + +contract InvariantAfterInvariantTest is DSTest { + AfterInvariantHandler handler; + + function setUp() public { + handler = new AfterInvariantHandler(); + } + + function targetSelectors() public returns (FuzzSelector[] memory) { + FuzzSelector[] memory targets = new FuzzSelector[](1); + bytes4[] memory selectors = new bytes4[](1); + selectors[0] = handler.inc.selector; + targets[0] = FuzzSelector(address(handler), selectors); + return targets; + } + + function afterInvariant() public { + require(handler.count() < 10, "afterInvariant failure"); + } + + /// forge-config: default.invariant.runs = 1 + /// forge-config: default.invariant.depth = 11 + function invariant_after_invariant_failure() public view { + require(handler.count() < 20, "invariant after invariant failure"); + } + + /// forge-config: default.invariant.runs = 1 + /// forge-config: default.invariant.depth = 11 + function invariant_failure() public view { + require(handler.count() < 9, "invariant failure"); + } + + /// forge-config: default.invariant.runs = 1 + /// forge-config: default.invariant.depth = 5 + function invariant_success() public view { + require(handler.count() < 11, "invariant should not fail"); + } +} From 47b2ce24bd1dc4abba6424b06387f5bd424caa7a Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Sat, 15 Jun 2024 14:08:48 +0530 Subject: [PATCH 411/430] fix(forge-bind): allow attrs and mod single_file imports (#8171) * fix(forge-bind): allow attrs and mod single_file imports * fmt nit * allow rustdoc::all * fix: file consistenct check * fix clippy --- crates/anvil/tests/it/main.rs | 1 + crates/sol-macro-gen/src/sol_macro_gen.rs | 88 +++++++++-------------- 2 files changed, 34 insertions(+), 55 deletions(-) diff --git a/crates/anvil/tests/it/main.rs b/crates/anvil/tests/it/main.rs index 5337e5bbd..559593b72 100644 --- a/crates/anvil/tests/it/main.rs +++ b/crates/anvil/tests/it/main.rs @@ -1,3 +1,4 @@ +#![allow(clippy::octal_escapes)] mod abi; mod anvil; mod anvil_api; diff --git a/crates/sol-macro-gen/src/sol_macro_gen.rs b/crates/sol-macro-gen/src/sol_macro_gen.rs index 6ef48f8cb..7867b6673 100644 --- a/crates/sol-macro-gen/src/sol_macro_gen.rs +++ b/crates/sol-macro-gen/src/sol_macro_gen.rs @@ -130,22 +130,15 @@ edition = "2021" fs::write(cargo_toml_path, toml_contents).wrap_err("Failed to write Cargo.toml")?; let mut lib_contents = String::new(); - if single_file { - write!( - &mut lib_contents, - r#"#![allow(unused_imports, clippy::all)] - //! This module contains the sol! generated bindings for solidity contracts. - //! This is autogenerated code. - //! Do not manually edit these files. - //! These files may be overwritten by the codegen system at any time."# - )?; - } else { - write!( - &mut lib_contents, - r#"#![allow(unused_imports)] - "# - )?; - }; + write!( + &mut lib_contents, + r#"#![allow(unused_imports, clippy::all, rustdoc::all)] + //! This module contains the sol! generated bindings for solidity contracts. + //! This is autogenerated code. + //! Do not manually edit these files. + //! These files may be overwritten by the codegen system at any time. + "# + )?; // Write src for instance in &self.instances { @@ -179,7 +172,7 @@ edition = "2021" let _ = fs::create_dir_all(bindings_path); - let mut mod_contents = r#"#![allow(clippy::all)] + let mut mod_contents = r#"#![allow(unused_imports, clippy::all, rustdoc::all)] //! This module contains the sol! generated bindings for solidity contracts. //! This is autogenerated code. //! Do not manually edit these files. @@ -190,16 +183,14 @@ edition = "2021" for instance in &self.instances { let name = instance.name.to_lowercase(); if !single_file { + // Module write!( mod_contents, r#"pub mod {}; "#, instance.name.to_lowercase() )?; - let mut contents = - r#"//! This module was autogenerated by the alloy sol!. - //! More information can be found here . - "#.to_string(); + let mut contents = String::new(); write!(contents, "{}", instance.expansion.as_ref().unwrap())?; let file = syn::parse_file(&contents)?; @@ -208,13 +199,8 @@ edition = "2021" fs::write(bindings_path.join(format!("{}.rs", name)), contents) .wrap_err("Failed to write file")?; } else { - let mut contents = format!( - r#"//! This module was autogenerated by the alloy sol!. - //! More information can be found here . - pub use {}::*; - "#, - name - ); + // Single File + let mut contents = String::new(); write!(contents, "{}\n\n", instance.expansion.as_ref().unwrap())?; write!(mod_contents, "{}", contents)?; } @@ -250,29 +236,23 @@ edition = "2021" } let mut super_contents = String::new(); - if is_mod { - // mod.rs - write!( - &mut super_contents, - r#"#![allow(clippy::all)] - //! This module contains the sol! generated bindings for solidity contracts. - //! This is autogenerated code. - //! Do not manually edit these files. - //! These files may be overwritten by the codegen system at any time. - "# - )?; - } else { - // lib.rs - write!( - &mut super_contents, - r#"#![allow(unused_imports)] + write!( + &mut super_contents, + r#"#![allow(unused_imports, clippy::all, rustdoc::all)] + //! This module contains the sol! generated bindings for solidity contracts. + //! This is autogenerated code. + //! Do not manually edit these files. + //! These files may be overwritten by the codegen system at any time. "# - )?; - }; + )?; if !single_file { for instance in &self.instances { let name = instance.name.to_lowercase(); - let path = crate_path.join(format!("src/{}.rs", name)); + let path = if is_mod { + crate_path.join(format!("{}.rs", name)) + } else { + crate_path.join(format!("src/{}.rs", name)) + }; let tokens = instance .expansion .as_ref() @@ -281,18 +261,16 @@ edition = "2021" self.check_file_contents(&path, &tokens)?; - if !is_mod { - write!( - &mut super_contents, - r#"pub mod {}; + write!( + &mut super_contents, + r#"pub mod {}; "#, - name - )?; - } + name + )?; } let super_path = - if is_mod { crate_path.join("src/mod.rs") } else { crate_path.join("src/lib.rs") }; + if is_mod { crate_path.join("mod.rs") } else { crate_path.join("src/lib.rs") }; self.check_file_contents(&super_path, &super_contents)?; } From fd878884eda640c2cc59a7fa66aef9288846ef0e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 16 Jun 2024 13:51:40 +0200 Subject: [PATCH 412/430] chore(deps): weekly `cargo update` (#8172) * chore(deps): weekly `cargo update` Updating git repository `https://github.com/bluealloy/revm.git` Updating git repository `https://github.com/alloy-rs/alloy` Updating git repository `https://github.com/paradigmxyz/revm-inspectors` Locking 45 packages to latest compatible versions Updating alloy-chains v0.1.18 -> v0.1.20 Updating aws-runtime v1.2.2 -> v1.2.3 Updating aws-sigv4 v1.2.1 -> v1.2.2 Updating aws-smithy-runtime-api v1.6.2 -> v1.7.0 Updating aws-smithy-types v1.1.10 -> v1.2.0 Updating clap v4.5.6 -> v4.5.7 Updating clap_builder v4.5.6 -> v4.5.7 Updating derive_more v0.99.17 -> v0.99.18 Updating fs4 v0.8.3 -> v0.8.4 Updating http-body-util v0.1.1 -> v0.1.2 Updating httparse v1.8.0 -> v1.9.3 Adding icu_collections v1.5.0 Adding icu_locid v1.5.0 Adding icu_locid_transform v1.5.0 Adding icu_locid_transform_data v1.5.0 Adding icu_normalizer v1.5.0 Adding icu_normalizer_data v1.5.0 Adding icu_properties v1.5.0 Adding icu_properties_data v1.5.0 Adding icu_provider v1.5.0 Adding icu_provider_macros v1.5.0 Updating idna v0.5.0 -> v1.0.0 Updating interprocess v2.1.1 -> v2.2.0 Adding litemap v0.7.3 Updating memchr v2.7.2 -> v2.7.4 Updating redox_syscall v0.5.1 -> v0.5.2 Updating regex v1.10.4 -> v1.10.5 Updating regex-automata v0.4.6 -> v0.4.7 Updating regex-lite v0.1.5 -> v0.1.6 Updating regex-syntax v0.8.3 -> v0.8.4 Updating rustls v0.23.9 -> v0.23.10 Adding stable_deref_trait v1.2.0 Adding synstructure v0.13.1 Adding tinystr v0.7.6 Removing unicode-bidi v0.3.15 Updating url v2.5.0 -> v2.5.1 Adding utf16_iter v1.0.5 Adding utf8_iter v1.0.4 Adding write16 v1.0.0 Adding writeable v0.5.5 Adding yoke v0.7.4 Adding yoke-derive v0.7.4 Adding zerofrom v0.1.4 Adding zerofrom-derive v0.1.4 Adding zerovec v0.10.2 Adding zerovec-derive v0.10.2 note: pass `--verbose` to see 158 unchanged dependencies behind latest * allow unicode --------- Co-authored-by: mattsse <19890894+mattsse@users.noreply.github.com> Co-authored-by: Matthias Seitz --- Cargo.lock | 374 +++++++++++++++++++++++++++++++++++++++++++---------- deny.toml | 1 + 2 files changed, 307 insertions(+), 68 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 596b73f95..1865177e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,9 +68,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "alloy-chains" -version = "0.1.18" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03fd095a9d70f4b1c5c102c84a4c782867a5c6416dbf6dcd42a63e7c7a89d3c8" +checksum = "d2feb5f466b3a786d5a622d8926418bc6a0d38bf632909f6ee9298a4a1d8c6e0" dependencies = [ "num_enum", "serde", @@ -626,7 +626,7 @@ dependencies = [ "alloy-transport", "futures", "http 1.1.0", - "rustls 0.23.9", + "rustls 0.23.10", "serde_json", "tokio", "tokio-tungstenite 0.23.0", @@ -1181,9 +1181,9 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.2.2" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75588e7ee5e8496eed939adac2035a6dbab9f7eb2acdd9ab2d31856dab6f3955" +checksum = "36978815abdd7297662bf906adff132941a02ecf425bc78fac7d90653ce87560" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -1293,9 +1293,9 @@ dependencies = [ [[package]] name = "aws-sigv4" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58b56f1cbe6fd4d0c2573df72868f20ab1c125ca9c9dbce17927a463433a2e57" +checksum = "31eed8d45759b2c5fe7fd304dd70739060e9e0de509209036eabea14d0720cce" dependencies = [ "aws-credential-types", "aws-smithy-http", @@ -1392,9 +1392,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime-api" -version = "1.6.2" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4179bd8a1c943e1aceb46c5b9fc014a561bd6c35a2153e816ba29076ee49d245" +checksum = "1b570ea39eb95bd32543f6e4032bce172cb6209b9bc8c83c770d08169e875afc" dependencies = [ "aws-smithy-async", "aws-smithy-types", @@ -1409,9 +1409,9 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.1.10" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b6764ba7e1c5ede1c9f9e4046645534f06c2581402461c559b481a420330a83" +checksum = "cfe321a6b21f5d8eabd0ade9c55d3d0335f3c3157fc2b3e87f05f34b539e4df5" dependencies = [ "base64-simd", "bytes", @@ -1711,7 +1711,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" dependencies = [ "memchr", - "regex-automata 0.4.6", + "regex-automata 0.4.7", "serde", ] @@ -2033,9 +2033,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.6" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9689a29b593160de5bc4aacab7b5d54fb52231de70122626c178e6a368994c7" +checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" dependencies = [ "clap_builder", "clap_derive", @@ -2043,9 +2043,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.6" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5387378c84f6faa26890ebf9f0a92989f8873d4d380467bcd0d8d8620424df" +checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" dependencies = [ "anstream", "anstyle", @@ -2633,15 +2633,15 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.17" +version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "convert_case", "proc-macro2", "quote", "rustc_version 0.4.0", - "syn 1.0.109", + "syn 2.0.66", ] [[package]] @@ -3929,9 +3929,9 @@ checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" [[package]] name = "fs4" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73969b81e8bc90a3828d913dd3973d80771bfb9d7fbe1a78a79122aad456af15" +checksum = "f7e180ac76c23b45e767bd7ae9579bc0bb458618c4bc71835926e098e61d15f8" dependencies = [ "rustix", "windows-sys 0.52.0", @@ -4369,8 +4369,8 @@ dependencies = [ "aho-corasick", "bstr 1.9.1", "log", - "regex-automata 0.4.6", - "regex-syntax 0.8.3", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", ] [[package]] @@ -4560,12 +4560,12 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", - "futures-core", + "futures-util", "http 1.1.0", "http-body 1.0.0", "pin-project-lite", @@ -4579,9 +4579,9 @@ checksum = "08a397c49fec283e3d6211adbe480be95aae5f304cfb923e9970e08956d5168a" [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "d0e7a4dd27b9476dc40cb050d3632d3bba3a70ddbff012285f7f8559a1e7e545" [[package]] name = "httpdate" @@ -4753,6 +4753,124 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f8ac670d7422d7f76b32e17a5db556510825b29ec9154f235977c9caba61036" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -4761,12 +4879,14 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.5.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "4716a3a0933a1d01c2f72450e89596eb51dd34ef3c211ccd875acdf1f8fe47ed" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "icu_normalizer", + "icu_properties", + "smallvec", + "utf8_iter", ] [[package]] @@ -4779,7 +4899,7 @@ dependencies = [ "globset", "log", "memchr", - "regex-automata 0.4.6", + "regex-automata 0.4.7", "same-file", "walkdir", "winapi-util", @@ -4926,9 +5046,9 @@ dependencies = [ [[package]] name = "interprocess" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13f2533e1f1a70bec71ea7a85d1c0a4dab141c314035ce76e51a19a2f48be708" +checksum = "67bafc2f5dbdad79a6d925649758d5472647b416028099f0b829d1b67fdd47d3" dependencies = [ "doctest-file", "futures-core", @@ -5114,7 +5234,7 @@ dependencies = [ "lalrpop-util", "petgraph", "regex", - "regex-syntax 0.8.3", + "regex-syntax 0.8.4", "string_cache", "term", "tiny-keccak", @@ -5128,7 +5248,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" dependencies = [ - "regex-automata 0.4.6", + "regex-automata 0.4.7", ] [[package]] @@ -5190,6 +5310,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + [[package]] name = "lock_api" version = "0.4.12" @@ -5302,9 +5428,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" @@ -5878,7 +6004,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.1", + "redox_syscall 0.5.2", "smallvec", "windows-targets 0.52.5", ] @@ -6371,7 +6497,7 @@ dependencies = [ "rand", "rand_chacha", "rand_xorshift", - "regex-syntax 0.8.3", + "regex-syntax 0.8.4", "rusty-fork", "tempfile", "unarray", @@ -6405,7 +6531,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", - "itertools 0.11.0", + "itertools 0.12.1", "proc-macro2", "quote", "syn 2.0.66", @@ -6594,9 +6720,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ "bitflags 2.5.0", ] @@ -6614,14 +6740,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.4" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.6", - "regex-syntax 0.8.3", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", ] [[package]] @@ -6635,20 +6761,20 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.3", + "regex-syntax 0.8.4", ] [[package]] name = "regex-lite" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b661b2f27137bdbc16f00eda72866a92bb28af1753ffbd56744fb6e2e9cd8e" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" [[package]] name = "regex-syntax" @@ -6658,9 +6784,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "reqwest" @@ -7027,9 +7153,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.9" +version = "0.23.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a218f0f6d05669de4eabfb24f31ce802035c952429d037507b4a4a39f0e60c5b" +checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402" dependencies = [ "once_cell", "ring", @@ -7772,6 +7898,12 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" @@ -7942,6 +8074,17 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -8133,6 +8276,16 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinytemplate" version = "1.2.1" @@ -8235,7 +8388,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.9", + "rustls 0.23.10", "rustls-pki-types", "tokio", ] @@ -8272,7 +8425,7 @@ checksum = "becd34a233e7e31a3dbf7c7241b38320f57393dcae8e7324b0167d21b8e320b0" dependencies = [ "futures-util", "log", - "rustls 0.23.9", + "rustls 0.23.10", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -8578,7 +8731,7 @@ dependencies = [ "httparse", "log", "rand", - "rustls 0.23.9", + "rustls 0.23.10", "rustls-pki-types", "sha1", "thiserror", @@ -8634,12 +8787,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - [[package]] name = "unicode-bom" version = "2.0.3" @@ -8697,9 +8844,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "f7c25da092f0a868cdf09e8674cd3b7ef3a7d92a24253e663a2fb85e2496de56" dependencies = [ "form_urlencoded", "idna", @@ -8718,6 +8865,18 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -9270,6 +9429,18 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "ws_stream_wasm" version = "0.7.4" @@ -9313,6 +9484,30 @@ dependencies = [ "is-terminal", ] +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.7.34" @@ -9333,6 +9528,27 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.1" @@ -9353,6 +9569,28 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "zerovec" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2cc8827d6c0994478a15c53f374f46fbd41bea663d809b14744bc42e6b109c" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97cf56601ee5052b4417d90c8755c6683473c926039908196cf35d99f893ebe7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "zip" version = "0.6.6" diff --git a/deny.toml b/deny.toml index d585fd650..fa335ee73 100644 --- a/deny.toml +++ b/deny.toml @@ -40,6 +40,7 @@ allow = [ "ISC", "Unicode-DFS-2016", "OpenSSL", + "Unicode-3.0", "Unlicense", "WTFPL", "BSL-1.0", From 76e23be0f11b08b6e2fe10b6537ee6a5c4de4f67 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 17 Jun 2024 13:40:09 +0200 Subject: [PATCH 413/430] chore(deps): bump alloy, revm (#8177) * chore(deps): bump alloy, revm * doctests --- Cargo.lock | 111 +++++++++++-------- Cargo.toml | 58 +++++----- crates/anvil/Cargo.toml | 14 ++- crates/anvil/core/src/eth/mod.rs | 3 +- crates/anvil/core/src/eth/transaction/mod.rs | 5 +- crates/anvil/src/cmd.rs | 2 +- crates/anvil/src/config.rs | 18 +-- crates/anvil/src/eth/api.rs | 3 +- crates/anvil/src/eth/backend/fork.rs | 3 +- crates/anvil/src/eth/backend/mem/mod.rs | 3 +- crates/anvil/src/eth/error.rs | 1 + crates/anvil/src/eth/otterscan/types.rs | 3 +- crates/anvil/src/eth/sign.rs | 6 +- crates/anvil/src/evm.rs | 4 +- crates/anvil/src/lib.rs | 6 +- crates/anvil/tests/it/anvil_api.rs | 7 +- crates/anvil/tests/it/api.rs | 17 +-- crates/anvil/tests/it/eip4844.rs | 3 +- crates/anvil/tests/it/fork.rs | 17 +-- crates/anvil/tests/it/gas.rs | 11 +- crates/anvil/tests/it/logs.rs | 8 +- crates/anvil/tests/it/optimism.rs | 9 +- crates/anvil/tests/it/otterscan.rs | 3 +- crates/anvil/tests/it/pubsub.rs | 13 ++- crates/anvil/tests/it/revert.rs | 3 +- crates/anvil/tests/it/sign.rs | 9 +- crates/anvil/tests/it/traces.rs | 9 +- crates/anvil/tests/it/transaction.rs | 6 +- crates/anvil/tests/it/txpool.rs | 3 +- crates/anvil/tests/it/utils.rs | 26 +++-- crates/cast/Cargo.toml | 17 +-- crates/cast/bin/cmd/mktx.rs | 4 +- crates/cast/bin/cmd/send.rs | 9 +- crates/cast/bin/cmd/wallet/mod.rs | 38 ++++--- crates/cast/bin/cmd/wallet/vanity.rs | 16 +-- crates/cast/bin/tx.rs | 3 +- crates/cast/src/lib.rs | 12 +- crates/cheatcodes/Cargo.toml | 2 +- crates/cheatcodes/src/error.rs | 4 +- crates/cheatcodes/src/script.rs | 6 +- crates/cheatcodes/src/utils.rs | 10 +- crates/common/Cargo.toml | 31 ++++-- crates/common/src/fmt/ui.rs | 4 +- crates/common/src/provider/mod.rs | 12 +- crates/common/src/transactions.rs | 6 +- crates/evm/core/Cargo.toml | 12 +- crates/evm/core/src/backend/mod.rs | 3 +- crates/evm/core/src/fork/backend.rs | 3 +- crates/forge/Cargo.toml | 16 +-- crates/forge/bin/cmd/create.rs | 7 +- crates/forge/tests/cli/create.rs | 6 +- crates/forge/tests/cli/utils.rs | 4 +- crates/script/Cargo.toml | 1 + crates/script/src/broadcast.rs | 11 +- crates/script/src/sequence.rs | 3 +- crates/script/src/transaction.rs | 3 +- crates/wallets/Cargo.toml | 2 +- crates/wallets/src/error.rs | 4 +- crates/wallets/src/utils.rs | 6 +- crates/wallets/src/wallet_signer.rs | 8 +- 60 files changed, 369 insertions(+), 278 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1865177e6..b7080d198 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,7 +80,7 @@ dependencies = [ [[package]] name = "alloy-consensus" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" +source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" dependencies = [ "alloy-eips", "alloy-primitives", @@ -93,7 +93,7 @@ dependencies = [ [[package]] name = "alloy-contract" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" +source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -133,7 +133,7 @@ dependencies = [ [[package]] name = "alloy-eips" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" +source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -148,7 +148,7 @@ dependencies = [ [[package]] name = "alloy-genesis" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" +source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" dependencies = [ "alloy-primitives", "alloy-serde", @@ -171,7 +171,7 @@ dependencies = [ [[package]] name = "alloy-json-rpc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" +source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" dependencies = [ "alloy-primitives", "serde", @@ -183,13 +183,14 @@ dependencies = [ [[package]] name = "alloy-network" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" +source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" dependencies = [ "alloy-consensus", "alloy-eips", "alloy-json-rpc", "alloy-primitives", "alloy-rpc-types-eth", + "alloy-serde", "alloy-signer", "alloy-sol-types", "async-trait", @@ -228,7 +229,7 @@ dependencies = [ [[package]] name = "alloy-provider" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" +source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" dependencies = [ "alloy-chains", "alloy-consensus", @@ -240,6 +241,7 @@ dependencies = [ "alloy-rpc-client", "alloy-rpc-types-eth", "alloy-rpc-types-trace", + "alloy-rpc-types-txpool", "alloy-transport", "alloy-transport-http", "alloy-transport-ipc", @@ -263,7 +265,7 @@ dependencies = [ [[package]] name = "alloy-pubsub" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" +source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -303,7 +305,7 @@ dependencies = [ [[package]] name = "alloy-rpc-client" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" +source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -327,17 +329,18 @@ dependencies = [ [[package]] name = "alloy-rpc-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" +source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" dependencies = [ "alloy-rpc-types-eth", "alloy-rpc-types-trace", + "alloy-rpc-types-txpool", "alloy-serde", ] [[package]] name = "alloy-rpc-types-engine" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" +source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" dependencies = [ "alloy-consensus", "alloy-eips", @@ -354,11 +357,10 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" +source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" dependencies = [ "alloy-consensus", "alloy-eips", - "alloy-genesis", "alloy-primitives", "alloy-rlp", "alloy-serde", @@ -372,7 +374,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" +source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -381,10 +383,21 @@ dependencies = [ "serde_json", ] +[[package]] +name = "alloy-rpc-types-txpool" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "serde", +] + [[package]] name = "alloy-serde" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" +source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" dependencies = [ "alloy-primitives", "serde", @@ -394,7 +407,7 @@ dependencies = [ [[package]] name = "alloy-signer" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" +source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -409,7 +422,7 @@ dependencies = [ [[package]] name = "alloy-signer-aws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" +source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" dependencies = [ "alloy-consensus", "alloy-network", @@ -426,7 +439,7 @@ dependencies = [ [[package]] name = "alloy-signer-gcp" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" +source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" dependencies = [ "alloy-consensus", "alloy-network", @@ -443,7 +456,7 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" +source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -460,38 +473,38 @@ dependencies = [ ] [[package]] -name = "alloy-signer-trezor" +name = "alloy-signer-local" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" +source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" dependencies = [ "alloy-consensus", "alloy-network", "alloy-primitives", "alloy-signer", "async-trait", - "semver 1.0.23", + "coins-bip32", + "coins-bip39", + "elliptic-curve", + "eth-keystore", + "k256", + "rand", "thiserror", - "tracing", - "trezor-client", ] [[package]] -name = "alloy-signer-wallet" +name = "alloy-signer-trezor" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" +source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" dependencies = [ "alloy-consensus", "alloy-network", "alloy-primitives", "alloy-signer", "async-trait", - "coins-bip32", - "coins-bip39", - "elliptic-curve", - "eth-keystore", - "k256", - "rand", + "semver 1.0.23", "thiserror", + "tracing", + "trezor-client", ] [[package]] @@ -569,7 +582,7 @@ dependencies = [ [[package]] name = "alloy-transport" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" +source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" dependencies = [ "alloy-json-rpc", "base64 0.22.1", @@ -586,7 +599,7 @@ dependencies = [ [[package]] name = "alloy-transport-http" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" +source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -600,7 +613,7 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" +source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -620,7 +633,7 @@ dependencies = [ [[package]] name = "alloy-transport-ws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" +source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -753,8 +766,9 @@ dependencies = [ "alloy-rpc-client", "alloy-rpc-types", "alloy-rpc-types-trace", + "alloy-serde", "alloy-signer", - "alloy-signer-wallet", + "alloy-signer-local", "alloy-sol-types", "alloy-transport", "alloy-transport-ipc", @@ -1861,8 +1875,9 @@ dependencies = [ "alloy-provider", "alloy-rlp", "alloy-rpc-types", + "alloy-serde", "alloy-signer", - "alloy-signer-wallet", + "alloy-signer-local", "alloy-sol-types", "alloy-transport", "async-trait", @@ -3253,8 +3268,9 @@ dependencies = [ "alloy-primitives", "alloy-provider", "alloy-rpc-types", + "alloy-serde", "alloy-signer", - "alloy-signer-wallet", + "alloy-signer-local", "alloy-sol-macro-expander", "alloy-sol-macro-input", "alloy-transport", @@ -3380,6 +3396,7 @@ dependencies = [ "alloy-primitives", "alloy-provider", "alloy-rpc-types", + "alloy-serde", "alloy-signer", "alloy-transport", "async-recursion", @@ -3499,7 +3516,7 @@ dependencies = [ "alloy-provider", "alloy-rpc-types", "alloy-signer", - "alloy-signer-wallet", + "alloy-signer-local", "alloy-sol-types", "base64 0.22.1", "const-hex", @@ -3590,6 +3607,7 @@ dependencies = [ "alloy-rpc-client", "alloy-rpc-types", "alloy-rpc-types-engine", + "alloy-serde", "alloy-sol-types", "alloy-transport", "alloy-transport-http", @@ -3752,6 +3770,7 @@ dependencies = [ "alloy-primitives", "alloy-provider", "alloy-rpc-types", + "alloy-serde", "alloy-sol-types", "alloy-transport", "arrayvec", @@ -3902,8 +3921,8 @@ dependencies = [ "alloy-signer-aws", "alloy-signer-gcp", "alloy-signer-ledger", + "alloy-signer-local", "alloy-signer-trezor", - "alloy-signer-wallet", "alloy-sol-types", "async-trait", "aws-config", @@ -6885,7 +6904,7 @@ dependencies = [ [[package]] name = "revm" version = "9.0.0" -source = "git+https://github.com/bluealloy/revm.git?rev=a28a543#a28a5439b9cfb7494cbd670da10cbedcfe6c5854" +source = "git+https://github.com/bluealloy/revm.git?rev=dd98b3b#dd98b3bb977396d23966e0a2f40d97678d931573" dependencies = [ "auto_impl", "cfg-if", @@ -6899,7 +6918,7 @@ dependencies = [ [[package]] name = "revm-inspectors" version = "0.1.0" -source = "git+https://github.com/paradigmxyz/revm-inspectors?rev=5cf339c#5cf339cada76e3dc38c864aab870d3cdb7b6860d" +source = "git+https://github.com/paradigmxyz/revm-inspectors?rev=79774a6#79774a6e4add9da9247130bf73305531092d0895" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -6915,7 +6934,7 @@ dependencies = [ [[package]] name = "revm-interpreter" version = "5.0.0" -source = "git+https://github.com/bluealloy/revm.git?rev=a28a543#a28a5439b9cfb7494cbd670da10cbedcfe6c5854" +source = "git+https://github.com/bluealloy/revm.git?rev=dd98b3b#dd98b3bb977396d23966e0a2f40d97678d931573" dependencies = [ "revm-primitives", "serde", @@ -6924,7 +6943,7 @@ dependencies = [ [[package]] name = "revm-precompile" version = "7.0.0" -source = "git+https://github.com/bluealloy/revm.git?rev=a28a543#a28a5439b9cfb7494cbd670da10cbedcfe6c5854" +source = "git+https://github.com/bluealloy/revm.git?rev=dd98b3b#dd98b3bb977396d23966e0a2f40d97678d931573" dependencies = [ "aurora-engine-modexp", "c-kzg", @@ -6941,7 +6960,7 @@ dependencies = [ [[package]] name = "revm-primitives" version = "4.0.0" -source = "git+https://github.com/bluealloy/revm.git?rev=a28a543#a28a5439b9cfb7494cbd670da10cbedcfe6c5854" +source = "git+https://github.com/bluealloy/revm.git?rev=dd98b3b#dd98b3bb977396d23966e0a2f40d97678d931573" dependencies = [ "alloy-primitives", "auto_impl", diff --git a/Cargo.toml b/Cargo.toml index 663b727cc..368d94521 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -160,7 +160,7 @@ foundry-compilers = { version = "0.7.0", default-features = false } # no default features to avoid c-kzg revm = { version = "9.0.0", default-features = false } revm-primitives = { version = "4.0.0", default-features = false } -revm-inspectors = { git = "https://github.com/paradigmxyz/revm-inspectors", rev = "5cf339c", features = [ +revm-inspectors = { git = "https://github.com/paradigmxyz/revm-inspectors", rev = "79774a6", features = [ "serde", ] } @@ -168,30 +168,30 @@ revm-inspectors = { git = "https://github.com/paradigmxyz/revm-inspectors", rev ethers-contract-abigen = { version = "2.0.14", default-features = false } ## alloy -alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } -alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } -alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } -alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } -alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } -alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } -alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } -alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } -alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } -alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } -alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } -alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } -alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } -alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } -alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } -alloy-signer-wallet = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } -alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } -alloy-signer-gcp = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } -alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } -alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } -alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } -alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } -alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } -alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "00d81d7", default-features = false } +alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } +alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } +alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } +alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } +alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } +alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } +alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } +alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } +alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } +alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } +alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } +alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } +alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } +alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } +alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } +alloy-signer-local = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } +alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } +alloy-signer-gcp = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } +alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } +alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } +alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } +alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } +alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } +alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } alloy-primitives = { version = "0.7.1", features = ["getrandom", "rand"] } alloy-dyn-abi = "0.7.1" alloy-json-abi = "0.7.1" @@ -251,7 +251,7 @@ tower-http = "0.5" soldeer = "0.2.15" [patch.crates-io] -revm = { git = "https://github.com/bluealloy/revm.git", rev = "a28a543" } -revm-interpreter = { git = "https://github.com/bluealloy/revm.git", rev = "a28a543" } -revm-precompile = { git = "https://github.com/bluealloy/revm.git", rev = "a28a543" } -revm-primitives = { git = "https://github.com/bluealloy/revm.git", rev = "a28a543" } +revm = { git = "https://github.com/bluealloy/revm.git", rev = "dd98b3b" } +revm-interpreter = { git = "https://github.com/bluealloy/revm.git", rev = "dd98b3b" } +revm-precompile = { git = "https://github.com/bluealloy/revm.git", rev = "dd98b3b" } +revm-primitives = { git = "https://github.com/bluealloy/revm.git", rev = "dd98b3b" } diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index 749db58db..9d31ce6d6 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -51,12 +51,19 @@ alloy-network.workspace = true alloy-eips.workspace = true alloy-rlp.workspace = true alloy-signer = { workspace = true, features = ["eip712"] } -alloy-signer-wallet = { workspace = true, features = ["mnemonic"] } +alloy-signer-local = { workspace = true, features = ["mnemonic"] } alloy-sol-types = { workspace = true, features = ["std"] } alloy-dyn-abi = { workspace = true, features = ["std", "eip712"] } -alloy-rpc-types.workspace = true +alloy-rpc-types = { workspace = true, features = ["txpool"] } alloy-rpc-types-trace.workspace = true -alloy-provider = { workspace = true, features = ["reqwest", "ws", "ipc"] } +alloy-serde.workspace = true +alloy-provider = { workspace = true, features = [ + "reqwest", + "ws", + "ipc", + "debug-api", + "trace-api", +] } alloy-transport.workspace = true alloy-chains.workspace = true alloy-genesis.workspace = true @@ -109,6 +116,7 @@ tikv-jemallocator = { workspace = true, optional = true } alloy-json-abi.workspace = true 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-json-rpc.workspace = true alloy-pubsub.workspace = true diff --git a/crates/anvil/core/src/eth/mod.rs b/crates/anvil/core/src/eth/mod.rs index 1d39a488f..0f6205f14 100644 --- a/crates/anvil/core/src/eth/mod.rs +++ b/crates/anvil/core/src/eth/mod.rs @@ -7,9 +7,10 @@ use alloy_rpc_types::{ pubsub::{Params as SubscriptionParams, SubscriptionKind}, request::TransactionRequest, state::StateOverride, - BlockId, BlockNumberOrTag as BlockNumber, Filter, WithOtherFields, + BlockId, BlockNumberOrTag as BlockNumber, Filter, }; use alloy_rpc_types_trace::geth::{GethDebugTracingOptions, GethDefaultTracingOptions}; +use alloy_serde::WithOtherFields; pub mod block; pub mod proof; diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 013759d3b..81840bfa5 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -10,9 +10,10 @@ use alloy_eips::eip2718::{Decodable2718, Eip2718Error, Encodable2718}; use alloy_primitives::{Address, Bloom, Bytes, Log, Signature, TxHash, TxKind, B256, U256, U64}; use alloy_rlp::{length_of_length, Decodable, Encodable, Header}; use alloy_rpc_types::{ - other::OtherFields, request::TransactionRequest, AccessList, AnyTransactionReceipt, - Signature as RpcSignature, Transaction as RpcTransaction, TransactionReceipt, WithOtherFields, + request::TransactionRequest, AccessList, AnyTransactionReceipt, Signature as RpcSignature, + Transaction as RpcTransaction, TransactionReceipt, }; +use alloy_serde::{OtherFields, WithOtherFields}; use bytes::BufMut; use foundry_evm::traces::CallTraceNode; use revm::{ diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index 576c63a9f..ca86a06e1 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -5,7 +5,7 @@ use crate::{ }; use alloy_genesis::Genesis; use alloy_primitives::{utils::Unit, U256}; -use alloy_signer_wallet::coins_bip39::{English, Mnemonic}; +use alloy_signer_local::coins_bip39::{English, Mnemonic}; use anvil_server::ServerConfig; use clap::Parser; use core::fmt; diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index f9fe56c2c..097513847 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -20,9 +20,9 @@ use alloy_primitives::{hex, utils::Unit, U256}; use alloy_provider::Provider; use alloy_rpc_types::BlockNumberOrTag; use alloy_signer::Signer; -use alloy_signer_wallet::{ +use alloy_signer_local::{ coins_bip39::{English, Mnemonic}, - LocalWallet, MnemonicBuilder, + MnemonicBuilder, PrivateKeySigner, }; use alloy_transport::{Transport, TransportError}; use anvil_server::ServerConfig; @@ -99,13 +99,13 @@ pub struct NodeConfig { /// The hardfork to use pub hardfork: Option, /// Signer accounts that will be initialised with `genesis_balance` in the genesis block - pub genesis_accounts: Vec, + pub genesis_accounts: Vec, /// Native token balance of every genesis account in the genesis block pub genesis_balance: U256, /// Genesis block timestamp pub genesis_timestamp: Option, /// Signer accounts that can sign messages/transactions from the EVM node - pub signer_accounts: Vec, + pub signer_accounts: Vec, /// Configured block time for the EVM chain. Use `None` to mine a new block for every tx pub block_time: Option, /// Disable auto, interval mining mode uns use `MiningMode::None` instead @@ -206,7 +206,7 @@ Private Keys ); for (idx, wallet) in self.genesis_accounts.iter().enumerate() { - let hex = hex::encode(wallet.signer().to_bytes()); + let hex = hex::encode(wallet.credential().to_bytes()); let _ = write!(config_string, "\n({idx}) 0x{hex}"); } @@ -312,7 +312,7 @@ Genesis Timestamp for wallet in &self.genesis_accounts { available_accounts.push(format!("{:?}", wallet.address())); - private_keys.push(format!("0x{}", hex::encode(wallet.signer().to_bytes()))); + private_keys.push(format!("0x{}", hex::encode(wallet.credential().to_bytes()))); } if let Some(ref gen) = self.account_generator { @@ -588,14 +588,14 @@ impl NodeConfig { /// Sets the genesis accounts #[must_use] - pub fn with_genesis_accounts(mut self, accounts: Vec) -> Self { + pub fn with_genesis_accounts(mut self, accounts: Vec) -> Self { self.genesis_accounts = accounts; self } /// Sets the signer accounts #[must_use] - pub fn with_signer_accounts(mut self, accounts: Vec) -> Self { + pub fn with_signer_accounts(mut self, accounts: Vec) -> Self { self.signer_accounts = accounts; self } @@ -1243,7 +1243,7 @@ impl AccountGenerator { } impl AccountGenerator { - pub fn gen(&self) -> Vec { + pub fn gen(&self) -> Vec { let builder = MnemonicBuilder::::default().phrase(self.phrase.as_str()); // use the derivation path diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 49e0db680..7f76e6a70 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -42,12 +42,13 @@ use alloy_rpc_types::{ txpool::{TxpoolContent, TxpoolInspect, TxpoolInspectSummary, TxpoolStatus}, AccessList, AccessListWithGasUsed, Block, BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, EIP1186AccountProofResponse, FeeHistory, Filter, FilteredParams, Log, - Transaction, WithOtherFields, + Transaction, }; use alloy_rpc_types_trace::{ geth::{DefaultFrame, GethDebugTracingOptions, GethDefaultTracingOptions, GethTrace}, parity::LocalizedTransactionTrace, }; +use alloy_serde::WithOtherFields; use alloy_transport::TransportErrorKind; use anvil_core::{ eth::{ diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index feecc9487..25e001da0 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -9,12 +9,13 @@ use alloy_provider::{ use alloy_rpc_types::{ request::TransactionRequest, AccessListWithGasUsed, Block, BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, EIP1186AccountProofResponse, FeeHistory, - Filter, Log, Transaction, WithOtherFields, + Filter, Log, Transaction, }; use alloy_rpc_types_trace::{ geth::{GethDebugTracingOptions, GethTrace}, parity::LocalizedTransactionTrace as Trace, }; +use alloy_serde::WithOtherFields; use alloy_transport::TransportError; use anvil_core::eth::transaction::{convert_to_anvil_receipt, ReceiptResponse}; use foundry_common::provider::{ProviderBuilder, RetryProvider}; diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 238fe2ef9..0e130c8e5 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -39,12 +39,13 @@ use alloy_rpc_types::{ request::TransactionRequest, serde_helpers::JsonStorageKey, state::StateOverride, AccessList, Block as AlloyBlock, BlockId, BlockNumberOrTag as BlockNumber, EIP1186AccountProofResponse as AccountProof, EIP1186StorageProof as StorageProof, Filter, - FilteredParams, Header as AlloyHeader, Log, Transaction, TransactionReceipt, WithOtherFields, + FilteredParams, Header as AlloyHeader, Log, Transaction, TransactionReceipt, }; use alloy_rpc_types_trace::{ geth::{DefaultFrame, GethDebugTracingOptions, GethDefaultTracingOptions, GethTrace}, parity::LocalizedTransactionTrace, }; +use alloy_serde::WithOtherFields; use alloy_trie::{proof::ProofRetainer, HashBuilder, Nibbles}; use anvil_core::{ eth::{ diff --git a/crates/anvil/src/eth/error.rs b/crates/anvil/src/eth/error.rs index f7818585d..fe31fbc2a 100644 --- a/crates/anvil/src/eth/error.rs +++ b/crates/anvil/src/eth/error.rs @@ -111,6 +111,7 @@ where InvalidHeader::PrevrandaoNotSet => Self::PrevrandaoNotSet, }, EVMError::Database(err) => err.into(), + EVMError::Precompile(err) => Self::Message(err), EVMError::Custom(err) => Self::Message(err), } } diff --git a/crates/anvil/src/eth/otterscan/types.rs b/crates/anvil/src/eth/otterscan/types.rs index 7f8576257..57abc0c40 100644 --- a/crates/anvil/src/eth/otterscan/types.rs +++ b/crates/anvil/src/eth/otterscan/types.rs @@ -3,11 +3,12 @@ use crate::eth::{ error::{BlockchainError, Result}, }; use alloy_primitives::{Address, Bytes, FixedBytes, B256, U256 as rU256, U256}; -use alloy_rpc_types::{Block, BlockTransactions, Transaction, WithOtherFields}; +use alloy_rpc_types::{Block, BlockTransactions, Transaction}; use alloy_rpc_types_trace::parity::{ Action, CallAction, CallType, CreateAction, CreateOutput, LocalizedTransactionTrace, RewardAction, TraceOutput, }; +use alloy_serde::WithOtherFields; use anvil_core::eth::transaction::ReceiptResponse; use foundry_evm::traces::CallKind; use futures::future::join_all; diff --git a/crates/anvil/src/eth/sign.rs b/crates/anvil/src/eth/sign.rs index 949a517fa..c122d54dd 100644 --- a/crates/anvil/src/eth/sign.rs +++ b/crates/anvil/src/eth/sign.rs @@ -4,7 +4,7 @@ use alloy_dyn_abi::TypedData; use alloy_network::TxSignerSync; use alloy_primitives::{Address, Signature, B256}; use alloy_signer::Signer as AlloySigner; -use alloy_signer_wallet::LocalWallet; +use alloy_signer_local::PrivateKeySigner; use anvil_core::eth::transaction::{ optimism::{DepositTransaction, DepositTransactionRequest}, TypedTransaction, TypedTransactionRequest, @@ -47,11 +47,11 @@ pub trait Signer: Send + Sync { /// Maintains developer keys pub struct DevSigner { addresses: Vec
, - accounts: HashMap, + accounts: HashMap, } impl DevSigner { - pub fn new(accounts: Vec) -> Self { + pub fn new(accounts: Vec) -> Self { let addresses = accounts.iter().map(|wallet| wallet.address()).collect::>(); let accounts = addresses.iter().cloned().zip(accounts).collect(); Self { addresses, accounts } diff --git a/crates/anvil/src/evm.rs b/crates/anvil/src/evm.rs index 0e0d30f77..794d2ce85 100644 --- a/crates/anvil/src/evm.rs +++ b/crates/anvil/src/evm.rs @@ -33,12 +33,14 @@ mod tests { use crate::{evm::inject_precompiles, PrecompileFactory}; use alloy_primitives::Address; use foundry_evm::revm::primitives::{address, Bytes, Precompile, PrecompileResult, SpecId}; + use revm::primitives::PrecompileOutput; #[test] fn build_evm_with_extra_precompiles() { const PRECOMPILE_ADDR: Address = address!("0000000000000000000000000000000000000071"); + fn my_precompile(_bytes: &Bytes, _gas_limit: u64) -> PrecompileResult { - Ok((0, Bytes::new())) + Ok(PrecompileOutput { bytes: Bytes::new(), gas_used: 0 }) } #[derive(Debug)] diff --git a/crates/anvil/src/lib.rs b/crates/anvil/src/lib.rs index da08b0a4a..471f7b05c 100644 --- a/crates/anvil/src/lib.rs +++ b/crates/anvil/src/lib.rs @@ -21,7 +21,7 @@ use crate::{ tasks::TaskManager, }; use alloy_primitives::{Address, U256}; -use alloy_signer_wallet::LocalWallet; +use alloy_signer_local::PrivateKeySigner; use eth::backend::fork::ClientFork; use foundry_common::provider::{ProviderBuilder, RetryProvider}; use foundry_evm::revm; @@ -165,7 +165,7 @@ pub async fn try_spawn(mut config: NodeConfig) -> io::Result<(EthApi, NodeHandle .alloc .values() .filter_map(|acc| acc.private_key) - .flat_map(|k| LocalWallet::from_bytes(&k)) + .flat_map(|k| PrivateKeySigner::from_bytes(&k)) .collect::>(); if !genesis_signers.is_empty() { signers.push(Box::new(DevSigner::new(genesis_signers))); @@ -332,7 +332,7 @@ impl NodeHandle { } /// Signer accounts that can sign messages/transactions from the EVM node - pub fn dev_wallets(&self) -> impl Iterator + '_ { + pub fn dev_wallets(&self) -> impl Iterator + '_ { self.config.signer_accounts.iter().cloned() } diff --git a/crates/anvil/tests/it/anvil_api.rs b/crates/anvil/tests/it/anvil_api.rs index 11aa40bdc..d83365eac 100644 --- a/crates/anvil/tests/it/anvil_api.rs +++ b/crates/anvil/tests/it/anvil_api.rs @@ -5,10 +5,11 @@ use crate::{ fork::fork_config, utils::http_provider_with_signer, }; -use alloy_network::{EthereumSigner, TransactionBuilder}; +use alloy_network::{EthereumWallet, TransactionBuilder}; use alloy_primitives::{address, fixed_bytes, Address, U256, U64}; use alloy_provider::{ext::TxPoolApi, Provider}; -use alloy_rpc_types::{BlockId, BlockNumberOrTag, TransactionRequest, WithOtherFields}; +use alloy_rpc_types::{BlockId, BlockNumberOrTag, TransactionRequest}; +use alloy_serde::WithOtherFields; use anvil::{eth::api::CLIENT_VERSION, spawn, Hardfork, NodeConfig}; use anvil_core::{ eth::EthRequest, @@ -632,7 +633,7 @@ async fn can_remove_pool_transactions() { let (api, handle) = spawn(NodeConfig::test()).await; let wallet = handle.dev_wallets().next().unwrap(); - let signer: EthereumSigner = wallet.clone().into(); + let signer: EthereumWallet = wallet.clone().into(); let from = wallet.address(); let provider = http_provider_with_signer(&handle.http_endpoint(), signer); diff --git a/crates/anvil/tests/it/api.rs b/crates/anvil/tests/it/api.rs index b7d6f51c5..13f8200ef 100644 --- a/crates/anvil/tests/it/api.rs +++ b/crates/anvil/tests/it/api.rs @@ -2,15 +2,16 @@ use crate::{ abi::{MulticallContract, SimpleStorage}, - utils::{connect_pubsub_with_signer, http_provider_with_signer}, + utils::{connect_pubsub_with_wallet, http_provider_with_signer}, }; -use alloy_network::{EthereumSigner, TransactionBuilder}; +use alloy_network::{EthereumWallet, TransactionBuilder}; use alloy_primitives::{Address, ChainId, B256, U256}; use alloy_provider::Provider; use alloy_rpc_types::{ request::TransactionRequest, state::AccountOverride, BlockId, BlockNumberOrTag, - BlockTransactions, WithOtherFields, + BlockTransactions, }; +use alloy_serde::WithOtherFields; use anvil::{eth::api::CLIENT_VERSION, spawn, NodeConfig, CHAIN_ID}; use std::{collections::HashMap, time::Duration}; @@ -99,7 +100,7 @@ async fn can_get_block_by_number() { let (_api, handle) = spawn(NodeConfig::test()).await; let accounts: Vec<_> = handle.dev_wallets().collect(); - let signer: EthereumSigner = accounts[0].clone().into(); + let signer: EthereumWallet = accounts[0].clone().into(); let from = accounts[0].address(); let to = accounts[1].address(); @@ -129,11 +130,11 @@ async fn can_get_pending_block() { let (api, handle) = spawn(NodeConfig::test()).await; let accounts: Vec<_> = handle.dev_wallets().collect(); - let signer: EthereumSigner = accounts[0].clone().into(); + let signer: EthereumWallet = accounts[0].clone().into(); let from = accounts[0].address(); let to = accounts[1].address(); - let provider = connect_pubsub_with_signer(&handle.http_endpoint(), signer).await; + let provider = connect_pubsub_with_wallet(&handle.http_endpoint(), signer).await; let block = provider.get_block(BlockId::pending(), false.into()).await.unwrap().unwrap(); assert_eq!(block.header.number.unwrap(), 1); @@ -165,7 +166,7 @@ async fn can_call_on_pending_block() { let (api, handle) = spawn(NodeConfig::test()).await; let wallet = handle.dev_wallets().next().unwrap(); - let signer: EthereumSigner = wallet.clone().into(); + let signer: EthereumWallet = wallet.clone().into(); let sender = wallet.address(); let provider = http_provider_with_signer(&handle.http_endpoint(), signer); @@ -242,7 +243,7 @@ async fn can_call_on_pending_block() { async fn can_call_with_state_override() { let (api, handle) = spawn(NodeConfig::test()).await; let wallet = handle.dev_wallets().next().unwrap(); - let signer: EthereumSigner = wallet.clone().into(); + let signer: EthereumWallet = wallet.clone().into(); let account = wallet.address(); let provider = http_provider_with_signer(&handle.http_endpoint(), signer); diff --git a/crates/anvil/tests/it/eip4844.rs b/crates/anvil/tests/it/eip4844.rs index 1d0df312b..19842aa75 100644 --- a/crates/anvil/tests/it/eip4844.rs +++ b/crates/anvil/tests/it/eip4844.rs @@ -4,7 +4,8 @@ use alloy_eips::eip4844::{DATA_GAS_PER_BLOB, MAX_DATA_GAS_PER_BLOCK}; use alloy_network::TransactionBuilder; use alloy_primitives::U256; use alloy_provider::Provider; -use alloy_rpc_types::{BlockId, TransactionRequest, WithOtherFields}; +use alloy_rpc_types::{BlockId, TransactionRequest}; +use alloy_serde::WithOtherFields; use anvil::{spawn, Hardfork, NodeConfig}; #[tokio::test(flavor = "multi_thread")] diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index 3ef8665ea..83c235f5c 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -4,14 +4,15 @@ use crate::{ abi::{Greeter, ERC721}, utils::{http_provider, http_provider_with_signer}, }; -use alloy_network::{EthereumSigner, TransactionBuilder}; +use alloy_network::{EthereumWallet, TransactionBuilder}; use alloy_primitives::{address, Address, Bytes, TxKind, U256}; use alloy_provider::Provider; use alloy_rpc_types::{ request::{TransactionInput, TransactionRequest}, - BlockId, BlockNumberOrTag, WithOtherFields, + BlockId, BlockNumberOrTag, }; -use alloy_signer_wallet::LocalWallet; +use alloy_serde::WithOtherFields; +use alloy_signer_local::PrivateKeySigner; use anvil::{eth::EthApi, spawn, NodeConfig, NodeHandle}; use anvil_core::types::Forking; use foundry_common::provider::get_http_provider; @@ -421,7 +422,7 @@ async fn can_deploy_greeter_on_fork() { let (_api, handle) = spawn(fork_config().with_fork_block_number(Some(14723772u64))).await; let wallet = handle.dev_wallets().next().unwrap(); - let signer: EthereumSigner = wallet.into(); + let signer: EthereumWallet = wallet.into(); let provider = http_provider_with_signer(&handle.http_endpoint(), signer); @@ -570,13 +571,13 @@ async fn test_fork_can_send_tx() { let (api, handle) = spawn(fork_config().with_blocktime(Some(std::time::Duration::from_millis(800)))).await; - let wallet = LocalWallet::random(); + let wallet = PrivateKeySigner::random(); let signer = wallet.address(); let provider = handle.http_provider(); // let provider = SignerMiddleware::new(provider, wallet); api.anvil_set_balance(signer, U256::MAX).await.unwrap(); - api.anvil_impersonate_account(signer).await.unwrap(); // Added until SignerFiller for alloy-provider is fixed. + api.anvil_impersonate_account(signer).await.unwrap(); // Added until WalletFiller for alloy-provider is fixed. let balance = provider.get_balance(signer).await.unwrap(); assert_eq!(balance, U256::MAX); @@ -603,7 +604,7 @@ async fn test_fork_nft_set_approve_all() { .await; // create and fund a random wallet - let wallet = LocalWallet::random(); + let wallet = PrivateKeySigner::random(); let signer = wallet.address(); api.anvil_set_balance(signer, U256::from(1000e18)).await.unwrap(); @@ -1033,7 +1034,7 @@ async fn can_override_fork_chain_id() { .await; let wallet = handle.dev_wallets().next().unwrap(); - let signer: EthereumSigner = wallet.into(); + let signer: EthereumWallet = wallet.into(); let provider = http_provider_with_signer(&handle.http_endpoint(), signer); let greeter_contract = diff --git a/crates/anvil/tests/it/gas.rs b/crates/anvil/tests/it/gas.rs index f7ab59004..af28983f1 100644 --- a/crates/anvil/tests/it/gas.rs +++ b/crates/anvil/tests/it/gas.rs @@ -1,10 +1,11 @@ //! Gas related tests use crate::utils::http_provider_with_signer; -use alloy_network::{EthereumSigner, TransactionBuilder}; +use alloy_network::{EthereumWallet, TransactionBuilder}; use alloy_primitives::{Address, U256}; use alloy_provider::Provider; -use alloy_rpc_types::{BlockId, TransactionRequest, WithOtherFields}; +use alloy_rpc_types::{BlockId, TransactionRequest}; +use alloy_serde::WithOtherFields; use anvil::{eth::fees::INITIAL_BASE_FEE, spawn, NodeConfig}; const GAS_TRANSFER: u128 = 21_000; @@ -17,7 +18,7 @@ async fn test_basefee_full_block() { .await; let wallet = handle.dev_wallets().next().unwrap(); - let signer: EthereumSigner = wallet.clone().into(); + let signer: EthereumWallet = wallet.clone().into(); let provider = http_provider_with_signer(&handle.http_endpoint(), signer); @@ -62,7 +63,7 @@ async fn test_basefee_half_block() { .await; let wallet = handle.dev_wallets().next().unwrap(); - let signer: EthereumSigner = wallet.clone().into(); + let signer: EthereumWallet = wallet.clone().into(); let provider = http_provider_with_signer(&handle.http_endpoint(), signer); @@ -94,7 +95,7 @@ async fn test_basefee_empty_block() { let (api, handle) = spawn(NodeConfig::test().with_base_fee(Some(INITIAL_BASE_FEE))).await; let wallet = handle.dev_wallets().next().unwrap(); - let signer: EthereumSigner = wallet.clone().into(); + let signer: EthereumWallet = wallet.clone().into(); let provider = http_provider_with_signer(&handle.http_endpoint(), signer); diff --git a/crates/anvil/tests/it/logs.rs b/crates/anvil/tests/it/logs.rs index afcb34019..40d032d7c 100644 --- a/crates/anvil/tests/it/logs.rs +++ b/crates/anvil/tests/it/logs.rs @@ -4,7 +4,7 @@ use crate::{ abi::SimpleStorage::{self}, utils::{http_provider_with_signer, ws_provider_with_signer}, }; -use alloy_network::EthereumSigner; +use alloy_network::EthereumWallet; use alloy_primitives::B256; use alloy_provider::Provider; use alloy_rpc_types::{BlockNumberOrTag, Filter}; @@ -17,7 +17,7 @@ async fn get_past_events() { let wallet = handle.dev_wallets().next().unwrap(); let account = wallet.address(); - let signer: EthereumSigner = wallet.into(); + let signer: EthereumWallet = wallet.into(); let provider = http_provider_with_signer(&handle.http_endpoint(), signer); @@ -86,7 +86,7 @@ async fn get_all_events() { let wallet = handle.dev_wallets().next().unwrap(); let account = wallet.address(); - let signer: EthereumSigner = wallet.into(); + let signer: EthereumWallet = wallet.into(); let provider = http_provider_with_signer(&handle.http_endpoint(), signer); @@ -153,7 +153,7 @@ async fn watch_events() { let wallet = handle.dev_wallets().next().unwrap(); let account = wallet.address(); - let signer: EthereumSigner = wallet.into(); + let signer: EthereumWallet = wallet.into(); let provider = http_provider_with_signer(&handle.http_endpoint(), signer.clone()); diff --git a/crates/anvil/tests/it/optimism.rs b/crates/anvil/tests/it/optimism.rs index 60d506aee..3406e6865 100644 --- a/crates/anvil/tests/it/optimism.rs +++ b/crates/anvil/tests/it/optimism.rs @@ -2,10 +2,11 @@ use crate::utils::http_provider_with_signer; use alloy_eips::eip2718::Encodable2718; -use alloy_network::{EthereumSigner, TransactionBuilder}; +use alloy_network::{EthereumWallet, TransactionBuilder}; use alloy_primitives::{b256, U128, U256}; use alloy_provider::Provider; -use alloy_rpc_types::{optimism::OptimismTransactionFields, TransactionRequest, WithOtherFields}; +use alloy_rpc_types::{optimism::OptimismTransactionFields, TransactionRequest}; +use alloy_serde::WithOtherFields; use anvil::{spawn, Hardfork, NodeConfig}; #[tokio::test(flavor = "multi_thread")] @@ -46,7 +47,7 @@ async fn test_send_value_deposit_transaction() { spawn(NodeConfig::test().with_optimism(true).with_hardfork(Some(Hardfork::Paris))).await; let accounts: Vec<_> = handle.dev_wallets().collect(); - let signer: EthereumSigner = accounts[0].clone().into(); + let signer: EthereumWallet = accounts[0].clone().into(); let from = accounts[0].address(); let to = accounts[1].address(); @@ -94,7 +95,7 @@ async fn test_send_value_raw_deposit_transaction() { spawn(NodeConfig::test().with_optimism(true).with_hardfork(Some(Hardfork::Paris))).await; let accounts: Vec<_> = handle.dev_wallets().collect(); - let signer: EthereumSigner = accounts[0].clone().into(); + let signer: EthereumWallet = accounts[0].clone().into(); let from = accounts[0].address(); let to = accounts[1].address(); diff --git a/crates/anvil/tests/it/otterscan.rs b/crates/anvil/tests/it/otterscan.rs index 573fc1c7e..dc0f297fd 100644 --- a/crates/anvil/tests/it/otterscan.rs +++ b/crates/anvil/tests/it/otterscan.rs @@ -3,7 +3,8 @@ use crate::abi::MulticallContract; use alloy_primitives::{address, Address, Bytes, U256}; use alloy_provider::Provider; -use alloy_rpc_types::{BlockNumberOrTag, BlockTransactions, TransactionRequest, WithOtherFields}; +use alloy_rpc_types::{BlockNumberOrTag, BlockTransactions, TransactionRequest}; +use alloy_serde::WithOtherFields; use alloy_sol_types::{sol, SolCall, SolError}; use anvil::{ eth::otterscan::types::{ diff --git a/crates/anvil/tests/it/pubsub.rs b/crates/anvil/tests/it/pubsub.rs index d11208feb..fec22ca41 100644 --- a/crates/anvil/tests/it/pubsub.rs +++ b/crates/anvil/tests/it/pubsub.rs @@ -1,11 +1,12 @@ //! tests for subscriptions -use crate::utils::{connect_pubsub, connect_pubsub_with_signer}; -use alloy_network::{EthereumSigner, TransactionBuilder}; +use crate::utils::{connect_pubsub, connect_pubsub_with_wallet}; +use alloy_network::{EthereumWallet, TransactionBuilder}; use alloy_primitives::{Address, U256}; use alloy_provider::Provider; use alloy_pubsub::Subscription; -use alloy_rpc_types::{Block as AlloyBlock, Filter, TransactionRequest, WithOtherFields}; +use alloy_rpc_types::{Block as AlloyBlock, Filter, TransactionRequest}; +use alloy_serde::WithOtherFields; use alloy_sol_types::sol; use anvil::{spawn, NodeConfig}; use futures::StreamExt; @@ -117,7 +118,7 @@ async fn test_sub_logs_impersonated() { let (api, handle) = spawn(NodeConfig::test()).await; let wallet = handle.dev_wallets().next().unwrap(); let provider = - connect_pubsub_with_signer(&handle.ws_endpoint(), EthereumSigner::from(wallet.clone())) + connect_pubsub_with_wallet(&handle.ws_endpoint(), EthereumWallet::from(wallet.clone())) .await; // impersonate account @@ -160,7 +161,7 @@ async fn test_filters_legacy() { let (_api, handle) = spawn(NodeConfig::test()).await; let wallet = handle.dev_wallets().next().unwrap(); let provider = - connect_pubsub_with_signer(&handle.ws_endpoint(), EthereumSigner::from(wallet.clone())) + connect_pubsub_with_wallet(&handle.ws_endpoint(), EthereumWallet::from(wallet.clone())) .await; let from = wallet.address(); @@ -197,7 +198,7 @@ async fn test_filters() { let (_api, handle) = spawn(NodeConfig::test()).await; let wallet = handle.dev_wallets().next().unwrap(); let provider = - connect_pubsub_with_signer(&handle.ws_endpoint(), EthereumSigner::from(wallet.clone())) + connect_pubsub_with_wallet(&handle.ws_endpoint(), EthereumWallet::from(wallet.clone())) .await; let from = wallet.address(); diff --git a/crates/anvil/tests/it/revert.rs b/crates/anvil/tests/it/revert.rs index 051f6be66..55762fd0f 100644 --- a/crates/anvil/tests/it/revert.rs +++ b/crates/anvil/tests/it/revert.rs @@ -2,7 +2,8 @@ use crate::abi::VendingMachine; use alloy_network::TransactionBuilder; use alloy_primitives::{bytes, U256}; use alloy_provider::Provider; -use alloy_rpc_types::{TransactionRequest, WithOtherFields}; +use alloy_rpc_types::TransactionRequest; +use alloy_serde::WithOtherFields; use alloy_sol_types::sol; use anvil::{spawn, NodeConfig}; diff --git a/crates/anvil/tests/it/sign.rs b/crates/anvil/tests/it/sign.rs index c279619f2..0ff56f364 100644 --- a/crates/anvil/tests/it/sign.rs +++ b/crates/anvil/tests/it/sign.rs @@ -1,9 +1,10 @@ use crate::utils::http_provider_with_signer; use alloy_dyn_abi::TypedData; -use alloy_network::EthereumSigner; +use alloy_network::EthereumWallet; use alloy_primitives::{Address, U256}; use alloy_provider::Provider; -use alloy_rpc_types::{TransactionRequest, WithOtherFields}; +use alloy_rpc_types::TransactionRequest; +use alloy_serde::WithOtherFields; use alloy_signer::Signer; use anvil::{spawn, NodeConfig}; @@ -310,7 +311,7 @@ async fn can_sign_transaction() { async fn rejects_different_chain_id() { let (_api, handle) = spawn(NodeConfig::test()).await; let wallet = handle.dev_wallets().next().unwrap().with_chain_id(Some(1)); - let provider = http_provider_with_signer(&handle.http_endpoint(), EthereumSigner::from(wallet)); + let provider = http_provider_with_signer(&handle.http_endpoint(), EthereumWallet::from(wallet)); let tx = TransactionRequest::default().to(Address::random()).value(U256::from(100)); let tx = WithOtherFields::new(tx); @@ -324,7 +325,7 @@ async fn rejects_invalid_chain_id() { let (_api, handle) = spawn(NodeConfig::test()).await; let wallet = handle.dev_wallets().next().unwrap(); let wallet = wallet.with_chain_id(Some(99u64)); - let provider = http_provider_with_signer(&handle.http_endpoint(), EthereumSigner::from(wallet)); + let provider = http_provider_with_signer(&handle.http_endpoint(), EthereumWallet::from(wallet)); let tx = TransactionRequest::default().to(Address::random()).value(U256::from(100u64)); let tx = WithOtherFields::new(tx); let res = provider.send_transaction(tx).await; diff --git a/crates/anvil/tests/it/traces.rs b/crates/anvil/tests/it/traces.rs index 38f7a61a1..1c4927300 100644 --- a/crates/anvil/tests/it/traces.rs +++ b/crates/anvil/tests/it/traces.rs @@ -1,15 +1,16 @@ use crate::{fork::fork_config, utils::http_provider_with_signer}; -use alloy_network::{EthereumSigner, TransactionBuilder}; +use alloy_network::{EthereumWallet, TransactionBuilder}; use alloy_primitives::{hex, Address, Bytes, U256}; use alloy_provider::{ ext::{DebugApi, TraceApi}, Provider, }; -use alloy_rpc_types::{BlockNumberOrTag, TransactionRequest, WithOtherFields}; +use alloy_rpc_types::{BlockNumberOrTag, TransactionRequest}; use alloy_rpc_types_trace::{ geth::{GethDebugTracingCallOptions, GethTrace}, parity::{Action, LocalizedTransactionTrace}, }; +use alloy_serde::WithOtherFields; use alloy_sol_types::sol; use anvil::{spawn, Hardfork, NodeConfig}; @@ -98,7 +99,7 @@ sol!( async fn test_transfer_debug_trace_call() { let (_api, handle) = spawn(NodeConfig::test()).await; let wallets = handle.dev_wallets().collect::>(); - let deployer: EthereumSigner = wallets[0].clone().into(); + let deployer: EthereumWallet = wallets[0].clone().into(); let provider = http_provider_with_signer(&handle.http_endpoint(), deployer); let contract_addr = DebugTraceContract::deploy_builder(provider.clone()) @@ -107,7 +108,7 @@ async fn test_transfer_debug_trace_call() { .await .unwrap(); - let caller: EthereumSigner = wallets[1].clone().into(); + let caller: EthereumWallet = wallets[1].clone().into(); let caller_provider = http_provider_with_signer(&handle.http_endpoint(), caller); let contract = DebugTraceContract::new(contract_addr, caller_provider); diff --git a/crates/anvil/tests/it/transaction.rs b/crates/anvil/tests/it/transaction.rs index 74c82133a..1565e587b 100644 --- a/crates/anvil/tests/it/transaction.rs +++ b/crates/anvil/tests/it/transaction.rs @@ -2,14 +2,14 @@ use crate::{ abi::{Greeter, MulticallContract, SimpleStorage}, utils::{connect_pubsub, http_provider_with_signer}, }; -use alloy_network::{EthereumSigner, TransactionBuilder}; +use alloy_network::{EthereumWallet, TransactionBuilder}; use alloy_primitives::{Address, Bytes, FixedBytes, U256}; use alloy_provider::Provider; use alloy_rpc_types::{ state::{AccountOverride, StateOverride}, AccessList, AccessListItem, BlockId, BlockNumberOrTag, BlockTransactions, TransactionRequest, - WithOtherFields, }; +use alloy_serde::WithOtherFields; use anvil::{spawn, Hardfork, NodeConfig}; use eyre::Ok; use futures::{future::join_all, FutureExt, StreamExt}; @@ -299,7 +299,7 @@ async fn can_deploy_greeter_http() { let (_api, handle) = spawn(NodeConfig::test()).await; let wallet = handle.dev_wallets().next().unwrap(); - let signer: EthereumSigner = wallet.clone().into(); + let signer: EthereumWallet = wallet.clone().into(); let alloy_provider = http_provider_with_signer(&handle.http_endpoint(), signer); diff --git a/crates/anvil/tests/it/txpool.rs b/crates/anvil/tests/it/txpool.rs index 0882d1977..c329b27fa 100644 --- a/crates/anvil/tests/it/txpool.rs +++ b/crates/anvil/tests/it/txpool.rs @@ -3,7 +3,8 @@ use alloy_network::TransactionBuilder; use alloy_primitives::U256; use alloy_provider::{ext::TxPoolApi, Provider}; -use alloy_rpc_types::{TransactionRequest, WithOtherFields}; +use alloy_rpc_types::TransactionRequest; +use alloy_serde::WithOtherFields; use anvil::{spawn, NodeConfig}; #[tokio::test(flavor = "multi_thread")] diff --git a/crates/anvil/tests/it/utils.rs b/crates/anvil/tests/it/utils.rs index 56c803702..368377513 100644 --- a/crates/anvil/tests/it/utils.rs +++ b/crates/anvil/tests/it/utils.rs @@ -1,4 +1,4 @@ -use alloy_network::{Ethereum, EthereumSigner}; +use alloy_network::{Ethereum, EthereumWallet}; use foundry_common::provider::{ get_http_provider, ProviderBuilder, RetryProvider, RetryProviderWithSigner, }; @@ -9,19 +9,19 @@ pub fn http_provider(http_endpoint: &str) -> RetryProvider { pub fn http_provider_with_signer( http_endpoint: &str, - signer: EthereumSigner, + signer: EthereumWallet, ) -> RetryProviderWithSigner { ProviderBuilder::new(http_endpoint) - .build_with_signer(signer) + .build_with_wallet(signer) .expect("failed to build Alloy HTTP provider with signer") } pub fn ws_provider_with_signer( ws_endpoint: &str, - signer: EthereumSigner, + signer: EthereumWallet, ) -> RetryProviderWithSigner { ProviderBuilder::new(ws_endpoint) - .build_with_signer(signer) + .build_with_wallet(signer) .expect("failed to build Alloy WS provider with signer") } @@ -31,33 +31,35 @@ pub async fn connect_pubsub(conn_str: &str) -> RootProvider { } use alloy_provider::{ - fillers::{ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller, SignerFiller}, + fillers::{ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller, WalletFiller}, Identity, RootProvider, }; use alloy_transport::BoxTransport; + type PubsubSigner = FillProvider< JoinFill< JoinFill, NonceFiller>, ChainIdFiller>, - SignerFiller, + WalletFiller, >, RootProvider, BoxTransport, Ethereum, >; -pub async fn connect_pubsub_with_signer(conn_str: &str, signer: EthereumSigner) -> PubsubSigner { + +pub async fn connect_pubsub_with_wallet(conn_str: &str, wallet: EthereumWallet) -> PubsubSigner { alloy_provider::ProviderBuilder::new() .with_recommended_fillers() - .signer(signer) + .wallet(wallet) .on_builtin(conn_str) .await .unwrap() } -pub async fn ipc_provider_with_signer( +pub async fn ipc_provider_with_wallet( ipc_endpoint: &str, - signer: EthereumSigner, + wallet: EthereumWallet, ) -> RetryProviderWithSigner { ProviderBuilder::new(ipc_endpoint) - .build_with_signer(signer) + .build_with_wallet(wallet) .expect("failed to build Alloy IPC provider with signer") } diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index ddcee1ba2..02312eb78 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -33,21 +33,22 @@ foundry-config.workspace = true foundry-evm.workspace = true foundry-wallets.workspace = true +alloy-chains.workspace = true +alloy-consensus = { workspace = true, features = ["serde", "kzg"] } +alloy-contract.workspace = true alloy-dyn-abi.workspace = true alloy-json-abi.workspace = true +alloy-json-rpc.workspace = true +alloy-network.workspace = true alloy-primitives.workspace = true -alloy-rlp.workspace = true alloy-provider = { workspace = true, features = ["reqwest", "ws", "ipc"] } -alloy-transport.workspace = true +alloy-rlp.workspace = true alloy-rpc-types = { workspace = true, features = ["eth"] } -alloy-json-rpc.workspace = true +alloy-serde.workspace = true +alloy-signer-local = { workspace = true, features = ["mnemonic", "keystore"] } alloy-signer.workspace = true -alloy-signer-wallet = { workspace = true, features = ["mnemonic", "keystore"] } -alloy-contract.workspace = true -alloy-consensus = { workspace = true, features = ["serde", "kzg"] } -alloy-network.workspace = true alloy-sol-types.workspace = true -alloy-chains.workspace = true +alloy-transport.workspace = true chrono.workspace = true evm-disassembler.workspace = true diff --git a/crates/cast/bin/cmd/mktx.rs b/crates/cast/bin/cmd/mktx.rs index cd0fdfecd..db8dbf0fe 100644 --- a/crates/cast/bin/cmd/mktx.rs +++ b/crates/cast/bin/cmd/mktx.rs @@ -1,5 +1,5 @@ use crate::tx::{self, CastTxBuilder}; -use alloy_network::{eip2718::Encodable2718, EthereumSigner, TransactionBuilder}; +use alloy_network::{eip2718::Encodable2718, EthereumWallet, TransactionBuilder}; use alloy_signer::Signer; use clap::Parser; use eyre::Result; @@ -90,7 +90,7 @@ impl MakeTxArgs { .build(from) .await?; - let tx = tx.build(&EthereumSigner::new(signer)).await?; + let tx = tx.build(&EthereumWallet::new(signer)).await?; let signed_tx = hex::encode(tx.encoded_2718()); println!("0x{signed_tx}"); diff --git a/crates/cast/bin/cmd/send.rs b/crates/cast/bin/cmd/send.rs index d684edcd4..55094346f 100644 --- a/crates/cast/bin/cmd/send.rs +++ b/crates/cast/bin/cmd/send.rs @@ -1,7 +1,8 @@ use crate::tx::{self, CastTxBuilder}; -use alloy_network::{AnyNetwork, EthereumSigner}; +use alloy_network::{AnyNetwork, EthereumWallet}; use alloy_provider::{Provider, ProviderBuilder}; -use alloy_rpc_types::{TransactionRequest, WithOtherFields}; +use alloy_rpc_types::TransactionRequest; +use alloy_serde::WithOtherFields; use alloy_signer::Signer; use alloy_transport::Transport; use cast::Cast; @@ -157,9 +158,9 @@ impl SendTxArgs { tx::validate_from_address(eth.wallet.from, from)?; - let signer = EthereumSigner::from(signer); + let wallet = EthereumWallet::from(signer); let provider = ProviderBuilder::<_, _, AnyNetwork>::default() - .signer(signer) + .wallet(wallet) .on_provider(&provider); let (tx, _) = builder.build(from).await?; diff --git a/crates/cast/bin/cmd/wallet/mod.rs b/crates/cast/bin/cmd/wallet/mod.rs index c468803ce..2b50630c3 100644 --- a/crates/cast/bin/cmd/wallet/mod.rs +++ b/crates/cast/bin/cmd/wallet/mod.rs @@ -1,9 +1,9 @@ use alloy_dyn_abi::TypedData; use alloy_primitives::{Address, Signature, B256}; use alloy_signer::Signer; -use alloy_signer_wallet::{ +use alloy_signer_local::{ coins_bip39::{English, Entropy, Mnemonic}, - LocalWallet, MnemonicBuilder, + MnemonicBuilder, PrivateKeySigner, }; use clap::Parser; use eyre::{Context, Result}; @@ -218,8 +218,12 @@ impl WalletSubcommands { }; for _ in 0..number { - let (wallet, uuid) = - LocalWallet::new_keystore(&path, &mut rng, password.clone(), None)?; + let (wallet, uuid) = PrivateKeySigner::new_keystore( + &path, + &mut rng, + password.clone(), + None, + )?; if let Some(json) = json_values.as_mut() { json.push(json!({ @@ -241,17 +245,20 @@ impl WalletSubcommands { } } else { for _ in 0..number { - let wallet = LocalWallet::random_with(&mut rng); + let wallet = PrivateKeySigner::random_with(&mut rng); if let Some(json) = json_values.as_mut() { json.push(json!({ "address": wallet.address().to_checksum(None), - "private_key": format!("0x{}", hex::encode(wallet.signer().to_bytes())), + "private_key": format!("0x{}", hex::encode(wallet.credential().to_bytes())), })) } else { println!("Successfully created new keypair."); println!("Address: {}", wallet.address().to_checksum(None)); - println!("Private key: 0x{}", hex::encode(wallet.signer().to_bytes())); + println!( + "Private key: 0x{}", + hex::encode(wallet.credential().to_bytes()) + ); } } @@ -284,7 +291,7 @@ impl WalletSubcommands { for (i, wallet) in wallets.iter().enumerate() { println!("- Account {i}:"); println!("Address: {}", wallet.address()); - println!("Private key: 0x{}\n", hex::encode(wallet.signer().to_bytes())); + println!("Private key: 0x{}\n", hex::encode(wallet.credential().to_bytes())); } } Self::Vanity(cmd) => { @@ -363,7 +370,7 @@ flag to set your key via: ) })?; - let private_key = wallet.signer().to_bytes(); + let private_key = wallet.credential().to_bytes(); let password = if let Some(password) = unsafe_password { password } else { @@ -372,7 +379,7 @@ flag to set your key via: }; let mut rng = thread_rng(); - let (wallet, _) = LocalWallet::encrypt_keystore( + let (wallet, _) = PrivateKeySigner::encrypt_keystore( dir, &mut rng, private_key, @@ -418,9 +425,12 @@ flag to set your key via: WalletSigner::Local(wallet) => { if verbose { println!("Address: {}", wallet.address()); - println!("Private key: 0x{}", hex::encode(wallet.signer().to_bytes())); + println!( + "Private key: 0x{}", + hex::encode(wallet.credential().to_bytes()) + ); } else { - println!("0x{}", hex::encode(wallet.signer().to_bytes())); + println!("0x{}", hex::encode(wallet.credential().to_bytes())); } } _ => { @@ -451,9 +461,9 @@ flag to set your key via: rpassword::prompt_password("Enter password: ")? }; - let wallet = LocalWallet::decrypt_keystore(keypath, password)?; + let wallet = PrivateKeySigner::decrypt_keystore(keypath, password)?; - let private_key = B256::from_slice(&wallet.signer().to_bytes()); + let private_key = B256::from_slice(&wallet.credential().to_bytes()); let success_message = format!("{}'s private key is: {}", &account_name, private_key); diff --git a/crates/cast/bin/cmd/wallet/vanity.rs b/crates/cast/bin/cmd/wallet/vanity.rs index 5bab0e318..1e94b5f9a 100644 --- a/crates/cast/bin/cmd/wallet/vanity.rs +++ b/crates/cast/bin/cmd/wallet/vanity.rs @@ -1,6 +1,6 @@ use alloy_primitives::Address; use alloy_signer::{k256::ecdsa::SigningKey, utils::secret_key_to_address}; -use alloy_signer_wallet::LocalWallet; +use alloy_signer_local::PrivateKeySigner; use clap::{builder::TypedValueParser, Parser}; use eyre::Result; use rayon::iter::{self, ParallelIterator}; @@ -63,16 +63,16 @@ struct Wallets { } impl WalletData { - pub fn new(wallet: &LocalWallet) -> Self { + pub fn new(wallet: &PrivateKeySigner) -> Self { Self { address: wallet.address().to_checksum(None), - private_key: format!("0x{}", hex::encode(wallet.signer().to_bytes())), + private_key: format!("0x{}", hex::encode(wallet.credential().to_bytes())), } } } impl VanityArgs { - pub fn run(self) -> Result { + pub fn run(self) -> Result { let Self { starts_with, ends_with, nonce, save_path } = self; let mut left_exact_hex = None; let mut left_regex = None; @@ -160,7 +160,7 @@ impl VanityArgs { String::new() }, wallet.address().to_checksum(None), - hex::encode(wallet.signer().to_bytes()), + hex::encode(wallet.credential().to_bytes()), ); Ok(wallet) @@ -170,7 +170,7 @@ impl VanityArgs { /// Saves the specified `wallet` to a 'vanity_addresses.json' file at the given `save_path`. /// If the file exists, the wallet data is appended to the existing content; /// otherwise, a new file is created. -fn save_wallet_to_file(wallet: &LocalWallet, path: &Path) -> Result<()> { +fn save_wallet_to_file(wallet: &PrivateKeySigner, path: &Path) -> Result<()> { let mut wallets = if path.exists() { let data = fs::read_to_string(path)?; serde_json::from_str::(&data).unwrap_or_default() @@ -185,7 +185,7 @@ fn save_wallet_to_file(wallet: &LocalWallet, path: &Path) -> Result<()> { } /// Generates random wallets until `matcher` matches the wallet address, returning the wallet. -pub fn find_vanity_address(matcher: T) -> Option { +pub fn find_vanity_address(matcher: T) -> Option { wallet_generator().find_any(create_matcher(matcher)).map(|(key, _)| key.into()) } @@ -194,7 +194,7 @@ pub fn find_vanity_address(matcher: T) -> Option pub fn find_vanity_address_with_nonce( matcher: T, nonce: u64, -) -> Option { +) -> Option { wallet_generator().find_any(create_nonce_matcher(matcher, nonce)).map(|(key, _)| key.into()) } diff --git a/crates/cast/bin/tx.rs b/crates/cast/bin/tx.rs index 77ed93bab..c0f1e1a97 100644 --- a/crates/cast/bin/tx.rs +++ b/crates/cast/bin/tx.rs @@ -3,7 +3,8 @@ use alloy_json_abi::Function; use alloy_network::{AnyNetwork, TransactionBuilder}; use alloy_primitives::{Address, TxKind}; use alloy_provider::Provider; -use alloy_rpc_types::{TransactionRequest, WithOtherFields}; +use alloy_rpc_types::TransactionRequest; +use alloy_serde::WithOtherFields; use alloy_transport::Transport; use eyre::Result; use foundry_cli::{ diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index 002212fcd..e84b5ee95 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -14,7 +14,8 @@ use alloy_provider::{ PendingTransactionBuilder, Provider, }; use alloy_rlp::Decodable; -use alloy_rpc_types::{BlockId, BlockNumberOrTag, Filter, TransactionRequest, WithOtherFields}; +use alloy_rpc_types::{BlockId, BlockNumberOrTag, Filter, TransactionRequest}; +use alloy_serde::WithOtherFields; use alloy_sol_types::sol; use alloy_transport::Transport; use base::{Base, NumberWithBase, ToBase}; @@ -94,7 +95,8 @@ where /// /// ``` /// use alloy_primitives::{Address, U256, Bytes}; - /// use alloy_rpc_types::{TransactionRequest, WithOtherFields}; + /// use alloy_rpc_types::{TransactionRequest}; + /// use alloy_serde::WithOtherFields; /// use cast::Cast; /// use alloy_provider::{RootProvider, ProviderBuilder, network::AnyNetwork}; /// use std::str::FromStr; @@ -175,7 +177,8 @@ where /// ``` /// use cast::{Cast}; /// use alloy_primitives::{Address, U256, Bytes}; - /// use alloy_rpc_types::{TransactionRequest, WithOtherFields}; + /// use alloy_rpc_types::{TransactionRequest}; + /// use alloy_serde::WithOtherFields; /// use alloy_provider::{RootProvider, ProviderBuilder, network::AnyNetwork}; /// use std::str::FromStr; /// use alloy_sol_types::{sol, SolCall}; @@ -236,7 +239,8 @@ where /// ``` /// use cast::{Cast}; /// use alloy_primitives::{Address, U256, Bytes}; - /// use alloy_rpc_types::{TransactionRequest, WithOtherFields}; + /// use alloy_serde::WithOtherFields; + /// use alloy_rpc_types::{TransactionRequest}; /// use alloy_provider::{RootProvider, ProviderBuilder, network::AnyNetwork}; /// use std::str::FromStr; /// use alloy_sol_types::{sol, SolCall}; diff --git a/crates/cheatcodes/Cargo.toml b/crates/cheatcodes/Cargo.toml index 074740765..41415c2f8 100644 --- a/crates/cheatcodes/Cargo.toml +++ b/crates/cheatcodes/Cargo.toml @@ -30,7 +30,7 @@ alloy-sol-types.workspace = true alloy-provider.workspace = true alloy-rpc-types.workspace = true alloy-signer.workspace = true -alloy-signer-wallet = { workspace = true, features = [ +alloy-signer-local = { workspace = true, features = [ "mnemonic-all-languages", "keystore", ] } diff --git a/crates/cheatcodes/src/error.rs b/crates/cheatcodes/src/error.rs index 508e7173e..ec4459d3b 100644 --- a/crates/cheatcodes/src/error.rs +++ b/crates/cheatcodes/src/error.rs @@ -1,7 +1,7 @@ use crate::Vm; use alloy_primitives::{Address, Bytes}; use alloy_signer::Error as SignerError; -use alloy_signer_wallet::WalletError; +use alloy_signer_local::LocalSignerError; use alloy_sol_types::SolError; use foundry_common::errors::FsPathError; use foundry_config::UnresolvedEnvVarError; @@ -299,7 +299,7 @@ impl_from!( std::str::Utf8Error, std::string::FromUtf8Error, UnresolvedEnvVarError, - WalletError, + LocalSignerError, SignerError, WalletSignerError, ); diff --git a/crates/cheatcodes/src/script.rs b/crates/cheatcodes/src/script.rs index 97c65106a..1f84e2475 100644 --- a/crates/cheatcodes/src/script.rs +++ b/crates/cheatcodes/src/script.rs @@ -2,7 +2,7 @@ use crate::{Cheatcode, CheatsCtxt, DatabaseExt, Result, Vm::*}; use alloy_primitives::{Address, B256, U256}; -use alloy_signer_wallet::LocalWallet; +use alloy_signer_local::PrivateKeySigner; use foundry_wallets::{multi_wallet::MultiWallet, WalletSigner}; use parking_lot::Mutex; use std::sync::Arc; @@ -107,12 +107,12 @@ impl ScriptWallets { /// Locks inner Mutex and adds a signer to the [MultiWallet]. pub fn add_private_key(&self, private_key: &B256) -> Result<()> { - self.add_local_signer(LocalWallet::from_bytes(private_key)?); + self.add_local_signer(PrivateKeySigner::from_bytes(private_key)?); Ok(()) } /// Locks inner Mutex and adds a signer to the [MultiWallet]. - pub fn add_local_signer(&self, wallet: LocalWallet) { + pub fn add_local_signer(&self, wallet: PrivateKeySigner) { self.inner.lock().multi_wallet.add_signer(WalletSigner::Local(wallet)); } diff --git a/crates/cheatcodes/src/utils.rs b/crates/cheatcodes/src/utils.rs index f4f3ab485..8d4c547df 100644 --- a/crates/cheatcodes/src/utils.rs +++ b/crates/cheatcodes/src/utils.rs @@ -3,12 +3,12 @@ use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Result, Vm::*}; use alloy_primitives::{keccak256, Address, B256, U256}; use alloy_signer::{Signer, SignerSync}; -use alloy_signer_wallet::{ +use alloy_signer_local::{ coins_bip39::{ ChineseSimplified, ChineseTraditional, Czech, English, French, Italian, Japanese, Korean, Portuguese, Spanish, Wordlist, }, - LocalWallet, MnemonicBuilder, + MnemonicBuilder, PrivateKeySigner, }; use alloy_sol_types::SolValue; use foundry_common::ens::namehash; @@ -268,8 +268,8 @@ pub(super) fn parse_private_key(private_key: &U256) -> Result { SigningKey::from_bytes((&bytes).into()).map_err(Into::into) } -pub(super) fn parse_wallet(private_key: &U256) -> Result { - parse_private_key(private_key).map(LocalWallet::from) +pub(super) fn parse_wallet(private_key: &U256) -> Result { + parse_private_key(private_key).map(PrivateKeySigner::from) } fn derive_key_str(mnemonic: &str, path: &str, index: u32, language: &str) -> Result { @@ -302,7 +302,7 @@ fn derive_key(mnemonic: &str, path: &str, index: u32) -> Result { .phrase(mnemonic) .derivation_path(derive_key_path(path, index))? .build()?; - let private_key = U256::from_be_bytes(wallet.signer().to_bytes().into()); + let private_key = U256::from_be_bytes(wallet.credential().to_bytes().into()); Ok(private_key.abi_encode()) } diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 907f3b625..c8c79534d 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -18,22 +18,31 @@ foundry-compilers.workspace = true foundry-config.workspace = true foundry-linking.workspace = true +alloy-consensus.workspace = true +alloy-contract.workspace = true alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } alloy-json-abi.workspace = true -alloy-primitives = { workspace = true, features = ["serde", "getrandom", "arbitrary", "rlp"] } -alloy-rpc-types-engine.workspace = true -alloy-rpc-types = { workspace = true, features = ["eth"] } -alloy-rpc-client.workspace = true -alloy-provider.workspace = true -alloy-transport.workspace = true -alloy-transport-http = { workspace = true, features = ["reqwest", "reqwest-rustls-tls"] } -alloy-transport-ws.workspace = true -alloy-transport-ipc.workspace = true alloy-json-rpc.workspace = true +alloy-primitives = { workspace = true, features = [ + "serde", + "getrandom", + "arbitrary", + "rlp", +] } +alloy-provider.workspace = true alloy-pubsub.workspace = true +alloy-rpc-client.workspace = true +alloy-rpc-types = { workspace = true, features = ["eth"] } +alloy-rpc-types-engine.workspace = true +alloy-serde.workspace = true alloy-sol-types.workspace = true -alloy-contract.workspace = true -alloy-consensus.workspace = true +alloy-transport-http = { workspace = true, features = [ + "reqwest", + "reqwest-rustls-tls", +] } +alloy-transport-ipc.workspace = true +alloy-transport-ws.workspace = true +alloy-transport.workspace = true tower.workspace = true diff --git a/crates/common/src/fmt/ui.rs b/crates/common/src/fmt/ui.rs index 63b2905a8..e192f3cad 100644 --- a/crates/common/src/fmt/ui.rs +++ b/crates/common/src/fmt/ui.rs @@ -4,9 +4,9 @@ use crate::TransactionReceiptWithRevertReason; use alloy_consensus::{AnyReceiptEnvelope, Receipt, ReceiptWithBloom, TxType}; use alloy_primitives::*; use alloy_rpc_types::{ - other::OtherFields, AnyTransactionReceipt, Block, BlockTransactions, Log, Transaction, - TransactionReceipt, + AnyTransactionReceipt, Block, BlockTransactions, Log, Transaction, TransactionReceipt, }; +use alloy_serde::OtherFields; use serde::Deserialize; /// length of the name column for pretty formatting `{:>20}{value}` diff --git a/crates/common/src/provider/mod.rs b/crates/common/src/provider/mod.rs index 388a795dd..ef7b62055 100644 --- a/crates/common/src/provider/mod.rs +++ b/crates/common/src/provider/mod.rs @@ -8,8 +8,8 @@ use crate::{ provider::runtime_transport::RuntimeTransportBuilder, ALCHEMY_FREE_TIER_CUPS, REQUEST_TIMEOUT, }; use alloy_provider::{ - fillers::{ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller, SignerFiller}, - network::{AnyNetwork, EthereumSigner}, + fillers::{ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller, WalletFiller}, + network::{AnyNetwork, EthereumWallet}, Identity, ProviderBuilder as AlloyProviderBuilder, RootProvider, }; use alloy_rpc_client::ClientBuilder; @@ -34,7 +34,7 @@ pub type RetryProvider = RootProvider = FillProvider< JoinFill< JoinFill, NonceFiller>, ChainIdFiller>, - SignerFiller, + WalletFiller, >, RootProvider, N>, RetryBackoffService, @@ -268,8 +268,8 @@ impl ProviderBuilder { Ok(provider) } - /// Constructs the `RetryProvider` with a signer - pub fn build_with_signer(self, signer: EthereumSigner) -> Result { + /// Constructs the `RetryProvider` with a wallet. + pub fn build_with_wallet(self, wallet: EthereumWallet) -> Result { let Self { url, chain: _, @@ -301,7 +301,7 @@ impl ProviderBuilder { let provider = AlloyProviderBuilder::<_, _, AnyNetwork>::default() .with_recommended_fillers() - .signer(signer) + .wallet(wallet) .on_provider(RootProvider::new(client)); Ok(provider) diff --git a/crates/common/src/transactions.rs b/crates/common/src/transactions.rs index 50fcf7d99..9a6ba190e 100644 --- a/crates/common/src/transactions.rs +++ b/crates/common/src/transactions.rs @@ -1,6 +1,8 @@ -//! wrappers for transactions +//! Wrappers for transactions. + use alloy_provider::{network::AnyNetwork, Provider}; -use alloy_rpc_types::{AnyTransactionReceipt, BlockId, WithOtherFields}; +use alloy_rpc_types::{AnyTransactionReceipt, BlockId}; +use alloy_serde::WithOtherFields; use alloy_transport::Transport; use eyre::Result; use serde::{Deserialize, Serialize}; diff --git a/crates/evm/core/Cargo.toml b/crates/evm/core/Cargo.toml index c0b1b3e3d..b8253b494 100644 --- a/crates/evm/core/Cargo.toml +++ b/crates/evm/core/Cargo.toml @@ -20,13 +20,19 @@ foundry-config.workspace = true foundry-macros.workspace = true alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } -alloy-json-abi.workspace = true -alloy-primitives = { workspace = true, features = ["serde", "getrandom", "arbitrary", "rlp"] } alloy-genesis.workspace = true +alloy-json-abi.workspace = true +alloy-primitives = { workspace = true, features = [ + "serde", + "getrandom", + "arbitrary", + "rlp", +] } alloy-provider.workspace = true -alloy-transport.workspace = true alloy-rpc-types.workspace = true +alloy-serde.workspace = true alloy-sol-types.workspace = true +alloy-transport.workspace = true revm = { workspace = true, features = [ "std", diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index b96829196..257d72bdb 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -9,7 +9,8 @@ use crate::{ }; use alloy_genesis::GenesisAccount; use alloy_primitives::{b256, keccak256, Address, B256, U256}; -use alloy_rpc_types::{Block, BlockNumberOrTag, BlockTransactions, Transaction, WithOtherFields}; +use alloy_rpc_types::{Block, BlockNumberOrTag, BlockTransactions, Transaction}; +use alloy_serde::WithOtherFields; use eyre::Context; use foundry_common::{is_known_system_sender, SYSTEM_TRANSACTION_TYPE}; use revm::{ diff --git a/crates/evm/core/src/fork/backend.rs b/crates/evm/core/src/fork/backend.rs index 468be738d..6bd1caaa1 100644 --- a/crates/evm/core/src/fork/backend.rs +++ b/crates/evm/core/src/fork/backend.rs @@ -5,7 +5,8 @@ use crate::{ }; use alloy_primitives::{keccak256, Address, Bytes, B256, U256}; use alloy_provider::{network::AnyNetwork, Provider}; -use alloy_rpc_types::{Block, BlockId, Transaction, WithOtherFields}; +use alloy_rpc_types::{Block, BlockId, Transaction}; +use alloy_serde::WithOtherFields; use alloy_transport::Transport; use eyre::WrapErr; use foundry_common::NON_ARCHIVE_NODE_WARNING; diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 995053fee..493a8c042 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -56,18 +56,19 @@ forge-sol-macro-gen.workspace = true foundry-cli.workspace = true foundry-debugger.workspace = true +alloy-chains.workspace = true +alloy-consensus.workspace = true alloy-dyn-abi.workspace = true alloy-json-abi.workspace = true +alloy-network.workspace = true alloy-primitives = { workspace = true, features = ["serde"] } -alloy-rpc-types.workspace = true alloy-provider = { workspace = true, features = ["reqwest", "ws", "ipc"] } -alloy-network.workspace = true -alloy-transport.workspace = true +alloy-rpc-types.workspace = true +alloy-serde.workspace = true alloy-signer.workspace = true -alloy-sol-macro-input.workspace = true alloy-sol-macro-expander = { workspace = true, features = ["json"] } -alloy-consensus.workspace = true -alloy-chains.workspace = true +alloy-sol-macro-input.workspace = true +alloy-transport.workspace = true async-trait = "0.1" clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } @@ -108,7 +109,6 @@ soldeer.workspace = true [target.'cfg(unix)'.dependencies] tikv-jemallocator = { workspace = true, optional = true } - [dev-dependencies] anvil.workspace = true foundry-test-utils.workspace = true @@ -125,7 +125,7 @@ svm = { package = "svm-rs", version = "0.5", default-features = false, features tempfile.workspace = true tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] } -alloy-signer-wallet.workspace = true +alloy-signer-local.workspace = true [features] default = ["rustls", "jemalloc"] diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index 3b0bc12bc..13ab69da6 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -1,10 +1,11 @@ use alloy_chains::Chain; use alloy_dyn_abi::{DynSolValue, JsonAbiExt, Specifier}; use alloy_json_abi::{Constructor, JsonAbi}; -use alloy_network::{AnyNetwork, EthereumSigner, TransactionBuilder}; +use alloy_network::{AnyNetwork, EthereumWallet, TransactionBuilder}; use alloy_primitives::{Address, Bytes}; use alloy_provider::{Provider, ProviderBuilder}; -use alloy_rpc_types::{AnyTransactionReceipt, TransactionRequest, WithOtherFields}; +use alloy_rpc_types::{AnyTransactionReceipt, TransactionRequest}; +use alloy_serde::WithOtherFields; use alloy_signer::Signer; use alloy_transport::{Transport, TransportError}; use clap::{Parser, ValueHint}; @@ -143,7 +144,7 @@ impl CreateArgs { let signer = self.eth.wallet.signer().await?; let deployer = signer.address(); let provider = ProviderBuilder::<_, _, AnyNetwork>::default() - .signer(EthereumSigner::new(signer)) + .wallet(EthereumWallet::new(signer)) .on_provider(provider); self.deploy(abi, bin, params, provider, chain_id, deployer).await } diff --git a/crates/forge/tests/cli/create.rs b/crates/forge/tests/cli/create.rs index 20f0aa7cc..c9484db0f 100644 --- a/crates/forge/tests/cli/create.rs +++ b/crates/forge/tests/cli/create.rs @@ -135,7 +135,7 @@ forgetest_async!(can_create_template_contract, |prj, cmd| { let (_api, handle) = spawn(NodeConfig::test()).await; let rpc = handle.http_endpoint(); let wallet = handle.dev_wallets().next().unwrap(); - let pk = hex::encode(wallet.signer().to_bytes()); + let pk = hex::encode(wallet.credential().to_bytes()); // explicitly byte code hash for consistent checks let config = Config { bytecode_hash: BytecodeHash::None, ..Default::default() }; @@ -201,7 +201,7 @@ forgetest_async!(can_create_with_constructor_args, |prj, cmd| { let (_api, handle) = spawn(NodeConfig::test()).await; let rpc = handle.http_endpoint(); let wallet = handle.dev_wallets().next().unwrap(); - let pk = hex::encode(wallet.signer().to_bytes()); + let pk = hex::encode(wallet.credential().to_bytes()); // explicitly byte code hash for consistent checks let config = Config { bytecode_hash: BytecodeHash::None, ..Default::default() }; @@ -276,7 +276,7 @@ forgetest_async!(can_create_and_call, |prj, cmd| { let (_api, handle) = spawn(NodeConfig::test()).await; let rpc = handle.http_endpoint(); let wallet = handle.dev_wallets().next().unwrap(); - let pk = hex::encode(wallet.signer().to_bytes()); + let pk = hex::encode(wallet.credential().to_bytes()); // explicitly byte code hash for consistent checks let config = Config { bytecode_hash: BytecodeHash::None, ..Default::default() }; diff --git a/crates/forge/tests/cli/utils.rs b/crates/forge/tests/cli/utils.rs index 75523e50a..094255195 100644 --- a/crates/forge/tests/cli/utils.rs +++ b/crates/forge/tests/cli/utils.rs @@ -2,7 +2,7 @@ use alloy_chains::NamedChain; use alloy_primitives::Address; -use alloy_signer_wallet::LocalWallet; +use alloy_signer_local::PrivateKeySigner; /// Returns the current millis since unix epoch. /// @@ -45,7 +45,7 @@ pub struct EnvExternalities { impl EnvExternalities { pub fn address(&self) -> Option
{ - let pk: LocalWallet = self.pk.parse().ok()?; + let pk: PrivateKeySigner = self.pk.parse().ok()?; Some(pk.address()) } diff --git a/crates/script/Cargo.toml b/crates/script/Cargo.toml index 9c9879392..4d97f3fc9 100644 --- a/crates/script/Cargo.toml +++ b/crates/script/Cargo.toml @@ -46,6 +46,7 @@ dialoguer = { version = "0.11", default-features = false } indicatif = "0.17" alloy-signer.workspace = true +alloy-serde.workspace = true alloy-network.workspace = true alloy-provider.workspace = true alloy-chains.workspace = true diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index aaf866a11..b7eb9f5b0 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -4,10 +4,11 @@ use crate::{ }; use alloy_chains::Chain; use alloy_eips::eip2718::Encodable2718; -use alloy_network::{AnyNetwork, EthereumSigner, TransactionBuilder}; +use alloy_network::{AnyNetwork, EthereumWallet, TransactionBuilder}; use alloy_primitives::{utils::format_units, Address, TxHash}; use alloy_provider::{utils::Eip1559Estimation, Provider}; -use alloy_rpc_types::{TransactionRequest, WithOtherFields}; +use alloy_rpc_types::TransactionRequest; +use alloy_serde::WithOtherFields; use alloy_transport::Transport; use eyre::{bail, Context, Result}; use forge_verify::provider::VerificationProviderType; @@ -102,7 +103,7 @@ pub async fn send_transaction( #[derive(Clone)] pub enum SendTransactionKind<'a> { Unlocked(Address), - Raw(&'a EthereumSigner), + Raw(&'a EthereumWallet), } /// Represents how to send _all_ transactions @@ -110,7 +111,7 @@ pub enum SendTransactionsKind { /// Send via `eth_sendTransaction` and rely on the `from` address being unlocked. Unlocked(HashSet
), /// Send a signed transaction via `eth_sendRawTransaction` - Raw(HashMap), + Raw(HashMap), } impl SendTransactionsKind { @@ -223,7 +224,7 @@ impl BundledState { let signers = signers .into_iter() - .map(|(addr, signer)| (addr, EthereumSigner::new(signer))) + .map(|(addr, signer)| (addr, EthereumWallet::new(signer))) .collect(); SendTransactionsKind::Raw(signers) diff --git a/crates/script/src/sequence.rs b/crates/script/src/sequence.rs index f95262aae..0eee95951 100644 --- a/crates/script/src/sequence.rs +++ b/crates/script/src/sequence.rs @@ -4,7 +4,8 @@ use crate::{ verify::VerifyBundle, }; use alloy_primitives::{Address, TxHash}; -use alloy_rpc_types::{AnyTransactionReceipt, TransactionRequest, WithOtherFields}; +use alloy_rpc_types::{AnyTransactionReceipt, TransactionRequest}; +use alloy_serde::WithOtherFields; use eyre::{ContextCompat, Result, WrapErr}; use forge_verify::provider::VerificationProviderType; use foundry_cli::utils::{now, Git}; diff --git a/crates/script/src/transaction.rs b/crates/script/src/transaction.rs index fd63734d1..84657ebf3 100644 --- a/crates/script/src/transaction.rs +++ b/crates/script/src/transaction.rs @@ -1,7 +1,8 @@ use super::ScriptResult; use alloy_dyn_abi::JsonAbiExt; use alloy_primitives::{Address, Bytes, TxKind, B256}; -use alloy_rpc_types::{request::TransactionRequest, WithOtherFields}; +use alloy_rpc_types::request::TransactionRequest; +use alloy_serde::WithOtherFields; use eyre::{ContextCompat, Result, WrapErr}; use foundry_common::{fmt::format_token_raw, ContractData, SELECTOR_LEN}; use foundry_evm::{constants::DEFAULT_CREATE2_DEPLOYER, traces::CallTraceDecoder}; diff --git a/crates/wallets/Cargo.toml b/crates/wallets/Cargo.toml index 34dfda6d4..dc013ae62 100644 --- a/crates/wallets/Cargo.toml +++ b/crates/wallets/Cargo.toml @@ -17,7 +17,7 @@ foundry-config.workspace = true alloy-primitives.workspace = true alloy-signer = { workspace = true, features = ["eip712"] } -alloy-signer-wallet = { workspace = true, features = ["mnemonic", "keystore"] } +alloy-signer-local = { workspace = true, features = ["mnemonic", "keystore"] } alloy-signer-ledger = { workspace = true, features = ["eip712"] } alloy-signer-trezor.workspace = true alloy-network.workspace = true diff --git a/crates/wallets/src/error.rs b/crates/wallets/src/error.rs index c7ac55b82..4e299b055 100644 --- a/crates/wallets/src/error.rs +++ b/crates/wallets/src/error.rs @@ -1,7 +1,7 @@ use alloy_signer::k256::ecdsa; use alloy_signer_ledger::LedgerError; +use alloy_signer_local::LocalSignerError; use alloy_signer_trezor::TrezorError; -use alloy_signer_wallet::WalletError; use hex::FromHexError; #[cfg(feature = "aws-kms")] @@ -21,7 +21,7 @@ pub enum PrivateKeyError { #[derive(Debug, thiserror::Error)] pub enum WalletSignerError { #[error(transparent)] - Local(#[from] WalletError), + Local(#[from] LocalSignerError), #[error(transparent)] Ledger(#[from] LedgerError), #[error(transparent)] diff --git a/crates/wallets/src/utils.rs b/crates/wallets/src/utils.rs index eaaf81e45..da19b6d9e 100644 --- a/crates/wallets/src/utils.rs +++ b/crates/wallets/src/utils.rs @@ -1,8 +1,8 @@ use crate::{error::PrivateKeyError, PendingSigner, WalletSigner}; use alloy_primitives::B256; use alloy_signer_ledger::HDPath as LedgerHDPath; +use alloy_signer_local::PrivateKeySigner; use alloy_signer_trezor::HDPath as TrezorHDPath; -use alloy_signer_wallet::LocalWallet; use eyre::{Context, Result}; use foundry_config::Config; use hex::FromHex; @@ -24,7 +24,7 @@ pub fn create_private_key_signer(private_key_str: &str) -> Result ensure_pk_not_env(private_key_str)?; eyre::bail!("Failed to decode private key") }; - match LocalWallet::from_bytes(&private_key) { + match PrivateKeySigner::from_bytes(&private_key) { Ok(pk) => Ok(WalletSigner::Local(pk)), Err(err) => { ensure_pk_not_env(private_key_str)?; @@ -141,7 +141,7 @@ pub fn create_keystore_signer( }?; if let Some(password) = password { - let wallet = LocalWallet::decrypt_keystore(path, password) + let wallet = PrivateKeySigner::decrypt_keystore(path, password) .wrap_err_with(|| format!("Failed to decrypt keystore {path:?}"))?; Ok((Some(WalletSigner::Local(wallet)), None)) } else { diff --git a/crates/wallets/src/wallet_signer.rs b/crates/wallets/src/wallet_signer.rs index d0f1de4a2..f1f7bad88 100644 --- a/crates/wallets/src/wallet_signer.rs +++ b/crates/wallets/src/wallet_signer.rs @@ -5,8 +5,8 @@ use alloy_network::TxSigner; use alloy_primitives::{Address, ChainId, B256}; use alloy_signer::{Signature, 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}; -use alloy_signer_wallet::{coins_bip39::English, LocalWallet, MnemonicBuilder}; use alloy_sol_types::{Eip712Domain, SolStruct}; use async_trait::async_trait; use std::path::PathBuf; @@ -29,7 +29,7 @@ pub type Result = std::result::Result; #[derive(Debug)] pub enum WalletSigner { /// Wrapper around local wallet. e.g. private key, mnemonic - Local(LocalWallet), + Local(PrivateKeySigner), /// Wrapper around Ledger signer. Ledger(LedgerSigner), /// Wrapper around Trezor signer. @@ -107,7 +107,7 @@ impl WalletSigner { } pub fn from_private_key(private_key: &B256) -> Result { - Ok(Self::Local(LocalWallet::from_bytes(private_key)?)) + Ok(Self::Local(PrivateKeySigner::from_bytes(private_key)?)) } /// Returns a list of addresses available to use with current signer @@ -263,7 +263,7 @@ impl PendingSigner { match self { Self::Keystore(path) => { let password = rpassword::prompt_password("Enter keystore password:")?; - Ok(WalletSigner::Local(LocalWallet::decrypt_keystore(path, password)?)) + Ok(WalletSigner::Local(PrivateKeySigner::decrypt_keystore(path, password)?)) } Self::Interactive => { let private_key = rpassword::prompt_password("Enter private key:")?; From fd185c85cacd04101195c7050d4084de488a8a98 Mon Sep 17 00:00:00 2001 From: sodamntired Date: Mon, 17 Jun 2024 16:00:36 +0300 Subject: [PATCH 414/430] feat: debug_getRawTransaction RPC endpoint (#8162) * feat: debug_getRawTransaction rpc endpoint * clippy happy * conflicts resolved * chore: tests + refactor * fix --- crates/anvil/core/src/eth/mod.rs | 35 ++++++++++++++ crates/anvil/src/eth/api.rs | 68 +++++++++++++++++++++++++++- crates/anvil/tests/it/transaction.rs | 25 +++++++++- 3 files changed, 125 insertions(+), 3 deletions(-) diff --git a/crates/anvil/core/src/eth/mod.rs b/crates/anvil/core/src/eth/mod.rs index 0f6205f14..d2fa41a7b 100644 --- a/crates/anvil/core/src/eth/mod.rs +++ b/crates/anvil/core/src/eth/mod.rs @@ -196,6 +196,18 @@ pub enum EthRequest { #[cfg_attr(feature = "serde", serde(rename = "eth_getTransactionByBlockNumberAndIndex"))] EthGetTransactionByBlockNumberAndIndex(BlockNumber, Index), + #[cfg_attr( + feature = "serde", + serde(rename = "eth_getRawTransactionByHash", with = "sequence") + )] + EthGetRawTransactionByHash(TxHash), + + #[cfg_attr(feature = "serde", serde(rename = "eth_getRawTransactionByBlockHashAndIndex"))] + EthGetRawTransactionByBlockHashAndIndex(TxHash, Index), + + #[cfg_attr(feature = "serde", serde(rename = "eth_getRawTransactionByBlockNumberAndIndex"))] + EthGetRawTransactionByBlockNumberAndIndex(BlockNumber, Index), + #[cfg_attr(feature = "serde", serde(rename = "eth_getTransactionReceipt", with = "sequence"))] EthGetTransactionReceipt(B256), @@ -266,6 +278,10 @@ pub enum EthRequest { #[cfg_attr(feature = "serde", serde(rename = "eth_syncing", with = "empty_params"))] EthSyncing(()), + /// geth's `debug_getRawTransaction` endpoint + #[cfg_attr(feature = "serde", serde(rename = "debug_getRawTransaction", with = "sequence"))] + DebugGetRawTransaction(TxHash), + /// geth's `debug_traceTransaction` endpoint #[cfg_attr(feature = "serde", serde(rename = "debug_traceTransaction"))] DebugTraceTransaction( @@ -1421,6 +1437,25 @@ mod tests { let _req = serde_json::from_value::(value).unwrap(); } + #[test] + fn test_serde_debug_raw_transaction() { + let s = r#"{"jsonrpc":"2.0","method":"debug_getRawTransaction","params":["0x3ed3a89bc10115a321aee238c02de214009f8532a65368e5df5eaf732ee7167c"],"id":1}"#; + let value: serde_json::Value = serde_json::from_str(s).unwrap(); + let _req = serde_json::from_value::(value).unwrap(); + + let s = r#"{"jsonrpc":"2.0","method":"eth_getRawTransactionByHash","params":["0x3ed3a89bc10115a321aee238c02de214009f8532a65368e5df5eaf732ee7167c"],"id":1}"#; + let value: serde_json::Value = serde_json::from_str(s).unwrap(); + let _req = serde_json::from_value::(value).unwrap(); + + let s = r#"{"jsonrpc":"2.0","method":"eth_getRawTransactionByBlockHashAndIndex","params":["0x3ed3a89bc10115a321aee238c02de214009f8532a65368e5df5eaf732ee7167c",1],"id":1}"#; + let value: serde_json::Value = serde_json::from_str(s).unwrap(); + let _req = serde_json::from_value::(value).unwrap(); + + let s = r#"{"jsonrpc":"2.0","method":"eth_getRawTransactionByBlockNumberAndIndex","params":["0x3ed3a89b",0],"id":1}"#; + let value: serde_json::Value = serde_json::from_str(s).unwrap(); + let _req = serde_json::from_value::(value).unwrap(); + } + #[test] fn test_serde_debug_trace_transaction() { let s = r#"{"method": "debug_traceTransaction", "params": diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 7f76e6a70..e28a8c462 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -31,7 +31,7 @@ use crate::{ revm::primitives::{BlobExcessGasAndPrice, Output}, ClientFork, LoggingManager, Miner, MiningMode, StorageInfo, }; -use alloy_consensus::transaction::eip4844::TxEip4844Variant; +use alloy_consensus::{transaction::eip4844::TxEip4844Variant, TxEnvelope}; use alloy_dyn_abi::TypedData; use alloy_eips::eip2718::Encodable2718; use alloy_network::eip2718::Decodable2718; @@ -235,6 +235,15 @@ impl EthApi { EthRequest::EthEstimateGas(call, block, overrides) => { self.estimate_gas(call, block, overrides).await.to_rpc_result() } + EthRequest::EthGetRawTransactionByHash(hash) => { + self.raw_transaction(hash).await.to_rpc_result() + } + EthRequest::EthGetRawTransactionByBlockHashAndIndex(hash, index) => { + self.raw_transaction_by_block_hash_and_index(hash, index).await.to_rpc_result() + } + EthRequest::EthGetRawTransactionByBlockNumberAndIndex(num, index) => { + self.raw_transaction_by_block_number_and_index(num, index).await.to_rpc_result() + } EthRequest::EthGetTransactionByBlockHashAndIndex(hash, index) => { self.transaction_by_block_hash_and_index(hash, index).await.to_rpc_result() } @@ -265,7 +274,10 @@ impl EthApi { EthRequest::EthFeeHistory(count, newest, reward_percentiles) => { self.fee_history(count, newest, reward_percentiles).await.to_rpc_result() } - + // non eth-standard rpc calls + EthRequest::DebugGetRawTransaction(hash) => { + self.raw_transaction(hash).await.to_rpc_result() + } // non eth-standard rpc calls EthRequest::DebugTraceTransaction(tx, opts) => { self.debug_trace_transaction(tx, opts).await.to_rpc_result() @@ -464,6 +476,19 @@ impl EthApi { Ok(block_request) } + async fn inner_raw_transaction(&self, hash: B256) -> Result> { + 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())) + }), + None => Ok(None), + }, + } + } + /// Returns the current client version. /// /// Handler for ETH RPC call: `web3_clientVersion` @@ -906,6 +931,7 @@ impl EthApi { } let request = self.build_typed_tx_request(request, nonce)?; + // if the sender is currently impersonated we need to "bypass" signing let pending_transaction = if self.is_impersonated(from) { let bypass_signature = self.backend.cheats().bypass_signature(); @@ -1437,6 +1463,44 @@ impl EthApi { Ok(self.filters.uninstall_filter(id).await.is_some()) } + /// Returns EIP-2718 encoded raw transaction + /// + /// Handler for RPC call: `debug_getRawTransaction` + pub async fn raw_transaction(&self, hash: B256) -> Result> { + node_info!("debug_getRawTransaction"); + self.inner_raw_transaction(hash).await + } + + /// Returns EIP-2718 encoded raw transaction by block hash and index + /// + /// Handler for RPC call: `eth_getRawTransactionByBlockHashAndIndex` + pub async fn raw_transaction_by_block_hash_and_index( + &self, + block_hash: B256, + index: Index, + ) -> 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, + None => Ok(None), + } + } + + /// Returns EIP-2718 encoded raw transaction by block number and index + /// + /// Handler for RPC call: `eth_getRawTransactionByBlockNumberAndIndex` + pub async fn raw_transaction_by_block_number_and_index( + &self, + block_number: BlockNumber, + index: Index, + ) -> 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, + None => Ok(None), + } + } + /// Returns traces for the transaction hash for geth's tracing endpoint /// /// Handler for RPC call: `debug_traceTransaction` diff --git a/crates/anvil/tests/it/transaction.rs b/crates/anvil/tests/it/transaction.rs index 1565e587b..10b996854 100644 --- a/crates/anvil/tests/it/transaction.rs +++ b/crates/anvil/tests/it/transaction.rs @@ -723,7 +723,30 @@ async fn can_get_pending_transaction() { } #[tokio::test(flavor = "multi_thread")] -async fn test_first_noce_is_zero() { +async fn can_get_raw_transaction() { + let (api, handle) = spawn(NodeConfig::test()).await; + + // first test the pending tx, disable auto mine + api.anvil_set_auto_mine(false).await.unwrap(); + + let provider = handle.http_provider(); + + let from = handle.dev_wallets().next().unwrap().address(); + let tx = TransactionRequest::default().from(from).value(U256::from(1488)).to(Address::random()); + let tx = WithOtherFields::new(tx); + let tx = provider.send_transaction(tx).await.unwrap(); + + let res1 = api.raw_transaction(*tx.tx_hash()).await; + assert!(res1.is_ok()); + + api.mine_one().await; + let res2 = api.raw_transaction(*tx.tx_hash()).await; + + assert_eq!(res1.unwrap(), res2.unwrap()); +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_first_nonce_is_zero() { let (api, handle) = spawn(NodeConfig::test()).await; api.anvil_set_auto_mine(false).await.unwrap(); From f6ad1e5d22ef725f12d062dd44d09ed22d4a2496 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Mon, 17 Jun 2024 16:48:40 +0300 Subject: [PATCH 415/430] fix(invariant): weight invariant selectors by number of selectors (#8176) * fix(invariant): weight invariant selectors by number of selectors - Consolidate FuzzRunIdentifiedContracts logic - add function to flatten contracts function in order to be used by strategy - test * Changes after review: cleanup --- crates/evm/evm/src/executors/invariant/mod.rs | 28 ++--- crates/evm/fuzz/src/invariant/filters.rs | 3 +- crates/evm/fuzz/src/invariant/mod.rs | 89 +++++++++++++++ crates/evm/fuzz/src/strategies/invariants.rs | 67 ++++-------- crates/evm/fuzz/src/strategies/mod.rs | 2 +- crates/evm/fuzz/src/strategies/state.rs | 43 +------- crates/forge/tests/it/invariant.rs | 27 +++++ .../common/InvariantSelectorsWeight.t.sol | 101 ++++++++++++++++++ 8 files changed, 249 insertions(+), 111 deletions(-) create mode 100644 testdata/default/fuzz/invariant/common/InvariantSelectorsWeight.t.sol diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 4cbd8505d..1eee0cafc 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -18,7 +18,7 @@ use foundry_evm_fuzz::{ ArtifactFilters, BasicTxDetails, FuzzRunIdentifiedContracts, InvariantContract, RandomCallGenerator, SenderFilters, TargetedContracts, }, - strategies::{collect_created_contracts, invariant_strat, override_call_strat, EvmFuzzState}, + strategies::{invariant_strat, override_call_strat, EvmFuzzState}, FuzzCase, FuzzFixtures, FuzzedCases, }; use foundry_evm_traces::CallTraceArena; @@ -251,17 +251,14 @@ impl<'a> InvariantExecutor<'a> { // Collect created contracts and add to fuzz targets only if targeted contracts // are updatable. - if targeted_contracts.is_updatable { - if let Err(error) = collect_created_contracts( - &state_changeset, - self.project_contracts, - self.setup_contracts, - &self.artifact_filters, - &targeted_contracts, - &mut created_contracts, - ) { - warn!(target: "forge::test", "{error}"); - } + if let Err(error) = &targeted_contracts.collect_created_contracts( + &state_changeset, + self.project_contracts, + self.setup_contracts, + &self.artifact_filters, + &mut created_contracts, + ) { + warn!(target: "forge::test", "{error}"); } fuzz_runs.push(FuzzCase { @@ -319,12 +316,7 @@ impl<'a> InvariantExecutor<'a> { } // We clear all the targeted contracts created during this run. - if !created_contracts.is_empty() { - let mut writable_targeted = targeted_contracts.targets.lock(); - for addr in created_contracts.iter() { - writable_targeted.remove(addr); - } - } + let _ = &targeted_contracts.clear_created_contracts(created_contracts); if gas_report_traces.borrow().len() < self.config.gas_report_samples as usize { gas_report_traces.borrow_mut().push(run_traces); diff --git a/crates/evm/fuzz/src/invariant/filters.rs b/crates/evm/fuzz/src/invariant/filters.rs index d0ebb33e5..520e5b5af 100644 --- a/crates/evm/fuzz/src/invariant/filters.rs +++ b/crates/evm/fuzz/src/invariant/filters.rs @@ -25,8 +25,7 @@ impl ArtifactFilters { /// Gets all the targeted functions from `artifact`. Returns error, if selectors do not match /// the `artifact`. /// - /// An empty vector means that it targets any mutable function. See `select_random_function` for - /// more. + /// An empty vector means that it targets any mutable function. pub fn get_targeted_functions( &self, artifact: &ArtifactId, diff --git a/crates/evm/fuzz/src/invariant/mod.rs b/crates/evm/fuzz/src/invariant/mod.rs index 26c94351e..82938f6cc 100644 --- a/crates/evm/fuzz/src/invariant/mod.rs +++ b/crates/evm/fuzz/src/invariant/mod.rs @@ -8,6 +8,8 @@ pub use call_override::RandomCallGenerator; mod filters; pub use filters::{ArtifactFilters, SenderFilters}; +use foundry_common::{ContractsByAddress, ContractsByArtifact}; +use foundry_evm_core::utils::StateChangeset; pub type TargetedContracts = BTreeMap)>; @@ -44,6 +46,93 @@ impl FuzzRunIdentifiedContracts { }; f(abi, abi_f); } + + /// Returns flatten target contract address and functions to be fuzzed. + /// Includes contract targeted functions if specified, else all mutable contract functions. + pub fn fuzzed_functions(&self) -> Vec<(Address, Function)> { + let mut fuzzed_functions = vec![]; + for (contract, (_, abi, functions)) in self.targets.lock().iter() { + if !abi.functions.is_empty() { + for function in abi_fuzzed_functions(abi, functions) { + fuzzed_functions.push((*contract, function.clone())); + } + } + } + fuzzed_functions + } + + /// If targets are updatable, collect all contracts created during an invariant run (which + /// haven't been discovered yet). + pub fn collect_created_contracts( + &self, + state_changeset: &StateChangeset, + project_contracts: &ContractsByArtifact, + setup_contracts: &ContractsByAddress, + artifact_filters: &ArtifactFilters, + created_contracts: &mut Vec
, + ) -> eyre::Result<()> { + if self.is_updatable { + let mut targets = self.targets.lock(); + for (address, account) in state_changeset { + if setup_contracts.contains_key(address) { + continue; + } + if !account.is_touched() { + continue; + } + let Some(code) = &account.info.code else { + continue; + }; + if code.is_empty() { + continue; + } + let Some((artifact, contract)) = + project_contracts.find_by_deployed_code(code.original_byte_slice()) + else { + continue; + }; + let Some(functions) = + artifact_filters.get_targeted_functions(artifact, &contract.abi)? + else { + continue; + }; + created_contracts.push(*address); + targets.insert(*address, (artifact.name.clone(), contract.abi.clone(), functions)); + } + } + Ok(()) + } + + /// Clears targeted contracts created during an invariant run. + pub fn clear_created_contracts(&self, created_contracts: Vec
) { + if !created_contracts.is_empty() { + let mut targets = self.targets.lock(); + for addr in created_contracts.iter() { + targets.remove(addr); + } + } + } +} + +/// Helper to retrieve functions to fuzz for specified abi. +/// Returns specified targeted functions if any, else mutable abi functions. +pub(crate) fn abi_fuzzed_functions( + abi: &JsonAbi, + targeted_functions: &[Function], +) -> Vec { + if !targeted_functions.is_empty() { + targeted_functions.to_vec() + } else { + abi.functions() + .filter(|&func| { + !matches!( + func.state_mutability, + alloy_json_abi::StateMutability::Pure | alloy_json_abi::StateMutability::View + ) + }) + .cloned() + .collect() + } } /// Details of a transaction generated by invariant strategy for fuzzing a target. diff --git a/crates/evm/fuzz/src/strategies/invariants.rs b/crates/evm/fuzz/src/strategies/invariants.rs index 1c143f922..b3bb21372 100644 --- a/crates/evm/fuzz/src/strategies/invariants.rs +++ b/crates/evm/fuzz/src/strategies/invariants.rs @@ -1,10 +1,13 @@ use super::{fuzz_calldata, fuzz_param_from_state}; use crate::{ - invariant::{BasicTxDetails, CallDetails, FuzzRunIdentifiedContracts, SenderFilters}, + invariant::{ + abi_fuzzed_functions, BasicTxDetails, CallDetails, FuzzRunIdentifiedContracts, + SenderFilters, + }, strategies::{fuzz_calldata_from_state, fuzz_param, EvmFuzzState}, FuzzFixtures, }; -use alloy_json_abi::{Function, JsonAbi}; +use alloy_json_abi::Function; use alloy_primitives::Address; use parking_lot::RwLock; use proptest::prelude::*; @@ -37,7 +40,8 @@ pub fn override_call_strat( let (_, contract_specs) = contracts.iter().nth(rand_index).unwrap(); contract_specs }); - select_random_function(abi, functions) + let fuzzed_functions = abi_fuzzed_functions(abi, functions); + any::().prop_map(move |index| index.get(&fuzzed_functions).clone()) }; func.prop_flat_map(move |func| { @@ -64,27 +68,18 @@ pub fn invariant_strat( fuzz_fixtures: FuzzFixtures, ) -> impl Strategy { let senders = Rc::new(senders); - any::() - .prop_flat_map(move |selector| { - let (contract, func) = { - let contracts = contracts.targets.lock(); - let contracts = - contracts.iter().filter(|(_, (_, abi, _))| !abi.functions.is_empty()); - let (&contract, (_, abi, functions)) = selector.select(contracts); - - let func = select_random_function(abi, functions); - (contract, func) - }; - - let senders = senders.clone(); - let fuzz_state = fuzz_state.clone(); - let fuzz_fixtures = fuzz_fixtures.clone(); - func.prop_flat_map(move |func| { - let sender = select_random_sender(&fuzz_state, senders.clone(), dictionary_weight); - let contract = - fuzz_contract_with_calldata(&fuzz_state, &fuzz_fixtures, contract, func); - (sender, contract) - }) + any::() + .prop_flat_map(move |index| { + let (target_address, target_function) = + index.get(&contracts.fuzzed_functions()).clone(); + let sender = select_random_sender(&fuzz_state, senders.clone(), dictionary_weight); + let call_details = fuzz_contract_with_calldata( + &fuzz_state, + &fuzz_fixtures, + target_address, + target_function, + ); + (sender, call_details) }) .prop_map(|(sender, call_details)| BasicTxDetails { sender, call_details }) } @@ -112,30 +107,6 @@ fn select_random_sender( } } -/// Strategy to select a random mutable function from the abi. -/// -/// If `targeted_functions` is not empty, select one from it. Otherwise, take any -/// of the available abi functions. -fn select_random_function( - abi: &JsonAbi, - targeted_functions: &[Function], -) -> impl Strategy { - let functions = if !targeted_functions.is_empty() { - targeted_functions.to_vec() - } else { - abi.functions() - .filter(|&func| { - !matches!( - func.state_mutability, - alloy_json_abi::StateMutability::Pure | alloy_json_abi::StateMutability::View - ) - }) - .cloned() - .collect() - }; - any::().prop_map(move |index| index.get(&functions).clone()) -} - /// Given a function, it returns a proptest strategy which generates valid abi-encoded calldata /// for that function's input types. pub fn fuzz_contract_with_calldata( diff --git a/crates/evm/fuzz/src/strategies/mod.rs b/crates/evm/fuzz/src/strategies/mod.rs index bf284591b..1d8e647a5 100644 --- a/crates/evm/fuzz/src/strategies/mod.rs +++ b/crates/evm/fuzz/src/strategies/mod.rs @@ -11,7 +11,7 @@ mod calldata; pub use calldata::{fuzz_calldata, fuzz_calldata_from_state}; mod state; -pub use state::{collect_created_contracts, EvmFuzzState}; +pub use state::EvmFuzzState; mod invariants; pub use invariants::{fuzz_contract_with_calldata, invariant_strat, override_call_strat}; diff --git a/crates/evm/fuzz/src/strategies/state.rs b/crates/evm/fuzz/src/strategies/state.rs index 39dd3a467..cf29c9420 100644 --- a/crates/evm/fuzz/src/strategies/state.rs +++ b/crates/evm/fuzz/src/strategies/state.rs @@ -1,8 +1,7 @@ -use crate::invariant::{ArtifactFilters, BasicTxDetails, FuzzRunIdentifiedContracts}; +use crate::invariant::{BasicTxDetails, FuzzRunIdentifiedContracts}; use alloy_dyn_abi::{DynSolType, DynSolValue, EventExt, FunctionExt}; use alloy_json_abi::{Function, JsonAbi}; use alloy_primitives::{Address, Bytes, Log, B256, U256}; -use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; use foundry_config::FuzzDictionaryConfig; use foundry_evm_core::utils::StateChangeset; use indexmap::IndexSet; @@ -381,43 +380,3 @@ impl FuzzDictionary { ); } } - -/// Collects all created contracts from a StateChangeset which haven't been discovered yet. Stores -/// them at `targeted_contracts` and `created_contracts`. -pub fn collect_created_contracts( - state_changeset: &StateChangeset, - project_contracts: &ContractsByArtifact, - setup_contracts: &ContractsByAddress, - artifact_filters: &ArtifactFilters, - targeted_contracts: &FuzzRunIdentifiedContracts, - created_contracts: &mut Vec
, -) -> eyre::Result<()> { - let mut writable_targeted = targeted_contracts.targets.lock(); - for (address, account) in state_changeset { - if setup_contracts.contains_key(address) { - continue; - } - if !account.is_touched() { - continue; - } - let Some(code) = &account.info.code else { - continue; - }; - if code.is_empty() { - continue; - } - let Some((artifact, contract)) = - project_contracts.find_by_deployed_code(code.original_byte_slice()) - else { - continue; - }; - let Some(functions) = artifact_filters.get_targeted_functions(artifact, &contract.abi)? - else { - continue; - }; - created_contracts.push(*address); - writable_targeted - .insert(*address, (artifact.name.clone(), contract.abi.clone(), functions)); - } - Ok(()) -} diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index f2a8d69f9..19500ab63 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -264,6 +264,10 @@ async fn test_invariant() { ), ("invariant_success()", true, None, None, None), ], + ), + ( + "default/fuzz/invariant/common/InvariantSelectorsWeight.t.sol:InvariantSelectorsWeightTest", + vec![("invariant_selectors_weight()", true, None, None, None)], ) ]), ); @@ -765,3 +769,26 @@ async fn test_invariant_after_invariant() { )]), ); } + +#[tokio::test(flavor = "multi_thread")] +async fn test_invariant_selectors_weight() { + let mut opts = TEST_DATA_DEFAULT.test_opts.clone(); + opts.fuzz.seed = Some(U256::from(119u32)); + + let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantSelectorsWeight.t.sol"); + let mut runner = TEST_DATA_DEFAULT.runner(); + runner.test_options = opts.clone(); + runner.test_options.invariant.runs = 1; + runner.test_options.invariant.depth = 30; + runner.test_options.invariant.failure_persist_dir = + Some(tempfile::tempdir().unwrap().into_path()); + + let results = runner.test_collect(&filter); + assert_multiple( + &results, + BTreeMap::from([( + "default/fuzz/invariant/common/InvariantSelectorsWeight.t.sol:InvariantSelectorsWeightTest", + vec![("invariant_selectors_weight()", true, None, None, None)], + )]), + ) +} diff --git a/testdata/default/fuzz/invariant/common/InvariantSelectorsWeight.t.sol b/testdata/default/fuzz/invariant/common/InvariantSelectorsWeight.t.sol new file mode 100644 index 000000000..918fd7b01 --- /dev/null +++ b/testdata/default/fuzz/invariant/common/InvariantSelectorsWeight.t.sol @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "ds-test/test.sol"; + +contract HandlerWithOneSelector { + uint256 public hit1; + + function selector1() external { + hit1 += 1; + } +} + +contract HandlerWithFiveSelectors { + uint256 public hit2; + uint256 public hit3; + uint256 public hit4; + uint256 public hit5; + uint256 public hit6; + + function selector2() external { + hit2 += 1; + } + + function selector3() external { + hit3 += 1; + } + + function selector4() external { + hit4 += 1; + } + + function selector5() external { + hit5 += 1; + } + + function selector6() external { + hit6 += 1; + } +} + +contract HandlerWithFourSelectors { + uint256 public hit7; + uint256 public hit8; + uint256 public hit9; + uint256 public hit10; + + function selector7() external { + hit7 += 1; + } + + function selector8() external { + hit8 += 1; + } + + function selector9() external { + hit9 += 1; + } + + function selector10() external { + hit10 += 1; + } +} + +contract InvariantSelectorsWeightTest is DSTest { + HandlerWithOneSelector handlerOne; + HandlerWithFiveSelectors handlerTwo; + HandlerWithFourSelectors handlerThree; + + function setUp() public { + handlerOne = new HandlerWithOneSelector(); + handlerTwo = new HandlerWithFiveSelectors(); + handlerThree = new HandlerWithFourSelectors(); + } + + function afterInvariant() public { + // selector hits before and after https://github.com/foundry-rs/foundry/issues/2986 + // hit1: 11 | hit2: 4 | hit3: 0 | hit4: 0 | hit5: 4 | hit6: 1 | hit7: 2 | hit8: 2 | hit9: 2 | hit10: 4 + // hit1: 2 | hit2: 5 | hit3: 4 | hit4: 5 | hit5: 3 | hit6: 1 | hit7: 4 | hit8: 1 | hit9: 1 | hit10: 4 + + uint256 hit1 = handlerOne.hit1(); + uint256 hit2 = handlerTwo.hit2(); + uint256 hit3 = handlerTwo.hit3(); + uint256 hit4 = handlerTwo.hit4(); + uint256 hit5 = handlerTwo.hit5(); + uint256 hit6 = handlerTwo.hit6(); + uint256 hit7 = handlerThree.hit7(); + uint256 hit8 = handlerThree.hit8(); + uint256 hit9 = handlerThree.hit9(); + uint256 hit10 = handlerThree.hit10(); + + require( + hit1 > 0 && hit2 > 0 && hit3 > 0 && hit4 > 0 && hit5 > 0 && hit6 > 0 && hit7 > 0 && hit8 > 0 && hit9 > 0 + && hit10 > 0 + ); + } + + /// forge-config: default.invariant.runs = 1 + /// forge-config: default.invariant.depth = 30 + function invariant_selectors_weight() public view {} +} From f6730662831d0473f7207f4c0474a4bb6ee016eb Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 17 Jun 2024 17:28:44 +0200 Subject: [PATCH 416/430] perf: reduce clones in fuzzed_functions (#8178) --- crates/evm/fuzz/src/invariant/mod.rs | 26 +++++++++----------- crates/evm/fuzz/src/strategies/invariants.rs | 2 +- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/crates/evm/fuzz/src/invariant/mod.rs b/crates/evm/fuzz/src/invariant/mod.rs index 82938f6cc..fdc0b9d56 100644 --- a/crates/evm/fuzz/src/invariant/mod.rs +++ b/crates/evm/fuzz/src/invariant/mod.rs @@ -1,5 +1,6 @@ use alloy_json_abi::{Function, JsonAbi}; use alloy_primitives::{Address, Bytes}; +use itertools::Either; use parking_lot::Mutex; use std::{collections::BTreeMap, sync::Arc}; @@ -116,22 +117,19 @@ impl FuzzRunIdentifiedContracts { /// Helper to retrieve functions to fuzz for specified abi. /// Returns specified targeted functions if any, else mutable abi functions. -pub(crate) fn abi_fuzzed_functions( - abi: &JsonAbi, - targeted_functions: &[Function], -) -> Vec { +pub(crate) fn abi_fuzzed_functions<'a>( + abi: &'a JsonAbi, + targeted_functions: &'a [Function], +) -> impl Iterator { if !targeted_functions.is_empty() { - targeted_functions.to_vec() + Either::Left(targeted_functions.iter()) } else { - abi.functions() - .filter(|&func| { - !matches!( - func.state_mutability, - alloy_json_abi::StateMutability::Pure | alloy_json_abi::StateMutability::View - ) - }) - .cloned() - .collect() + Either::Right(abi.functions().filter(|&func| { + !matches!( + func.state_mutability, + alloy_json_abi::StateMutability::Pure | alloy_json_abi::StateMutability::View + ) + })) } } diff --git a/crates/evm/fuzz/src/strategies/invariants.rs b/crates/evm/fuzz/src/strategies/invariants.rs index b3bb21372..55af574c5 100644 --- a/crates/evm/fuzz/src/strategies/invariants.rs +++ b/crates/evm/fuzz/src/strategies/invariants.rs @@ -40,7 +40,7 @@ pub fn override_call_strat( let (_, contract_specs) = contracts.iter().nth(rand_index).unwrap(); contract_specs }); - let fuzzed_functions = abi_fuzzed_functions(abi, functions); + let fuzzed_functions: Vec<_> = abi_fuzzed_functions(abi, functions).cloned().collect(); any::().prop_map(move |index| index.get(&fuzzed_functions).clone()) }; From 55ac4e4e918bb3cb17ff0b69d73c6a1190c80f78 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 17 Jun 2024 19:11:27 +0300 Subject: [PATCH 417/430] bump compilers (#8153) * [do not merge] patch compilers * fix import * fix doc * update patch * rm patch --- Cargo.lock | 85 ++++++++++++++++++++--- Cargo.toml | 4 +- crates/cast/bin/cmd/call.rs | 2 +- crates/cast/bin/cmd/run.rs | 2 +- crates/cast/bin/cmd/storage.rs | 9 ++- crates/chisel/benches/session_source.rs | 2 +- crates/chisel/src/executor.rs | 2 +- crates/chisel/src/session_source.rs | 23 +++--- crates/chisel/tests/cache.rs | 2 +- crates/cli/src/opts/build/core.rs | 6 +- crates/cli/src/opts/build/mod.rs | 2 +- crates/cli/src/opts/build/paths.rs | 2 +- crates/common/src/compile.rs | 11 +-- crates/common/src/term.rs | 2 +- crates/config/src/lib.rs | 26 +++---- crates/config/src/providers/remappings.rs | 2 +- crates/config/src/utils.rs | 2 +- crates/config/src/vyper.rs | 2 +- crates/debugger/src/tui/draw.rs | 4 +- crates/doc/src/builder.rs | 2 +- crates/evm/coverage/src/anchors.rs | 2 +- crates/evm/coverage/src/lib.rs | 2 +- crates/evm/evm/src/executors/tracing.rs | 2 +- crates/forge/bin/cmd/clone.rs | 14 ++-- crates/forge/bin/cmd/coverage.rs | 3 +- crates/forge/bin/cmd/flatten.rs | 19 ++--- crates/forge/bin/cmd/fmt.rs | 2 +- crates/forge/bin/cmd/init.rs | 2 +- crates/forge/tests/cli/cmd.rs | 2 +- crates/forge/tests/cli/config.rs | 4 +- crates/forge/tests/cli/create.rs | 2 +- crates/forge/tests/it/test_helpers.rs | 4 +- crates/verify/src/bytecode.rs | 4 +- crates/verify/src/etherscan/flatten.rs | 3 +- crates/verify/src/lib.rs | 2 +- crates/verify/src/provider.rs | 3 +- 36 files changed, 173 insertions(+), 89 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b7080d198..3622135a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3489,9 +3489,9 @@ dependencies = [ [[package]] name = "foundry-block-explorers" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35344cf275788b0450c4b36d452b812d1122d622bafb29887ce244b8099a86ad" +checksum = "328cd3498bd9a7615d3a2f2e0e94a5d2ffc1db54cb6b4e933d01f77272ff43e5" dependencies = [ "alloy-chains", "alloy-json-abi", @@ -3642,29 +3642,27 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81f9b10619d07d765a0336b1990ffcb1bb7b806a59b4d2e65cb78f5d77f373c5" +checksum = "e2ea36984de5126fe2b05efb22d04148c39f244de1cc285493e3c2a4cfcc843a" dependencies = [ "alloy-json-abi", "alloy-primitives", "auto_impl", - "cfg-if", "derivative", "dirs 5.0.1", - "dunce", "dyn-clone", + "foundry-compilers-artifacts", + "foundry-compilers-core", "fs_extra", "futures-util", "home", "itertools 0.13.0", "md-5", - "memmap2 0.9.4", "once_cell", "path-slash", "rand", "rayon", - "regex", "semver 1.0.23", "serde", "serde_json", @@ -3676,11 +3674,80 @@ dependencies = [ "thiserror", "tokio", "tracing", - "walkdir", "winnow 0.6.13", "yansi", ] +[[package]] +name = "foundry-compilers-artifacts" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5795278ec3d726eb4ec932ea452a05c880780fbb677aef06f5eeab3d0e2ae075" +dependencies = [ + "foundry-compilers-artifacts-solc", + "foundry-compilers-artifacts-vyper", +] + +[[package]] +name = "foundry-compilers-artifacts-solc" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f19355ab89cd2b782c6d791d7cb16b7733074a7945443668183e7c258d007a" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "foundry-compilers-core", + "futures-util", + "md-5", + "path-slash", + "rayon", + "semver 1.0.23", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", + "walkdir", + "yansi", +] + +[[package]] +name = "foundry-compilers-artifacts-vyper" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05564ba0e04eb361e264e68d5e7d5b69e272d7d378f22c5c6a2edbe6d1cc3b58" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "foundry-compilers-artifacts-solc", + "path-slash", + "serde", +] + +[[package]] +name = "foundry-compilers-core" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "208e1a34268fbfccea0a8d4c974db01bfa44dac370724907ad404ccb79455b64" +dependencies = [ + "alloy-primitives", + "cfg-if", + "dunce", + "fs_extra", + "memmap2 0.9.4", + "once_cell", + "path-slash", + "regex", + "semver 1.0.23", + "serde", + "serde_json", + "svm-rs", + "tempfile", + "thiserror", + "tokio", + "walkdir", +] + [[package]] name = "foundry-config" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 368d94521..c088c2573 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -153,8 +153,8 @@ foundry-wallets = { path = "crates/wallets" } foundry-linking = { path = "crates/linking" } # solc & compilation utilities -foundry-block-explorers = { version = "0.4.0", default-features = false } -foundry-compilers = { version = "0.7.0", default-features = false } +foundry-block-explorers = { version = "0.4.1", default-features = false } +foundry-compilers = { version = "0.8.0", default-features = false } ## revm # no default features to avoid c-kzg diff --git a/crates/cast/bin/cmd/call.rs b/crates/cast/bin/cmd/call.rs index 5f4975fcc..0499f5e7b 100644 --- a/crates/cast/bin/cmd/call.rs +++ b/crates/cast/bin/cmd/call.rs @@ -9,7 +9,7 @@ use foundry_cli::{ utils::{self, handle_traces, parse_ether_value, TraceResult}, }; use foundry_common::ens::NameOrAddress; -use foundry_compilers::EvmVersion; +use foundry_compilers::artifacts::EvmVersion; use foundry_config::{find_project_root_path, Config}; use foundry_evm::{executors::TracingExecutor, opts::EvmOpts}; use std::str::FromStr; diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index 451964045..c830f5ab1 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -9,7 +9,7 @@ use foundry_cli::{ utils::{handle_traces, init_progress, TraceResult}, }; use foundry_common::{is_known_system_sender, SYSTEM_TRANSACTION_TYPE}; -use foundry_compilers::EvmVersion; +use foundry_compilers::artifacts::EvmVersion; use foundry_config::{find_project_root_path, Config}; use foundry_evm::{ executors::{EvmError, TracingExecutor}, diff --git a/crates/cast/bin/cmd/storage.rs b/crates/cast/bin/cmd/storage.rs index aedf84471..117130943 100644 --- a/crates/cast/bin/cmd/storage.rs +++ b/crates/cast/bin/cmd/storage.rs @@ -19,9 +19,12 @@ use foundry_common::{ ens::NameOrAddress, }; use foundry_compilers::{ - artifacts::StorageLayout, - compilers::{solc::SolcCompiler, Compiler, CompilerSettings}, - Artifact, ConfigurableContractArtifact, Project, Solc, + artifacts::{ConfigurableContractArtifact, StorageLayout}, + compilers::{ + solc::{Solc, SolcCompiler}, + Compiler, CompilerSettings, + }, + Artifact, Project, }; use foundry_config::{ figment::{self, value::Dict, Metadata, Profile}, diff --git a/crates/chisel/benches/session_source.rs b/crates/chisel/benches/session_source.rs index 37f35f05e..f54944966 100644 --- a/crates/chisel/benches/session_source.rs +++ b/crates/chisel/benches/session_source.rs @@ -1,6 +1,6 @@ use chisel::session_source::{SessionSource, SessionSourceConfig}; use criterion::{criterion_group, Criterion}; -use foundry_compilers::Solc; +use foundry_compilers::solc::Solc; use once_cell::sync::Lazy; use semver::Version; use std::hint::black_box; diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index a177aa211..58059a0ac 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -1392,7 +1392,7 @@ impl<'a> Iterator for InstructionIter<'a> { #[cfg(test)] mod tests { use super::*; - use foundry_compilers::{error::SolcError, Solc}; + use foundry_compilers::{error::SolcError, solc::Solc}; use semver::Version; use std::sync::Mutex; diff --git a/crates/chisel/src/session_source.rs b/crates/chisel/src/session_source.rs index 500cd97ef..f83eefeae 100644 --- a/crates/chisel/src/session_source.rs +++ b/crates/chisel/src/session_source.rs @@ -7,8 +7,8 @@ use eyre::Result; use forge_fmt::solang_ext::SafeUnwrap; use foundry_compilers::{ - artifacts::{Settings, Source, Sources}, - CompilerOutput, Solc, SolcInput, + artifacts::{CompilerOutput, Settings, SolcInput, Source, Sources}, + compilers::solc::Solc, }; use foundry_config::{Config, SolcReq}; use foundry_evm::{backend::Backend, opts::EvmOpts}; @@ -115,16 +115,15 @@ impl SessionSourceConfig { } } - let solc = - if let Some(solc) = Solc::find_svm_installed_version(version.to_string())? { - solc - } else { - if self.foundry_config.offline { - eyre::bail!("can't install missing solc {version} in offline mode") - } - println!("{}", format!("Installing solidity version {version}...").green()); - Solc::blocking_install(&version)? - }; + let solc = if let Some(solc) = Solc::find_svm_installed_version(&version)? { + solc + } else { + if self.foundry_config.offline { + eyre::bail!("can't install missing solc {version} in offline mode") + } + println!("{}", format!("Installing solidity version {version}...").green()); + Solc::blocking_install(&version)? + }; Ok(solc) } SolcReq::Local(solc) => { diff --git a/crates/chisel/tests/cache.rs b/crates/chisel/tests/cache.rs index 7be980ec8..5f0864bee 100644 --- a/crates/chisel/tests/cache.rs +++ b/crates/chisel/tests/cache.rs @@ -1,5 +1,5 @@ use chisel::session::ChiselSession; -use foundry_compilers::EvmVersion; +use foundry_compilers::artifacts::EvmVersion; use foundry_config::{Config, SolcReq}; use semver::Version; use serial_test::serial; diff --git a/crates/cli/src/opts/build/core.rs b/crates/cli/src/opts/build/core.rs index 5f27afaa6..a6d9d9911 100644 --- a/crates/cli/src/opts/build/core.rs +++ b/crates/cli/src/opts/build/core.rs @@ -3,8 +3,10 @@ use crate::{opts::CompilerArgs, utils::LoadConfig}; use clap::{Parser, ValueHint}; use eyre::Result; use foundry_compilers::{ - artifacts::RevertStrings, compilers::multi::MultiCompiler, remappings::Remapping, - utils::canonicalized, Project, + artifacts::{remappings::Remapping, RevertStrings}, + compilers::multi::MultiCompiler, + utils::canonicalized, + Project, }; use foundry_config::{ figment, diff --git a/crates/cli/src/opts/build/mod.rs b/crates/cli/src/opts/build/mod.rs index 2ed2ea463..4ffd1c3bf 100644 --- a/crates/cli/src/opts/build/mod.rs +++ b/crates/cli/src/opts/build/mod.rs @@ -1,5 +1,5 @@ use clap::Parser; -use foundry_compilers::{artifacts::output_selection::ContractOutputSelection, EvmVersion}; +use foundry_compilers::artifacts::{output_selection::ContractOutputSelection, EvmVersion}; use serde::Serialize; mod core; diff --git a/crates/cli/src/opts/build/paths.rs b/crates/cli/src/opts/build/paths.rs index 9ec2dc001..9553eedb0 100644 --- a/crates/cli/src/opts/build/paths.rs +++ b/crates/cli/src/opts/build/paths.rs @@ -1,6 +1,6 @@ use clap::{Parser, ValueHint}; use eyre::Result; -use foundry_compilers::remappings::Remapping; +use foundry_compilers::artifacts::remappings::Remapping; use foundry_config::{ figment, figment::{ diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index 3488e6482..ffefe141a 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -5,11 +5,14 @@ use comfy_table::{presets::ASCII_MARKDOWN, Attribute, Cell, CellAlignment, Color use eyre::{Context, Result}; use foundry_block_explorers::contract::Metadata; use foundry_compilers::{ - artifacts::{BytecodeObject, ContractBytecodeSome, Libraries, Source}, - compilers::{multi::MultiCompilerLanguage, solc::SolcCompiler, Compiler}, - remappings::Remapping, + artifacts::{remappings::Remapping, BytecodeObject, ContractBytecodeSome, Libraries, Source}, + compilers::{ + multi::MultiCompilerLanguage, + solc::{Solc, SolcCompiler}, + Compiler, + }, report::{BasicStdoutReporter, NoReporter, Report}, - Artifact, Project, ProjectBuilder, ProjectCompileOutput, ProjectPathsConfig, Solc, SolcConfig, + Artifact, Project, ProjectBuilder, ProjectCompileOutput, ProjectPathsConfig, SolcConfig, }; use foundry_linking::Linker; use num_format::{Locale, ToFormattedString}; diff --git a/crates/common/src/term.rs b/crates/common/src/term.rs index 3fcc8f7fb..925456e4c 100644 --- a/crates/common/src/term.rs +++ b/crates/common/src/term.rs @@ -1,6 +1,6 @@ //! terminal utils use foundry_compilers::{ - remappings::Remapping, + artifacts::remappings::Remapping, report::{self, BasicStdoutReporter, Reporter}, }; use once_cell::sync::Lazy; diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 463ea82aa..e9396e3a4 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -19,20 +19,20 @@ use figment::{ use foundry_compilers::{ artifacts::{ output_selection::{ContractOutputSelection, OutputSelection}, - serde_helpers, BytecodeHash, DebuggingSettings, Libraries, ModelCheckerSettings, - ModelCheckerTarget, Optimizer, OptimizerDetails, RevertStrings, Settings, SettingsMetadata, - Severity, + remappings::{RelativeRemapping, Remapping}, + serde_helpers, BytecodeHash, DebuggingSettings, EvmVersion, Libraries, + ModelCheckerSettings, ModelCheckerTarget, Optimizer, OptimizerDetails, RevertStrings, + Settings, SettingsMetadata, Severity, }, cache::SOLIDITY_FILES_CACHE_FILENAME, compilers::{ multi::{MultiCompiler, MultiCompilerSettings}, - solc::SolcCompiler, + solc::{Solc, SolcCompiler}, vyper::{Vyper, VyperSettings}, Compiler, }, error::SolcError, - remappings::{RelativeRemapping, Remapping}, - ConfigurableArtifacts, EvmVersion, Project, ProjectPathsConfig, Solc, + ConfigurableArtifacts, Project, ProjectPathsConfig, }; use inflector::Inflector; use regex::Regex; @@ -851,7 +851,7 @@ impl Config { if let Some(ref solc) = self.solc { let solc = match solc { SolcReq::Version(version) => { - if let Some(solc) = Solc::find_svm_installed_version(version.to_string())? { + if let Some(solc) = Solc::find_svm_installed_version(version)? { solc } else { if self.offline { @@ -913,12 +913,12 @@ impl Config { /// # Example /// /// ``` - /// use foundry_compilers::Solc; + /// use foundry_compilers::solc::Solc; /// use foundry_config::Config; /// let config = Config::load_with_root(".").sanitized(); /// let paths = config.project_paths::(); /// ``` - pub fn project_paths(&self) -> ProjectPathsConfig { + pub fn project_paths(&self) -> ProjectPathsConfig { let mut builder = ProjectPathsConfig::builder() .cache(self.cache_path.join(SOLIDITY_FILES_CACHE_FILENAME)) .sources(&self.src) @@ -1240,7 +1240,8 @@ impl Config { /// Returns all libraries with applied remappings. Same as `self.solc_settings()?.libraries`. pub fn libraries_with_remappings(&self) -> Result { - Ok(self.parsed_libraries()?.with_applied_remappings(&self.project_paths())) + let paths: ProjectPathsConfig = self.project_paths(); + Ok(self.parsed_libraries()?.apply(|libs| paths.apply_lib_remappings(libs))) } /// Returns the configured `solc` `Settings` that includes: @@ -2816,9 +2817,8 @@ mod tests { etherscan::ResolvedEtherscanConfigs, }; use figment::error::Kind::InvalidType; - use foundry_compilers::{ - artifacts::{ModelCheckerEngine, YulDetails}, - compilers::vyper::settings::VyperOptimizationMode, + use foundry_compilers::artifacts::{ + vyper::VyperOptimizationMode, ModelCheckerEngine, YulDetails, }; use similar_asserts::assert_eq; use std::{collections::BTreeMap, fs::File, io::Write}; diff --git a/crates/config/src/providers/remappings.rs b/crates/config/src/providers/remappings.rs index 2bfbf4ca4..41b3fb80d 100644 --- a/crates/config/src/providers/remappings.rs +++ b/crates/config/src/providers/remappings.rs @@ -3,7 +3,7 @@ use figment::{ value::{Dict, Map}, Error, Metadata, Profile, Provider, }; -use foundry_compilers::remappings::{RelativeRemapping, Remapping}; +use foundry_compilers::artifacts::remappings::{RelativeRemapping, Remapping}; use std::{ borrow::Cow, collections::{btree_map::Entry, BTreeMap, HashSet}, diff --git a/crates/config/src/utils.rs b/crates/config/src/utils.rs index cb2350008..17af4789d 100644 --- a/crates/config/src/utils.rs +++ b/crates/config/src/utils.rs @@ -4,7 +4,7 @@ use crate::Config; use alloy_primitives::U256; use eyre::WrapErr; use figment::value::Value; -use foundry_compilers::{ +use foundry_compilers::artifacts::{ remappings::{Remapping, RemappingError}, EvmVersion, }; diff --git a/crates/config/src/vyper.rs b/crates/config/src/vyper.rs index 79d3fc01d..2af46b4b6 100644 --- a/crates/config/src/vyper.rs +++ b/crates/config/src/vyper.rs @@ -1,6 +1,6 @@ //! Vyper specific configuration types. -use foundry_compilers::compilers::vyper::settings::VyperOptimizationMode; +use foundry_compilers::artifacts::vyper::VyperOptimizationMode; use serde::{Deserialize, Serialize}; use std::path::PathBuf; diff --git a/crates/debugger/src/tui/draw.rs b/crates/debugger/src/tui/draw.rs index 06056034c..7ac0c64e5 100644 --- a/crates/debugger/src/tui/draw.rs +++ b/crates/debugger/src/tui/draw.rs @@ -3,7 +3,9 @@ use super::context::{BufferKind, DebuggerContext}; use crate::op::OpcodeParam; use alloy_primitives::U256; -use foundry_compilers::{compilers::multi::MultiCompilerLanguage, sourcemap::SourceElement}; +use foundry_compilers::{ + artifacts::sourcemap::SourceElement, compilers::multi::MultiCompilerLanguage, +}; use ratatui::{ layout::{Alignment, Constraint, Direction, Layout, Rect}, style::{Color, Modifier, Style}, diff --git a/crates/doc/src/builder.rs b/crates/doc/src/builder.rs index 3c8270a3a..e21e80c22 100644 --- a/crates/doc/src/builder.rs +++ b/crates/doc/src/builder.rs @@ -3,7 +3,7 @@ use crate::{ ParseSource, Parser, Preprocessor, }; use forge_fmt::{FormatterConfig, Visitable}; -use foundry_compilers::{utils::source_files_iter, SOLC_EXTENSIONS}; +use foundry_compilers::{compilers::solc::SOLC_EXTENSIONS, utils::source_files_iter}; use foundry_config::{filter::expand_globs, DocConfig}; use itertools::Itertools; use mdbook::MDBook; diff --git a/crates/evm/coverage/src/anchors.rs b/crates/evm/coverage/src/anchors.rs index a31ef0237..ad4ad744d 100644 --- a/crates/evm/coverage/src/anchors.rs +++ b/crates/evm/coverage/src/anchors.rs @@ -1,6 +1,6 @@ use super::{CoverageItem, CoverageItemKind, ItemAnchor, SourceLocation}; use eyre::ensure; -use foundry_compilers::sourcemap::{SourceElement, SourceMap}; +use foundry_compilers::artifacts::sourcemap::{SourceElement, SourceMap}; use foundry_evm_core::utils::IcPcMap; use revm::interpreter::opcode; use rustc_hash::{FxHashMap, FxHashSet}; diff --git a/crates/evm/coverage/src/lib.rs b/crates/evm/coverage/src/lib.rs index 15588296f..22f4b9808 100644 --- a/crates/evm/coverage/src/lib.rs +++ b/crates/evm/coverage/src/lib.rs @@ -9,7 +9,7 @@ extern crate tracing; use alloy_primitives::{Bytes, B256}; -use foundry_compilers::sourcemap::SourceMap; +use foundry_compilers::artifacts::sourcemap::SourceMap; use semver::Version; use std::{ collections::{BTreeMap, HashMap}, diff --git a/crates/evm/evm/src/executors/tracing.rs b/crates/evm/evm/src/executors/tracing.rs index a175aecb6..08c5d92ef 100644 --- a/crates/evm/evm/src/executors/tracing.rs +++ b/crates/evm/evm/src/executors/tracing.rs @@ -1,5 +1,5 @@ use crate::executors::{Executor, ExecutorBuilder}; -use foundry_compilers::EvmVersion; +use foundry_compilers::artifacts::EvmVersion; use foundry_config::{utils::evm_spec_id, Chain, Config}; use foundry_evm_core::{backend::Backend, fork::CreateFork, opts::EvmOpts}; use revm::primitives::{Env, SpecId}; diff --git a/crates/forge/bin/cmd/clone.rs b/crates/forge/bin/cmd/clone.rs index 822f595fb..97f6383e3 100644 --- a/crates/forge/bin/cmd/clone.rs +++ b/crates/forge/bin/cmd/clone.rs @@ -10,9 +10,13 @@ use foundry_block_explorers::{ use foundry_cli::{opts::EtherscanOpts, p_println, utils::Git}; use foundry_common::{compile::ProjectCompiler, fs}; use foundry_compilers::{ - artifacts::{output_selection::ContractOutputSelection, Settings, StorageLayout}, - remappings::{RelativeRemapping, Remapping}, - ConfigurableContractArtifact, ProjectCompileOutput, ProjectPathsConfig, Solc, + artifacts::{ + output_selection::ContractOutputSelection, + remappings::{RelativeRemapping, Remapping}, + ConfigurableContractArtifact, Settings, StorageLayout, + }, + compilers::solc::Solc, + ProjectCompileOutput, ProjectPathsConfig, }; use foundry_config::{Chain, Config}; use std::{ @@ -388,9 +392,9 @@ fn update_config_by_metadata( } // apply remapping on libraries - let path_config = config.project_paths(); + let path_config: ProjectPathsConfig = config.project_paths(); let libraries = libraries - .with_applied_remappings(&path_config) + .apply(|libs| path_config.apply_lib_remappings(libs)) .with_stripped_file_prefixes(&path_config.root); // update libraries diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index b6a549742..dc9b25c14 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -19,8 +19,7 @@ use foundry_cli::{ }; use foundry_common::{compile::ProjectCompiler, fs}; use foundry_compilers::{ - artifacts::{CompactBytecode, CompactDeployedBytecode}, - sourcemap::SourceMap, + artifacts::{sourcemap::SourceMap, CompactBytecode, CompactDeployedBytecode}, Artifact, ArtifactId, Project, ProjectCompileOutput, }; use foundry_config::{Config, SolcReq}; diff --git a/crates/forge/bin/cmd/flatten.rs b/crates/forge/bin/cmd/flatten.rs index 3cd5abb68..64c4ea78f 100644 --- a/crates/forge/bin/cmd/flatten.rs +++ b/crates/forge/bin/cmd/flatten.rs @@ -4,8 +4,12 @@ use foundry_cli::{ opts::{CoreBuildArgs, ProjectPathsArgs}, utils::LoadConfig, }; -use foundry_common::{compile::compile_target, fs}; -use foundry_compilers::{compilers::solc::SolcLanguage, error::SolcError, flatten::Flattener}; +use foundry_common::fs; +use foundry_compilers::{ + compilers::solc::SolcLanguage, + error::SolcError, + flatten::{Flattener, FlattenerError}, +}; use std::path::PathBuf; /// CLI arguments for `forge flatten`. @@ -42,18 +46,17 @@ impl FlattenArgs { let project = config.create_project(false, true)?; let target_path = dunce::canonicalize(target_path)?; - let compiler_output = compile_target(&target_path, &project, false); + let flattener = Flattener::new(project.clone(), &target_path); - let flattened = match compiler_output { - Ok(compiler_output) => { - Flattener::new(&project, &compiler_output, &target_path).map(|f| f.flatten()) - } - Err(_) => { + let flattened = match flattener { + Ok(flattener) => Ok(flattener.flatten()), + Err(FlattenerError::Compilation(_)) => { // Fallback to the old flattening implementation if we couldn't compile the target // successfully. This would be the case if the target has invalid // syntax. (e.g. Solang) project.paths.clone().with_language::().flatten(&target_path) } + Err(FlattenerError::Other(err)) => Err(err), } .map_err(|err: SolcError| eyre::eyre!("Failed to flatten: {err}"))?; diff --git a/crates/forge/bin/cmd/fmt.rs b/crates/forge/bin/cmd/fmt.rs index 55ba71780..bcfae7769 100644 --- a/crates/forge/bin/cmd/fmt.rs +++ b/crates/forge/bin/cmd/fmt.rs @@ -3,7 +3,7 @@ use eyre::{Context, Result}; use forge_fmt::{format_to, parse}; use foundry_cli::utils::{FoundryPathExt, LoadConfig}; use foundry_common::{fs, term::cli_warn}; -use foundry_compilers::{compilers::solc::SolcLanguage, SOLC_EXTENSIONS}; +use foundry_compilers::{compilers::solc::SolcLanguage, solc::SOLC_EXTENSIONS}; use foundry_config::{filter::expand_globs, impl_figment_convert_basic}; use rayon::prelude::*; use similar::{ChangeTag, TextDiff}; diff --git a/crates/forge/bin/cmd/init.rs b/crates/forge/bin/cmd/init.rs index 5c2b56ea8..f19bc1de2 100644 --- a/crates/forge/bin/cmd/init.rs +++ b/crates/forge/bin/cmd/init.rs @@ -3,7 +3,7 @@ use clap::{Parser, ValueHint}; use eyre::Result; use foundry_cli::{p_println, utils::Git}; use foundry_common::fs; -use foundry_compilers::remappings::Remapping; +use foundry_compilers::artifacts::remappings::Remapping; use foundry_config::Config; use std::path::{Path, PathBuf}; use yansi::Paint; diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 1c851dd09..016ac29f0 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -1,7 +1,7 @@ //! Contains various tests for checking forge's commands use crate::constants::*; -use foundry_compilers::{artifacts::Metadata, remappings::Remapping, ConfigurableContractArtifact}; +use foundry_compilers::artifacts::{remappings::Remapping, ConfigurableContractArtifact, Metadata}; use foundry_config::{ parse_with_profile, BasicConfig, Chain, Config, FuzzConfig, InvariantConfig, SolidityErrorCode, }; diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index bd5598470..c7b3abd2e 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -4,7 +4,7 @@ use alloy_primitives::{Address, B256, U256}; use foundry_cli::utils as forge_utils; use foundry_compilers::{ artifacts::{BytecodeHash, OptimizerDetails, RevertStrings, YulDetails}, - Solc, + solc::Solc, }; use foundry_config::{ cache::{CachedChains, CachedEndpoints, StorageCachingConfig}, @@ -13,7 +13,7 @@ use foundry_config::{ }; use foundry_evm::opts::EvmOpts; use foundry_test_utils::{ - foundry_compilers::{remappings::Remapping, EvmVersion}, + foundry_compilers::artifacts::{remappings::Remapping, EvmVersion}, util::{pretty_err, OutputExt, TestCommand, OTHER_SOLC_VERSION}, }; use path_slash::PathBufExt; diff --git a/crates/forge/tests/cli/create.rs b/crates/forge/tests/cli/create.rs index c9484db0f..0df24f88b 100644 --- a/crates/forge/tests/cli/create.rs +++ b/crates/forge/tests/cli/create.rs @@ -6,7 +6,7 @@ use crate::{ }; use alloy_primitives::Address; use anvil::{spawn, NodeConfig}; -use foundry_compilers::{artifacts::BytecodeHash, remappings::Remapping}; +use foundry_compilers::artifacts::{remappings::Remapping, BytecodeHash}; use foundry_config::Config; use foundry_test_utils::{ forgetest, forgetest_async, diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index d9f5298b1..3bf2ae327 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -6,8 +6,8 @@ use forge::{ TestOptionsBuilder, }; use foundry_compilers::{ - artifacts::{Libraries, Settings}, - EvmVersion, Project, ProjectCompileOutput, SolcConfig, + artifacts::{EvmVersion, Libraries, Settings}, + Project, ProjectCompileOutput, SolcConfig, }; use foundry_config::{ fs_permissions::PathPermission, Config, FsPermissions, FuzzConfig, FuzzDictionaryConfig, diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index 159233348..44b1f5542 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -10,9 +10,9 @@ use foundry_cli::{ }; use foundry_common::{compile::ProjectCompiler, provider::ProviderBuilder}; use foundry_compilers::{ - artifacts::{BytecodeHash, BytecodeObject, CompactContractBytecode}, + artifacts::{BytecodeHash, BytecodeObject, CompactContractBytecode, EvmVersion}, info::ContractInfo, - Artifact, EvmVersion, + Artifact, }; use foundry_config::{figment, filter::SkipBuildFilter, impl_figment_convert, Chain, Config}; use foundry_evm::{ diff --git a/crates/verify/src/etherscan/flatten.rs b/crates/verify/src/etherscan/flatten.rs index ac586b48c..92c0083e6 100644 --- a/crates/verify/src/etherscan/flatten.rs +++ b/crates/verify/src/etherscan/flatten.rs @@ -9,7 +9,8 @@ use foundry_compilers::{ solc::{SolcCompiler, SolcLanguage, SolcVersionedInput}, Compiler, CompilerInput, }, - AggregatedCompilerOutput, Solc, + solc::Solc, + AggregatedCompilerOutput, }; use semver::{BuildMetadata, Version}; use std::{collections::BTreeMap, path::Path}; diff --git a/crates/verify/src/lib.rs b/crates/verify/src/lib.rs index d3edcf62f..5edda5b08 100644 --- a/crates/verify/src/lib.rs +++ b/crates/verify/src/lib.rs @@ -17,7 +17,7 @@ use foundry_cli::{ utils::{self, LoadConfig}, }; use foundry_common::{compile::ProjectCompiler, ContractsByArtifact}; -use foundry_compilers::{info::ContractInfo, EvmVersion, Solc}; +use foundry_compilers::{artifacts::EvmVersion, compilers::solc::Solc, info::ContractInfo}; use foundry_config::{figment, impl_figment_convert, impl_figment_convert_cast, Config, SolcReq}; use itertools::Itertools; use provider::VerificationProviderType; diff --git a/crates/verify/src/provider.rs b/crates/verify/src/provider.rs index 7d29f6146..12dfb23f6 100644 --- a/crates/verify/src/provider.rs +++ b/crates/verify/src/provider.rs @@ -9,7 +9,8 @@ use foundry_common::compile::ProjectCompiler; use foundry_compilers::{ artifacts::{output_selection::OutputSelection, Metadata, Source}, compilers::{multi::MultiCompilerParsedSource, solc::SolcCompiler, CompilerSettings}, - Graph, Project, Solc, + solc::Solc, + Graph, Project, }; use foundry_config::Config; use semver::Version; From 35356b032ee8dfb8dc4a797ff06419ed56a4e980 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 17 Jun 2024 20:41:43 +0300 Subject: [PATCH 418/430] fix: make `paths` a positional argument (#8158) * fix: move paths to BuildArgs * tests * dirs -> paths --- crates/cli/src/opts/build/core.rs | 5 ----- crates/forge/bin/cmd/build.rs | 11 ++++++++--- crates/forge/tests/cli/cmd.rs | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/crates/cli/src/opts/build/core.rs b/crates/cli/src/opts/build/core.rs index a6d9d9911..81fbdf4cb 100644 --- a/crates/cli/src/opts/build/core.rs +++ b/crates/cli/src/opts/build/core.rs @@ -128,11 +128,6 @@ pub struct CoreBuildArgs { #[serde(skip)] pub skip: Option>, - /// Build source files from specified paths. - #[arg(long, short, num_args(0..))] - #[serde(skip)] - pub paths: Option>, - #[command(flatten)] #[serde(flatten)] pub compiler: CompilerArgs, diff --git a/crates/forge/bin/cmd/build.rs b/crates/forge/bin/cmd/build.rs index 82d5c834e..8e7f7c9a3 100644 --- a/crates/forge/bin/cmd/build.rs +++ b/crates/forge/bin/cmd/build.rs @@ -18,6 +18,7 @@ use foundry_config::{ Config, }; use serde::Serialize; +use std::path::PathBuf; use watchexec::config::{InitConfig, RuntimeConfig}; foundry_config::merge_impl_figment_convert!(BuildArgs, args); @@ -46,6 +47,10 @@ foundry_config::merge_impl_figment_convert!(BuildArgs, args); #[derive(Clone, Debug, Default, Serialize, Parser)] #[command(next_help_heading = "Build options", about = None, long_about = None)] // override doc pub struct BuildArgs { + /// Build source files from specified paths. + #[serde(skip)] + pub paths: Option>, + /// Print compiled contract names. #[arg(long)] #[serde(skip)] @@ -86,9 +91,9 @@ impl BuildArgs { // Collect sources to compile if build subdirectories specified. let mut files = vec![]; - if let Some(dirs) = self.args.paths { - for dir in dirs { - files.extend(source_files_iter(dir, MultiCompilerLanguage::FILE_EXTENSIONS)); + if let Some(paths) = self.paths { + for path in paths { + files.extend(source_files_iter(path, MultiCompilerLanguage::FILE_EXTENSIONS)); } } diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 016ac29f0..65f5f6ae8 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -1696,7 +1696,7 @@ function test_bar() external {} // Build 2 files within test dir prj.clear(); - cmd.args(["build", "--paths", "test", "--force"]); + cmd.args(["build", "test", "--force"]); cmd.unchecked_output().stdout_matches_path( PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("tests/fixtures/can_build_path_with_two_files.stdout"), @@ -1705,7 +1705,7 @@ function test_bar() external {} // Build one file within src dir prj.clear(); cmd.forge_fuse(); - cmd.args(["build", "--paths", "src", "--force"]); + cmd.args(["build", "src", "--force"]); cmd.unchecked_output().stdout_matches_path( PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("tests/fixtures/can_build_path_with_one_file.stdout"), @@ -1714,7 +1714,7 @@ function test_bar() external {} // Build 3 files from test and src dirs prj.clear(); cmd.forge_fuse(); - cmd.args(["build", "--paths", "src", "test", "--force"]); + cmd.args(["build", "src", "test", "--force"]); cmd.unchecked_output().stdout_matches_path( PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("tests/fixtures/can_build_path_with_three_files.stdout"), @@ -1723,7 +1723,7 @@ function test_bar() external {} // Build single test file prj.clear(); cmd.forge_fuse(); - cmd.args(["build", "--paths", "test/Bar.sol", "--force"]); + cmd.args(["build", "test/Bar.sol", "--force"]); cmd.unchecked_output().stdout_matches_path( PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("tests/fixtures/can_build_path_with_one_file.stdout"), From c8f771a05157c27dbd7397f46fa347d6c1abb752 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Tue, 18 Jun 2024 12:36:29 +0530 Subject: [PATCH 419/430] chore(deps): pin alloy 0.1.1 (#8182) * chore(deps): pin alloy 0.1.1 * bump revm and apply patch --- Cargo.lock | 273 +++++++++++++++++++++++------------ Cargo.toml | 54 +++---- crates/cheatcodes/src/lib.rs | 2 +- 3 files changed, 212 insertions(+), 117 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3622135a9..aead342de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,8 +79,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cc7579e4fb5558af44810f542c90d1145dba8b92c08211c215196160c48d2ea" dependencies = [ "alloy-eips", "alloy-primitives", @@ -92,8 +93,9 @@ dependencies = [ [[package]] name = "alloy-contract" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "860887f0f7e1e17db33ada75c3c516164a5e11aa89f0311f4d23b82abcf2d807" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -132,8 +134,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bdbc8d98cc36ebe17bb5b42d0873137bc76628a4ee0f7e7acad5b8fc59d3597" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -147,8 +150,9 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e10a047066076b32d52b3228e95a4f7793db7a204f648aa1a1ea675085bffd8" dependencies = [ "alloy-primitives", "alloy-serde", @@ -170,8 +174,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d06d33b79246313c4103ef9596c721674a926f1ddc8b605aa2bac4d8ba94ee34" dependencies = [ "alloy-primitives", "serde", @@ -182,8 +187,9 @@ dependencies = [ [[package]] name = "alloy-network" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef742b478a2db5c27063cde82128dfbecffcd38237d7f682a91d3ecf6aa1836c" dependencies = [ "alloy-consensus", "alloy-eips", @@ -228,8 +234,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200b786259a17acf318b9c423afe9669bec24ce9cdf59de153ff9a4009914bb6" dependencies = [ "alloy-chains", "alloy-consensus", @@ -254,7 +261,7 @@ dependencies = [ "futures-utils-wasm", "lru", "pin-project 1.1.5", - "reqwest 0.12.4", + "reqwest 0.12.5", "serde", "serde_json", "tokio", @@ -264,8 +271,9 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e6e6c1eab938a18a8e88d430cc9d548edf54c850a550873888285c85428eca" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -304,8 +312,9 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "328a6a14aba6152ddf6d01bac5e17a70dbe9d6f343bf402b995c30bac63a1fbf" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -316,7 +325,7 @@ dependencies = [ "alloy-transport-ws", "futures", "pin-project 1.1.5", - "reqwest 0.12.4", + "reqwest 0.12.5", "serde", "serde_json", "tokio", @@ -328,8 +337,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3164e7d8a718a22ede70b2c1d2bb554a8b4bd8e56c07ab630b75c74c06c53752" dependencies = [ "alloy-rpc-types-eth", "alloy-rpc-types-trace", @@ -339,8 +349,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90c3de574f90d9b939e3ee666a74bea29fb1a2ae66f1569b111bb6a922b1c762" dependencies = [ "alloy-consensus", "alloy-eips", @@ -356,8 +367,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bce0676f144be1eae71122d1d417885a3b063add0353b35e46cdf1440d6b33b1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -373,8 +385,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a39c52613dc4d9995ff284b496158594ae63f9bfc58b5ef04e48ec5da2e3d747" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -385,8 +398,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-txpool" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aeb7995e8859f3931b6199e13a533c9fde89affa900addb7218db2f15f9687d" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -396,8 +410,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c224916316519558d8c2b6a60dc7626688c08f1b8951774702562dbcb8666ee" dependencies = [ "alloy-primitives", "serde", @@ -406,8 +421,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "227c5fd0ed6e06e1ccc30593f8ff6d9fb907ac5f03a709a6d687f0943494a229" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -421,8 +437,9 @@ dependencies = [ [[package]] name = "alloy-signer-aws" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "723009d673de375a1f2d5fe4a1b32fea144001578db54b2dd5c817eaa9f09c25" dependencies = [ "alloy-consensus", "alloy-network", @@ -438,8 +455,9 @@ dependencies = [ [[package]] name = "alloy-signer-gcp" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97eae91202844e3cde7281d8343fd848bbae8bd53cf3f92450a66ba989c12b34" dependencies = [ "alloy-consensus", "alloy-network", @@ -455,8 +473,9 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d3a65e52c3c1848510d69e73e716139839f701134465bf44b77a7e43c1362f6" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -474,8 +493,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66c44057ac1e8707f8c6a983db9f83ac1265c9e05be81d432acf2aad2880e1c0" dependencies = [ "alloy-consensus", "alloy-network", @@ -493,8 +513,9 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85f68408899f493c3fd432e680f7ab586a38b9d2c8c117190855157dac70d9c4" dependencies = [ "alloy-consensus", "alloy-network", @@ -581,8 +602,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3628d81530263fe837a09cd527022f5728202a669973f04270942f4d390b5f5" dependencies = [ "alloy-json-rpc", "base64 0.22.1", @@ -598,12 +620,13 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f35d34e7a51503c9ff267404a5850bd58f991b7ab524b892f364901e3576376" dependencies = [ "alloy-json-rpc", "alloy-transport", - "reqwest 0.12.4", + "reqwest 0.12.5", "serde_json", "tower", "tracing", @@ -612,8 +635,9 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7d2f106151a583f7d258fe8cc846c5196da90a9f502d4b3516c56d63e1f25a2" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -632,8 +656,9 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=6cb3713#6cb3713a14c047cd5fcf17b99d27055341c41aaf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20a80da44d3709c4ceaf47745ad820eae8f121404b9ffd8e285522ac4eb06681" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -642,7 +667,7 @@ dependencies = [ "rustls 0.23.10", "serde_json", "tokio", - "tokio-tungstenite 0.23.0", + "tokio-tungstenite 0.23.1", "tracing", "ws_stream_wasm", ] @@ -1978,7 +2003,7 @@ dependencies = [ "foundry-evm", "once_cell", "regex", - "reqwest 0.12.4", + "reqwest 0.12.5", "revm", "rustyline", "semver 1.0.23", @@ -3318,7 +3343,7 @@ dependencies = [ "proptest", "rayon", "regex", - "reqwest 0.12.4", + "reqwest 0.12.5", "revm-inspectors", "rustc-hash", "semver 1.0.23", @@ -3467,7 +3492,7 @@ dependencies = [ "itertools 0.13.0", "once_cell", "regex", - "reqwest 0.12.4", + "reqwest 0.12.5", "revm-primitives", "semver 1.0.23", "serde", @@ -3497,7 +3522,7 @@ dependencies = [ "alloy-json-abi", "alloy-primitives", "foundry-compilers", - "reqwest 0.12.4", + "reqwest 0.12.5", "semver 1.0.23", "serde", "serde_json", @@ -3625,7 +3650,7 @@ dependencies = [ "foundry-macros", "num-format", "once_cell", - "reqwest 0.12.4", + "reqwest 0.12.5", "rustc-hash", "semver 1.0.23", "serde", @@ -3767,7 +3792,7 @@ dependencies = [ "once_cell", "path-slash", "regex", - "reqwest 0.12.4", + "reqwest 0.12.5", "revm-primitives", "semver 1.0.23", "serde", @@ -4665,9 +4690,9 @@ checksum = "08a397c49fec283e3d6211adbe480be95aae5f304cfb923e9970e08956d5168a" [[package]] name = "httparse" -version = "1.9.3" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0e7a4dd27b9476dc40cb050d3632d3bba3a70ddbff012285f7f8559a1e7e545" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "httpdate" @@ -4753,19 +4778,21 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.26.0" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" +checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" dependencies = [ "futures-util", "http 1.1.0", "hyper 1.3.1", "hyper-util", - "rustls 0.22.4", + "rustls 0.23.10", + "rustls-native-certs 0.7.0", "rustls-pki-types", "tokio", - "tokio-rustls 0.25.0", + "tokio-rustls 0.26.0", "tower-service", + "webpki-roots", ] [[package]] @@ -5592,9 +5619,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] @@ -6685,6 +6712,53 @@ dependencies = [ "memchr", ] +[[package]] +name = "quinn" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4ceeeeabace7857413798eb1ffa1e9c905a9946a57d81fb69b4b71c4d8eb3ad" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls 0.23.10", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddf517c03a109db8100448a4be38d498df8a210a99fe0e1b9eaf39e78c640efe" +dependencies = [ + "bytes", + "rand", + "ring", + "rustc-hash", + "rustls 0.23.10", + "slab", + "thiserror", + "tinyvec", + "tracing", +] + +[[package]] +name = "quinn-udp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9096629c45860fc7fb143e125eb826b5e721e10be3263160c7d60ca832cf8c46" +dependencies = [ + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "quote" version = "1.0.36" @@ -6921,9 +6995,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" +checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" dependencies = [ "base64 0.22.1", "bytes", @@ -6934,7 +7008,7 @@ dependencies = [ "http-body 1.0.0", "http-body-util", "hyper 1.3.1", - "hyper-rustls 0.26.0", + "hyper-rustls 0.27.2", "hyper-tls", "hyper-util", "ipnet", @@ -6946,17 +7020,18 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.22.4", + "quinn", + "rustls 0.23.10", "rustls-native-certs 0.7.0", "rustls-pemfile 2.1.2", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 0.1.2", + "sync_wrapper 1.0.1", "tokio", "tokio-native-tls", - "tokio-rustls 0.25.0", + "tokio-rustls 0.26.0", "tokio-util", "tower-service", "url", @@ -6971,7 +7046,7 @@ dependencies = [ [[package]] name = "revm" version = "9.0.0" -source = "git+https://github.com/bluealloy/revm.git?rev=dd98b3b#dd98b3bb977396d23966e0a2f40d97678d931573" +source = "git+https://github.com/bluealloy/revm.git?rev=41e2f7f#41e2f7f9740c0fb70c5ba888a36453712b6de39c" dependencies = [ "auto_impl", "cfg-if", @@ -6985,7 +7060,7 @@ dependencies = [ [[package]] name = "revm-inspectors" version = "0.1.0" -source = "git+https://github.com/paradigmxyz/revm-inspectors?rev=79774a6#79774a6e4add9da9247130bf73305531092d0895" +source = "git+https://github.com/paradigmxyz/revm-inspectors?rev=4fe17f0#4fe17f08797450d9d5df315e724d14c9f3749b3f" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -7001,7 +7076,7 @@ dependencies = [ [[package]] name = "revm-interpreter" version = "5.0.0" -source = "git+https://github.com/bluealloy/revm.git?rev=dd98b3b#dd98b3bb977396d23966e0a2f40d97678d931573" +source = "git+https://github.com/bluealloy/revm.git?rev=41e2f7f#41e2f7f9740c0fb70c5ba888a36453712b6de39c" dependencies = [ "revm-primitives", "serde", @@ -7010,7 +7085,7 @@ dependencies = [ [[package]] name = "revm-precompile" version = "7.0.0" -source = "git+https://github.com/bluealloy/revm.git?rev=dd98b3b#dd98b3bb977396d23966e0a2f40d97678d931573" +source = "git+https://github.com/bluealloy/revm.git?rev=41e2f7f#41e2f7f9740c0fb70c5ba888a36453712b6de39c" dependencies = [ "aurora-engine-modexp", "c-kzg", @@ -7027,7 +7102,7 @@ dependencies = [ [[package]] name = "revm-primitives" version = "4.0.0" -source = "git+https://github.com/bluealloy/revm.git?rev=dd98b3b#dd98b3bb977396d23966e0a2f40d97678d931573" +source = "git+https://github.com/bluealloy/revm.git?rev=41e2f7f#41e2f7f9740c0fb70c5ba888a36453712b6de39c" dependencies = [ "alloy-primitives", "auto_impl", @@ -7859,9 +7934,9 @@ dependencies = [ [[package]] name = "simple-home-dir" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c433538e900807402974e89beb88a98bda36e6f70f09a7225cdf11d013b8efe8" +checksum = "c221cbc8c1ff6bdf949b12cc011456c510ec6840654b444c7374c78e928ce344" dependencies = [ "windows-sys 0.52.0", ] @@ -7935,7 +8010,7 @@ dependencies = [ "futures", "once_cell", "regex", - "reqwest 0.12.4", + "reqwest 0.12.5", "rpassword", "serde", "serde_derive", @@ -8091,7 +8166,7 @@ dependencies = [ "dirs 5.0.1", "fs4", "once_cell", - "reqwest 0.12.4", + "reqwest 0.12.5", "semver 1.0.23", "serde", "serde_json", @@ -8505,9 +8580,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.23.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "becd34a233e7e31a3dbf7c7241b38320f57393dcae8e7324b0167d21b8e320b0" +checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd" dependencies = [ "futures-util", "log", @@ -9198,9 +9273,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.2" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c452ad30530b54a4d8e71952716a212b08efd0f3562baa66c29a618b07da7c3" +checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" dependencies = [ "rustls-pki-types", ] @@ -9759,10 +9834,30 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.10+zstd.1.5.6" +version = "2.0.11+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" +checksum = "75652c55c0b6f3e6f12eb786fe1bc960396bf05a1eb3bf1f3691c3610ac2e6d4" dependencies = [ "cc", "pkg-config", ] + +[[patch.unused]] +name = "revm" +version = "9.0.0" +source = "git+https://github.com/bluealloy/revm.git?rev=dd98b3b#dd98b3bb977396d23966e0a2f40d97678d931573" + +[[patch.unused]] +name = "revm-interpreter" +version = "5.0.0" +source = "git+https://github.com/bluealloy/revm.git?rev=dd98b3b#dd98b3bb977396d23966e0a2f40d97678d931573" + +[[patch.unused]] +name = "revm-precompile" +version = "7.0.0" +source = "git+https://github.com/bluealloy/revm.git?rev=dd98b3b#dd98b3bb977396d23966e0a2f40d97678d931573" + +[[patch.unused]] +name = "revm-primitives" +version = "4.0.0" +source = "git+https://github.com/bluealloy/revm.git?rev=dd98b3b#dd98b3bb977396d23966e0a2f40d97678d931573" diff --git a/Cargo.toml b/Cargo.toml index c088c2573..719e5f332 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -158,9 +158,9 @@ foundry-compilers = { version = "0.8.0", default-features = false } ## revm # no default features to avoid c-kzg -revm = { version = "9.0.0", default-features = false } -revm-primitives = { version = "4.0.0", default-features = false } -revm-inspectors = { git = "https://github.com/paradigmxyz/revm-inspectors", rev = "79774a6", features = [ +revm = { version = "9.0.0", git = "https://github.com/bluealloy/revm.git", rev = "41e2f7f", default-features = false } +revm-primitives = { version = "4.0.0", git = "https://github.com/bluealloy/revm.git", rev = "41e2f7f", default-features = false } +revm-inspectors = { git = "https://github.com/paradigmxyz/revm-inspectors", rev = "4fe17f0", features = [ "serde", ] } @@ -168,30 +168,30 @@ revm-inspectors = { git = "https://github.com/paradigmxyz/revm-inspectors", rev ethers-contract-abigen = { version = "2.0.14", default-features = false } ## alloy -alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } -alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } -alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } -alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } -alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } -alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } -alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } -alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } -alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } -alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } -alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } -alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } -alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } -alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } -alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } -alloy-signer-local = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } -alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } -alloy-signer-gcp = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } -alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } -alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } -alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } -alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } -alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } -alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "6cb3713", default-features = false } +alloy-consensus = { version = "0.1.1", default-features = false } +alloy-contract = { version = "0.1.1", default-features = false } +alloy-eips = { version = "0.1.1", default-features = false } +alloy-genesis = { version = "0.1.1", default-features = false } +alloy-json-rpc = { version = "0.1.1", default-features = false } +alloy-network = { version = "0.1.1", default-features = false } +alloy-node-bindings = { version = "0.1.1", default-features = false } +alloy-provider = { version = "0.1.1", default-features = false } +alloy-pubsub = { version = "0.1.1", default-features = false } +alloy-rpc-client = { version = "0.1.1", default-features = false } +alloy-rpc-types-engine = { version = "0.1.1", default-features = false } +alloy-rpc-types-trace = { version = "0.1.1", default-features = false } +alloy-rpc-types = { version = "0.1.1", default-features = false } +alloy-serde = { version = "0.1.1", default-features = false } +alloy-signer = { version = "0.1.1", default-features = false } +alloy-signer-local = { version = "0.1.1", default-features = false } +alloy-signer-aws = { version = "0.1.1", default-features = false } +alloy-signer-gcp = { version = "0.1.1", default-features = false } +alloy-signer-ledger = { version = "0.1.1", default-features = false } +alloy-signer-trezor = { version = "0.1.1", default-features = false } +alloy-transport = { version = "0.1.1", default-features = false } +alloy-transport-http = { version = "0.1.1", default-features = false } +alloy-transport-ipc = { version = "0.1.1", default-features = false } +alloy-transport-ws = { version = "0.1.1", default-features = false } alloy-primitives = { version = "0.7.1", features = ["getrandom", "rand"] } alloy-dyn-abi = "0.7.1" alloy-json-abi = "0.7.1" diff --git a/crates/cheatcodes/src/lib.rs b/crates/cheatcodes/src/lib.rs index be13f0f8e..9e143e8db 100644 --- a/crates/cheatcodes/src/lib.rs +++ b/crates/cheatcodes/src/lib.rs @@ -151,6 +151,6 @@ impl<'cheats, 'evm, DB: DatabaseExt> std::ops::DerefMut for CheatsCtxt<'cheats, impl<'cheats, 'evm, DB: DatabaseExt> CheatsCtxt<'cheats, 'evm, DB> { #[inline] pub(crate) fn is_precompile(&self, address: &Address) -> bool { - self.precompiles.contains_key(address) + self.precompiles.contains(address) } } From 9e271d00370bfaf97e04e9dfdc8e6ece942fc600 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 18 Jun 2024 09:38:53 +0200 Subject: [PATCH 420/430] perf: new-type TargetedContracts (#8180) --- .../evm/evm/src/executors/invariant/error.rs | 2 +- crates/evm/evm/src/executors/invariant/mod.rs | 60 +++-- crates/evm/fuzz/src/invariant/mod.rs | 212 +++++++++++------- crates/evm/fuzz/src/strategies/invariants.rs | 27 +-- crates/evm/fuzz/src/strategies/state.rs | 6 +- crates/forge/tests/it/invariant.rs | 2 +- .../common/InvariantShrinkWithAssert.t.sol | 2 +- 7 files changed, 179 insertions(+), 132 deletions(-) diff --git a/crates/evm/evm/src/executors/invariant/error.rs b/crates/evm/evm/src/executors/invariant/error.rs index 22d751ed5..7c47ac0a4 100644 --- a/crates/evm/evm/src/executors/invariant/error.rs +++ b/crates/evm/evm/src/executors/invariant/error.rs @@ -80,7 +80,7 @@ impl FailedInvariantCaseData { ) -> Self { // Collect abis of fuzzed and invariant contracts to decode custom error. let revert_reason = RevertDecoder::new() - .with_abis(targeted_contracts.targets.lock().iter().map(|(_, (_, abi, _))| abi)) + .with_abis(targeted_contracts.targets.lock().iter().map(|(_, c)| &c.abi)) .with_abi(invariant_contract.abi) .decode(call_result.result.as_ref(), Some(call_result.exit_reason)); diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 1eee0cafc..0918b3eba 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -7,16 +7,13 @@ use alloy_sol_types::{sol, SolCall}; use eyre::{eyre, ContextCompat, Result}; use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; use foundry_config::InvariantConfig; -use foundry_evm_core::{ - constants::{ - CALLER, CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME, - }, - utils::get_function, +use foundry_evm_core::constants::{ + CALLER, CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME, }; use foundry_evm_fuzz::{ invariant::{ ArtifactFilters, BasicTxDetails, FuzzRunIdentifiedContracts, InvariantContract, - RandomCallGenerator, SenderFilters, TargetedContracts, + RandomCallGenerator, SenderFilters, TargetedContract, TargetedContracts, }, strategies::{invariant_strat, override_call_strat, EvmFuzzState}, FuzzCase, FuzzFixtures, FuzzedCases, @@ -31,7 +28,7 @@ use proptest::{ use result::{assert_after_invariant, assert_invariants, can_continue}; use revm::primitives::HashMap; use shrink::shrink_sequence; -use std::{cell::RefCell, collections::BTreeMap, sync::Arc}; +use std::{cell::RefCell, collections::btree_map::Entry, sync::Arc}; mod error; pub use error::{InvariantFailures, InvariantFuzzError}; @@ -517,7 +514,7 @@ impl<'a> InvariantExecutor<'a> { let excluded = self.call_sol_default(to, &IInvariantTest::excludeContractsCall {}).excludedContracts; - let mut contracts: TargetedContracts = self + let contracts = self .setup_contracts .iter() .filter(|&(addr, (identifier, _))| { @@ -528,8 +525,11 @@ impl<'a> InvariantExecutor<'a> { (excluded.is_empty() || !excluded.contains(addr)) && self.artifact_filters.matches(identifier) }) - .map(|(addr, (identifier, abi))| (*addr, (identifier.clone(), abi.clone(), vec![]))) + .map(|(addr, (identifier, abi))| { + (*addr, TargetedContract::new(identifier.clone(), abi.clone())) + }) .collect(); + let mut contracts = TargetedContracts { inner: contracts }; self.target_interfaces(to, &mut contracts)?; @@ -561,7 +561,7 @@ impl<'a> InvariantExecutor<'a> { // the specified interfaces for the same address. For example: // `[(addr1, ["IERC20", "IOwnable"])]` and `[(addr1, ["IERC20"]), (addr1, ("IOwnable"))]` // should be equivalent. - let mut combined: TargetedContracts = BTreeMap::new(); + let mut combined = TargetedContracts::new(); // Loop through each address and its associated artifact identifiers. // We're borrowing here to avoid taking full ownership. @@ -577,18 +577,18 @@ impl<'a> InvariantExecutor<'a> { .entry(*addr) // If the entry exists, extends its ABI with the function list. .and_modify(|entry| { - let (_, contract_abi, _) = entry; - // Extend the ABI's function list with the new functions. - contract_abi.functions.extend(contract.abi.functions.clone()); + entry.abi.functions.extend(contract.abi.functions.clone()); }) // Otherwise insert it into the map. - .or_insert_with(|| (identifier.to_string(), contract.abi.clone(), vec![])); + .or_insert_with(|| { + TargetedContract::new(identifier.to_string(), contract.abi.clone()) + }); } } } - targeted_contracts.extend(combined); + targeted_contracts.extend(combined.inner); Ok(()) } @@ -623,26 +623,18 @@ impl<'a> InvariantExecutor<'a> { selectors: &[Selector], targeted_contracts: &mut TargetedContracts, ) -> eyre::Result<()> { - if let Some((name, abi, address_selectors)) = targeted_contracts.get_mut(&address) { - // The contract is already part of our filter, and all we do is specify that we're - // only looking at specific functions coming from `bytes4_array`. - for &selector in selectors { - address_selectors.push(get_function(name, selector, abi).cloned()?); + let contract = match targeted_contracts.entry(address) { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => { + let (identifier, abi) = self.setup_contracts.get(&address).ok_or_else(|| { + eyre::eyre!( + "[targetSelectors] address does not have an associated contract: {address}" + ) + })?; + entry.insert(TargetedContract::new(identifier.clone(), abi.clone())) } - } else { - let (name, abi) = self.setup_contracts.get(&address).ok_or_else(|| { - eyre::eyre!( - "[targetSelectors] address does not have an associated contract: {address}" - ) - })?; - - let functions = selectors - .iter() - .map(|&selector| get_function(name, selector, abi).cloned()) - .collect::, _>>()?; - - targeted_contracts.insert(address, (name.to_string(), abi.clone(), functions)); - } + }; + contract.add_selectors(selectors.iter().copied())?; Ok(()) } diff --git a/crates/evm/fuzz/src/invariant/mod.rs b/crates/evm/fuzz/src/invariant/mod.rs index fdc0b9d56..9138443be 100644 --- a/crates/evm/fuzz/src/invariant/mod.rs +++ b/crates/evm/fuzz/src/invariant/mod.rs @@ -1,5 +1,5 @@ use alloy_json_abi::{Function, JsonAbi}; -use alloy_primitives::{Address, Bytes}; +use alloy_primitives::{Address, Bytes, Selector}; use itertools::Either; use parking_lot::Mutex; use std::{collections::BTreeMap, sync::Arc}; @@ -10,11 +10,10 @@ pub use call_override::RandomCallGenerator; mod filters; pub use filters::{ArtifactFilters, SenderFilters}; use foundry_common::{ContractsByAddress, ContractsByArtifact}; -use foundry_evm_core::utils::StateChangeset; - -pub type TargetedContracts = BTreeMap)>; +use foundry_evm_core::utils::{get_function, StateChangeset}; /// Contracts identified as targets during a fuzz run. +/// /// During execution, any newly created contract is added as target and used through the rest of /// the fuzz run if the collection is updatable (no `targetContract` specified in `setUp`). #[derive(Clone, Debug)] @@ -26,42 +25,11 @@ pub struct FuzzRunIdentifiedContracts { } impl FuzzRunIdentifiedContracts { + /// Creates a new `FuzzRunIdentifiedContracts` instance. pub fn new(targets: TargetedContracts, is_updatable: bool) -> Self { Self { targets: Arc::new(Mutex::new(targets)), is_updatable } } - /// Returns fuzzed contract abi and fuzzed function from address and provided calldata. - /// - /// Used to decode return values and logs in order to add values into fuzz dictionary. - pub fn with_fuzzed_artifacts( - &self, - tx: &BasicTxDetails, - f: impl FnOnce(Option<&JsonAbi>, Option<&Function>), - ) { - let targets = self.targets.lock(); - let (abi, abi_f) = match targets.get(&tx.call_details.target) { - Some((_, abi, _)) => { - (Some(abi), abi.functions().find(|f| f.selector() == tx.call_details.calldata[..4])) - } - None => (None, None), - }; - f(abi, abi_f); - } - - /// Returns flatten target contract address and functions to be fuzzed. - /// Includes contract targeted functions if specified, else all mutable contract functions. - pub fn fuzzed_functions(&self) -> Vec<(Address, Function)> { - let mut fuzzed_functions = vec![]; - for (contract, (_, abi, functions)) in self.targets.lock().iter() { - if !abi.functions.is_empty() { - for function in abi_fuzzed_functions(abi, functions) { - fuzzed_functions.push((*contract, function.clone())); - } - } - } - fuzzed_functions - } - /// If targets are updatable, collect all contracts created during an invariant run (which /// haven't been discovered yet). pub fn collect_created_contracts( @@ -72,34 +40,41 @@ impl FuzzRunIdentifiedContracts { artifact_filters: &ArtifactFilters, created_contracts: &mut Vec
, ) -> eyre::Result<()> { - if self.is_updatable { - let mut targets = self.targets.lock(); - for (address, account) in state_changeset { - if setup_contracts.contains_key(address) { - continue; - } - if !account.is_touched() { - continue; - } - let Some(code) = &account.info.code else { - continue; - }; - if code.is_empty() { - continue; - } - let Some((artifact, contract)) = - project_contracts.find_by_deployed_code(code.original_byte_slice()) - else { - continue; - }; - let Some(functions) = - artifact_filters.get_targeted_functions(artifact, &contract.abi)? - else { - continue; - }; - created_contracts.push(*address); - targets.insert(*address, (artifact.name.clone(), contract.abi.clone(), functions)); + if !self.is_updatable { + return Ok(()); + } + + let mut targets = self.targets.lock(); + for (address, account) in state_changeset { + if setup_contracts.contains_key(address) { + continue; } + if !account.is_touched() { + continue; + } + let Some(code) = &account.info.code else { + continue; + }; + if code.is_empty() { + continue; + } + let Some((artifact, contract)) = + project_contracts.find_by_deployed_code(code.original_byte_slice()) + else { + continue; + }; + let Some(functions) = + artifact_filters.get_targeted_functions(artifact, &contract.abi)? + else { + continue; + }; + created_contracts.push(*address); + let contract = TargetedContract { + identifier: artifact.name.clone(), + abi: contract.abi.clone(), + targeted_functions: functions, + }; + targets.insert(*address, contract); } Ok(()) } @@ -115,21 +90,102 @@ impl FuzzRunIdentifiedContracts { } } -/// Helper to retrieve functions to fuzz for specified abi. -/// Returns specified targeted functions if any, else mutable abi functions. -pub(crate) fn abi_fuzzed_functions<'a>( - abi: &'a JsonAbi, - targeted_functions: &'a [Function], -) -> impl Iterator { - if !targeted_functions.is_empty() { - Either::Left(targeted_functions.iter()) - } else { - Either::Right(abi.functions().filter(|&func| { - !matches!( - func.state_mutability, - alloy_json_abi::StateMutability::Pure | alloy_json_abi::StateMutability::View - ) - })) +/// A collection of contracts identified as targets for invariant testing. +#[derive(Clone, Debug, Default)] +pub struct TargetedContracts { + /// The inner map of targeted contracts. + pub inner: BTreeMap, +} + +impl TargetedContracts { + /// Returns a new `TargetedContracts` instance. + pub fn new() -> Self { + Self::default() + } + + /// Returns fuzzed contract abi and fuzzed function from address and provided calldata. + /// + /// Used to decode return values and logs in order to add values into fuzz dictionary. + pub fn fuzzed_artifacts(&self, tx: &BasicTxDetails) -> (Option<&JsonAbi>, Option<&Function>) { + match self.inner.get(&tx.call_details.target) { + Some(c) => ( + Some(&c.abi), + c.abi.functions().find(|f| f.selector() == tx.call_details.calldata[..4]), + ), + None => (None, None), + } + } + + /// Returns flatten target contract address and functions to be fuzzed. + /// Includes contract targeted functions if specified, else all mutable contract functions. + pub fn fuzzed_functions(&self) -> impl Iterator { + self.inner + .iter() + .filter(|(_, c)| !c.abi.functions.is_empty()) + .flat_map(|(contract, c)| c.abi_fuzzed_functions().map(move |f| (contract, f))) + } +} + +impl std::ops::Deref for TargetedContracts { + type Target = BTreeMap; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl std::ops::DerefMut for TargetedContracts { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +/// A contract identified as targets for invariant testing. +#[derive(Clone, Debug)] +pub struct TargetedContract { + /// The contract identifier. This is only used in error messages. + pub identifier: String, + /// The contract's ABI. + pub abi: JsonAbi, + /// The targeted functions of the contract. + pub targeted_functions: Vec, +} + +impl TargetedContract { + /// Returns a new `TargetedContract` instance. + pub fn new(identifier: String, abi: JsonAbi) -> Self { + Self { identifier, abi, targeted_functions: Vec::new() } + } + + /// Helper to retrieve functions to fuzz for specified abi. + /// Returns specified targeted functions if any, else mutable abi functions. + pub fn abi_fuzzed_functions(&self) -> impl Iterator { + if !self.targeted_functions.is_empty() { + Either::Left(self.targeted_functions.iter()) + } else { + Either::Right(self.abi.functions().filter(|&func| { + !matches!( + func.state_mutability, + alloy_json_abi::StateMutability::Pure | alloy_json_abi::StateMutability::View + ) + })) + } + } + + /// Returns the function for the given selector. + pub fn get_function(&self, selector: Selector) -> eyre::Result<&Function> { + get_function(&self.identifier, selector, &self.abi) + } + + /// Adds the specified selectors to the targeted functions. + pub fn add_selectors( + &mut self, + selectors: impl IntoIterator, + ) -> eyre::Result<()> { + for selector in selectors { + self.targeted_functions.push(self.get_function(selector)?.clone()); + } + Ok(()) } } diff --git a/crates/evm/fuzz/src/strategies/invariants.rs b/crates/evm/fuzz/src/strategies/invariants.rs index 55af574c5..c7d04dd1a 100644 --- a/crates/evm/fuzz/src/strategies/invariants.rs +++ b/crates/evm/fuzz/src/strategies/invariants.rs @@ -1,9 +1,6 @@ use super::{fuzz_calldata, fuzz_param_from_state}; use crate::{ - invariant::{ - abi_fuzzed_functions, BasicTxDetails, CallDetails, FuzzRunIdentifiedContracts, - SenderFilters, - }, + invariant::{BasicTxDetails, CallDetails, FuzzRunIdentifiedContracts, SenderFilters}, strategies::{fuzz_calldata_from_state, fuzz_param, EvmFuzzState}, FuzzFixtures, }; @@ -11,6 +8,7 @@ use alloy_json_abi::Function; use alloy_primitives::Address; use parking_lot::RwLock; use proptest::prelude::*; +use rand::seq::IteratorRandom; use std::{rc::Rc, sync::Arc}; /// Given a target address, we generate random calldata. @@ -32,15 +30,13 @@ pub fn override_call_strat( let func = { let contracts = contracts.targets.lock(); - let (_, abi, functions) = contracts.get(&target_address).unwrap_or_else(|| { + let contract = contracts.get(&target_address).unwrap_or_else(|| { // Choose a random contract if target selected by lazy strategy is not in fuzz run // identified contracts. This can happen when contract is created in `setUp` call // but is not included in targetContracts. - let rand_index = rand::thread_rng().gen_range(0..contracts.len()); - let (_, contract_specs) = contracts.iter().nth(rand_index).unwrap(); - contract_specs + contracts.values().choose(&mut rand::thread_rng()).unwrap() }); - let fuzzed_functions: Vec<_> = abi_fuzzed_functions(abi, functions).cloned().collect(); + let fuzzed_functions: Vec<_> = contract.abi_fuzzed_functions().cloned().collect(); any::().prop_map(move |index| index.get(&fuzzed_functions).clone()) }; @@ -68,16 +64,17 @@ pub fn invariant_strat( fuzz_fixtures: FuzzFixtures, ) -> impl Strategy { let senders = Rc::new(senders); - any::() - .prop_flat_map(move |index| { - let (target_address, target_function) = - index.get(&contracts.fuzzed_functions()).clone(); + any::() + .prop_flat_map(move |selector| { + let contracts = contracts.targets.lock(); + let functions = contracts.fuzzed_functions(); + let (target_address, target_function) = selector.select(functions); let sender = select_random_sender(&fuzz_state, senders.clone(), dictionary_weight); let call_details = fuzz_contract_with_calldata( &fuzz_state, &fuzz_fixtures, - target_address, - target_function, + *target_address, + target_function.clone(), ); (sender, call_details) }) diff --git a/crates/evm/fuzz/src/strategies/state.rs b/crates/evm/fuzz/src/strategies/state.rs index cf29c9420..f4f1dde92 100644 --- a/crates/evm/fuzz/src/strategies/state.rs +++ b/crates/evm/fuzz/src/strategies/state.rs @@ -64,10 +64,12 @@ impl EvmFuzzState { run_depth: u32, ) { let mut dict = self.inner.write(); - fuzzed_contracts.with_fuzzed_artifacts(tx, |target_abi, target_function| { + { + let targets = fuzzed_contracts.targets.lock(); + let (target_abi, target_function) = targets.fuzzed_artifacts(tx); dict.insert_logs_values(target_abi, logs, run_depth); dict.insert_result_values(target_function, result, run_depth); - }); + } dict.insert_new_state_values(state_changeset); } diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index 19500ab63..b6e564d49 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -773,7 +773,7 @@ async fn test_invariant_after_invariant() { #[tokio::test(flavor = "multi_thread")] async fn test_invariant_selectors_weight() { let mut opts = TEST_DATA_DEFAULT.test_opts.clone(); - opts.fuzz.seed = Some(U256::from(119u32)); + opts.fuzz.seed = Some(U256::from(100u32)); let filter = Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantSelectorsWeight.t.sol"); let mut runner = TEST_DATA_DEFAULT.runner(); diff --git a/testdata/default/fuzz/invariant/common/InvariantShrinkWithAssert.t.sol b/testdata/default/fuzz/invariant/common/InvariantShrinkWithAssert.t.sol index c189e2507..d5dcfe674 100644 --- a/testdata/default/fuzz/invariant/common/InvariantShrinkWithAssert.t.sol +++ b/testdata/default/fuzz/invariant/common/InvariantShrinkWithAssert.t.sol @@ -86,7 +86,7 @@ contract InvariantShrinkWithAssert is DSTest { } function invariant_with_assert() public { - assertTrue(counter.number() != 3); + assertTrue(counter.number() != 3, "wrong counter"); } } From a131937c521936139fe46ba7c689ae69ed77ba6d Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Tue, 18 Jun 2024 14:09:27 +0300 Subject: [PATCH 421/430] feat: cast etherscan-source --flatten (#8159) --- crates/cast/bin/main.rs | 11 +++++++---- crates/cast/bin/opts.rs | 8 ++++++-- crates/cast/src/lib.rs | 35 ++++++++++++++++++++++++++++++++- crates/common/src/compile.rs | 31 ++++++++++++++++------------- crates/forge/bin/cmd/flatten.rs | 11 ++++++----- 5 files changed, 70 insertions(+), 26 deletions(-) diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index e17fd9d86..0f6673d4f 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -530,18 +530,21 @@ async fn main() -> Result<()> { CastSubcommand::RightShift { value, bits, base_in, base_out } => { println!("{}", SimpleCast::right_shift(&value, &bits, base_in.as_deref(), &base_out)?); } - CastSubcommand::EtherscanSource { address, directory, etherscan } => { + CastSubcommand::EtherscanSource { address, directory, etherscan, flatten } => { let config = Config::from(ðerscan); let chain = config.chain.unwrap_or_default(); let api_key = config.get_etherscan_api_key(Some(chain)).unwrap_or_default(); - match directory { - Some(dir) => { + match (directory, flatten) { + (Some(dir), false) => { SimpleCast::expand_etherscan_source_to_directory(chain, address, api_key, dir) .await? } - None => { + (None, false) => { println!("{}", SimpleCast::etherscan_source(chain, address, api_key).await?); } + (dir, true) => { + SimpleCast::etherscan_source_flatten(chain, address, api_key, dir).await?; + } } } CastSubcommand::Create2(cmd) => { diff --git a/crates/cast/bin/opts.rs b/crates/cast/bin/opts.rs index 4efe4245a..decd60374 100644 --- a/crates/cast/bin/opts.rs +++ b/crates/cast/bin/opts.rs @@ -808,8 +808,12 @@ pub enum CastSubcommand { /// The contract's address. address: String, - /// The output directory to expand source tree into. - #[arg(short, value_hint = ValueHint::DirPath)] + /// Whether to flatten the source code. + #[arg(long, short)] + flatten: bool, + + /// The output directory/file to expand source tree into. + #[arg(short, value_hint = ValueHint::DirPath, alias = "path")] directory: Option, #[command(flatten)] diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index e84b5ee95..4934d8dd3 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -25,9 +25,11 @@ use eyre::{Context, ContextCompat, Result}; use foundry_block_explorers::Client; use foundry_common::{ abi::{encode_function_args, get_func}, + compile::etherscan_project, fmt::*, - TransactionReceiptWithRevertReason, + fs, TransactionReceiptWithRevertReason, }; +use foundry_compilers::flatten::Flattener; use foundry_config::Chain; use futures::{future::Either, FutureExt, StreamExt}; use rayon::prelude::*; @@ -1847,6 +1849,37 @@ impl SimpleCast { Ok(()) } + /// Fetches the source code of verified contracts from etherscan, flattens it and writes it to + /// the given path or stdout. + pub async fn etherscan_source_flatten( + chain: Chain, + contract_address: String, + etherscan_api_key: String, + output_path: Option, + ) -> Result<()> { + let client = Client::new(chain, etherscan_api_key)?; + let metadata = client.contract_source_code(contract_address.parse()?).await?; + let Some(metadata) = metadata.items.first() else { + eyre::bail!("Empty contract source code") + }; + + let tmp = tempfile::tempdir()?; + let project = etherscan_project(metadata, tmp.path())?; + let target_path = project.find_contract_path(&metadata.contract_name)?; + + let flattened = Flattener::new(project, &target_path)?.flatten(); + + if let Some(path) = output_path { + fs::create_dir_all(path.parent().unwrap())?; + fs::write(&path, flattened)?; + println!("Flattened file written at {}", path.display()); + } else { + println!("{flattened}"); + } + + Ok(()) + } + /// Disassembles hex encoded bytecode into individual / human readable opcodes /// /// # Example diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index ffefe141a..3be317e51 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -165,18 +165,8 @@ impl ProjectCompiler { { let quiet = self.quiet.unwrap_or(false); let bail = self.bail.unwrap_or(true); - #[allow(clippy::collapsible_else_if)] - let reporter = if quiet { - Report::new(NoReporter::default()) - } else { - if std::io::stdout().is_terminal() { - Report::new(SpinnerReporter::spawn()) - } else { - Report::new(BasicStdoutReporter::default()) - } - }; - let output = foundry_compilers::report::with_scoped(&reporter, || { + let output = with_compilation_reporter(self.quiet.unwrap_or(false), || { tracing::debug!("compiling project"); let timer = Instant::now(); @@ -187,9 +177,6 @@ impl ProjectCompiler { r })?; - // need to drop the reporter here, so that the spinner terminates - drop(reporter); - if bail && output.has_compiler_errors() { eyre::bail!("{output}") } @@ -554,3 +541,19 @@ pub fn etherscan_project( .no_artifacts() .build(compiler)?) } + +/// 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 { + Report::new(NoReporter::default()) + } else { + if std::io::stdout().is_terminal() { + Report::new(SpinnerReporter::spawn()) + } else { + Report::new(BasicStdoutReporter::default()) + } + }; + + foundry_compilers::report::with_scoped(&reporter, f) +} diff --git a/crates/forge/bin/cmd/flatten.rs b/crates/forge/bin/cmd/flatten.rs index 64c4ea78f..c4a011337 100644 --- a/crates/forge/bin/cmd/flatten.rs +++ b/crates/forge/bin/cmd/flatten.rs @@ -4,7 +4,7 @@ use foundry_cli::{ opts::{CoreBuildArgs, ProjectPathsArgs}, utils::LoadConfig, }; -use foundry_common::fs; +use foundry_common::{compile::with_compilation_reporter, fs}; use foundry_compilers::{ compilers::solc::SolcLanguage, error::SolcError, @@ -40,13 +40,14 @@ impl FlattenArgs { // flatten is a subset of `BuildArgs` so we can reuse that to get the config let build_args = CoreBuildArgs { project_paths, ..Default::default() }; - let mut config = build_args.try_load_config_emit_warnings()?; - // `Flattener` uses the typed AST for better flattening results. - config.ast = true; + let config = build_args.try_load_config_emit_warnings()?; let project = config.create_project(false, true)?; let target_path = dunce::canonicalize(target_path)?; - let flattener = Flattener::new(project.clone(), &target_path); + + let flattener = with_compilation_reporter(build_args.silent, || { + Flattener::new(project.clone(), &target_path) + }); let flattened = match flattener { Ok(flattener) => Ok(flattener.flatten()), From 6f41cd91255639d4e53059bc84f591d1b48583dc Mon Sep 17 00:00:00 2001 From: Samuel Laferriere Date: Tue, 18 Jun 2024 20:20:15 +0800 Subject: [PATCH 422/430] fix(anvil): block dumps (#8160) * implemented latest_block dump/load * update to dump/load all blocks instead of only latest * refactored state loading into storage.rs, and added load-dump cycle test * fix clippy errors for anvil * remove SerializableHeader and use Header (now serializable) * clippy happy --------- Co-authored-by: Matthias Seitz --- crates/anvil/core/src/eth/transaction/mod.rs | 2 +- .../core/src/eth/transaction/optimism.rs | 3 +- crates/anvil/src/eth/backend/db.rs | 33 ++++++++++++ crates/anvil/src/eth/backend/mem/fork_db.rs | 6 ++- .../anvil/src/eth/backend/mem/in_memory_db.rs | 13 ++--- crates/anvil/src/eth/backend/mem/mod.rs | 15 +++--- crates/anvil/src/eth/backend/mem/storage.rs | 50 ++++++++++++++++++- 7 files changed, 104 insertions(+), 18 deletions(-) diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 81840bfa5..83db64465 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -594,7 +594,7 @@ impl PendingTransaction { } /// Container type for signed, typed transactions. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum TypedTransaction { /// Legacy transaction type Legacy(Signed), diff --git a/crates/anvil/core/src/eth/transaction/optimism.rs b/crates/anvil/core/src/eth/transaction/optimism.rs index 912bdf26c..4a147297b 100644 --- a/crates/anvil/core/src/eth/transaction/optimism.rs +++ b/crates/anvil/core/src/eth/transaction/optimism.rs @@ -3,6 +3,7 @@ use alloy_primitives::{keccak256, Address, Bytes, ChainId, Signature, TxKind, B2 use alloy_rlp::{ length_of_length, Decodable, Encodable, Error as DecodeError, Header as RlpHeader, }; +use serde::{Deserialize, Serialize}; use std::mem; #[derive(Clone, Debug, PartialEq, Eq)] @@ -228,7 +229,7 @@ impl Encodable for DepositTransactionRequest { /// An op-stack deposit transaction. /// See -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct DepositTransaction { pub nonce: u64, pub source_hash: B256, diff --git a/crates/anvil/src/eth/backend/db.rs b/crates/anvil/src/eth/backend/db.rs index 852956d73..9182cdadb 100644 --- a/crates/anvil/src/eth/backend/db.rs +++ b/crates/anvil/src/eth/backend/db.rs @@ -1,8 +1,10 @@ //! Helper types for working with [revm](foundry_evm::revm) use crate::revm::primitives::AccountInfo; +use alloy_consensus::Header; use alloy_primitives::{keccak256, Address, Bytes, B256, U256, U64}; use alloy_rpc_types::BlockId; +use anvil_core::eth::{block::Block, transaction::TypedTransaction}; use foundry_common::errors::FsPathError; use foundry_evm::{ backend::{DatabaseError, DatabaseResult, MemDb, RevertSnapshotAction, StateSnapshot}, @@ -116,6 +118,7 @@ pub trait Db: &self, at: BlockEnv, best_number: U64, + blocks: Vec, ) -> DatabaseResult>; /// Deserialize and add all chain data to the backend storage @@ -188,6 +191,7 @@ impl + Send + Sync + Clone + fmt::Debug> D &self, _at: BlockEnv, _best_number: U64, + _blocks: Vec, ) -> DatabaseResult> { Ok(None) } @@ -318,6 +322,8 @@ pub struct SerializableState { pub accounts: BTreeMap, /// The best block number of the state, can be different from block number (Arbitrum chain). pub best_block_number: Option, + #[serde(default)] + pub blocks: Vec, } impl SerializableState { @@ -344,3 +350,30 @@ pub struct SerializableAccountRecord { pub code: Bytes, pub storage: BTreeMap, } + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct SerializableBlock { + pub header: Header, + pub transactions: Vec, + pub ommers: Vec
, +} + +impl From for SerializableBlock { + fn from(block: Block) -> Self { + Self { + header: block.header, + transactions: block.transactions.into_iter().map(Into::into).collect(), + ommers: block.ommers.into_iter().map(Into::into).collect(), + } + } +} + +impl From for Block { + fn from(block: SerializableBlock) -> Self { + Self { + header: block.header, + transactions: block.transactions.into_iter().map(Into::into).collect(), + ommers: block.ommers.into_iter().map(Into::into).collect(), + } + } +} diff --git a/crates/anvil/src/eth/backend/mem/fork_db.rs b/crates/anvil/src/eth/backend/mem/fork_db.rs index 93070ef88..ae325f975 100644 --- a/crates/anvil/src/eth/backend/mem/fork_db.rs +++ b/crates/anvil/src/eth/backend/mem/fork_db.rs @@ -1,7 +1,7 @@ use crate::{ eth::backend::db::{ - Db, MaybeForkedDatabase, MaybeFullDatabase, SerializableAccountRecord, SerializableState, - StateDb, + Db, MaybeForkedDatabase, MaybeFullDatabase, SerializableAccountRecord, SerializableBlock, + SerializableState, StateDb, }, revm::primitives::AccountInfo, }; @@ -36,6 +36,7 @@ impl Db for ForkedDatabase { &self, at: BlockEnv, best_number: U64, + blocks: Vec, ) -> DatabaseResult> { let mut db = self.database().clone(); let accounts = self @@ -64,6 +65,7 @@ impl Db for ForkedDatabase { block: Some(at), accounts, best_block_number: Some(best_number), + blocks, })) } diff --git a/crates/anvil/src/eth/backend/mem/in_memory_db.rs b/crates/anvil/src/eth/backend/mem/in_memory_db.rs index 2049c9f90..6269727c1 100644 --- a/crates/anvil/src/eth/backend/mem/in_memory_db.rs +++ b/crates/anvil/src/eth/backend/mem/in_memory_db.rs @@ -2,8 +2,8 @@ use crate::{ eth::backend::db::{ - Db, MaybeForkedDatabase, MaybeFullDatabase, SerializableAccountRecord, SerializableState, - StateDb, + Db, MaybeForkedDatabase, MaybeFullDatabase, SerializableAccountRecord, SerializableBlock, + SerializableState, StateDb, }, mem::state::state_root, revm::{db::DbAccount, primitives::AccountInfo}, @@ -37,6 +37,7 @@ impl Db for MemDb { &self, at: BlockEnv, best_number: U64, + blocks: Vec, ) -> DatabaseResult> { let accounts = self .inner @@ -65,6 +66,7 @@ impl Db for MemDb { block: Some(at), accounts, best_block_number: Some(best_number), + blocks, })) } @@ -137,7 +139,7 @@ mod tests { use foundry_evm::revm::primitives::{Bytecode, KECCAK_EMPTY}; use std::{collections::BTreeMap, str::FromStr}; - // verifies that all substantial aspects of a loaded account remain the state after an account + // verifies that all substantial aspects of a loaded account remain the same after an account // is dumped and reloaded #[test] fn test_dump_reload_cycle() { @@ -147,7 +149,6 @@ mod tests { let mut dump_db = MemDb::default(); let contract_code = Bytecode::new_raw(Bytes::from("fake contract code")); - dump_db.insert_account( test_addr, AccountInfo { @@ -157,10 +158,10 @@ mod tests { nonce: 1234, }, ); - dump_db.set_storage_at(test_addr, U256::from(1234567), U256::from(1)).unwrap(); - let state = dump_db.dump_state(Default::default(), U64::ZERO).unwrap().unwrap(); + // blocks dumping/loading tested in storage.rs + let state = dump_db.dump_state(Default::default(), U64::ZERO, Vec::new()).unwrap().unwrap(); let mut load_db = MemDb::default(); diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 0e130c8e5..d40c7a07d 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -737,7 +737,8 @@ impl Backend { pub async fn serialized_state(&self) -> Result { let at = self.env.read().block.clone(); let best_number = self.blockchain.storage.read().best_number; - let state = self.db.read().await.dump_state(at, best_number)?; + let blocks = self.blockchain.storage.read().serialized_blocks(); + let state = self.db.read().await.dump_state(at, best_number, blocks)?; state.ok_or_else(|| { RpcError::invalid_params("Dumping state not supported with the current configuration") .into() @@ -766,14 +767,16 @@ impl Backend { state.best_block_number.unwrap_or(block.number.to::()); } - if !self.db.write().await.load_state(state)? { - Err(RpcError::invalid_params( + if !self.db.write().await.load_state(state.clone())? { + return Err(RpcError::invalid_params( "Loading state not supported with the current configuration", ) - .into()) - } else { - Ok(true) + .into()); } + + self.blockchain.storage.write().load_blocks(state.blocks.clone()); + + Ok(true) } /// Deserialize and add all chain data to the backend storage diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index 46365739e..5a8c3fe25 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -1,7 +1,7 @@ //! In-memory blockchain storage use crate::eth::{ backend::{ - db::{MaybeFullDatabase, StateDb}, + db::{MaybeFullDatabase, SerializableBlock, StateDb}, mem::cache::DiskStateCache, }, pool::transactions::PoolTransaction, @@ -319,6 +319,21 @@ impl BlockchainStorage { } } } + + pub fn serialized_blocks(&self) -> Vec { + self.blocks.values().map(|block| block.clone().into()).collect() + } + + /// Deserialize and add all blocks data to the backend storage + pub fn load_blocks(&mut self, serializable_blocks: Vec) { + for serializable_block in serializable_blocks.iter() { + let block: Block = serializable_block.clone().into(); + let block_hash = block.header.hash_slow(); + let block_number = block.header.number; + self.blocks.insert(block_hash, block); + self.hashes.insert(U64::from(block_number), block_hash); + } + } } /// A simple in-memory blockchain @@ -427,7 +442,9 @@ pub struct MinedTransactionReceipt { mod tests { use super::*; use crate::eth::backend::db::Db; - use alloy_primitives::Address; + use alloy_primitives::{hex, Address}; + use alloy_rlp::Decodable; + use anvil_core::eth::transaction::TypedTransaction; use foundry_evm::{ backend::MemDb, revm::{ @@ -499,4 +516,33 @@ mod tests { assert_eq!(acc.balance, rU256::from(balance)); } } + + // verifies that blocks in BlockchainStorage remain the same when dumped and reloaded + #[test] + fn test_storage_dump_reload_cycle() { + let mut dump_storage = BlockchainStorage::empty(); + + let partial_header = PartialHeader { gas_limit: 123456, ..Default::default() }; + let bytes_first = &mut &hex::decode("f86b02843b9aca00830186a094d3e8763675e4c425df46cc3b5c0f6cbdac39604687038d7ea4c68000802ba00eb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5aea03a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18").unwrap()[..]; + let tx: MaybeImpersonatedTransaction = + TypedTransaction::decode(&mut &bytes_first[..]).unwrap().into(); + let block = Block::new::( + partial_header.clone(), + vec![tx.clone()], + vec![], + ); + let block_hash = block.header.hash_slow(); + dump_storage.blocks.insert(block_hash, block); + + let serialized_blocks = dump_storage.serialized_blocks(); + + let mut load_storage = BlockchainStorage::empty(); + + load_storage.load_blocks(serialized_blocks); + + let loaded_block = load_storage.blocks.get(&block_hash).unwrap(); + assert_eq!(loaded_block.header.gas_limit, partial_header.gas_limit); + let loaded_tx = loaded_block.transactions.first().unwrap(); + assert_eq!(loaded_tx, &tx); + } } From 67238345280957f53203b2ea54b3fb003c22a316 Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 18 Jun 2024 15:34:08 +0300 Subject: [PATCH 423/430] feat(invariant): add excludeSelectors() filter (#8185) * feat(invariant): add excludeSelectors() filter * Apply suggestions from code review Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * Review changes: shorter err message --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- crates/evm/evm/src/executors/invariant/mod.rs | 24 ++++++++--- crates/evm/fuzz/src/invariant/mod.rs | 19 ++++++--- crates/forge/tests/it/invariant.rs | 4 ++ .../invariant/target/ExcludeSelectors.t.sol | 41 +++++++++++++++++++ 4 files changed, 78 insertions(+), 10 deletions(-) create mode 100644 testdata/default/fuzz/invariant/target/ExcludeSelectors.t.sol diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 0918b3eba..a260fcb56 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -71,6 +71,9 @@ sol! { #[derive(Default)] function excludeContracts() public view returns (address[] memory excludedContracts); + #[derive(Default)] + function excludeSelectors() public view returns (FuzzSelector[] memory excludedSelectors); + #[derive(Default)] function excludeSenders() public view returns (address[] memory excludedSenders); @@ -605,22 +608,31 @@ impl<'a> InvariantExecutor<'a> { if selectors.is_empty() { continue; } - self.add_address_with_functions(*address, selectors, targeted_contracts)?; + self.add_address_with_functions(*address, selectors, false, targeted_contracts)?; } } + // Collect contract functions marked as target for fuzzing campaign. let selectors = self.call_sol_default(address, &IInvariantTest::targetSelectorsCall {}); for IInvariantTest::FuzzSelector { addr, selectors } in selectors.targetedSelectors { - self.add_address_with_functions(addr, &selectors, targeted_contracts)?; + self.add_address_with_functions(addr, &selectors, false, targeted_contracts)?; } + + // Collect contract functions excluded from fuzzing campaign. + let selectors = self.call_sol_default(address, &IInvariantTest::excludeSelectorsCall {}); + for IInvariantTest::FuzzSelector { addr, selectors } in selectors.excludedSelectors { + self.add_address_with_functions(addr, &selectors, true, targeted_contracts)?; + } + Ok(()) } - /// Adds the address and fuzzable functions to `TargetedContracts`. + /// Adds the address and fuzzed or excluded functions to `TargetedContracts`. fn add_address_with_functions( &self, address: Address, selectors: &[Selector], + should_exclude: bool, targeted_contracts: &mut TargetedContracts, ) -> eyre::Result<()> { let contract = match targeted_contracts.entry(address) { @@ -628,13 +640,15 @@ impl<'a> InvariantExecutor<'a> { Entry::Vacant(entry) => { let (identifier, abi) = self.setup_contracts.get(&address).ok_or_else(|| { eyre::eyre!( - "[targetSelectors] address does not have an associated contract: {address}" + "[{}] address does not have an associated contract: {}", + if should_exclude { "excludeSelectors" } else { "targetSelectors" }, + address ) })?; entry.insert(TargetedContract::new(identifier.clone(), abi.clone())) } }; - contract.add_selectors(selectors.iter().copied())?; + contract.add_selectors(selectors.iter().copied(), should_exclude)?; Ok(()) } diff --git a/crates/evm/fuzz/src/invariant/mod.rs b/crates/evm/fuzz/src/invariant/mod.rs index 9138443be..d6b3e574d 100644 --- a/crates/evm/fuzz/src/invariant/mod.rs +++ b/crates/evm/fuzz/src/invariant/mod.rs @@ -73,6 +73,7 @@ impl FuzzRunIdentifiedContracts { identifier: artifact.name.clone(), abi: contract.abi.clone(), targeted_functions: functions, + excluded_functions: Vec::new(), }; targets.insert(*address, contract); } @@ -140,7 +141,7 @@ impl std::ops::DerefMut for TargetedContracts { } } -/// A contract identified as targets for invariant testing. +/// A contract identified as target for invariant testing. #[derive(Clone, Debug)] pub struct TargetedContract { /// The contract identifier. This is only used in error messages. @@ -149,16 +150,19 @@ pub struct TargetedContract { pub abi: JsonAbi, /// The targeted functions of the contract. pub targeted_functions: Vec, + /// The excluded functions of the contract. + pub excluded_functions: Vec, } impl TargetedContract { /// Returns a new `TargetedContract` instance. pub fn new(identifier: String, abi: JsonAbi) -> Self { - Self { identifier, abi, targeted_functions: Vec::new() } + Self { identifier, abi, targeted_functions: Vec::new(), excluded_functions: Vec::new() } } /// Helper to retrieve functions to fuzz for specified abi. - /// Returns specified targeted functions if any, else mutable abi functions. + /// Returns specified targeted functions if any, else mutable abi functions that are not + /// marked as excluded. pub fn abi_fuzzed_functions(&self) -> impl Iterator { if !self.targeted_functions.is_empty() { Either::Left(self.targeted_functions.iter()) @@ -167,7 +171,7 @@ impl TargetedContract { !matches!( func.state_mutability, alloy_json_abi::StateMutability::Pure | alloy_json_abi::StateMutability::View - ) + ) && !self.excluded_functions.contains(func) })) } } @@ -181,9 +185,14 @@ impl TargetedContract { pub fn add_selectors( &mut self, selectors: impl IntoIterator, + should_exclude: bool, ) -> eyre::Result<()> { for selector in selectors { - self.targeted_functions.push(self.get_function(selector)?.clone()); + if should_exclude { + self.excluded_functions.push(self.get_function(selector)?.clone()); + } else { + self.targeted_functions.push(self.get_function(selector)?.clone()); + } } Ok(()) } diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index b6e564d49..45bcda9d5 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -102,6 +102,10 @@ async fn test_invariant() { "default/fuzz/invariant/target/TargetSelectors.t.sol:TargetSelectors", vec![("invariantTrueWorld()", true, None, None, None)], ), + ( + "default/fuzz/invariant/target/ExcludeSelectors.t.sol:ExcludeSelectors", + vec![("invariantFalseWorld()", true, None, None, None)], + ), ( "default/fuzz/invariant/targetAbi/ExcludeArtifacts.t.sol:ExcludeArtifacts", vec![("invariantShouldPass()", true, None, None, None)], diff --git a/testdata/default/fuzz/invariant/target/ExcludeSelectors.t.sol b/testdata/default/fuzz/invariant/target/ExcludeSelectors.t.sol new file mode 100644 index 000000000..526da0c67 --- /dev/null +++ b/testdata/default/fuzz/invariant/target/ExcludeSelectors.t.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.18; + +import "ds-test/test.sol"; + +struct FuzzSelector { + address addr; + bytes4[] selectors; +} + +contract Hello { + bool public world = false; + + function change() public { + world = true; + } + + function real_change() public { + world = false; + } +} + +contract ExcludeSelectors is DSTest { + Hello hello; + + function setUp() public { + hello = new Hello(); + } + + function excludeSelectors() public returns (FuzzSelector[] memory) { + FuzzSelector[] memory targets = new FuzzSelector[](1); + bytes4[] memory selectors = new bytes4[](1); + selectors[0] = Hello.change.selector; + targets[0] = FuzzSelector(address(hello), selectors); + return targets; + } + + function invariantFalseWorld() public { + require(hello.world() == false, "true world"); + } +} From 272a09ff70f79d39f3e7e641d703600cced306a6 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Tue, 18 Jun 2024 18:04:42 +0530 Subject: [PATCH 424/430] fix(cheatcodes): use `call.bytecode_address` in mockCalls (#8184) * fix(cheatcodes): handle delegatecalls in vm.mockCalls using `bytecode_address` * add: repro test * nit: forge fmt --- crates/cheatcodes/src/inspector.rs | 2 +- testdata/default/cheats/MockCall.t.sol | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 449ebdaae..d7cfc3c5e 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -879,7 +879,7 @@ impl Inspector for Cheatcodes { } // Handle mocked calls - if let Some(mocks) = self.mocked_calls.get(&call.target_address) { + if let Some(mocks) = self.mocked_calls.get(&call.bytecode_address) { let ctx = MockCallDataContext { calldata: call.input.clone(), value: call.transfer_value() }; if let Some(return_data) = mocks.get(&ctx).or_else(|| { diff --git a/testdata/default/cheats/MockCall.t.sol b/testdata/default/cheats/MockCall.t.sol index a70b3572b..df7ee89c7 100644 --- a/testdata/default/cheats/MockCall.t.sol +++ b/testdata/default/cheats/MockCall.t.sol @@ -42,6 +42,20 @@ contract NestedMock { } } +contract NestedMockDelegateCall { + Mock private inner; + + constructor(Mock _inner) { + inner = _inner; + } + + function sum() public returns (uint256) { + (, bytes memory dataA) = address(inner).delegatecall(abi.encodeWithSelector(Mock.numberA.selector)); + (, bytes memory dataB) = address(inner).delegatecall(abi.encodeWithSelector(Mock.numberB.selector)); + return abi.decode(dataA, (uint256)) + abi.decode(dataB, (uint256)); + } +} + contract MockCallTest is DSTest { Vm constant vm = Vm(HEVM_ADDRESS); @@ -72,6 +86,18 @@ contract MockCallTest is DSTest { assertEq(target.sum(), 10); } + // Ref: https://github.com/foundry-rs/foundry/issues/8066 + function testMockNestedDelegate() public { + Mock inner = new Mock(); + NestedMockDelegateCall target = new NestedMockDelegateCall(inner); + + assertEq(target.sum(), 3); + + vm.mockCall(address(inner), abi.encodeWithSelector(inner.numberB.selector), abi.encode(9)); + + assertEq(target.sum(), 10); + } + function testMockSelector() public { Mock target = new Mock(); assertEq(target.add(5, 5), 10); From d744da25f3e4912f81a761a768f57141f824094e Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 18 Jun 2024 15:22:51 +0200 Subject: [PATCH 425/430] chore: remove rU256 alias (#8188) --- crates/anvil/src/eth/backend/mem/storage.rs | 10 +++++----- crates/anvil/src/eth/otterscan/types.rs | 4 ++-- crates/cast/bin/cmd/logs.rs | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index 5a8c3fe25..3531057ad 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -449,7 +449,7 @@ mod tests { backend::MemDb, revm::{ db::DatabaseRef, - primitives::{AccountInfo, U256 as rU256}, + primitives::{AccountInfo, U256}, }, }; @@ -468,7 +468,7 @@ mod tests { let mut state = MemDb::default(); let addr = Address::random(); - let info = AccountInfo::from_balance(rU256::from(1337)); + let info = AccountInfo::from_balance(U256::from(1337)); state.insert_account(addr, info); storage.insert(one, StateDb::new(state)); storage.insert(two, StateDb::new(MemDb::default())); @@ -482,7 +482,7 @@ mod tests { let loaded = storage.get(&one).unwrap(); let acc = loaded.basic_ref(addr).unwrap().unwrap(); - assert_eq!(acc.balance, rU256::from(1337u64)); + assert_eq!(acc.balance, U256::from(1337u64)); } #[tokio::test(flavor = "multi_thread")] @@ -496,7 +496,7 @@ mod tests { let hash = B256::from(U256::from(idx)); let addr = Address::from_word(hash); let balance = (idx * 2) as u64; - let info = AccountInfo::from_balance(rU256::from(balance)); + let info = AccountInfo::from_balance(U256::from(balance)); state.insert_account(addr, info); storage.insert(hash, StateDb::new(state)); } @@ -513,7 +513,7 @@ mod tests { let loaded = storage.get(&hash).unwrap(); let acc = loaded.basic_ref(addr).unwrap().unwrap(); let balance = (idx * 2) as u64; - assert_eq!(acc.balance, rU256::from(balance)); + assert_eq!(acc.balance, U256::from(balance)); } } diff --git a/crates/anvil/src/eth/otterscan/types.rs b/crates/anvil/src/eth/otterscan/types.rs index 57abc0c40..048e264a3 100644 --- a/crates/anvil/src/eth/otterscan/types.rs +++ b/crates/anvil/src/eth/otterscan/types.rs @@ -2,7 +2,7 @@ use crate::eth::{ backend::mem::{storage::MinedTransaction, Backend}, error::{BlockchainError, Result}, }; -use alloy_primitives::{Address, Bytes, FixedBytes, B256, U256 as rU256, U256}; +use alloy_primitives::{Address, Bytes, FixedBytes, B256, U256}; use alloy_rpc_types::{Block, BlockTransactions, Transaction}; use alloy_rpc_types_trace::parity::{ Action, CallAction, CallType, CreateAction, CreateOutput, LocalizedTransactionTrace, @@ -295,7 +295,7 @@ impl OtsInternalOperation { .filter_map(|node| { let r#type = match node.trace.kind { _ if node.is_selfdestruct() => OtsInternalOperationType::SelfDestruct, - CallKind::Call if node.trace.value != rU256::ZERO => { + CallKind::Call if node.trace.value != U256::ZERO => { OtsInternalOperationType::Transfer } CallKind::Create => OtsInternalOperationType::Create, diff --git a/crates/cast/bin/cmd/logs.rs b/crates/cast/bin/cmd/logs.rs index 3c538f210..7d92ab935 100644 --- a/crates/cast/bin/cmd/logs.rs +++ b/crates/cast/bin/cmd/logs.rs @@ -221,7 +221,7 @@ fn build_filter_topics(topics: Vec) -> Result { #[cfg(test)] mod tests { use super::*; - use alloy_primitives::{U160, U256 as rU256}; + use alloy_primitives::{U160, U256}; use alloy_rpc_types::ValueOrArray; const ADDRESS: &str = "0x4D1A2e2bB4F88F0250f26Ffff098B0b30B26BF38"; @@ -286,7 +286,7 @@ mod tests { #[test] fn test_build_filter_sig_with_arguments() { let addr = Address::from_str(ADDRESS).unwrap(); - let addr = rU256::from(U160::from_be_bytes(addr.0 .0)); + let addr = U256::from(U160::from_be_bytes(addr.0 .0)); let expected = Filter { block_option: FilterBlockOption::Range { from_block: None, to_block: None }, address: vec![].into(), @@ -311,7 +311,7 @@ mod tests { #[test] fn test_build_filter_sig_with_skipped_arguments() { let addr = Address::from_str(ADDRESS).unwrap(); - let addr = rU256::from(U160::from_be_bytes(addr.0 .0)); + let addr = U256::from(U160::from_be_bytes(addr.0 .0)); let expected = Filter { block_option: FilterBlockOption::Range { from_block: None, to_block: None }, address: vec![].into(), From 77cc4296e80bcfc50c4ceb909fe20886a2b7116c Mon Sep 17 00:00:00 2001 From: Paul Peregud Date: Tue, 18 Jun 2024 15:23:06 +0200 Subject: [PATCH 426/430] pick a random value for prevrandao for each block (#8187) Co-authored-by: Pawel Peregud --- crates/anvil/src/eth/backend/mem/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index d40c7a07d..bb3de1b09 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -941,6 +941,9 @@ impl Backend { env.block.blob_excess_gas_and_price = current_excess_blob_gas_and_price; env.block.timestamp = U256::from(self.time.next_timestamp()); + // pick a random value for prevrandao + env.block.prevrandao = Some(B256::random()); + let best_hash = self.blockchain.storage.read().best_hash; if self.prune_state_history_config.is_state_history_supported() { From 4d6c77c126334e3b403ebc4c0152884aa35dc2af Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 18 Jun 2024 19:15:27 +0200 Subject: [PATCH 427/430] chore: fix patches (#8189) --- Cargo.lock | 20 -------------------- Cargo.toml | 12 ++++++------ 2 files changed, 6 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aead342de..481ff0646 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9841,23 +9841,3 @@ dependencies = [ "cc", "pkg-config", ] - -[[patch.unused]] -name = "revm" -version = "9.0.0" -source = "git+https://github.com/bluealloy/revm.git?rev=dd98b3b#dd98b3bb977396d23966e0a2f40d97678d931573" - -[[patch.unused]] -name = "revm-interpreter" -version = "5.0.0" -source = "git+https://github.com/bluealloy/revm.git?rev=dd98b3b#dd98b3bb977396d23966e0a2f40d97678d931573" - -[[patch.unused]] -name = "revm-precompile" -version = "7.0.0" -source = "git+https://github.com/bluealloy/revm.git?rev=dd98b3b#dd98b3bb977396d23966e0a2f40d97678d931573" - -[[patch.unused]] -name = "revm-primitives" -version = "4.0.0" -source = "git+https://github.com/bluealloy/revm.git?rev=dd98b3b#dd98b3bb977396d23966e0a2f40d97678d931573" diff --git a/Cargo.toml b/Cargo.toml index 719e5f332..9a327b7a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -158,8 +158,8 @@ foundry-compilers = { version = "0.8.0", default-features = false } ## revm # no default features to avoid c-kzg -revm = { version = "9.0.0", git = "https://github.com/bluealloy/revm.git", rev = "41e2f7f", default-features = false } -revm-primitives = { version = "4.0.0", git = "https://github.com/bluealloy/revm.git", rev = "41e2f7f", default-features = false } +revm = { version = "9.0.0", default-features = false } +revm-primitives = { version = "4.0.0", default-features = false } revm-inspectors = { git = "https://github.com/paradigmxyz/revm-inspectors", rev = "4fe17f0", features = [ "serde", ] } @@ -251,7 +251,7 @@ tower-http = "0.5" soldeer = "0.2.15" [patch.crates-io] -revm = { git = "https://github.com/bluealloy/revm.git", rev = "dd98b3b" } -revm-interpreter = { git = "https://github.com/bluealloy/revm.git", rev = "dd98b3b" } -revm-precompile = { git = "https://github.com/bluealloy/revm.git", rev = "dd98b3b" } -revm-primitives = { git = "https://github.com/bluealloy/revm.git", rev = "dd98b3b" } +revm = { git = "https://github.com/bluealloy/revm.git", rev = "41e2f7f" } +revm-interpreter = { git = "https://github.com/bluealloy/revm.git", rev = "41e2f7f" } +revm-precompile = { git = "https://github.com/bluealloy/revm.git", rev = "41e2f7f" } +revm-primitives = { git = "https://github.com/bluealloy/revm.git", rev = "41e2f7f" } From 91074f1e9dec3b636db219d42f4441dbfb26b30f Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 18 Jun 2024 20:19:27 +0200 Subject: [PATCH 428/430] perf: optimize load_contracts (#8190) --- .../evm/evm/src/executors/invariant/replay.rs | 11 +++---- crates/evm/traces/src/decoder/mod.rs | 9 +++--- crates/evm/traces/src/identifier/local.rs | 2 +- crates/evm/traces/src/lib.rs | 30 +++++++++---------- crates/forge/src/runner.rs | 13 ++++---- 5 files changed, 30 insertions(+), 35 deletions(-) diff --git a/crates/evm/evm/src/executors/invariant/replay.rs b/crates/evm/evm/src/executors/invariant/replay.rs index d3d974d29..cf9fa12e8 100644 --- a/crates/evm/evm/src/executors/invariant/replay.rs +++ b/crates/evm/evm/src/executors/invariant/replay.rs @@ -30,7 +30,7 @@ pub fn replay_run( logs: &mut Vec, traces: &mut Traces, coverage: &mut Option, - inputs: Vec, + inputs: &[BasicTxDetails], ) -> Result> { // We want traces for a failed case. executor.set_tracing(true); @@ -38,7 +38,7 @@ pub fn replay_run( let mut counterexample_sequence = vec![]; // Replay each call from the sequence, collect logs, traces and coverage. - for tx in inputs.iter() { + for tx in inputs { let call_result = executor.transact_raw( tx.sender, tx.call_details.target, @@ -57,10 +57,7 @@ pub fn replay_run( } // Identify newly generated contracts, if they exist. - ided_contracts.extend(load_contracts( - vec![(TraceKind::Execution, call_result.traces.clone().unwrap())], - known_contracts, - )); + ided_contracts.extend(load_contracts(call_result.traces.as_slice(), known_contracts)); // Create counter example to be used in failed case. counterexample_sequence.push(BaseCounterExample::from_invariant_call( @@ -133,7 +130,7 @@ pub fn replay_error( logs, traces, coverage, - calls, + &calls, ) } } diff --git a/crates/evm/traces/src/decoder/mod.rs b/crates/evm/traces/src/decoder/mod.rs index 2dbdbd7bf..ec1ea199f 100644 --- a/crates/evm/traces/src/decoder/mod.rs +++ b/crates/evm/traces/src/decoder/mod.rs @@ -200,7 +200,7 @@ impl CallTraceDecoder { /// /// Unknown contracts are contracts that either lack a label or an ABI. pub fn identify(&mut self, trace: &CallTraceArena, identifier: &mut impl TraceIdentifier) { - self.collect_identities(identifier.identify_addresses(self.addresses(trace))); + self.collect_identities(identifier.identify_addresses(self.trace_addresses(trace))); } /// Adds a single event to the decoder. @@ -230,7 +230,8 @@ impl CallTraceDecoder { self.revert_decoder.push_error(error); } - fn addresses<'a>( + /// Returns an iterator over the trace addresses. + pub fn trace_addresses<'a>( &'a self, arena: &'a CallTraceArena, ) -> impl Iterator)> + Clone + 'a { @@ -243,8 +244,8 @@ impl CallTraceDecoder { node.trace.kind.is_any_create().then_some(&node.trace.output[..]), ) }) - .filter(|(address, _)| { - !self.labels.contains_key(*address) || !self.contracts.contains_key(*address) + .filter(|&(address, _)| { + !self.labels.contains_key(address) || !self.contracts.contains_key(address) }) } diff --git a/crates/evm/traces/src/identifier/local.rs b/crates/evm/traces/src/identifier/local.rs index e82d73378..04680b01a 100644 --- a/crates/evm/traces/src/identifier/local.rs +++ b/crates/evm/traces/src/identifier/local.rs @@ -95,7 +95,7 @@ impl<'a> LocalTraceIdentifier<'a> { /// artifact with a greater code length if the exact code length is not found. fn find_index(&self, len: usize) -> usize { let (Ok(mut idx) | Err(mut idx)) = - self.ordered_ids.binary_search_by(|(_, probe)| probe.cmp(&len)); + self.ordered_ids.binary_search_by_key(&len, |(_, probe)| *probe); // In case of multiple artifacts with the same code length, we need to find the first one. while idx > 0 && self.ordered_ids[idx - 1].1 == len { diff --git a/crates/evm/traces/src/lib.rs b/crates/evm/traces/src/lib.rs index 052be9c05..d352abfc8 100644 --- a/crates/evm/traces/src/lib.rs +++ b/crates/evm/traces/src/lib.rs @@ -20,7 +20,7 @@ use yansi::{Color, Paint}; /// /// Identifiers figure out what ABIs and labels belong to all the addresses of the trace. pub mod identifier; -use identifier::LocalTraceIdentifier; +use identifier::{LocalTraceIdentifier, TraceIdentifier}; mod decoder; pub use decoder::{CallTraceDecoder, CallTraceDecoderBuilder}; @@ -295,21 +295,19 @@ fn trace_color(trace: &CallTrace) -> Color { } /// Given a list of traces and artifacts, it returns a map connecting address to abi -pub fn load_contracts(traces: Traces, known_contracts: &ContractsByArtifact) -> ContractsByAddress { +pub fn load_contracts<'a>( + traces: impl IntoIterator, + known_contracts: &ContractsByArtifact, +) -> ContractsByAddress { let mut local_identifier = LocalTraceIdentifier::new(known_contracts); - let mut decoder = CallTraceDecoderBuilder::new().build(); - for (_, trace) in &traces { - decoder.identify(trace, &mut local_identifier); - } - - decoder - .contracts - .iter() - .filter_map(|(addr, name)| { - if let Ok(Some((_, contract))) = known_contracts.find_by_name_or_identifier(name) { - return Some((*addr, (name.clone(), contract.abi.clone()))); + let decoder = CallTraceDecoder::new(); + let mut contracts = ContractsByAddress::new(); + for trace in traces { + for address in local_identifier.identify_addresses(decoder.trace_addresses(trace)) { + if let (Some(contract), Some(abi)) = (address.contract, address.abi) { + contracts.insert(address.address, (contract, abi.into_owned())); } - None - }) - .collect() + } + } + contracts } diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index ffd22bf10..d2213cd37 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -384,8 +384,8 @@ impl<'a> ContractRunner<'a> { find_time, ); - let identified_contracts = - has_invariants.then(|| load_contracts(setup.traces.clone(), &known_contracts)); + let identified_contracts = has_invariants + .then(|| load_contracts(setup.traces.iter().map(|(_, t)| t), &known_contracts)); let test_results = functions .par_iter() .map(|&func| { @@ -596,13 +596,12 @@ impl<'a> ContractRunner<'a> { { // Create calls from failed sequence and check if invariant still broken. let txes = call_sequence - .clone() - .into_iter() + .iter() .map(|seq| BasicTxDetails { sender: seq.sender.unwrap_or_default(), call_details: CallDetails { target: seq.addr.unwrap_or_default(), - calldata: seq.calldata, + calldata: seq.calldata.clone(), }, }) .collect::>(); @@ -626,7 +625,7 @@ impl<'a> ContractRunner<'a> { &mut logs, &mut traces, &mut coverage, - txes, + &txes, ); return TestResult { status: TestStatus::Failure, @@ -728,7 +727,7 @@ impl<'a> ContractRunner<'a> { &mut logs, &mut traces, &mut coverage, - last_run_inputs.clone(), + &last_run_inputs, ) { error!(%err, "Failed to replay last invariant run"); } From 588a1d7cd887d17986066f7c3a641f85c9d6c743 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 19 Jun 2024 00:22:33 +0200 Subject: [PATCH 429/430] chore(deps): move more deps to workspace (#8192) --- Cargo.toml | 13 ++++++++++++- crates/anvil/Cargo.toml | 10 +++++----- crates/anvil/server/Cargo.toml | 8 ++++---- crates/cast/Cargo.toml | 8 ++++---- crates/cheatcodes/Cargo.toml | 8 ++++---- crates/chisel/Cargo.toml | 2 +- crates/cli/Cargo.toml | 4 ++-- crates/common/Cargo.toml | 14 +++++++------- crates/config/Cargo.toml | 14 +++++++------- crates/doc/Cargo.toml | 6 +++--- crates/evm/core/Cargo.toml | 20 ++++++++++---------- crates/evm/coverage/Cargo.toml | 6 +++--- crates/evm/evm/Cargo.toml | 8 ++++---- crates/evm/fuzz/Cargo.toml | 10 +++++----- crates/evm/traces/Cargo.toml | 10 +++++----- crates/fmt/Cargo.toml | 2 +- crates/forge/Cargo.toml | 14 +++++++------- crates/linking/Cargo.toml | 4 ++-- crates/script/Cargo.toml | 8 ++++---- crates/test-utils/Cargo.toml | 8 ++++---- crates/verify/Cargo.toml | 8 ++++---- crates/wallets/Cargo.toml | 4 ++-- 22 files changed, 100 insertions(+), 89 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9a327b7a3..f40876ac8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -205,6 +205,9 @@ alloy-rlp = "0.3.3" solang-parser = "=0.3.3" ## misc +async-trait = "0.1" +auto_impl = "1" +walkdir = "2" proc-macro2 = "1.0.82" quote = "1.0" syn = "2.0" @@ -218,24 +221,32 @@ chrono = { version = "0.4", default-features = false, features = [ ] } color-eyre = "0.6" derive_more = "0.99" +dunce = "1" evm-disassembler = "0.5" eyre = "0.6" +figment = "0.10" +futures = "0.3" hex = { package = "const-hex", version = "1.6", features = ["hex"] } itertools = "0.13" jsonpath_lib = "0.3" k256 = "0.13" -similar-asserts = "1.5" +once_cell = "1" +parking_lot = "0.12" rand = "0.8" rustc-hash = "1.1" +semver = "1" serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["arbitrary_precision"] } +similar-asserts = "1.5" strum = "0.26" +thiserror = "1" toml = "0.8" tracing = "0.1" tracing-subscriber = "0.3" vergen = { version = "8", default-features = false } indexmap = "2.2" tikv-jemallocator = "0.5.4" +url = "2" num-format = "0.4.4" yansi = { version = "1.0", features = ["detect-tty", "detect-env"] } tempfile = "3.10" diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index 9d31ce6d6..c374a8c8c 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -80,16 +80,16 @@ tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] } # async tokio = { workspace = true, features = ["time"] } -parking_lot = "0.12" -futures = "0.3" -async-trait = "0.1" +parking_lot.workspace = true +futures.workspace = true +async-trait.workspace = true # misc flate2 = "1.0" serde_repr = "0.1" serde_json.workspace = true serde.workspace = true -thiserror = "1" +thiserror .workspace = true yansi.workspace = true tempfile.workspace = true itertools.workspace = true @@ -104,7 +104,7 @@ clap = { version = "4", features = [ ], optional = true } clap_complete = { version = "4", optional = true } chrono.workspace = true -auto_impl = "1" +auto_impl.workspace = true ctrlc = { version = "3", optional = true } fdlimit = { version = "0.3", optional = true } clap_complete_fig = "4" diff --git a/crates/anvil/server/Cargo.toml b/crates/anvil/server/Cargo.toml index 051da35c7..282e09013 100644 --- a/crates/anvil/server/Cargo.toml +++ b/crates/anvil/server/Cargo.toml @@ -24,8 +24,8 @@ tower-http = { workspace = true, features = ["trace", "cors"] } tracing.workspace = true # async -parking_lot = "0.12" -futures = "0.3" +parking_lot.workspace = true +futures.workspace = true # ipc interprocess = { version = "2", optional = true, features = ["tokio"] } @@ -35,8 +35,8 @@ tokio-util = { version = "0.7", features = ["codec"], optional = true } # misc serde_json.workspace = true serde.workspace = true -async-trait = "0.1" -thiserror = "1" +async-trait.workspace = true +thiserror.workspace = true clap = { version = "4", features = ["derive", "env"], optional = true } pin-project = "1" diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index 02312eb78..525c2c3f7 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -53,7 +53,7 @@ alloy-transport.workspace = true chrono.workspace = true evm-disassembler.workspace = true eyre.workspace = true -futures = "0.3" +futures.workspace = true hex.workspace = true rand.workspace = true rayon.workspace = true @@ -70,12 +70,12 @@ clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } clap_complete = "4" clap_complete_fig = "4" comfy-table = "7" -dunce = "1" +dunce.workspace = true indicatif = "0.17" itertools.workspace = true regex = { version = "1", default-features = false } rpassword = "7" -semver = "1" +semver.workspace = true tempfile.workspace = true tokio = { workspace = true, features = ["macros", "signal"] } tracing.workspace = true @@ -87,7 +87,7 @@ tikv-jemallocator = { workspace = true, optional = true } [dev-dependencies] foundry-test-utils.workspace = true -async-trait = "0.1" +async-trait.workspace = true criterion = "0.5" [features] diff --git a/crates/cheatcodes/Cargo.toml b/crates/cheatcodes/Cargo.toml index 41415c2f8..b155a8643 100644 --- a/crates/cheatcodes/Cargo.toml +++ b/crates/cheatcodes/Cargo.toml @@ -34,7 +34,7 @@ alloy-signer-local = { workspace = true, features = [ "mnemonic-all-languages", "keystore", ] } -parking_lot = "0.12" +parking_lot.workspace = true eyre.workspace = true hex.workspace = true @@ -46,10 +46,10 @@ base64.workspace = true toml = { workspace = true, features = ["preserve_order"] } tracing.workspace = true k256.workspace = true -walkdir = "2" +walkdir.workspace = true p256 = "0.13.2" -thiserror = "1" -semver = "1" +thiserror.workspace = true +semver.workspace = true rustc-hash.workspace = true dialoguer = "0.11.0" rand = "0.8" diff --git a/crates/chisel/Cargo.toml b/crates/chisel/Cargo.toml index f527deb2e..64e218f97 100644 --- a/crates/chisel/Cargo.toml +++ b/crates/chisel/Cargo.toml @@ -55,7 +55,7 @@ regex = "1" reqwest.workspace = true revm.workspace = true rustyline = "12" -semver = "1" +semver.workspace = true serde_json.workspace = true serde.workspace = true solang-parser.workspace = true diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 7b66c7045..f13dbe5f6 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -34,7 +34,7 @@ color-eyre.workspace = true dotenvy = "0.15" eyre.workspace = true indicatif = "0.17" -once_cell = "1" +once_cell.workspace = true regex = { version = "1", default-features = false } serde.workspace = true strsim = "0.10" @@ -45,7 +45,7 @@ tracing-subscriber = { workspace = true, features = ["registry", "env-filter", " tracing.workspace = true yansi.workspace = true hex.workspace = true -futures = "0.3" +futures.workspace = true [dev-dependencies] tempfile.workspace = true diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index c8c79534d..953835abc 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -46,21 +46,21 @@ alloy-transport.workspace = true tower.workspace = true -async-trait = "0.1" +async-trait.workspace = true clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } comfy-table = "7" -dunce = "1" +dunce.workspace = true eyre.workspace = true -once_cell = "1" +once_cell.workspace = true reqwest.workspace = true -semver = "1" +semver.workspace = true serde_json.workspace = true serde.workspace = true -thiserror = "1" +thiserror.workspace = true tokio.workspace = true tracing.workspace = true -url = "2" -walkdir = "2" +url.workspace = true +walkdir.workspace = true yansi.workspace = true rustc-hash.workspace = true num-format.workspace = true diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml index c487c78c6..445d80940 100644 --- a/crates/config/Cargo.toml +++ b/crates/config/Cargo.toml @@ -21,32 +21,32 @@ revm-primitives.workspace = true solang-parser.workspace = true dirs-next = "2" -dunce = "1" +dunce.workspace = true eyre.workspace = true -figment = { version = "0.10", features = ["toml", "env"] } +figment = { workspace = true, features = ["toml", "env"] } globset = "0.4" glob = "0.3" Inflector = "0.11" number_prefix = "0.4" -once_cell = "1" +once_cell.workspace = true regex = "1" reqwest.workspace = true -semver = { version = "1", features = ["serde"] } +semver = { workspace = true, features = ["serde"] } serde_json.workspace = true serde_regex = "1" serde.workspace = true -thiserror = "1" +thiserror.workspace = true toml = { version = "0.8", features = ["preserve_order"] } toml_edit = "0.22.4" tracing.workspace = true -walkdir = "2" +walkdir.workspace = true [target.'cfg(target_os = "windows")'.dependencies] path-slash = "0.2.1" [dev-dependencies] similar-asserts.workspace = true -figment = { version = "0.10", features = ["test"] } +figment = { workspace = true, features = ["test"] } tempfile.workspace = true [features] diff --git a/crates/doc/Cargo.toml b/crates/doc/Cargo.toml index 36b107a9e..ab74ef697 100644 --- a/crates/doc/Cargo.toml +++ b/crates/doc/Cargo.toml @@ -20,17 +20,17 @@ foundry-config.workspace = true alloy-primitives.workspace = true -auto_impl = "1" +auto_impl.workspace = true derive_more = "0.99" eyre.workspace = true itertools.workspace = true mdbook = { version = "0.4", default-features = false, features = ["search"] } -once_cell = "1" +once_cell.workspace = true rayon.workspace = true serde_json.workspace = true serde.workspace = true solang-parser.workspace = true -thiserror = "1" +thiserror.workspace = true toml.workspace = true tracing.workspace = true regex = "1.10.2" diff --git a/crates/evm/core/Cargo.toml b/crates/evm/core/Cargo.toml index b8253b494..d44f2cb4b 100644 --- a/crates/evm/core/Cargo.toml +++ b/crates/evm/core/Cargo.toml @@ -48,21 +48,21 @@ revm = { workspace = true, features = [ revm-inspectors.workspace = true arrayvec.workspace = true -auto_impl = "1" +auto_impl.workspace = true derive_more.workspace = true -eyre = "0.6" -futures = "0.3" +eyre.workspace = true +futures.workspace = true hex.workspace = true itertools.workspace = true -once_cell = "1" -parking_lot = "0.12" +once_cell.workspace = true +parking_lot.workspace = true rustc-hash.workspace = true -serde = "1" -serde_json = "1" -thiserror = "1" +serde.workspace = true +serde_json.workspace = true +thiserror.workspace = true tokio = { workspace = true, features = ["time", "macros"] } -tracing = "0.1" -url = "2" +tracing.workspace = true +url.workspace = true [dev-dependencies] foundry-test-utils.workspace = true diff --git a/crates/evm/coverage/Cargo.toml b/crates/evm/coverage/Cargo.toml index 6858159de..777c37d99 100644 --- a/crates/evm/coverage/Cargo.toml +++ b/crates/evm/coverage/Cargo.toml @@ -19,9 +19,9 @@ foundry-compilers.workspace = true foundry-evm-core.workspace = true alloy-primitives.workspace = true -eyre = "0.6" +eyre.workspace = true revm.workspace = true -semver = "1" -tracing = "0.1" +semver.workspace = true +tracing.workspace = true rustc-hash.workspace = true rayon.workspace = true diff --git a/crates/evm/evm/Cargo.toml b/crates/evm/evm/Cargo.toml index 3b00c9d66..bcd933a4b 100644 --- a/crates/evm/evm/Cargo.toml +++ b/crates/evm/evm/Cargo.toml @@ -45,9 +45,9 @@ revm = { workspace = true, default-features = false, features = [ revm-inspectors.workspace = true arrayvec.workspace = true -eyre = "0.6" -parking_lot = "0.12" +eyre.workspace = true +parking_lot.workspace = true proptest = "1" -thiserror = "1" -tracing = "0.1" +thiserror.workspace = true +tracing.workspace = true indicatif = "0.17" diff --git a/crates/evm/fuzz/Cargo.toml b/crates/evm/fuzz/Cargo.toml index 4101c080f..0106b1d27 100644 --- a/crates/evm/fuzz/Cargo.toml +++ b/crates/evm/fuzz/Cargo.toml @@ -39,13 +39,13 @@ revm = { workspace = true, features = [ "arbitrary", ] } -eyre = "0.6" +eyre .workspace = true itertools.workspace = true -parking_lot = "0.12" +parking_lot.workspace = true proptest = "1" rand.workspace = true -serde = "1" -thiserror = "1" -tracing = "0.1" +serde.workspace = true +thiserror.workspace = true +tracing.workspace = true indexmap.workspace = true ahash.workspace = true diff --git a/crates/evm/traces/Cargo.toml b/crates/evm/traces/Cargo.toml index 8c2ccb3ae..b076db127 100644 --- a/crates/evm/traces/Cargo.toml +++ b/crates/evm/traces/Cargo.toml @@ -31,14 +31,14 @@ alloy-primitives = { workspace = true, features = [ alloy-sol-types.workspace = true revm-inspectors.workspace = true -eyre = "0.6" -futures = "0.3" +eyre .workspace = true +futures.workspace = true hex.workspace = true itertools.workspace = true -once_cell = "1" -serde = "1" +once_cell.workspace = true +serde.workspace = true tokio = { workspace = true, features = ["time", "macros"] } -tracing = "0.1" +tracing.workspace = true yansi.workspace = true rustc-hash.workspace = true tempfile.workspace = true diff --git a/crates/fmt/Cargo.toml b/crates/fmt/Cargo.toml index 835b2e8b7..0bc3e06a6 100644 --- a/crates/fmt/Cargo.toml +++ b/crates/fmt/Cargo.toml @@ -20,7 +20,7 @@ alloy-primitives.workspace = true ariadne = "0.4" itertools.workspace = true solang-parser.workspace = true -thiserror = "1" +thiserror.workspace = true tracing.workspace = true [dev-dependencies] diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 493a8c042..23a4f22a6 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -70,26 +70,26 @@ alloy-sol-macro-expander = { workspace = true, features = ["json"] } alloy-sol-macro-input.workspace = true alloy-transport.workspace = true -async-trait = "0.1" +async-trait.workspace = true clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } clap_complete = "4" clap_complete_fig = "4" dialoguer = { version = "0.11", default-features = false } -dunce = "1" -futures = "0.3" +dunce.workspace = true +futures.workspace = true hex.workspace = true indicatif = "0.17" itertools.workspace = true -once_cell = "1" -parking_lot = "0.12" +once_cell.workspace = true +parking_lot.workspace = true regex = { version = "1", default-features = false } reqwest = { workspace = true, features = ["json"] } -semver = "1" +semver.workspace = true serde_json.workspace = true similar = { version = "2", features = ["inline"] } solang-parser.workspace = true strum = { workspace = true, features = ["derive"] } -thiserror = "1" +thiserror.workspace = true tokio = { workspace = true, features = ["time"] } toml = { version = "0.8", features = ["preserve_order"] } toml_edit = "0.22.4" diff --git a/crates/linking/Cargo.toml b/crates/linking/Cargo.toml index 2587244a2..15d0d113b 100644 --- a/crates/linking/Cargo.toml +++ b/crates/linking/Cargo.toml @@ -15,6 +15,6 @@ workspace = true [dependencies] foundry-compilers = { workspace = true, features = ["full"] } -semver = "1" +semver.workspace = true alloy-primitives = { workspace = true, features = ["rlp"] } -thiserror = "1" +thiserror.workspace = true diff --git a/crates/script/Cargo.toml b/crates/script/Cargo.toml index 4d97f3fc9..a57a11817 100644 --- a/crates/script/Cargo.toml +++ b/crates/script/Cargo.toml @@ -28,16 +28,16 @@ hex.workspace = true serde.workspace = true eyre.workspace = true serde_json.workspace = true -dunce = "1" +dunce.workspace = true foundry-compilers = { workspace = true, features = ["full"] } tracing.workspace = true clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } -semver = "1" -futures = "0.3" +semver.workspace = true +futures.workspace = true async-recursion = "1.0.5" itertools.workspace = true -parking_lot = "0.12" +parking_lot.workspace = true yansi.workspace = true revm-inspectors.workspace = true alloy-rpc-types.workspace = true diff --git a/crates/test-utils/Cargo.toml b/crates/test-utils/Cargo.toml index 710050dab..95ef8d6c9 100644 --- a/crates/test-utils/Cargo.toml +++ b/crates/test-utils/Cargo.toml @@ -24,14 +24,14 @@ alloy-provider.workspace = true eyre.workspace = true fd-lock = "4.0.0" -once_cell = "1" -parking_lot = "0.12" +once_cell.workspace = true +parking_lot.workspace = true similar-asserts.workspace = true regex = "1" serde_json.workspace = true -tracing = "0.1" +tracing.workspace = true tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] } -walkdir = "2" +walkdir.workspace = true rand.workspace = true [features] diff --git a/crates/verify/Cargo.toml b/crates/verify/Cargo.toml index 378a292b8..f308381d4 100644 --- a/crates/verify/Cargo.toml +++ b/crates/verify/Cargo.toml @@ -33,11 +33,11 @@ foundry-block-explorers = { workspace = true, features = ["foundry-compilers"] } clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } reqwest = { workspace = true, features = ["json"] } -async-trait = "0.1" -futures = "0.3" -semver = "1" +async-trait.workspace = true +futures.workspace = true +semver.workspace = true regex = { version = "1", default-features = false } -once_cell = "1" +once_cell.workspace = true yansi.workspace = true itertools.workspace = true diff --git a/crates/wallets/Cargo.toml b/crates/wallets/Cargo.toml index dc013ae62..183a97854 100644 --- a/crates/wallets/Cargo.toml +++ b/crates/wallets/Cargo.toml @@ -37,14 +37,14 @@ gcloud-sdk = { version = "0.24", features = [ "google-longrunning", ], optional = true } -async-trait = "0.1" +async-trait.workspace = true clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } derive_builder = "0.20.0" eyre.workspace = true hex = { workspace = true, features = ["serde"] } rpassword = "7" serde.workspace = true -thiserror = "1" +thiserror.workspace = true tracing.workspace = true [dev-dependencies] From 4af6cfaef200ad3ffa598ba419f79b15cb962e52 Mon Sep 17 00:00:00 2001 From: funnybird Date: Wed, 19 Jun 2024 17:20:08 +0800 Subject: [PATCH 430/430] feat(cast): ux upgrade cast block returning block time in readable format. (#8195) * feat(cast): support readable time for cast block * fix: remove `time` * Update crates/common/src/fmt/ui.rs * fmt --------- Co-authored-by: fenghaojiang Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- Cargo.lock | 1 + crates/common/Cargo.toml | 1 + crates/common/src/fmt/ui.rs | 5 ++++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 481ff0646..da7aee449 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3639,6 +3639,7 @@ dependencies = [ "alloy-transport-ipc", "alloy-transport-ws", "async-trait", + "chrono", "clap", "comfy-table", "dunce", diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 953835abc..afc8aa237 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -64,6 +64,7 @@ walkdir.workspace = true yansi.workspace = true rustc-hash.workspace = true num-format.workspace = true +chrono.workspace = true [dev-dependencies] foundry-macros.workspace = true diff --git a/crates/common/src/fmt/ui.rs b/crates/common/src/fmt/ui.rs index e192f3cad..0fe8dccd1 100644 --- a/crates/common/src/fmt/ui.rs +++ b/crates/common/src/fmt/ui.rs @@ -488,7 +488,7 @@ receiptsRoot {} sha3Uncles {} size {} stateRoot {} -timestamp {} +timestamp {} ({}) withdrawalsRoot {} totalDifficulty {}{}", block.header.base_fee_per_gas.pretty(), @@ -509,6 +509,9 @@ totalDifficulty {}{}", block.size.pretty(), block.header.state_root.pretty(), block.header.timestamp.pretty(), + chrono::DateTime::from_timestamp(block.header.timestamp as i64, 0) + .expect("block timestamp in range") + .to_rfc2822(), block.header.withdrawals_root.pretty(), block.header.total_difficulty.pretty(), block.other.pretty()